diff --git a/app/build.gradle b/app/build.gradle index 6ce4e245f77..df003aa855f 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,38 +1,20 @@ - -buildscript { - repositories { - google() - mavenCentral() - } - dependencies { - classpath "com.android.tools.build:gradle:$gradlePluginVersion" - classpath files('libs/gradle-witness.jar') - classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion" - classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlinVersion" - classpath "com.google.gms:google-services:$googleServicesVersion" - classpath "com.google.dagger:hilt-android-gradle-plugin:$daggerVersion" - } -} - plugins { - id 'kotlin-kapt' + id 'com.google.devtools.ksp' id 'com.google.dagger.hilt.android' } apply plugin: 'com.android.application' apply plugin: 'kotlin-android' apply plugin: 'witness' -apply plugin: 'kotlin-kapt' apply plugin: 'kotlin-parcelize' apply plugin: 'kotlinx-serialization' -apply plugin: 'dagger.hilt.android.plugin' -configurations.all { - exclude module: "commons-logging" +configurations.forEach { + it.exclude module: "commons-logging" } -def canonicalVersionCode = 379 -def canonicalVersionName = "1.19.1" +def canonicalVersionCode = 380 +def canonicalVersionName = "1.19.2" def postFixSize = 10 def abiPostFix = ['armeabi-v7a' : 1, @@ -67,13 +49,9 @@ android { } packagingOptions { - exclude 'LICENSE.txt' - exclude 'LICENSE' - exclude 'NOTICE' - exclude 'asm-license.txt' - exclude 'META-INF/LICENSE' - exclude 'META-INF/NOTICE' - exclude 'META-INF/proguard/androidx-annotations.pro' + resources { + excludes += ['LICENSE.txt', 'LICENSE', 'NOTICE', 'asm-license.txt', 'META-INF/LICENSE', 'META-INF/NOTICE', 'META-INF/proguard/androidx-annotations.pro'] + } } splits { @@ -88,8 +66,9 @@ android { buildFeatures { compose true } + composeOptions { - kotlinCompilerExtensionVersion '1.4.7' + kotlinCompilerExtensionVersion '1.5.14' } defaultConfig { @@ -111,8 +90,8 @@ android { buildConfigField "String", "USER_AGENT", "\"OWA\"" buildConfigField "String[]", "LANGUAGES", "new String[]{\"" + autoResConfig().collect { s -> s.replace('-r', '_') }.join('", "') + '"}' buildConfigField "int", "CANONICAL_VERSION_CODE", "$canonicalVersionCode" + resourceConfigurations += [] - resConfigs autoResConfig() testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" // The following argument makes the Android Test Orchestrator run its // "pm clear" command after each test invocation. This command ensures @@ -172,7 +151,7 @@ android { } } - applicationVariants.all { variant -> + applicationVariants.forEach { variant -> variant.outputs.each { output -> def abiName = output.getFilter("ABI") ?: 'universal' def postFix = abiPostFix.get(abiName, 0) @@ -183,10 +162,6 @@ android { } } - lintOptions { - abortOnError true - baseline file("lint-baseline.xml") - } testOptions { unitTests { @@ -195,7 +170,6 @@ android { } buildFeatures { - dataBinding true viewBinding true } @@ -215,9 +189,11 @@ android { } } - task testPlayDebugUnitTestCoverageReport(type: JacocoReport, dependsOn: "testPlayDebugUnitTest") { + tasks.register('testPlayDebugUnitTestCoverageReport', JacocoReport) { + dependsOn 'testPlayDebugUnitTest' + reports { - xml.enabled = true + xml.required = true } // Add files that should not be listed in the report (e.g. generated Files from dagger) @@ -235,12 +211,20 @@ android { // This is enabled with 'enableUnitTestCoverage' in the 'debug' build type. executionData.from = "${project.buildDir}/outputs/unit_test_code_coverage/playDebugUnitTest/testPlayDebugUnitTest.exec" } + + + testNamespace 'network.loki.messenger.test' + lint { + abortOnError true + baseline file('lint-baseline.xml') + } } dependencies { - implementation("com.google.dagger:hilt-android:2.46.1") - kapt("com.google.dagger:hilt-android-compiler:2.44") + implementation("com.google.dagger:hilt-android:$daggerHiltVersion") + ksp("com.google.dagger:hilt-compiler:$daggerHiltVersion") + ksp("androidx.hilt:hilt-compiler:$jetpackHiltVersion") implementation "androidx.appcompat:appcompat:$appcompatVersion" implementation 'androidx.recyclerview:recyclerview:1.2.1' @@ -260,6 +244,7 @@ dependencies { implementation "androidx.lifecycle:lifecycle-extensions:2.2.0" implementation "androidx.paging:paging-runtime-ktx:$pagingVersion" implementation 'androidx.activity:activity-ktx:1.5.1' + implementation 'androidx.activity:activity-compose:1.5.1' implementation 'androidx.fragment:fragment-ktx:1.5.3' implementation "androidx.core:core-ktx:$coreVersion" implementation "androidx.work:work-runtime-ktx:2.7.1" @@ -281,18 +266,14 @@ dependencies { implementation 'commons-net:commons-net:3.7.2' implementation 'com.github.chrisbanes:PhotoView:2.1.3' implementation "com.github.bumptech.glide:glide:$glideVersion" - annotationProcessor "com.github.bumptech.glide:compiler:$glideVersion" - kapt "com.github.bumptech.glide:compiler:$glideVersion" + implementation "com.github.bumptech.glide:compose:1.0.0-beta01" + ksp "com.github.bumptech.glide:ksp:$glideVersion" implementation 'com.makeramen:roundedimageview:2.1.0' implementation 'com.pnikosis:materialish-progress:1.5' implementation 'org.greenrobot:eventbus:3.0.0' implementation 'pl.tajchert:waitingdots:0.1.0' implementation 'com.vanniktech:android-image-cropper:4.5.0' implementation 'com.melnykov:floatingactionbutton:1.3.0' - implementation 'com.google.zxing:android-integration:3.1.0' - implementation "com.google.dagger:hilt-android:$daggerVersion" - kapt "com.google.dagger:hilt-compiler:$daggerVersion" - implementation 'com.google.zxing:core:3.2.1' implementation ('com.davemorrissey.labs:subsampling-scale-image-view:3.6.0') { exclude group: 'com.android.support', module: 'support-annotations' } @@ -304,7 +285,6 @@ dependencies { exclude group: 'com.squareup.okhttp', module: 'okhttp-urlconnection' } implementation 'com.annimon:stream:1.1.8' - implementation project(':stickyheader') implementation 'com.github.dmytrodanylyk.circular-progress-button:library:1.1.3-S2' implementation 'androidx.sqlite:sqlite-ktx:2.3.1' implementation 'net.zetetic:sqlcipher-android:4.5.4@aar' @@ -370,7 +350,6 @@ dependencies { testImplementation 'org.conscrypt:conscrypt-openjdk-uber:2.5.2' // For Robolectric testImplementation 'app.cash.turbine:turbine:1.1.0' - implementation 'com.github.bumptech.glide:compose:1.0.0-alpha.5' implementation "androidx.compose.ui:ui:$composeVersion" implementation "androidx.compose.animation:animation:$composeVersion" @@ -389,7 +368,8 @@ dependencies { implementation "androidx.camera:camera-lifecycle:1.3.2" implementation "androidx.camera:camera-view:1.3.2" - implementation "com.google.mlkit:barcode-scanning:17.2.0" + // Note: ZXing 3.5.3 is the latest stable release as of 2024/08/21 + implementation "com.google.zxing:core:$zxingVersion" } static def getLastCommitTimestamp() { @@ -410,8 +390,3 @@ def autoResConfig() { .collect { matcher -> matcher.group(1) } .sort() } - -// Allow references to generated code -kapt { - correctErrorTypes = true -} diff --git a/app/src/androidTest/AndroidManifest.xml b/app/src/androidTest/AndroidManifest.xml index deab87dd621..023120e1a8e 100644 --- a/app/src/androidTest/AndroidManifest.xml +++ b/app/src/androidTest/AndroidManifest.xml @@ -1,6 +1,5 @@ + xmlns:android="http://schemas.android.com/apk/res/android"> diff --git a/app/src/androidTest/java/network/loki/messenger/HomeActivityTests.kt b/app/src/androidTest/java/network/loki/messenger/HomeActivityTests.kt index 6fb3888ff2a..2f3ba9fb10a 100644 --- a/app/src/androidTest/java/network/loki/messenger/HomeActivityTests.kt +++ b/app/src/androidTest/java/network/loki/messenger/HomeActivityTests.kt @@ -40,7 +40,11 @@ import org.session.libsignal.utilities.guava.Optional import org.thoughtcrime.securesms.conversation.v2.ConversationActivityV2 import org.thoughtcrime.securesms.conversation.v2.input_bar.InputBar import org.thoughtcrime.securesms.home.HomeActivity -import org.thoughtcrime.securesms.mms.GlideApp +import com.bumptech.glide.Glide + +/** + * Currently not used as part of our CI/Deployment processes !!!! + */ @RunWith(AndroidJUnit4::class) @LargeTest @@ -71,7 +75,7 @@ class HomeActivityTests { onView(allOf(isDescendantOfA(withId(R.id.inputBar)),withId(R.id.inputBarEditText))).perform(ViewActions.replaceText(messageToSend)) if (linkPreview != null) { val activity = activityMonitor.waitForActivity() as ConversationActivityV2 - val glide = GlideApp.with(activity) + val glide = Glide.with(activity) activity.findViewById(R.id.inputBar).updateLinkPreviewDraft(glide, linkPreview) } onView(allOf(isDescendantOfA(withId(R.id.inputBar)),inputButtonWithDrawable(R.drawable.ic_arrow_up))).perform(ViewActions.click()) @@ -107,7 +111,7 @@ class HomeActivityTests { } - private fun goToMyChat() { +/* private fun goToMyChat() { onView(withId(R.id.newConversationButton)).perform(ViewActions.click()) onView(withId(R.id.createPrivateChatButton)).perform(ViewActions.click()) // new chat @@ -122,7 +126,7 @@ class HomeActivityTests { onView(withId(R.id.publicKeyEditText)).perform(ViewActions.typeText(copied)) onView(withId(R.id.publicKeyEditText)).perform(ViewActions.closeSoftKeyboard()) onView(withId(R.id.createPrivateChatButton)).perform(ViewActions.click()) - } + }*/ @Test fun testLaunches_dismiss_seedView() { @@ -145,7 +149,7 @@ class HomeActivityTests { onView(withId(R.id.seedReminderView)).check(matches(not(isDisplayed()))) } - @Test +/* @Test fun testChat_withSelf() { setupLoggedInState() goToMyChat() @@ -176,7 +180,7 @@ class HomeActivityTests { onView(isRoot()).perform(waitFor(1000)) // no other way for this to work apparently onView(withText(dialogPromptText)).check(matches(isDisplayed())) - } + }*/ /** * Perform action of waiting for a specific time. diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 9ed5bc0b539..98f9aa4b5f7 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -291,10 +291,6 @@ android:name="org.thoughtcrime.securesms.scribbles.StickerSelectActivity" android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize" android:theme="@style/Theme.Session.ForceDark" /> - + + @@ -328,7 +326,6 @@ - - + diff --git a/app/src/main/java/org/thoughtcrime/securesms/BaseActivity.java b/app/src/main/java/org/thoughtcrime/securesms/BaseActivity.java index d5286698cdd..d44978b05b3 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/BaseActivity.java +++ b/app/src/main/java/org/thoughtcrime/securesms/BaseActivity.java @@ -4,12 +4,8 @@ import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory; -import android.os.Build; -import android.os.Build.VERSION; -import android.os.Build.VERSION_CODES; -import androidx.annotation.NonNull; + import androidx.fragment.app.FragmentActivity; -import android.view.KeyEvent; import org.session.libsession.utilities.TextSecurePreferences; import org.session.libsession.utilities.dynamiclanguage.DynamicLanguageActivityHelper; diff --git a/app/src/main/java/org/thoughtcrime/securesms/MediaDocumentsAdapter.java b/app/src/main/java/org/thoughtcrime/securesms/MediaDocumentsAdapter.java deleted file mode 100644 index bbc00472a59..00000000000 --- a/app/src/main/java/org/thoughtcrime/securesms/MediaDocumentsAdapter.java +++ /dev/null @@ -1,132 +0,0 @@ -package org.thoughtcrime.securesms; - - -import android.content.ActivityNotFoundException; -import android.content.Context; -import android.content.Intent; -import android.database.Cursor; -import androidx.annotation.NonNull; -import androidx.recyclerview.widget.RecyclerView; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.TextView; -import android.widget.Toast; - -import org.thoughtcrime.securesms.MediaDocumentsAdapter.HeaderViewHolder; -import org.thoughtcrime.securesms.MediaDocumentsAdapter.ViewHolder; -import org.thoughtcrime.securesms.components.DocumentView; -import org.thoughtcrime.securesms.database.CursorRecyclerViewAdapter; -import org.thoughtcrime.securesms.database.MediaDatabase; -import org.session.libsignal.utilities.Log; -import org.thoughtcrime.securesms.mms.DocumentSlide; -import org.thoughtcrime.securesms.mms.PartAuthority; -import org.thoughtcrime.securesms.mms.Slide; -import org.thoughtcrime.securesms.util.DateUtils; -import org.thoughtcrime.securesms.util.MediaUtil; -import org.thoughtcrime.securesms.util.StickyHeaderDecoration; - -import org.session.libsession.utilities.Util; - -import java.util.Calendar; -import java.util.Date; -import java.util.Locale; - -import network.loki.messenger.R; - -import static com.codewaves.stickyheadergrid.StickyHeaderGridLayoutManager.TAG; - -public class MediaDocumentsAdapter extends CursorRecyclerViewAdapter implements StickyHeaderDecoration.StickyHeaderAdapter { - - private final Calendar calendar; - private final Locale locale; - - MediaDocumentsAdapter(Context context, Cursor cursor, Locale locale) { - super(context, cursor); - - this.calendar = Calendar.getInstance(); - this.locale = locale; - } - - @Override - public ViewHolder onCreateItemViewHolder(ViewGroup parent, int viewType) { - return new ViewHolder(LayoutInflater.from(getContext()).inflate(R.layout.media_overview_document_item, parent, false)); - } - - @Override - public void onBindItemViewHolder(ViewHolder viewHolder, @NonNull Cursor cursor) { - MediaDatabase.MediaRecord mediaRecord = MediaDatabase.MediaRecord.from(getContext(), cursor); - Slide slide = MediaUtil.getSlideForAttachment(getContext(), mediaRecord.getAttachment()); - - if (slide != null && slide.hasDocument()) { - viewHolder.documentView.setDocument((DocumentSlide)slide, false); - viewHolder.date.setText(DateUtils.getRelativeDate(getContext(), locale, mediaRecord.getDate())); - viewHolder.documentView.setVisibility(View.VISIBLE); - viewHolder.date.setVisibility(View.VISIBLE); - viewHolder.documentView.setOnClickListener(view -> { - Intent intent = new Intent(Intent.ACTION_VIEW); - intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); - intent.setDataAndType(PartAuthority.getAttachmentPublicUri(slide.getUri()), slide.getContentType()); - try { - getContext().startActivity(intent); - } catch (ActivityNotFoundException anfe) { - Log.w(TAG, "No activity existed to view the media."); - Toast.makeText(getContext(), R.string.ConversationItem_unable_to_open_media, Toast.LENGTH_LONG).show(); - } - }); - } else { - viewHolder.documentView.setVisibility(View.GONE); - viewHolder.date.setVisibility(View.GONE); - } - } - - @Override - public long getHeaderId(int position) { - if (!isActiveCursor()) return -1; - if (isHeaderPosition(position)) return -1; - if (isFooterPosition(position)) return -1; - if (position >= getItemCount()) return -1; - if (position < 0) return -1; - - Cursor cursor = getCursorAtPositionOrThrow(position); - MediaDatabase.MediaRecord mediaRecord = MediaDatabase.MediaRecord.from(getContext(), cursor); - - calendar.setTime(new Date(mediaRecord.getDate())); - return Util.hashCode(calendar.get(Calendar.YEAR), calendar.get(Calendar.DAY_OF_YEAR)); - } - - @Override - public HeaderViewHolder onCreateHeaderViewHolder(ViewGroup parent) { - return new HeaderViewHolder(LayoutInflater.from(getContext()).inflate(R.layout.media_overview_document_item_header, parent, false)); - } - - @Override - public void onBindHeaderViewHolder(HeaderViewHolder viewHolder, int position) { - Cursor cursor = getCursorAtPositionOrThrow(position); - MediaDatabase.MediaRecord mediaRecord = MediaDatabase.MediaRecord.from(getContext(), cursor); - viewHolder.textView.setText(DateUtils.getRelativeDate(getContext(), locale, mediaRecord.getDate())); - } - - public static class ViewHolder extends RecyclerView.ViewHolder { - - private final DocumentView documentView; - private final TextView date; - - public ViewHolder(View itemView) { - super(itemView); - this.documentView = itemView.findViewById(R.id.document_view); - this.date = itemView.findViewById(R.id.date); - } - } - - static class HeaderViewHolder extends RecyclerView.ViewHolder { - - private final TextView textView; - - HeaderViewHolder(View itemView) { - super(itemView); - this.textView = itemView.findViewById(R.id.text); - } - } - -} diff --git a/app/src/main/java/org/thoughtcrime/securesms/MediaGalleryAdapter.java b/app/src/main/java/org/thoughtcrime/securesms/MediaGalleryAdapter.java deleted file mode 100644 index 62766d1cd78..00000000000 --- a/app/src/main/java/org/thoughtcrime/securesms/MediaGalleryAdapter.java +++ /dev/null @@ -1,173 +0,0 @@ -/* - * Copyright (C) 2015 Open Whisper Systems - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package org.thoughtcrime.securesms; - -import android.content.Context; -import androidx.annotation.NonNull; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.TextView; - -import com.codewaves.stickyheadergrid.StickyHeaderGridAdapter; - - -import org.thoughtcrime.securesms.conversation.v2.utilities.ThumbnailView; -import org.thoughtcrime.securesms.database.MediaDatabase.MediaRecord; -import org.thoughtcrime.securesms.database.loaders.BucketedThreadMediaLoader.BucketedThreadMedia; -import org.thoughtcrime.securesms.mms.GlideRequests; -import org.thoughtcrime.securesms.mms.Slide; -import org.thoughtcrime.securesms.util.MediaUtil; - -import java.util.Collection; -import java.util.HashSet; -import java.util.Locale; -import java.util.Set; - -import network.loki.messenger.R; - -class MediaGalleryAdapter extends StickyHeaderGridAdapter { - - @SuppressWarnings("unused") - private static final String TAG = MediaGalleryAdapter.class.getSimpleName(); - - private final Context context; - private final GlideRequests glideRequests; - private final Locale locale; - private final ItemClickListener itemClickListener; - private final Set selected; - - private BucketedThreadMedia media; - - private static class ViewHolder extends StickyHeaderGridAdapter.ItemViewHolder { - ThumbnailView imageView; - View selectedIndicator; - - ViewHolder(View v) { - super(v); - imageView = v.findViewById(R.id.image); - selectedIndicator = v.findViewById(R.id.selected_indicator); - } - } - - private static class HeaderHolder extends StickyHeaderGridAdapter.HeaderViewHolder { - TextView textView; - - HeaderHolder(View itemView) { - super(itemView); - textView = itemView.findViewById(R.id.text); - } - } - - MediaGalleryAdapter(@NonNull Context context, - @NonNull GlideRequests glideRequests, - BucketedThreadMedia media, - Locale locale, - ItemClickListener clickListener) - { - this.context = context; - this.glideRequests = glideRequests; - this.locale = locale; - this.media = media; - this.itemClickListener = clickListener; - this.selected = new HashSet<>(); - } - - public void setMedia(BucketedThreadMedia media) { - this.media = media; - } - - @Override - public StickyHeaderGridAdapter.HeaderViewHolder onCreateHeaderViewHolder(ViewGroup parent, int headerType) { - return new HeaderHolder(LayoutInflater.from(context).inflate(R.layout.media_overview_gallery_item_header, parent, false)); - } - - @Override - public ItemViewHolder onCreateItemViewHolder(ViewGroup parent, int itemType) { - return new ViewHolder(LayoutInflater.from(context).inflate(R.layout.media_overview_gallery_item, parent, false)); - } - - @Override - public void onBindHeaderViewHolder(StickyHeaderGridAdapter.HeaderViewHolder viewHolder, int section) { - ((HeaderHolder)viewHolder).textView.setText(media.getName(section, locale)); - } - - @Override - public void onBindItemViewHolder(ItemViewHolder viewHolder, int section, int offset) { - MediaRecord mediaRecord = media.get(section, offset); - ThumbnailView thumbnailView = ((ViewHolder)viewHolder).imageView; - View selectedIndicator = ((ViewHolder)viewHolder).selectedIndicator; - Slide slide = MediaUtil.getSlideForAttachment(context, mediaRecord.getAttachment()); - - if (slide != null) { - thumbnailView.setImageResource(glideRequests, slide, false); - } - - thumbnailView.setOnClickListener(view -> itemClickListener.onMediaClicked(mediaRecord)); - thumbnailView.setOnLongClickListener(view -> { - itemClickListener.onMediaLongClicked(mediaRecord); - return true; - }); - - selectedIndicator.setVisibility(selected.contains(mediaRecord) ? View.VISIBLE : View.GONE); - } - - @Override - public int getSectionCount() { - return media.getSectionCount(); - } - - @Override - public int getSectionItemCount(int section) { - return media.getSectionItemCount(section); - } - - public void toggleSelection(@NonNull MediaRecord mediaRecord) { - if (!selected.remove(mediaRecord)) { - selected.add(mediaRecord); - } - notifyDataSetChanged(); - } - - public int getSelectedMediaCount() { - return selected.size(); - } - - @NonNull - public Collection getSelectedMedia() { - return new HashSet<>(selected); - } - - public void clearSelection() { - selected.clear(); - notifyDataSetChanged(); - } - - void selectAllMedia() { - for (int section = 0; section < media.getSectionCount(); section++) { - for (int item = 0; item < media.getSectionItemCount(section); item++) { - selected.add(media.get(section, item)); - } - } - this.notifyDataSetChanged(); - } - - interface ItemClickListener { - void onMediaClicked(@NonNull MediaRecord mediaRecord); - void onMediaLongClicked(MediaRecord mediaRecord); - } -} diff --git a/app/src/main/java/org/thoughtcrime/securesms/MediaOverviewActivity.java b/app/src/main/java/org/thoughtcrime/securesms/MediaOverviewActivity.java deleted file mode 100644 index 95ba15c82e8..00000000000 --- a/app/src/main/java/org/thoughtcrime/securesms/MediaOverviewActivity.java +++ /dev/null @@ -1,504 +0,0 @@ -/* - * Copyright (C) 2015 Open Whisper Systems - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package org.thoughtcrime.securesms; - -import android.annotation.SuppressLint; -import android.content.Context; -import android.content.Intent; -import android.content.res.Configuration; -import android.content.res.Resources; -import android.database.Cursor; -import android.os.Build; -import android.os.Bundle; -import android.view.LayoutInflater; -import android.view.Menu; -import android.view.MenuItem; -import android.view.View; -import android.view.ViewGroup; -import android.view.Window; -import android.widget.TextView; -import android.widget.Toast; - -import androidx.annotation.NonNull; -import androidx.appcompat.app.ActionBar; -import androidx.appcompat.app.AlertDialog; -import androidx.appcompat.app.AppCompatActivity; -import androidx.appcompat.view.ActionMode; -import androidx.appcompat.widget.Toolbar; -import androidx.fragment.app.Fragment; -import androidx.fragment.app.FragmentManager; -import androidx.fragment.app.FragmentStatePagerAdapter; -import androidx.loader.app.LoaderManager; -import androidx.loader.content.Loader; -import androidx.recyclerview.widget.DividerItemDecoration; -import androidx.recyclerview.widget.LinearLayoutManager; -import androidx.recyclerview.widget.RecyclerView; -import androidx.viewpager.widget.ViewPager; - -import com.codewaves.stickyheadergrid.StickyHeaderGridLayoutManager; -import com.google.android.material.tabs.TabLayout; - -import org.session.libsession.messaging.messages.control.DataExtractionNotification; -import org.session.libsession.messaging.sending_receiving.MessageSender; -import org.session.libsession.snode.SnodeAPI; -import org.session.libsession.utilities.Address; -import org.thoughtcrime.securesms.database.CursorRecyclerViewAdapter; -import org.thoughtcrime.securesms.database.MediaDatabase; -import org.thoughtcrime.securesms.database.loaders.BucketedThreadMediaLoader; -import org.thoughtcrime.securesms.database.loaders.BucketedThreadMediaLoader.BucketedThreadMedia; -import org.thoughtcrime.securesms.database.loaders.ThreadMediaLoader; -import org.thoughtcrime.securesms.mms.GlideApp; -import org.thoughtcrime.securesms.permissions.Permissions; -import org.session.libsession.utilities.recipients.Recipient; -import org.thoughtcrime.securesms.util.AttachmentUtil; -import org.thoughtcrime.securesms.util.SaveAttachmentTask; -import org.thoughtcrime.securesms.util.StickyHeaderDecoration; -import org.session.libsession.utilities.Util; -import org.session.libsession.utilities.ViewUtil; -import org.session.libsession.utilities.task.ProgressDialogAsyncTask; - -import java.util.Collection; -import java.util.LinkedList; -import java.util.List; -import java.util.Locale; - -import kotlin.Unit; -import network.loki.messenger.R; - -/** - * Activity for displaying media attachments in-app - */ -public class MediaOverviewActivity extends PassphraseRequiredActionBarActivity { - - @SuppressWarnings("unused") - private final static String TAG = MediaOverviewActivity.class.getSimpleName(); - - public static final String ADDRESS_EXTRA = "address"; - - private Toolbar toolbar; - private TabLayout tabLayout; - private ViewPager viewPager; - private Recipient recipient; - - @Override - protected void onCreate(Bundle bundle, boolean ready) { - setContentView(R.layout.media_overview_activity); - - initializeResources(); - initializeToolbar(); - - this.tabLayout.setupWithViewPager(viewPager); - this.viewPager.setAdapter(new MediaOverviewPagerAdapter(getSupportFragmentManager())); - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - super.onOptionsItemSelected(item); - - switch (item.getItemId()) { - case android.R.id.home: finish(); return true; - } - - return false; - } - - private void initializeResources() { - Address address = getIntent().getParcelableExtra(ADDRESS_EXTRA); - - this.viewPager = ViewUtil.findById(this, R.id.pager); - this.toolbar = ViewUtil.findById(this, R.id.toolbar); - this.tabLayout = ViewUtil.findById(this, R.id.tab_layout); - this.recipient = Recipient.from(this, address, true); - } - - private void initializeToolbar() { - setSupportActionBar(this.toolbar); - ActionBar actionBar = getSupportActionBar(); - actionBar.setTitle(recipient.toShortString()); - actionBar.setDisplayHomeAsUpEnabled(true); - actionBar.setHomeButtonEnabled(true); - this.recipient.addListener(recipient -> { - Util.runOnMain(() -> actionBar.setTitle(recipient.toShortString())); - }); - } - - public void onEnterMultiSelect() { - tabLayout.setEnabled(false); - viewPager.setEnabled(false); - } - - public void onExitMultiSelect() { - tabLayout.setEnabled(true); - viewPager.setEnabled(true); - } - - private class MediaOverviewPagerAdapter extends FragmentStatePagerAdapter { - - MediaOverviewPagerAdapter(FragmentManager fragmentManager) { - super(fragmentManager); - } - - @Override - public Fragment getItem(int position) { - Fragment fragment; - - if (position == 0) fragment = new MediaOverviewGalleryFragment(); - else if (position == 1) fragment = new MediaOverviewDocumentsFragment(); - else throw new AssertionError(); - - Bundle args = new Bundle(); - args.putString(MediaOverviewGalleryFragment.ADDRESS_EXTRA, recipient.getAddress().serialize()); - args.putSerializable(MediaOverviewGalleryFragment.LOCALE_EXTRA, Locale.getDefault()); - - fragment.setArguments(args); - - return fragment; - } - - @Override - public int getCount() { - return 2; - } - - @Override - public CharSequence getPageTitle(int position) { - if (position == 0) return getString(R.string.MediaOverviewActivity_Media); - else if (position == 1) return getString(R.string.MediaOverviewActivity_Documents); - else throw new AssertionError(); - } - } - - public static abstract class MediaOverviewFragment extends Fragment implements LoaderManager.LoaderCallbacks { - - public static final String ADDRESS_EXTRA = "address"; - public static final String LOCALE_EXTRA = "locale_extra"; - - protected TextView noMedia; - protected Recipient recipient; - protected RecyclerView recyclerView; - protected Locale locale; - - @Override - public void onCreate(Bundle bundle) { - super.onCreate(bundle); - - String address = getArguments().getString(ADDRESS_EXTRA); - Locale locale = (Locale)getArguments().getSerializable(LOCALE_EXTRA); - - if (address == null) throw new AssertionError(); - if (locale == null) throw new AssertionError(); - - this.recipient = Recipient.from(getContext(), Address.fromSerialized(address), true); - this.locale = locale; - - getLoaderManager().initLoader(0, null, this); - } - } - - public static class MediaOverviewGalleryFragment - extends MediaOverviewFragment - implements MediaGalleryAdapter.ItemClickListener - { - - private StickyHeaderGridLayoutManager gridManager; - private ActionMode actionMode; - private ActionModeCallback actionModeCallback = new ActionModeCallback(); - - @Override - public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - View view = inflater.inflate(R.layout.media_overview_gallery_fragment, container, false); - - this.recyclerView = ViewUtil.findById(view, R.id.media_grid); - this.noMedia = ViewUtil.findById(view, R.id.no_images); - this.gridManager = new StickyHeaderGridLayoutManager(getResources().getInteger(R.integer.media_overview_cols)); - - this.recyclerView.setAdapter(new MediaGalleryAdapter(getContext(), - GlideApp.with(this), - new BucketedThreadMedia(getContext()), - locale, - this)); - this.recyclerView.setLayoutManager(gridManager); - this.recyclerView.setHasFixedSize(true); - - return view; - } - - @Override - public void onConfigurationChanged(Configuration newConfig) { - super.onConfigurationChanged(newConfig); - if (gridManager != null) { - this.gridManager = new StickyHeaderGridLayoutManager(getResources().getInteger(R.integer.media_overview_cols)); - this.recyclerView.setLayoutManager(gridManager); - } - } - - @Override - public @NonNull Loader onCreateLoader(int i, Bundle bundle) { - return new BucketedThreadMediaLoader(getContext(), recipient.getAddress()); - } - - @Override - public void onLoadFinished(@NonNull Loader loader, BucketedThreadMedia bucketedThreadMedia) { - ((MediaGalleryAdapter) recyclerView.getAdapter()).setMedia(bucketedThreadMedia); - ((MediaGalleryAdapter) recyclerView.getAdapter()).notifyAllSectionsDataSetChanged(); - - noMedia.setVisibility(recyclerView.getAdapter().getItemCount() > 0 ? View.GONE : View.VISIBLE); - getActivity().invalidateOptionsMenu(); - } - - @Override - public void onLoaderReset(@NonNull Loader cursorLoader) { - ((MediaGalleryAdapter) recyclerView.getAdapter()).setMedia(new BucketedThreadMedia(getContext())); - } - - @Override - public void onMediaClicked(@NonNull MediaDatabase.MediaRecord mediaRecord) { - if (actionMode != null) { - handleMediaMultiSelectClick(mediaRecord); - } else { - handleMediaPreviewClick(mediaRecord); - } - } - - private void handleMediaMultiSelectClick(@NonNull MediaDatabase.MediaRecord mediaRecord) { - MediaGalleryAdapter adapter = getListAdapter(); - - adapter.toggleSelection(mediaRecord); - if (adapter.getSelectedMediaCount() == 0) { - actionMode.finish(); - } else { - actionMode.setTitle(String.valueOf(adapter.getSelectedMediaCount())); - } - } - - private void handleMediaPreviewClick(@NonNull MediaDatabase.MediaRecord mediaRecord) { - if (mediaRecord.getAttachment().getDataUri() == null) { - return; - } - - Context context = getContext(); - if (context == null) { - return; - } - - Intent intent = new Intent(context, MediaPreviewActivity.class); - intent.putExtra(MediaPreviewActivity.DATE_EXTRA, mediaRecord.getDate()); - intent.putExtra(MediaPreviewActivity.SIZE_EXTRA, mediaRecord.getAttachment().getSize()); - intent.putExtra(MediaPreviewActivity.ADDRESS_EXTRA, recipient.getAddress()); - intent.putExtra(MediaPreviewActivity.OUTGOING_EXTRA, mediaRecord.isOutgoing()); - intent.putExtra(MediaPreviewActivity.LEFT_IS_RECENT_EXTRA, true); - - intent.setDataAndType(mediaRecord.getAttachment().getDataUri(), mediaRecord.getContentType()); - context.startActivity(intent); - } - - @Override - public void onMediaLongClicked(MediaDatabase.MediaRecord mediaRecord) { - if (actionMode == null) { - ((MediaGalleryAdapter) recyclerView.getAdapter()).toggleSelection(mediaRecord); - recyclerView.getAdapter().notifyDataSetChanged(); - - enterMultiSelect(); - } - } - - @SuppressWarnings("CodeBlock2Expr") - @SuppressLint({"InlinedApi", "StaticFieldLeak"}) - private void handleSaveMedia(@NonNull Collection mediaRecords) { - final Context context = requireContext(); - - SaveAttachmentTask.showWarningDialog(context, mediaRecords.size(), () -> { - Permissions.with(this) - .request(android.Manifest.permission.WRITE_EXTERNAL_STORAGE) - .maxSdkVersion(Build.VERSION_CODES.P) - .withPermanentDenialDialog(getString(R.string.MediaPreviewActivity_signal_needs_the_storage_permission_in_order_to_write_to_external_storage_but_it_has_been_permanently_denied)) - .onAnyDenied(() -> Toast.makeText(getContext(), R.string.MediaPreviewActivity_unable_to_write_to_external_storage_without_permission, Toast.LENGTH_LONG).show()) - .onAllGranted(() -> { - new ProgressDialogAsyncTask>( - context, - R.string.MediaOverviewActivity_collecting_attachments, - R.string.please_wait) { - @Override - protected List doInBackground(Void... params) { - List attachments = new LinkedList<>(); - - for (MediaDatabase.MediaRecord mediaRecord : mediaRecords) { - if (mediaRecord.getAttachment().getDataUri() != null) { - attachments.add(new SaveAttachmentTask.Attachment(mediaRecord.getAttachment().getDataUri(), - mediaRecord.getContentType(), - mediaRecord.getDate(), - mediaRecord.getAttachment().getFileName())); - } - } - - return attachments; - } - - @Override - protected void onPostExecute(List attachments) { - super.onPostExecute(attachments); - SaveAttachmentTask saveTask = new SaveAttachmentTask(context, attachments.size()); - saveTask.executeOnExecutor(THREAD_POOL_EXECUTOR, - attachments.toArray(new SaveAttachmentTask.Attachment[attachments.size()])); - actionMode.finish(); - boolean containsIncoming = mediaRecords.parallelStream().anyMatch(m -> !m.isOutgoing()); - if (containsIncoming) { - sendMediaSavedNotificationIfNeeded(); - } - } - }.execute(); - }) - .execute(); - return Unit.INSTANCE; - }); - } - - private void sendMediaSavedNotificationIfNeeded() { - if (recipient.isGroupRecipient()) return; - DataExtractionNotification message = new DataExtractionNotification(new DataExtractionNotification.Kind.MediaSaved(SnodeAPI.getNowWithOffset())); - MessageSender.send(message, recipient.getAddress()); - } - - @SuppressLint("StaticFieldLeak") - private void handleDeleteMedia(@NonNull Collection mediaRecords) { - int recordCount = mediaRecords.size(); - - DeleteMediaDialog.show( - requireContext(), - recordCount, - () -> new ProgressDialogAsyncTask( - requireContext(), - R.string.MediaOverviewActivity_Media_delete_progress_title, - R.string.MediaOverviewActivity_Media_delete_progress_message) { - @Override - protected Void doInBackground(MediaDatabase.MediaRecord... records) { - if (records == null || records.length == 0) { - return null; - } - - for (MediaDatabase.MediaRecord record : records) { - AttachmentUtil.deleteAttachment(getContext(), record.getAttachment()); - } - return null; - } - }.execute(mediaRecords.toArray(new MediaDatabase.MediaRecord[mediaRecords.size()]))); - } - - private void handleSelectAllMedia() { - getListAdapter().selectAllMedia(); - actionMode.setTitle(String.valueOf(getListAdapter().getSelectedMediaCount())); - } - - private MediaGalleryAdapter getListAdapter() { - return (MediaGalleryAdapter) recyclerView.getAdapter(); - } - - private void enterMultiSelect() { - actionMode = ((AppCompatActivity) getActivity()).startSupportActionMode(actionModeCallback); - ((MediaOverviewActivity) getActivity()).onEnterMultiSelect(); - } - - private class ActionModeCallback implements ActionMode.Callback { - - private int originalStatusBarColor; - - @Override - public boolean onCreateActionMode(ActionMode mode, Menu menu) { - mode.getMenuInflater().inflate(R.menu.media_overview_context, menu); - mode.setTitle("1"); - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - Window window = getActivity().getWindow(); - originalStatusBarColor = window.getStatusBarColor(); - window.setStatusBarColor(getResources().getColor(R.color.action_mode_status_bar)); - } - return true; - } - - @Override - public boolean onPrepareActionMode(ActionMode mode, Menu menu) { - return false; - } - - @Override - public boolean onActionItemClicked(ActionMode mode, MenuItem menuItem) { - switch (menuItem.getItemId()) { - case R.id.save: - handleSaveMedia(getListAdapter().getSelectedMedia()); - return true; - case R.id.delete: - handleDeleteMedia(getListAdapter().getSelectedMedia()); - actionMode.finish(); - return true; - case R.id.select_all: - handleSelectAllMedia(); - return true; - } - return false; - } - - @Override - public void onDestroyActionMode(ActionMode mode) { - actionMode = null; - getListAdapter().clearSelection(); - ((MediaOverviewActivity) getActivity()).onExitMultiSelect(); - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - getActivity().getWindow().setStatusBarColor(originalStatusBarColor); - } - } - } - } - - public static class MediaOverviewDocumentsFragment extends MediaOverviewFragment { - - @Override - public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - View view = inflater.inflate(R.layout.media_overview_documents_fragment, container, false); - MediaDocumentsAdapter adapter = new MediaDocumentsAdapter(getContext(), null, locale); - - this.recyclerView = ViewUtil.findById(view, R.id.recycler_view); - this.noMedia = ViewUtil.findById(view, R.id.no_documents); - - this.recyclerView.setAdapter(adapter); - this.recyclerView.setLayoutManager(new LinearLayoutManager(getContext(), LinearLayoutManager.VERTICAL, false)); - this.recyclerView.addItemDecoration(new StickyHeaderDecoration(adapter, false, true)); - this.recyclerView.addItemDecoration(new DividerItemDecoration(getContext(), DividerItemDecoration.VERTICAL)); - - return view; - } - - @Override - public @NonNull Loader onCreateLoader(int id, Bundle args) { - return new ThreadMediaLoader(getContext(), recipient.getAddress(), false); - } - - @Override - public void onLoadFinished(@NonNull Loader loader, Cursor data) { - ((CursorRecyclerViewAdapter)this.recyclerView.getAdapter()).changeCursor(data); - getActivity().invalidateOptionsMenu(); - - this.noMedia.setVisibility(data.getCount() > 0 ? View.GONE : View.VISIBLE); - } - - @Override - public void onLoaderReset(@NonNull Loader loader) { - ((CursorRecyclerViewAdapter)this.recyclerView.getAdapter()).changeCursor(null); - getActivity().invalidateOptionsMenu(); - } - } -} diff --git a/app/src/main/java/org/thoughtcrime/securesms/MediaPreviewActivity.java b/app/src/main/java/org/thoughtcrime/securesms/MediaPreviewActivity.java index 2e67becbfd1..c71f5d041cf 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/MediaPreviewActivity.java +++ b/app/src/main/java/org/thoughtcrime/securesms/MediaPreviewActivity.java @@ -17,7 +17,6 @@ package org.thoughtcrime.securesms; import android.annotation.SuppressLint; -import android.annotation.TargetApi; import android.content.Context; import android.content.Intent; import android.database.Cursor; @@ -26,7 +25,6 @@ import android.os.AsyncTask; import android.os.Build; import android.os.Build.VERSION; -import android.os.Build.VERSION_CODES; import android.os.Bundle; import android.os.Handler; import android.os.Looper; @@ -57,6 +55,9 @@ import androidx.viewpager.widget.PagerAdapter; import androidx.viewpager.widget.ViewPager; +import com.bumptech.glide.Glide; +import com.bumptech.glide.RequestManager; + import org.session.libsession.messaging.messages.control.DataExtractionNotification; import org.session.libsession.messaging.sending_receiving.MessageSender; import org.session.libsession.messaging.sending_receiving.attachments.DatabaseAttachment; @@ -70,10 +71,9 @@ import org.thoughtcrime.securesms.database.MediaDatabase.MediaRecord; import org.thoughtcrime.securesms.database.loaders.PagingMediaLoader; import org.thoughtcrime.securesms.database.model.MmsMessageRecord; +import org.thoughtcrime.securesms.media.MediaOverviewActivity; import org.thoughtcrime.securesms.mediapreview.MediaPreviewViewModel; import org.thoughtcrime.securesms.mediapreview.MediaRailAdapter; -import org.thoughtcrime.securesms.mms.GlideApp; -import org.thoughtcrime.securesms.mms.GlideRequests; import org.thoughtcrime.securesms.mms.Slide; import org.thoughtcrime.securesms.permissions.Permissions; import org.thoughtcrime.securesms.util.AttachmentUtil; @@ -281,7 +281,7 @@ private void initializeViews() { mediaPager.setOffscreenPageLimit(1); albumRail = findViewById(R.id.media_preview_album_rail); - albumRailAdapter = new MediaRailAdapter(GlideApp.with(this), this, false); + albumRailAdapter = new MediaRailAdapter(Glide.with(this), this, false); albumRail.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false)); albumRail.setAdapter(albumRailAdapter); @@ -370,7 +370,7 @@ private void initializeMedia() { if (conversationRecipient != null) { getSupportLoaderManager().restartLoader(0, null, this); } else { - adapter = new SingleItemPagerAdapter(this, GlideApp.with(this), getWindow(), initialMediaUri, initialMediaType, initialMediaSize); + adapter = new SingleItemPagerAdapter(this, Glide.with(this), getWindow(), initialMediaUri, initialMediaType, initialMediaSize); mediaPager.setAdapter(adapter); if (initialCaption != null) { @@ -391,9 +391,7 @@ private int cleanupMedia() { } private void showOverview() { - Intent intent = new Intent(this, MediaOverviewActivity.class); - intent.putExtra(MediaOverviewActivity.ADDRESS_EXTRA, conversationRecipient.getAddress()); - startActivity(intent); + startActivity(MediaOverviewActivity.createIntent(this, conversationRecipient.getAddress())); } private void forward() { @@ -518,7 +516,7 @@ public void onLoadFinished(@NonNull Loader> loader, @Nulla mediaPager.removeOnPageChangeListener(viewPagerListener); - adapter = new CursorPagerAdapter(this, GlideApp.with(this), getWindow(), data.first, data.second, leftIsRecent); + adapter = new CursorPagerAdapter(this, Glide.with(this), getWindow(), data.first, data.second, leftIsRecent); mediaPager.setAdapter(adapter); viewModel.setCursor(this, data.first, leftIsRecent); @@ -588,7 +586,7 @@ public void onPageScrollStateChanged(int state) { private static class SingleItemPagerAdapter extends MediaItemAdapter { - private final GlideRequests glideRequests; + private final RequestManager glideRequests; private final Window window; private final Uri uri; private final String mediaType; @@ -596,7 +594,7 @@ private static class SingleItemPagerAdapter extends MediaItemAdapter { private final LayoutInflater inflater; - SingleItemPagerAdapter(@NonNull Context context, @NonNull GlideRequests glideRequests, + SingleItemPagerAdapter(@NonNull Context context, @NonNull RequestManager glideRequests, @NonNull Window window, @NonNull Uri uri, @NonNull String mediaType, long size) { @@ -663,14 +661,14 @@ private static class CursorPagerAdapter extends MediaItemAdapter { private final WeakHashMap mediaViews = new WeakHashMap<>(); private final Context context; - private final GlideRequests glideRequests; + private final RequestManager glideRequests; private final Window window; private final Cursor cursor; private final boolean leftIsRecent; private int autoPlayPosition; - CursorPagerAdapter(@NonNull Context context, @NonNull GlideRequests glideRequests, + CursorPagerAdapter(@NonNull Context context, @NonNull RequestManager glideRequests, @NonNull Window window, @NonNull Cursor cursor, int autoPlayPosition, boolean leftIsRecent) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/PassphraseRequiredActionBarActivity.java b/app/src/main/java/org/thoughtcrime/securesms/PassphraseRequiredActionBarActivity.java index dbe7c4a4330..1c9f4b2e57f 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/PassphraseRequiredActionBarActivity.java +++ b/app/src/main/java/org/thoughtcrime/securesms/PassphraseRequiredActionBarActivity.java @@ -34,7 +34,7 @@ public abstract class PassphraseRequiredActionBarActivity extends BaseActionBarA private BroadcastReceiver clearKeyReceiver; @Override - protected final void onCreate(Bundle savedInstanceState) { + protected void onCreate(Bundle savedInstanceState) { Log.i(TAG, "onCreate(" + savedInstanceState + ")"); onPreCreate(); diff --git a/app/src/main/java/org/thoughtcrime/securesms/audio/AudioCodec.java b/app/src/main/java/org/thoughtcrime/securesms/audio/AudioCodec.java index 699e9ba9730..64c7ac3df25 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/audio/AudioCodec.java +++ b/app/src/main/java/org/thoughtcrime/securesms/audio/AudioCodec.java @@ -1,22 +1,19 @@ package org.thoughtcrime.securesms.audio; -import android.annotation.TargetApi; import android.media.AudioFormat; import android.media.AudioRecord; import android.media.MediaCodec; import android.media.MediaCodecInfo; import android.media.MediaFormat; import android.media.MediaRecorder; -import android.os.Build; -import org.session.libsignal.utilities.Log; import org.session.libsession.utilities.Util; +import org.session.libsignal.utilities.Log; import java.io.IOException; import java.io.OutputStream; import java.nio.ByteBuffer; -@TargetApi(Build.VERSION_CODES.JELLY_BEAN) public class AudioCodec { private static final String TAG = AudioCodec.class.getSimpleName(); diff --git a/app/src/main/java/org/thoughtcrime/securesms/audio/AudioSlidePlayer.java b/app/src/main/java/org/thoughtcrime/securesms/audio/AudioSlidePlayer.java index 3aa159bc1e6..fc88656469f 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/audio/AudioSlidePlayer.java +++ b/app/src/main/java/org/thoughtcrime/securesms/audio/AudioSlidePlayer.java @@ -46,7 +46,7 @@ public class AudioSlidePlayer implements SensorEventListener { private final @NonNull Handler progressEventHandler; private final @NonNull AudioManager audioManager; private final @NonNull SensorManager sensorManager; - private final @NonNull Sensor proximitySensor; + private final Sensor proximitySensor; private final @Nullable WakeLock wakeLock; private @NonNull WeakReference listener; @@ -132,7 +132,9 @@ public void onPlaybackStateChanged(int playbackState) { mediaPlayer.seekTo((long) (mediaPlayer.getDuration() * progress)); } - sensorManager.registerListener(AudioSlidePlayer.this, proximitySensor, SensorManager.SENSOR_DELAY_NORMAL); + if(proximitySensor != null) { + sensorManager.registerListener(AudioSlidePlayer.this, proximitySensor, SensorManager.SENSOR_DELAY_NORMAL); + } setPlaying(AudioSlidePlayer.this); } diff --git a/app/src/main/java/org/thoughtcrime/securesms/avatar/AvatarSelection.kt b/app/src/main/java/org/thoughtcrime/securesms/avatar/AvatarSelection.kt index adf4b0927c1..ddf7ac5c77b 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/avatar/AvatarSelection.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/avatar/AvatarSelection.kt @@ -18,7 +18,6 @@ import org.session.libsignal.utilities.ExternalStorageUtil.getImageDir import org.session.libsignal.utilities.Log import org.session.libsignal.utilities.NoExternalStorageException import org.thoughtcrime.securesms.util.FileProviderUtil -import org.thoughtcrime.securesms.util.IntentUtils import java.io.File import java.io.IOException import java.util.LinkedList @@ -104,13 +103,8 @@ class AvatarSelection( includeClear: Boolean ): Intent { val extraIntents: MutableList = LinkedList() - var galleryIntent = Intent(Intent.ACTION_PICK) - galleryIntent.setDataAndType(MediaStore.Images.Media.INTERNAL_CONTENT_URI, "image/*") - - if (!IntentUtils.isResolvable(context, galleryIntent)) { - galleryIntent = Intent(Intent.ACTION_GET_CONTENT) - galleryIntent.setType("image/*") - } + val galleryIntent = Intent(Intent.ACTION_OPEN_DOCUMENT) + galleryIntent.setType("image/*") if (tempCaptureFile != null) { val uri = FileProviderUtil.getUriFor(context, tempCaptureFile) diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/AttachmentTypeSelector.java b/app/src/main/java/org/thoughtcrime/securesms/components/AttachmentTypeSelector.java index e56651db254..1c6a4097f58 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/AttachmentTypeSelector.java +++ b/app/src/main/java/org/thoughtcrime/securesms/components/AttachmentTypeSelector.java @@ -2,15 +2,10 @@ import android.Manifest; import android.animation.Animator; -import android.annotation.TargetApi; import android.app.Activity; import android.content.Context; import android.graphics.drawable.BitmapDrawable; import android.net.Uri; -import android.os.Build; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.loader.app.LoaderManager; import android.util.Pair; import android.view.Gravity; import android.view.LayoutInflater; @@ -26,8 +21,12 @@ import android.widget.LinearLayout; import android.widget.PopupWindow; -import org.thoughtcrime.securesms.permissions.Permissions; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.loader.app.LoaderManager; + import org.session.libsession.utilities.ViewUtil; +import org.thoughtcrime.securesms.permissions.Permissions; import network.loki.messenger.R; @@ -126,25 +125,19 @@ public void show(@NonNull Activity activity, final @NonNull View anchor) { public void onGlobalLayout() { getContentView().getViewTreeObserver().removeGlobalOnLayoutListener(this); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - animateWindowInCircular(anchor, getContentView()); - } else { - animateWindowInTranslate(getContentView()); - } + animateWindowInCircular(anchor, getContentView()); } }); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - animateButtonIn(imageButton, ANIMATION_DURATION / 2); - animateButtonIn(cameraButton, ANIMATION_DURATION / 2); + animateButtonIn(imageButton, ANIMATION_DURATION / 2); + animateButtonIn(cameraButton, ANIMATION_DURATION / 2); - animateButtonIn(audioButton, ANIMATION_DURATION / 3); - animateButtonIn(locationButton, ANIMATION_DURATION / 3); - animateButtonIn(documentButton, ANIMATION_DURATION / 4); - animateButtonIn(gifButton, ANIMATION_DURATION / 4); - animateButtonIn(contactButton, 0); - animateButtonIn(closeButton, 0); - } + animateButtonIn(audioButton, ANIMATION_DURATION / 3); + animateButtonIn(locationButton, ANIMATION_DURATION / 3); + animateButtonIn(documentButton, ANIMATION_DURATION / 4); + animateButtonIn(gifButton, ANIMATION_DURATION / 4); + animateButtonIn(contactButton, 0); + animateButtonIn(closeButton, 0); } private void updateHeight() { @@ -159,11 +152,7 @@ private void updateHeight() { @Override public void dismiss() { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - animateWindowOutCircular(currentAnchor, getContentView()); - } else { - animateWindowOutTranslate(getContentView()); - } + animateWindowOutCircular(currentAnchor, getContentView()); } public void setListener(@Nullable AttachmentClickedListener listener) { @@ -182,7 +171,6 @@ private void animateButtonIn(View button, int delay) { button.startAnimation(animation); } - @TargetApi(Build.VERSION_CODES.LOLLIPOP) private void animateWindowInCircular(@Nullable View anchor, @NonNull View contentView) { Pair coordinates = getClickOrigin(anchor, contentView); Animator animator = ViewAnimationUtils.createCircularReveal(contentView, @@ -201,7 +189,6 @@ private void animateWindowInTranslate(@NonNull View contentView) { getContentView().startAnimation(animation); } - @TargetApi(Build.VERSION_CODES.LOLLIPOP) private void animateWindowOutCircular(@Nullable View anchor, @NonNull View contentView) { Pair coordinates = getClickOrigin(anchor, contentView); Animator animator = ViewAnimationUtils.createCircularReveal(getContentView(), diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/ComposeText.java b/app/src/main/java/org/thoughtcrime/securesms/components/ComposeText.java index 42825360c08..2365bc843b9 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/ComposeText.java +++ b/app/src/main/java/org/thoughtcrime/securesms/components/ComposeText.java @@ -1,7 +1,6 @@ package org.thoughtcrime.securesms.components; import android.content.Context; -import android.os.Build; import android.os.Bundle; import android.text.InputType; import android.text.Spannable; @@ -16,15 +15,14 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import androidx.annotation.RequiresApi; import androidx.core.os.BuildCompat; import androidx.core.view.inputmethod.EditorInfoCompat; import androidx.core.view.inputmethod.InputConnectionCompat; import androidx.core.view.inputmethod.InputContentInfoCompat; -import org.thoughtcrime.securesms.components.emoji.EmojiEditText; -import org.session.libsignal.utilities.Log; import org.session.libsession.utilities.TextSecurePreferences; +import org.session.libsignal.utilities.Log; +import org.thoughtcrime.securesms.components.emoji.EmojiEditText; public class ComposeText extends EmojiEditText { @@ -136,7 +134,6 @@ public InputConnection onCreateInputConnection(EditorInfo editorInfo) { editorInfo.imeOptions &= ~EditorInfo.IME_FLAG_NO_ENTER_ACTION; } - if (Build.VERSION.SDK_INT < 21) return inputConnection; if (mediaListener == null) return inputConnection; if (inputConnection == null) return null; @@ -154,7 +151,6 @@ private void initialize() { } } - @RequiresApi(api = Build.VERSION_CODES.HONEYCOMB_MR2) private static class CommitContentListener implements InputConnectionCompat.OnCommitContentListener { private static final String TAG = CommitContentListener.class.getSimpleName(); diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/ConversationItemAlertView.java b/app/src/main/java/org/thoughtcrime/securesms/components/ConversationItemAlertView.java index 88bd1718a11..2a8de38d334 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/ConversationItemAlertView.java +++ b/app/src/main/java/org/thoughtcrime/securesms/components/ConversationItemAlertView.java @@ -1,9 +1,7 @@ package org.thoughtcrime.securesms.components; -import android.annotation.TargetApi; import android.content.Context; import android.content.res.TypedArray; -import android.os.Build.VERSION_CODES; import android.util.AttributeSet; import android.view.View; import android.widget.ImageView; @@ -27,7 +25,6 @@ public ConversationItemAlertView(Context context, AttributeSet attrs) { initialize(attrs); } - @TargetApi(VERSION_CODES.HONEYCOMB) public ConversationItemAlertView(final Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); initialize(attrs); diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/HidingLinearLayout.java b/app/src/main/java/org/thoughtcrime/securesms/components/HidingLinearLayout.java index 980735aa485..bdb7c2fdf0d 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/HidingLinearLayout.java +++ b/app/src/main/java/org/thoughtcrime/securesms/components/HidingLinearLayout.java @@ -1,9 +1,6 @@ package org.thoughtcrime.securesms.components; -import android.annotation.TargetApi; import android.content.Context; -import android.os.Build; -import androidx.interpolator.view.animation.FastOutSlowInInterpolator; import android.util.AttributeSet; import android.view.animation.AlphaAnimation; import android.view.animation.Animation; @@ -11,6 +8,8 @@ import android.view.animation.ScaleAnimation; import android.widget.LinearLayout; +import androidx.interpolator.view.animation.FastOutSlowInInterpolator; + public class HidingLinearLayout extends LinearLayout { public HidingLinearLayout(Context context) { @@ -21,7 +20,6 @@ public HidingLinearLayout(Context context, AttributeSet attrs) { super(context, attrs); } - @TargetApi(Build.VERSION_CODES.HONEYCOMB) public HidingLinearLayout(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/InputPanel.java b/app/src/main/java/org/thoughtcrime/securesms/components/InputPanel.java index e81757026cf..ad375c09924 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/InputPanel.java +++ b/app/src/main/java/org/thoughtcrime/securesms/components/InputPanel.java @@ -1,13 +1,12 @@ package org.thoughtcrime.securesms.components; -import android.annotation.TargetApi; import android.content.Context; import android.net.Uri; -import android.os.Build; -import androidx.annotation.NonNull; import android.util.AttributeSet; import android.widget.LinearLayout; +import androidx.annotation.NonNull; + public class InputPanel extends LinearLayout { public InputPanel(Context context) { @@ -18,7 +17,6 @@ public InputPanel(Context context, AttributeSet attrs) { super(context, attrs); } - @TargetApi(Build.VERSION_CODES.HONEYCOMB) public InputPanel(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/KeyboardAwareLinearLayout.java b/app/src/main/java/org/thoughtcrime/securesms/components/KeyboardAwareLinearLayout.java index 338b8017702..bebc12a7e5c 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/KeyboardAwareLinearLayout.java +++ b/app/src/main/java/org/thoughtcrime/securesms/components/KeyboardAwareLinearLayout.java @@ -16,26 +16,25 @@ */ package org.thoughtcrime.securesms.components; -import android.annotation.TargetApi; import android.content.Context; import android.graphics.Rect; -import android.os.Build; -import android.os.Build.VERSION_CODES; import android.preference.PreferenceManager; -import androidx.appcompat.widget.LinearLayoutCompat; import android.util.AttributeSet; -import org.session.libsignal.utilities.Log; import android.view.Surface; import android.view.View; -import network.loki.messenger.R; +import androidx.appcompat.widget.LinearLayoutCompat; + import org.session.libsession.utilities.ServiceUtil; import org.session.libsession.utilities.Util; +import org.session.libsignal.utilities.Log; import java.lang.reflect.Field; import java.util.HashSet; import java.util.Set; +import network.loki.messenger.R; + /** * LinearLayout that, when a view container, will report back when it thinks a soft keyboard * has been opened and what its height would be. @@ -95,7 +94,7 @@ private void updateRotation() { } private void updateKeyboardState() { - if (viewInset == 0 && Build.VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) viewInset = getViewInset(); + if (viewInset == 0) viewInset = getViewInset(); getWindowVisibleDisplayFrame(rect); @@ -118,7 +117,6 @@ private void updateKeyboardState() { } } - @TargetApi(VERSION_CODES.LOLLIPOP) private int getViewInset() { try { Field attachInfoField = View.class.getDeclaredField("mAttachInfo"); diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/MediaView.java b/app/src/main/java/org/thoughtcrime/securesms/components/MediaView.java index 14b70a53d62..cdce4b72603 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/MediaView.java +++ b/app/src/main/java/org/thoughtcrime/securesms/components/MediaView.java @@ -3,24 +3,24 @@ import android.content.Context; import android.net.Uri; -import android.os.Build; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.annotation.RequiresApi; import android.util.AttributeSet; import android.view.View; import android.view.Window; import android.widget.FrameLayout; -import network.loki.messenger.R; -import org.thoughtcrime.securesms.mms.GlideRequests; -import org.thoughtcrime.securesms.mms.VideoSlide; -import org.thoughtcrime.securesms.video.VideoPlayer; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import com.bumptech.glide.RequestManager; import org.session.libsession.utilities.Stub; +import org.thoughtcrime.securesms.mms.VideoSlide; +import org.thoughtcrime.securesms.video.VideoPlayer; import java.io.IOException; +import network.loki.messenger.R; + public class MediaView extends FrameLayout { private ZoomingImageView imageView; @@ -41,12 +41,6 @@ public MediaView(@NonNull Context context, @Nullable AttributeSet attrs, int def initialize(); } - @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) - public MediaView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) { - super(context, attrs, defStyleAttr, defStyleRes); - initialize(); - } - private void initialize() { inflate(getContext(), R.layout.media_view, this); @@ -54,7 +48,7 @@ private void initialize() { this.videoView = new Stub<>(findViewById(R.id.video_player_stub)); } - public void set(@NonNull GlideRequests glideRequests, + public void set(@NonNull RequestManager glideRequests, @NonNull Window window, @NonNull Uri source, @NonNull String mediaType, diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/ProfilePictureView.kt b/app/src/main/java/org/thoughtcrime/securesms/components/ProfilePictureView.kt index 5ec9bc096c0..9511bddb6ab 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/ProfilePictureView.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/ProfilePictureView.kt @@ -20,8 +20,8 @@ import org.session.libsession.utilities.GroupUtil import org.session.libsession.utilities.recipients.Recipient import org.session.libsignal.utilities.Log import org.thoughtcrime.securesms.dependencies.DatabaseComponent -import org.thoughtcrime.securesms.mms.GlideApp -import org.thoughtcrime.securesms.mms.GlideRequests +import com.bumptech.glide.Glide +import com.bumptech.glide.RequestManager class ProfilePictureView @JvmOverloads constructor( context: Context, attrs: AttributeSet? = null @@ -29,7 +29,7 @@ class ProfilePictureView @JvmOverloads constructor( private val TAG = "ProfilePictureView" private val binding = ViewProfilePictureBinding.inflate(LayoutInflater.from(context), this) - private val glide: GlideRequests = GlideApp.with(this) + private val glide: RequestManager = Glide.with(this) private val prefs = AppTextSecurePreferences(context) private val userPublicKey = prefs.getLocalNumber() var publicKey: String? = null diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/RecentPhotoViewRail.java b/app/src/main/java/org/thoughtcrime/securesms/components/RecentPhotoViewRail.java index 542b7e8ba21..98bce61010e 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/RecentPhotoViewRail.java +++ b/app/src/main/java/org/thoughtcrime/securesms/components/RecentPhotoViewRail.java @@ -1,12 +1,18 @@ package org.thoughtcrime.securesms.components; -import android.annotation.TargetApi; import android.content.Context; import android.database.Cursor; import android.net.Uri; import android.os.Bundle; import android.provider.MediaStore; +import android.util.AttributeSet; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.FrameLayout; +import android.widget.ImageView; + import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.loader.app.LoaderManager; @@ -14,23 +20,17 @@ import androidx.recyclerview.widget.DefaultItemAnimator; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; -import android.util.AttributeSet; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.FrameLayout; -import android.widget.ImageView; +import com.bumptech.glide.Glide; import com.bumptech.glide.load.Key; import com.bumptech.glide.load.engine.DiskCacheStrategy; import com.bumptech.glide.signature.MediaStoreSignature; -import network.loki.messenger.R; +import org.session.libsession.utilities.ViewUtil; import org.thoughtcrime.securesms.database.CursorRecyclerViewAdapter; import org.thoughtcrime.securesms.database.loaders.RecentPhotosLoader; -import org.thoughtcrime.securesms.mms.GlideApp; -import org.session.libsession.utilities.ViewUtil; +import network.loki.messenger.R; public class RecentPhotoViewRail extends FrameLayout implements LoaderManager.LoaderCallbacks { @@ -118,7 +118,7 @@ public void onBindItemViewHolder(RecentPhotoViewHolder viewHolder, @NonNull Curs Key signature = new MediaStoreSignature(mimeType, dateModified, orientation); - GlideApp.with(getContext().getApplicationContext()) + Glide.with(getContext().getApplicationContext()) .load(uri) .signature(signature) .diskCacheStrategy(DiskCacheStrategy.NONE) @@ -130,14 +130,12 @@ public void onBindItemViewHolder(RecentPhotoViewHolder viewHolder, @NonNull Curs } - @TargetApi(16) @SuppressWarnings("SuspiciousNameCombination") private String getWidthColumn(int orientation) { if (orientation == 0 || orientation == 180) return MediaStore.Images.ImageColumns.WIDTH; else return MediaStore.Images.ImageColumns.HEIGHT; } - @TargetApi(16) @SuppressWarnings("SuspiciousNameCombination") private String getHeightColumn(int orientation) { if (orientation == 0 || orientation == 180) return MediaStore.Images.ImageColumns.HEIGHT; diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/SearchToolbar.java b/app/src/main/java/org/thoughtcrime/securesms/components/SearchToolbar.java index 9032b26a2b7..07d0883bd2d 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/SearchToolbar.java +++ b/app/src/main/java/org/thoughtcrime/securesms/components/SearchToolbar.java @@ -94,15 +94,11 @@ public void display(float x, float y) { searchItem.expandActionView(); - if (Build.VERSION.SDK_INT >= 21) { - Animator animator = ViewAnimationUtils.createCircularReveal(this, (int)x, (int)y, 0, getWidth()); - animator.setDuration(400); - - setVisibility(View.VISIBLE); - animator.start(); - } else { - setVisibility(View.VISIBLE); - } + Animator animator = ViewAnimationUtils.createCircularReveal(this, (int)x, (int)y, 0, getWidth()); + animator.setDuration(400); + + setVisibility(View.VISIBLE); + animator.start(); } } @@ -116,19 +112,15 @@ private void hide() { if (listener != null) listener.onSearchClosed(); - if (Build.VERSION.SDK_INT >= 21) { - Animator animator = ViewAnimationUtils.createCircularReveal(this, (int)x, (int)y, getWidth(), 0); - animator.setDuration(400); - animator.addListener(new AnimationCompleteListener() { - @Override - public void onAnimationEnd(Animator animation) { - setVisibility(View.INVISIBLE); - } - }); - animator.start(); - } else { - setVisibility(View.INVISIBLE); - } + Animator animator = ViewAnimationUtils.createCircularReveal(this, (int)x, (int)y, getWidth(), 0); + animator.setDuration(400); + animator.addListener(new AnimationCompleteListener() { + @Override + public void onAnimationEnd(Animator animation) { + setVisibility(View.INVISIBLE); + } + }); + animator.start(); } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/SquareFrameLayout.java b/app/src/main/java/org/thoughtcrime/securesms/components/SquareFrameLayout.java index 3bf15fb2729..5d369de3b35 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/SquareFrameLayout.java +++ b/app/src/main/java/org/thoughtcrime/securesms/components/SquareFrameLayout.java @@ -1,10 +1,7 @@ package org.thoughtcrime.securesms.components; -import android.annotation.TargetApi; import android.content.Context; import android.content.res.TypedArray; -import android.graphics.BitmapFactory; -import android.os.Build.VERSION_CODES; import android.util.AttributeSet; import android.widget.FrameLayout; @@ -24,7 +21,7 @@ public SquareFrameLayout(Context context, AttributeSet attrs) { this(context, attrs, 0); } - @TargetApi(VERSION_CODES.HONEYCOMB) @SuppressWarnings("unused") + @SuppressWarnings("unused") public SquareFrameLayout(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/StickerView.java b/app/src/main/java/org/thoughtcrime/securesms/components/StickerView.java index 98a623eef32..fd20283ae17 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/StickerView.java +++ b/app/src/main/java/org/thoughtcrime/securesms/components/StickerView.java @@ -8,7 +8,7 @@ import android.widget.FrameLayout; import org.thoughtcrime.securesms.conversation.v2.utilities.ThumbnailView; -import org.thoughtcrime.securesms.mms.GlideRequests; +import com.bumptech.glide.RequestManager; import org.thoughtcrime.securesms.mms.Slide; import org.thoughtcrime.securesms.mms.SlideClickListener; diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/ZoomingImageView.java b/app/src/main/java/org/thoughtcrime/securesms/components/ZoomingImageView.java index 265f707df95..b246bca4d35 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/ZoomingImageView.java +++ b/app/src/main/java/org/thoughtcrime/securesms/components/ZoomingImageView.java @@ -25,7 +25,7 @@ import org.thoughtcrime.securesms.components.subsampling.AttachmentBitmapDecoder; import org.thoughtcrime.securesms.components.subsampling.AttachmentRegionDecoder; import org.thoughtcrime.securesms.mms.DecryptableStreamUriLoader.DecryptableUri; -import org.thoughtcrime.securesms.mms.GlideRequests; +import com.bumptech.glide.RequestManager; import org.thoughtcrime.securesms.mms.PartAuthority; import org.thoughtcrime.securesms.util.BitmapDecodingException; import org.thoughtcrime.securesms.util.BitmapUtil; @@ -62,7 +62,7 @@ public ZoomingImageView(Context context, AttributeSet attrs, int defStyleAttr) { } @SuppressLint("StaticFieldLeak") - public void setImageUri(@NonNull GlideRequests glideRequests, @NonNull Uri uri, @NonNull String contentType) + public void setImageUri(@NonNull RequestManager glideRequests, @NonNull Uri uri, @NonNull String contentType) { final Context context = getContext(); final int maxTextureSize = BitmapUtil.getMaxTextureSize(); @@ -97,7 +97,7 @@ protected void onPostExecute(@Nullable Pair dimensions) { }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); } - private void setImageViewUri(@NonNull GlideRequests glideRequests, @NonNull Uri uri) { + private void setImageViewUri(@NonNull RequestManager glideRequests, @NonNull Uri uri) { photoView.setVisibility(View.VISIBLE); subsamplingImageView.setVisibility(View.GONE); diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/camera/CameraView.java b/app/src/main/java/org/thoughtcrime/securesms/components/camera/CameraView.java index 11f6c4104b0..5b04e39289c 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/camera/CameraView.java +++ b/app/src/main/java/org/thoughtcrime/securesms/components/camera/CameraView.java @@ -15,7 +15,6 @@ Portions Copyright (C) 2007 The Android Open Source Project package org.thoughtcrime.securesms.components.camera; -import android.annotation.TargetApi; import android.app.Activity; import android.content.Context; import android.content.pm.ActivityInfo; @@ -28,26 +27,26 @@ Portions Copyright (C) 2007 The Android Open Source Project import android.hardware.Camera.Size; import android.os.AsyncTask; import android.os.Build; -import android.os.Build.VERSION; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; import android.util.AttributeSet; -import org.session.libsignal.utilities.Log; import android.view.OrientationEventListener; import android.view.ViewGroup; -import network.loki.messenger.R; -import org.thoughtcrime.securesms.util.BitmapUtil; -import org.session.libsignal.utilities.guava.Optional; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import org.session.libsession.utilities.TextSecurePreferences; import org.session.libsession.utilities.Util; +import org.session.libsignal.utilities.Log; +import org.session.libsignal.utilities.guava.Optional; +import org.thoughtcrime.securesms.util.BitmapUtil; import java.io.IOException; import java.util.Collections; import java.util.LinkedList; import java.util.List; +import network.loki.messenger.R; + @SuppressWarnings("deprecation") public class CameraView extends ViewGroup { private static final String TAG = CameraView.class.getSimpleName(); @@ -91,7 +90,6 @@ public CameraView(Context context, AttributeSet attrs, int defStyle) { addView(surface); } - @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH) public void onResume() { if (state != State.PAUSED) return; state = State.RESUMED; @@ -255,33 +253,15 @@ public boolean isMultiCamera() { return Camera.getNumberOfCameras() > 1; } - public boolean isRearCamera() { - return cameraId == CameraInfo.CAMERA_FACING_BACK; - } - - public void flipCamera() { - if (Camera.getNumberOfCameras() > 1) { - cameraId = cameraId == CameraInfo.CAMERA_FACING_BACK - ? CameraInfo.CAMERA_FACING_FRONT - : CameraInfo.CAMERA_FACING_BACK; - onPause(); - onResume(); - TextSecurePreferences.setDirectCaptureCameraId(getContext(), cameraId); - } - } - - @TargetApi(14) private void onCameraReady(final @NonNull Camera camera) { final Parameters parameters = camera.getParameters(); - if (VERSION.SDK_INT >= 14) { - parameters.setRecordingHint(true); - final List focusModes = parameters.getSupportedFocusModes(); - if (focusModes.contains(Parameters.FOCUS_MODE_CONTINUOUS_PICTURE)) { - parameters.setFocusMode(Parameters.FOCUS_MODE_CONTINUOUS_PICTURE); - } else if (focusModes.contains(Parameters.FOCUS_MODE_CONTINUOUS_VIDEO)) { - parameters.setFocusMode(Parameters.FOCUS_MODE_CONTINUOUS_VIDEO); - } + parameters.setRecordingHint(true); + final List focusModes = parameters.getSupportedFocusModes(); + if (focusModes.contains(Parameters.FOCUS_MODE_CONTINUOUS_PICTURE)) { + parameters.setFocusMode(Parameters.FOCUS_MODE_CONTINUOUS_PICTURE); + } else if (focusModes.contains(Parameters.FOCUS_MODE_CONTINUOUS_VIDEO)) { + parameters.setFocusMode(Parameters.FOCUS_MODE_CONTINUOUS_VIDEO); } displayOrientation = CameraUtils.getCameraDisplayOrientation(getActivity(), getCameraInfo()); @@ -465,7 +445,7 @@ private Rect getCroppedRect(Size cameraPreviewSize, Rect visibleRect, int rotati } final float newWidth = visibleRect.width() * scale; final float newHeight = visibleRect.height() * scale; - final float centerX = (VERSION.SDK_INT < 14 || isTroublemaker()) ? previewWidth - newWidth / 2 : previewWidth / 2; + final float centerX = (isTroublemaker()) ? previewWidth - newWidth / 2 : previewWidth / 2; final float centerY = previewHeight / 2; visibleRect.set((int) (centerX - newWidth / 2), diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/emoji/EmojiKeyboardProvider.java b/app/src/main/java/org/thoughtcrime/securesms/components/emoji/EmojiKeyboardProvider.java index 72419f24c69..d34db1d8102 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/emoji/EmojiKeyboardProvider.java +++ b/app/src/main/java/org/thoughtcrime/securesms/components/emoji/EmojiKeyboardProvider.java @@ -12,7 +12,7 @@ import org.thoughtcrime.securesms.components.emoji.EmojiPageViewGridAdapter.VariationSelectorListener; -import org.thoughtcrime.securesms.mms.GlideRequests; +import com.bumptech.glide.RequestManager; import org.thoughtcrime.securesms.util.ResUtil; import org.session.libsession.utilities.ThemeUtil; @@ -87,7 +87,7 @@ public int getProviderIconView(boolean selected) { } @Override - public void loadCategoryTabIcon(@NonNull GlideRequests glideRequests, @NonNull ImageView imageView, int index) { + public void loadCategoryTabIcon(@NonNull RequestManager glideRequests, @NonNull ImageView imageView, int index) { Drawable drawable = ResUtil.getDrawable(context, models.get(index).getIconAttr()); imageView.setImageDrawable(drawable); } diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/emoji/EmojiVariationSelectorPopup.java b/app/src/main/java/org/thoughtcrime/securesms/components/emoji/EmojiVariationSelectorPopup.java index 7f8282d2c64..14a901b3bab 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/emoji/EmojiVariationSelectorPopup.java +++ b/app/src/main/java/org/thoughtcrime/securesms/components/emoji/EmojiVariationSelectorPopup.java @@ -1,6 +1,7 @@ package org.thoughtcrime.securesms.components.emoji; import android.content.Context; +import android.graphics.drawable.ColorDrawable; import android.view.LayoutInflater; import android.view.ViewGroup; import android.widget.ImageView; @@ -8,6 +9,8 @@ import androidx.annotation.NonNull; +import org.session.libsession.utilities.ThemeUtil; + import java.util.List; import network.loki.messenger.R; @@ -26,7 +29,9 @@ public EmojiVariationSelectorPopup(@NonNull Context context, @NonNull EmojiEvent this.listener = listener; this.list = (ViewGroup) getContentView(); - setBackgroundDrawable(null); + setBackgroundDrawable( + new ColorDrawable(ThemeUtil.getThemedColor(context, R.attr.colorPrimary)) + ); setOutsideTouchable(true); } diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/emoji/MediaKeyboard.java b/app/src/main/java/org/thoughtcrime/securesms/components/emoji/MediaKeyboard.java index 0aa3c33c716..acb53f77676 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/emoji/MediaKeyboard.java +++ b/app/src/main/java/org/thoughtcrime/securesms/components/emoji/MediaKeyboard.java @@ -17,7 +17,7 @@ import org.thoughtcrime.securesms.components.InputAwareLayout.InputView; import org.thoughtcrime.securesms.components.RepeatableImageKey; import org.session.libsignal.utilities.Log; -import org.thoughtcrime.securesms.mms.GlideApp; +import com.bumptech.glide.Glide; import java.util.Arrays; @@ -158,7 +158,7 @@ private void initView() { this.searchButton = view.findViewById(R.id.media_keyboard_search); this.addButton = view.findViewById(R.id.media_keyboard_add); - this.categoryTabAdapter = new MediaKeyboardBottomTabAdapter(GlideApp.with(this), this); + this.categoryTabAdapter = new MediaKeyboardBottomTabAdapter(Glide.with(this), this); categoryTabs.setLayoutManager(new LinearLayoutManager(getContext(), LinearLayoutManager.HORIZONTAL, false)); categoryTabs.setAdapter(categoryTabAdapter); diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/emoji/MediaKeyboardBottomTabAdapter.java b/app/src/main/java/org/thoughtcrime/securesms/components/emoji/MediaKeyboardBottomTabAdapter.java index 0d1b98d474e..08a2ec528ff 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/emoji/MediaKeyboardBottomTabAdapter.java +++ b/app/src/main/java/org/thoughtcrime/securesms/components/emoji/MediaKeyboardBottomTabAdapter.java @@ -9,20 +9,20 @@ import org.thoughtcrime.securesms.components.emoji.MediaKeyboardProvider.TabIconProvider; -import org.thoughtcrime.securesms.mms.GlideRequests; +import com.bumptech.glide.RequestManager; import network.loki.messenger.R; public class MediaKeyboardBottomTabAdapter extends RecyclerView.Adapter { - private final GlideRequests glideRequests; + private final RequestManager glideRequests; private final EventListener eventListener; private TabIconProvider tabIconProvider; private int activePosition; private int count; - public MediaKeyboardBottomTabAdapter(@NonNull GlideRequests glideRequests, @NonNull EventListener eventListener) { + public MediaKeyboardBottomTabAdapter(@NonNull RequestManager glideRequests, @NonNull EventListener eventListener) { this.glideRequests = glideRequests; this.eventListener = eventListener; } @@ -71,7 +71,7 @@ public MediaKeyboardBottomTabViewHolder(@NonNull View itemView) { this.indicator = itemView.findViewById(R.id.media_keyboard_bottom_tab_indicator); } - void bind(@NonNull GlideRequests glideRequests, + void bind(@NonNull RequestManager glideRequests, @NonNull EventListener eventListener, @NonNull TabIconProvider tabIconProvider, int index, diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/emoji/MediaKeyboardProvider.java b/app/src/main/java/org/thoughtcrime/securesms/components/emoji/MediaKeyboardProvider.java index 9f0ce016e8d..21bd6b3a483 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/emoji/MediaKeyboardProvider.java +++ b/app/src/main/java/org/thoughtcrime/securesms/components/emoji/MediaKeyboardProvider.java @@ -8,7 +8,7 @@ -import org.thoughtcrime.securesms.mms.GlideRequests; +import com.bumptech.glide.RequestManager; public interface MediaKeyboardProvider { @LayoutRes int getProviderIconView(boolean selected); @@ -48,6 +48,6 @@ void present(@NonNull MediaKeyboardProvider provider, } interface TabIconProvider { - void loadCategoryTabIcon(@NonNull GlideRequests glideRequests, @NonNull ImageView imageView, int index); + void loadCategoryTabIcon(@NonNull RequestManager glideRequests, @NonNull ImageView imageView, int index); } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/contacts/ContactSelectionListAdapter.kt b/app/src/main/java/org/thoughtcrime/securesms/contacts/ContactSelectionListAdapter.kt index 2d758c90150..e299277bf55 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/contacts/ContactSelectionListAdapter.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/contacts/ContactSelectionListAdapter.kt @@ -6,10 +6,10 @@ import android.view.ViewGroup import androidx.recyclerview.widget.RecyclerView import network.loki.messenger.databinding.ContactSelectionListDividerBinding import org.session.libsession.utilities.recipients.Recipient -import org.thoughtcrime.securesms.mms.GlideRequests +import com.bumptech.glide.RequestManager class ContactSelectionListAdapter(private val context: Context, private val multiSelect: Boolean) : RecyclerView.Adapter() { - lateinit var glide: GlideRequests + lateinit var glide: RequestManager val selectedContacts = mutableSetOf() var items = listOf() set(value) { field = value; notifyDataSetChanged() } diff --git a/app/src/main/java/org/thoughtcrime/securesms/contacts/ContactSelectionListFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/contacts/ContactSelectionListFragment.kt index 0b0ddf4b3da..7c74fb8983e 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/contacts/ContactSelectionListFragment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/contacts/ContactSelectionListFragment.kt @@ -11,7 +11,7 @@ import androidx.recyclerview.widget.LinearLayoutManager import network.loki.messenger.databinding.ContactSelectionListFragmentBinding import org.session.libsession.utilities.recipients.Recipient import org.session.libsignal.utilities.Log -import org.thoughtcrime.securesms.mms.GlideApp +import com.bumptech.glide.Glide class ContactSelectionListFragment : Fragment(), LoaderManager.LoaderCallbacks>, ContactClickListener { private lateinit var binding: ContactSelectionListFragmentBinding @@ -27,7 +27,7 @@ class ContactSelectionListFragment : Fragment(), LoaderManager.LoaderCallbacks> { private lateinit var binding: ActivitySelectContactsBinding @@ -21,7 +21,7 @@ class SelectContactsActivity : PassphraseRequiredActionBarActivity(), LoaderMana private lateinit var usersToExclude: Set private val selectContactsAdapter by lazy { - SelectContactsAdapter(this, GlideApp.with(this)) + SelectContactsAdapter(this, Glide.with(this)) } companion object { diff --git a/app/src/main/java/org/thoughtcrime/securesms/contacts/SelectContactsAdapter.kt b/app/src/main/java/org/thoughtcrime/securesms/contacts/SelectContactsAdapter.kt index 5e3ae1213c3..2a788f71a2e 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/contacts/SelectContactsAdapter.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/contacts/SelectContactsAdapter.kt @@ -4,10 +4,10 @@ import android.content.Context import androidx.recyclerview.widget.RecyclerView import android.view.ViewGroup import org.session.libsession.utilities.Address -import org.thoughtcrime.securesms.mms.GlideRequests +import com.bumptech.glide.RequestManager import org.session.libsession.utilities.recipients.Recipient -class SelectContactsAdapter(private val context: Context, private val glide: GlideRequests) : RecyclerView.Adapter() { +class SelectContactsAdapter(private val context: Context, private val glide: RequestManager) : RecyclerView.Adapter() { val selectedMembers = mutableSetOf() var members = listOf() set(value) { field = value; notifyDataSetChanged() } diff --git a/app/src/main/java/org/thoughtcrime/securesms/contacts/UserView.kt b/app/src/main/java/org/thoughtcrime/securesms/contacts/UserView.kt index f9fd5287070..e0ca2a4242f 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/contacts/UserView.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/contacts/UserView.kt @@ -10,7 +10,7 @@ import network.loki.messenger.databinding.ViewUserBinding import org.session.libsession.messaging.contacts.Contact import org.session.libsession.utilities.recipients.Recipient import org.thoughtcrime.securesms.dependencies.DatabaseComponent -import org.thoughtcrime.securesms.mms.GlideRequests +import com.bumptech.glide.RequestManager class UserView : LinearLayout { private lateinit var binding: ViewUserBinding @@ -45,7 +45,7 @@ class UserView : LinearLayout { // endregion // region Updating - fun bind(user: Recipient, glide: GlideRequests, actionIndicator: ActionIndicator, isSelected: Boolean = false) { + fun bind(user: Recipient, glide: RequestManager, actionIndicator: ActionIndicator, isSelected: Boolean = false) { val isLocalUser = user.isLocalNumber fun getUserDisplayName(publicKey: String): String { if (isLocalUser) return context.getString(R.string.MessageRecord_you) diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/start/home/StartConversation.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/start/home/StartConversation.kt index fcf13c9cf11..15ca2167779 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/start/home/StartConversation.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/start/home/StartConversation.kt @@ -7,11 +7,13 @@ import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.verticalScroll +import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Surface import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color import androidx.compose.ui.input.nestedscroll.nestedScroll import androidx.compose.ui.platform.rememberNestedScrollInteropConnection import androidx.compose.ui.res.stringResource @@ -22,16 +24,18 @@ import org.thoughtcrime.securesms.conversation.start.NullStartConversationDelega import org.thoughtcrime.securesms.conversation.start.StartConversationDelegate import org.thoughtcrime.securesms.ui.Divider import org.thoughtcrime.securesms.ui.ItemButton +import org.thoughtcrime.securesms.ui.components.AppBarCloseIcon +import org.thoughtcrime.securesms.ui.components.BasicAppBar +import org.thoughtcrime.securesms.ui.components.QrImage +import org.thoughtcrime.securesms.ui.contentDescription +import org.thoughtcrime.securesms.ui.theme.LocalColors import org.thoughtcrime.securesms.ui.theme.LocalDimensions +import org.thoughtcrime.securesms.ui.theme.LocalType import org.thoughtcrime.securesms.ui.theme.PreviewTheme import org.thoughtcrime.securesms.ui.theme.SessionColorsParameterProvider import org.thoughtcrime.securesms.ui.theme.ThemeColors -import org.thoughtcrime.securesms.ui.theme.LocalColors -import org.thoughtcrime.securesms.ui.components.AppBar -import org.thoughtcrime.securesms.ui.components.QrImage -import org.thoughtcrime.securesms.ui.contentDescription -import org.thoughtcrime.securesms.ui.theme.LocalType +@OptIn(ExperimentalMaterial3Api::class) @Composable internal fun StartConversationScreen( accountId: String, @@ -41,7 +45,11 @@ internal fun StartConversationScreen( LocalColors.current.backgroundSecondary, shape = MaterialTheme.shapes.small )) { - AppBar(stringResource(R.string.dialog_start_conversation_title), onClose = delegate::onDialogClosePressed) + BasicAppBar( + title = stringResource(R.string.dialog_start_conversation_title), + backgroundColor = Color.Transparent, // transparent to show the rounded shape of the container + actions = { AppBarCloseIcon(onClose = delegate::onDialogClosePressed) } + ) Surface( modifier = Modifier.nestedScroll(rememberNestedScrollInteropConnection()), color = LocalColors.current.backgroundSecondary diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/start/invitefriend/InviteFriend.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/start/invitefriend/InviteFriend.kt index 54abf663037..3453fb5722a 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/start/invitefriend/InviteFriend.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/start/invitefriend/InviteFriend.kt @@ -8,24 +8,28 @@ import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding +import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.Preview import network.loki.messenger.R -import org.thoughtcrime.securesms.ui.theme.LocalDimensions -import org.thoughtcrime.securesms.ui.theme.PreviewTheme -import org.thoughtcrime.securesms.ui.theme.LocalColors -import org.thoughtcrime.securesms.ui.components.AppBar +import org.thoughtcrime.securesms.ui.components.AppBarCloseIcon +import org.thoughtcrime.securesms.ui.components.BackAppBar import org.thoughtcrime.securesms.ui.components.SlimOutlineButton import org.thoughtcrime.securesms.ui.components.SlimOutlineCopyButton import org.thoughtcrime.securesms.ui.components.border import org.thoughtcrime.securesms.ui.contentDescription +import org.thoughtcrime.securesms.ui.theme.LocalColors +import org.thoughtcrime.securesms.ui.theme.LocalDimensions import org.thoughtcrime.securesms.ui.theme.LocalType +import org.thoughtcrime.securesms.ui.theme.PreviewTheme +@OptIn(ExperimentalMaterial3Api::class) @Composable internal fun InviteFriend( accountId: String, @@ -38,7 +42,12 @@ internal fun InviteFriend( LocalColors.current.backgroundSecondary, shape = MaterialTheme.shapes.small )) { - AppBar(stringResource(R.string.invite_a_friend), onBack = onBack, onClose = onClose) + BackAppBar( + title = stringResource(R.string.invite_a_friend), + backgroundColor = Color.Transparent, // transparent to show the rounded shape of the container + onBack = onBack, + actions = { AppBarCloseIcon(onClose = onClose) } + ) Column( modifier = Modifier.padding(horizontal = LocalDimensions.current.spacing) .padding(top = LocalDimensions.current.spacing), diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/start/newmessage/NewMessage.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/start/newmessage/NewMessage.kt index f0a6e21b4c3..df54f9cae86 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/start/newmessage/NewMessage.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/start/newmessage/NewMessage.kt @@ -17,6 +17,7 @@ import androidx.compose.foundation.pager.HorizontalPager import androidx.compose.foundation.pager.rememberPagerState import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.verticalScroll +import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Scaffold import androidx.compose.material3.Surface @@ -29,6 +30,7 @@ import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color import androidx.compose.ui.input.nestedscroll.nestedScroll import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.platform.LocalView @@ -43,9 +45,10 @@ import kotlinx.coroutines.flow.emptyFlow import network.loki.messenger.R import org.thoughtcrime.securesms.conversation.start.StartConversationFragment.Companion.PEEK_RATIO import org.thoughtcrime.securesms.ui.LoadingArcOr -import org.thoughtcrime.securesms.ui.components.AppBar +import org.thoughtcrime.securesms.ui.components.AppBarCloseIcon +import org.thoughtcrime.securesms.ui.components.BackAppBar import org.thoughtcrime.securesms.ui.components.BorderlessButtonWithIcon -import org.thoughtcrime.securesms.ui.components.MaybeScanQrCode +import org.thoughtcrime.securesms.ui.components.QRScannerScreen import org.thoughtcrime.securesms.ui.components.PrimaryOutlineButton import org.thoughtcrime.securesms.ui.components.SessionOutlinedTextField import org.thoughtcrime.securesms.ui.components.SessionTabRow @@ -60,7 +63,7 @@ import kotlin.math.max private val TITLES = listOf(R.string.enter_account_id, R.string.qrScan) -@OptIn(ExperimentalFoundationApi::class) +@OptIn(ExperimentalFoundationApi::class, ExperimentalMaterial3Api::class) @Composable internal fun NewMessage( state: State, @@ -76,12 +79,17 @@ internal fun NewMessage( LocalColors.current.backgroundSecondary, shape = MaterialTheme.shapes.small )) { - AppBar(stringResource(R.string.messageNew), onClose = onClose, onBack = onBack) + BackAppBar( + title = stringResource(R.string.messageNew), + backgroundColor = Color.Transparent, // transparent to show the rounded shape of the container + onBack = onBack, + actions = { AppBarCloseIcon(onClose = onClose) } + ) SessionTabRow(pagerState, TITLES) HorizontalPager(pagerState) { when (TITLES[it]) { R.string.enter_account_id -> EnterAccountId(state, callbacks, onHelp) - R.string.qrScan -> MaybeScanQrCode(qrErrors, onScan = callbacks::onScanQrCode) + R.string.qrScan -> QRScannerScreen(qrErrors, onScan = callbacks::onScanQrCode) } } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityV2.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityV2.kt index f4d70b7141a..d3192abc052 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityV2.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityV2.kt @@ -155,7 +155,7 @@ import org.thoughtcrime.securesms.mediasend.Media import org.thoughtcrime.securesms.mediasend.MediaSendActivity import org.thoughtcrime.securesms.mms.AudioSlide import org.thoughtcrime.securesms.mms.GifSlide -import org.thoughtcrime.securesms.mms.GlideApp +import com.bumptech.glide.Glide import org.thoughtcrime.securesms.mms.ImageSlide import org.thoughtcrime.securesms.mms.MediaConstraints import org.thoughtcrime.securesms.mms.Slide @@ -347,7 +347,7 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe adapter } - private val glide by lazy { GlideApp.with(this) } + private val glide by lazy { Glide.with(this) } private val lockViewHitMargin by lazy { toPx(40, resources) } private val gifButton by lazy { InputBarButton(this, R.drawable.ic_gif_white_24dp, hasOpaqueBackground = true, isGIFButton = true) } private val documentButton by lazy { InputBarButton(this, R.drawable.ic_document_small_dark, hasOpaqueBackground = true) } diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationAdapter.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationAdapter.kt index 40a089d4f62..1c57dc8d5f4 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationAdapter.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationAdapter.kt @@ -26,7 +26,7 @@ import org.thoughtcrime.securesms.conversation.v2.messages.VisibleMessageViewDel import org.thoughtcrime.securesms.database.CursorRecyclerViewAdapter import org.thoughtcrime.securesms.database.model.MessageRecord import org.thoughtcrime.securesms.dependencies.DatabaseComponent -import org.thoughtcrime.securesms.mms.GlideRequests +import com.bumptech.glide.RequestManager import org.thoughtcrime.securesms.preferences.PrivacySettingsActivity import org.thoughtcrime.securesms.showSessionDialog import java.util.concurrent.atomic.AtomicLong @@ -42,7 +42,7 @@ class ConversationAdapter( private val onItemLongPress: (MessageRecord, Int, VisibleMessageView) -> Unit, private val onDeselect: (MessageRecord, Int) -> Unit, private val onAttachmentNeedsDownload: (DatabaseAttachment) -> Unit, - private val glide: GlideRequests, + private val glide: RequestManager, lifecycleCoroutineScope: LifecycleCoroutineScope ) : CursorRecyclerViewAdapter(context, cursor) { private val messageDB by lazy { DatabaseComponent.get(context).mmsSmsDatabase() } diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationViewModel.kt index cbae0e757f1..b0a541a9e89 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationViewModel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationViewModel.kt @@ -261,8 +261,14 @@ class ConversationViewModel( _recipient.updateTo(repository.maybeGetRecipientForThreadId(threadId)) } - fun hidesInputBar(): Boolean = openGroup?.canWrite != true && - blindedRecipient?.blocksCommunityMessageRequests == true + /** + * The input should be hidden when: + * - We are in a community without write access + * - We are dealing with a contact from a community (blinded recipient) that does not allow + * requests form community members + */ + fun hidesInputBar(): Boolean = openGroup?.canWrite == false || + blindedRecipient?.blocksCommunityMessageRequests == true fun legacyBannerRecipient(context: Context): Recipient? = recipient?.run { storage.getLastLegacyRecipient(address.serialize())?.let { Recipient.from(context, Address.fromSerialized(it), false) } diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/Util.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/Util.kt index ff33a58e91b..da8852d1d69 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/Util.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/Util.kt @@ -16,15 +16,9 @@ */ package org.thoughtcrime.securesms.conversation.v2 -import android.annotation.TargetApi -import android.app.ActivityManager -import android.content.ClipData -import android.content.ClipboardManager import android.content.Context import android.graphics.Typeface import android.net.Uri -import android.os.Build.VERSION -import android.os.Build.VERSION_CODES import android.text.Spannable import android.text.SpannableString import android.text.TextUtils @@ -33,24 +27,16 @@ import android.view.View import com.annimon.stream.Stream import com.google.android.mms.pdu_alt.CharacterSets import com.google.android.mms.pdu_alt.EncodedStringValue +import org.session.libsignal.utilities.Log +import org.thoughtcrime.securesms.components.ComposeText import java.io.ByteArrayOutputStream import java.io.IOException import java.io.UnsupportedEncodingException -import java.security.SecureRandom -import java.util.Arrays import java.util.Collections -import java.util.concurrent.TimeUnit -import kotlin.math.max -import kotlin.math.min -import network.loki.messenger.R -import org.session.libsignal.utilities.Log -import org.thoughtcrime.securesms.components.ComposeText object Util { private val TAG: String = Log.tag(Util::class.java) - private val BUILD_LIFESPAN = TimeUnit.DAYS.toMillis(90) - fun asList(vararg elements: T): List { val result = mutableListOf() // LinkedList() Collections.addAll(result, *elements) @@ -106,19 +92,6 @@ object Util { return sb.toString() } - fun rightPad(value: String, length: Int): String { - if (value.length >= length) { - return value - } - - val out = StringBuilder(value) - while (out.length < length) { - out.append(" ") - } - - return out.toString() - } - fun isEmpty(value: Array?): Boolean { return value == null || value.size == 0 } @@ -135,64 +108,6 @@ object Util { return charSequence == null || charSequence.length == 0 } - fun hasItems(collection: Collection<*>?): Boolean { - return collection != null && !collection.isEmpty() - } - - fun getOrDefault(map: Map, key: K, defaultValue: V): V? { - return if (map.containsKey(key)) map[key] else defaultValue - } - - fun getFirstNonEmpty(vararg values: String?): String { - for (value in values) { - if (!value.isNullOrEmpty()) { return value } - } - return "" - } - - fun emptyIfNull(value: String?): String { - return value ?: "" - } - - fun emptyIfNull(value: CharSequence?): CharSequence { - return value ?: "" - } - - fun getBoldedString(value: String?): CharSequence { - val spanned = SpannableString(value) - spanned.setSpan( - StyleSpan(Typeface.BOLD), 0, - spanned.length, - Spannable.SPAN_EXCLUSIVE_EXCLUSIVE - ) - - return spanned - } - - fun toIsoString(bytes: ByteArray?): String { - try { - return String(bytes!!, charset(CharacterSets.MIMENAME_ISO_8859_1)) - } catch (e: UnsupportedEncodingException) { - throw AssertionError("ISO_8859_1 must be supported!") - } - } - - fun toIsoBytes(isoString: String): ByteArray { - try { - return isoString.toByteArray(charset(CharacterSets.MIMENAME_ISO_8859_1)) - } catch (e: UnsupportedEncodingException) { - throw AssertionError("ISO_8859_1 must be supported!") - } - } - - fun toUtf8Bytes(utf8String: String): ByteArray { - try { - return utf8String.toByteArray(charset(CharacterSets.MIMENAME_UTF_8)) - } catch (e: UnsupportedEncodingException) { - throw AssertionError("UTF_8 must be supported!") - } - } - fun wait(lock: Any, timeout: Long) { try { (lock as Object).wait(timeout) @@ -227,20 +142,6 @@ object Util { return parts } - fun combine(vararg elements: ByteArray?): ByteArray { - try { - val baos = ByteArrayOutputStream() - - for (element in elements) { - baos.write(element) - } - - return baos.toByteArray() - } catch (e: IOException) { - throw AssertionError(e) - } - } - fun trim(input: ByteArray?, length: Int): ByteArray { val result = ByteArray(length) System.arraycopy(input, 0, result, 0, result.size) @@ -248,57 +149,11 @@ object Util { return result } - fun getSecretBytes(size: Int): ByteArray { - return getSecretBytes(SecureRandom(), size) - } - - fun getSecretBytes(secureRandom: SecureRandom, size: Int): ByteArray { - val secret = ByteArray(size) - secureRandom.nextBytes(secret) - return secret - } - - fun getRandomElement(elements: Array): T { - return elements[SecureRandom().nextInt(elements.size)] - } - - fun getRandomElement(elements: List): T { - return elements[SecureRandom().nextInt(elements.size)] - } - - fun equals(a: Any?, b: Any?): Boolean { - return a === b || (a != null && a == b) - } - - fun hashCode(vararg objects: Any?): Int { - return objects.contentHashCode() - } - fun uri(uri: String?): Uri? { return if (uri == null) null else Uri.parse(uri) } - @TargetApi(VERSION_CODES.KITKAT) - fun isLowMemory(context: Context): Boolean { - val activityManager = context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager - - return (VERSION.SDK_INT >= VERSION_CODES.KITKAT && activityManager.isLowRamDevice) || - activityManager.largeMemoryClass <= 64 - } - - fun clamp(value: Int, min: Int, max: Int): Int { - return min(max(value.toDouble(), min.toDouble()), max.toDouble()).toInt() - } - - fun clamp(value: Long, min: Long, max: Long): Long { - return min(max(value.toDouble(), min.toDouble()), max.toDouble()).toLong() - } - - fun clamp(value: Float, min: Float, max: Float): Float { - return min(max(value.toDouble(), min.toDouble()), max.toDouble()).toFloat() - } - /** * Returns half of the difference between the given length, and the length when scaled by the * given scale. @@ -308,74 +163,6 @@ object Util { return (length - scaledLength) / 2 } - fun readTextFromClipboard(context: Context): String? { - run { - val clipboardManager = - context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager - return if (clipboardManager.hasPrimaryClip() && clipboardManager.primaryClip!!.itemCount > 0) { - clipboardManager.primaryClip!!.getItemAt(0).text.toString() - } else { - null - } - } - } - - fun writeTextToClipboard(context: Context, text: String) { - writeTextToClipboard(context, context.getString(R.string.app_name), text) - } - - fun writeTextToClipboard(context: Context, label: String, text: String) { - val clipboard = context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager - val clip = ClipData.newPlainText(label, text) - clipboard.setPrimaryClip(clip) - } - - fun toIntExact(value: Long): Int { - if (value.toInt().toLong() != value) { - throw ArithmeticException("integer overflow") - } - return value.toInt() - } - - fun isEquals(first: Long?, second: Long): Boolean { - return first != null && first == second - } - - @SafeVarargs - fun concatenatedList(vararg items: Collection): List { - val concat: MutableList = ArrayList( - Stream.of(*items).reduce(0) { sum: Int, list: Collection -> sum + list.size }) - - for (list in items) { - concat.addAll(list) - } - - return concat - } - - fun isLong(value: String): Boolean { - try { - value.toLong() - return true - } catch (e: NumberFormatException) { - return false - } - } - - fun parseInt(integer: String, defaultValue: Int): Int { - return try { - integer.toInt() - } catch (e: NumberFormatException) { - defaultValue - } - } - - // Method to determine if we're currently in a left-to-right or right-to-left language like Arabic - fun usingRightToLeftLanguage(context: Context): Boolean { - val config = context.resources.configuration - return config.layoutDirection == View.LAYOUT_DIRECTION_RTL - } - // Method to determine if we're currently in a left-to-right or right-to-left language like Arabic fun usingLeftToRightLanguage(context: Context): Boolean { val config = context.resources.configuration diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/components/AlbumThumbnailView.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/components/AlbumThumbnailView.kt index 57c42a7719a..dba6bf5b7b8 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/components/AlbumThumbnailView.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/components/AlbumThumbnailView.kt @@ -20,7 +20,7 @@ import org.thoughtcrime.securesms.MediaPreviewActivity import org.thoughtcrime.securesms.components.CornerMask import org.thoughtcrime.securesms.conversation.v2.utilities.ThumbnailView import org.thoughtcrime.securesms.database.model.MmsMessageRecord -import org.thoughtcrime.securesms.mms.GlideRequests +import com.bumptech.glide.RequestManager import org.thoughtcrime.securesms.mms.Slide import org.thoughtcrime.securesms.util.ActivityDispatcher @@ -80,7 +80,7 @@ class AlbumThumbnailView : RelativeLayout { slideSize = -1 } - fun bind(glideRequests: GlideRequests, message: MmsMessageRecord, + fun bind(glideRequests: RequestManager, message: MmsMessageRecord, isStart: Boolean, isEnd: Boolean) { slides = message.slideDeck.thumbnailSlides if (slides.isEmpty()) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/components/LinkPreviewDraftView.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/components/LinkPreviewDraftView.kt index 9c414f34fd4..9bad72f296f 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/components/LinkPreviewDraftView.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/components/LinkPreviewDraftView.kt @@ -7,7 +7,7 @@ import android.widget.LinearLayout import androidx.core.view.isVisible import network.loki.messenger.databinding.ViewLinkPreviewDraftBinding import org.session.libsession.messaging.sending_receiving.link_preview.LinkPreview -import org.thoughtcrime.securesms.mms.GlideRequests +import com.bumptech.glide.RequestManager import org.thoughtcrime.securesms.mms.ImageSlide import org.thoughtcrime.securesms.util.toPx @@ -27,7 +27,7 @@ class LinkPreviewDraftView : LinearLayout { binding.linkPreviewDraftCancelButton.setOnClickListener { cancel() } } - fun update(glide: GlideRequests, linkPreview: LinkPreview) { + fun update(glide: RequestManager, linkPreview: LinkPreview) { // Hide the loader and show the content view binding.linkPreviewDraftContainer.isVisible = true binding.linkPreviewDraftLoader.isVisible = false diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/components/MentionCandidateSelectionView.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/components/MentionCandidateSelectionView.kt index 303c17c5a5c..5698ddd0bbc 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/components/MentionCandidateSelectionView.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/components/MentionCandidateSelectionView.kt @@ -9,13 +9,13 @@ import android.widget.BaseAdapter import android.widget.ListView import org.session.libsession.messaging.mentions.Mention import org.thoughtcrime.securesms.dependencies.DatabaseComponent -import org.thoughtcrime.securesms.mms.GlideRequests +import com.bumptech.glide.RequestManager import org.thoughtcrime.securesms.util.toPx class MentionCandidateSelectionView(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : ListView(context, attrs, defStyleAttr) { private var mentionCandidates = listOf() set(newValue) { field = newValue; mentionCandidateSelectionViewAdapter.mentionCandidates = newValue } - var glide: GlideRequests? = null + var glide: RequestManager? = null set(newValue) { field = newValue; mentionCandidateSelectionViewAdapter.glide = newValue } var openGroupServer: String? = null set(newValue) { field = newValue; mentionCandidateSelectionViewAdapter.openGroupServer = openGroupServer } @@ -28,7 +28,7 @@ class MentionCandidateSelectionView(context: Context, attrs: AttributeSet?, defS private class Adapter(private val context: Context) : BaseAdapter() { var mentionCandidates = listOf() set(newValue) { field = newValue; notifyDataSetChanged() } - var glide: GlideRequests? = null + var glide: RequestManager? = null var openGroupServer: String? = null var openGroupRoom: String? = null diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/components/MentionCandidateView.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/components/MentionCandidateView.kt index d5442639154..14dc6263ab4 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/components/MentionCandidateView.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/components/MentionCandidateView.kt @@ -8,13 +8,13 @@ import android.widget.LinearLayout import network.loki.messenger.databinding.ViewMentionCandidateBinding import org.session.libsession.messaging.mentions.Mention import org.thoughtcrime.securesms.groups.OpenGroupManager -import org.thoughtcrime.securesms.mms.GlideRequests +import com.bumptech.glide.RequestManager class MentionCandidateView : LinearLayout { private lateinit var binding: ViewMentionCandidateBinding var mentionCandidate = Mention("", "") set(newValue) { field = newValue; update() } - var glide: GlideRequests? = null + var glide: RequestManager? = null var openGroupServer: String? = null var openGroupRoom: String? = null diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/input_bar/InputBar.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/input_bar/InputBar.kt index 8e38c7d38e9..c8aacdeb6e3 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/input_bar/InputBar.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/input_bar/InputBar.kt @@ -27,7 +27,7 @@ import org.thoughtcrime.securesms.conversation.v2.messages.QuoteView import org.thoughtcrime.securesms.conversation.v2.messages.QuoteViewDelegate import org.thoughtcrime.securesms.database.model.MessageRecord import org.thoughtcrime.securesms.database.model.MmsMessageRecord -import org.thoughtcrime.securesms.mms.GlideRequests +import com.bumptech.glide.RequestManager import org.thoughtcrime.securesms.util.addTextChangedListener import org.thoughtcrime.securesms.util.contains @@ -188,7 +188,7 @@ class InputBar @JvmOverloads constructor( private fun startRecordingVoiceMessage() { delegate?.startRecordingVoiceMessage() } - fun draftQuote(thread: Recipient, message: MessageRecord, glide: GlideRequests) { + fun draftQuote(thread: Recipient, message: MessageRecord, glide: RequestManager) { quoteView?.let(binding.inputBarAdditionalContentContainer::removeView) quote = message @@ -238,7 +238,7 @@ class InputBar @JvmOverloads constructor( requestLayout() } - fun updateLinkPreviewDraft(glide: GlideRequests, updatedLinkPreview: LinkPreview) { + fun updateLinkPreviewDraft(glide: RequestManager, updatedLinkPreview: LinkPreview) { // Update our `linkPreview` property with the new (provided as an argument to this function) // then update the View from that. linkPreview = updatedLinkPreview.also { linkPreviewDraftView?.update(glide, it) } diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/input_bar/InputBarButton.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/input_bar/InputBarButton.kt index c88e05dbfbb..d2ec4b2d698 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/input_bar/InputBarButton.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/input_bar/InputBarButton.kt @@ -148,11 +148,8 @@ class InputBarButton : RelativeLayout { private fun onDown(event: MotionEvent) { expand() - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - performHapticFeedback(HapticFeedbackConstants.CONTEXT_CLICK) - } else { - performHapticFeedback(HapticFeedbackConstants.LONG_PRESS) - } + performHapticFeedback(HapticFeedbackConstants.CONTEXT_CLICK) + longPressCallback?.let { gestureHandler.removeCallbacks(it) } val newLongPressCallback = Runnable { onLongPress?.invoke() } this.longPressCallback = newLongPressCallback diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/input_bar/InputBarEditText.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/input_bar/InputBarEditText.kt index a42222f412f..449fe8cfd4d 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/input_bar/InputBarEditText.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/input_bar/InputBarEditText.kt @@ -3,7 +3,6 @@ package org.thoughtcrime.securesms.conversation.v2.input_bar import android.content.Context import android.content.res.Resources import android.net.Uri -import android.os.Build import android.util.AttributeSet import android.view.inputmethod.EditorInfo import android.view.inputmethod.InputConnection @@ -57,7 +56,7 @@ class InputBarEditText : AppCompatEditText { InputConnectionCompat.OnCommitContentListener { inputContentInfo, flags, opts -> val lacksPermission = (flags and InputConnectionCompat.INPUT_CONTENT_GRANT_READ_URI_PERMISSION) != 0 // read and display inputContentInfo asynchronously - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N_MR1 && lacksPermission) { + if (lacksPermission) { try { inputContentInfo.requestPermission() } catch (e: Exception) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/menus/ConversationMenuHelper.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/menus/ConversationMenuHelper.kt index 177becd497f..8d018d68130 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/menus/ConversationMenuHelper.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/menus/ConversationMenuHelper.kt @@ -24,7 +24,7 @@ import org.session.libsession.utilities.TextSecurePreferences import org.session.libsession.utilities.recipients.Recipient import org.session.libsignal.utilities.guava.Optional import org.session.libsignal.utilities.toHexString -import org.thoughtcrime.securesms.MediaOverviewActivity +import org.thoughtcrime.securesms.media.MediaOverviewActivity import org.thoughtcrime.securesms.ShortcutLauncherActivity import org.thoughtcrime.securesms.calls.WebRtcCallActivity import org.thoughtcrime.securesms.contacts.SelectContactsActivity @@ -149,10 +149,8 @@ object ConversationMenuHelper { } private fun showAllMedia(context: Context, thread: Recipient) { - val intent = Intent(context, MediaOverviewActivity::class.java) - intent.putExtra(MediaOverviewActivity.ADDRESS_EXTRA, thread.address) val activity = context as AppCompatActivity - activity.startActivity(intent) + activity.startActivity(MediaOverviewActivity.createIntent(context, thread.address)) } private fun search(context: Context) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/LinkPreviewView.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/LinkPreviewView.kt index 4e6066edb32..8cf80dc0902 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/LinkPreviewView.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/LinkPreviewView.kt @@ -15,7 +15,7 @@ import org.thoughtcrime.securesms.components.CornerMask import org.thoughtcrime.securesms.conversation.v2.ModalUrlBottomSheet import org.thoughtcrime.securesms.conversation.v2.utilities.MessageBubbleUtilities import org.thoughtcrime.securesms.database.model.MmsMessageRecord -import org.thoughtcrime.securesms.mms.GlideRequests +import com.bumptech.glide.RequestManager import org.thoughtcrime.securesms.mms.ImageSlide class LinkPreviewView : LinearLayout { @@ -32,7 +32,7 @@ class LinkPreviewView : LinearLayout { // region Updating fun bind( message: MmsMessageRecord, - glide: GlideRequests, + glide: RequestManager, isStartOfMessageCluster: Boolean, isEndOfMessageCluster: Boolean ) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/QuoteView.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/QuoteView.kt index 77565244a07..40cf4bc1e01 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/QuoteView.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/QuoteView.kt @@ -18,7 +18,7 @@ import org.session.libsession.utilities.getColorFromAttr import org.session.libsession.utilities.recipients.Recipient import org.thoughtcrime.securesms.conversation.v2.utilities.MentionUtilities import org.thoughtcrime.securesms.database.SessionContactDatabase -import org.thoughtcrime.securesms.mms.GlideRequests +import com.bumptech.glide.RequestManager import org.thoughtcrime.securesms.mms.SlideDeck import org.thoughtcrime.securesms.util.MediaUtil import org.thoughtcrime.securesms.util.getAccentColor @@ -68,7 +68,7 @@ class QuoteView @JvmOverloads constructor(context: Context, attrs: AttributeSet? // region Updating fun bind(authorPublicKey: String, body: String?, attachments: SlideDeck?, thread: Recipient, isOutgoingMessage: Boolean, isOpenGroupInvitation: Boolean, threadID: Long, - isOriginalMissing: Boolean, glide: GlideRequests) { + isOriginalMissing: Boolean, glide: RequestManager) { // Author val author = contactDb.getContactWithAccountID(authorPublicKey) val localNumber = TextSecurePreferences.getLocalNumber(context) diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/VisibleMessageContentView.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/VisibleMessageContentView.kt index b320e72e265..dcce528234c 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/VisibleMessageContentView.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/VisibleMessageContentView.kt @@ -38,8 +38,8 @@ import org.thoughtcrime.securesms.conversation.v2.utilities.TextUtilities.getInt import org.thoughtcrime.securesms.database.model.MessageRecord import org.thoughtcrime.securesms.database.model.MmsMessageRecord import org.thoughtcrime.securesms.database.model.SmsMessageRecord -import org.thoughtcrime.securesms.mms.GlideApp -import org.thoughtcrime.securesms.mms.GlideRequests +import com.bumptech.glide.Glide +import com.bumptech.glide.RequestManager import org.thoughtcrime.securesms.util.GlowViewUtilities import org.thoughtcrime.securesms.util.SearchUtil import org.thoughtcrime.securesms.util.getAccentColor @@ -63,7 +63,7 @@ class VisibleMessageContentView : ConstraintLayout { message: MessageRecord, isStartOfMessageCluster: Boolean = true, isEndOfMessageCluster: Boolean = true, - glide: GlideRequests = GlideApp.with(this), + glide: RequestManager = Glide.with(this), thread: Recipient, searchQuery: String? = null, contactIsTrusted: Boolean = true, diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/VisibleMessageView.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/VisibleMessageView.kt index b2e3bba81b4..9f7f620ab50 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/VisibleMessageView.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/VisibleMessageView.kt @@ -52,8 +52,8 @@ import org.thoughtcrime.securesms.database.ThreadDatabase import org.thoughtcrime.securesms.database.model.MessageRecord import org.thoughtcrime.securesms.groups.OpenGroupManager import org.thoughtcrime.securesms.home.UserDetailsBottomSheet -import org.thoughtcrime.securesms.mms.GlideApp -import org.thoughtcrime.securesms.mms.GlideRequests +import com.bumptech.glide.Glide +import com.bumptech.glide.RequestManager import org.thoughtcrime.securesms.util.DateUtils import org.thoughtcrime.securesms.util.disableClipping import org.thoughtcrime.securesms.util.toDp @@ -141,7 +141,7 @@ class VisibleMessageView : FrameLayout { message: MessageRecord, previous: MessageRecord? = null, next: MessageRecord? = null, - glide: GlideRequests = GlideApp.with(this), + glide: RequestManager = Glide.with(this), searchQuery: String? = null, contact: Contact? = null, senderAccountID: String, diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/utilities/AttachmentManager.java b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/utilities/AttachmentManager.java index 76b95d7b175..ee98f623f2a 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/utilities/AttachmentManager.java +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/utilities/AttachmentManager.java @@ -44,7 +44,7 @@ import org.thoughtcrime.securesms.mms.AudioSlide; import org.thoughtcrime.securesms.mms.DocumentSlide; import org.thoughtcrime.securesms.mms.GifSlide; -import org.thoughtcrime.securesms.mms.GlideRequests; +import com.bumptech.glide.RequestManager; import org.thoughtcrime.securesms.mms.ImageSlide; import org.thoughtcrime.securesms.mms.MediaConstraints; import org.thoughtcrime.securesms.mms.PartAuthority; @@ -126,7 +126,7 @@ private void setSlide(@NonNull Slide slide) { } @SuppressLint("StaticFieldLeak") - public ListenableFuture setMedia(@NonNull final GlideRequests glideRequests, + public ListenableFuture setMedia(@NonNull final RequestManager glideRequests, @NonNull final Uri uri, @NonNull final MediaType mediaType, @NonNull final MediaConstraints constraints, diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/utilities/ThumbnailView.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/utilities/ThumbnailView.kt index 02c683aac61..83932b2ce48 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/utilities/ThumbnailView.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/utilities/ThumbnailView.kt @@ -25,8 +25,8 @@ import org.session.libsignal.utilities.SettableFuture import org.thoughtcrime.securesms.components.GlideBitmapListeningTarget import org.thoughtcrime.securesms.components.GlideDrawableListeningTarget import org.thoughtcrime.securesms.mms.DecryptableStreamUriLoader.DecryptableUri -import org.thoughtcrime.securesms.mms.GlideRequest -import org.thoughtcrime.securesms.mms.GlideRequests +import com.bumptech.glide.RequestBuilder +import com.bumptech.glide.RequestManager import org.thoughtcrime.securesms.mms.Slide open class ThumbnailView @JvmOverloads constructor( @@ -104,13 +104,13 @@ open class ThumbnailView @JvmOverloads constructor( } fun setImageResource( - glide: GlideRequests, + glide: RequestManager, slide: Slide, isPreview: Boolean ): ListenableFuture = setImageResource(glide, slide, isPreview, 0, 0) fun setImageResource( - glide: GlideRequests, slide: Slide, + glide: RequestManager, slide: Slide, isPreview: Boolean, naturalWidth: Int, naturalHeight: Int ): ListenableFuture { @@ -152,9 +152,9 @@ open class ThumbnailView @JvmOverloads constructor( } private fun buildThumbnailGlideRequest( - glide: GlideRequests, + glide: RequestManager, slide: Slide - ): GlideRequest = glide.load(DecryptableUri(slide.thumbnailUri!!)) + ): RequestBuilder = glide.load(DecryptableUri(slide.thumbnailUri!!)) .diskCacheStrategy(DiskCacheStrategy.NONE) .overrideDimensions() .transition(DrawableTransitionOptions.withCrossFade()) @@ -162,21 +162,21 @@ open class ThumbnailView @JvmOverloads constructor( .missingThumbnailPicture(slide.isInProgress) private fun buildPlaceholderGlideRequest( - glide: GlideRequests, + glide: RequestManager, slide: Slide - ): GlideRequest = glide.asBitmap() + ): RequestBuilder = glide.asBitmap() .load(slide.getPlaceholderRes(context.theme)) .diskCacheStrategy(DiskCacheStrategy.NONE) .overrideDimensions() .fitCenter() - open fun clear(glideRequests: GlideRequests) { + open fun clear(glideRequests: RequestManager) { glideRequests.clear(binding.thumbnailImage) slide = null } fun setImageResource( - glideRequests: GlideRequests, + glideRequests: RequestManager, uri: Uri ): ListenableFuture = glideRequests.load(DecryptableUri(uri)) .diskCacheStrategy(DiskCacheStrategy.NONE) @@ -184,19 +184,19 @@ open class ThumbnailView @JvmOverloads constructor( .transform(CenterCrop()) .intoDrawableTargetAsFuture() - private fun GlideRequest.intoDrawableTargetAsFuture() = + private fun RequestBuilder.intoDrawableTargetAsFuture() = SettableFuture().also { binding.run { GlideDrawableListeningTarget(thumbnailImage, thumbnailLoadIndicator, it) }.let { into(it) } } - private fun GlideRequest.overrideDimensions() = + private fun RequestBuilder.overrideDimensions() = dimensDelegate.resourceSize().takeIf { 0 !in it } ?.let { override(it[WIDTH], it[HEIGHT]) } ?: override(getDefaultWidth(), getDefaultHeight()) } -private fun GlideRequest.missingThumbnailPicture( +private fun RequestBuilder.missingThumbnailPicture( inProgress: Boolean ) = takeIf { inProgress } ?: apply(RequestOptions.errorOf(R.drawable.ic_missing_thumbnail_picture)) diff --git a/app/src/main/java/org/thoughtcrime/securesms/crypto/AttachmentSecretProvider.java b/app/src/main/java/org/thoughtcrime/securesms/crypto/AttachmentSecretProvider.java index 0344551cab8..844ba975f15 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/crypto/AttachmentSecretProvider.java +++ b/app/src/main/java/org/thoughtcrime/securesms/crypto/AttachmentSecretProvider.java @@ -1,14 +1,14 @@ package org.thoughtcrime.securesms.crypto; +import static org.session.libsignal.utilities.Util.SECURE_RANDOM; + import android.content.Context; import android.os.Build; import androidx.annotation.NonNull; import org.session.libsession.utilities.TextSecurePreferences; -import java.security.SecureRandom; - /** * A provider that is responsible for creating or retrieving the AttachmentSecret model. * @@ -59,31 +59,22 @@ private AttachmentSecret getUnencryptedAttachmentSecret(@NonNull Context context { AttachmentSecret attachmentSecret = AttachmentSecret.fromString(unencryptedSecret); - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) { - return attachmentSecret; - } else { - KeyStoreHelper.SealedData encryptedSecret = KeyStoreHelper.seal(attachmentSecret.serialize().getBytes()); + KeyStoreHelper.SealedData encryptedSecret = KeyStoreHelper.seal(attachmentSecret.serialize().getBytes()); - TextSecurePreferences.setAttachmentEncryptedSecret(context, encryptedSecret.serialize()); - TextSecurePreferences.setAttachmentUnencryptedSecret(context, null); + TextSecurePreferences.setAttachmentEncryptedSecret(context, encryptedSecret.serialize()); + TextSecurePreferences.setAttachmentUnencryptedSecret(context, null); - return attachmentSecret; - } + return attachmentSecret; } private AttachmentSecret getEncryptedAttachmentSecret(@NonNull String serializedEncryptedSecret) { - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) { - throw new AssertionError("OS downgrade not supported. KeyStore sealed data exists on platform < M!"); - } else { - KeyStoreHelper.SealedData encryptedSecret = KeyStoreHelper.SealedData.fromString(serializedEncryptedSecret); - return AttachmentSecret.fromString(new String(KeyStoreHelper.unseal(encryptedSecret))); - } + KeyStoreHelper.SealedData encryptedSecret = KeyStoreHelper.SealedData.fromString(serializedEncryptedSecret); + return AttachmentSecret.fromString(new String(KeyStoreHelper.unseal(encryptedSecret))); } private AttachmentSecret createAndStoreAttachmentSecret(@NonNull Context context) { - SecureRandom random = new SecureRandom(); byte[] secret = new byte[32]; - random.nextBytes(secret); + SECURE_RANDOM.nextBytes(secret); AttachmentSecret attachmentSecret = new AttachmentSecret(null, null, secret); storeAttachmentSecret(context, attachmentSecret); @@ -92,12 +83,8 @@ private AttachmentSecret createAndStoreAttachmentSecret(@NonNull Context context } private void storeAttachmentSecret(@NonNull Context context, @NonNull AttachmentSecret attachmentSecret) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - KeyStoreHelper.SealedData encryptedSecret = KeyStoreHelper.seal(attachmentSecret.serialize().getBytes()); - TextSecurePreferences.setAttachmentEncryptedSecret(context, encryptedSecret.serialize()); - } else { - TextSecurePreferences.setAttachmentUnencryptedSecret(context, attachmentSecret.serialize()); - } + KeyStoreHelper.SealedData encryptedSecret = KeyStoreHelper.seal(attachmentSecret.serialize().getBytes()); + TextSecurePreferences.setAttachmentEncryptedSecret(context, encryptedSecret.serialize()); } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/crypto/BiometricSecretProvider.kt b/app/src/main/java/org/thoughtcrime/securesms/crypto/BiometricSecretProvider.kt index ac12a69bbe5..1329ad0dc30 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/crypto/BiometricSecretProvider.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/crypto/BiometricSecretProvider.kt @@ -42,9 +42,8 @@ class BiometricSecretProvider { builder.setUnlockedDeviceRequired(true) } - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { - builder.setInvalidatedByBiometricEnrollment(true) - } + builder.setInvalidatedByBiometricEnrollment(true) + keyGenerator.initialize(builder.build()) keyGenerator.generateKeyPair() } diff --git a/app/src/main/java/org/thoughtcrime/securesms/crypto/DatabaseSecretProvider.java b/app/src/main/java/org/thoughtcrime/securesms/crypto/DatabaseSecretProvider.java index 0ad22c3a909..ab79f7ea55d 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/crypto/DatabaseSecretProvider.java +++ b/app/src/main/java/org/thoughtcrime/securesms/crypto/DatabaseSecretProvider.java @@ -1,6 +1,8 @@ package org.thoughtcrime.securesms.crypto; +import static org.session.libsignal.utilities.Util.SECURE_RANDOM; + import android.content.Context; import android.os.Build; import androidx.annotation.NonNull; @@ -8,7 +10,6 @@ import org.session.libsession.utilities.TextSecurePreferences; import java.io.IOException; -import java.security.SecureRandom; public class DatabaseSecretProvider { @@ -35,43 +36,30 @@ private DatabaseSecret getUnencryptedDatabaseSecret(@NonNull Context context, @N try { DatabaseSecret databaseSecret = new DatabaseSecret(unencryptedSecret); - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) { - return databaseSecret; - } else { - KeyStoreHelper.SealedData encryptedSecret = KeyStoreHelper.seal(databaseSecret.asBytes()); + KeyStoreHelper.SealedData encryptedSecret = KeyStoreHelper.seal(databaseSecret.asBytes()); - TextSecurePreferences.setDatabaseEncryptedSecret(context, encryptedSecret.serialize()); - TextSecurePreferences.setDatabaseUnencryptedSecret(context, null); + TextSecurePreferences.setDatabaseEncryptedSecret(context, encryptedSecret.serialize()); + TextSecurePreferences.setDatabaseUnencryptedSecret(context, null); - return databaseSecret; - } + return databaseSecret; } catch (IOException e) { throw new AssertionError(e); } } private DatabaseSecret getEncryptedDatabaseSecret(@NonNull String serializedEncryptedSecret) { - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) { - throw new AssertionError("OS downgrade not supported. KeyStore sealed data exists on platform < M!"); - } else { - KeyStoreHelper.SealedData encryptedSecret = KeyStoreHelper.SealedData.fromString(serializedEncryptedSecret); - return new DatabaseSecret(KeyStoreHelper.unseal(encryptedSecret)); - } + KeyStoreHelper.SealedData encryptedSecret = KeyStoreHelper.SealedData.fromString(serializedEncryptedSecret); + return new DatabaseSecret(KeyStoreHelper.unseal(encryptedSecret)); } private DatabaseSecret createAndStoreDatabaseSecret(@NonNull Context context) { - SecureRandom random = new SecureRandom(); byte[] secret = new byte[32]; - random.nextBytes(secret); + SECURE_RANDOM.nextBytes(secret); DatabaseSecret databaseSecret = new DatabaseSecret(secret); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - KeyStoreHelper.SealedData encryptedSecret = KeyStoreHelper.seal(databaseSecret.asBytes()); - TextSecurePreferences.setDatabaseEncryptedSecret(context, encryptedSecret.serialize()); - } else { - TextSecurePreferences.setDatabaseUnencryptedSecret(context, databaseSecret.asString()); - } + KeyStoreHelper.SealedData encryptedSecret = KeyStoreHelper.seal(databaseSecret.asBytes()); + TextSecurePreferences.setDatabaseEncryptedSecret(context, encryptedSecret.serialize()); return databaseSecret; } diff --git a/app/src/main/java/org/thoughtcrime/securesms/crypto/IdentityKeyUtil.java b/app/src/main/java/org/thoughtcrime/securesms/crypto/IdentityKeyUtil.java index 62aaf58f1a1..4db46a3abc4 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/crypto/IdentityKeyUtil.java +++ b/app/src/main/java/org/thoughtcrime/securesms/crypto/IdentityKeyUtil.java @@ -129,27 +129,19 @@ public static String retrieve(Context context, String key) { } private static String getUnencryptedSecret(String key, String unencryptedSecret, Context context) { - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) { - return unencryptedSecret; - } else { - KeyStoreHelper.SealedData encryptedSecret = KeyStoreHelper.seal(unencryptedSecret.getBytes()); + KeyStoreHelper.SealedData encryptedSecret = KeyStoreHelper.seal(unencryptedSecret.getBytes()); - // save the encrypted suffix secret "key_encrypted" - save(context,key+ENCRYPTED_SUFFIX,encryptedSecret.serialize()); - // delete the regular secret "key" - delete(context,key); + // save the encrypted suffix secret "key_encrypted" + save(context,key+ENCRYPTED_SUFFIX,encryptedSecret.serialize()); + // delete the regular secret "key" + delete(context,key); - return unencryptedSecret; - } + return unencryptedSecret; } private static String getEncryptedSecret(String encryptedSecret) { - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) { - throw new AssertionError("OS downgrade not supported. KeyStore sealed data exists on platform < M!"); - } else { - KeyStoreHelper.SealedData sealedData = KeyStoreHelper.SealedData.fromString(encryptedSecret); - return new String(KeyStoreHelper.unseal(sealedData)); - } + KeyStoreHelper.SealedData sealedData = KeyStoreHelper.SealedData.fromString(encryptedSecret); + return new String(KeyStoreHelper.unseal(sealedData)); } @@ -157,17 +149,14 @@ public static void save(Context context, String key, String value) { SharedPreferences preferences = context.getSharedPreferences(MASTER_SECRET_UTIL_PREFERENCES_NAME, 0); Editor preferencesEditor = preferences.edit(); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - boolean isEncryptedSuffix = key.endsWith(ENCRYPTED_SUFFIX); - if (isEncryptedSuffix) { - preferencesEditor.putString(key, value); - } else { - KeyStoreHelper.SealedData encryptedSecret = KeyStoreHelper.seal(value.getBytes()); - preferencesEditor.putString(key+ENCRYPTED_SUFFIX, encryptedSecret.serialize()); - } - } else { + boolean isEncryptedSuffix = key.endsWith(ENCRYPTED_SUFFIX); + if (isEncryptedSuffix) { preferencesEditor.putString(key, value); + } else { + KeyStoreHelper.SealedData encryptedSecret = KeyStoreHelper.seal(value.getBytes()); + preferencesEditor.putString(key+ENCRYPTED_SUFFIX, encryptedSecret.serialize()); } + if (!preferencesEditor.commit()) throw new AssertionError("failed to save identity key/value to shared preferences"); } diff --git a/app/src/main/java/org/thoughtcrime/securesms/crypto/ModernEncryptingPartOutputStream.java b/app/src/main/java/org/thoughtcrime/securesms/crypto/ModernEncryptingPartOutputStream.java index ab4efe84a9d..0bd62f21431 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/crypto/ModernEncryptingPartOutputStream.java +++ b/app/src/main/java/org/thoughtcrime/securesms/crypto/ModernEncryptingPartOutputStream.java @@ -1,6 +1,8 @@ package org.thoughtcrime.securesms.crypto; +import static org.session.libsignal.utilities.Util.SECURE_RANDOM; + import androidx.annotation.NonNull; import android.util.Pair; @@ -11,7 +13,6 @@ import java.security.InvalidAlgorithmParameterException; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; -import java.security.SecureRandom; import javax.crypto.Cipher; import javax.crypto.CipherOutputStream; @@ -31,7 +32,7 @@ public static Pair createFor(@NonNull AttachmentSecret att throws IOException { byte[] random = new byte[32]; - new SecureRandom().nextBytes(random); + SECURE_RANDOM.nextBytes(random); try { Mac mac = Mac.getInstance("HmacSHA256"); diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/AttachmentDatabase.java b/app/src/main/java/org/thoughtcrime/securesms/database/AttachmentDatabase.java index 45172e2f6fe..5d7301261e7 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/AttachmentDatabase.java +++ b/app/src/main/java/org/thoughtcrime/securesms/database/AttachmentDatabase.java @@ -966,11 +966,6 @@ class ThumbnailFetchCallable implements Callable { @SuppressLint("NewApi") private ThumbnailData generateVideoThumbnail(AttachmentId attachmentId) { - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) { - Log.w(TAG, "Video thumbnails not supported..."); - return null; - } - DataInfo dataInfo = getAttachmentDataFileInfo(attachmentId, DATA); if (dataInfo == null) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/GroupDatabase.java b/app/src/main/java/org/thoughtcrime/securesms/database/GroupDatabase.java index 66d01114ef1..6e1f72c5680 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/GroupDatabase.java +++ b/app/src/main/java/org/thoughtcrime/securesms/database/GroupDatabase.java @@ -1,5 +1,7 @@ package org.thoughtcrime.securesms.database; +import static org.session.libsignal.utilities.Util.SECURE_RANDOM; + import android.annotation.SuppressLint; import android.content.ContentValues; import android.content.Context; @@ -26,7 +28,6 @@ import org.thoughtcrime.securesms.util.BitmapUtil; import java.io.Closeable; -import java.security.SecureRandom; import java.util.Collections; import java.util.LinkedList; import java.util.List; @@ -303,7 +304,7 @@ public void updateProfilePicture(String groupID, Bitmap newValue) { public void updateProfilePicture(String groupID, byte[] newValue) { long avatarId; - if (newValue != null) avatarId = Math.abs(new SecureRandom().nextLong()); + if (newValue != null) avatarId = Math.abs(SECURE_RANDOM.nextLong()); else avatarId = 0; @@ -458,12 +459,6 @@ public void setActive(String groupId, boolean active) { database.update(TABLE_NAME, values, GROUP_ID + " = ?", new String[] {groupId}); } - public byte[] allocateGroupId() { - byte[] groupId = new byte[16]; - new SecureRandom().nextBytes(groupId); - return groupId; - } - public boolean hasGroup(@NonNull String groupId) { try (Cursor cursor = databaseHelper.getReadableDatabase().rawQuery( "SELECT 1 FROM " + TABLE_NAME + " WHERE " + GROUP_ID + " = ? LIMIT 1", diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/LokiAPIDatabase.kt b/app/src/main/java/org/thoughtcrime/securesms/database/LokiAPIDatabase.kt index f1f999242c7..ff22ae14e33 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/LokiAPIDatabase.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/LokiAPIDatabase.kt @@ -166,8 +166,6 @@ class LokiAPIDatabase(context: Context, helper: SQLCipherOpenHelper) : Database( const val RESET_SEQ_NO = "UPDATE $lastMessageServerIDTable SET $lastMessageServerID = 0;" - const val EMPTY_VERSION = "0.0.0" - // endregion } @@ -175,15 +173,7 @@ class LokiAPIDatabase(context: Context, helper: SQLCipherOpenHelper) : Database( val database = databaseHelper.readableDatabase return database.get(snodePoolTable, "${Companion.dummyKey} = ?", wrap("dummy_key")) { cursor -> val snodePoolAsString = cursor.getString(cursor.getColumnIndexOrThrow(snodePool)) - snodePoolAsString.split(", ").mapNotNull { snodeAsString -> - val components = snodeAsString.split("-") - val address = components[0] - val port = components.getOrNull(1)?.toIntOrNull() ?: return@mapNotNull null - val ed25519Key = components.getOrNull(2) ?: return@mapNotNull null - val x25519Key = components.getOrNull(3) ?: return@mapNotNull null - val version = components.getOrNull(4) ?: EMPTY_VERSION - Snode(address, port, Snode.KeySet(ed25519Key, x25519Key), version) - } + snodePoolAsString.split(", ").mapNotNull(::Snode) }?.toSet() ?: setOf() } @@ -231,18 +221,7 @@ class LokiAPIDatabase(context: Context, helper: SQLCipherOpenHelper) : Database( val database = databaseHelper.readableDatabase fun get(indexPath: String): Snode? { return database.get(onionRequestPathTable, "${Companion.indexPath} = ?", wrap(indexPath)) { cursor -> - val snodeAsString = cursor.getString(cursor.getColumnIndexOrThrow(snode)) - val components = snodeAsString.split("-") - val address = components[0] - val port = components.getOrNull(1)?.toIntOrNull() - val ed25519Key = components.getOrNull(2) - val x25519Key = components.getOrNull(3) - val version = components.getOrNull(4) ?: EMPTY_VERSION - if (port != null && ed25519Key != null && x25519Key != null) { - Snode(address, port, Snode.KeySet(ed25519Key, x25519Key), version) - } else { - null - } + Snode(cursor.getString(cursor.getColumnIndexOrThrow(snode))) } } val result = mutableListOf>() @@ -276,15 +255,7 @@ class LokiAPIDatabase(context: Context, helper: SQLCipherOpenHelper) : Database( val database = databaseHelper.readableDatabase return database.get(swarmTable, "${Companion.swarmPublicKey} = ?", wrap(publicKey)) { cursor -> val swarmAsString = cursor.getString(cursor.getColumnIndexOrThrow(swarm)) - swarmAsString.split(", ").mapNotNull { targetAsString -> - val components = targetAsString.split("-") - val address = components[0] - val port = components.getOrNull(1)?.toIntOrNull() ?: return@mapNotNull null - val ed25519Key = components.getOrNull(2) ?: return@mapNotNull null - val x25519Key = components.getOrNull(3) ?: return@mapNotNull null - val version = components.getOrNull(4) ?: EMPTY_VERSION - Snode(address, port, Snode.KeySet(ed25519Key, x25519Key), version) - } + swarmAsString.split(", ").mapNotNull(::Snode) }?.toSet() } diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/MmsDatabase.kt b/app/src/main/java/org/thoughtcrime/securesms/database/MmsDatabase.kt index 9d3a6c9c184..5a2a9155de0 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/MmsDatabase.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/MmsDatabase.kt @@ -46,11 +46,11 @@ import org.session.libsession.utilities.IdentityKeyMismatchList import org.session.libsession.utilities.NetworkFailure import org.session.libsession.utilities.NetworkFailureList import org.session.libsession.utilities.TextSecurePreferences.Companion.isReadReceiptsEnabled -import org.session.libsession.utilities.Util.toIsoBytes import org.session.libsession.utilities.recipients.Recipient import org.session.libsignal.utilities.JsonUtil import org.session.libsignal.utilities.Log import org.session.libsignal.utilities.ThreadUtils.queue +import org.session.libsignal.utilities.Util.SECURE_RANDOM import org.session.libsignal.utilities.guava.Optional import org.thoughtcrime.securesms.attachments.MmsNotificationAttachment import org.thoughtcrime.securesms.database.SmsDatabase.InsertListener @@ -66,7 +66,6 @@ import org.thoughtcrime.securesms.mms.SlideDeck import org.thoughtcrime.securesms.util.asSequence import java.io.Closeable import java.io.IOException -import java.security.SecureRandom import java.util.LinkedList class MmsDatabase(context: Context, databaseHelper: SQLCipherOpenHelper) : MessagingDatabase(context, databaseHelper) { @@ -1200,7 +1199,7 @@ class MmsDatabase(context: Context, databaseHelper: SQLCipherOpenHelper) : Messa inner class OutgoingMessageReader(private val message: OutgoingMediaMessage?, private val threadId: Long) { - private val id = SecureRandom().nextLong() + private val id = SECURE_RANDOM.nextLong() val current: MessageRecord get() { val slideDeck = SlideDeck(context, message!!.attachments) diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/SmsDatabase.java b/app/src/main/java/org/thoughtcrime/securesms/database/SmsDatabase.java index 84b94418342..f02498112fa 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/SmsDatabase.java +++ b/app/src/main/java/org/thoughtcrime/securesms/database/SmsDatabase.java @@ -17,6 +17,8 @@ */ package org.thoughtcrime.securesms.database; +import static org.session.libsignal.utilities.Util.SECURE_RANDOM; + import android.content.ContentValues; import android.content.Context; import android.database.Cursor; @@ -49,7 +51,6 @@ import org.thoughtcrime.securesms.dependencies.DatabaseComponent; import java.io.Closeable; import java.io.IOException; -import java.security.SecureRandom; import java.util.Arrays; import java.util.Collections; import java.util.LinkedList; @@ -784,7 +785,7 @@ public class OutgoingMessageReader { public OutgoingMessageReader(OutgoingTextMessage message, long threadId) { this.message = message; this.threadId = threadId; - this.id = new SecureRandom().nextLong(); + this.id = SECURE_RANDOM.nextLong(); } public MessageRecord getCurrent() { diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/helpers/SQLCipherOpenHelper.java b/app/src/main/java/org/thoughtcrime/securesms/database/helpers/SQLCipherOpenHelper.java index cd1988e83ff..9ee3a6957c4 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/helpers/SQLCipherOpenHelper.java +++ b/app/src/main/java/org/thoughtcrime/securesms/database/helpers/SQLCipherOpenHelper.java @@ -252,11 +252,9 @@ public static void migrateSqlCipher3To4IfNeeded(@NonNull Context context, @NonNu NotificationManager notificationManager = context.getSystemService(NotificationManager.class); String channelId = context.getString(R.string.NotificationChannel_failures); - if (NotificationChannels.supported()) { - NotificationChannel channel = new NotificationChannel(channelId, channelId, NotificationManager.IMPORTANCE_HIGH); - channel.enableVibration(true); - notificationManager.createNotificationChannel(channel); - } + NotificationChannel channel = new NotificationChannel(channelId, channelId, NotificationManager.IMPORTANCE_HIGH); + channel.enableVibration(true); + notificationManager.createNotificationChannel(channel); NotificationCompat.Builder builder = new NotificationCompat.Builder(context, channelId) .setSmallIcon(R.drawable.ic_notification) @@ -266,10 +264,6 @@ public static void migrateSqlCipher3To4IfNeeded(@NonNull Context context, @NonNu .setContentText(context.getString(R.string.ErrorNotifier_migration_downgrade)) .setAutoCancel(true); - if (!NotificationChannels.supported()) { - builder.setPriority(NotificationCompat.PRIORITY_HIGH); - } - notificationManager.notify(5874, builder.build()); // Throw the error (app will crash but there is nothing else we can do unfortunately) diff --git a/app/src/main/java/org/thoughtcrime/securesms/giph/ui/GiphyAdapter.java b/app/src/main/java/org/thoughtcrime/securesms/giph/ui/GiphyAdapter.java index 8972fd8e889..b884ef6a197 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/giph/ui/GiphyAdapter.java +++ b/app/src/main/java/org/thoughtcrime/securesms/giph/ui/GiphyAdapter.java @@ -29,8 +29,8 @@ import org.session.libsignal.utilities.Log; import org.thoughtcrime.securesms.giph.model.ChunkedImageUrl; import org.thoughtcrime.securesms.giph.model.GiphyImage; -import org.thoughtcrime.securesms.mms.GlideApp; -import org.thoughtcrime.securesms.mms.GlideRequests; +import com.bumptech.glide.Glide; +import com.bumptech.glide.RequestManager; import java.util.List; import java.util.concurrent.ExecutionException; @@ -43,7 +43,7 @@ class GiphyAdapter extends RecyclerView.Adapter { private static final String TAG = GiphyAdapter.class.getSimpleName(); private final Context context; - private final GlideRequests glideRequests; + private final RequestManager glideRequests; private List images; private OnItemClickListener listener; @@ -117,7 +117,7 @@ public synchronized void setModelReady() { } } - GiphyAdapter(@NonNull Context context, @NonNull GlideRequests glideRequests, @NonNull List images) { + GiphyAdapter(@NonNull Context context, @NonNull RequestManager glideRequests, @NonNull List images) { this.context = context.getApplicationContext(); this.glideRequests = glideRequests; this.images = images; @@ -150,7 +150,7 @@ public void onBindViewHolder(@NonNull GiphyViewHolder holder, int position) { holder.thumbnail.setAspectRatio(image.getGifAspectRatio()); holder.gifProgress.setVisibility(View.GONE); - RequestBuilder thumbnailRequest = GlideApp.with(context) + RequestBuilder thumbnailRequest = Glide.with(context) .load(new ChunkedImageUrl(image.getStillUrl(), image.getStillSize())) .diskCacheStrategy(DiskCacheStrategy.NONE); diff --git a/app/src/main/java/org/thoughtcrime/securesms/giph/ui/GiphyFragment.java b/app/src/main/java/org/thoughtcrime/securesms/giph/ui/GiphyFragment.java index 5e401c23fc7..d4b1d642dc7 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/giph/ui/GiphyFragment.java +++ b/app/src/main/java/org/thoughtcrime/securesms/giph/ui/GiphyFragment.java @@ -19,7 +19,7 @@ import org.thoughtcrime.securesms.giph.model.GiphyImage; import org.thoughtcrime.securesms.giph.net.GiphyLoader; import org.thoughtcrime.securesms.giph.util.InfiniteScrollListener; -import org.thoughtcrime.securesms.mms.GlideApp; +import com.bumptech.glide.Glide; import org.session.libsession.utilities.TextSecurePreferences; import org.session.libsession.utilities.ViewUtil; @@ -54,7 +54,7 @@ public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup viewGroup, public void onActivityCreated(Bundle bundle) { super.onActivityCreated(bundle); - this.giphyAdapter = new GiphyAdapter(getActivity(), GlideApp.with(this), new LinkedList<>()); + this.giphyAdapter = new GiphyAdapter(getActivity(), Glide.with(this), new LinkedList<>()); this.giphyAdapter.setListener(this); setLayoutManager(TextSecurePreferences.isGifSearchInGridLayout(getContext())); diff --git a/app/src/main/java/org/thoughtcrime/securesms/glide/PaddedHeadersInterceptor.java b/app/src/main/java/org/thoughtcrime/securesms/glide/PaddedHeadersInterceptor.java index 5d0ab584b76..9c347eb414e 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/glide/PaddedHeadersInterceptor.java +++ b/app/src/main/java/org/thoughtcrime/securesms/glide/PaddedHeadersInterceptor.java @@ -1,9 +1,10 @@ package org.thoughtcrime.securesms.glide; +import static org.session.libsignal.utilities.Util.SECURE_RANDOM; + import androidx.annotation.NonNull; import java.io.IOException; -import java.security.SecureRandom; import okhttp3.Headers; import okhttp3.Interceptor; @@ -30,15 +31,15 @@ public class PaddedHeadersInterceptor implements Interceptor { private @NonNull Headers getPaddedHeaders(@NonNull Headers headers) { return headers.newBuilder() - .add(PADDING_HEADER, getRandomString(new SecureRandom(), MIN_RANDOM_BYTES, MAX_RANDOM_BYTES)) + .add(PADDING_HEADER, getRandomString(MIN_RANDOM_BYTES, MAX_RANDOM_BYTES)) .build(); } - private static @NonNull String getRandomString(@NonNull SecureRandom secureRandom, int minLength, int maxLength) { - char[] buffer = new char[secureRandom.nextInt(maxLength - minLength) + minLength]; + private static @NonNull String getRandomString(int minLength, int maxLength) { + char[] buffer = new char[SECURE_RANDOM.nextInt(maxLength - minLength) + minLength]; for (int i = 0 ; i < buffer.length; i++) { - buffer[i] = (char) (secureRandom.nextInt(74) + 48); // Random char from 0-Z + buffer[i] = (char) (SECURE_RANDOM.nextInt(74) + 48); // Random char from 0-Z } return new String(buffer); diff --git a/app/src/main/java/org/thoughtcrime/securesms/groups/CreateGroupFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/groups/CreateGroupFragment.kt index 7bfea9aab05..acb3af8db4a 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/groups/CreateGroupFragment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/groups/CreateGroupFragment.kt @@ -29,7 +29,7 @@ import org.thoughtcrime.securesms.conversation.start.StartConversationDelegate import org.thoughtcrime.securesms.conversation.v2.ConversationActivityV2 import org.thoughtcrime.securesms.dependencies.DatabaseComponent import org.thoughtcrime.securesms.keyboard.emoji.KeyboardPageSearchView -import org.thoughtcrime.securesms.mms.GlideApp +import com.bumptech.glide.Glide import org.thoughtcrime.securesms.util.fadeIn import org.thoughtcrime.securesms.util.fadeOut import javax.inject.Inject @@ -55,7 +55,7 @@ class CreateGroupFragment : Fragment() { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - val adapter = SelectContactsAdapter(requireContext(), GlideApp.with(requireContext())) + val adapter = SelectContactsAdapter(requireContext(), Glide.with(requireContext())) binding.backButton.setOnClickListener { delegate.onDialogBackPressed() } binding.closeButton.setOnClickListener { delegate.onDialogClosePressed() } binding.contactSearch.callbacks = object : KeyboardPageSearchView.Callbacks { diff --git a/app/src/main/java/org/thoughtcrime/securesms/groups/EditClosedGroupActivity.kt b/app/src/main/java/org/thoughtcrime/securesms/groups/EditClosedGroupActivity.kt index da982589c28..2d9192ac7a9 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/groups/EditClosedGroupActivity.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/groups/EditClosedGroupActivity.kt @@ -37,7 +37,7 @@ import org.thoughtcrime.securesms.database.Storage import org.thoughtcrime.securesms.dependencies.ConfigFactory import org.thoughtcrime.securesms.dependencies.DatabaseComponent import org.thoughtcrime.securesms.groups.ClosedGroupManager.updateLegacyGroup -import org.thoughtcrime.securesms.mms.GlideApp +import com.bumptech.glide.Glide import org.thoughtcrime.securesms.util.fadeIn import org.thoughtcrime.securesms.util.fadeOut import java.io.IOException @@ -76,9 +76,9 @@ class EditClosedGroupActivity : PassphraseRequiredActionBarActivity() { private val memberListAdapter by lazy { if (isSelfAdmin) - EditClosedGroupMembersAdapter(this, GlideApp.with(this), isSelfAdmin, this::onMemberClick) + EditClosedGroupMembersAdapter(this, Glide.with(this), isSelfAdmin, this::onMemberClick) else - EditClosedGroupMembersAdapter(this, GlideApp.with(this), isSelfAdmin) + EditClosedGroupMembersAdapter(this, Glide.with(this), isSelfAdmin) } private lateinit var mainContentContainer: LinearLayout diff --git a/app/src/main/java/org/thoughtcrime/securesms/groups/EditClosedGroupMembersAdapter.kt b/app/src/main/java/org/thoughtcrime/securesms/groups/EditClosedGroupMembersAdapter.kt index b2d0f6255a4..5127e3be724 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/groups/EditClosedGroupMembersAdapter.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/groups/EditClosedGroupMembersAdapter.kt @@ -5,13 +5,13 @@ import androidx.recyclerview.widget.RecyclerView import android.view.ViewGroup import org.session.libsession.utilities.Address import org.thoughtcrime.securesms.contacts.UserView -import org.thoughtcrime.securesms.mms.GlideRequests +import com.bumptech.glide.RequestManager import org.session.libsession.utilities.recipients.Recipient import org.session.libsession.utilities.TextSecurePreferences class EditClosedGroupMembersAdapter( private val context: Context, - private val glide: GlideRequests, + private val glide: RequestManager, private val admin: Boolean, private val memberClickListener: ((String) -> Unit)? = null ) : RecyclerView.Adapter() { diff --git a/app/src/main/java/org/thoughtcrime/securesms/home/HomeActivity.kt b/app/src/main/java/org/thoughtcrime/securesms/home/HomeActivity.kt index b534e6c9d97..2d683abe8fb 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/home/HomeActivity.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/home/HomeActivity.kt @@ -62,8 +62,8 @@ import org.thoughtcrime.securesms.home.search.GlobalSearchInputLayout import org.thoughtcrime.securesms.home.search.GlobalSearchResult import org.thoughtcrime.securesms.home.search.GlobalSearchViewModel import org.thoughtcrime.securesms.messagerequests.MessageRequestsActivity -import org.thoughtcrime.securesms.mms.GlideApp -import org.thoughtcrime.securesms.mms.GlideRequests +import com.bumptech.glide.Glide +import com.bumptech.glide.RequestManager import org.thoughtcrime.securesms.notifications.PushRegistry import org.thoughtcrime.securesms.permissions.Permissions import org.thoughtcrime.securesms.preferences.SettingsActivity @@ -89,7 +89,7 @@ class HomeActivity : PassphraseRequiredActionBarActivity(), GlobalSearchInputLayout.GlobalSearchInputLayoutListener { private lateinit var binding: ActivityHomeBinding - private lateinit var glide: GlideRequests + private lateinit var glide: RequestManager @Inject lateinit var threadDb: ThreadDatabase @Inject lateinit var mmsSmsDatabase: MmsSmsDatabase @@ -148,7 +148,7 @@ class HomeActivity : PassphraseRequiredActionBarActivity(), // Set custom toolbar setSupportActionBar(binding.toolbar) // Set up Glide - glide = GlideApp.with(this) + glide = Glide.with(this) // Set up toolbar buttons binding.profileButton.setOnClickListener { openSettings() } binding.searchViewContainer.setOnClickListener { diff --git a/app/src/main/java/org/thoughtcrime/securesms/home/HomeAdapter.kt b/app/src/main/java/org/thoughtcrime/securesms/home/HomeAdapter.kt index 5410df9d478..453d15050a5 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/home/HomeAdapter.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/home/HomeAdapter.kt @@ -7,10 +7,10 @@ import androidx.recyclerview.widget.DiffUtil import androidx.recyclerview.widget.ListUpdateCallback import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView.NO_ID +import com.bumptech.glide.RequestManager import network.loki.messenger.R import network.loki.messenger.databinding.ViewMessageRequestBannerBinding import org.thoughtcrime.securesms.dependencies.ConfigFactory -import org.thoughtcrime.securesms.mms.GlideRequests class HomeAdapter( private val context: Context, @@ -74,7 +74,7 @@ class HomeAdapter( return data.threads[offsetPosition].threadId } - lateinit var glide: GlideRequests + lateinit var glide: RequestManager override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder = when (viewType) { @@ -104,7 +104,6 @@ class HomeAdapter( holder.binding.run { messageRequests?.let { unreadCountTextView.text = it.count - timestampTextView.text = it.timestamp } } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/home/PathActivity.kt b/app/src/main/java/org/thoughtcrime/securesms/home/PathActivity.kt index db0c4d11cc5..f399b602b43 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/home/PathActivity.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/home/PathActivity.kt @@ -15,6 +15,7 @@ import android.widget.RelativeLayout import android.widget.TextView import android.widget.Toast import androidx.annotation.ColorRes +import androidx.core.content.ContextCompat import androidx.localbroadcastmanager.content.LocalBroadcastManager import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.GlobalScope @@ -38,7 +39,6 @@ import org.thoughtcrime.securesms.util.disableClipping import org.thoughtcrime.securesms.util.fadeIn import org.thoughtcrime.securesms.util.fadeOut import org.thoughtcrime.securesms.util.getAccentColor -import org.thoughtcrime.securesms.util.getColorWithID class PathActivity : PassphraseRequiredActionBarActivity() { private lateinit var binding: ActivityPathBinding @@ -264,8 +264,8 @@ class PathActivity : PassphraseRequiredActionBarActivity() { job?.cancel() job = GlobalScope.launch { withContext(Dispatchers.Main) { + delay(dotAnimationStartDelay) while (isActive) { - delay(dotAnimationStartDelay) expand() delay(EXPAND_ANIM_DELAY_MILLS) collapse() @@ -283,7 +283,7 @@ class PathActivity : PassphraseRequiredActionBarActivity() { private fun expand() { dotView.animateSizeChange(R.dimen.path_row_dot_size, R.dimen.path_row_expanded_dot_size) @ColorRes val startColorID = if (UiModeUtilities.isDayUiMode(context)) R.color.transparent_black_30 else R.color.black - val startColor = context.resources.getColorWithID(startColorID, context.theme) + val startColor = ContextCompat.getColor(context, startColorID) val endColor = context.getAccentColor() GlowViewUtilities.animateShadowColorChange(dotView, startColor, endColor) } @@ -292,7 +292,7 @@ class PathActivity : PassphraseRequiredActionBarActivity() { dotView.animateSizeChange(R.dimen.path_row_expanded_dot_size, R.dimen.path_row_dot_size) @ColorRes val endColorID = if (UiModeUtilities.isDayUiMode(context)) R.color.transparent_black_30 else R.color.black val startColor = context.getAccentColor() - val endColor = context.resources.getColorWithID(endColorID, context.theme) + val endColor = ContextCompat.getColor(context, endColorID) GlowViewUtilities.animateShadowColorChange(dotView, startColor, endColor) } diff --git a/app/src/main/java/org/thoughtcrime/securesms/home/PathStatusView.kt b/app/src/main/java/org/thoughtcrime/securesms/home/PathStatusView.kt index 7ab7bfb5084..1c31a2ee178 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/home/PathStatusView.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/home/PathStatusView.kt @@ -9,6 +9,7 @@ import android.graphics.Paint import android.util.AttributeSet import android.view.View import androidx.annotation.ColorInt +import androidx.core.content.ContextCompat import androidx.lifecycle.coroutineScope import androidx.localbroadcastmanager.content.LocalBroadcastManager import kotlinx.coroutines.Dispatchers @@ -17,7 +18,6 @@ import kotlinx.coroutines.withContext import network.loki.messenger.R import org.session.libsession.snode.OnionRequestAPI import org.thoughtcrime.securesms.conversation.v2.ViewUtil -import org.thoughtcrime.securesms.util.getColorWithID import org.thoughtcrime.securesms.util.toPx class PathStatusView : View { @@ -104,7 +104,7 @@ class PathStatusView : View { sessionShadowColor = hasPathsColor } else { setBackgroundResource(R.drawable.paths_building_dot) - val pathsBuildingColor = resources.getColorWithID(R.color.paths_building, context.theme) + val pathsBuildingColor = ContextCompat.getColor(context, R.color.paths_building) mainColor = pathsBuildingColor sessionShadowColor = pathsBuildingColor } diff --git a/app/src/main/java/org/thoughtcrime/securesms/home/search/GlobalSearchViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/home/search/GlobalSearchViewModel.kt index fb4a61a5587..4a25556a42f 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/home/search/GlobalSearchViewModel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/home/search/GlobalSearchViewModel.kt @@ -10,21 +10,19 @@ import kotlinx.coroutines.delay import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.buffer import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.mapLatest import kotlinx.coroutines.flow.merge -import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.launch import kotlinx.coroutines.plus -import org.session.libsignal.utilities.SettableFuture import org.thoughtcrime.securesms.search.SearchRepository import org.thoughtcrime.securesms.search.model.SearchResult -import java.util.concurrent.TimeUnit import javax.inject.Inject +import kotlin.coroutines.resume +import kotlin.coroutines.suspendCoroutine @OptIn(ExperimentalCoroutinesApi::class) @HiltViewModel @@ -47,11 +45,8 @@ class GlobalSearchViewModel @Inject constructor( // User input delay in case we get a new query within a few hundred ms this // coroutine will be cancelled and the expensive query will not be run. delay(300) - val settableFuture = SettableFuture() - searchRepository.query(query.toString(), settableFuture::set) try { - // search repository doesn't play nicely with suspend functions (yet) - settableFuture.get(10_000, TimeUnit.MILLISECONDS).toGlobalSearchResult() + searchRepository.suspendQuery(query.toString()).toGlobalSearchResult() } catch (e: Exception) { GlobalSearchResult(query.toString()) } @@ -69,6 +64,12 @@ class GlobalSearchViewModel @Inject constructor( } } +private suspend fun SearchRepository.suspendQuery(query: String): SearchResult { + return suspendCoroutine { cont -> + query(query, cont::resume) + } +} + /** * Re-emit whenever refreshes emits. * */ diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobmanager/Constraint.java b/app/src/main/java/org/thoughtcrime/securesms/jobmanager/Constraint.java index 854804d746c..dd766bbbb32 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobmanager/Constraint.java +++ b/app/src/main/java/org/thoughtcrime/securesms/jobmanager/Constraint.java @@ -8,11 +8,6 @@ public interface Constraint { boolean isMet(); - @NonNull String getFactoryKey(); - - @RequiresApi(26) - void applyToJobInfo(@NonNull JobInfo.Builder jobInfoBuilder); - interface Factory { T create(); } diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobmanager/KeepAliveService.java b/app/src/main/java/org/thoughtcrime/securesms/jobmanager/KeepAliveService.java deleted file mode 100644 index 121686671ce..00000000000 --- a/app/src/main/java/org/thoughtcrime/securesms/jobmanager/KeepAliveService.java +++ /dev/null @@ -1,24 +0,0 @@ -package org.thoughtcrime.securesms.jobmanager; - -import android.app.Service; -import android.content.Intent; -import android.os.IBinder; -import androidx.annotation.Nullable; - -/** - * Service that keeps the application in memory while the app is closed. - * - * Important: Should only be used on API < 26. - */ -public class KeepAliveService extends Service { - - @Override - public @Nullable IBinder onBind(Intent intent) { - return null; - } - - @Override - public int onStartCommand(Intent intent, int flags, int startId) { - return START_STICKY; - } -} diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobmanager/impl/NetworkConstraint.java b/app/src/main/java/org/thoughtcrime/securesms/jobmanager/impl/NetworkConstraint.java index 1546883b856..7b128bf9fe8 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobmanager/impl/NetworkConstraint.java +++ b/app/src/main/java/org/thoughtcrime/securesms/jobmanager/impl/NetworkConstraint.java @@ -28,17 +28,6 @@ public boolean isMet() { return activeNetworkInfo != null && activeNetworkInfo.isConnected(); } - @Override - public @NonNull String getFactoryKey() { - return KEY; - } - - @RequiresApi(26) - @Override - public void applyToJobInfo(@NonNull JobInfo.Builder jobInfoBuilder) { - jobInfoBuilder.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY); - } - public static final class Factory implements Constraint.Factory { private final Application application; diff --git a/app/src/main/java/org/thoughtcrime/securesms/logging/LogFile.java b/app/src/main/java/org/thoughtcrime/securesms/logging/LogFile.java index 909f19e08c0..cfe6cc38093 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/logging/LogFile.java +++ b/app/src/main/java/org/thoughtcrime/securesms/logging/LogFile.java @@ -1,6 +1,7 @@ package org.thoughtcrime.securesms.logging; import static org.session.libsignal.crypto.CipherUtil.CIPHER_LOCK; +import static org.session.libsignal.utilities.Util.SECURE_RANDOM; import androidx.annotation.NonNull; @@ -17,7 +18,6 @@ import java.security.InvalidAlgorithmParameterException; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; -import java.security.SecureRandom; import javax.crypto.BadPaddingException; import javax.crypto.Cipher; @@ -64,7 +64,7 @@ public static class Writer { } void writeEntry(@NonNull String entry) throws IOException { - new SecureRandom().nextBytes(ivBuffer); + SECURE_RANDOM.nextBytes(ivBuffer); byte[] plaintext = entry.getBytes(); try { diff --git a/app/src/main/java/org/thoughtcrime/securesms/logging/LogSecretProvider.java b/app/src/main/java/org/thoughtcrime/securesms/logging/LogSecretProvider.java index aabd2811db0..533e1a1d234 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/logging/LogSecretProvider.java +++ b/app/src/main/java/org/thoughtcrime/securesms/logging/LogSecretProvider.java @@ -1,5 +1,7 @@ package org.thoughtcrime.securesms.logging; +import static org.session.libsignal.utilities.Util.SECURE_RANDOM; + import android.content.Context; import android.os.Build; import androidx.annotation.NonNull; @@ -9,7 +11,6 @@ import org.session.libsession.utilities.TextSecurePreferences; import java.io.IOException; -import java.security.SecureRandom; class LogSecretProvider { @@ -31,25 +32,16 @@ private static byte[] parseUnencryptedSecret(String secret) { } private static byte[] parseEncryptedSecret(String secret) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - KeyStoreHelper.SealedData encryptedSecret = KeyStoreHelper.SealedData.fromString(secret); - return KeyStoreHelper.unseal(encryptedSecret); - } else { - throw new AssertionError("OS downgrade not supported. KeyStore sealed data exists on platform < M!"); - } + KeyStoreHelper.SealedData encryptedSecret = KeyStoreHelper.SealedData.fromString(secret); + return KeyStoreHelper.unseal(encryptedSecret); } private static byte[] createAndStoreSecret(@NonNull Context context) { - SecureRandom random = new SecureRandom(); byte[] secret = new byte[32]; - random.nextBytes(secret); + SECURE_RANDOM.nextBytes(secret); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - KeyStoreHelper.SealedData encryptedSecret = KeyStoreHelper.seal(secret); - TextSecurePreferences.setLogEncryptedSecret(context, encryptedSecret.serialize()); - } else { - TextSecurePreferences.setLogUnencryptedSecret(context, Base64.encodeBytes(secret)); - } + KeyStoreHelper.SealedData encryptedSecret = KeyStoreHelper.seal(secret); + TextSecurePreferences.setLogEncryptedSecret(context, encryptedSecret.serialize()); return secret; } diff --git a/app/src/main/java/org/thoughtcrime/securesms/media/AttachmentHeader.kt b/app/src/main/java/org/thoughtcrime/securesms/media/AttachmentHeader.kt new file mode 100644 index 00000000000..7191450ac4e --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/media/AttachmentHeader.kt @@ -0,0 +1,30 @@ +package org.thoughtcrime.securesms.media + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import org.thoughtcrime.securesms.ui.theme.LocalColors +import org.thoughtcrime.securesms.ui.theme.LocalDimensions +import org.thoughtcrime.securesms.ui.theme.LocalType + +@Composable +fun AttachmentHeader( + text: String, + modifier: Modifier = Modifier +){ + Text( + modifier = modifier + .background(LocalColors.current.background) + .fillMaxWidth() + .padding( + horizontal = LocalDimensions.current.smallSpacing, + vertical = LocalDimensions.current.xsSpacing + ), + text = text, + style = LocalType.current.xl, + color = LocalColors.current.text + ) +} \ No newline at end of file diff --git a/app/src/main/java/org/thoughtcrime/securesms/media/DocumentsPage.kt b/app/src/main/java/org/thoughtcrime/securesms/media/DocumentsPage.kt new file mode 100644 index 00000000000..0f4b8c58b85 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/media/DocumentsPage.kt @@ -0,0 +1,111 @@ +package org.thoughtcrime.securesms.media + +import androidx.compose.foundation.ExperimentalFoundationApi +import androidx.compose.foundation.Image +import androidx.compose.foundation.background +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.items +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.input.nestedscroll.NestedScrollConnection +import androidx.compose.ui.input.nestedscroll.nestedScroll +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.dp +import network.loki.messenger.R +import org.session.libsession.utilities.Util +import org.thoughtcrime.securesms.ui.theme.LocalColors +import org.thoughtcrime.securesms.ui.theme.LocalDimensions +import org.thoughtcrime.securesms.ui.theme.LocalType + +@OptIn(ExperimentalFoundationApi::class) +@Composable +fun DocumentsPage( + nestedScrollConnection: NestedScrollConnection, + content: TabContent?, + onItemClicked: (MediaOverviewItem) -> Unit, +) { + when { + content == null -> { + // Loading + } + + content.isEmpty() -> { + Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) { + Text( + text = stringResource(R.string.media_overview_documents_fragment__no_documents_found), + style = LocalType.current.base, + color = LocalColors.current.text + ) + } + } + + else -> { + LazyColumn( + modifier = Modifier + .nestedScroll(nestedScrollConnection) + .fillMaxSize() + .padding(2.dp), + verticalArrangement = Arrangement.spacedBy(LocalDimensions.current.xxsSpacing) + ) { + for ((bucketTitle, files) in content) { + stickyHeader { + AttachmentHeader(text = bucketTitle) + } + + items(files) { file -> + Row( + modifier = Modifier + .clickable(onClick = { onItemClicked(file) }) + .padding(LocalDimensions.current.smallSpacing), + horizontalArrangement = Arrangement.spacedBy(LocalDimensions.current.xxsSpacing), + verticalAlignment = Alignment.CenterVertically, + ) { + Image( + painterResource(R.drawable.ic_document_large_dark), + contentDescription = null + ) + + Column(verticalArrangement = Arrangement.spacedBy(LocalDimensions.current.xxxsSpacing)) { + Text( + text = file.fileName.orEmpty(), + style = LocalType.current.large, + color = LocalColors.current.text + ) + + Row(modifier = Modifier.fillMaxWidth()) { + Text( + modifier = Modifier.weight(1f), + text = Util.getPrettyFileSize(file.fileSize), + style = LocalType.current.small, + color = LocalColors.current.textSecondary, + textAlign = TextAlign.Start, + ) + + Text( + text = file.date, + style = LocalType.current.small, + color = LocalColors.current.textSecondary, + textAlign = TextAlign.End, + ) + } + } + + } + } + } + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/org/thoughtcrime/securesms/media/FixedTimeBuckets.kt b/app/src/main/java/org/thoughtcrime/securesms/media/FixedTimeBuckets.kt new file mode 100644 index 00000000000..f01276f78ea --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/media/FixedTimeBuckets.kt @@ -0,0 +1,45 @@ +package org.thoughtcrime.securesms.media + +import androidx.annotation.StringRes +import network.loki.messenger.R +import java.time.ZonedDateTime +import java.time.temporal.WeekFields +import java.util.Locale + +/** + * A data structure that describes a series of time points in the past. It's primarily + * used to bucket items into categories like "Today", "Yesterday", "This week", "This month", etc. + * + * Call [getBucketText] to get the appropriate string resource for a given time. If no bucket is + * appropriate, it will return null. + */ +class FixedTimeBuckets( + private val startOfToday: ZonedDateTime, + private val startOfYesterday: ZonedDateTime, + private val startOfThisWeek: ZonedDateTime, + private val startOfThisMonth: ZonedDateTime +) { + constructor(now: ZonedDateTime = ZonedDateTime.now()) : this( + startOfToday = now.toLocalDate().atStartOfDay(now.zone), + startOfYesterday = now.toLocalDate().minusDays(1).atStartOfDay(now.zone), + startOfThisWeek = now.toLocalDate() + .with(WeekFields.of(Locale.getDefault()).dayOfWeek(), 1) + .atStartOfDay(now.zone), + startOfThisMonth = now.toLocalDate().withDayOfMonth(1).atStartOfDay(now.zone) + ) + + /** + * Test the given time against the buckets and return the appropriate string resource the time + * bucket. If no bucket is appropriate, it will return null. + */ + @StringRes + fun getBucketText(time: ZonedDateTime): Int? { + return when { + time >= startOfToday -> R.string.BucketedThreadMedia_Today + time >= startOfYesterday -> R.string.BucketedThreadMedia_Yesterday + time >= startOfThisWeek -> R.string.BucketedThreadMedia_This_week + time >= startOfThisMonth -> R.string.BucketedThreadMedia_This_month + else -> null + } + } +} \ No newline at end of file diff --git a/app/src/main/java/org/thoughtcrime/securesms/media/MediaOverviewActivity.kt b/app/src/main/java/org/thoughtcrime/securesms/media/MediaOverviewActivity.kt new file mode 100644 index 00000000000..7111c000178 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/media/MediaOverviewActivity.kt @@ -0,0 +1,43 @@ +package org.thoughtcrime.securesms.media + +import android.content.Context +import android.content.Intent +import android.os.Bundle +import androidx.activity.viewModels +import androidx.core.content.IntentCompat +import dagger.hilt.android.AndroidEntryPoint +import org.session.libsession.utilities.Address +import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity +import org.thoughtcrime.securesms.ui.setComposeContent +import javax.inject.Inject + +@AndroidEntryPoint +class MediaOverviewActivity : PassphraseRequiredActionBarActivity() { + @Inject + lateinit var viewModelFactory: MediaOverviewViewModel.AssistedFactory + + private val viewModel: MediaOverviewViewModel by viewModels { + viewModelFactory.create(IntentCompat.getParcelableExtra(intent, EXTRA_ADDRESS, Address::class.java)!!) + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + setComposeContent { + MediaOverviewScreen(viewModel, onClose = this::finish) + } + + supportActionBar?.hide() + } + + companion object { + private const val EXTRA_ADDRESS = "address" + + @JvmStatic + fun createIntent(context: Context, address: Address): Intent { + return Intent(context, MediaOverviewActivity::class.java).apply { + putExtra(EXTRA_ADDRESS, address) + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/org/thoughtcrime/securesms/media/MediaOverviewScreen.kt b/app/src/main/java/org/thoughtcrime/securesms/media/MediaOverviewScreen.kt new file mode 100644 index 00000000000..2f727512650 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/media/MediaOverviewScreen.kt @@ -0,0 +1,311 @@ +package org.thoughtcrime.securesms.media + +import android.Manifest.permission.WRITE_EXTERNAL_STORAGE +import android.content.ActivityNotFoundException +import android.os.Build +import android.widget.Toast +import androidx.activity.compose.BackHandler +import androidx.activity.compose.rememberLauncherForActivityResult +import androidx.activity.result.contract.ActivityResultContracts +import androidx.compose.foundation.ExperimentalFoundationApi +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.pager.HorizontalPager +import androidx.compose.foundation.pager.rememberPagerState +import androidx.compose.material3.BasicAlertDialog +import androidx.compose.material3.CircularProgressIndicator +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Scaffold +import androidx.compose.material3.Text +import androidx.compose.material3.TopAppBarDefaults +import androidx.compose.material3.rememberTopAppBarState +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.hapticfeedback.HapticFeedbackType +import androidx.compose.ui.input.nestedscroll.nestedScroll +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.platform.LocalHapticFeedback +import network.loki.messenger.R +import org.thoughtcrime.securesms.ui.AlertDialog +import org.thoughtcrime.securesms.ui.DialogButtonModel +import org.thoughtcrime.securesms.ui.GetString +import org.thoughtcrime.securesms.ui.components.SessionTabRow +import org.thoughtcrime.securesms.ui.theme.LocalColors +import org.thoughtcrime.securesms.ui.theme.LocalDimensions +import org.thoughtcrime.securesms.ui.theme.LocalType + +@OptIn( + ExperimentalFoundationApi::class, ExperimentalMaterial3Api::class, +) +@Composable +fun MediaOverviewScreen( + viewModel: MediaOverviewViewModel, + onClose: () -> Unit, +) { + val selectedItems by viewModel.selectedItemIDs.collectAsState() + val selectionMode by viewModel.inSelectionMode.collectAsState() + val topAppBarState = rememberTopAppBarState() + var showingDeleteConfirmation by remember { mutableStateOf(false) } + var showingSaveAttachmentWarning by remember { mutableStateOf(false) } + val context = LocalContext.current + val requestStoragePermission = + rememberLauncherForActivityResult(ActivityResultContracts.RequestPermission()) { granted -> + if (granted) { + viewModel.onSaveClicked() + } else { + Toast.makeText( + context, + R.string.MediaPreviewActivity_unable_to_write_to_external_storage_without_permission, + Toast.LENGTH_LONG + ).show() + } + } + + // In selection mode, the app bar should not be scrollable and should be pinned + val appBarScrollBehavior = if (selectionMode) { + TopAppBarDefaults.pinnedScrollBehavior(topAppBarState, canScroll = { false }) + } else { + TopAppBarDefaults.enterAlwaysScrollBehavior(topAppBarState) + } + + // Reset the top app bar offset (so that it shows up) when entering selection mode + LaunchedEffect(selectionMode) { + if (selectionMode) { + topAppBarState.heightOffset = 0f + } + } + + BackHandler(onBack = viewModel::onBackClicked) + + // Event handling + LaunchedEffect(viewModel.events) { + viewModel.events.collect { event -> + when (event) { + MediaOverviewEvent.Close -> onClose() + is MediaOverviewEvent.NavigateToActivity -> { + try { + context.startActivity(event.intent) + } catch (e: ActivityNotFoundException) { + Toast.makeText( + context, + R.string.ConversationItem_unable_to_open_media, + Toast.LENGTH_LONG + ).show() + } + } + + is MediaOverviewEvent.ShowSaveAttachmentError -> { + val message = context.resources.getQuantityText( + R.plurals.ConversationFragment_error_while_saving_attachments_to_sd_card, + event.errorCount + ) + Toast.makeText(context, message, Toast.LENGTH_LONG).show() + } + + is MediaOverviewEvent.ShowSaveAttachmentSuccess -> { + val message = if (event.directory.isNotBlank()) { + context.resources.getString( + R.string.SaveAttachmentTask_saved_to, + event.directory + ) + } else { + context.resources.getString(R.string.SaveAttachmentTask_saved) + } + + Toast.makeText(context, message, Toast.LENGTH_LONG).show() + } + } + } + } + + Scaffold( + modifier = Modifier.nestedScroll(appBarScrollBehavior.nestedScrollConnection), + topBar = { + MediaOverviewTopAppBar( + selectionMode = selectionMode, + title = viewModel.title.collectAsState().value, + onBackClicked = viewModel::onBackClicked, + onSaveClicked = { showingSaveAttachmentWarning = true }, + onDeleteClicked = { showingDeleteConfirmation = true }, + onSelectAllClicked = viewModel::onSelectAllClicked, + numSelected = selectedItems.size, + appBarScrollBehavior = appBarScrollBehavior + ) + } + ) { paddings -> + Column( + modifier = Modifier + .padding(paddings) + .fillMaxSize() + ) { + val pagerState = rememberPagerState(pageCount = { MediaOverviewTab.entries.size }) + val selectedTab by viewModel.selectedTab.collectAsState() + + // Apply "selectedTab" view model state to pager + LaunchedEffect(selectedTab) { + pagerState.animateScrollToPage(selectedTab.ordinal) + } + + // Apply "selectedTab" pager state to view model + LaunchedEffect(pagerState.currentPage) { + viewModel.onTabItemClicked(MediaOverviewTab.entries[pagerState.currentPage]) + } + + SessionTabRow( + pagerState = pagerState, + titles = MediaOverviewTab.entries.map { it.titleResId } + ) + + val content = viewModel.mediaListState.collectAsState() + val canLongPress = viewModel.canLongPress.collectAsState().value + + HorizontalPager( + pagerState, + modifier = Modifier + .weight(1f) + .fillMaxWidth() + ) { index -> + when (MediaOverviewTab.entries[index]) { + MediaOverviewTab.Media -> { + val haptics = LocalHapticFeedback.current + + MediaPage( + content = content.value?.mediaContent, + selectedItemIDs = selectedItems, + onItemClicked = viewModel::onItemClicked, + nestedScrollConnection = appBarScrollBehavior.nestedScrollConnection, + onItemLongClicked = if(canLongPress){{ + haptics.performHapticFeedback(HapticFeedbackType.LongPress) + viewModel.onItemLongClicked(it) + }} else null + ) + } + + MediaOverviewTab.Documents -> DocumentsPage( + nestedScrollConnection = appBarScrollBehavior.nestedScrollConnection, + content = content.value?.documentContent, + onItemClicked = viewModel::onItemClicked + ) + } + } + } + } + + if (showingDeleteConfirmation) { + DeleteConfirmationDialog( + onDismissRequest = { showingDeleteConfirmation = false }, + onAccepted = viewModel::onDeleteClicked, + numSelected = selectedItems.size + ) + } + + if (showingSaveAttachmentWarning) { + SaveAttachmentWarningDialog( + onDismissRequest = { showingSaveAttachmentWarning = false }, + onAccepted = { + if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.P) { + requestStoragePermission.launch(WRITE_EXTERNAL_STORAGE) + } else { + viewModel.onSaveClicked() + } + }, + numSelected = selectedItems.size + ) + } + + val showingActionDialog = viewModel.showingActionProgress.collectAsState().value + if (showingActionDialog != null) { + ActionProgressDialog(showingActionDialog) + } +} + +@Composable +private fun SaveAttachmentWarningDialog( + onDismissRequest: () -> Unit, + onAccepted: () -> Unit, + numSelected: Int, +) { + val context = LocalContext.current + AlertDialog( + onDismissRequest = onDismissRequest, + title = context.getString(R.string.ConversationFragment_save_to_sd_card), + text = context.resources.getQuantityString( + R.plurals.ConversationFragment_saving_n_media_to_storage_warning, + numSelected, + numSelected + ), + buttons = listOf( + DialogButtonModel(GetString(R.string.save), onClick = onAccepted), + DialogButtonModel(GetString(android.R.string.cancel), dismissOnClick = true) + ) + ) +} + +@Composable +private fun DeleteConfirmationDialog( + onDismissRequest: () -> Unit, + onAccepted: () -> Unit, + numSelected: Int, +) { + val context = LocalContext.current + AlertDialog( + onDismissRequest = onDismissRequest, + title = context.resources.getQuantityString( + R.plurals.ConversationFragment_delete_selected_messages, numSelected + ), + text = context.resources.getQuantityString( + R.plurals.ConversationFragment_this_will_permanently_delete_all_n_selected_messages, + numSelected, + numSelected, + ), + buttons = listOf( + DialogButtonModel(GetString(R.string.delete), color = LocalColors.current.danger, onClick = onAccepted), + DialogButtonModel(GetString(android.R.string.cancel), dismissOnClick = true) + ) + ) +} + +@Composable +@OptIn(ExperimentalMaterial3Api::class) +private fun ActionProgressDialog( + text: String +) { + BasicAlertDialog( + onDismissRequest = {}, + ) { + Row( + modifier = Modifier + .background(LocalColors.current.background, shape = MaterialTheme.shapes.medium) + .padding(LocalDimensions.current.mediumSpacing), + horizontalArrangement = Arrangement.spacedBy(LocalDimensions.current.smallSpacing), + verticalAlignment = Alignment.CenterVertically, + ) { + CircularProgressIndicator(color = LocalColors.current.primary) + Text( + text, + style = LocalType.current.large, + color = LocalColors.current.text + ) + } + } +} + +private val MediaOverviewTab.titleResId: Int + get() = when (this) { + MediaOverviewTab.Media -> R.string.MediaOverviewActivity_Media + MediaOverviewTab.Documents -> R.string.MediaOverviewActivity_Documents + } + diff --git a/app/src/main/java/org/thoughtcrime/securesms/media/MediaOverviewTopAppBar.kt b/app/src/main/java/org/thoughtcrime/securesms/media/MediaOverviewTopAppBar.kt new file mode 100644 index 00000000000..570885f2123 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/media/MediaOverviewTopAppBar.kt @@ -0,0 +1,59 @@ +package org.thoughtcrime.securesms.media + +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.material3.TopAppBarScrollBehavior +import androidx.compose.runtime.Composable +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource +import network.loki.messenger.R +import org.thoughtcrime.securesms.ui.components.ActionAppBar +import org.thoughtcrime.securesms.ui.components.AppBarBackIcon +import org.thoughtcrime.securesms.ui.theme.LocalColors + +@Composable +@OptIn(ExperimentalMaterial3Api::class) +fun MediaOverviewTopAppBar( + selectionMode: Boolean, + numSelected: Int, + title: String, + onBackClicked: () -> Unit, + onSaveClicked: () -> Unit, + onDeleteClicked: () -> Unit, + onSelectAllClicked: () -> Unit, + appBarScrollBehavior: TopAppBarScrollBehavior +) { + ActionAppBar( + title = title, + actionModeTitle = numSelected.toString(), + navigationIcon = { AppBarBackIcon(onBack = onBackClicked) }, + scrollBehavior = appBarScrollBehavior, + actionMode = selectionMode, + actionModeActions = { + IconButton(onClick = onSaveClicked) { + Icon( + painterResource(R.drawable.ic_baseline_save_24), + contentDescription = stringResource(R.string.save), + tint = LocalColors.current.text, + ) + } + + IconButton(onClick = onDeleteClicked) { + Icon( + painterResource(R.drawable.ic_baseline_delete_24), + contentDescription = stringResource(R.string.delete), + tint = LocalColors.current.text, + ) + } + + IconButton(onClick = onSelectAllClicked) { + Icon( + painterResource(R.drawable.ic_baseline_select_all_24), + contentDescription = stringResource(R.string.MediaOverviewActivity_Select_all), + tint = LocalColors.current.text, + ) + } + } + ) +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/media/MediaOverviewViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/media/MediaOverviewViewModel.kt new file mode 100644 index 00000000000..a0b629737de --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/media/MediaOverviewViewModel.kt @@ -0,0 +1,431 @@ +package org.thoughtcrime.securesms.media + +import android.app.Application +import android.content.Context +import android.content.Intent +import android.net.Uri +import androidx.lifecycle.AndroidViewModel +import androidx.lifecycle.ViewModel +import androidx.lifecycle.ViewModelProvider +import androidx.lifecycle.viewModelScope +import dagger.assisted.Assisted +import dagger.assisted.AssistedInject +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.MutableSharedFlow +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.SharedFlow +import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.onStart +import kotlinx.coroutines.flow.shareIn +import kotlinx.coroutines.flow.stateIn +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext +import network.loki.messenger.R +import org.session.libsession.messaging.messages.control.DataExtractionNotification +import org.session.libsession.messaging.sending_receiving.MessageSender +import org.session.libsession.snode.SnodeAPI +import org.session.libsession.utilities.Address +import org.session.libsession.utilities.recipients.Recipient +import org.thoughtcrime.securesms.MediaPreviewActivity +import org.thoughtcrime.securesms.database.DatabaseContentProviders +import org.thoughtcrime.securesms.database.MediaDatabase +import org.thoughtcrime.securesms.database.MediaDatabase.MediaRecord +import org.thoughtcrime.securesms.database.ThreadDatabase +import org.thoughtcrime.securesms.mms.PartAuthority +import org.thoughtcrime.securesms.mms.Slide +import org.thoughtcrime.securesms.util.AttachmentUtil +import org.thoughtcrime.securesms.util.DateUtils +import org.thoughtcrime.securesms.util.MediaUtil +import org.thoughtcrime.securesms.util.SaveAttachmentTask +import org.thoughtcrime.securesms.util.asSequence +import org.thoughtcrime.securesms.util.observeChanges +import java.time.Instant +import java.time.LocalDate +import java.time.ZoneId +import java.time.ZonedDateTime +import java.time.format.DateTimeFormatter +import java.util.Locale + +class MediaOverviewViewModel( + private val address: Address, + private val application: Application, + private val threadDatabase: ThreadDatabase, + private val mediaDatabase: MediaDatabase +) : AndroidViewModel(application) { + private val timeBuckets by lazy { FixedTimeBuckets() } + private val monthTimeBucketFormatter = + DateTimeFormatter.ofPattern("MMMM yyyy", Locale.getDefault()) + + private val recipient: SharedFlow = application.contentResolver + .observeChanges(DatabaseContentProviders.Attachment.CONTENT_URI) + .onStart { emit(DatabaseContentProviders.Attachment.CONTENT_URI) } + .map { Recipient.from(application, address, false) } + .shareIn(viewModelScope, SharingStarted.Eagerly, replay = 1) + + val title: StateFlow = recipient + .map { it.toShortString() } + .stateIn(viewModelScope, SharingStarted.Eagerly, "") + + val mediaListState: StateFlow = recipient + .map { recipient -> + withContext(Dispatchers.Default) { + val threadId = threadDatabase.getOrCreateThreadIdFor(recipient) + val mediaItems = mediaDatabase.getGalleryMediaForThread(threadId) + .use { cursor -> + cursor.asSequence() + .map { MediaRecord.from(application, it) } + .groupRecordsByTimeBuckets() + } + + val documentItems = mediaDatabase.getDocumentMediaForThread(threadId) + .use { cursor -> + cursor.asSequence() + .map { MediaRecord.from(application, it) } + .groupRecordsByRelativeTime() + } + + MediaOverviewContent( + mediaContent = mediaItems, + documentContent = documentItems, + ) + } + } + .stateIn(viewModelScope, SharingStarted.Eagerly, null) + + private val mutableSelectedItemIDs = MutableStateFlow(emptySet()) + val selectedItemIDs: StateFlow> get() = mutableSelectedItemIDs + + val inSelectionMode: StateFlow = selectedItemIDs + .map { it.isNotEmpty() } + .stateIn(viewModelScope, SharingStarted.Eagerly, mutableSelectedItemIDs.value.isNotEmpty()) + + val canLongPress: StateFlow = inSelectionMode + .map { !it } + .stateIn(viewModelScope, SharingStarted.Eagerly, true) + + private val mutableEvents = MutableSharedFlow() + val events get() = mutableEvents + + private val mutableSelectedTab = MutableStateFlow(MediaOverviewTab.Media) + val selectedTab: StateFlow get() = mutableSelectedTab + + private val mutableShowingActionProgress = MutableStateFlow(null) + val showingActionProgress: StateFlow get() = mutableShowingActionProgress + + private val selectedMedia: Sequence + get() { + val selected = selectedItemIDs.value + return mediaListState.value + ?.mediaContent + ?.asSequence() + .orEmpty() + .flatMap { it.second.asSequence() } + .filter { it.id in selected } + } + + private fun Sequence.groupRecordsByTimeBuckets(): List>> { + return this + .groupBy { record -> + val time = + ZonedDateTime.ofInstant(Instant.ofEpochMilli(record.date), ZoneId.of("UTC")) + timeBuckets.getBucketText(time)?.let(application::getString) + ?: time.toLocalDate().withDayOfMonth(1) + } + .map { (bucket, records) -> + val bucketTitle = when (bucket) { + is String -> bucket + is LocalDate -> bucket.format(monthTimeBucketFormatter) + else -> error("Invalid bucket type: $bucket") + } + + bucketTitle to records.map { record -> + MediaOverviewItem( + id = record.attachment.attachmentId.rowId, + slide = MediaUtil.getSlideForAttachment(application, record.attachment), + mediaRecord = record, + date = bucketTitle + ) + } + } + } + + private fun Sequence.groupRecordsByRelativeTime(): List>> { + return this + .groupBy { record -> + DateUtils.getRelativeDate(application, Locale.getDefault(), record.date) + } + .map { (bucket, records) -> + bucket to records.map { record -> + MediaOverviewItem( + id = record.attachment.attachmentId.rowId, + slide = MediaUtil.getSlideForAttachment(application, record.attachment), + mediaRecord = record, + date = bucket + ) + } + } + } + + + fun onItemClicked(item: MediaOverviewItem) { + if (inSelectionMode.value) { + val newSet = mutableSelectedItemIDs.value.toMutableSet() + if (item.id in newSet) { + newSet.remove(item.id) + } else { + newSet.add(item.id) + } + + mutableSelectedItemIDs.value = newSet + } else if (!item.slide.hasDocument()) { + val mediaRecord = item.mediaRecord + + // The item clicked is a media item, so we should open the media viewer + val intent = Intent(application, MediaPreviewActivity::class.java) + intent.putExtra(MediaPreviewActivity.DATE_EXTRA, mediaRecord.date) + intent.putExtra(MediaPreviewActivity.SIZE_EXTRA, mediaRecord.attachment.size) + intent.putExtra(MediaPreviewActivity.ADDRESS_EXTRA, address) + intent.putExtra(MediaPreviewActivity.OUTGOING_EXTRA, mediaRecord.isOutgoing) + intent.putExtra(MediaPreviewActivity.LEFT_IS_RECENT_EXTRA, true) + + intent.setDataAndType( + mediaRecord.attachment.dataUri, + mediaRecord.contentType + ) + + viewModelScope.launch { + mutableEvents.emit(MediaOverviewEvent.NavigateToActivity(intent)) + } + } else { + val intent = Intent(Intent.ACTION_VIEW) + intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) + intent.setDataAndType( + PartAuthority.getAttachmentPublicUri(item.slide.uri), + item.slide.contentType + ) + + viewModelScope.launch { + mutableEvents.emit(MediaOverviewEvent.NavigateToActivity(intent)) + } + } + } + + fun onTabItemClicked(tab: MediaOverviewTab) { + if (inSelectionMode.value) { + // Not allowing to switch tabs while in selection mode + return + } + + mutableSelectedTab.value = tab + } + + fun onItemLongClicked(id: Long) { + mutableSelectedItemIDs.value = setOf(id) + } + + fun onSaveClicked() { + if (!inSelectionMode.value) { + // Not in selection mode, so we should not be able to save + return + } + + viewModelScope.launch { + val selectedMedia = selectedMedia.toList() + + mutableShowingActionProgress.value = application.resources.getQuantityString( + R.plurals.ConversationFragment_saving_n_attachments, + selectedMedia.size, + selectedMedia.size, + ) + + val attachments = selectedMedia + .asSequence() + .mapNotNull { + val uri = it.mediaRecord.attachment.dataUri ?: return@mapNotNull null + SaveAttachmentTask.Attachment( + uri = uri, + contentType = it.mediaRecord.contentType, + date = it.mediaRecord.date, + fileName = it.mediaRecord.attachment.fileName, + ) + } + + var savedDirectory: String? = null + var successCount = 0 + var errorCount = 0 + + for (attachment in attachments) { + val directory = withContext(Dispatchers.Default) { + kotlin.runCatching { + SaveAttachmentTask.saveAttachment(application, attachment) + }.getOrNull() + } + + if (directory == null) { + errorCount += 1 + } else { + savedDirectory = directory + successCount += 1 + } + } + + if (successCount > 0) { + mutableEvents.emit(MediaOverviewEvent.ShowSaveAttachmentSuccess( + savedDirectory.orEmpty(), + successCount + )) + } else if (errorCount > 0) { + mutableEvents.emit(MediaOverviewEvent.ShowSaveAttachmentError(errorCount)) + } + + // Send a notification of attachment saved if we are in a 1to1 chat and the + // attachments saved are from the other party (a.k.a let other person know + // that you saved their attachments, but don't need to let the whole world know as + // in groups/communities) + if (selectedMedia.any { !it.mediaRecord.isOutgoing } && + successCount > 0 && + !address.isGroup) { + withContext(Dispatchers.Default) { + val timestamp = SnodeAPI.nowWithOffset + val kind = DataExtractionNotification.Kind.MediaSaved(timestamp) + val message = DataExtractionNotification(kind) + MessageSender.send(message, address) + } + } + + mutableShowingActionProgress.value = null + mutableSelectedItemIDs.value = emptySet() + } + + } + + fun onDeleteClicked() { + if (!inSelectionMode.value) { + // Not in selection mode, so we should not be able to delete + return + } + + viewModelScope.launch { + mutableShowingActionProgress.value = application.getString(R.string.MediaOverviewActivity_Media_delete_progress_message) + + // Delete the selected media items, and retrieve the thread ID for the address if any + val threadId = withContext(Dispatchers.Default) { + for (media in selectedMedia) { + kotlin.runCatching { + AttachmentUtil.deleteAttachment(application, media.mediaRecord.attachment) + } + } + + threadDatabase.getThreadIdIfExistsFor(address.serialize()) + } + + // Notify the content provider that the thread has been updated + if (threadId >= 0) { + application.contentResolver.notifyChange(DatabaseContentProviders.Conversation.getUriForThread(threadId), null) + } + + mutableShowingActionProgress.value = null + mutableSelectedItemIDs.value = emptySet() + } + } + + fun onSelectAllClicked() { + if (!inSelectionMode.value) { + // Not in selection mode, so we should not be able to select all + return + } + + val allItems = mediaListState.value?.let { content -> + when (selectedTab.value) { + MediaOverviewTab.Media -> content.mediaContent + MediaOverviewTab.Documents -> content.documentContent + } + } ?: return + + mutableSelectedItemIDs.value = allItems + .asSequence() + .flatMap { it.second } + .mapTo(hashSetOf()) { it.id } + } + + fun onBackClicked() { + if (inSelectionMode.value) { + // Clear selection mode by clear selecting items + mutableSelectedItemIDs.value = emptySet() + } else { + viewModelScope.launch { + mutableEvents.emit(MediaOverviewEvent.Close) + } + } + } + + @dagger.assisted.AssistedFactory + interface AssistedFactory { + fun create(address: Address): Factory + } + + class Factory @AssistedInject constructor( + @Assisted private val address: Address, + private val application: Application, + private val threadDatabase: ThreadDatabase, + private val mediaDatabase: MediaDatabase + ) : ViewModelProvider.Factory { + @Suppress("UNCHECKED_CAST") + override fun create(modelClass: Class): T = MediaOverviewViewModel( + address, + application, + threadDatabase, + mediaDatabase + ) as T + } +} + + +enum class MediaOverviewTab { + Media, + Documents, +} + +sealed interface MediaOverviewEvent { + data object Close : MediaOverviewEvent + data class ShowSaveAttachmentError(val errorCount: Int) : MediaOverviewEvent + data class ShowSaveAttachmentSuccess(val directory: String, val successCount: Int) : MediaOverviewEvent + data class NavigateToActivity(val intent: Intent) : MediaOverviewEvent +} + +typealias BucketTitle = String +typealias TabContent = List>> + +data class MediaOverviewContent( + val mediaContent: TabContent, + val documentContent: TabContent +) + +data class MediaOverviewItem( + val id: Long, + val slide: Slide, + val date: String, + val mediaRecord: MediaRecord, +) { + val showPlayOverlay: Boolean + get() = slide.hasPlayOverlay() + + val thumbnailUri: Uri? + get() = slide.thumbnailUri + + val hasPlaceholder: Boolean + get() = slide.hasPlaceholder() + + val fileName: String? + get() = slide.fileName.orNull() + + val fileSize: Long + get() = slide.fileSize + + fun placeholder(context: Context): Int { + return slide.getPlaceholderRes(context.theme) + } +} + diff --git a/app/src/main/java/org/thoughtcrime/securesms/media/MediaPage.kt b/app/src/main/java/org/thoughtcrime/securesms/media/MediaPage.kt new file mode 100644 index 00000000000..49b44b50558 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/media/MediaPage.kt @@ -0,0 +1,225 @@ +package org.thoughtcrime.securesms.media + +import androidx.compose.animation.Crossfade +import androidx.compose.foundation.ExperimentalFoundationApi +import androidx.compose.foundation.Image +import androidx.compose.foundation.background +import androidx.compose.foundation.clickable +import androidx.compose.foundation.combinedClickable +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.aspectRatio +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.ColorFilter +import androidx.compose.ui.input.nestedscroll.NestedScrollConnection +import androidx.compose.ui.input.nestedscroll.nestedScroll +import androidx.compose.ui.layout.ContentScale +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.colorResource +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.unit.dp +import com.bumptech.glide.integration.compose.CrossFade +import com.bumptech.glide.integration.compose.ExperimentalGlideComposeApi +import com.bumptech.glide.integration.compose.GlideImage +import com.bumptech.glide.load.engine.DiskCacheStrategy +import network.loki.messenger.R +import org.thoughtcrime.securesms.mms.DecryptableStreamUriLoader +import org.thoughtcrime.securesms.ui.theme.LocalColors +import org.thoughtcrime.securesms.ui.theme.LocalDimensions +import org.thoughtcrime.securesms.ui.theme.LocalType +import kotlin.math.ceil + +private val MEDIA_SPACING = 2.dp + +@OptIn(ExperimentalFoundationApi::class) +@Composable +fun MediaPage( + nestedScrollConnection: NestedScrollConnection, + content: TabContent?, + selectedItemIDs: Set, + onItemClicked: (MediaOverviewItem) -> Unit, + onItemLongClicked: ((Long) -> Unit)?, +) { + val columnCount = LocalContext.current.resources.getInteger(R.integer.media_overview_cols) + + Crossfade(content, label = "Media content animation") { state -> + when { + state == null -> { + // Loading state + } + + state.isEmpty() -> { + Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) { + Text( + text = stringResource(R.string.media_overview_activity__no_media), + style = LocalType.current.base, + color = LocalColors.current.text + ) + } + } + + else -> { + LazyColumn( + modifier = Modifier + .nestedScroll(nestedScrollConnection) + .fillMaxSize() + .padding(MEDIA_SPACING), + verticalArrangement = Arrangement.spacedBy(MEDIA_SPACING) + ) { + for ((header, thumbnails) in state) { + stickyHeader { + AttachmentHeader(text = header) + } + + val numRows = ceil(thumbnails.size / columnCount.toFloat()).toInt() + + // Row of thumbnails + items(numRows) { rowIndex -> + ThumbnailRow( + columnCount = columnCount, + thumbnails = thumbnails, + rowIndex = rowIndex, + onItemClicked = onItemClicked, + onItemLongClicked = onItemLongClicked, + selectedItemIDs = selectedItemIDs + ) + } + } + } + } + } + + } + +} + +@Composable +@OptIn(ExperimentalGlideComposeApi::class, ExperimentalFoundationApi::class) +private fun ThumbnailRow( + columnCount: Int, + thumbnails: List, + rowIndex: Int, + onItemClicked: (MediaOverviewItem) -> Unit, + onItemLongClicked: ((Long) -> Unit)?, + selectedItemIDs: Set +) { + Row(horizontalArrangement = Arrangement.spacedBy(MEDIA_SPACING)) { + repeat(columnCount) { columnIndex -> + val item = thumbnails.getOrNull(rowIndex * columnCount + columnIndex) + if (item != null) { + Box( + modifier = Modifier + .weight(1f) + .aspectRatio(1f) + .let { + when { + onItemLongClicked != null -> { + it.combinedClickable( + onClick = { onItemClicked(item) }, + onLongClick = { onItemLongClicked(item.id) } + ) + } + + else -> { + it.clickable { onItemClicked(item) } + } + } + }, + contentAlignment = Alignment.Center + ) { + val uri = item.thumbnailUri + + if (uri != null) { + GlideImage( + DecryptableStreamUriLoader.DecryptableUri(uri), + modifier = Modifier.fillMaxSize(), + contentScale = ContentScale.Crop, + contentDescription = null, + transition = CrossFade, + ) { + it.diskCacheStrategy(DiskCacheStrategy.NONE) + } + } else { + // The resource given by the placeholder needs tinting according to our theme. + // But the missing thumbnail picture does not. + var (placeholder, shouldTint) = if (item.hasPlaceholder) { + item.placeholder(LocalContext.current) to true + } else { + R.drawable.ic_missing_thumbnail_picture to false + } + + if (placeholder == 0) { + placeholder = R.drawable.ic_missing_thumbnail_picture + shouldTint = false + } + + Image( + painter = painterResource(placeholder), + contentDescription = null, + modifier = Modifier.fillMaxSize(), + contentScale = ContentScale.Inside, + colorFilter = if (shouldTint) { + ColorFilter.tint(LocalColors.current.textSecondary) + } else { + null + } + ) + } + + when { + item.showPlayOverlay -> { + // The code below is translated from thumbnail_view.xml: + // Trying to show a green play button on a white background. + Box( + modifier = Modifier + .size(48.dp) + .background(Color.White, shape = CircleShape), + contentAlignment = Alignment.Center, + ) { + Image( + modifier = Modifier.padding(start = LocalDimensions.current.xxxsSpacing), + painter = painterResource(R.drawable.triangle_right), + contentDescription = null, + colorFilter = ColorFilter.tint(LocalColors.current.primary) + ) + } + } + } + + + Crossfade( + modifier = Modifier.fillMaxSize(), + targetState = item.id in selectedItemIDs, + label = "Showing selected state" + ) { selected -> + if (selected) { + Image( + modifier = Modifier + .fillMaxSize() + .background(Color.Black.copy(alpha = 0.4f)), + contentScale = ContentScale.Inside, + painter = painterResource(R.drawable.ic_check_white_48dp), + contentDescription = stringResource(R.string.AccessibilityId_select), + ) + } + } + } + } else { + Spacer(modifier = Modifier.weight(1f)) + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/org/thoughtcrime/securesms/mediapreview/MediaRailAdapter.java b/app/src/main/java/org/thoughtcrime/securesms/mediapreview/MediaRailAdapter.java index 6492069780c..e695274847f 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/mediapreview/MediaRailAdapter.java +++ b/app/src/main/java/org/thoughtcrime/securesms/mediapreview/MediaRailAdapter.java @@ -14,7 +14,7 @@ import org.thoughtcrime.securesms.conversation.v2.utilities.ThumbnailView; import org.thoughtcrime.securesms.mediasend.Media; -import org.thoughtcrime.securesms.mms.GlideRequests; +import com.bumptech.glide.RequestManager; import org.thoughtcrime.securesms.util.StableIdGenerator; import java.util.ArrayList; @@ -25,7 +25,7 @@ public class MediaRailAdapter extends RecyclerView.Adapter media; private final RailItemListener listener; private final boolean editable; @@ -34,7 +34,7 @@ public class MediaRailAdapter extends RecyclerView.Adapter(); this.listener = listener; @@ -148,7 +148,7 @@ static class MediaViewHolder extends MediaRailViewHolder { captionIndicator = itemView.findViewById(R.id.rail_item_caption); } - void bind(@NonNull Media media, boolean isActive, @NonNull GlideRequests glideRequests, + void bind(@NonNull Media media, boolean isActive, @NonNull RequestManager glideRequests, @NonNull RailItemListener railItemListener, int distanceFromActive, boolean editable) { image.setImageResource(glideRequests, media.getUri()); diff --git a/app/src/main/java/org/thoughtcrime/securesms/mediasend/Camera1Fragment.java b/app/src/main/java/org/thoughtcrime/securesms/mediasend/Camera1Fragment.java index 23f9d33e079..3ef7090c303 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/mediasend/Camera1Fragment.java +++ b/app/src/main/java/org/thoughtcrime/securesms/mediasend/Camera1Fragment.java @@ -36,7 +36,7 @@ import network.loki.messenger.R; import org.session.libsignal.utilities.Log; -import org.thoughtcrime.securesms.mms.GlideApp; +import com.bumptech.glide.Glide; import org.session.libsession.utilities.ServiceUtil; import org.thoughtcrime.securesms.util.Stopwatch; import org.session.libsession.utilities.TextSecurePreferences; @@ -236,7 +236,7 @@ private void onCaptureClicked() { Transformation transformation = frontFacing ? new MultiTransformation<>(new CenterCrop(), new FlipTransformation()) : new CenterCrop(); - GlideApp.with(this) + Glide.with(this) .asBitmap() .load(jpegData) .transform(transformation) diff --git a/app/src/main/java/org/thoughtcrime/securesms/mediasend/MediaPickerFolderAdapter.java b/app/src/main/java/org/thoughtcrime/securesms/mediasend/MediaPickerFolderAdapter.java index 778883849b2..1973ad17000 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/mediasend/MediaPickerFolderAdapter.java +++ b/app/src/main/java/org/thoughtcrime/securesms/mediasend/MediaPickerFolderAdapter.java @@ -14,18 +14,18 @@ import network.loki.messenger.R; -import org.thoughtcrime.securesms.mms.GlideRequests; +import com.bumptech.glide.RequestManager; import java.util.ArrayList; import java.util.List; class MediaPickerFolderAdapter extends RecyclerView.Adapter { - private final GlideRequests glideRequests; + private final RequestManager glideRequests; private final EventListener eventListener; private final List folders; - MediaPickerFolderAdapter(@NonNull GlideRequests glideRequests, @NonNull EventListener eventListener) { + MediaPickerFolderAdapter(@NonNull RequestManager glideRequests, @NonNull EventListener eventListener) { this.glideRequests = glideRequests; this.eventListener = eventListener; this.folders = new ArrayList<>(); @@ -74,7 +74,7 @@ static class FolderViewHolder extends RecyclerView.ViewHolder { count = itemView.findViewById(R.id.mediapicker_folder_item_count); } - void bind(@NonNull MediaFolder folder, @NonNull GlideRequests glideRequests, @NonNull EventListener eventListener) { + void bind(@NonNull MediaFolder folder, @NonNull RequestManager glideRequests, @NonNull EventListener eventListener) { title.setText(folder.getTitle()); count.setText(String.valueOf(folder.getItemCount())); diff --git a/app/src/main/java/org/thoughtcrime/securesms/mediasend/MediaPickerFolderFragment.java b/app/src/main/java/org/thoughtcrime/securesms/mediasend/MediaPickerFolderFragment.java index b64e837b5a3..290d09778ab 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/mediasend/MediaPickerFolderFragment.java +++ b/app/src/main/java/org/thoughtcrime/securesms/mediasend/MediaPickerFolderFragment.java @@ -19,7 +19,7 @@ import android.view.ViewGroup; import android.view.WindowManager; -import org.thoughtcrime.securesms.mms.GlideApp; +import com.bumptech.glide.Glide; import org.session.libsession.utilities.recipients.Recipient; import org.session.libsignal.utilities.guava.Optional; @@ -79,7 +79,7 @@ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceStat super.onViewCreated(view, savedInstanceState); RecyclerView list = view.findViewById(R.id.mediapicker_folder_list); - MediaPickerFolderAdapter adapter = new MediaPickerFolderAdapter(GlideApp.with(this), this); + MediaPickerFolderAdapter adapter = new MediaPickerFolderAdapter(Glide.with(this), this); layoutManager = new GridLayoutManager(requireContext(), 2); onScreenWidthChanged(getScreenWidth()); diff --git a/app/src/main/java/org/thoughtcrime/securesms/mediasend/MediaPickerItemAdapter.java b/app/src/main/java/org/thoughtcrime/securesms/mediasend/MediaPickerItemAdapter.java index 99cc6bff933..b184197fe15 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/mediasend/MediaPickerItemAdapter.java +++ b/app/src/main/java/org/thoughtcrime/securesms/mediasend/MediaPickerItemAdapter.java @@ -12,7 +12,7 @@ import com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions; import network.loki.messenger.R; -import org.thoughtcrime.securesms.mms.GlideRequests; +import com.bumptech.glide.RequestManager; import org.thoughtcrime.securesms.util.MediaUtil; import org.thoughtcrime.securesms.util.StableIdGenerator; @@ -24,7 +24,7 @@ public class MediaPickerItemAdapter extends RecyclerView.Adapter { - private final GlideRequests glideRequests; + private final RequestManager glideRequests; private final EventListener eventListener; private final List media; private final List selected; @@ -33,7 +33,7 @@ public class MediaPickerItemAdapter extends RecyclerView.Adapter(); @@ -109,7 +109,7 @@ static class ItemViewHolder extends RecyclerView.ViewHolder { selectOrder = itemView.findViewById(R.id.mediapicker_select_order); } - void bind(@NonNull Media media, boolean multiSelect, List selected, int maxSelection, @NonNull GlideRequests glideRequests, @NonNull EventListener eventListener) { + void bind(@NonNull Media media, boolean multiSelect, List selected, int maxSelection, @NonNull RequestManager glideRequests, @NonNull EventListener eventListener) { glideRequests.load(media.getUri()) .diskCacheStrategy(DiskCacheStrategy.NONE) .transition(DrawableTransitionOptions.withCrossFade()) diff --git a/app/src/main/java/org/thoughtcrime/securesms/mediasend/MediaPickerItemFragment.java b/app/src/main/java/org/thoughtcrime/securesms/mediasend/MediaPickerItemFragment.java index 9e3db73bfc1..8fd7559b4d5 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/mediasend/MediaPickerItemFragment.java +++ b/app/src/main/java/org/thoughtcrime/securesms/mediasend/MediaPickerItemFragment.java @@ -21,7 +21,7 @@ import android.view.WindowManager; import android.widget.Toast; -import org.thoughtcrime.securesms.mms.GlideApp; +import com.bumptech.glide.Glide; import org.session.libsession.utilities.Util; import java.util.ArrayList; @@ -91,7 +91,7 @@ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceStat RecyclerView imageList = view.findViewById(R.id.mediapicker_item_list); - adapter = new MediaPickerItemAdapter(GlideApp.with(this), this, maxSelection); + adapter = new MediaPickerItemAdapter(Glide.with(this), this, maxSelection); layoutManager = new GridLayoutManager(requireContext(), 4); imageList.setLayoutManager(layoutManager); diff --git a/app/src/main/java/org/thoughtcrime/securesms/mediasend/MediaRepository.java b/app/src/main/java/org/thoughtcrime/securesms/mediasend/MediaRepository.java index 76c6b4355d5..a9e7081ef8e 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/mediasend/MediaRepository.java +++ b/app/src/main/java/org/thoughtcrime/securesms/mediasend/MediaRepository.java @@ -1,6 +1,5 @@ package org.thoughtcrime.securesms.mediasend; -import android.annotation.TargetApi; import android.content.Context; import android.database.Cursor; import android.net.Uri; @@ -200,14 +199,12 @@ private List getPopulatedMedia(@NonNull Context context, @NonNull List pageChangeListener.onPageSelected(fragmentPager.getCurrentItem())); - mediaRailAdapter = new MediaRailAdapter(GlideApp.with(this), this, true); + mediaRailAdapter = new MediaRailAdapter(Glide.with(this), this, true); mediaRail.setLayoutManager(new LinearLayoutManager(requireContext(), LinearLayoutManager.HORIZONTAL, false)); mediaRail.setAdapter(mediaRailAdapter); diff --git a/app/src/main/java/org/thoughtcrime/securesms/mediasend/MediaSendGifFragment.java b/app/src/main/java/org/thoughtcrime/securesms/mediasend/MediaSendGifFragment.java index fec59e07054..41293c62560 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/mediasend/MediaSendGifFragment.java +++ b/app/src/main/java/org/thoughtcrime/securesms/mediasend/MediaSendGifFragment.java @@ -12,7 +12,7 @@ import network.loki.messenger.R; import org.thoughtcrime.securesms.mms.DecryptableStreamUriLoader; -import org.thoughtcrime.securesms.mms.GlideApp; +import com.bumptech.glide.Glide; public class MediaSendGifFragment extends Fragment implements MediaSendPageFragment { @@ -40,7 +40,7 @@ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceStat super.onViewCreated(view, savedInstanceState); uri = getArguments().getParcelable(KEY_URI); - GlideApp.with(this).load(new DecryptableStreamUriLoader.DecryptableUri(uri)).into((ImageView) view); + Glide.with(this).load(new DecryptableStreamUriLoader.DecryptableUri(uri)).into((ImageView) view); } @Override diff --git a/app/src/main/java/org/thoughtcrime/securesms/messagerequests/MessageRequestView.kt b/app/src/main/java/org/thoughtcrime/securesms/messagerequests/MessageRequestView.kt index 6584f4e5196..1fb1f38ce4e 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/messagerequests/MessageRequestView.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/messagerequests/MessageRequestView.kt @@ -11,7 +11,7 @@ import network.loki.messenger.databinding.ViewMessageRequestBinding import org.session.libsession.utilities.recipients.Recipient import org.thoughtcrime.securesms.conversation.v2.utilities.MentionUtilities.highlightMentions import org.thoughtcrime.securesms.database.model.ThreadRecord -import org.thoughtcrime.securesms.mms.GlideRequests +import com.bumptech.glide.RequestManager import org.thoughtcrime.securesms.util.DateUtils import java.util.Locale @@ -32,7 +32,7 @@ class MessageRequestView : LinearLayout { // endregion // region Updating - fun bind(thread: ThreadRecord, glide: GlideRequests) { + fun bind(thread: ThreadRecord, glide: RequestManager) { this.thread = thread val senderDisplayName = getUserDisplayName(thread.recipient) ?: thread.recipient.address.toString() diff --git a/app/src/main/java/org/thoughtcrime/securesms/messagerequests/MessageRequestsActivity.kt b/app/src/main/java/org/thoughtcrime/securesms/messagerequests/MessageRequestsActivity.kt index caecbcd87d0..233222dabba 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/messagerequests/MessageRequestsActivity.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/messagerequests/MessageRequestsActivity.kt @@ -17,8 +17,8 @@ import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity import org.thoughtcrime.securesms.conversation.v2.ConversationActivityV2 import org.thoughtcrime.securesms.database.ThreadDatabase import org.thoughtcrime.securesms.database.model.ThreadRecord -import org.thoughtcrime.securesms.mms.GlideApp -import org.thoughtcrime.securesms.mms.GlideRequests +import com.bumptech.glide.Glide +import com.bumptech.glide.RequestManager import org.thoughtcrime.securesms.showSessionDialog import org.thoughtcrime.securesms.util.ConfigurationMessageUtilities import org.thoughtcrime.securesms.util.push @@ -28,7 +28,7 @@ import javax.inject.Inject class MessageRequestsActivity : PassphraseRequiredActionBarActivity(), ConversationClickListener, LoaderManager.LoaderCallbacks { private lateinit var binding: ActivityMessageRequestsBinding - private lateinit var glide: GlideRequests + private lateinit var glide: RequestManager @Inject lateinit var threadDb: ThreadDatabase @@ -43,7 +43,7 @@ class MessageRequestsActivity : PassphraseRequiredActionBarActivity(), Conversat binding = ActivityMessageRequestsBinding.inflate(layoutInflater) setContentView(binding.root) - glide = GlideApp.with(this) + glide = Glide.with(this) adapter.setHasStableIds(true) adapter.glide = glide diff --git a/app/src/main/java/org/thoughtcrime/securesms/messagerequests/MessageRequestsAdapter.kt b/app/src/main/java/org/thoughtcrime/securesms/messagerequests/MessageRequestsAdapter.kt index 07f3f02c662..cb352d83b7e 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/messagerequests/MessageRequestsAdapter.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/messagerequests/MessageRequestsAdapter.kt @@ -15,7 +15,7 @@ import org.session.libsession.utilities.ThemeUtil import org.thoughtcrime.securesms.database.CursorRecyclerViewAdapter import org.thoughtcrime.securesms.database.model.ThreadRecord import org.thoughtcrime.securesms.dependencies.DatabaseComponent -import org.thoughtcrime.securesms.mms.GlideRequests +import com.bumptech.glide.RequestManager class MessageRequestsAdapter( context: Context, @@ -23,7 +23,7 @@ class MessageRequestsAdapter( val listener: ConversationClickListener ) : CursorRecyclerViewAdapter(context, cursor) { private val threadDatabase = DatabaseComponent.get(context).threadDatabase() - lateinit var glide: GlideRequests + lateinit var glide: RequestManager class ViewHolder(val view: MessageRequestView) : RecyclerView.ViewHolder(view) diff --git a/app/src/main/java/org/thoughtcrime/securesms/mms/Slide.kt b/app/src/main/java/org/thoughtcrime/securesms/mms/Slide.kt index e284c1ce22d..05d8167d9a7 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/mms/Slide.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/mms/Slide.kt @@ -21,7 +21,6 @@ import android.content.res.Resources import android.net.Uri import androidx.annotation.DrawableRes import com.squareup.phrase.Phrase -import java.security.SecureRandom import network.loki.messenger.R import org.session.libsession.messaging.sending_receiving.attachments.Attachment import org.session.libsession.messaging.sending_receiving.attachments.AttachmentTransferProgress @@ -29,6 +28,7 @@ import org.session.libsession.messaging.sending_receiving.attachments.UriAttachm import org.session.libsession.utilities.StringSubstitutionConstants.EMOJI_KEY import org.session.libsession.utilities.Util.equals import org.session.libsession.utilities.Util.hashCode +import org.session.libsignal.utilities.Util.SECURE_RANDOM import org.session.libsignal.utilities.guava.Optional import org.thoughtcrime.securesms.conversation.v2.Util import org.thoughtcrime.securesms.util.MediaUtil @@ -160,7 +160,7 @@ abstract class Slide(@JvmField protected val context: Context, protected val att ): Attachment { val resolvedType = Optional.fromNullable(MediaUtil.getMimeType(context, uri)).or(defaultMime) - val fastPreflightId = SecureRandom().nextLong().toString() + val fastPreflightId = SECURE_RANDOM.nextLong().toString() return UriAttachment( uri, if (hasThumbnail) uri else null, diff --git a/app/src/main/java/org/thoughtcrime/securesms/net/ChunkedDataFetcher.java b/app/src/main/java/org/thoughtcrime/securesms/net/ChunkedDataFetcher.java index af37b863944..e50846b1c96 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/net/ChunkedDataFetcher.java +++ b/app/src/main/java/org/thoughtcrime/securesms/net/ChunkedDataFetcher.java @@ -1,5 +1,7 @@ package org.thoughtcrime.securesms.net; +import static org.session.libsignal.utilities.Util.SECURE_RANDOM; + import androidx.annotation.NonNull; import android.text.TextUtils; @@ -15,7 +17,6 @@ import java.io.FilterInputStream; import java.io.IOException; import java.io.InputStream; -import java.security.SecureRandom; import java.util.ArrayList; import java.util.LinkedList; import java.util.List; @@ -54,7 +55,7 @@ public RequestController fetch(@NonNull String url, long contentLength, @NonNull private RequestController fetchChunksWithUnknownTotalSize(@NonNull String url, @NonNull Callback callback) { CompositeRequestController compositeController = new CompositeRequestController(); - long chunkSize = new SecureRandom().nextInt(1024) + 1024; + long chunkSize = SECURE_RANDOM.nextInt(1024) + 1024; Request request = new Request.Builder() .url(url) .cacheControl(NO_CACHE) diff --git a/app/src/main/java/org/thoughtcrime/securesms/notifications/AbstractNotificationBuilder.java b/app/src/main/java/org/thoughtcrime/securesms/notifications/AbstractNotificationBuilder.java index 64617eb6a1c..d67ad47832f 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/notifications/AbstractNotificationBuilder.java +++ b/app/src/main/java/org/thoughtcrime/securesms/notifications/AbstractNotificationBuilder.java @@ -50,8 +50,8 @@ protected CharSequence getStyledMessage(@NonNull Recipient recipient, @Nullable } public void setAlarms(@Nullable Uri ringtone, VibrateState vibrate) { - Uri defaultRingtone = NotificationChannels.supported() ? NotificationChannels.getMessageRingtone(context) : TextSecurePreferences.getNotificationRingtone(context); - boolean defaultVibrate = NotificationChannels.supported() ? NotificationChannels.getMessageVibrate(context) : TextSecurePreferences.isNotificationVibrateEnabled(context); + Uri defaultRingtone = NotificationChannels.getMessageRingtone(context); + boolean defaultVibrate = NotificationChannels.getMessageVibrate(context); if (ringtone == null && !TextUtils.isEmpty(defaultRingtone.toString())) setSound(defaultRingtone); else if (ringtone != null && !ringtone.toString().isEmpty()) setSound(ringtone); diff --git a/app/src/main/java/org/thoughtcrime/securesms/notifications/MultipleRecipientNotificationBuilder.java b/app/src/main/java/org/thoughtcrime/securesms/notifications/MultipleRecipientNotificationBuilder.java index 7042a1766a0..0578bb2c9cd 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/notifications/MultipleRecipientNotificationBuilder.java +++ b/app/src/main/java/org/thoughtcrime/securesms/notifications/MultipleRecipientNotificationBuilder.java @@ -37,10 +37,6 @@ public MultipleRecipientNotificationBuilder(Context context, NotificationPrivacy setContentIntent(PendingIntent.getActivity(context, 0, new Intent(context, HomeActivity.class), PendingIntent.FLAG_IMMUTABLE)); setCategory(NotificationCompat.CATEGORY_MESSAGE); setGroupSummary(true); - - if (!NotificationChannels.supported()) { - setPriority(TextSecurePreferences.getNotificationPriority(context)); - } } public void setMessageCount(int messageCount, int threadCount) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/notifications/NotificationChannels.java b/app/src/main/java/org/thoughtcrime/securesms/notifications/NotificationChannels.java index 8bf1d7d8d8a..51ff467549d 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/notifications/NotificationChannels.java +++ b/app/src/main/java/org/thoughtcrime/securesms/notifications/NotificationChannels.java @@ -1,15 +1,12 @@ package org.thoughtcrime.securesms.notifications; -import android.annotation.TargetApi; import android.app.NotificationChannel; import android.app.NotificationChannelGroup; import android.app.NotificationManager; import android.content.Context; -import android.content.Intent; import android.media.AudioAttributes; import android.net.Uri; import android.os.AsyncTask; -import android.os.Build; import android.provider.Settings; import android.text.TextUtils; @@ -24,7 +21,6 @@ import org.session.libsession.utilities.ServiceUtil; import org.session.libsession.utilities.TextSecurePreferences; import org.session.libsession.utilities.recipients.Recipient; -import org.session.libsession.utilities.recipients.Recipient.VibrateState; import org.session.libsignal.utilities.Log; import org.thoughtcrime.securesms.database.RecipientDatabase; import org.thoughtcrime.securesms.dependencies.DatabaseComponent; @@ -63,10 +59,6 @@ public class NotificationChannels { * ignored for API < 26. */ public static synchronized void create(@NonNull Context context) { - if (!supported()) { - return; - } - NotificationManager notificationManager = ServiceUtil.getNotificationManager(context); int oldVersion = TextSecurePreferences.getNotificationChannelVersion(context); @@ -82,32 +74,6 @@ public static synchronized void create(@NonNull Context context) { }); } - /** - * Recreates all notification channels for contacts with custom notifications enabled. Should be - * safe to call repeatedly. Needs to be executed on a background thread. - */ - @WorkerThread - public static synchronized void restoreContactNotificationChannels(@NonNull Context context) { - if (!NotificationChannels.supported()) { - return; - } - - RecipientDatabase db = DatabaseComponent.get(context).recipientDatabase(); - - try (RecipientDatabase.RecipientReader reader = db.getRecipientsWithNotificationChannels()) { - Recipient recipient; - while ((recipient = reader.getNext()) != null) { - NotificationManager notificationManager = ServiceUtil.getNotificationManager(context); - if (!channelExists(notificationManager.getNotificationChannel(recipient.getNotificationChannel()))) { - String id = createChannelFor(context, recipient); - db.setNotificationChannel(recipient, id); - } - } - } - - ensureCustomChannelConsistency(context); - } - /** * @return The channel ID for the default messages channel. */ @@ -115,13 +81,6 @@ public static synchronized void restoreContactNotificationChannels(@NonNull Cont return getMessagesChannelId(TextSecurePreferences.getNotificationMessagesChannelVersion(context)); } - /** - * @return Whether or not notification channels are supported. - */ - public static boolean supported() { - return Build.VERSION.SDK_INT >= 26; - } - /** * @return A name suitable to be displayed as the notification channel title. */ @@ -137,119 +96,18 @@ public static boolean supported() { } } - /** - * Creates a channel for the specified recipient. - * @return The channel ID for the newly-created channel. - */ - public static synchronized String createChannelFor(@NonNull Context context, @NonNull Recipient recipient) { - VibrateState vibrateState = recipient.getMessageVibrate(); - boolean vibrationEnabled = vibrateState == VibrateState.DEFAULT ? TextSecurePreferences.isNotificationVibrateEnabled(context) : vibrateState == VibrateState.ENABLED; - Uri messageRingtone = recipient.getMessageRingtone() != null ? recipient.getMessageRingtone() : getMessageRingtone(context); - String displayName = getChannelDisplayNameFor(context, recipient.getName(), recipient.getProfileName(), recipient.getAddress()); - - return createChannelFor(context, recipient.getAddress(), displayName, messageRingtone, vibrationEnabled); - } - - /** - * More verbose version of {@link #createChannelFor(Context, Recipient)}. - */ - public static synchronized @Nullable String createChannelFor(@NonNull Context context, - @NonNull Address address, - @NonNull String displayName, - @Nullable Uri messageSound, - boolean vibrationEnabled) - { - if (!supported()) { - return null; - } - String channelId = generateChannelIdFor(address); - NotificationChannel channel = new NotificationChannel(channelId, displayName, NotificationManager.IMPORTANCE_HIGH); - setLedPreference(channel, TextSecurePreferences.getNotificationLedColor(context)); - channel.setGroup(CATEGORY_MESSAGES); - channel.enableVibration(vibrationEnabled); - - if (messageSound != null) { - channel.setSound(messageSound, new AudioAttributes.Builder().setContentType(AudioAttributes.CONTENT_TYPE_UNKNOWN) - .setUsage(AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_INSTANT) - .build()); - } - - NotificationManager notificationManager = ServiceUtil.getNotificationManager(context); - notificationManager.createNotificationChannel(channel); - - return channelId; - } - - /** - * Deletes the channel generated for the provided recipient. Safe to call even if there was never - * a channel made for that recipient. - */ - public static synchronized void deleteChannelFor(@NonNull Context context, @NonNull Recipient recipient) { - if (!supported()) { - return; - } - - NotificationManager notificationManager = ServiceUtil.getNotificationManager(context); - String channel = recipient.getNotificationChannel(); - - if (channel != null) { - Log.i(TAG, "Deleting channel"); - notificationManager.deleteNotificationChannel(channel); - } - } - - /** - * Navigates the user to the system settings for the desired notification channel. - */ - public static void openChannelSettings(@NonNull Context context, @NonNull String channelId) { - if (!supported()) { - return; - } - - Intent intent = new Intent(Settings.ACTION_CHANNEL_NOTIFICATION_SETTINGS); - intent.putExtra(Settings.EXTRA_CHANNEL_ID, channelId); - intent.putExtra(Settings.EXTRA_APP_PACKAGE, context.getPackageName()); - context.startActivity(intent); - } - - /** - * Updates the LED color for message notifications and all contact-specific message notification - * channels. Performs database operations and should therefore be invoked on a background thread. - */ - @WorkerThread - public static synchronized void updateMessagesLedColor(@NonNull Context context, @NonNull Integer color) { - if (!supported()) { - return; - } - Log.i(TAG, "Updating LED color."); - - NotificationManager notificationManager = ServiceUtil.getNotificationManager(context); - - updateMessageChannel(context, channel -> setLedPreference(channel, color)); - updateAllRecipientChannelLedColors(context, notificationManager, color); - - ensureCustomChannelConsistency(context); - } /** * @return The message ringtone set for the default message channel. */ public static synchronized @NonNull Uri getMessageRingtone(@NonNull Context context) { - if (!supported()) { - return Uri.EMPTY; - } - Uri sound = ServiceUtil.getNotificationManager(context).getNotificationChannel(getMessagesChannel(context)).getSound(); return sound == null ? Uri.EMPTY : sound; } public static synchronized @Nullable Uri getMessageRingtone(@NonNull Context context, @NonNull Recipient recipient) { - if (!supported() || recipient.getNotificationChannel() == null) { - return null; - } - NotificationManager notificationManager = ServiceUtil.getNotificationManager(context); NotificationChannel channel = notificationManager.getNotificationChannel(recipient.getNotificationChannel()); @@ -265,9 +123,6 @@ public static synchronized void updateMessagesLedColor(@NonNull Context context, * Update the message ringtone for the default message channel. */ public static synchronized void updateMessageRingtone(@NonNull Context context, @Nullable Uri uri) { - if (!supported()) { - return; - } Log.i(TAG, "Updating default message ringtone with URI: " + String.valueOf(uri)); updateMessageChannel(context, channel -> { @@ -275,121 +130,23 @@ public static synchronized void updateMessageRingtone(@NonNull Context context, }); } - /** - * Updates the message ringtone for a specific recipient. If that recipient has no channel, this - * does nothing. - * - * This has to update the database, and therefore should be run on a background thread. - */ - @WorkerThread - public static synchronized void updateMessageRingtone(@NonNull Context context, @NonNull Recipient recipient, @Nullable Uri uri) { - if (!supported() || recipient.getNotificationChannel() == null) { - return; - } - Log.i(TAG, "Updating recipient message ringtone with URI: " + String.valueOf(uri)); - - String newChannelId = generateChannelIdFor(recipient.getAddress()); - boolean success = updateExistingChannel(ServiceUtil.getNotificationManager(context), - recipient.getNotificationChannel(), - generateChannelIdFor(recipient.getAddress()), - channel -> channel.setSound(uri == null ? Settings.System.DEFAULT_NOTIFICATION_URI : uri, getRingtoneAudioAttributes())); - - DatabaseComponent.get(context).recipientDatabase().setNotificationChannel(recipient, success ? newChannelId : null); - ensureCustomChannelConsistency(context); - } /** * @return The vibrate settings for the default message channel. */ public static synchronized boolean getMessageVibrate(@NonNull Context context) { - if (!supported()) { - return false; - } - return ServiceUtil.getNotificationManager(context).getNotificationChannel(getMessagesChannel(context)).shouldVibrate(); } - /** - * @return The vibrate setting for a specific recipient. If that recipient has no channel, this - * will return the setting for the default message channel. - */ - public static synchronized boolean getMessageVibrate(@NonNull Context context, @NonNull Recipient recipient) { - if (!supported()) { - return getMessageVibrate(context); - } - - NotificationManager notificationManager = ServiceUtil.getNotificationManager(context); - NotificationChannel channel = notificationManager.getNotificationChannel(recipient.getNotificationChannel()); - - if (!channelExists(channel)) { - Log.w(TAG, "Recipient didn't have a channel. Returning message default."); - return getMessageVibrate(context); - } - - return channel.shouldVibrate(); - } - /** * Sets the vibrate property for the default message channel. */ public static synchronized void updateMessageVibrate(@NonNull Context context, boolean enabled) { - if (!supported()) { - return; - } Log.i(TAG, "Updating default vibrate with value: " + enabled); updateMessageChannel(context, channel -> channel.enableVibration(enabled)); } - /** - * Updates the message ringtone for a specific recipient. If that recipient has no channel, this - * does nothing. - * - * This has to update the database and should therefore be run on a background thread. - */ - @WorkerThread - public static synchronized void updateMessageVibrate(@NonNull Context context, @NonNull Recipient recipient, VibrateState vibrateState) { - if (!supported() || recipient.getNotificationChannel() == null) { - return ; - } - Log.i(TAG, "Updating recipient vibrate with value: " + vibrateState); - - boolean enabled = vibrateState == VibrateState.DEFAULT ? getMessageVibrate(context) : vibrateState == VibrateState.ENABLED; - String newChannelId = generateChannelIdFor(recipient.getAddress()); - boolean success = updateExistingChannel(ServiceUtil.getNotificationManager(context), - recipient.getNotificationChannel(), - newChannelId, - channel -> channel.enableVibration(enabled)); - - DatabaseComponent.get(context).recipientDatabase().setNotificationChannel(recipient, success ? newChannelId : null); - ensureCustomChannelConsistency(context); - } - - /** - * Updates the name of an existing channel to match the recipient's current name. Will have no - * effect if the recipient doesn't have an existing valid channel. - */ - public static synchronized void updateContactChannelName(@NonNull Context context, @NonNull Recipient recipient) { - if (!supported() || recipient.getNotificationChannel() == null) { - return; - } - Log.i(TAG, "Updating contact channel name"); - - NotificationManager notificationManager = ServiceUtil.getNotificationManager(context); - - if (notificationManager.getNotificationChannel(recipient.getNotificationChannel()) == null) { - Log.w(TAG, "Tried to update the name of a channel, but that channel doesn't exist."); - return; - } - - NotificationChannel channel = new NotificationChannel(recipient.getNotificationChannel(), - getChannelDisplayNameFor(context, recipient.getName(), recipient.getProfileName(), recipient.getAddress()), - NotificationManager.IMPORTANCE_HIGH); - channel.setGroup(CATEGORY_MESSAGES); - notificationManager.createNotificationChannel(channel); - } - - @TargetApi(26) @WorkerThread public static synchronized void ensureCustomChannelConsistency(@NonNull Context context) { NotificationManager notificationManager = ServiceUtil.getNotificationManager(context); @@ -421,7 +178,6 @@ public static synchronized void ensureCustomChannelConsistency(@NonNull Context } } - @TargetApi(26) private static void onCreate(@NonNull Context context, @NonNull NotificationManager notificationManager) { NotificationChannelGroup messagesGroup = new NotificationChannelGroup(CATEGORY_MESSAGES, context.getResources().getString(R.string.NotificationChannel_group_messages)); notificationManager.createNotificationChannelGroup(messagesGroup); @@ -454,7 +210,6 @@ private static void onCreate(@NonNull Context context, @NonNull NotificationMana } } - @TargetApi(26) private static void onUpgrade(@NonNull NotificationManager notificationManager, int oldVersion, int newVersion) { Log.i(TAG, "Upgrading channels from " + oldVersion + " to " + newVersion); @@ -469,7 +224,6 @@ private static void onUpgrade(@NonNull NotificationManager notificationManager, } } - @TargetApi(26) private static void setLedPreference(@NonNull NotificationChannel channel, @NonNull Integer ledColor) { if ("none".equals(ledColor)) { channel.enableLights(false); @@ -484,7 +238,6 @@ private static void setLedPreference(@NonNull NotificationChannel channel, @NonN return CONTACT_PREFIX + address.serialize() + "_" + System.currentTimeMillis(); } - @TargetApi(26) private static @NonNull NotificationChannel copyChannel(@NonNull NotificationChannel original, @NonNull String id) { NotificationChannel copy = new NotificationChannel(id, original.getName(), original.getImportance()); @@ -505,27 +258,7 @@ private static String getMessagesChannelId(int version) { return MESSAGES_PREFIX + version; } - @WorkerThread - @TargetApi(26) - private static void updateAllRecipientChannelLedColors(@NonNull Context context, @NonNull NotificationManager notificationManager, @NonNull Integer color) { - RecipientDatabase database = DatabaseComponent.get(context).recipientDatabase(); - - try (RecipientDatabase.RecipientReader recipients = database.getRecipientsWithNotificationChannels()) { - Recipient recipient; - while ((recipient = recipients.getNext()) != null) { - assert recipient.getNotificationChannel() != null; - - String newChannelId = generateChannelIdFor(recipient.getAddress()); - boolean success = updateExistingChannel(notificationManager, recipient.getNotificationChannel(), newChannelId, channel -> setLedPreference(channel, color)); - - database.setNotificationChannel(recipient, success ? newChannelId : null); - } - } - - ensureCustomChannelConsistency(context); - } - @TargetApi(26) private static void updateMessageChannel(@NonNull Context context, @NonNull ChannelUpdater updater) { NotificationManager notificationManager = ServiceUtil.getNotificationManager(context); int existingVersion = TextSecurePreferences.getNotificationMessagesChannelVersion(context); @@ -539,7 +272,6 @@ private static void updateMessageChannel(@NonNull Context context, @NonNull Chan } } - @TargetApi(26) private static boolean updateExistingChannel(@NonNull NotificationManager notificationManager, @NonNull String channelId, @NonNull String newChannelId, @@ -559,20 +291,17 @@ private static boolean updateExistingChannel(@NonNull NotificationManager notifi return true; } - @TargetApi(21) private static AudioAttributes getRingtoneAudioAttributes() { return new AudioAttributes.Builder().setContentType(AudioAttributes.CONTENT_TYPE_UNKNOWN) .setUsage(AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_INSTANT) .build(); } - @TargetApi(26) private static boolean channelExists(@Nullable NotificationChannel channel) { return channel != null && !NotificationChannel.DEFAULT_CHANNEL_ID.equals(channel.getId()); } private interface ChannelUpdater { - @TargetApi(26) void update(@NonNull NotificationChannel channel); } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/notifications/NotificationState.java b/app/src/main/java/org/thoughtcrime/securesms/notifications/NotificationState.java index 108aa12c51d..7bea5594ede 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/notifications/NotificationState.java +++ b/app/src/main/java/org/thoughtcrime/securesms/notifications/NotificationState.java @@ -51,8 +51,7 @@ public void addNotification(NotificationItem item) { Recipient recipient = notifications.getFirst().getRecipient(); if (recipient != null) { - return NotificationChannels.supported() ? NotificationChannels.getMessageRingtone(context, recipient) - : recipient.resolve().getMessageRingtone(); + return NotificationChannels.getMessageRingtone(context, recipient); } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/notifications/PendingMessageNotificationBuilder.java b/app/src/main/java/org/thoughtcrime/securesms/notifications/PendingMessageNotificationBuilder.java index 935d575c569..ef69abdc261 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/notifications/PendingMessageNotificationBuilder.java +++ b/app/src/main/java/org/thoughtcrime/securesms/notifications/PendingMessageNotificationBuilder.java @@ -32,9 +32,5 @@ public PendingMessageNotificationBuilder(Context context, NotificationPrivacyPre setContentIntent(PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_IMMUTABLE)); setAutoCancel(true); setAlarms(null, Recipient.VibrateState.DEFAULT); - - if (!NotificationChannels.supported()) { - setPriority(TextSecurePreferences.getNotificationPriority(context)); - } } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/notifications/SingleRecipientNotificationBuilder.java b/app/src/main/java/org/thoughtcrime/securesms/notifications/SingleRecipientNotificationBuilder.java index 146021ac684..0ac16ea6522 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/notifications/SingleRecipientNotificationBuilder.java +++ b/app/src/main/java/org/thoughtcrime/securesms/notifications/SingleRecipientNotificationBuilder.java @@ -13,7 +13,6 @@ import android.graphics.RectF; import android.graphics.drawable.Drawable; import android.net.Uri; -import android.os.Build; import android.text.SpannableStringBuilder; import androidx.annotation.NonNull; @@ -24,11 +23,10 @@ import androidx.core.app.RemoteInput; import androidx.core.content.ContextCompat; +import com.bumptech.glide.Glide; import com.bumptech.glide.load.engine.DiskCacheStrategy; -import org.session.libsession.avatars.ContactColors; import org.session.libsession.avatars.ContactPhoto; -import org.session.libsession.avatars.ResourceContactPhoto; import org.session.libsession.messaging.contacts.Contact; import org.session.libsession.utilities.NotificationPrivacyPreference; import org.session.libsession.utilities.TextSecurePreferences; @@ -38,7 +36,6 @@ import org.thoughtcrime.securesms.database.SessionContactDatabase; import org.thoughtcrime.securesms.dependencies.DatabaseComponent; import org.thoughtcrime.securesms.mms.DecryptableStreamUriLoader; -import org.thoughtcrime.securesms.mms.GlideApp; import org.thoughtcrime.securesms.mms.Slide; import org.thoughtcrime.securesms.mms.SlideDeck; import org.thoughtcrime.securesms.util.AvatarPlaceholderGenerator; @@ -69,10 +66,6 @@ public SingleRecipientNotificationBuilder(@NonNull Context context, @NonNull Not setSmallIcon(R.drawable.ic_notification); setColor(ContextCompat.getColor(context, R.color.accent_green)); setCategory(NotificationCompat.CATEGORY_MESSAGE); - - if (!NotificationChannels.supported()) { - setPriority(TextSecurePreferences.getNotificationPriority(context)); - } } public void setThread(@NonNull Recipient recipient) { @@ -91,7 +84,7 @@ public void setThread(@NonNull Recipient recipient) { try { // AC: For some reason, if not use ".asBitmap()" method, the returned BitmapDrawable // wraps a recycled bitmap and leads to a crash. - Bitmap iconBitmap = GlideApp.with(context.getApplicationContext()) + Bitmap iconBitmap = Glide.with(context.getApplicationContext()) .asBitmap() .load(contactPhoto) .diskCacheStrategy(DiskCacheStrategy.NONE) @@ -179,13 +172,11 @@ public void addActions(@NonNull PendingIntent markReadIntent, Action replyAction = new Action(R.drawable.ic_reply_white_36dp, actionName, quickReplyIntent); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { - replyAction = new Action.Builder(R.drawable.ic_reply_white_36dp, - actionName, - wearableReplyIntent) - .addRemoteInput(new RemoteInput.Builder(DefaultMessageNotifier.EXTRA_REMOTE_REPLY).setLabel(label).build()) - .build(); - } + replyAction = new Action.Builder(R.drawable.ic_reply_white_36dp, + actionName, + wearableReplyIntent) + .addRemoteInput(new RemoteInput.Builder(DefaultMessageNotifier.EXTRA_REMOTE_REPLY).setLabel(label).build()) + .build(); Action wearableReplyAction = new Action.Builder(R.drawable.ic_reply, actionName, @@ -293,7 +284,7 @@ private Bitmap getBigPicture(@NonNull SlideDeck slideDeck) @SuppressWarnings("ConstantConditions") Uri uri = slideDeck.getThumbnailSlide().getThumbnailUri(); - return GlideApp.with(context.getApplicationContext()) + return Glide.with(context.getApplicationContext()) .asBitmap() .load(new DecryptableStreamUriLoader.DecryptableUri(uri)) .diskCacheStrategy(DiskCacheStrategy.NONE) diff --git a/app/src/main/java/org/thoughtcrime/securesms/onboarding/loadaccount/LoadAccount.kt b/app/src/main/java/org/thoughtcrime/securesms/onboarding/loadaccount/LoadAccount.kt index cc1033ee964..56d1c54ea47 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/onboarding/loadaccount/LoadAccount.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/onboarding/loadaccount/LoadAccount.kt @@ -26,7 +26,7 @@ import network.loki.messenger.R import org.thoughtcrime.securesms.onboarding.ui.ContinuePrimaryOutlineButton import org.thoughtcrime.securesms.ui.theme.LocalDimensions import org.thoughtcrime.securesms.ui.theme.PreviewTheme -import org.thoughtcrime.securesms.ui.components.MaybeScanQrCode +import org.thoughtcrime.securesms.ui.components.QRScannerScreen import org.thoughtcrime.securesms.ui.components.SessionOutlinedTextField import org.thoughtcrime.securesms.ui.components.SessionTabRow import org.thoughtcrime.securesms.ui.theme.LocalType @@ -52,7 +52,7 @@ internal fun LoadAccountScreen( ) { page -> when (TITLES[page]) { R.string.sessionRecoveryPassword -> RecoveryPassword(state, onChange, onContinue) - R.string.qrScan -> MaybeScanQrCode(qrErrors, onScan = onScan) + R.string.qrScan -> QRScannerScreen(qrErrors, onScan = onScan) } } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/onboarding/loadaccount/LoadAccountActivity.kt b/app/src/main/java/org/thoughtcrime/securesms/onboarding/loadaccount/LoadAccountActivity.kt index 3c7a6f6a56c..8669db87e4b 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/onboarding/loadaccount/LoadAccountActivity.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/onboarding/loadaccount/LoadAccountActivity.kt @@ -6,6 +6,7 @@ import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.lifecycle.lifecycleScope import dagger.hilt.android.AndroidEntryPoint +import javax.inject.Inject import kotlinx.coroutines.launch import network.loki.messenger.R import org.session.libsession.utilities.TextSecurePreferences @@ -14,7 +15,6 @@ import org.thoughtcrime.securesms.onboarding.manager.LoadAccountManager import org.thoughtcrime.securesms.onboarding.messagenotifications.MessageNotificationsActivity import org.thoughtcrime.securesms.ui.setComposeContent import org.thoughtcrime.securesms.util.start -import javax.inject.Inject @AndroidEntryPoint class LoadAccountActivity : BaseActionBarActivity() { diff --git a/app/src/main/java/org/thoughtcrime/securesms/onboarding/loadaccount/LoadAccountViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/onboarding/loadaccount/LoadAccountViewModel.kt index f98c725deac..bdb67161458 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/onboarding/loadaccount/LoadAccountViewModel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/onboarding/loadaccount/LoadAccountViewModel.kt @@ -4,6 +4,7 @@ import android.app.Application import androidx.lifecycle.AndroidViewModel import androidx.lifecycle.viewModelScope import dagger.hilt.android.lifecycle.HiltViewModel +import javax.inject.Inject import kotlinx.coroutines.channels.BufferOverflow import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableStateFlow @@ -17,7 +18,6 @@ import org.session.libsignal.crypto.MnemonicCodec import org.session.libsignal.crypto.MnemonicCodec.DecodingError.InputTooShort import org.session.libsignal.crypto.MnemonicCodec.DecodingError.InvalidWord import org.thoughtcrime.securesms.crypto.MnemonicUtilities -import javax.inject.Inject class LoadAccountEvent(val mnemonic: ByteArray) @@ -54,6 +54,7 @@ internal class LoadAccountViewModel @Inject constructor( } fun onScanQrCode(string: String) { + viewModelScope.launch { try { codec.decodeMnemonicOrHexAsByteArray(string).let(::onSuccess) diff --git a/app/src/main/java/org/thoughtcrime/securesms/permissions/Permissions.java b/app/src/main/java/org/thoughtcrime/securesms/permissions/Permissions.java index 88ee67cb4de..f38d4c8613d 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/permissions/Permissions.java +++ b/app/src/main/java/org/thoughtcrime/securesms/permissions/Permissions.java @@ -1,9 +1,9 @@ package org.thoughtcrime.securesms.permissions; +import static org.session.libsignal.utilities.Util.SECURE_RANDOM; + import android.app.Activity; -import android.app.AlertDialog; import android.content.Context; -import android.content.DialogInterface; import android.content.Intent; import android.content.pm.PackageManager; import android.net.Uri; @@ -11,9 +11,7 @@ import android.provider.Settings; import android.util.DisplayMetrics; import android.view.Display; -import android.view.ViewGroup; import android.view.WindowManager; -import android.widget.Button; import androidx.annotation.DrawableRes; import androidx.annotation.NonNull; @@ -28,13 +26,10 @@ import org.thoughtcrime.securesms.util.LRUCache; import java.lang.ref.WeakReference; -import java.security.SecureRandom; import java.util.Arrays; import java.util.List; import java.util.Map; -import network.loki.messenger.R; - public class Permissions { private static final Map OUTSTANDING = new LRUCache<>(2); @@ -172,7 +167,7 @@ private void executePermissionsRequestWithRationale(PermissionsRequest request) } private void executePermissionsRequest(PermissionsRequest request) { - int requestCode = new SecureRandom().nextInt(65434) + 100; + int requestCode = SECURE_RANDOM.nextInt(65434) + 100; synchronized (OUTSTANDING) { OUTSTANDING.put(requestCode, request); @@ -215,15 +210,8 @@ private static String[] filterNotGranted(@NonNull Context context, String... per .toArray(new String[0]); } - public static boolean hasAny(@NonNull Context context, String... permissions) { - return Build.VERSION.SDK_INT < Build.VERSION_CODES.M || - Stream.of(permissions).anyMatch(permission -> ContextCompat.checkSelfPermission(context, permission) == PackageManager.PERMISSION_GRANTED); - - } - public static boolean hasAll(@NonNull Context context, String... permissions) { - return Build.VERSION.SDK_INT < Build.VERSION_CODES.M || - Stream.of(permissions).allMatch(permission -> ContextCompat.checkSelfPermission(context, permission) == PackageManager.PERMISSION_GRANTED); + return Stream.of(permissions).allMatch(permission -> ContextCompat.checkSelfPermission(context, permission) == PackageManager.PERMISSION_GRANTED); } diff --git a/app/src/main/java/org/thoughtcrime/securesms/preferences/BlockedContactsAdapter.kt b/app/src/main/java/org/thoughtcrime/securesms/preferences/BlockedContactsAdapter.kt index e0b92bdbea9..e59d86c9129 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/preferences/BlockedContactsAdapter.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/preferences/BlockedContactsAdapter.kt @@ -9,7 +9,7 @@ import androidx.recyclerview.widget.RecyclerView import network.loki.messenger.R import network.loki.messenger.databinding.BlockedContactLayoutBinding import org.session.libsession.utilities.recipients.Recipient -import org.thoughtcrime.securesms.mms.GlideApp +import com.bumptech.glide.Glide import org.thoughtcrime.securesms.util.adapter.SelectableItem typealias SelectableRecipient = SelectableItem @@ -43,7 +43,7 @@ class BlockedContactsAdapter(val viewModel: BlockedContactsViewModel) : ListAdap class ViewHolder(itemView: View): RecyclerView.ViewHolder(itemView) { - val glide = GlideApp.with(itemView) + val glide = Glide.with(itemView) val binding = BlockedContactLayoutBinding.bind(itemView) fun bind(selectable: SelectableRecipient, toggle: (SelectableRecipient) -> Unit) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/preferences/NotificationsPreferenceFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/preferences/NotificationsPreferenceFragment.kt index fa6461acc44..2d262848774 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/preferences/NotificationsPreferenceFragment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/preferences/NotificationsPreferenceFragment.kt @@ -56,14 +56,14 @@ class NotificationsPreferenceFragment : ListSummaryPreferenceFragment() { true } - if (NotificationChannels.supported()) { - prefs.setNotificationRingtone( - NotificationChannels.getMessageRingtone(requireContext()).toString() - ) - prefs.setNotificationVibrateEnabled( - NotificationChannels.getMessageVibrate(requireContext()) - ) - } + + prefs.setNotificationRingtone( + NotificationChannels.getMessageRingtone(requireContext()).toString() + ) + prefs.setNotificationVibrateEnabled( + NotificationChannels.getMessageVibrate(requireContext()) + ) + findPreference(TextSecurePreferences.RINGTONE_PREF)!!.onPreferenceChangeListener = RingtoneSummaryListener() findPreference(TextSecurePreferences.NOTIFICATION_PRIVACY_PREF)!!.onPreferenceChangeListener = NotificationPrivacyListener() findPreference(TextSecurePreferences.VIBRATE_PREF)!!.onPreferenceChangeListener = @@ -99,18 +99,18 @@ class NotificationsPreferenceFragment : ListSummaryPreferenceFragment() { true } initializeListSummary(findPreference(TextSecurePreferences.NOTIFICATION_PRIVACY_PREF) as ListPreference?) - if (NotificationChannels.supported()) { - findPreference(TextSecurePreferences.NOTIFICATION_PRIORITY_PREF)!!.onPreferenceClickListener = - Preference.OnPreferenceClickListener { - val intent = Intent(Settings.ACTION_CHANNEL_NOTIFICATION_SETTINGS) - intent.putExtra( - Settings.EXTRA_CHANNEL_ID, NotificationChannels.getMessagesChannel(requireContext()) - ) - intent.putExtra(Settings.EXTRA_APP_PACKAGE, requireContext().packageName) - startActivity(intent) - true - } - } + + findPreference(TextSecurePreferences.NOTIFICATION_PRIORITY_PREF)!!.onPreferenceClickListener = + Preference.OnPreferenceClickListener { + val intent = Intent(Settings.ACTION_CHANNEL_NOTIFICATION_SETTINGS) + intent.putExtra( + Settings.EXTRA_CHANNEL_ID, NotificationChannels.getMessagesChannel(requireContext()) + ) + intent.putExtra(Settings.EXTRA_APP_PACKAGE, requireContext().packageName) + startActivity(intent) + true + } + initializeRingtoneSummary(findPreference(TextSecurePreferences.RINGTONE_PREF)) initializeMessageVibrateSummary(findPreference(TextSecurePreferences.VIBRATE_PREF) as SwitchPreferenceCompat?) } diff --git a/app/src/main/java/org/thoughtcrime/securesms/preferences/PrivacySettingsPreferenceFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/preferences/PrivacySettingsPreferenceFragment.kt index 21b12496bdc..8404a4f8e28 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/preferences/PrivacySettingsPreferenceFragment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/preferences/PrivacySettingsPreferenceFragment.kt @@ -78,17 +78,11 @@ class PrivacySettingsPreferenceFragment : ListSummaryPreferenceFragment() { title(R.string.CallNotificationBuilder_system_notification_title) text(R.string.CallNotificationBuilder_system_notification_message) button(R.string.activity_notification_settings_title) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - Intent(Settings.ACTION_APP_NOTIFICATION_SETTINGS) - .putExtra(Settings.EXTRA_APP_PACKAGE, BuildConfig.APPLICATION_ID) - } else { - Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS) - .setData(Uri.parse("package:" + BuildConfig.APPLICATION_ID)) - } - .apply { addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) } - .takeIf { IntentUtils.isResolvable(requireContext(), it) }.let { - startActivity(it) - } + Intent(Settings.ACTION_APP_NOTIFICATION_SETTINGS) + .putExtra(Settings.EXTRA_APP_PACKAGE, BuildConfig.APPLICATION_ID) + .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) + .takeIf { IntentUtils.isResolvable(requireContext(), it) } + ?.let { startActivity(it) } } button(R.string.dismiss) } diff --git a/app/src/main/java/org/thoughtcrime/securesms/preferences/QRCodeActivity.kt b/app/src/main/java/org/thoughtcrime/securesms/preferences/QRCodeActivity.kt index 9778a1c8b8c..52cb345fab0 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/preferences/QRCodeActivity.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/preferences/QRCodeActivity.kt @@ -27,7 +27,7 @@ import org.thoughtcrime.securesms.conversation.v2.ConversationActivityV2 import org.thoughtcrime.securesms.database.threadDatabase import org.thoughtcrime.securesms.ui.theme.LocalDimensions import org.thoughtcrime.securesms.ui.theme.LocalColors -import org.thoughtcrime.securesms.ui.components.MaybeScanQrCode +import org.thoughtcrime.securesms.ui.components.QRScannerScreen import org.thoughtcrime.securesms.ui.components.QrImage import org.thoughtcrime.securesms.ui.components.SessionTabRow import org.thoughtcrime.securesms.ui.contentDescription @@ -54,7 +54,7 @@ class QRCodeActivity : PassphraseRequiredActionBarActivity() { } } - fun onScan(string: String) { + private fun onScan(string: String) { if (!PublicKeyValidation.isValid(string)) { errors.tryEmit(getString(R.string.this_qr_code_does_not_contain_an_account_id)) } else if (!isFinishing) { @@ -83,7 +83,7 @@ private fun Tabs(accountId: String, errors: Flow, onScan: (String) -> Un ) { page -> when (TITLES[page]) { R.string.view -> QrPage(accountId) - R.string.scan -> MaybeScanQrCode(errors, onScan = onScan) + R.string.scan -> QRScannerScreen(errors, onScan = onScan) } } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/preferences/RadioOptionAdapter.kt b/app/src/main/java/org/thoughtcrime/securesms/preferences/RadioOptionAdapter.kt index 60b5fb8b2c4..1e0bcd27c0d 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/preferences/RadioOptionAdapter.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/preferences/RadioOptionAdapter.kt @@ -10,7 +10,7 @@ import androidx.recyclerview.widget.ListAdapter import androidx.recyclerview.widget.RecyclerView import network.loki.messenger.R import network.loki.messenger.databinding.ItemSelectableBinding -import org.thoughtcrime.securesms.mms.GlideApp +import com.bumptech.glide.Glide import org.thoughtcrime.securesms.ui.GetString import java.util.Objects @@ -45,7 +45,7 @@ class RadioOptionAdapter( } class ViewHolder(itemView: View): RecyclerView.ViewHolder(itemView) { - val glide = GlideApp.with(itemView) + val glide = Glide.with(itemView) val binding = ItemSelectableBinding.bind(itemView) fun bind(option: RadioOption, isSelected: Boolean, toggleSelection: (RadioOption) -> Unit) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/preferences/SettingsActivity.kt b/app/src/main/java/org/thoughtcrime/securesms/preferences/SettingsActivity.kt index ffc2865d787..fc52541987e 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/preferences/SettingsActivity.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/preferences/SettingsActivity.kt @@ -7,7 +7,6 @@ import android.content.Context import android.content.Intent import android.content.IntentFilter import android.net.Uri -import android.os.Build import android.os.Bundle import android.os.Parcelable import android.util.SparseArray @@ -65,6 +64,7 @@ import org.session.libsession.utilities.TextSecurePreferences import org.session.libsession.utilities.recipients.Recipient import org.session.libsession.utilities.truncateIdForDisplay import org.session.libsignal.utilities.Log +import org.session.libsignal.utilities.Util.SECURE_RANDOM import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity import org.thoughtcrime.securesms.avatar.AvatarSelection import org.thoughtcrime.securesms.components.ProfilePictureView @@ -93,7 +93,6 @@ import org.thoughtcrime.securesms.util.NetworkUtils import org.thoughtcrime.securesms.util.push import org.thoughtcrime.securesms.util.show import java.io.File -import java.security.SecureRandom import javax.inject.Inject @AndroidEntryPoint @@ -210,7 +209,7 @@ class SettingsActivity : PassphraseRequiredActionBarActivity() { override fun onCreateOptionsMenu(menu: Menu): Boolean { menuInflater.inflate(R.menu.settings_general, menu) - if (BuildConfig.DEBUG && Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + if (BuildConfig.DEBUG) { menu.findItem(R.id.action_qr_code)?.contentDescription = resources.getString(R.string.AccessibilityId_view_qr_code) } return true @@ -314,7 +313,7 @@ class SettingsActivity : PassphraseRequiredActionBarActivity() { val userConfig = configFactory.user AvatarHelper.setAvatar(this, Address.fromSerialized(TextSecurePreferences.getLocalNumber(this)!!), profilePicture) - prefs.setProfileAvatarId(SecureRandom().nextInt() ) + prefs.setProfileAvatarId(SECURE_RANDOM.nextInt() ) ProfileKeyUtil.setEncodedProfileKey(this, encodedProfileKey) // Attempt to grab the details we require to update the profile picture diff --git a/app/src/main/java/org/thoughtcrime/securesms/preferences/widgets/SignalListPreference.java b/app/src/main/java/org/thoughtcrime/securesms/preferences/widgets/SignalListPreference.java index d8e90db1562..6cafc6035f7 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/preferences/widgets/SignalListPreference.java +++ b/app/src/main/java/org/thoughtcrime/securesms/preferences/widgets/SignalListPreference.java @@ -2,14 +2,12 @@ import android.content.Context; -import android.os.Build; -import androidx.annotation.RequiresApi; -import androidx.preference.ListPreference; -import androidx.preference.Preference; -import androidx.preference.PreferenceViewHolder; import android.util.AttributeSet; import android.widget.TextView; +import androidx.preference.ListPreference; +import androidx.preference.PreferenceViewHolder; + import network.loki.messenger.R; public class SignalListPreference extends ListPreference { @@ -18,13 +16,11 @@ public class SignalListPreference extends ListPreference { private CharSequence summary; private OnPreferenceClickListener clickListener; - @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) public SignalListPreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); initialize(); } - @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) public SignalListPreference(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); initialize(); diff --git a/app/src/main/java/org/thoughtcrime/securesms/reactions/ReactionRecipientsAdapter.java b/app/src/main/java/org/thoughtcrime/securesms/reactions/ReactionRecipientsAdapter.java index 34a427547b4..79717eabb1d 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/reactions/ReactionRecipientsAdapter.java +++ b/app/src/main/java/org/thoughtcrime/securesms/reactions/ReactionRecipientsAdapter.java @@ -13,7 +13,7 @@ import org.thoughtcrime.securesms.components.ProfilePictureView; import org.thoughtcrime.securesms.components.emoji.EmojiImageView; import org.thoughtcrime.securesms.database.model.MessageId; -import org.thoughtcrime.securesms.mms.GlideApp; +import com.bumptech.glide.Glide; import java.util.Collections; import java.util.List; diff --git a/app/src/main/java/org/thoughtcrime/securesms/repository/ConversationRepository.kt b/app/src/main/java/org/thoughtcrime/securesms/repository/ConversationRepository.kt index 2a00440ddaf..7f10b1eb20d 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/repository/ConversationRepository.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/repository/ConversationRepository.kt @@ -8,7 +8,6 @@ import app.cash.copper.Query import app.cash.copper.flow.observeQuery import dagger.hilt.android.qualifiers.ApplicationContext import kotlin.coroutines.resume -import kotlin.coroutines.resumeWithException import kotlin.coroutines.suspendCoroutine import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.map @@ -59,15 +58,15 @@ interface ConversationRepository { fun deleteLocally(recipient: Recipient, message: MessageRecord) fun deleteAllLocalMessagesInThreadFromSenderOfMessage(messageRecord: MessageRecord) fun setApproved(recipient: Recipient, isApproved: Boolean) - suspend fun deleteForEveryone(threadId: Long, recipient: Recipient, message: MessageRecord): ResultOf + suspend fun deleteForEveryone(threadId: Long, recipient: Recipient, message: MessageRecord): Result fun buildUnsendRequest(recipient: Recipient, message: MessageRecord): UnsendRequest? - suspend fun deleteMessageWithoutUnsendRequest(threadId: Long, messages: Set): ResultOf - suspend fun banUser(threadId: Long, recipient: Recipient): ResultOf - suspend fun banAndDeleteAll(threadId: Long, recipient: Recipient): ResultOf - suspend fun deleteThread(threadId: Long): ResultOf - suspend fun deleteMessageRequest(thread: ThreadRecord): ResultOf - suspend fun clearAllMessageRequests(block: Boolean): ResultOf - suspend fun acceptMessageRequest(threadId: Long, recipient: Recipient): ResultOf + suspend fun deleteMessageWithoutUnsendRequest(threadId: Long, messages: Set): Result + suspend fun banUser(threadId: Long, recipient: Recipient): Result + suspend fun banAndDeleteAll(threadId: Long, recipient: Recipient): Result + suspend fun deleteThread(threadId: Long): Result + suspend fun deleteMessageRequest(thread: ThreadRecord): Result + suspend fun clearAllMessageRequests(block: Boolean): Result + suspend fun acceptMessageRequest(threadId: Long, recipient: Recipient): Result fun declineMessageRequest(threadId: Long) fun hasReceived(threadId: Long): Boolean } @@ -185,7 +184,7 @@ class DefaultConversationRepository @Inject constructor( threadId: Long, recipient: Recipient, message: MessageRecord - ): ResultOf = suspendCoroutine { continuation -> + ): Result = suspendCoroutine { continuation -> buildUnsendRequest(recipient, message)?.let { unsendRequest -> MessageSender.send(unsendRequest, recipient.address) } @@ -196,10 +195,10 @@ class DefaultConversationRepository @Inject constructor( OpenGroupApi.deleteMessage(messageServerID, openGroup.room, openGroup.server) .success { messageDataProvider.deleteMessage(message.id, !message.isMms) - continuation.resume(ResultOf.Success(Unit)) + continuation.resume(Result.success(Unit)) }.fail { error -> Log.w("TAG", "Call to OpenGroupApi.deleteForEveryone failed - attempting to resume..") - continuation.resumeWithException(error) + continuation.resume(Result.failure(error)) } } @@ -229,10 +228,10 @@ class DefaultConversationRepository @Inject constructor( } SnodeAPI.deleteMessage(publicKey, listOf(serverHash)) .success { - continuation.resume(ResultOf.Success(Unit)) + continuation.resume(Result.success(Unit)) }.fail { error -> Log.w("ConversationRepository", "Call to SnodeAPI.deleteMessage failed - attempting to resume..") - continuation.resumeWithException(error) + continuation.resume(Result.failure(error)) } } } @@ -250,7 +249,7 @@ class DefaultConversationRepository @Inject constructor( override suspend fun deleteMessageWithoutUnsendRequest( threadId: Long, messages: Set - ): ResultOf = suspendCoroutine { continuation -> + ): Result = suspendCoroutine { continuation -> val openGroup = lokiThreadDb.getOpenGroupChat(threadId) if (openGroup != null) { val messageServerIDs = mutableMapOf() @@ -264,7 +263,7 @@ class DefaultConversationRepository @Inject constructor( .success { messageDataProvider.deleteMessage(message.id, !message.isMms) }.fail { error -> - continuation.resumeWithException(error) + continuation.resume(Result.failure(error)) } } } else { @@ -276,22 +275,22 @@ class DefaultConversationRepository @Inject constructor( } } } - continuation.resume(ResultOf.Success(Unit)) + continuation.resume(Result.success(Unit)) } - override suspend fun banUser(threadId: Long, recipient: Recipient): ResultOf = + override suspend fun banUser(threadId: Long, recipient: Recipient): Result = suspendCoroutine { continuation -> val accountID = recipient.address.toString() val openGroup = lokiThreadDb.getOpenGroupChat(threadId)!! OpenGroupApi.ban(accountID, openGroup.room, openGroup.server) .success { - continuation.resume(ResultOf.Success(Unit)) + continuation.resume(Result.success(Unit)) }.fail { error -> - continuation.resumeWithException(error) + continuation.resume(Result.failure(error)) } } - override suspend fun banAndDeleteAll(threadId: Long, recipient: Recipient): ResultOf = + override suspend fun banAndDeleteAll(threadId: Long, recipient: Recipient): Result = suspendCoroutine { continuation -> // Note: This accountId could be the blinded Id val accountID = recipient.address.toString() @@ -299,25 +298,25 @@ class DefaultConversationRepository @Inject constructor( OpenGroupApi.banAndDeleteAll(accountID, openGroup.room, openGroup.server) .success { - continuation.resume(ResultOf.Success(Unit)) + continuation.resume(Result.success(Unit)) }.fail { error -> - continuation.resumeWithException(error) + continuation.resume(Result.failure(error)) } } - override suspend fun deleteThread(threadId: Long): ResultOf { + override suspend fun deleteThread(threadId: Long): Result { sessionJobDb.cancelPendingMessageSendJobs(threadId) storage.deleteConversation(threadId) - return ResultOf.Success(Unit) + return Result.success(Unit) } - override suspend fun deleteMessageRequest(thread: ThreadRecord): ResultOf { + override suspend fun deleteMessageRequest(thread: ThreadRecord): Result { sessionJobDb.cancelPendingMessageSendJobs(thread.threadId) storage.deleteConversation(thread.threadId) - return ResultOf.Success(Unit) + return Result.success(Unit) } - override suspend fun clearAllMessageRequests(block: Boolean): ResultOf { + override suspend fun clearAllMessageRequests(block: Boolean): Result { threadDb.readerFor(threadDb.unapprovedConversationList).use { reader -> while (reader.next != null) { deleteMessageRequest(reader.current) @@ -325,18 +324,18 @@ class DefaultConversationRepository @Inject constructor( if (block) { setBlocked(recipient, true) } } } - return ResultOf.Success(Unit) + return Result.success(Unit) } - override suspend fun acceptMessageRequest(threadId: Long, recipient: Recipient): ResultOf = suspendCoroutine { continuation -> + override suspend fun acceptMessageRequest(threadId: Long, recipient: Recipient): Result = suspendCoroutine { continuation -> storage.setRecipientApproved(recipient, true) val message = MessageRequestResponse(true) MessageSender.send(message, Destination.from(recipient.address), isSyncMessage = recipient.isLocalNumber) .success { threadDb.setHasSent(threadId, true) - continuation.resume(ResultOf.Success(Unit)) + continuation.resume(Result.success(Unit)) }.fail { error -> - continuation.resumeWithException(error) + continuation.resume(Result.failure(error)) } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/repository/ResultOf.kt b/app/src/main/java/org/thoughtcrime/securesms/repository/ResultOf.kt deleted file mode 100644 index 96ae97e510c..00000000000 --- a/app/src/main/java/org/thoughtcrime/securesms/repository/ResultOf.kt +++ /dev/null @@ -1,43 +0,0 @@ -package org.thoughtcrime.securesms.repository - -import kotlinx.coroutines.CancellationException - -sealed class ResultOf { - - data class Success(val value: R) : ResultOf() - - data class Failure(val throwable: Throwable) : ResultOf() - - inline fun onFailure(block: (throwable: Throwable) -> Unit) = this.also { - if (this is Failure) { - block(throwable) - } - } - - inline fun onSuccess(block: (value: T) -> Unit) = this.also { - if (this is Success) { - block(value) - } - } - - inline fun flatMap(mapper: (T) -> R): ResultOf = when (this) { - is Success -> wrap { mapper(value) } - is Failure -> Failure(throwable) - } - - fun getOrThrow(): T = when (this) { - is Success -> value - is Failure -> throw throwable - } - - companion object { - inline fun wrap(block: () -> T): ResultOf = - try { - Success(block()) - } catch (e: CancellationException) { - throw e - } catch (e: Exception) { - Failure(e) - } - } -} diff --git a/app/src/main/java/org/thoughtcrime/securesms/scribbles/StickerSelectFragment.java b/app/src/main/java/org/thoughtcrime/securesms/scribbles/StickerSelectFragment.java index ae19d13b647..70547b46702 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/scribbles/StickerSelectFragment.java +++ b/app/src/main/java/org/thoughtcrime/securesms/scribbles/StickerSelectFragment.java @@ -37,13 +37,13 @@ import network.loki.messenger.R; -import org.thoughtcrime.securesms.mms.GlideApp; -import org.thoughtcrime.securesms.mms.GlideRequests; +import com.bumptech.glide.Glide; +import com.bumptech.glide.RequestManager; public class StickerSelectFragment extends Fragment implements LoaderManager.LoaderCallbacks { private RecyclerView recyclerView; - private GlideRequests glideRequests; + private RequestManager glideRequests; private String assetDirectory; private StickerSelectionListener listener; @@ -71,7 +71,7 @@ public static StickerSelectFragment newInstance(String assetDirectory) { public void onActivityCreated(Bundle bundle) { super.onActivityCreated(bundle); - this.glideRequests = GlideApp.with(this); + this.glideRequests = Glide.with(this); this.assetDirectory = getArguments().getString("assetDirectory"); getLoaderManager().initLoader(0, null, this); @@ -99,11 +99,11 @@ public void setListener(StickerSelectionListener listener) { class StickersAdapter extends RecyclerView.Adapter { - private final GlideRequests glideRequests; + private final RequestManager glideRequests; private final String[] stickerFiles; private final LayoutInflater layoutInflater; - StickersAdapter(@NonNull Context context, @NonNull GlideRequests glideRequests, @NonNull String[] stickerFiles) { + StickersAdapter(@NonNull Context context, @NonNull RequestManager glideRequests, @NonNull String[] stickerFiles) { this.glideRequests = glideRequests; this.stickerFiles = stickerFiles; this.layoutInflater = LayoutInflater.from(context); diff --git a/app/src/main/java/org/thoughtcrime/securesms/scribbles/UriGlideRenderer.java b/app/src/main/java/org/thoughtcrime/securesms/scribbles/UriGlideRenderer.java index 15d4d524346..f7e028c0fd8 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/scribbles/UriGlideRenderer.java +++ b/app/src/main/java/org/thoughtcrime/securesms/scribbles/UriGlideRenderer.java @@ -11,6 +11,7 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import com.bumptech.glide.RequestBuilder; import com.bumptech.glide.load.engine.DiskCacheStrategy; import com.bumptech.glide.request.target.SimpleTarget; import com.bumptech.glide.request.transition.Transition; @@ -20,8 +21,7 @@ import org.thoughtcrime.securesms.imageeditor.Renderer; import org.thoughtcrime.securesms.imageeditor.RendererContext; import org.thoughtcrime.securesms.mms.DecryptableStreamUriLoader; -import org.thoughtcrime.securesms.mms.GlideApp; -import org.thoughtcrime.securesms.mms.GlideRequest; +import com.bumptech.glide.Glide; import java.util.concurrent.ExecutionException; @@ -97,8 +97,8 @@ public void onResourceReady(@NonNull Bitmap resource, @Nullable Transition getBitmapGlideRequest(@NonNull Context context) { - return GlideApp.with(context) + private RequestBuilder getBitmapGlideRequest(@NonNull Context context) { + return Glide.with(context) .asBitmap() .diskCacheStrategy(DiskCacheStrategy.NONE) .override(maxWidth, maxHeight) diff --git a/app/src/main/java/org/thoughtcrime/securesms/scribbles/widget/VerticalSlideColorPicker.java b/app/src/main/java/org/thoughtcrime/securesms/scribbles/widget/VerticalSlideColorPicker.java index bd9afcadff6..1cf46baf4c6 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/scribbles/widget/VerticalSlideColorPicker.java +++ b/app/src/main/java/org/thoughtcrime/securesms/scribbles/widget/VerticalSlideColorPicker.java @@ -23,7 +23,6 @@ package org.thoughtcrime.securesms.scribbles.widget; -import android.annotation.TargetApi; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Bitmap; @@ -34,7 +33,6 @@ import android.graphics.Path; import android.graphics.RectF; import android.graphics.Shader; -import android.os.Build; import android.util.AttributeSet; import android.view.MotionEvent; import android.view.View; @@ -98,12 +96,6 @@ public VerticalSlideColorPicker(Context context, AttributeSet attrs, int defStyl init(); } - @TargetApi(Build.VERSION_CODES.LOLLIPOP) - public VerticalSlideColorPicker(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { - super(context, attrs, defStyleAttr, defStyleRes); - init(); - } - private void init() { setWillNotDraw(false); diff --git a/app/src/main/java/org/thoughtcrime/securesms/service/DirectShareService.java b/app/src/main/java/org/thoughtcrime/securesms/service/DirectShareService.java index 0516dc2856f..c8a67926a1f 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/service/DirectShareService.java +++ b/app/src/main/java/org/thoughtcrime/securesms/service/DirectShareService.java @@ -19,7 +19,7 @@ import org.thoughtcrime.securesms.database.ThreadDatabase; import org.thoughtcrime.securesms.database.model.ThreadRecord; import org.thoughtcrime.securesms.dependencies.DatabaseComponent; -import org.thoughtcrime.securesms.mms.GlideApp; +import com.bumptech.glide.Glide; import org.thoughtcrime.securesms.util.BitmapUtil; import java.util.LinkedList; @@ -50,7 +50,7 @@ public List onGetChooserTargets(ComponentName targetActivityName, if (recipient.getContactPhoto() != null) { try { - avatar = GlideApp.with(this) + avatar = Glide.with(this) .asBitmap() .load(recipient.getContactPhoto()) .circleCrop() diff --git a/app/src/main/java/org/thoughtcrime/securesms/service/GenericForegroundService.java b/app/src/main/java/org/thoughtcrime/securesms/service/GenericForegroundService.java deleted file mode 100644 index 52a259d5bec..00000000000 --- a/app/src/main/java/org/thoughtcrime/securesms/service/GenericForegroundService.java +++ /dev/null @@ -1,128 +0,0 @@ -package org.thoughtcrime.securesms.service; - - -import android.app.PendingIntent; -import android.app.Service; -import android.content.Context; -import android.content.Intent; -import android.os.IBinder; - -import androidx.annotation.DrawableRes; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.core.app.NotificationCompat; -import androidx.core.content.ContextCompat; - -import org.session.libsignal.utilities.Log; -import org.session.libsignal.utilities.guava.Preconditions; -import org.thoughtcrime.securesms.home.HomeActivity; -import org.thoughtcrime.securesms.notifications.NotificationChannels; - -import network.loki.messenger.R; - -public class GenericForegroundService extends Service { - - private static final String TAG = GenericForegroundService.class.getSimpleName(); - - private static final int NOTIFICATION_ID = 827353982; - private static final String EXTRA_TITLE = "extra_title"; - private static final String EXTRA_CHANNEL_ID = "extra_channel_id"; - private static final String EXTRA_ICON_RES = "extra_icon_res"; - - private static final String ACTION_START = "start"; - private static final String ACTION_STOP = "stop"; - - private int foregroundCount; - private String activeTitle; - private String activeChannelId; - private int activeIconRes; - - @Override - public void onCreate() { - - } - - @Override - public int onStartCommand(Intent intent, int flags, int startId) { - synchronized (GenericForegroundService.class) { - if (intent != null && ACTION_START.equals(intent.getAction())) handleStart(intent); - else if (intent != null && ACTION_STOP.equals(intent.getAction())) handleStop(); - else throw new IllegalStateException("Action needs to be START or STOP."); - - return START_NOT_STICKY; - } - } - - - private void handleStart(@NonNull Intent intent) { - String title = Preconditions.checkNotNull(intent.getStringExtra(EXTRA_TITLE)); - String channelId = Preconditions.checkNotNull(intent.getStringExtra(EXTRA_CHANNEL_ID)); - int iconRes = intent.getIntExtra(EXTRA_ICON_RES, R.drawable.ic_notification); - - Log.i(TAG, "handleStart() Title: " + title + " ChannelId: " + channelId); - - foregroundCount++; - - if (foregroundCount == 1) { - Log.d(TAG, "First request. Title: " + title + " ChannelId: " + channelId); - activeTitle = title; - activeChannelId = channelId; - activeIconRes = iconRes; - } - - postObligatoryForegroundNotification(activeTitle, activeChannelId, activeIconRes); - } - - private void handleStop() { - Log.i(TAG, "handleStop()"); - - postObligatoryForegroundNotification(activeTitle, activeChannelId, activeIconRes); - - foregroundCount--; - - if (foregroundCount == 0) { - Log.d(TAG, "Last request. Ending foreground service."); - stopForeground(true); - stopSelf(); - } - } - - private void postObligatoryForegroundNotification(String title, String channelId, @DrawableRes int iconRes) { - startForeground(NOTIFICATION_ID, new NotificationCompat.Builder(this, channelId) - .setSmallIcon(iconRes) - .setContentTitle(title) - .setContentIntent(PendingIntent.getActivity(this, 0, new Intent(this, HomeActivity.class), PendingIntent.FLAG_IMMUTABLE)) - .build()); - } - - @Nullable - @Override - public IBinder onBind(Intent intent) { - return null; - } - - public static void startForegroundTask(@NonNull Context context, @NonNull String task) { - startForegroundTask(context, task, NotificationChannels.OTHER); - } - - public static void startForegroundTask(@NonNull Context context, @NonNull String task, @NonNull String channelId) { - startForegroundTask(context, task, channelId, R.drawable.ic_notification); - } - - public static void startForegroundTask(@NonNull Context context, @NonNull String task, @NonNull String channelId, @DrawableRes int iconRes) { - Intent intent = new Intent(context, GenericForegroundService.class); - intent.setAction(ACTION_START); - intent.putExtra(EXTRA_TITLE, task); - intent.putExtra(EXTRA_CHANNEL_ID, channelId); - intent.putExtra(EXTRA_ICON_RES, iconRes); - - ContextCompat.startForegroundService(context, intent); - } - - public static void stopForegroundTask(@NonNull Context context) { - Intent intent = new Intent(context, GenericForegroundService.class); - intent.setAction(ACTION_STOP); - - ContextCompat.startForegroundService(context, intent); - } -} diff --git a/app/src/main/java/org/thoughtcrime/securesms/service/KeyCachingService.java b/app/src/main/java/org/thoughtcrime/securesms/service/KeyCachingService.java index f919af7ad66..cf250665ef6 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/service/KeyCachingService.java +++ b/app/src/main/java/org/thoughtcrime/securesms/service/KeyCachingService.java @@ -35,6 +35,7 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.core.app.NotificationCompat; +import androidx.core.app.ServiceCompat; import org.session.libsession.utilities.ServiceUtil; import org.session.libsession.utilities.TextSecurePreferences; @@ -122,7 +123,7 @@ public void setMasterSecret(final Object masterSecret) { KeyCachingService.masterSecret = masterSecret; foregroundService(); - + new AsyncTask() { @Override protected Void doInBackground(Void... params) { @@ -252,11 +253,18 @@ private void foregroundService() { builder.setContentIntent(buildLaunchIntent()); stopForeground(true); + + int type = 0; if (Build.VERSION.SDK_INT >= 34) { - startForeground(SERVICE_RUNNING_ID, builder.build(), ServiceInfo.FOREGROUND_SERVICE_TYPE_SPECIAL_USE); - } else { - startForeground(SERVICE_RUNNING_ID, builder.build()); + type = ServiceInfo.FOREGROUND_SERVICE_TYPE_SPECIAL_USE; } + + ServiceCompat.startForeground( + this, + SERVICE_RUNNING_ID, + builder.build(), + type + ); } private PendingIntent buildLockIntent() { diff --git a/app/src/main/java/org/thoughtcrime/securesms/service/WebRtcCallService.kt b/app/src/main/java/org/thoughtcrime/securesms/service/WebRtcCallService.kt index f40a075d8d7..78f1a554708 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/service/WebRtcCallService.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/service/WebRtcCallService.kt @@ -7,9 +7,12 @@ import android.content.Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT import android.content.Intent.FLAG_ACTIVITY_NEW_TASK import android.content.IntentFilter import android.content.pm.PackageManager +import android.content.pm.ServiceInfo import android.media.AudioManager +import android.os.Build import android.os.ResultReceiver import android.telephony.TelephonyManager +import androidx.core.app.ServiceCompat import androidx.core.content.ContextCompat import androidx.core.os.bundleOf import androidx.lifecycle.LifecycleService @@ -723,9 +726,11 @@ class WebRtcCallService : LifecycleService(), CallManager.WebRtcListener { private fun setCallInProgressNotification(type: Int, recipient: Recipient?) { try { - startForeground( + ServiceCompat.startForeground( + this, CallNotificationBuilder.WEBRTC_NOTIFICATION, - CallNotificationBuilder.getCallInProgressNotification(this, type, recipient) + CallNotificationBuilder.getCallInProgressNotification(this, type, recipient), + if (Build.VERSION.SDK_INT >= 30) ServiceInfo.FOREGROUND_SERVICE_TYPE_MICROPHONE else 0 ) } catch (e: IllegalStateException) { Log.e(TAG, "Failed to setCallInProgressNotification as a foreground service for type: ${type}, trying to update instead", e) diff --git a/app/src/main/java/org/thoughtcrime/securesms/ui/AlertDialog.kt b/app/src/main/java/org/thoughtcrime/securesms/ui/AlertDialog.kt index df66cef4718..5894e1072a2 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/ui/AlertDialog.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/ui/AlertDialog.kt @@ -1,6 +1,7 @@ package org.thoughtcrime.securesms.ui import androidx.compose.foundation.background +import androidx.compose.foundation.border import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.IntrinsicSize @@ -26,6 +27,7 @@ import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp import network.loki.messenger.R import org.thoughtcrime.securesms.ui.theme.LocalColors import org.thoughtcrime.securesms.ui.theme.LocalDimensions @@ -56,11 +58,17 @@ fun AlertDialog( onDismissRequest = onDismissRequest, content = { Box( - modifier = Modifier.background(color = LocalColors.current.backgroundSecondary, + modifier = Modifier.background( + color = LocalColors.current.backgroundSecondary, shape = MaterialTheme.shapes.small) + .border( + width = 1.dp, + color = LocalColors.current.borders, + shape = MaterialTheme.shapes.small) + ) { // only show the 'x' button is required - if(showCloseButton) { + if (showCloseButton) { IconButton( onClick = onDismissRequest, modifier = Modifier.align(Alignment.TopEnd) @@ -78,7 +86,7 @@ fun AlertDialog( horizontalAlignment = Alignment.CenterHorizontally, modifier = Modifier .fillMaxWidth() - .padding(top = LocalDimensions.current.smallSpacing) + .padding(top = LocalDimensions.current.spacing) .padding(horizontal = LocalDimensions.current.smallSpacing) ) { title?.let { @@ -123,7 +131,12 @@ fun AlertDialog( } @Composable -fun DialogButton(text: String, modifier: Modifier, color: Color = Color.Unspecified, onClick: () -> Unit) { +fun DialogButton( + text: String, + modifier: Modifier, + color: Color = Color.Unspecified, + onClick: () -> Unit +) { TextButton( modifier = modifier, shape = RectangleShape, @@ -135,8 +148,7 @@ fun DialogButton(text: String, modifier: Modifier, color: Color = Color.Unspecif style = LocalType.current.large.bold(), textAlign = TextAlign.Center, modifier = Modifier.padding( - top = LocalDimensions.current.smallSpacing, - bottom = LocalDimensions.current.spacing + vertical = LocalDimensions.current.smallSpacing ) ) } @@ -144,7 +156,7 @@ fun DialogButton(text: String, modifier: Modifier, color: Color = Color.Unspecif @Preview @Composable -fun PreviewSimpleDialog(){ +fun PreviewSimpleDialog() { PreviewTheme { AlertDialog( onDismissRequest = {}, @@ -166,7 +178,7 @@ fun PreviewSimpleDialog(){ @Preview @Composable -fun PreviewXCloseDialog(){ +fun PreviewXCloseDialog() { PreviewTheme { AlertDialog( title = stringResource(R.string.urlOpen), diff --git a/app/src/main/java/org/thoughtcrime/securesms/ui/components/AppBar.kt b/app/src/main/java/org/thoughtcrime/securesms/ui/components/AppBar.kt index b64a482d8a1..ff9369e8a24 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/ui/components/AppBar.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/ui/components/AppBar.kt @@ -1,53 +1,193 @@ package org.thoughtcrime.securesms.ui.components -import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.RowScope +import androidx.compose.material3.CenterAlignedTopAppBar +import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.Text +import androidx.compose.material3.TopAppBarDefaults +import androidx.compose.material3.TopAppBarScrollBehavior import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.PreviewParameter import network.loki.messenger.R +import org.thoughtcrime.securesms.ui.Divider +import org.thoughtcrime.securesms.ui.theme.LocalColors import org.thoughtcrime.securesms.ui.theme.LocalDimensions import org.thoughtcrime.securesms.ui.theme.LocalType import org.thoughtcrime.securesms.ui.theme.PreviewTheme import org.thoughtcrime.securesms.ui.theme.SessionColorsParameterProvider import org.thoughtcrime.securesms.ui.theme.ThemeColors +@OptIn(ExperimentalMaterial3Api::class) @Preview @Composable fun AppBarPreview( @PreviewParameter(SessionColorsParameterProvider::class) colors: ThemeColors ) { PreviewTheme(colors) { - AppBar(title = "Title", {}, {}) + Column() { + BasicAppBar(title = "Basic App Bar") + Divider() + BasicAppBar( + title = "Basic App Bar With Color", + backgroundColor = LocalColors.current.backgroundSecondary + ) + Divider() + BackAppBar(title = "Back Bar", onBack = {}) + Divider() + ActionAppBar( + title = "Action mode", + actionMode = true, + actionModeActions = { + IconButton(onClick = {}) { + Icon( + painter = painterResource(id = R.drawable.check), + contentDescription = "check" + ) + } + }) + } } } +/** + * Basic structure for an app bar. + * It can be passed navigation content and actions + */ +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun BasicAppBar( + title: String, + modifier: Modifier = Modifier, + scrollBehavior: TopAppBarScrollBehavior? = null, + backgroundColor: Color = LocalColors.current.background, + navigationIcon: @Composable () -> Unit = {}, + actions: @Composable RowScope.() -> Unit = {}, +) { + CenterAlignedTopAppBar( + modifier = modifier, + title = { + AppBarText(title = title) + }, + colors = appBarColors(backgroundColor), + navigationIcon = navigationIcon, + actions = actions, + scrollBehavior = scrollBehavior + ) +} + +/** + * Common use case of an app bar with a back button + */ +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun BackAppBar( + title: String, + onBack: () -> Unit, + modifier: Modifier = Modifier, + scrollBehavior: TopAppBarScrollBehavior? = null, + backgroundColor: Color = LocalColors.current.background, + actions: @Composable RowScope.() -> Unit = {}, +) { + BasicAppBar( + modifier = modifier, + title = title, + navigationIcon = { + AppBarBackIcon(onBack = onBack) + }, + actions = actions, + scrollBehavior = scrollBehavior, + backgroundColor = backgroundColor + ) +} + +@ExperimentalMaterial3Api @Composable -fun AppBar(title: String, onClose: () -> Unit = {}, onBack: (() -> Unit)? = null) { - Row(modifier = Modifier.height(LocalDimensions.current.appBarHeight), verticalAlignment = Alignment.CenterVertically) { - Box(contentAlignment = Alignment.Center, modifier = Modifier.size(LocalDimensions.current.appBarHeight)) { - onBack?.let { - IconButton(onClick = it) { - Icon(painter = painterResource(id = R.drawable.ic_prev), contentDescription = "back") +fun ActionAppBar( + title: String, + modifier: Modifier = Modifier, + scrollBehavior: TopAppBarScrollBehavior? = null, + backgroundColor: Color = LocalColors.current.background, + actionMode: Boolean = false, + actionModeTitle: String = "", + navigationIcon: @Composable () -> Unit = {}, + actions: @Composable RowScope.() -> Unit = {}, + actionModeActions: @Composable (RowScope.() -> Unit) = {}, +) { + CenterAlignedTopAppBar( + modifier = modifier, + title = { + if (!actionMode) { + AppBarText(title = title) + } + }, + navigationIcon = { + if (actionMode) { + Row( + horizontalArrangement = Arrangement.spacedBy(LocalDimensions.current.xxxsSpacing), + verticalAlignment = Alignment.CenterVertically + ) { + navigationIcon() + AppBarText(title = actionModeTitle) } + } else { + navigationIcon() } - } - Spacer(modifier = Modifier.weight(1f)) - Text(text = title, style = LocalType.current.h4) - Spacer(modifier = Modifier.weight(1f)) - Box(contentAlignment = Alignment.Center, modifier = Modifier.size(LocalDimensions.current.appBarHeight)) { - IconButton(onClick = onClose) { - Icon(painter = painterResource(id = R.drawable.ic_x), contentDescription = "close") + }, + scrollBehavior = scrollBehavior, + colors = appBarColors(backgroundColor), + actions = { + if (actionMode) { + actionModeActions() + } else { + actions() } } + ) +} + +@Composable +fun AppBarText(title: String) { + Text(text = title, style = LocalType.current.h4) +} + +@Composable +fun AppBarBackIcon(onBack: () -> Unit) { + IconButton(onClick = onBack) { + Icon( + painter = painterResource(id = R.drawable.ic_baseline_arrow_back_24), + contentDescription = stringResource(R.string.back) + ) } } + +@Composable +fun AppBarCloseIcon(onClose: () -> Unit) { + IconButton(onClick = onClose) { + Icon( + painter = painterResource(id = R.drawable.ic_x), + contentDescription = stringResource(id = R.string.close) + ) + } +} + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun appBarColors(backgroundColor: Color) = TopAppBarDefaults.centerAlignedTopAppBarColors() + .copy( + containerColor = backgroundColor, + scrolledContainerColor = backgroundColor, + navigationIconContentColor = LocalColors.current.text, + titleContentColor = LocalColors.current.text, + actionIconContentColor = LocalColors.current.text + ) diff --git a/app/src/main/java/org/thoughtcrime/securesms/ui/components/QR.kt b/app/src/main/java/org/thoughtcrime/securesms/ui/components/QR.kt index c58f7dc97ff..9661b3bc06a 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/ui/components/QR.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/ui/components/QR.kt @@ -47,25 +47,27 @@ import com.google.accompanist.permissions.ExperimentalPermissionsApi import com.google.accompanist.permissions.isGranted import com.google.accompanist.permissions.rememberPermissionState import com.google.accompanist.permissions.shouldShowRationale -import com.google.mlkit.vision.barcode.BarcodeScanner -import com.google.mlkit.vision.barcode.BarcodeScannerOptions -import com.google.mlkit.vision.barcode.BarcodeScanning -import com.google.mlkit.vision.barcode.common.Barcode -import com.google.mlkit.vision.common.InputImage +import com.google.zxing.BinaryBitmap +import com.google.zxing.ChecksumException +import com.google.zxing.FormatException +import com.google.zxing.NotFoundException +import com.google.zxing.PlanarYUVLuminanceSource +import com.google.zxing.Result +import com.google.zxing.common.HybridBinarizer +import com.google.zxing.qrcode.QRCodeReader +import java.util.concurrent.Executors import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.launch import network.loki.messenger.R import org.session.libsignal.utilities.Log -import org.thoughtcrime.securesms.ui.theme.LocalColors import org.thoughtcrime.securesms.ui.theme.LocalDimensions import org.thoughtcrime.securesms.ui.theme.LocalType -import java.util.concurrent.Executors private const val TAG = "NewMessageFragment" @OptIn(ExperimentalPermissionsApi::class) @Composable -fun MaybeScanQrCode( +fun QRScannerScreen( errors: Flow, onClickSettings: () -> Unit = LocalContext.current.run { { Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS).apply { @@ -137,17 +139,13 @@ fun ScanQrCode(errors: Flow, onScan: (String) -> Unit) { runCatching { cameraProvider.get().unbindAll() - val options = BarcodeScannerOptions.Builder() - .setBarcodeFormats(Barcode.FORMAT_QR_CODE) - .build() - val scanner = BarcodeScanning.getClient(options) - cameraProvider.get().bindToLifecycle( LocalLifecycleOwner.current, selector, preview, - buildAnalysisUseCase(scanner, onScan) + buildAnalysisUseCase(QRCodeReader(), onScan) ) + }.onFailure { Log.e(TAG, "error binding camera", it) } DisposableEffect(cameraProvider) { @@ -211,32 +209,51 @@ fun ScanQrCode(errors: Flow, onScan: (String) -> Unit) { @SuppressLint("UnsafeOptInUsageError") private fun buildAnalysisUseCase( - scanner: BarcodeScanner, + scanner: QRCodeReader, onBarcodeScanned: (String) -> Unit ): ImageAnalysis = ImageAnalysis.Builder() .setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST) .build().apply { - setAnalyzer(Executors.newSingleThreadExecutor(), Analyzer(scanner, onBarcodeScanned)) + setAnalyzer(Executors.newSingleThreadExecutor(), QRCodeAnalyzer(scanner, onBarcodeScanned)) } -class Analyzer( - private val scanner: BarcodeScanner, +class QRCodeAnalyzer( + private val qrCodeReader: QRCodeReader, private val onBarcodeScanned: (String) -> Unit ): ImageAnalysis.Analyzer { + + // Note: This analyze method is called once per frame of the camera feed. @SuppressLint("UnsafeOptInUsageError") override fun analyze(image: ImageProxy) { - InputImage.fromMediaImage( - image.image!!, - image.imageInfo.rotationDegrees - ).let(scanner::process).apply { - addOnSuccessListener { barcodes -> - barcodes.forEach { - it.rawValue?.let(onBarcodeScanned) - } - } - addOnCompleteListener { - image.close() - } + // Grab the image data as a byte array so we can generate a PlanarYUVLuminanceSource from it + val buffer = image.planes[0].buffer + buffer.rewind() + val imageBytes = ByteArray(buffer.capacity()) + buffer.get(imageBytes) // IMPORTANT: This transfers data from the buffer INTO the imageBytes array, although it looks like it would go the other way around! + + // ZXing requires data as a BinaryBitmap to scan for QR codes, and to generate that we need to feed it a PlanarYUVLuminanceSource + val luminanceSource = PlanarYUVLuminanceSource(imageBytes, image.width, image.height, 0, 0, image.width, image.height, false) + val binaryBitmap = BinaryBitmap(HybridBinarizer(luminanceSource)) + + // Attempt to extract a QR code from the binary bitmap, and pass it through to our `onBarcodeScanned` method if we find one + try { + val result: Result = qrCodeReader.decode(binaryBitmap) + val resultTxt = result.text + // No need to close the image here - it'll always make it to the end, and calling `onBarcodeScanned` + // with a valid contact / recovery phrase / community code will stop calling this `analyze` method. + onBarcodeScanned(resultTxt) + } + catch (nfe: NotFoundException) { /* Hits if there is no QR code in the image */ } + catch (fe: FormatException) { /* Hits if we found a QR code but failed to decode it */ } + catch (ce: ChecksumException) { /* Hits if we found a QR code which is corrupted */ } + catch (e: Exception) { + // Hits if there's a genuine problem + Log.e("QR", "error", e) } + + // Remember to close the image when we're done with it! + // IMPORTANT: It is CLOSING the image that allows this method to run again! If we don't + // close the image this method runs precisely ONCE and that's it, which is essentially useless. + image.close() } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/util/BitmapUtil.java b/app/src/main/java/org/thoughtcrime/securesms/util/BitmapUtil.java index 3bb0d2db219..93b21512ea9 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/util/BitmapUtil.java +++ b/app/src/main/java/org/thoughtcrime/securesms/util/BitmapUtil.java @@ -20,7 +20,7 @@ import org.session.libsession.utilities.MediaTypes; import org.session.libsignal.utilities.Log; -import org.thoughtcrime.securesms.mms.GlideApp; +import com.bumptech.glide.Glide; import org.thoughtcrime.securesms.mms.MediaConstraints; import org.session.libsession.utilities.Util; @@ -98,7 +98,7 @@ private static ScaleResult createScaledBytes(@NonNull Context context, int attempts = 0; byte[] bytes; - Bitmap scaledBitmap = GlideApp.with(context.getApplicationContext()) + Bitmap scaledBitmap = Glide.with(context.getApplicationContext()) .asBitmap() .load(model) .skipMemoryCache(true) @@ -164,7 +164,7 @@ public static Bitmap createScaledBitmap(Context context, T model, int maxWid throws BitmapDecodingException { try { - return GlideApp.with(context.getApplicationContext()) + return Glide.with(context.getApplicationContext()) .asBitmap() .load(model) .centerInside() diff --git a/app/src/main/java/org/thoughtcrime/securesms/util/DateUtils.java b/app/src/main/java/org/thoughtcrime/securesms/util/DateUtils.java index 66c838cc1db..6fe2193c9b9 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/util/DateUtils.java +++ b/app/src/main/java/org/thoughtcrime/securesms/util/DateUtils.java @@ -18,17 +18,15 @@ import android.annotation.SuppressLint; import android.content.Context; +import android.text.format.DateFormat; + import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import android.os.Build; -import android.text.format.DateFormat; - import org.session.libsignal.utilities.Log; import java.text.ParseException; import java.text.SimpleDateFormat; -import java.util.Calendar; import java.util.Date; import java.util.Locale; import java.util.concurrent.TimeUnit; @@ -127,11 +125,7 @@ private static String getLocalizedPattern(String template, Locale locale) { @SuppressLint("ObsoleteSdkInt") public static long parseIso8601(@Nullable String date) { SimpleDateFormat format; - if (Build.VERSION.SDK_INT >= 24) { - format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssX", Locale.getDefault()); - } else { - format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ", Locale.getDefault()); - } + format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssX", Locale.getDefault()); if (date.isEmpty()) { return -1; diff --git a/app/src/main/java/org/thoughtcrime/securesms/util/FileProviderUtil.java b/app/src/main/java/org/thoughtcrime/securesms/util/FileProviderUtil.java index ed28c7c2e20..c05832352a4 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/util/FileProviderUtil.java +++ b/app/src/main/java/org/thoughtcrime/securesms/util/FileProviderUtil.java @@ -14,10 +14,7 @@ public class FileProviderUtil { private static final String AUTHORITY = "network.loki.securesms.fileprovider"; public static Uri getUriFor(@NonNull Context context, @NonNull File file) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) - return FileProvider.getUriForFile(context, AUTHORITY, file); - else - return Uri.fromFile(file); + return FileProvider.getUriForFile(context, AUTHORITY, file); } public static boolean delete(@NonNull Context context, @NonNull Uri uri) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/util/GeneralUtilities.kt b/app/src/main/java/org/thoughtcrime/securesms/util/GeneralUtilities.kt index 9124765763b..cc40e0cc921 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/util/GeneralUtilities.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/util/GeneralUtilities.kt @@ -1,20 +1,9 @@ package org.thoughtcrime.securesms.util import android.content.res.Resources -import android.os.Build -import androidx.annotation.ColorRes import androidx.recyclerview.widget.RecyclerView -import kotlin.math.max import kotlin.math.roundToInt -fun Resources.getColorWithID(@ColorRes id: Int, theme: Resources.Theme?): Int { - return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - getColor(id, theme) - } else { - @Suppress("DEPRECATION") getColor(id) - } -} - fun toPx(dp: Int, resources: Resources): Int { return toPx(dp.toFloat(), resources).roundToInt() } diff --git a/app/src/main/java/org/thoughtcrime/securesms/util/GlowView.kt b/app/src/main/java/org/thoughtcrime/securesms/util/GlowView.kt index a0c0da24fe6..46ad8212333 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/util/GlowView.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/util/GlowView.kt @@ -11,7 +11,6 @@ import android.view.animation.AccelerateDecelerateInterpolator import android.widget.LinearLayout import android.widget.RelativeLayout import androidx.annotation.ColorInt -import androidx.annotation.ColorRes import network.loki.messenger.R import kotlin.math.roundToInt @@ -22,18 +21,6 @@ interface GlowView { object GlowViewUtilities { - fun animateColorIdChange(context: Context, view: GlowView, @ColorRes startColorID: Int, @ColorRes endColorID: Int) { - val startColor = context.resources.getColorWithID(startColorID, context.theme) - val endColor = context.resources.getColorWithID(endColorID, context.theme) - val animation = ValueAnimator.ofObject(ArgbEvaluator(), startColor, endColor) - animation.duration = 250 - animation.addUpdateListener { animator -> - val color = animator.animatedValue as Int - view.mainColor = color - } - animation.start() - } - fun animateColorChange(view: GlowView, @ColorInt startColor: Int, @ColorInt endColor: Int) { val animation = ValueAnimator.ofObject(ArgbEvaluator(), startColor, endColor) animation.duration = 250 @@ -44,18 +31,6 @@ object GlowViewUtilities { animation.start() } - fun animateShadowColorIdChange(context: Context, view: GlowView, @ColorRes startColorID: Int, @ColorRes endColorID: Int) { - val startColor = context.resources.getColorWithID(startColorID, context.theme) - val endColor = context.resources.getColorWithID(endColorID, context.theme) - val animation = ValueAnimator.ofObject(ArgbEvaluator(), startColor, endColor) - animation.duration = 250 - animation.addUpdateListener { animator -> - val color = animator.animatedValue as Int - view.sessionShadowColor = color - } - animation.start() - } - fun animateShadowColorChange( view: GlowView, @ColorInt startColor: Int, diff --git a/app/src/main/java/org/thoughtcrime/securesms/util/IP2Country.kt b/app/src/main/java/org/thoughtcrime/securesms/util/IP2Country.kt index 004dec8a5ec..446f0286c51 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/util/IP2Country.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/util/IP2Country.kt @@ -5,45 +5,64 @@ import android.content.Context import android.content.Intent import android.content.IntentFilter import androidx.localbroadcastmanager.content.LocalBroadcastManager -import org.session.libsignal.utilities.Log import com.opencsv.CSVReader import org.session.libsession.snode.OnionRequestAPI +import org.session.libsignal.utilities.Log import org.session.libsignal.utilities.ThreadUtils import java.io.File import java.io.FileOutputStream import java.io.FileReader +import java.util.SortedMap +import java.util.TreeMap class IP2Country private constructor(private val context: Context) { private val pathsBuiltEventReceiver: BroadcastReceiver val countryNamesCache = mutableMapOf() - private fun Ipv4Int(ip:String) = ip.takeWhile { it != '/' }.split('.').foldIndexed(0L) { i, acc, s -> - val asInt = s.toLong() - acc + (asInt shl (8 * (3-i))) + private fun Ipv4Int(ip: String): Int { + var result = 0L + var currentValue = 0L + var octetIndex = 0 + + for (char in ip) { + if (char == '.' || char == '/') { + result = result or (currentValue shl (8 * (3 - octetIndex))) + currentValue = 0 + octetIndex++ + if (char == '/') break + } else { + currentValue = currentValue * 10 + (char - '0') + } + } + + // Handle the last octet + result = result or (currentValue shl (8 * (3 - octetIndex))) + + return result.toInt() } - private val ipv4ToCountry by lazy { + private val ipv4ToCountry: TreeMap by lazy { val file = loadFile("geolite2_country_blocks_ipv4.csv") - val csv = CSVReader(FileReader(file.absoluteFile)).apply { - skip(1) - } + CSVReader(FileReader(file.absoluteFile)).use { csv -> + csv.skip(1) - csv.readAll() - .associate { cols -> - Ipv4Int(cols[0]) to cols[1].toIntOrNull() - } + csv.asSequence().associateTo(TreeMap()) { cols -> + Ipv4Int(cols[0]).toInt() to cols[1].toIntOrNull() + } + } } - private val countryToNames by lazy { + private val countryToNames: Map by lazy { val file = loadFile("geolite2_country_locations_english.csv") - val csv = CSVReader(FileReader(file.absoluteFile)).apply { - skip(1) - } - csv.readAll() + CSVReader(FileReader(file.absoluteFile)).use { csv -> + csv.skip(1) + + csv.asSequence() .filter { cols -> !cols[0].isNullOrEmpty() && !cols[1].isNullOrEmpty() } .associate { cols -> cols[0].toInt() to cols[5] } + } } // region Initialization @@ -95,9 +114,8 @@ class IP2Country private constructor(private val context: Context) { // return early if cached countryNamesCache[ip]?.let { return it } - val comps = ipv4ToCountry.asSequence() - - val bestMatchCountry = comps.lastOrNull { it.key <= Ipv4Int(ip) }?.let { (_, code) -> + val ipInt = Ipv4Int(ip) + val bestMatchCountry = ipv4ToCountry.floorEntry(ipInt)?.let { (_, code) -> if (code != null) { countryToNames[code] } else { @@ -127,3 +145,4 @@ class IP2Country private constructor(private val context: Context) { } // endregion } + diff --git a/app/src/main/java/org/thoughtcrime/securesms/util/ListUtil.java b/app/src/main/java/org/thoughtcrime/securesms/util/ListUtil.java index 20dd6d68cf0..321a3abfc07 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/util/ListUtil.java +++ b/app/src/main/java/org/thoughtcrime/securesms/util/ListUtil.java @@ -2,10 +2,7 @@ import androidx.annotation.NonNull; -import com.annimon.stream.Stream; - import java.util.ArrayList; -import java.util.Collection; import java.util.List; public final class ListUtil { @@ -21,16 +18,4 @@ public static List> chunk(@NonNull List list, int chunkSize) { return chunks; } - - @SafeVarargs - public static List concat(Collection... items) { - //noinspection Convert2MethodRef - final List concat = new ArrayList<>(Stream.of(items).map(Collection::size).reduce(0, (lhs, rhs) -> lhs+rhs)); - - for (Collection list : items) { - concat.addAll(list); - } - - return concat; - } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/util/LongClickCopySpan.java b/app/src/main/java/org/thoughtcrime/securesms/util/LongClickCopySpan.java deleted file mode 100644 index ce8439a20d5..00000000000 --- a/app/src/main/java/org/thoughtcrime/securesms/util/LongClickCopySpan.java +++ /dev/null @@ -1,74 +0,0 @@ -package org.thoughtcrime.securesms.util; - -import android.annotation.TargetApi; -import android.content.ClipData; -import android.content.Context; -import androidx.annotation.ColorInt; -import androidx.annotation.NonNull; -import android.text.TextPaint; -import android.text.style.URLSpan; -import android.view.View; -import android.widget.Toast; - -import network.loki.messenger.R; - -public class LongClickCopySpan extends URLSpan { - private static final String PREFIX_MAILTO = "mailto:"; - private static final String PREFIX_TEL = "tel:"; - - private boolean isHighlighted; - @ColorInt - private int highlightColor; - - public LongClickCopySpan(String url) { - super(url); - } - - void onLongClick(View widget) { - Context context = widget.getContext(); - String preparedUrl = prepareUrl(getURL()); - copyUrl(context, preparedUrl); - Toast.makeText(context, - context.getString(R.string.ConversationItem_copied_text, preparedUrl), Toast.LENGTH_SHORT).show(); - } - - @Override - public void updateDrawState(@NonNull TextPaint ds) { - super.updateDrawState(ds); - ds.bgColor = highlightColor; - ds.setUnderlineText(!isHighlighted); - } - - void setHighlighted(boolean highlighted, @ColorInt int highlightColor) { - this.isHighlighted = highlighted; - this.highlightColor = highlightColor; - } - - private void copyUrl(Context context, String url) { - int sdk = android.os.Build.VERSION.SDK_INT; - if (sdk < android.os.Build.VERSION_CODES.HONEYCOMB) { - @SuppressWarnings("deprecation") android.text.ClipboardManager clipboard = - (android.text.ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE); - clipboard.setText(url); - } else { - copyUriSdk11(context, url); - } - } - - @TargetApi(android.os.Build.VERSION_CODES.HONEYCOMB) - private void copyUriSdk11(Context context, String url) { - android.content.ClipboardManager clipboard = - (android.content.ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE); - ClipData clip = ClipData.newPlainText(context.getString(R.string.app_name), url); - clipboard.setPrimaryClip(clip); - } - - private String prepareUrl(String url) { - if (url.startsWith(PREFIX_MAILTO)) { - return url.substring(PREFIX_MAILTO.length()); - } else if (url.startsWith(PREFIX_TEL)) { - return url.substring(PREFIX_TEL.length()); - } - return url; - } -} diff --git a/app/src/main/java/org/thoughtcrime/securesms/util/MediaUtil.java b/app/src/main/java/org/thoughtcrime/securesms/util/MediaUtil.java index 5c4894c12ba..fa2fd6b503a 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/util/MediaUtil.java +++ b/app/src/main/java/org/thoughtcrime/securesms/util/MediaUtil.java @@ -22,7 +22,7 @@ import org.thoughtcrime.securesms.mms.DecryptableStreamUriLoader.DecryptableUri; import org.thoughtcrime.securesms.mms.DocumentSlide; import org.thoughtcrime.securesms.mms.GifSlide; -import org.thoughtcrime.securesms.mms.GlideApp; +import com.bumptech.glide.Glide; import org.thoughtcrime.securesms.mms.ImageSlide; import org.thoughtcrime.securesms.mms.MmsSlide; import org.thoughtcrime.securesms.mms.PartAuthority; @@ -115,7 +115,7 @@ public static Pair getDimensions(@NonNull Context context, @Nu if (MediaUtil.isGif(contentType)) { try { - GifDrawable drawable = GlideApp.with(context) + GifDrawable drawable = Glide.with(context) .asGif() .skipMemoryCache(true) .diskCacheStrategy(DiskCacheStrategy.NONE) diff --git a/app/src/main/java/org/thoughtcrime/securesms/util/QRCodeUtilities.kt b/app/src/main/java/org/thoughtcrime/securesms/util/QRCodeUtilities.kt index 80eccae41aa..ae4fd9a3f45 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/util/QRCodeUtilities.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/util/QRCodeUtilities.kt @@ -6,6 +6,7 @@ import com.google.zxing.BarcodeFormat import com.google.zxing.EncodeHintType import com.google.zxing.qrcode.QRCodeWriter import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel +import org.session.libsignal.utilities.Log object QRCodeUtilities { @@ -34,5 +35,8 @@ object QRCodeUtilities { } } } - }.getOrNull() + }.getOrElse { + Log.e("QRCodeUtilities", "Failed to generate QR Code", it) + null + } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/util/SaveAttachmentTask.kt b/app/src/main/java/org/thoughtcrime/securesms/util/SaveAttachmentTask.kt index 8df2a7cd2be..ccbab486c63 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/util/SaveAttachmentTask.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/util/SaveAttachmentTask.kt @@ -57,15 +57,144 @@ class SaveAttachmentTask @JvmOverloads constructor(context: Context, count: Int button(R.string.no) } } - } - private val contextReference: WeakReference - private val attachmentCount: Int = count + fun saveAttachment(context: Context, attachment: Attachment): String? { + val contentType = checkNotNull(MediaUtil.getCorrectedMimeType(attachment.contentType)) + var fileName = attachment.fileName + + // Added for SES-2624 to prevent Android API 28 devices and lower from crashing because + // for unknown reasons it provides us with an empty filename when saving files. + // TODO: Further investigation into root cause and fix! + if (fileName.isNullOrEmpty()) fileName = generateOutputFileName(contentType, attachment.date) + + fileName = sanitizeOutputFileName(fileName) + val outputUri: Uri = getMediaStoreContentUriForType(contentType) + val mediaUri = createOutputUri(context, outputUri, contentType, fileName) + val updateValues = ContentValues() + PartAuthority.getAttachmentStream(context, attachment.uri).use { inputStream -> + if (inputStream == null) { + return null + } + if (outputUri.scheme == ContentResolver.SCHEME_FILE) { + FileOutputStream(mediaUri!!.path).use { outputStream -> + StreamUtil.copy(inputStream, outputStream) + MediaScannerConnection.scanFile(context, arrayOf(mediaUri.path), arrayOf(contentType), null) + } + } else { + context.contentResolver.openOutputStream(mediaUri!!, "w").use { outputStream -> + val total: Long = StreamUtil.copy(inputStream, outputStream) + if (total > 0) { + updateValues.put(MediaStore.MediaColumns.SIZE, total) + } + } + } + } + if (Build.VERSION.SDK_INT > 28) { + updateValues.put(MediaStore.MediaColumns.IS_PENDING, 0) + } + if (updateValues.size() > 0) { + context.contentResolver.update(mediaUri!!, updateValues, null, null) + } + return outputUri.lastPathSegment + } + + private fun generateOutputFileName(contentType: String, timestamp: Long): String { + val mimeTypeMap = MimeTypeMap.getSingleton() + val extension = mimeTypeMap.getExtensionFromMimeType(contentType) ?: "attach" + val dateFormatter = SimpleDateFormat("yyyy-MM-dd-HHmmss") + val base = "session-${dateFormatter.format(timestamp)}" + + return "${base}.${extension}"; + } + + private fun sanitizeOutputFileName(fileName: String): String { + return File(fileName).name + } - init { - this.contextReference = WeakReference(context) + private fun getMediaStoreContentUriForType(contentType: String): Uri { + return when { + contentType.startsWith("video/") -> + ExternalStorageUtil.getVideoUri() + contentType.startsWith("audio/") -> + ExternalStorageUtil.getAudioUri() + contentType.startsWith("image/") -> + ExternalStorageUtil.getImageUri() + else -> + ExternalStorageUtil.getDownloadUri() + } + } + + private fun createOutputUri(context: Context, outputUri: Uri, contentType: String, fileName: String): Uri? { + val fileParts: Array = getFileNameParts(fileName) + val base = fileParts[0] + val extension = fileParts[1] + val mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension) + val contentValues = ContentValues() + contentValues.put(MediaStore.MediaColumns.DISPLAY_NAME, fileName) + contentValues.put(MediaStore.MediaColumns.MIME_TYPE, mimeType) + contentValues.put(MediaStore.MediaColumns.DATE_ADDED, TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis())) + contentValues.put(MediaStore.MediaColumns.DATE_MODIFIED, TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis())) + if (Build.VERSION.SDK_INT > 28) { + contentValues.put(MediaStore.MediaColumns.IS_PENDING, 1) + } else if (outputUri.scheme == ContentResolver.SCHEME_FILE) { + val outputDirectory = File(outputUri.path) + var outputFile = File(outputDirectory, "$base.$extension") + var i = 0 + while (outputFile.exists()) { + outputFile = File(outputDirectory, base + "-" + ++i + "." + extension) + } + if (outputFile.isHidden) { + throw IOException("Specified name would not be visible") + } + return Uri.fromFile(outputFile) + } else { + var outputFileName = fileName + var dataPath = String.format("%s/%s", getExternalPathToFileForType(context, contentType), outputFileName) + var i = 0 + while (pathTaken(context, outputUri, dataPath)) { + Log.d(TAG, "The content exists. Rename and check again.") + outputFileName = base + "-" + ++i + "." + extension + dataPath = String.format("%s/%s", getExternalPathToFileForType(context, contentType), outputFileName) + } + contentValues.put(MediaStore.MediaColumns.DATA, dataPath) + } + return context.contentResolver.insert(outputUri, contentValues) + } + + private fun getExternalPathToFileForType(context: Context, contentType: String): String { + val storage: File = when { + contentType.startsWith("video/") -> + context.getExternalFilesDir(Environment.DIRECTORY_MOVIES)!! + contentType.startsWith("audio/") -> + context.getExternalFilesDir(Environment.DIRECTORY_MUSIC)!! + contentType.startsWith("image/") -> + context.getExternalFilesDir(Environment.DIRECTORY_PICTURES)!! + else -> + context.getExternalFilesDir(Environment.DIRECTORY_DOCUMENTS)!! + } + return storage.absolutePath + } + + private fun getFileNameParts(fileName: String): Array { + val tokens = fileName.split("\\.(?=[^\\.]+$)".toRegex()).toTypedArray() + return arrayOf(tokens[0], if (tokens.size > 1) tokens[1] else "") + } + + private fun pathTaken(context: Context, outputUri: Uri, dataPath: String): Boolean { + context.contentResolver.query(outputUri, arrayOf(MediaStore.MediaColumns.DATA), + MediaStore.MediaColumns.DATA + " = ?", arrayOf(dataPath), + null).use { cursor -> + if (cursor == null) { + throw IOException("Something is wrong with the filename to save") + } + return cursor.moveToFirst() + } + } } + private val contextReference = WeakReference(context) + private val attachmentCount: Int = count + @Deprecated("Deprecated in Java") override fun doInBackground(vararg attachments: Attachment?): Pair { if (attachments.isEmpty()) { @@ -97,137 +226,6 @@ class SaveAttachmentTask @JvmOverloads constructor(context: Context, count: Int } } - @Throws(IOException::class) - private fun saveAttachment(context: Context, attachment: Attachment): String? { - val contentType = Objects.requireNonNull(MediaUtil.getCorrectedMimeType(attachment.contentType))!! - var fileName = attachment.fileName - if (fileName == null) fileName = generateOutputFileName(contentType, attachment.date) - fileName = sanitizeOutputFileName(fileName) - val outputUri: Uri = getMediaStoreContentUriForType(contentType) - val mediaUri = createOutputUri(outputUri, contentType, fileName) - val updateValues = ContentValues() - PartAuthority.getAttachmentStream(context, attachment.uri).use { inputStream -> - if (inputStream == null) { - return null - } - if (outputUri.scheme == ContentResolver.SCHEME_FILE) { - FileOutputStream(mediaUri!!.path).use { outputStream -> - StreamUtil.copy(inputStream, outputStream) - MediaScannerConnection.scanFile(context, arrayOf(mediaUri.path), arrayOf(contentType), null) - } - } else { - context.contentResolver.openOutputStream(mediaUri!!, "w").use { outputStream -> - val total: Long = StreamUtil.copy(inputStream, outputStream) - if (total > 0) { - updateValues.put(MediaStore.MediaColumns.SIZE, total) - } - } - } - } - if (Build.VERSION.SDK_INT > 28) { - updateValues.put(MediaStore.MediaColumns.IS_PENDING, 0) - } - if (updateValues.size() > 0) { - getContext().contentResolver.update(mediaUri!!, updateValues, null, null) - } - return outputUri.lastPathSegment - } - - private fun getMediaStoreContentUriForType(contentType: String): Uri { - return when { - contentType.startsWith("video/") -> - ExternalStorageUtil.getVideoUri() - contentType.startsWith("audio/") -> - ExternalStorageUtil.getAudioUri() - contentType.startsWith("image/") -> - ExternalStorageUtil.getImageUri() - else -> - ExternalStorageUtil.getDownloadUri() - } - } - - @Throws(IOException::class) - private fun createOutputUri(outputUri: Uri, contentType: String, fileName: String): Uri? { - val fileParts: Array = getFileNameParts(fileName) - val base = fileParts[0] - val extension = fileParts[1] - val mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension) - val contentValues = ContentValues() - contentValues.put(MediaStore.MediaColumns.DISPLAY_NAME, fileName) - contentValues.put(MediaStore.MediaColumns.MIME_TYPE, mimeType) - contentValues.put(MediaStore.MediaColumns.DATE_ADDED, TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis())) - contentValues.put(MediaStore.MediaColumns.DATE_MODIFIED, TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis())) - if (Build.VERSION.SDK_INT > 28) { - contentValues.put(MediaStore.MediaColumns.IS_PENDING, 1) - } else if (Objects.equals(outputUri.scheme, ContentResolver.SCHEME_FILE)) { - val outputDirectory = File(outputUri.path) - var outputFile = File(outputDirectory, "$base.$extension") - var i = 0 - while (outputFile.exists()) { - outputFile = File(outputDirectory, base + "-" + ++i + "." + extension) - } - if (outputFile.isHidden) { - throw IOException("Specified name would not be visible") - } - return Uri.fromFile(outputFile) - } else { - var outputFileName = fileName - var dataPath = String.format("%s/%s", getExternalPathToFileForType(contentType), outputFileName) - var i = 0 - while (pathTaken(outputUri, dataPath)) { - Log.d(TAG, "The content exists. Rename and check again.") - outputFileName = base + "-" + ++i + "." + extension - dataPath = String.format("%s/%s", getExternalPathToFileForType(contentType), outputFileName) - } - contentValues.put(MediaStore.MediaColumns.DATA, dataPath) - } - return context.contentResolver.insert(outputUri, contentValues) - } - - private fun getFileNameParts(fileName: String): Array { - val tokens = fileName.split("\\.(?=[^\\.]+$)".toRegex()).toTypedArray() - return arrayOf(tokens[0], if (tokens.size > 1) tokens[1] else "") - } - - private fun getExternalPathToFileForType(contentType: String): String { - val storage: File = when { - contentType.startsWith("video/") -> - context.getExternalFilesDir(Environment.DIRECTORY_MOVIES)!! - contentType.startsWith("audio/") -> - context.getExternalFilesDir(Environment.DIRECTORY_MUSIC)!! - contentType.startsWith("image/") -> - context.getExternalFilesDir(Environment.DIRECTORY_PICTURES)!! - else -> - context.getExternalFilesDir(Environment.DIRECTORY_DOCUMENTS)!! - } - return storage.absolutePath - } - - @Throws(IOException::class) - private fun pathTaken(outputUri: Uri, dataPath: String): Boolean { - context.contentResolver.query(outputUri, arrayOf(MediaStore.MediaColumns.DATA), - MediaStore.MediaColumns.DATA + " = ?", arrayOf(dataPath), - null).use { cursor -> - if (cursor == null) { - throw IOException("Something is wrong with the filename to save") - } - return cursor.moveToFirst() - } - } - - private fun generateOutputFileName(contentType: String, timestamp: Long): String { - val mimeTypeMap = MimeTypeMap.getSingleton() - val extension = mimeTypeMap.getExtensionFromMimeType(contentType) ?: "attach" - val dateFormatter = SimpleDateFormat("yyyy-MM-dd-HHmmss") - val base = "session-${dateFormatter.format(timestamp)}" - - return "${base}.${extension}"; - } - - private fun sanitizeOutputFileName(fileName: String): String { - return File(fileName).name - } - @Deprecated("Deprecated in Java") override fun onPostExecute(result: Pair) { super.onPostExecute(result) @@ -255,4 +253,5 @@ class SaveAttachmentTask @JvmOverloads constructor(context: Context, count: Int } data class Attachment(val uri: Uri, val contentType: String, val date: Long, val fileName: String?) + } \ No newline at end of file diff --git a/app/src/main/java/org/thoughtcrime/securesms/util/StickyHeaderDecoration.java b/app/src/main/java/org/thoughtcrime/securesms/util/StickyHeaderDecoration.java index 04435fde0f9..0afe0192c0c 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/util/StickyHeaderDecoration.java +++ b/app/src/main/java/org/thoughtcrime/securesms/util/StickyHeaderDecoration.java @@ -131,7 +131,7 @@ protected int getHeaderTop(RecyclerView parent, View child, View header, int ada int layoutPos) { int headerHeight = getHeaderHeightForLayout(header); - int top = getChildY(parent, child) - headerHeight; + int top = (int)child.getY() - headerHeight; if (sticky && layoutPos == 0) { final int count = parent.getChildCount(); final long currentId = adapter.getHeaderId(adapterPos); @@ -142,7 +142,7 @@ protected int getHeaderTop(RecyclerView parent, View child, View header, int ada long nextId = adapter.getHeaderId(adapterPosHere); if (nextId != currentId) { final View next = parent.getChildAt(translatedChildPosition(parent, i)); - final int offset = getChildY(parent, next) - (headerHeight + getHeader(parent, adapter, adapterPosHere).itemView.getHeight()); + final int offset = (int)next.getY() - (headerHeight + getHeader(parent, adapter, adapterPosHere).itemView.getHeight()); if (offset < 0) { return offset; } else { @@ -162,16 +162,6 @@ private int translatedChildPosition(RecyclerView parent, int position) { return isReverseLayout(parent) ? parent.getChildCount() - 1 - position : position; } - private int getChildY(RecyclerView parent, View child) { - if (VERSION.SDK_INT < 11) { - Rect rect = new Rect(); - parent.getChildVisibleRect(child, rect, null); - return rect.top; - } else { - return (int)ViewCompat.getY(child); - } - } - protected int getHeaderHeightForLayout(View header) { return renderInline ? 0 : header.getHeight(); } diff --git a/app/src/main/java/org/thoughtcrime/securesms/video/EncryptedMediaDataSource.java b/app/src/main/java/org/thoughtcrime/securesms/video/EncryptedMediaDataSource.java index 8a3d2c3f745..78541e02a48 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/video/EncryptedMediaDataSource.java +++ b/app/src/main/java/org/thoughtcrime/securesms/video/EncryptedMediaDataSource.java @@ -1,22 +1,20 @@ package org.thoughtcrime.securesms.video; -import android.annotation.TargetApi; import android.media.MediaDataSource; -import android.os.Build; + import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import org.session.libsession.utilities.Util; import org.thoughtcrime.securesms.crypto.AttachmentSecret; import org.thoughtcrime.securesms.crypto.ClassicDecryptingPartInputStream; import org.thoughtcrime.securesms.crypto.ModernDecryptingPartInputStream; -import org.session.libsession.utilities.Util; import java.io.File; import java.io.IOException; import java.io.InputStream; -@TargetApi(Build.VERSION_CODES.M) public class EncryptedMediaDataSource extends MediaDataSource { private final AttachmentSecret attachmentSecret; diff --git a/app/src/main/java/org/thoughtcrime/securesms/webrtc/PeerConnectionWrapper.kt b/app/src/main/java/org/thoughtcrime/securesms/webrtc/PeerConnectionWrapper.kt index 480ba24ae33..eda4c1a39bd 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/webrtc/PeerConnectionWrapper.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/webrtc/PeerConnectionWrapper.kt @@ -1,8 +1,10 @@ package org.thoughtcrime.securesms.webrtc import android.content.Context +import org.session.libsignal.crypto.shuffledRandom import org.session.libsignal.utilities.Log import org.session.libsignal.utilities.SettableFuture +import org.session.libsignal.utilities.Util.SECURE_RANDOM import org.thoughtcrime.securesms.webrtc.video.Camera import org.thoughtcrime.securesms.webrtc.video.CameraEventListener import org.thoughtcrime.securesms.webrtc.video.CameraState @@ -22,9 +24,7 @@ import org.webrtc.SurfaceTextureHelper import org.webrtc.VideoSink import org.webrtc.VideoSource import org.webrtc.VideoTrack -import java.security.SecureRandom import java.util.concurrent.ExecutionException -import kotlin.random.asKotlinRandom class PeerConnectionWrapper(private val context: Context, private val factory: PeerConnectionFactory, @@ -49,8 +49,7 @@ class PeerConnectionWrapper(private val context: Context, private var isInitiator = false private fun initPeerConnection() { - val random = SecureRandom().asKotlinRandom() - val iceServers = listOf("freyr","angus","hereford","holstein", "brahman").shuffled(random).take(2).map { sub -> + val iceServers = listOf("freyr","angus","hereford","holstein", "brahman").shuffledRandom().take(2).map { sub -> PeerConnection.IceServer.builder("turn:$sub.getsession.org") .setUsername("session202111") .setPassword("053c268164bc7bd7") diff --git a/app/src/main/java/org/thoughtcrime/securesms/webrtc/audio/AudioManagerCompat.java b/app/src/main/java/org/thoughtcrime/securesms/webrtc/audio/AudioManagerCompat.java index da50cd0846f..50af4f36c04 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/webrtc/audio/AudioManagerCompat.java +++ b/app/src/main/java/org/thoughtcrime/securesms/webrtc/audio/AudioManagerCompat.java @@ -8,10 +8,8 @@ import android.media.AudioFocusRequest; import android.media.AudioManager; import android.media.SoundPool; -import android.os.Build; import androidx.annotation.NonNull; -import androidx.annotation.RequiresApi; import org.session.libsession.utilities.ServiceUtil; import org.session.libsignal.utilities.Log; @@ -116,14 +114,9 @@ private static float logVolume(int volume, int maxVolume) { abstract public void abandonCallAudioFocus(); public static AudioManagerCompat create(@NonNull Context context) { - if (Build.VERSION.SDK_INT >= 26) { - return new Api26AudioManagerCompat(context); - } else { - return new Api21AudioManagerCompat(context); - } + return new Api26AudioManagerCompat(context); } - @RequiresApi(26) private static class Api26AudioManagerCompat extends AudioManagerCompat { private static AudioAttributes AUDIO_ATTRIBUTES = new AudioAttributes.Builder() @@ -180,44 +173,4 @@ public void abandonCallAudioFocus() { audioFocusRequest = null; } } - - @RequiresApi(21) - private static class Api21AudioManagerCompat extends AudioManagerCompat { - - private static AudioAttributes AUDIO_ATTRIBUTES = new AudioAttributes.Builder() - .setContentType(AudioAttributes.CONTENT_TYPE_SPEECH) - .setUsage(AudioAttributes.USAGE_VOICE_COMMUNICATION) - .setLegacyStreamType(AudioManager.STREAM_VOICE_CALL) - .build(); - - private Api21AudioManagerCompat(@NonNull Context context) { - super(context); - } - - @Override - public SoundPool createSoundPool() { - return new SoundPool.Builder() - .setAudioAttributes(AUDIO_ATTRIBUTES) - .setMaxStreams(1) - .build(); - } - - @Override - public void requestCallAudioFocus() { - int result = audioManager.requestAudioFocus(onAudioFocusChangeListener, AudioManager.STREAM_VOICE_CALL, AUDIOFOCUS_GAIN); - - if (result != AudioManager.AUDIOFOCUS_REQUEST_GRANTED) { - Log.w(TAG, "Audio focus not granted. Result code: " + result); - } - } - - @Override - public void abandonCallAudioFocus() { - int result = audioManager.abandonAudioFocus(onAudioFocusChangeListener); - - if (result != AudioManager.AUDIOFOCUS_REQUEST_GRANTED) { - Log.w(TAG, "Audio focus abandon failed. Result code: " + result); - } - } - } } diff --git a/app/src/main/res/anim/camera_slide_from_bottom.xml b/app/src/main/res/anim/camera_slide_from_bottom.xml deleted file mode 100644 index 5d7343cf25d..00000000000 --- a/app/src/main/res/anim/camera_slide_from_bottom.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - \ No newline at end of file diff --git a/app/src/main/res/anim/fade_in.xml b/app/src/main/res/anim/fade_in.xml deleted file mode 100644 index 508f8be39d9..00000000000 --- a/app/src/main/res/anim/fade_in.xml +++ /dev/null @@ -1,6 +0,0 @@ - diff --git a/app/src/main/res/anim/fade_out.xml b/app/src/main/res/anim/fade_out.xml deleted file mode 100644 index e8f16d01aab..00000000000 --- a/app/src/main/res/anim/fade_out.xml +++ /dev/null @@ -1,6 +0,0 @@ - diff --git a/app/src/main/res/anim/fade_scale_in.xml b/app/src/main/res/anim/fade_scale_in.xml deleted file mode 100644 index 0f2def07d36..00000000000 --- a/app/src/main/res/anim/fade_scale_in.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/app/src/main/res/anim/slide_from_top.xml b/app/src/main/res/anim/slide_from_top.xml deleted file mode 100644 index 761b9151dc9..00000000000 --- a/app/src/main/res/anim/slide_from_top.xml +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/app/src/main/res/anim/slide_to_top.xml b/app/src/main/res/anim/slide_to_top.xml deleted file mode 100644 index cc204acb23e..00000000000 --- a/app/src/main/res/anim/slide_to_top.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - \ No newline at end of file diff --git a/app/src/main/res/animator/bottom_pause_to_play_animation.xml b/app/src/main/res/animator/bottom_pause_to_play_animation.xml deleted file mode 100644 index f5b474bb709..00000000000 --- a/app/src/main/res/animator/bottom_pause_to_play_animation.xml +++ /dev/null @@ -1,8 +0,0 @@ - - \ No newline at end of file diff --git a/app/src/main/res/animator/bottom_play_to_pause_animation.xml b/app/src/main/res/animator/bottom_play_to_pause_animation.xml deleted file mode 100644 index 4f2778d6853..00000000000 --- a/app/src/main/res/animator/bottom_play_to_pause_animation.xml +++ /dev/null @@ -1,8 +0,0 @@ - - \ No newline at end of file diff --git a/app/src/main/res/animator/rotate_90_animation.xml b/app/src/main/res/animator/rotate_90_animation.xml deleted file mode 100644 index 7d44ce6900a..00000000000 --- a/app/src/main/res/animator/rotate_90_animation.xml +++ /dev/null @@ -1,9 +0,0 @@ - - \ No newline at end of file diff --git a/app/src/main/res/animator/rotate_minus_90_animation.xml b/app/src/main/res/animator/rotate_minus_90_animation.xml deleted file mode 100644 index ef9e1b6f1f7..00000000000 --- a/app/src/main/res/animator/rotate_minus_90_animation.xml +++ /dev/null @@ -1,9 +0,0 @@ - - \ No newline at end of file diff --git a/app/src/main/res/animator/upper_pause_to_play_animation.xml b/app/src/main/res/animator/upper_pause_to_play_animation.xml deleted file mode 100644 index 880c7b0b83f..00000000000 --- a/app/src/main/res/animator/upper_pause_to_play_animation.xml +++ /dev/null @@ -1,8 +0,0 @@ - - \ No newline at end of file diff --git a/app/src/main/res/animator/upper_play_to_pause_animation.xml b/app/src/main/res/animator/upper_play_to_pause_animation.xml deleted file mode 100644 index ffa933231cb..00000000000 --- a/app/src/main/res/animator/upper_play_to_pause_animation.xml +++ /dev/null @@ -1,8 +0,0 @@ - - \ No newline at end of file diff --git a/app/src/main/res/color/icon_tab_selector.xml b/app/src/main/res/color/icon_tab_selector.xml deleted file mode 100644 index 30cd1bdc27e..00000000000 --- a/app/src/main/res/color/icon_tab_selector.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - - \ No newline at end of file diff --git a/app/src/main/res/drawable-hdpi/divet_lower_right_dark.png b/app/src/main/res/drawable-hdpi/divet_lower_right_dark.png deleted file mode 100644 index d37eece35ce..00000000000 Binary files a/app/src/main/res/drawable-hdpi/divet_lower_right_dark.png and /dev/null differ diff --git a/app/src/main/res/drawable-hdpi/ic_account_box_dark.png b/app/src/main/res/drawable-hdpi/ic_account_box_dark.png deleted file mode 100644 index 5aed1dfcd69..00000000000 Binary files a/app/src/main/res/drawable-hdpi/ic_account_box_dark.png and /dev/null differ diff --git a/app/src/main/res/drawable-hdpi/ic_account_box_light.png b/app/src/main/res/drawable-hdpi/ic_account_box_light.png deleted file mode 100644 index 042b0b85598..00000000000 Binary files a/app/src/main/res/drawable-hdpi/ic_account_box_light.png and /dev/null differ diff --git a/app/src/main/res/drawable-hdpi/ic_attach_grey600_24dp.png b/app/src/main/res/drawable-hdpi/ic_attach_grey600_24dp.png deleted file mode 100644 index 13c7f0c28dd..00000000000 Binary files a/app/src/main/res/drawable-hdpi/ic_attach_grey600_24dp.png and /dev/null differ diff --git a/app/src/main/res/drawable-hdpi/ic_audio_light.png b/app/src/main/res/drawable-hdpi/ic_audio_light.png deleted file mode 100644 index 45c439e07f0..00000000000 Binary files a/app/src/main/res/drawable-hdpi/ic_audio_light.png and /dev/null differ diff --git a/app/src/main/res/drawable-hdpi/ic_circle_fill_white_48dp.png b/app/src/main/res/drawable-hdpi/ic_circle_fill_white_48dp.png deleted file mode 100644 index a417946f2ac..00000000000 Binary files a/app/src/main/res/drawable-hdpi/ic_circle_fill_white_48dp.png and /dev/null differ diff --git a/app/src/main/res/drawable-hdpi/ic_contact_picture.png b/app/src/main/res/drawable-hdpi/ic_contact_picture.png deleted file mode 100644 index 3cfbff303a4..00000000000 Binary files a/app/src/main/res/drawable-hdpi/ic_contact_picture.png and /dev/null differ diff --git a/app/src/main/res/drawable-hdpi/ic_document_small_light.png b/app/src/main/res/drawable-hdpi/ic_document_small_light.png deleted file mode 100644 index a68e1c5a66c..00000000000 Binary files a/app/src/main/res/drawable-hdpi/ic_document_small_light.png and /dev/null differ diff --git a/app/src/main/res/drawable-hdpi/ic_image_light.png b/app/src/main/res/drawable-hdpi/ic_image_light.png deleted file mode 100644 index afce95a27b1..00000000000 Binary files a/app/src/main/res/drawable-hdpi/ic_image_light.png and /dev/null differ diff --git a/app/src/main/res/drawable-hdpi/ic_movie_creation_light.png b/app/src/main/res/drawable-hdpi/ic_movie_creation_light.png deleted file mode 100644 index dccabc292f6..00000000000 Binary files a/app/src/main/res/drawable-hdpi/ic_movie_creation_light.png and /dev/null differ diff --git a/app/src/main/res/drawable-hdpi/ic_note_to_self.png b/app/src/main/res/drawable-hdpi/ic_note_to_self.png deleted file mode 100644 index 1cca57650ff..00000000000 Binary files a/app/src/main/res/drawable-hdpi/ic_note_to_self.png and /dev/null differ diff --git a/app/src/main/res/drawable-hdpi/ic_photo_camera_light.png b/app/src/main/res/drawable-hdpi/ic_photo_camera_light.png deleted file mode 100644 index 2a98d9a12d9..00000000000 Binary files a/app/src/main/res/drawable-hdpi/ic_photo_camera_light.png and /dev/null differ diff --git a/app/src/main/res/drawable-hdpi/ic_unlocked_white_18dp.png b/app/src/main/res/drawable-hdpi/ic_unlocked_white_18dp.png deleted file mode 100644 index b1168e31dd0..00000000000 Binary files a/app/src/main/res/drawable-hdpi/ic_unlocked_white_18dp.png and /dev/null differ diff --git a/app/src/main/res/drawable-hdpi/ic_video_light.png b/app/src/main/res/drawable-hdpi/ic_video_light.png deleted file mode 100644 index 90830d11029..00000000000 Binary files a/app/src/main/res/drawable-hdpi/ic_video_light.png and /dev/null differ diff --git a/app/src/main/res/drawable-hdpi/ic_volume_up_light.png b/app/src/main/res/drawable-hdpi/ic_volume_up_light.png deleted file mode 100644 index b020322df87..00000000000 Binary files a/app/src/main/res/drawable-hdpi/ic_volume_up_light.png and /dev/null differ diff --git a/app/src/main/res/drawable-hdpi/ic_warning_light.png b/app/src/main/res/drawable-hdpi/ic_warning_light.png deleted file mode 100644 index 9ee69764769..00000000000 Binary files a/app/src/main/res/drawable-hdpi/ic_warning_light.png and /dev/null differ diff --git a/app/src/main/res/drawable-mdpi/divet_lower_right_dark.png b/app/src/main/res/drawable-mdpi/divet_lower_right_dark.png deleted file mode 100644 index 8d2bc439ec4..00000000000 Binary files a/app/src/main/res/drawable-mdpi/divet_lower_right_dark.png and /dev/null differ diff --git a/app/src/main/res/drawable-mdpi/ic_account_box_dark.png b/app/src/main/res/drawable-mdpi/ic_account_box_dark.png deleted file mode 100644 index 285689f3c39..00000000000 Binary files a/app/src/main/res/drawable-mdpi/ic_account_box_dark.png and /dev/null differ diff --git a/app/src/main/res/drawable-mdpi/ic_account_box_light.png b/app/src/main/res/drawable-mdpi/ic_account_box_light.png deleted file mode 100644 index 2d9417bd099..00000000000 Binary files a/app/src/main/res/drawable-mdpi/ic_account_box_light.png and /dev/null differ diff --git a/app/src/main/res/drawable-mdpi/ic_attach_grey600_24dp.png b/app/src/main/res/drawable-mdpi/ic_attach_grey600_24dp.png deleted file mode 100644 index 0ea546b1f94..00000000000 Binary files a/app/src/main/res/drawable-mdpi/ic_attach_grey600_24dp.png and /dev/null differ diff --git a/app/src/main/res/drawable-mdpi/ic_audio_light.png b/app/src/main/res/drawable-mdpi/ic_audio_light.png deleted file mode 100644 index fd2d3d06da4..00000000000 Binary files a/app/src/main/res/drawable-mdpi/ic_audio_light.png and /dev/null differ diff --git a/app/src/main/res/drawable-mdpi/ic_circle_fill_white_48dp.png b/app/src/main/res/drawable-mdpi/ic_circle_fill_white_48dp.png deleted file mode 100644 index 783f82cf1e7..00000000000 Binary files a/app/src/main/res/drawable-mdpi/ic_circle_fill_white_48dp.png and /dev/null differ diff --git a/app/src/main/res/drawable-mdpi/ic_contact_picture.png b/app/src/main/res/drawable-mdpi/ic_contact_picture.png deleted file mode 100644 index 3d605f1f5c1..00000000000 Binary files a/app/src/main/res/drawable-mdpi/ic_contact_picture.png and /dev/null differ diff --git a/app/src/main/res/drawable-mdpi/ic_document_small_light.png b/app/src/main/res/drawable-mdpi/ic_document_small_light.png deleted file mode 100644 index 8972c8b6260..00000000000 Binary files a/app/src/main/res/drawable-mdpi/ic_document_small_light.png and /dev/null differ diff --git a/app/src/main/res/drawable-mdpi/ic_image_light.png b/app/src/main/res/drawable-mdpi/ic_image_light.png deleted file mode 100644 index 3d57dd2b28b..00000000000 Binary files a/app/src/main/res/drawable-mdpi/ic_image_light.png and /dev/null differ diff --git a/app/src/main/res/drawable-mdpi/ic_movie_creation_light.png b/app/src/main/res/drawable-mdpi/ic_movie_creation_light.png deleted file mode 100644 index 3506288f98f..00000000000 Binary files a/app/src/main/res/drawable-mdpi/ic_movie_creation_light.png and /dev/null differ diff --git a/app/src/main/res/drawable-mdpi/ic_note_to_self.png b/app/src/main/res/drawable-mdpi/ic_note_to_self.png deleted file mode 100644 index 6ed37fdb10a..00000000000 Binary files a/app/src/main/res/drawable-mdpi/ic_note_to_self.png and /dev/null differ diff --git a/app/src/main/res/drawable-mdpi/ic_photo_camera_light.png b/app/src/main/res/drawable-mdpi/ic_photo_camera_light.png deleted file mode 100644 index 2c883fc7a04..00000000000 Binary files a/app/src/main/res/drawable-mdpi/ic_photo_camera_light.png and /dev/null differ diff --git a/app/src/main/res/drawable-mdpi/ic_unlocked_white_18dp.png b/app/src/main/res/drawable-mdpi/ic_unlocked_white_18dp.png deleted file mode 100644 index 3638a79920d..00000000000 Binary files a/app/src/main/res/drawable-mdpi/ic_unlocked_white_18dp.png and /dev/null differ diff --git a/app/src/main/res/drawable-mdpi/ic_video_light.png b/app/src/main/res/drawable-mdpi/ic_video_light.png deleted file mode 100644 index da0a760d392..00000000000 Binary files a/app/src/main/res/drawable-mdpi/ic_video_light.png and /dev/null differ diff --git a/app/src/main/res/drawable-mdpi/ic_volume_up_light.png b/app/src/main/res/drawable-mdpi/ic_volume_up_light.png deleted file mode 100644 index 6de890621b7..00000000000 Binary files a/app/src/main/res/drawable-mdpi/ic_volume_up_light.png and /dev/null differ diff --git a/app/src/main/res/drawable-mdpi/ic_warning_light.png b/app/src/main/res/drawable-mdpi/ic_warning_light.png deleted file mode 100644 index f4164a97462..00000000000 Binary files a/app/src/main/res/drawable-mdpi/ic_warning_light.png and /dev/null differ diff --git a/app/src/main/res/drawable-xhdpi/divet_lower_right_dark.png b/app/src/main/res/drawable-xhdpi/divet_lower_right_dark.png deleted file mode 100644 index e4a3d77a00b..00000000000 Binary files a/app/src/main/res/drawable-xhdpi/divet_lower_right_dark.png and /dev/null differ diff --git a/app/src/main/res/drawable-xhdpi/ic_account_box_dark.png b/app/src/main/res/drawable-xhdpi/ic_account_box_dark.png deleted file mode 100644 index 2559c4dbf1f..00000000000 Binary files a/app/src/main/res/drawable-xhdpi/ic_account_box_dark.png and /dev/null differ diff --git a/app/src/main/res/drawable-xhdpi/ic_account_box_light.png b/app/src/main/res/drawable-xhdpi/ic_account_box_light.png deleted file mode 100644 index ddd06d7487e..00000000000 Binary files a/app/src/main/res/drawable-xhdpi/ic_account_box_light.png and /dev/null differ diff --git a/app/src/main/res/drawable-xhdpi/ic_add_white_original_24dp.png b/app/src/main/res/drawable-xhdpi/ic_add_white_original_24dp.png deleted file mode 100644 index 67042105d29..00000000000 Binary files a/app/src/main/res/drawable-xhdpi/ic_add_white_original_24dp.png and /dev/null differ diff --git a/app/src/main/res/drawable-xhdpi/ic_attach_grey600_24dp.png b/app/src/main/res/drawable-xhdpi/ic_attach_grey600_24dp.png deleted file mode 100644 index 28aef9e9b1c..00000000000 Binary files a/app/src/main/res/drawable-xhdpi/ic_attach_grey600_24dp.png and /dev/null differ diff --git a/app/src/main/res/drawable-xhdpi/ic_audio_light.png b/app/src/main/res/drawable-xhdpi/ic_audio_light.png deleted file mode 100644 index 3fe1d58cb00..00000000000 Binary files a/app/src/main/res/drawable-xhdpi/ic_audio_light.png and /dev/null differ diff --git a/app/src/main/res/drawable-xhdpi/ic_circle_fill_white_48dp.png b/app/src/main/res/drawable-xhdpi/ic_circle_fill_white_48dp.png deleted file mode 100644 index 0bd9683860e..00000000000 Binary files a/app/src/main/res/drawable-xhdpi/ic_circle_fill_white_48dp.png and /dev/null differ diff --git a/app/src/main/res/drawable-xhdpi/ic_contact_picture.png b/app/src/main/res/drawable-xhdpi/ic_contact_picture.png deleted file mode 100644 index 3a152490841..00000000000 Binary files a/app/src/main/res/drawable-xhdpi/ic_contact_picture.png and /dev/null differ diff --git a/app/src/main/res/drawable-xhdpi/ic_document_small_light.png b/app/src/main/res/drawable-xhdpi/ic_document_small_light.png deleted file mode 100644 index d2378b17759..00000000000 Binary files a/app/src/main/res/drawable-xhdpi/ic_document_small_light.png and /dev/null differ diff --git a/app/src/main/res/drawable-xhdpi/ic_image_light.png b/app/src/main/res/drawable-xhdpi/ic_image_light.png deleted file mode 100644 index 5521a654cff..00000000000 Binary files a/app/src/main/res/drawable-xhdpi/ic_image_light.png and /dev/null differ diff --git a/app/src/main/res/drawable-xhdpi/ic_movie_creation_light.png b/app/src/main/res/drawable-xhdpi/ic_movie_creation_light.png deleted file mode 100644 index 5683744ed5c..00000000000 Binary files a/app/src/main/res/drawable-xhdpi/ic_movie_creation_light.png and /dev/null differ diff --git a/app/src/main/res/drawable-xhdpi/ic_note_to_self.png b/app/src/main/res/drawable-xhdpi/ic_note_to_self.png deleted file mode 100644 index cd1520cf748..00000000000 Binary files a/app/src/main/res/drawable-xhdpi/ic_note_to_self.png and /dev/null differ diff --git a/app/src/main/res/drawable-xhdpi/ic_photo_camera_light.png b/app/src/main/res/drawable-xhdpi/ic_photo_camera_light.png deleted file mode 100644 index 75a913f4494..00000000000 Binary files a/app/src/main/res/drawable-xhdpi/ic_photo_camera_light.png and /dev/null differ diff --git a/app/src/main/res/drawable-xhdpi/ic_unlocked_white_18dp.png b/app/src/main/res/drawable-xhdpi/ic_unlocked_white_18dp.png deleted file mode 100644 index 1d470e8f196..00000000000 Binary files a/app/src/main/res/drawable-xhdpi/ic_unlocked_white_18dp.png and /dev/null differ diff --git a/app/src/main/res/drawable-xhdpi/ic_video_light.png b/app/src/main/res/drawable-xhdpi/ic_video_light.png deleted file mode 100644 index 12c8a6d12a3..00000000000 Binary files a/app/src/main/res/drawable-xhdpi/ic_video_light.png and /dev/null differ diff --git a/app/src/main/res/drawable-xhdpi/ic_volume_up_light.png b/app/src/main/res/drawable-xhdpi/ic_volume_up_light.png deleted file mode 100644 index d62b97730c8..00000000000 Binary files a/app/src/main/res/drawable-xhdpi/ic_volume_up_light.png and /dev/null differ diff --git a/app/src/main/res/drawable-xhdpi/ic_warning_light.png b/app/src/main/res/drawable-xhdpi/ic_warning_light.png deleted file mode 100644 index 489662e29ac..00000000000 Binary files a/app/src/main/res/drawable-xhdpi/ic_warning_light.png and /dev/null differ diff --git a/app/src/main/res/drawable-xxhdpi/divet_lower_right_dark.png b/app/src/main/res/drawable-xxhdpi/divet_lower_right_dark.png deleted file mode 100644 index f07336f56b2..00000000000 Binary files a/app/src/main/res/drawable-xxhdpi/divet_lower_right_dark.png and /dev/null differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_account_box_dark.png b/app/src/main/res/drawable-xxhdpi/ic_account_box_dark.png deleted file mode 100644 index a95c8006c54..00000000000 Binary files a/app/src/main/res/drawable-xxhdpi/ic_account_box_dark.png and /dev/null differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_account_box_light.png b/app/src/main/res/drawable-xxhdpi/ic_account_box_light.png deleted file mode 100644 index 67716eff3b2..00000000000 Binary files a/app/src/main/res/drawable-xxhdpi/ic_account_box_light.png and /dev/null differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_attach_grey600_24dp.png b/app/src/main/res/drawable-xxhdpi/ic_attach_grey600_24dp.png deleted file mode 100644 index 7b0785e116d..00000000000 Binary files a/app/src/main/res/drawable-xxhdpi/ic_attach_grey600_24dp.png and /dev/null differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_audio_light.png b/app/src/main/res/drawable-xxhdpi/ic_audio_light.png deleted file mode 100644 index d53eef6ff62..00000000000 Binary files a/app/src/main/res/drawable-xxhdpi/ic_audio_light.png and /dev/null differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_circle_fill_white_48dp.png b/app/src/main/res/drawable-xxhdpi/ic_circle_fill_white_48dp.png deleted file mode 100644 index c83d5fba16a..00000000000 Binary files a/app/src/main/res/drawable-xxhdpi/ic_circle_fill_white_48dp.png and /dev/null differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_contact_picture.png b/app/src/main/res/drawable-xxhdpi/ic_contact_picture.png deleted file mode 100644 index 47187554f8c..00000000000 Binary files a/app/src/main/res/drawable-xxhdpi/ic_contact_picture.png and /dev/null differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_document_small_light.png b/app/src/main/res/drawable-xxhdpi/ic_document_small_light.png deleted file mode 100644 index 4bf082e0d83..00000000000 Binary files a/app/src/main/res/drawable-xxhdpi/ic_document_small_light.png and /dev/null differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_image_light.png b/app/src/main/res/drawable-xxhdpi/ic_image_light.png deleted file mode 100644 index 1614d5a5d17..00000000000 Binary files a/app/src/main/res/drawable-xxhdpi/ic_image_light.png and /dev/null differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_movie_creation_light.png b/app/src/main/res/drawable-xxhdpi/ic_movie_creation_light.png deleted file mode 100644 index 65d596e157a..00000000000 Binary files a/app/src/main/res/drawable-xxhdpi/ic_movie_creation_light.png and /dev/null differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_note_to_self.png b/app/src/main/res/drawable-xxhdpi/ic_note_to_self.png deleted file mode 100644 index 59bf9e76aa3..00000000000 Binary files a/app/src/main/res/drawable-xxhdpi/ic_note_to_self.png and /dev/null differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_photo_camera_light.png b/app/src/main/res/drawable-xxhdpi/ic_photo_camera_light.png deleted file mode 100644 index 5b3afefbc95..00000000000 Binary files a/app/src/main/res/drawable-xxhdpi/ic_photo_camera_light.png and /dev/null differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_unlocked_white_18dp.png b/app/src/main/res/drawable-xxhdpi/ic_unlocked_white_18dp.png deleted file mode 100644 index 6b933b1b931..00000000000 Binary files a/app/src/main/res/drawable-xxhdpi/ic_unlocked_white_18dp.png and /dev/null differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_video_light.png b/app/src/main/res/drawable-xxhdpi/ic_video_light.png deleted file mode 100644 index caedec82596..00000000000 Binary files a/app/src/main/res/drawable-xxhdpi/ic_video_light.png and /dev/null differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_volume_up_light.png b/app/src/main/res/drawable-xxhdpi/ic_volume_up_light.png deleted file mode 100644 index ab2186867f9..00000000000 Binary files a/app/src/main/res/drawable-xxhdpi/ic_volume_up_light.png and /dev/null differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_warning_light.png b/app/src/main/res/drawable-xxhdpi/ic_warning_light.png deleted file mode 100644 index 46255007f77..00000000000 Binary files a/app/src/main/res/drawable-xxhdpi/ic_warning_light.png and /dev/null differ diff --git a/app/src/main/res/drawable-xxxhdpi/ic_circle_fill_white_48dp.png b/app/src/main/res/drawable-xxxhdpi/ic_circle_fill_white_48dp.png deleted file mode 100644 index b6b9ab51bd7..00000000000 Binary files a/app/src/main/res/drawable-xxxhdpi/ic_circle_fill_white_48dp.png and /dev/null differ diff --git a/app/src/main/res/drawable-xxxhdpi/ic_document_small_light.png b/app/src/main/res/drawable-xxxhdpi/ic_document_small_light.png deleted file mode 100644 index e3aaf73f686..00000000000 Binary files a/app/src/main/res/drawable-xxxhdpi/ic_document_small_light.png and /dev/null differ diff --git a/app/src/main/res/drawable-xxxhdpi/ic_note_to_self.png b/app/src/main/res/drawable-xxxhdpi/ic_note_to_self.png deleted file mode 100644 index 149489384ff..00000000000 Binary files a/app/src/main/res/drawable-xxxhdpi/ic_note_to_self.png and /dev/null differ diff --git a/app/src/main/res/drawable-xxxhdpi/ic_photo_camera_light.png b/app/src/main/res/drawable-xxxhdpi/ic_photo_camera_light.png deleted file mode 100644 index 7f43cc3152c..00000000000 Binary files a/app/src/main/res/drawable-xxxhdpi/ic_photo_camera_light.png and /dev/null differ diff --git a/app/src/main/res/drawable-xxxhdpi/ic_unlocked_white_18dp.png b/app/src/main/res/drawable-xxxhdpi/ic_unlocked_white_18dp.png deleted file mode 100644 index 220934afd04..00000000000 Binary files a/app/src/main/res/drawable-xxxhdpi/ic_unlocked_white_18dp.png and /dev/null differ diff --git a/app/src/main/res/drawable-xxxhdpi/oxen_dark_mode.png b/app/src/main/res/drawable-xxxhdpi/oxen_dark_mode.png deleted file mode 100644 index 84c7bb10591..00000000000 Binary files a/app/src/main/res/drawable-xxxhdpi/oxen_dark_mode.png and /dev/null differ diff --git a/app/src/main/res/drawable/circle_tintable_4dp_inset.xml b/app/src/main/res/drawable/circle_tintable_4dp_inset.xml deleted file mode 100644 index 92b0e0830c1..00000000000 --- a/app/src/main/res/drawable/circle_tintable_4dp_inset.xml +++ /dev/null @@ -1,4 +0,0 @@ - - \ No newline at end of file diff --git a/app/src/main/res/drawable/colorpickerpreference_pref_swatch.xml b/app/src/main/res/drawable/colorpickerpreference_pref_swatch.xml deleted file mode 100644 index 927bb9f3140..00000000000 --- a/app/src/main/res/drawable/colorpickerpreference_pref_swatch.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - \ No newline at end of file diff --git a/app/src/main/res/drawable/compose_background_light.xml b/app/src/main/res/drawable/compose_background_light.xml deleted file mode 100644 index d6e191fe013..00000000000 --- a/app/src/main/res/drawable/compose_background_light.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/drawable/conversation_item_sent_indicator_text_shape.xml b/app/src/main/res/drawable/conversation_item_sent_indicator_text_shape.xml deleted file mode 100644 index 59da5934aa0..00000000000 --- a/app/src/main/res/drawable/conversation_item_sent_indicator_text_shape.xml +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - - - - - - - - - - - - - diff --git a/app/src/main/res/drawable/conversation_list_divider_shape.xml b/app/src/main/res/drawable/conversation_list_divider_shape.xml deleted file mode 100644 index 726ef9eaeaa..00000000000 --- a/app/src/main/res/drawable/conversation_list_divider_shape.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/app/src/main/res/drawable/conversation_list_item_background.xml b/app/src/main/res/drawable/conversation_list_item_background.xml deleted file mode 100644 index 2e3818f7e3c..00000000000 --- a/app/src/main/res/drawable/conversation_list_item_background.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - diff --git a/app/src/main/res/drawable/conversation_menu_gradient.xml b/app/src/main/res/drawable/conversation_menu_gradient.xml deleted file mode 100644 index 04862a87ac8..00000000000 --- a/app/src/main/res/drawable/conversation_menu_gradient.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - \ No newline at end of file diff --git a/app/src/main/res/drawable/conversation_pinned_background.xml b/app/src/main/res/drawable/conversation_pinned_background.xml deleted file mode 100644 index eb64dc7f5f4..00000000000 --- a/app/src/main/res/drawable/conversation_pinned_background.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/drawable/cross.xml b/app/src/main/res/drawable/cross.xml deleted file mode 100644 index 5b090de2b3c..00000000000 --- a/app/src/main/res/drawable/cross.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - \ No newline at end of file diff --git a/app/src/main/res/drawable/danger_dialog_button_background.xml b/app/src/main/res/drawable/danger_dialog_button_background.xml deleted file mode 100644 index 0f72e98c740..00000000000 --- a/app/src/main/res/drawable/danger_dialog_button_background.xml +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/drawable/default_session_background.xml b/app/src/main/res/drawable/default_session_background.xml deleted file mode 100644 index e961e985e6d..00000000000 --- a/app/src/main/res/drawable/default_session_background.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/app/src/main/res/drawable/emoji_variation_selector_background_light.xml b/app/src/main/res/drawable/emoji_variation_selector_background_light.xml deleted file mode 100644 index 415acd689e6..00000000000 --- a/app/src/main/res/drawable/emoji_variation_selector_background_light.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_any_emoji_32.xml b/app/src/main/res/drawable/ic_any_emoji_32.xml deleted file mode 100644 index ab794464375..00000000000 --- a/app/src/main/res/drawable/ic_any_emoji_32.xml +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - diff --git a/app/src/main/res/drawable/ic_baseline_arrow_forward_24.xml b/app/src/main/res/drawable/ic_baseline_arrow_forward_24.xml deleted file mode 100644 index 702e70c537c..00000000000 --- a/app/src/main/res/drawable/ic_baseline_arrow_forward_24.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/ic_baseline_call_made_24.xml b/app/src/main/res/drawable/ic_baseline_call_made_24.xml deleted file mode 100644 index 2183fbc7f59..00000000000 --- a/app/src/main/res/drawable/ic_baseline_call_made_24.xml +++ /dev/null @@ -1,11 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/ic_baseline_call_missed_24.xml b/app/src/main/res/drawable/ic_baseline_call_missed_24.xml deleted file mode 100644 index 28958d07ab2..00000000000 --- a/app/src/main/res/drawable/ic_baseline_call_missed_24.xml +++ /dev/null @@ -1,11 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/ic_baseline_call_received_24.xml b/app/src/main/res/drawable/ic_baseline_call_received_24.xml deleted file mode 100644 index 05192f9f308..00000000000 --- a/app/src/main/res/drawable/ic_baseline_call_received_24.xml +++ /dev/null @@ -1,11 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/ic_baseline_mic_24.xml b/app/src/main/res/drawable/ic_baseline_mic_24.xml deleted file mode 100644 index 791b4757047..00000000000 --- a/app/src/main/res/drawable/ic_baseline_mic_24.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/ic_baseline_night_mode_24.xml b/app/src/main/res/drawable/ic_baseline_night_mode_24.xml deleted file mode 100644 index a8eeb6d746d..00000000000 --- a/app/src/main/res/drawable/ic_baseline_night_mode_24.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/ic_baseline_pause_circle_filled_48.xml b/app/src/main/res/drawable/ic_baseline_pause_circle_filled_48.xml deleted file mode 100644 index 3088f825d4d..00000000000 --- a/app/src/main/res/drawable/ic_baseline_pause_circle_filled_48.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/ic_baseline_play_circle_filled_48.xml b/app/src/main/res/drawable/ic_baseline_play_circle_filled_48.xml deleted file mode 100644 index 1a38d82b422..00000000000 --- a/app/src/main/res/drawable/ic_baseline_play_circle_filled_48.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/ic_baseline_visibility_24.xml b/app/src/main/res/drawable/ic_baseline_visibility_24.xml deleted file mode 100644 index a3e222a2d15..00000000000 --- a/app/src/main/res/drawable/ic_baseline_visibility_24.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/ic_baseline_visibility_off_24.xml b/app/src/main/res/drawable/ic_baseline_visibility_off_24.xml deleted file mode 100644 index 92c48569fd4..00000000000 --- a/app/src/main/res/drawable/ic_baseline_visibility_off_24.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/ic_baseline_volume_mute_24.xml b/app/src/main/res/drawable/ic_baseline_volume_mute_24.xml deleted file mode 100644 index fc41db8b9f9..00000000000 --- a/app/src/main/res/drawable/ic_baseline_volume_mute_24.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/ic_circle.xml b/app/src/main/res/drawable/ic_circle.xml deleted file mode 100644 index b5b8615d22b..00000000000 --- a/app/src/main/res/drawable/ic_circle.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/ic_emoji_activity_light_20.xml b/app/src/main/res/drawable/ic_emoji_activity_light_20.xml deleted file mode 100644 index 809d801ee86..00000000000 --- a/app/src/main/res/drawable/ic_emoji_activity_light_20.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/ic_emoji_animal_light_20.xml b/app/src/main/res/drawable/ic_emoji_animal_light_20.xml deleted file mode 100644 index 63f2f9e10c3..00000000000 --- a/app/src/main/res/drawable/ic_emoji_animal_light_20.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/ic_emoji_emoticon_light_20.xml b/app/src/main/res/drawable/ic_emoji_emoticon_light_20.xml deleted file mode 100644 index 8b5df632c6e..00000000000 --- a/app/src/main/res/drawable/ic_emoji_emoticon_light_20.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/ic_emoji_flag_light_20.xml b/app/src/main/res/drawable/ic_emoji_flag_light_20.xml deleted file mode 100644 index f5b87e490cb..00000000000 --- a/app/src/main/res/drawable/ic_emoji_flag_light_20.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/ic_emoji_food_light_20.xml b/app/src/main/res/drawable/ic_emoji_food_light_20.xml deleted file mode 100644 index 49898b5a2ff..00000000000 --- a/app/src/main/res/drawable/ic_emoji_food_light_20.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/ic_emoji_object_light_20.xml b/app/src/main/res/drawable/ic_emoji_object_light_20.xml deleted file mode 100644 index 6aa4c053e60..00000000000 --- a/app/src/main/res/drawable/ic_emoji_object_light_20.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/ic_emoji_outline_keyboard.xml b/app/src/main/res/drawable/ic_emoji_outline_keyboard.xml deleted file mode 100644 index ee61f62a881..00000000000 --- a/app/src/main/res/drawable/ic_emoji_outline_keyboard.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/ic_emoji_people_light_20.xml b/app/src/main/res/drawable/ic_emoji_people_light_20.xml deleted file mode 100644 index 26867afd88b..00000000000 --- a/app/src/main/res/drawable/ic_emoji_people_light_20.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/ic_emoji_symbol_light_20.xml b/app/src/main/res/drawable/ic_emoji_symbol_light_20.xml deleted file mode 100644 index 5a5b27697b9..00000000000 --- a/app/src/main/res/drawable/ic_emoji_symbol_light_20.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/ic_emoji_travel_light_20.xml b/app/src/main/res/drawable/ic_emoji_travel_light_20.xml deleted file mode 100644 index 6a0448e21b4..00000000000 --- a/app/src/main/res/drawable/ic_emoji_travel_light_20.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/ic_launcher_foreground_monochrome.xml b/app/src/main/res/drawable/ic_launcher_foreground_monochrome.xml deleted file mode 100644 index 5f21434d45b..00000000000 --- a/app/src/main/res/drawable/ic_launcher_foreground_monochrome.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - diff --git a/app/src/main/res/drawable/ic_outline_info_24.xml b/app/src/main/res/drawable/ic_outline_info_24.xml deleted file mode 100644 index 35f7f5f611e..00000000000 --- a/app/src/main/res/drawable/ic_outline_info_24.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/ic_outline_keyboard_24.xml b/app/src/main/res/drawable/ic_outline_keyboard_24.xml deleted file mode 100644 index 4a2b3a78f1a..00000000000 --- a/app/src/main/res/drawable/ic_outline_keyboard_24.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/ic_outline_message_requests_24.xml b/app/src/main/res/drawable/ic_outline_message_requests_24.xml deleted file mode 100644 index e01bceed1ca..00000000000 --- a/app/src/main/res/drawable/ic_outline_message_requests_24.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/ic_outline_mic_none_24.xml b/app/src/main/res/drawable/ic_outline_mic_none_24.xml deleted file mode 100644 index 2810bc9a2ef..00000000000 --- a/app/src/main/res/drawable/ic_outline_mic_none_24.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/ic_outline_mic_off_24.xml b/app/src/main/res/drawable/ic_outline_mic_off_24.xml deleted file mode 100644 index 631ced9615e..00000000000 --- a/app/src/main/res/drawable/ic_outline_mic_off_24.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/ic_outline_night_mode_24.xml b/app/src/main/res/drawable/ic_outline_night_mode_24.xml deleted file mode 100644 index 8b8b2700ac5..00000000000 --- a/app/src/main/res/drawable/ic_outline_night_mode_24.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/ic_outline_night_mode_auto_24.xml b/app/src/main/res/drawable/ic_outline_night_mode_auto_24.xml deleted file mode 100644 index 379afd3360b..00000000000 --- a/app/src/main/res/drawable/ic_outline_night_mode_auto_24.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - diff --git a/app/src/main/res/drawable/ic_outline_photo_camera_24.xml b/app/src/main/res/drawable/ic_outline_photo_camera_24.xml deleted file mode 100644 index 59e933eea1d..00000000000 --- a/app/src/main/res/drawable/ic_outline_photo_camera_24.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - diff --git a/app/src/main/res/drawable/ic_outline_settings_24.xml b/app/src/main/res/drawable/ic_outline_settings_24.xml deleted file mode 100644 index c939e9ce8b1..00000000000 --- a/app/src/main/res/drawable/ic_outline_settings_24.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/ic_outline_videocam_24.xml b/app/src/main/res/drawable/ic_outline_videocam_24.xml deleted file mode 100644 index cce4a0b74fc..00000000000 --- a/app/src/main/res/drawable/ic_outline_videocam_24.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/ic_outline_videocam_off_24.xml b/app/src/main/res/drawable/ic_outline_videocam_off_24.xml deleted file mode 100644 index e3ebd9e65b5..00000000000 --- a/app/src/main/res/drawable/ic_outline_videocam_off_24.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/ic_recent_light_20.xml b/app/src/main/res/drawable/ic_recent_light_20.xml deleted file mode 100644 index 1e888e4f6cd..00000000000 --- a/app/src/main/res/drawable/ic_recent_light_20.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/ic_sticker_outline_keyboard.xml b/app/src/main/res/drawable/ic_sticker_outline_keyboard.xml deleted file mode 100644 index b334a9cff36..00000000000 --- a/app/src/main/res/drawable/ic_sticker_outline_keyboard.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/ic_ui_mode_24.xml b/app/src/main/res/drawable/ic_ui_mode_24.xml deleted file mode 100644 index 8e7eb285739..00000000000 --- a/app/src/main/res/drawable/ic_ui_mode_24.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/app/src/main/res/drawable/sticker_missing_background_light.xml b/app/src/main/res/drawable/image_loading_background.xml similarity index 58% rename from app/src/main/res/drawable/sticker_missing_background_light.xml rename to app/src/main/res/drawable/image_loading_background.xml index fdc4810c96b..d2e3dfbfa7c 100644 --- a/app/src/main/res/drawable/sticker_missing_background_light.xml +++ b/app/src/main/res/drawable/image_loading_background.xml @@ -3,7 +3,8 @@ xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle"> - - + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/message_bubble_background_received_end.xml b/app/src/main/res/drawable/message_bubble_background_received_end.xml deleted file mode 100644 index 09f5d7d6dfc..00000000000 --- a/app/src/main/res/drawable/message_bubble_background_received_end.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - - - diff --git a/app/src/main/res/drawable/message_bubble_background_received_middle.xml b/app/src/main/res/drawable/message_bubble_background_received_middle.xml deleted file mode 100644 index 78da45378e7..00000000000 --- a/app/src/main/res/drawable/message_bubble_background_received_middle.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - - - diff --git a/app/src/main/res/drawable/message_bubble_background_received_start.xml b/app/src/main/res/drawable/message_bubble_background_received_start.xml deleted file mode 100644 index 6908d244e64..00000000000 --- a/app/src/main/res/drawable/message_bubble_background_received_start.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - - - diff --git a/app/src/main/res/drawable/message_bubble_background_sent_alone.xml b/app/src/main/res/drawable/message_bubble_background_sent_alone.xml deleted file mode 100644 index 7ae0cae4936..00000000000 --- a/app/src/main/res/drawable/message_bubble_background_sent_alone.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/drawable/message_bubble_background_sent_end.xml b/app/src/main/res/drawable/message_bubble_background_sent_end.xml deleted file mode 100644 index 81325f1ad2b..00000000000 --- a/app/src/main/res/drawable/message_bubble_background_sent_end.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/drawable/message_bubble_background_sent_middle.xml b/app/src/main/res/drawable/message_bubble_background_sent_middle.xml deleted file mode 100644 index e8fa38aab85..00000000000 --- a/app/src/main/res/drawable/message_bubble_background_sent_middle.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - - - diff --git a/app/src/main/res/drawable/message_bubble_background_sent_start.xml b/app/src/main/res/drawable/message_bubble_background_sent_start.xml deleted file mode 100644 index 03f90a31f70..00000000000 --- a/app/src/main/res/drawable/message_bubble_background_sent_start.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - - - diff --git a/app/src/main/res/drawable/pause_icon.xml b/app/src/main/res/drawable/pause_icon.xml deleted file mode 100644 index e7f9fce53e8..00000000000 --- a/app/src/main/res/drawable/pause_icon.xml +++ /dev/null @@ -1,35 +0,0 @@ - - - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/drawable/pause_to_play_animation.xml b/app/src/main/res/drawable/pause_to_play_animation.xml deleted file mode 100644 index 4d19dc4f9f6..00000000000 --- a/app/src/main/res/drawable/pause_to_play_animation.xml +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/drawable/play_icon.xml b/app/src/main/res/drawable/play_icon.xml deleted file mode 100644 index 7472ac7de07..00000000000 --- a/app/src/main/res/drawable/play_icon.xml +++ /dev/null @@ -1,34 +0,0 @@ - - - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/drawable/play_to_pause_animation.xml b/app/src/main/res/drawable/play_to_pause_animation.xml deleted file mode 100644 index b8bc7934fe7..00000000000 --- a/app/src/main/res/drawable/play_to_pause_animation.xml +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/drawable/pn_option_background.xml b/app/src/main/res/drawable/pn_option_background.xml deleted file mode 100644 index f84067c640a..00000000000 --- a/app/src/main/res/drawable/pn_option_background.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/drawable/pn_option_background_deselect_transition.xml b/app/src/main/res/drawable/pn_option_background_deselect_transition.xml deleted file mode 100644 index 7fcb8e1160b..00000000000 --- a/app/src/main/res/drawable/pn_option_background_deselect_transition.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - - \ No newline at end of file diff --git a/app/src/main/res/drawable/pn_option_background_select_transition.xml b/app/src/main/res/drawable/pn_option_background_select_transition.xml deleted file mode 100644 index 21c58cf71d6..00000000000 --- a/app/src/main/res/drawable/pn_option_background_select_transition.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - - \ No newline at end of file diff --git a/app/src/main/res/drawable/pn_option_background_selected.xml b/app/src/main/res/drawable/pn_option_background_selected.xml deleted file mode 100644 index d94a5cfae76..00000000000 --- a/app/src/main/res/drawable/pn_option_background_selected.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/drawable/preference_single_no_padding.xml b/app/src/main/res/drawable/preference_single_no_padding.xml deleted file mode 100644 index 483894fcc29..00000000000 --- a/app/src/main/res/drawable/preference_single_no_padding.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/drawable/prominent_outline_button_medium_background_accent.xml b/app/src/main/res/drawable/prominent_outline_button_medium_background_accent.xml deleted file mode 100644 index 5f0a51ec40f..00000000000 --- a/app/src/main/res/drawable/prominent_outline_button_medium_background_accent.xml +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/drawable/setting_button_background.xml b/app/src/main/res/drawable/setting_button_background.xml deleted file mode 100644 index 2f177318e0e..00000000000 --- a/app/src/main/res/drawable/setting_button_background.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/drawable/sticker_button_light.xml b/app/src/main/res/drawable/sticker_button_light.xml deleted file mode 100644 index f20b4e89d19..00000000000 --- a/app/src/main/res/drawable/sticker_button_light.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - - - - - - - - - - diff --git a/app/src/main/res/drawable/sticky_date_header_background_light.xml b/app/src/main/res/drawable/sticky_date_header_background_light.xml deleted file mode 100644 index 3a92469278f..00000000000 --- a/app/src/main/res/drawable/sticky_date_header_background_light.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/app/src/main/res/drawable/unimportant_dialog_button_background.xml b/app/src/main/res/drawable/unimportant_dialog_button_background.xml deleted file mode 100644 index c517c1a135f..00000000000 --- a/app/src/main/res/drawable/unimportant_dialog_button_background.xml +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/drawable/unimportant_filled_button_medium_background.xml b/app/src/main/res/drawable/unimportant_filled_button_medium_background.xml deleted file mode 100644 index dc3b8df2381..00000000000 --- a/app/src/main/res/drawable/unimportant_filled_button_medium_background.xml +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/drawable/unimportant_outline_button_medium_background.xml b/app/src/main/res/drawable/unimportant_outline_button_medium_background.xml deleted file mode 100644 index d12f7408d91..00000000000 --- a/app/src/main/res/drawable/unimportant_outline_button_medium_background.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - diff --git a/app/src/main/res/drawable/unread_count_background_light.xml b/app/src/main/res/drawable/unread_count_background_light.xml deleted file mode 100644 index e87f5160986..00000000000 --- a/app/src/main/res/drawable/unread_count_background_light.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/layout/activity_edit_closed_group.xml b/app/src/main/res/layout/activity_edit_closed_group.xml index 78def9e972d..c43360e3628 100644 --- a/app/src/main/res/layout/activity_edit_closed_group.xml +++ b/app/src/main/res/layout/activity_edit_closed_group.xml @@ -47,7 +47,7 @@ android:textAlignment="center" android:paddingTop="12dp" android:paddingBottom="12dp" - android:inputType="text" + android:inputType="textCapWords" android:singleLine="true" android:imeOptions="actionDone" android:maxLength="@integer/max_group_and_community_name_length_chars" diff --git a/app/src/main/res/layout/activity_join_public_chat.xml b/app/src/main/res/layout/activity_join_public_chat.xml deleted file mode 100644 index e4d5fb433b7..00000000000 --- a/app/src/main/res/layout/activity_join_public_chat.xml +++ /dev/null @@ -1,38 +0,0 @@ - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/layout/activity_link_device.xml b/app/src/main/res/layout/activity_link_device.xml deleted file mode 100644 index 5c93e251c92..00000000000 --- a/app/src/main/res/layout/activity_link_device.xml +++ /dev/null @@ -1,20 +0,0 @@ - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/layout/activity_message_detail.xml b/app/src/main/res/layout/activity_message_detail.xml deleted file mode 100644 index 49c1af54e31..00000000000 --- a/app/src/main/res/layout/activity_message_detail.xml +++ /dev/null @@ -1,115 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -