From 8ad7005c5fee564d4bc18cd753c4170fe4cbb493 Mon Sep 17 00:00:00 2001 From: ThomasSession Date: Tue, 23 Jul 2024 18:34:18 +1000 Subject: [PATCH 01/50] Adding back the fullscreen intent permission --- app/src/main/AndroidManifest.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 9737a77d2e8..7e04ad44ca5 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -60,6 +60,7 @@ + From 631d93c20cbc663e33824da97324eb62ed6b05b7 Mon Sep 17 00:00:00 2001 From: Fanchao Liu <273191+simophin@users.noreply.github.com> Date: Wed, 24 Jul 2024 16:18:18 +1000 Subject: [PATCH 02/50] Replace kapt with ksp and update dependencies (#1560) --- app/build.gradle | 22 +++++----------- .../loki/messenger/HomeActivityTests.kt | 4 +-- .../securesms/MediaGalleryAdapter.java | 6 ++--- .../securesms/MediaOverviewActivity.java | 4 +-- .../securesms/MediaPreviewActivity.java | 18 ++++++------- .../PassphraseRequiredActionBarActivity.java | 2 +- .../securesms/calls/WebRtcCallActivity.kt | 4 +-- .../securesms/components/AvatarImageView.java | 10 +++---- .../securesms/components/MediaView.java | 4 +-- .../components/ProfilePictureView.kt | 6 ++--- .../components/RecentPhotoViewRail.java | 4 +-- .../securesms/components/StickerView.java | 2 +- .../components/ZoomingImageView.java | 6 ++--- .../emoji/EmojiKeyboardProvider.java | 4 +-- .../components/emoji/MediaKeyboard.java | 4 +-- .../emoji/MediaKeyboardBottomTabAdapter.java | 8 +++--- .../emoji/MediaKeyboardProvider.java | 4 +-- .../contacts/ContactSelectionListAdapter.kt | 4 +-- .../contacts/ContactSelectionListFragment.kt | 4 +-- .../contacts/SelectContactsActivity.kt | 4 +-- .../contacts/SelectContactsAdapter.kt | 4 +-- .../securesms/contacts/UserView.kt | 4 +-- .../conversation/v2/ConversationActivityV2.kt | 4 +-- .../conversation/v2/ConversationAdapter.kt | 4 +-- .../v2/components/AlbumThumbnailView.kt | 4 +-- .../v2/components/LinkPreviewDraftView.kt | 4 +-- .../MentionCandidateSelectionView.kt | 6 ++--- .../v2/components/MentionCandidateView.kt | 4 +-- .../conversation/v2/input_bar/InputBar.kt | 6 ++--- .../v2/messages/LinkPreviewView.kt | 4 +-- .../conversation/v2/messages/QuoteView.kt | 4 +-- .../v2/messages/VisibleMessageContentView.kt | 6 ++--- .../v2/messages/VisibleMessageView.kt | 6 ++--- .../v2/utilities/AttachmentManager.java | 4 +-- .../v2/utilities/ThumbnailView.kt | 26 +++++++++---------- .../securesms/giph/ui/GiphyAdapter.java | 10 +++---- .../securesms/giph/ui/GiphyFragment.java | 4 +-- .../securesms/groups/CreateGroupFragment.kt | 4 +-- .../groups/EditClosedGroupActivity.kt | 6 ++--- .../groups/EditClosedGroupMembersAdapter.kt | 4 +-- .../securesms/home/HomeActivity.kt | 8 +++--- .../securesms/home/HomeAdapter.kt | 4 +-- .../mediapreview/MediaRailAdapter.java | 8 +++--- .../securesms/mediasend/Camera1Fragment.java | 4 +-- .../mediasend/MediaPickerFolderAdapter.java | 8 +++--- .../mediasend/MediaPickerFolderFragment.java | 4 +-- .../mediasend/MediaPickerItemAdapter.java | 8 +++--- .../mediasend/MediaPickerItemFragment.java | 4 +-- .../mediasend/MediaSendFragment.java | 4 +-- .../mediasend/MediaSendGifFragment.java | 4 +-- .../messagerequests/MessageRequestView.kt | 4 +-- .../MessageRequestsActivity.kt | 8 +++--- .../messagerequests/MessageRequestsAdapter.kt | 4 +-- .../SingleRecipientNotificationBuilder.java | 6 ++--- .../preferences/BlockedContactsAdapter.kt | 4 +-- .../preferences/RadioOptionAdapter.kt | 4 +-- .../reactions/ReactionRecipientsAdapter.java | 2 +- .../scribbles/StickerSelectFragment.java | 12 ++++----- .../securesms/scribbles/UriGlideRenderer.java | 8 +++--- .../securesms/service/DirectShareService.java | 4 +-- .../securesms/util/BitmapUtil.java | 6 ++--- .../securesms/util/MediaUtil.java | 4 +-- build.gradle | 8 +++--- gradle.properties | 8 +++--- libsession/build.gradle | 8 +++++- 65 files changed, 193 insertions(+), 195 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 425cd58c2ce..de71e3209ac 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -10,22 +10,19 @@ buildscript { 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" @@ -89,7 +86,7 @@ android { compose true } composeOptions { - kotlinCompilerExtensionVersion '1.4.7' + kotlinCompilerExtensionVersion '1.5.14' } defaultConfig { @@ -239,8 +236,9 @@ android { 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' @@ -281,8 +279,7 @@ 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" + 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' @@ -290,8 +287,6 @@ dependencies { implementation 'com.theartofdev.edmodo:android-image-cropper:2.8.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 'mobi.upod:time-duration-picker:1.1.3' implementation 'com.google.zxing:core:3.2.1' implementation ('com.davemorrissey.labs:subsampling-scale-image-view:3.6.0') { @@ -415,8 +410,3 @@ def autoResConfig() { .collect { matcher -> matcher.group(1) } .sort() } - -// Allow references to generated code -kapt { - correctErrorTypes = true -} diff --git a/app/src/androidTest/java/network/loki/messenger/HomeActivityTests.kt b/app/src/androidTest/java/network/loki/messenger/HomeActivityTests.kt index 6fb3888ff2a..af1d8f6d222 100644 --- a/app/src/androidTest/java/network/loki/messenger/HomeActivityTests.kt +++ b/app/src/androidTest/java/network/loki/messenger/HomeActivityTests.kt @@ -40,7 +40,7 @@ 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 @RunWith(AndroidJUnit4::class) @LargeTest @@ -71,7 +71,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()) diff --git a/app/src/main/java/org/thoughtcrime/securesms/MediaGalleryAdapter.java b/app/src/main/java/org/thoughtcrime/securesms/MediaGalleryAdapter.java index 62766d1cd78..9a262a2b0a1 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/MediaGalleryAdapter.java +++ b/app/src/main/java/org/thoughtcrime/securesms/MediaGalleryAdapter.java @@ -29,7 +29,7 @@ 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 com.bumptech.glide.RequestManager; import org.thoughtcrime.securesms.mms.Slide; import org.thoughtcrime.securesms.util.MediaUtil; @@ -46,7 +46,7 @@ class MediaGalleryAdapter extends StickyHeaderGridAdapter { private static final String TAG = MediaGalleryAdapter.class.getSimpleName(); private final Context context; - private final GlideRequests glideRequests; + private final RequestManager glideRequests; private final Locale locale; private final ItemClickListener itemClickListener; private final Set selected; @@ -74,7 +74,7 @@ private static class HeaderHolder extends StickyHeaderGridAdapter.HeaderViewHold } MediaGalleryAdapter(@NonNull Context context, - @NonNull GlideRequests glideRequests, + @NonNull RequestManager glideRequests, BucketedThreadMedia media, Locale locale, ItemClickListener clickListener) diff --git a/app/src/main/java/org/thoughtcrime/securesms/MediaOverviewActivity.java b/app/src/main/java/org/thoughtcrime/securesms/MediaOverviewActivity.java index 95ba15c82e8..7715eab01e0 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/MediaOverviewActivity.java +++ b/app/src/main/java/org/thoughtcrime/securesms/MediaOverviewActivity.java @@ -61,7 +61,7 @@ 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 com.bumptech.glide.Glide; import org.thoughtcrime.securesms.permissions.Permissions; import org.session.libsession.utilities.recipients.Recipient; import org.thoughtcrime.securesms.util.AttachmentUtil; @@ -227,7 +227,7 @@ public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, this.gridManager = new StickyHeaderGridLayoutManager(getResources().getInteger(R.integer.media_overview_cols)); this.recyclerView.setAdapter(new MediaGalleryAdapter(getContext(), - GlideApp.with(this), + Glide.with(this), new BucketedThreadMedia(getContext()), locale, this)); diff --git a/app/src/main/java/org/thoughtcrime/securesms/MediaPreviewActivity.java b/app/src/main/java/org/thoughtcrime/securesms/MediaPreviewActivity.java index 2e67becbfd1..b609b54fc69 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/MediaPreviewActivity.java +++ b/app/src/main/java/org/thoughtcrime/securesms/MediaPreviewActivity.java @@ -72,8 +72,8 @@ import org.thoughtcrime.securesms.database.model.MmsMessageRecord; 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 com.bumptech.glide.Glide; +import com.bumptech.glide.RequestManager; 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) { @@ -518,7 +518,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 +588,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 +596,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 +663,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/calls/WebRtcCallActivity.kt b/app/src/main/java/org/thoughtcrime/securesms/calls/WebRtcCallActivity.kt index ea4108ba9aa..2e3fdbf4d7f 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/calls/WebRtcCallActivity.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/calls/WebRtcCallActivity.kt @@ -22,6 +22,7 @@ import androidx.core.content.ContextCompat import androidx.core.view.isVisible import androidx.lifecycle.lifecycleScope import androidx.localbroadcastmanager.content.LocalBroadcastManager +import com.bumptech.glide.Glide import com.bumptech.glide.load.engine.DiskCacheStrategy import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.Job @@ -38,7 +39,6 @@ import org.session.libsession.utilities.truncateIdForDisplay import org.session.libsignal.utilities.Log import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity import org.thoughtcrime.securesms.dependencies.DatabaseComponent -import org.thoughtcrime.securesms.mms.GlideApp import org.thoughtcrime.securesms.permissions.Permissions import org.thoughtcrime.securesms.service.WebRtcCallService import org.thoughtcrime.securesms.util.AvatarPlaceholderGenerator @@ -70,7 +70,7 @@ class WebRtcCallActivity : PassphraseRequiredActionBarActivity() { } private val viewModel by viewModels() - private val glide by lazy { GlideApp.with(this) } + private val glide by lazy { Glide.with(this) } private lateinit var binding: ActivityWebrtcBinding private var uiJob: Job? = null private var wantsToAnswer = false diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/AvatarImageView.java b/app/src/main/java/org/thoughtcrime/securesms/components/AvatarImageView.java index 573e8d2d2b6..0139e9932cf 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/AvatarImageView.java +++ b/app/src/main/java/org/thoughtcrime/securesms/components/AvatarImageView.java @@ -25,8 +25,8 @@ import org.session.libsession.utilities.ThemeUtil; import org.session.libsession.utilities.recipients.Recipient; import org.session.libsession.utilities.recipients.RecipientExporter; -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.AvatarPlaceholderGenerator; import java.util.Objects; @@ -117,10 +117,10 @@ public void update(String hexEncodedPublicKey) { } private void updateAvatar(Recipient recipient) { - setAvatar(GlideApp.with(getContext()), recipient, false); + setAvatar(Glide.with(getContext()), recipient, false); } - public void setAvatar(@NonNull GlideRequests requestManager, @Nullable Recipient recipient, boolean quickContactEnabled) { + public void setAvatar(@NonNull RequestManager requestManager, @Nullable Recipient recipient, boolean quickContactEnabled) { if (recipient != null) { if (recipient.isLocalNumber()) { setImageDrawable(new ResourceContactPhoto(R.drawable.ic_note_to_self).asDrawable(getContext(), recipient.getColor().toAvatarColor(getContext()), inverted)); @@ -156,7 +156,7 @@ public void setAvatar(@NonNull GlideRequests requestManager, @Nullable Recipient } } - public void clear(@NonNull GlideRequests glideRequests) { + public void clear(@NonNull RequestManager glideRequests) { glideRequests.clear(this); } 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..bb973c23ff2 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/MediaView.java +++ b/app/src/main/java/org/thoughtcrime/securesms/components/MediaView.java @@ -13,7 +13,7 @@ import android.widget.FrameLayout; import network.loki.messenger.R; -import org.thoughtcrime.securesms.mms.GlideRequests; +import com.bumptech.glide.RequestManager; import org.thoughtcrime.securesms.mms.VideoSlide; import org.thoughtcrime.securesms.video.VideoPlayer; @@ -54,7 +54,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 6d59bbfc92f..571c778d4ad 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..e1d3236a967 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/RecentPhotoViewRail.java +++ b/app/src/main/java/org/thoughtcrime/securesms/components/RecentPhotoViewRail.java @@ -28,7 +28,7 @@ import network.loki.messenger.R; import org.thoughtcrime.securesms.database.CursorRecyclerViewAdapter; import org.thoughtcrime.securesms.database.loaders.RecentPhotosLoader; -import org.thoughtcrime.securesms.mms.GlideApp; +import com.bumptech.glide.Glide; import org.session.libsession.utilities.ViewUtil; @@ -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) 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/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/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/v2/ConversationActivityV2.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityV2.kt index 62ea2e52d48..f2d91aa19b2 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 @@ -156,7 +156,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 @@ -349,7 +349,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/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/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/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/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 ee82a708c45..a971a5a1a63 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 @@ -91,7 +91,7 @@ class HomeActivity : PassphraseRequiredActionBarActivity(), } 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 @@ -151,7 +151,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..47b62521654 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) { 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/MediaSendFragment.java b/app/src/main/java/org/thoughtcrime/securesms/mediasend/MediaSendFragment.java index cba1529a515..84722c24c96 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/mediasend/MediaSendFragment.java +++ b/app/src/main/java/org/thoughtcrime/securesms/mediasend/MediaSendFragment.java @@ -41,7 +41,7 @@ import org.thoughtcrime.securesms.imageeditor.model.EditorModel; import org.session.libsignal.utilities.Log; import org.thoughtcrime.securesms.mediapreview.MediaRailAdapter; -import org.thoughtcrime.securesms.mms.GlideApp; +import com.bumptech.glide.Glide; import org.thoughtcrime.securesms.providers.BlobProvider; import org.session.libsession.utilities.recipients.Recipient; import org.thoughtcrime.securesms.scribbles.ImageEditorFragment; @@ -187,7 +187,7 @@ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceStat fragmentPager.addOnPageChangeListener(pageChangeListener); fragmentPager.post(() -> 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 3040bb774fd..36e51e35be8 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/messagerequests/MessageRequestsAdapter.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/messagerequests/MessageRequestsAdapter.kt @@ -14,7 +14,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, @@ -22,7 +22,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/notifications/SingleRecipientNotificationBuilder.java b/app/src/main/java/org/thoughtcrime/securesms/notifications/SingleRecipientNotificationBuilder.java index d5aeba6022f..6a0a8e0956e 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/notifications/SingleRecipientNotificationBuilder.java +++ b/app/src/main/java/org/thoughtcrime/securesms/notifications/SingleRecipientNotificationBuilder.java @@ -38,7 +38,7 @@ 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 com.bumptech.glide.Glide; import org.thoughtcrime.securesms.mms.Slide; import org.thoughtcrime.securesms.mms.SlideDeck; import org.thoughtcrime.securesms.util.AvatarPlaceholderGenerator; @@ -89,7 +89,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) @@ -291,7 +291,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/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/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/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/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/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/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/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/build.gradle b/build.gradle index 70d1f63fa4d..505f874e652 100644 --- a/build.gradle +++ b/build.gradle @@ -17,13 +17,15 @@ buildscript { classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlinVersion" classpath "com.google.gms:google-services:$googleServicesVersion" classpath "com.squareup:javapoet:1.13.0" - classpath "com.google.dagger:hilt-android-gradle-plugin:$daggerVersion" if (project.hasProperty('huawei')) classpath 'com.huawei.agconnect:agcp:1.9.1.300' } } -plugins{ - id("com.google.dagger.hilt.android") version "2.44" apply false +// List plugins AND their versions here, but don't apply. This allows you to use the plugin +// in your module without specifying the version. +plugins { + id 'com.google.devtools.ksp' version "$kotlinVersion-1.0.20" apply false + id 'com.google.dagger.hilt.android' version "$daggerHiltVersion" apply false } allprojects { diff --git a/gradle.properties b/gradle.properties index 61546525838..b40f4b59ca2 100644 --- a/gradle.properties +++ b/gradle.properties @@ -14,19 +14,19 @@ android.enableJetifier=true org.gradle.jvmargs=-Xmx2048M -Dkotlin.daemon.jvm.options\="-Xmx2048M" -org.gradle.unsafe.configuration-cache=true gradlePluginVersion=7.3.1 googleServicesVersion=4.3.12 -kotlinVersion=1.8.21 +kotlinVersion=1.9.24 android.useAndroidX=true appcompatVersion=1.6.1 coreVersion=1.13.1 composeVersion=1.6.4 coroutinesVersion=1.6.4 curve25519Version=0.6.0 -daggerVersion=2.46.1 -glideVersion=4.11.0 +jetpackHiltVersion=1.2.0 +daggerHiltVersion=2.51.1 +glideVersion=4.16.0 jacksonDatabindVersion=2.9.8 junitVersion=4.13.2 kotlinxJsonVersion=1.3.3 diff --git a/libsession/build.gradle b/libsession/build.gradle index 55146823ecd..2856e88a2ca 100644 --- a/libsession/build.gradle +++ b/libsession/build.gradle @@ -2,6 +2,8 @@ plugins { id 'com.android.library' id 'kotlin-android' id 'kotlinx-serialization' + id 'com.google.devtools.ksp' + id 'com.google.dagger.hilt.android' } android { @@ -21,6 +23,11 @@ dependencies { implementation project(":libsignal") implementation project(":libsession-util") implementation project(":liblazysodium") + + implementation("com.google.dagger:hilt-android:$daggerHiltVersion") + ksp("com.google.dagger:hilt-compiler:$daggerHiltVersion") + ksp("androidx.hilt:hilt-compiler:$jetpackHiltVersion") + implementation "net.java.dev.jna:jna:5.12.1@aar" implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlinVersion" implementation "androidx.core:core-ktx:$coreVersion" @@ -28,7 +35,6 @@ dependencies { implementation "androidx.preference:preference-ktx:$preferenceVersion" implementation "com.google.android.material:material:$materialVersion" implementation "com.google.protobuf:protobuf-java:$protobufVersion" - implementation "com.google.dagger:hilt-android:$daggerVersion" androidTestImplementation 'androidx.test.ext:junit:1.1.5' androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1' implementation "com.github.bumptech.glide:glide:$glideVersion" From 0a08eb62d4a27e0381f311e5916a90efce6ee549 Mon Sep 17 00:00:00 2001 From: bemusementpark Date: Wed, 24 Jul 2024 22:18:43 +0930 Subject: [PATCH 03/50] Fix leak Response body --- .../org/session/libsignal/utilities/HTTP.kt | 37 +++++++++---------- 1 file changed, 18 insertions(+), 19 deletions(-) diff --git a/libsignal/src/main/java/org/session/libsignal/utilities/HTTP.kt b/libsignal/src/main/java/org/session/libsignal/utilities/HTTP.kt index 0c1b2f83177..fd4a3f37028 100644 --- a/libsignal/src/main/java/org/session/libsignal/utilities/HTTP.kt +++ b/libsignal/src/main/java/org/session/libsignal/utilities/HTTP.kt @@ -115,18 +115,26 @@ object HTTP { } Verb.DELETE -> request.delete() } - lateinit var response: Response - try { - val connection: OkHttpClient = if (timeout != HTTP.timeout) { // Custom timeout - if (useSeedNodeConnection) { - throw IllegalStateException("Setting a custom timeout is only allowed for requests to snodes.") + return try { + when { + // Custom timeout + timeout != HTTP.timeout -> { + if (useSeedNodeConnection) { + throw IllegalStateException("Setting a custom timeout is only allowed for requests to snodes.") + } + getDefaultConnection(timeout) + } + useSeedNodeConnection -> seedNodeConnection + else -> defaultConnection + }.newCall(request.build()).execute().use { response -> + when (val statusCode = response.code) { + 200 -> response.body!!.bytes() + else -> { + Log.d("Loki", "${verb.rawValue} request to $url failed with status code: $statusCode.") + throw HTTPRequestFailedException(statusCode, null) + } } - getDefaultConnection(timeout) - } else { - if (useSeedNodeConnection) seedNodeConnection else defaultConnection } - - response = connection.newCall(request.build()).execute() } catch (exception: Exception) { Log.d("Loki", "${verb.rawValue} request to $url failed due to error: ${exception.localizedMessage}.") @@ -135,14 +143,5 @@ object HTTP { // Override the actual error so that we can correctly catch failed requests in OnionRequestAPI throw HTTPRequestFailedException(0, null, "HTTP request failed due to: ${exception.message}") } - return when (val statusCode = response.code) { - 200 -> { - response.body!!.bytes() - } - else -> { - Log.d("Loki", "${verb.rawValue} request to $url failed with status code: $statusCode.") - throw HTTPRequestFailedException(statusCode, null) - } - } } } From ac4f2525ab2697813a38401245eb0c2940be3414 Mon Sep 17 00:00:00 2001 From: bemusementpark Date: Thu, 25 Jul 2024 11:37:06 +0930 Subject: [PATCH 04/50] Fix longpress message fails --- .../securesms/conversation/v2/ConversationActivityV2.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 f2d91aa19b2..98f300dc661 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 @@ -37,7 +37,6 @@ import androidx.activity.result.contract.ActivityResultContracts import androidx.activity.viewModels import androidx.core.text.set import androidx.core.text.toSpannable -import androidx.core.view.drawToBitmap import androidx.core.view.isGone import androidx.core.view.isVisible import androidx.fragment.app.DialogFragment @@ -173,6 +172,7 @@ import org.thoughtcrime.securesms.util.DateUtils import org.thoughtcrime.securesms.util.MediaUtil import org.thoughtcrime.securesms.util.NetworkUtils import org.thoughtcrime.securesms.util.SaveAttachmentTask +import org.thoughtcrime.securesms.util.drawToBitmap import org.thoughtcrime.securesms.util.isScrolledToBottom import org.thoughtcrime.securesms.util.isScrolledToWithin30dpOfBottom import org.thoughtcrime.securesms.util.push From dec02cef5a4321629ebcef9332c477bc24cf134c Mon Sep 17 00:00:00 2001 From: Fanchao Liu <273191+simophin@users.noreply.github.com> Date: Mon, 29 Jul 2024 17:42:26 +1000 Subject: [PATCH 05/50] Fix double closing on memory file (#1579) Co-authored-by: fanchao --- .../java/org/thoughtcrime/securesms/util/MemoryFileUtil.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/util/MemoryFileUtil.java b/app/src/main/java/org/thoughtcrime/securesms/util/MemoryFileUtil.java index 4ee43e1e9ce..f193827efda 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/util/MemoryFileUtil.java +++ b/app/src/main/java/org/thoughtcrime/securesms/util/MemoryFileUtil.java @@ -22,7 +22,7 @@ public static ParcelFileDescriptor getParcelFileDescriptor(MemoryFile file) thro int fd = field.getInt(fileDescriptor); - return ParcelFileDescriptor.adoptFd(fd); + return ParcelFileDescriptor.fromFd(fd); } catch (IllegalAccessException e) { throw new IOException(e); } catch (InvocationTargetException e) { From 60c111d30ac12dc2a01971e91922932ff78d812e Mon Sep 17 00:00:00 2001 From: ThomasSession Date: Thu, 1 Aug 2024 09:01:56 +1000 Subject: [PATCH 06/50] Removing the snodes version patch --- .../thoughtcrime/securesms/ApplicationContext.java | 10 ---------- .../org/session/libsession/snode/OnionRequestAPI.kt | 11 ----------- .../libsession/utilities/TextSecurePreferences.kt | 12 ------------ 3 files changed, 33 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/ApplicationContext.java b/app/src/main/java/org/thoughtcrime/securesms/ApplicationContext.java index d306db50b59..1870cf62605 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/ApplicationContext.java +++ b/app/src/main/java/org/thoughtcrime/securesms/ApplicationContext.java @@ -215,16 +215,6 @@ public void onCreate() { MessagingModuleConfiguration.configure(this); super.onCreate(); - // we need to clear the snode and onionrequest databases once on first launch - // in order to apply a patch that adds a version number to the Snode objects. - if(!TextSecurePreferences.hasAppliedPatchSnodeVersion(this)) { - ThreadUtils.queue(() -> { - lokiAPIDatabase.clearSnodePool(); - lokiAPIDatabase.clearOnionRequestPaths(); - TextSecurePreferences.setHasAppliedPatchSnodeVersion(this, true); - }); - } - messagingModuleConfiguration = new MessagingModuleConfiguration( this, storage, diff --git a/libsession/src/main/java/org/session/libsession/snode/OnionRequestAPI.kt b/libsession/src/main/java/org/session/libsession/snode/OnionRequestAPI.kt index 3dcfc5b958a..ead454eb446 100644 --- a/libsession/src/main/java/org/session/libsession/snode/OnionRequestAPI.kt +++ b/libsession/src/main/java/org/session/libsession/snode/OnionRequestAPI.kt @@ -193,17 +193,6 @@ object OnionRequestAPI { val result = listOf( guardSnode ) + (0 until (pathSize - 1)).mapIndexed() { index, _ -> var pathSnode = unusedSnodes.getRandomElement() - // For the last node: We need to make sure the version is >= 2.8.0 - // to help with an issue that will disappear once the nodes are all updated - if(index == pathSize - 2) { - val suitableSnodes = unusedSnodes.filter { Util.compareVersions(it.version, "2.8.0") >= 0 } - pathSnode = if (suitableSnodes.isNotEmpty()) { - suitableSnodes.random() - } else { - throw InsufficientSnodesException() - } - } - // remove the snode from the unused list and return it unusedSnodes = unusedSnodes.minus(pathSnode) pathSnode diff --git a/libsession/src/main/java/org/session/libsession/utilities/TextSecurePreferences.kt b/libsession/src/main/java/org/session/libsession/utilities/TextSecurePreferences.kt index 4d35a89de2c..12fdd4ceaf6 100644 --- a/libsession/src/main/java/org/session/libsession/utilities/TextSecurePreferences.kt +++ b/libsession/src/main/java/org/session/libsession/utilities/TextSecurePreferences.kt @@ -296,8 +296,6 @@ interface TextSecurePreferences { const val ALLOW_MESSAGE_REQUESTS = "libsession.ALLOW_MESSAGE_REQUESTS" - const val PATCH_SNODE_VERSION_2024_07_23 = "libsession.patch_snode_version_2024_07_23" - @JvmStatic fun getLastConfigurationSyncTime(context: Context): Long { return getLongPreference(context, LAST_CONFIGURATION_SYNC_TIME, 0) @@ -982,16 +980,6 @@ interface TextSecurePreferences { fun clearAll(context: Context) { getDefaultSharedPreferences(context).edit().clear().commit() } - - @JvmStatic - fun hasAppliedPatchSnodeVersion(context: Context): Boolean { - return getBooleanPreference(context, PATCH_SNODE_VERSION_2024_07_23, false) - } - - @JvmStatic - fun setHasAppliedPatchSnodeVersion(context: Context, applied: Boolean) { - setBooleanPreference(context, PATCH_SNODE_VERSION_2024_07_23, applied) - } } } From 137a26c3bf1e8ae074410b2fbd823df6627f8dca Mon Sep 17 00:00:00 2001 From: ThomasSession Date: Thu, 1 Aug 2024 11:25:35 +1000 Subject: [PATCH 07/50] KeepAliveService doesn't seem to be used anywhere --- app/src/main/AndroidManifest.xml | 4 +--- .../jobmanager/KeepAliveService.java | 24 ------------------- app/src/main/res/values-v26/values.xml | 1 - app/src/main/res/values/values.xml | 1 - libsession/src/main/res/values/values.xml | 1 - 5 files changed, 1 insertion(+), 30 deletions(-) delete mode 100644 app/src/main/java/org/thoughtcrime/securesms/jobmanager/KeepAliveService.java diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 7e04ad44ca5..2d06893ca00 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -440,9 +440,7 @@ - + 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/res/values-v26/values.xml b/app/src/main/res/values-v26/values.xml index dc6b4b79413..c9cf9e837c0 100644 --- a/app/src/main/res/values-v26/values.xml +++ b/app/src/main/res/values-v26/values.xml @@ -1,5 +1,4 @@ - false true diff --git a/app/src/main/res/values/values.xml b/app/src/main/res/values/values.xml index c87ba9baddd..384c8df05b1 100644 --- a/app/src/main/res/values/values.xml +++ b/app/src/main/res/values/values.xml @@ -1,6 +1,5 @@ - true false diff --git a/libsession/src/main/res/values/values.xml b/libsession/src/main/res/values/values.xml index f493249a83e..899d1896872 100644 --- a/libsession/src/main/res/values/values.xml +++ b/libsession/src/main/res/values/values.xml @@ -1,6 +1,5 @@ - true false true From 33f23e5fa1b3f4ac5b74082334dbef39222431fe Mon Sep 17 00:00:00 2001 From: ThomasSession Date: Thu, 1 Aug 2024 11:27:43 +1000 Subject: [PATCH 08/50] Another unused or referenced service --- app/src/main/AndroidManifest.xml | 1 - .../service/GenericForegroundService.java | 128 ------------------ 2 files changed, 129 deletions(-) delete mode 100644 app/src/main/java/org/thoughtcrime/securesms/service/GenericForegroundService.java diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 2d06893ca00..e1a2e79694e 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -328,7 +328,6 @@ - Date: Thu, 1 Aug 2024 16:50:20 +1000 Subject: [PATCH 09/50] Cleaning up foreground service calls --- .../securesms/service/KeyCachingService.java | 18 +++++++++++++----- .../securesms/service/WebRtcCallService.kt | 13 +++++++++++-- 2 files changed, 24 insertions(+), 7 deletions(-) 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..47b5c52f164 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); - 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()); + + int type = 0; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) { + 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..6ee33721e8d 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,15 @@ 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 >= Build.VERSION_CODES.R) { + 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) From d8bcb83de5115d065ce7de78370ab962884f7942 Mon Sep 17 00:00:00 2001 From: bemusementpark Date: Sat, 27 Jul 2024 16:12:03 +0930 Subject: [PATCH 10/50] Fix app exit after Lock Screen --- app/src/main/AndroidManifest.xml | 2 +- .../main/java/org/thoughtcrime/securesms/home/HomeActivity.kt | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index e1a2e79694e..91d9572f90f 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -125,7 +125,7 @@ Date: Thu, 1 Aug 2024 19:36:38 +1000 Subject: [PATCH 11/50] Fix crashes on conversation screen (#1590) * Fixed onion request crashes * Use kotlin.Result instead --------- Co-authored-by: fanchao --- .../repository/ConversationRepository.kt | 63 +++++++++---------- .../securesms/repository/ResultOf.kt | 43 ------------- .../v2/ConversationViewModelTest.kt | 19 +++--- 3 files changed, 38 insertions(+), 87 deletions(-) delete mode 100644 app/src/main/java/org/thoughtcrime/securesms/repository/ResultOf.kt 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/test/java/org/thoughtcrime/securesms/conversation/v2/ConversationViewModelTest.kt b/app/src/test/java/org/thoughtcrime/securesms/conversation/v2/ConversationViewModelTest.kt index 7f4db828e43..1bd6a63c7f3 100644 --- a/app/src/test/java/org/thoughtcrime/securesms/conversation/v2/ConversationViewModelTest.kt +++ b/app/src/test/java/org/thoughtcrime/securesms/conversation/v2/ConversationViewModelTest.kt @@ -3,14 +3,12 @@ package org.thoughtcrime.securesms.conversation.v2 import com.goterl.lazysodium.utils.KeyPair import kotlinx.coroutines.flow.emptyFlow import kotlinx.coroutines.flow.first -import kotlinx.coroutines.runBlocking import org.hamcrest.CoreMatchers.endsWith import org.hamcrest.CoreMatchers.equalTo import org.hamcrest.CoreMatchers.notNullValue import org.hamcrest.CoreMatchers.nullValue import org.hamcrest.MatcherAssert.assertThat import org.junit.Before -import org.junit.BeforeClass import org.junit.Test import org.mockito.Mockito import org.mockito.Mockito.anyLong @@ -20,14 +18,11 @@ import org.mockito.kotlin.any import org.mockito.kotlin.mock import org.mockito.kotlin.whenever import org.session.libsession.utilities.recipients.Recipient -import org.session.libsignal.utilities.Log import org.thoughtcrime.securesms.BaseViewModelTest -import org.thoughtcrime.securesms.NoOpLogger import org.thoughtcrime.securesms.database.MmsDatabase import org.thoughtcrime.securesms.database.Storage import org.thoughtcrime.securesms.database.model.MessageRecord import org.thoughtcrime.securesms.repository.ConversationRepository -import org.thoughtcrime.securesms.repository.ResultOf class ConversationViewModelTest: BaseViewModelTest() { @@ -107,7 +102,7 @@ class ConversationViewModelTest: BaseViewModelTest() { val message = mock() val error = Throwable() whenever(repository.deleteForEveryone(anyLong(), any(), any())) - .thenReturn(ResultOf.Failure(error)) + .thenReturn(Result.failure(error)) viewModel.deleteForEveryone(message) @@ -120,7 +115,7 @@ class ConversationViewModelTest: BaseViewModelTest() { val message = mock() val error = Throwable() whenever(repository.deleteMessageWithoutUnsendRequest(anyLong(), anySet())) - .thenReturn(ResultOf.Failure(error)) + .thenReturn(Result.failure(error)) viewModel.deleteMessagesWithoutUnsendRequest(setOf(message)) @@ -130,7 +125,7 @@ class ConversationViewModelTest: BaseViewModelTest() { @Test fun `should emit error message on ban user failure`() = runBlockingTest { val error = Throwable() - whenever(repository.banUser(anyLong(), any())).thenReturn(ResultOf.Failure(error)) + whenever(repository.banUser(anyLong(), any())).thenReturn(Result.failure(error)) viewModel.banUser(recipient) @@ -139,7 +134,7 @@ class ConversationViewModelTest: BaseViewModelTest() { @Test fun `should emit a message on ban user success`() = runBlockingTest { - whenever(repository.banUser(anyLong(), any())).thenReturn(ResultOf.Success(Unit)) + whenever(repository.banUser(anyLong(), any())).thenReturn(Result.success(Unit)) viewModel.banUser(recipient) @@ -152,7 +147,7 @@ class ConversationViewModelTest: BaseViewModelTest() { @Test fun `should emit error message on ban user and delete all failure`() = runBlockingTest { val error = Throwable() - whenever(repository.banAndDeleteAll(anyLong(), any())).thenReturn(ResultOf.Failure(error)) + whenever(repository.banAndDeleteAll(anyLong(), any())).thenReturn(Result.failure(error)) viewModel.banAndDeleteAll(messageRecord) @@ -161,7 +156,7 @@ class ConversationViewModelTest: BaseViewModelTest() { @Test fun `should emit a message on ban user and delete all success`() = runBlockingTest { - whenever(repository.banAndDeleteAll(anyLong(), any())).thenReturn(ResultOf.Success(Unit)) + whenever(repository.banAndDeleteAll(anyLong(), any())).thenReturn(Result.success(Unit)) viewModel.banAndDeleteAll(messageRecord) @@ -188,7 +183,7 @@ class ConversationViewModelTest: BaseViewModelTest() { @Test fun `should remove shown message`() = runBlockingTest { // Given that a message is generated - whenever(repository.banUser(anyLong(), any())).thenReturn(ResultOf.Success(Unit)) + whenever(repository.banUser(anyLong(), any())).thenReturn(Result.success(Unit)) viewModel.banUser(recipient) assertThat(viewModel.uiState.value.uiMessages.size, equalTo(1)) // When the message is shown From fb7c24a26d38876cc3b25161836907e5bfee3850 Mon Sep 17 00:00:00 2001 From: ThomasSession Date: Fri, 2 Aug 2024 08:43:48 +1000 Subject: [PATCH 12/50] Update app/src/main/java/org/thoughtcrime/securesms/service/KeyCachingService.java Co-authored-by: Andrew --- .../org/thoughtcrime/securesms/service/KeyCachingService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 47b5c52f164..14b2c43af48 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/service/KeyCachingService.java +++ b/app/src/main/java/org/thoughtcrime/securesms/service/KeyCachingService.java @@ -255,7 +255,7 @@ private void foregroundService() { stopForeground(true); int type = 0; - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) { + if (Build.VERSION.SDK_INT >= 34) { type = ServiceInfo.FOREGROUND_SERVICE_TYPE_SPECIAL_USE; } From 7dcfba49324b18fe7635548343b6b65611b1f087 Mon Sep 17 00:00:00 2001 From: ThomasSession Date: Fri, 2 Aug 2024 08:44:30 +1000 Subject: [PATCH 13/50] Update app/src/main/java/org/thoughtcrime/securesms/service/KeyCachingService.java Co-authored-by: Andrew --- .../securesms/service/KeyCachingService.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) 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 14b2c43af48..cf250665ef6 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/service/KeyCachingService.java +++ b/app/src/main/java/org/thoughtcrime/securesms/service/KeyCachingService.java @@ -260,11 +260,11 @@ private void foregroundService() { } ServiceCompat.startForeground( - this, - SERVICE_RUNNING_ID, - builder.build(), - type - ); + this, + SERVICE_RUNNING_ID, + builder.build(), + type + ); } private PendingIntent buildLockIntent() { From e71f650dc0c9dd67d7b05278704bfcaae8b6a397 Mon Sep 17 00:00:00 2001 From: ThomasSession Date: Fri, 2 Aug 2024 08:44:39 +1000 Subject: [PATCH 14/50] Update app/src/main/java/org/thoughtcrime/securesms/service/WebRtcCallService.kt Co-authored-by: Andrew --- .../org/thoughtcrime/securesms/service/WebRtcCallService.kt | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) 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 6ee33721e8d..78f1a554708 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/service/WebRtcCallService.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/service/WebRtcCallService.kt @@ -730,11 +730,7 @@ class WebRtcCallService : LifecycleService(), CallManager.WebRtcListener { this, CallNotificationBuilder.WEBRTC_NOTIFICATION, CallNotificationBuilder.getCallInProgressNotification(this, type, recipient), - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { - ServiceInfo.FOREGROUND_SERVICE_TYPE_MICROPHONE - } else { - 0 - } + 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) From b93ec3be04f3370ac5b7d285295a0e142038fe82 Mon Sep 17 00:00:00 2001 From: bemusementpark Date: Fri, 2 Aug 2024 12:22:25 +0930 Subject: [PATCH 15/50] Optimise Snode and Snode.Version --- .../securesms/database/LokiAPIDatabase.kt | 35 ++---------- .../org/session/libsession/snode/SnodeAPI.kt | 34 +++++------- .../org/session/libsession/utilities/Util.kt | 33 ++---------- .../org/session/libsignal/utilities/Snode.kt | 54 ++++++++++++++++++- 4 files changed, 73 insertions(+), 83 deletions(-) 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/libsession/src/main/java/org/session/libsession/snode/SnodeAPI.kt b/libsession/src/main/java/org/session/libsession/snode/SnodeAPI.kt index 034ef0e7d23..9ceefe8386c 100644 --- a/libsession/src/main/java/org/session/libsession/snode/SnodeAPI.kt +++ b/libsession/src/main/java/org/session/libsession/snode/SnodeAPI.kt @@ -18,6 +18,7 @@ import nl.komponents.kovenant.task import org.session.libsession.messaging.MessagingModuleConfiguration import org.session.libsession.messaging.utilities.MessageWrapper import org.session.libsession.messaging.utilities.SodiumUtilities.sodium +import org.session.libsession.utilities.toByteArray import org.session.libsignal.crypto.getRandomElement import org.session.libsignal.database.LokiAPIDatabaseProtocol import org.session.libsignal.protos.SignalServiceProtos @@ -94,8 +95,6 @@ object SnodeAPI { const val KEY_ED25519 = "pubkey_ed25519" const val KEY_VERSION = "storage_server_version" - const val EMPTY_VERSION = "0.0.0" - // Error sealed class Error(val description: String) : Exception(description) { object Generic : Error("An error occurred.") @@ -191,7 +190,7 @@ object SnodeAPI { val x25519Key = rawSnodeAsJSON?.get(KEY_X25519) as? String val version = (rawSnodeAsJSON?.get(KEY_VERSION) as? ArrayList<*>) ?.filterIsInstance() // get the array as Integers - ?.joinToString(separator = ".") // turn it int a version string + ?.let(Snode::Version) // turn it int a version if (address != null && port != null && ed25519Key != null && x25519Key != null && address != "0.0.0.0" && version != null) { @@ -696,7 +695,7 @@ object SnodeAPI { getSingleTargetSnode(publicKey).bind { snode -> retryIfNeeded(maxRetryCount) { val signature = ByteArray(Sign.BYTES) - val verificationData = (Snode.Method.DeleteMessage.rawValue + serverHashes.fold("") { a, v -> a + v }).toByteArray() + val verificationData = sequenceOf(Snode.Method.DeleteMessage.rawValue).plus(serverHashes).toByteArray() sodium.cryptoSignDetached(signature, verificationData, verificationData.size.toLong(), userED25519KeyPair.secretKey.asBytes) val deleteMessageParams = mapOf( "pubkey" to userPublicKey, @@ -719,7 +718,7 @@ object SnodeAPI { val signature = json["signature"] as String val snodePublicKey = Key.fromHexString(hexSnodePublicKey) // The signature looks like ( PUBKEY_HEX || RMSG[0] || ... || RMSG[N] || DMSG[0] || ... || DMSG[M] ) - val message = (userPublicKey + serverHashes.fold("") { a, v -> a + v } + hashes.fold("") { a, v -> a + v }).toByteArray() + val message = sequenceOf(userPublicKey).plus(serverHashes).plus(hashes).toByteArray() sodium.cryptoSignVerifyDetached(Base64.decode(signature), message, message.size, snodePublicKey.asBytes) } } @@ -733,11 +732,10 @@ object SnodeAPI { } // Parsing - private fun parseSnodes(rawResponse: Any): List { - val json = rawResponse as? Map<*, *> - val rawSnodes = json?.get("snodes") as? List<*> - if (rawSnodes != null) { - return rawSnodes.mapNotNull { rawSnode -> + private fun parseSnodes(rawResponse: Any): List = + (rawResponse as? Map<*, *>) + ?.run { get("snodes") as? List<*> } + ?.mapNotNull { rawSnode -> val rawSnodeAsJSON = rawSnode as? Map<*, *> val address = rawSnodeAsJSON?.get("ip") as? String val portAsString = rawSnodeAsJSON?.get("port") as? String @@ -746,17 +744,12 @@ object SnodeAPI { val x25519Key = rawSnodeAsJSON?.get(KEY_X25519) as? String if (address != null && port != null && ed25519Key != null && x25519Key != null && address != "0.0.0.0") { - Snode("https://$address", port, Snode.KeySet(ed25519Key, x25519Key), EMPTY_VERSION) + Snode("https://$address", port, Snode.KeySet(ed25519Key, x25519Key), Snode.Version.ZERO) } else { Log.d("Loki", "Failed to parse snode from: ${rawSnode?.prettifiedDescription()}.") null } - } - } else { - Log.d("Loki", "Failed to parse snodes from: ${rawResponse.prettifiedDescription()}.") - return listOf() - } - } + } ?: listOf().also { Log.d("Loki", "Failed to parse snodes from: ${rawResponse.prettifiedDescription()}.") } fun deleteAllMessages(): Promise, Exception> { return retryIfNeeded(maxRetryCount) { @@ -796,8 +789,7 @@ object SnodeAPI { getSingleTargetSnode(userPublicKey).bind { snode -> retryIfNeeded(maxRetryCount) { // "expire" || expiry || messages[0] || ... || messages[N] - val verificationData = - (Snode.Method.Expire.rawValue + updatedExpiryMsWithNetworkOffset + serverHashes.fold("") { a, v -> a + v }).toByteArray() + val verificationData = sequenceOf(Snode.Method.Expire.rawValue, "$updatedExpiryMsWithNetworkOffset").plus(serverHashes).toByteArray() val signature = ByteArray(Sign.BYTES) sodium.cryptoSignDetached( signature, @@ -828,7 +820,7 @@ object SnodeAPI { val signature = json["signature"] as String val snodePublicKey = Key.fromHexString(hexSnodePublicKey) // The signature looks like ( PUBKEY_HEX || RMSG[0] || ... || RMSG[N] || DMSG[0] || ... || DMSG[M] ) - val message = (userPublicKey + serverHashes.fold("") { a, v -> a + v } + hashes.fold("") { a, v -> a + v }).toByteArray() + val message = sequenceOf(userPublicKey).plus(serverHashes).plus(hashes).toByteArray() if (sodium.cryptoSignVerifyDetached(Base64.decode(signature), message, message.size, snodePublicKey.asBytes)) { hashes to expiryApplied } else listOf() to 0L @@ -922,7 +914,7 @@ object SnodeAPI { val signature = json["signature"] as String val snodePublicKey = Key.fromHexString(hexSnodePublicKey) // The signature looks like ( PUBKEY_HEX || TIMESTAMP || DELETEDHASH[0] || ... || DELETEDHASH[N] ) - val message = (userPublicKey + timestamp.toString() + hashes.joinToString(separator = "")).toByteArray() + val message = sequenceOf(userPublicKey, "$timestamp").plus(hashes).toByteArray() sodium.cryptoSignVerifyDetached(Base64.decode(signature), message, message.size, snodePublicKey.asBytes) } } diff --git a/libsession/src/main/java/org/session/libsession/utilities/Util.kt b/libsession/src/main/java/org/session/libsession/utilities/Util.kt index 929f53e305e..d47754b7ed6 100644 --- a/libsession/src/main/java/org/session/libsession/utilities/Util.kt +++ b/libsession/src/main/java/org/session/libsession/utilities/Util.kt @@ -366,34 +366,6 @@ object Util { val digitGroups = (Math.log10(sizeBytes.toDouble()) / Math.log10(1024.0)).toInt() return DecimalFormat("#,##0.#").format(sizeBytes / Math.pow(1024.0, digitGroups.toDouble())) + " " + units[digitGroups] } - - /** - * Compares two version strings (for example "1.8.0") - * - * @param version1 the first version string to compare. - * @param version2 the second version string to compare. - * @return an integer indicating the result of the comparison: - * - 0 if the versions are equal - * - a positive number if version1 is greater than version2 - * - a negative number if version1 is less than version2 - */ - @JvmStatic - fun compareVersions(version1: String, version2: String): Int { - val parts1 = version1.split(".").map { it.toIntOrNull() ?: 0 } - val parts2 = version2.split(".").map { it.toIntOrNull() ?: 0 } - - val maxLength = maxOf(parts1.size, parts2.size) - val paddedParts1 = parts1 + List(maxLength - parts1.size) { 0 } - val paddedParts2 = parts2 + List(maxLength - parts2.size) { 0 } - - for (i in 0 until maxLength) { - val compare = paddedParts1[i].compareTo(paddedParts2[i]) - if (compare != 0) { - return compare - } - } - return 0 - } } fun T.runIf(condition: Boolean, block: T.() -> R): R where T: R = if (condition) block() else this @@ -440,3 +412,8 @@ fun Iterable.associateByNotNull( inline fun Iterable.groupByNotNull(keySelector: (E) -> K?): Map> = LinkedHashMap>().also { forEach { e -> keySelector(e)?.let { k -> it.getOrPut(k) { mutableListOf() } += e } } } + +fun Sequence.toByteArray(): ByteArray = ByteArrayOutputStream().use { output -> + forEach { it.byteInputStream().use { input -> input.copyTo(output) } } + output.toByteArray() +} diff --git a/libsignal/src/main/java/org/session/libsignal/utilities/Snode.kt b/libsignal/src/main/java/org/session/libsignal/utilities/Snode.kt index f6b11754ada..cc123a8527c 100644 --- a/libsignal/src/main/java/org/session/libsignal/utilities/Snode.kt +++ b/libsignal/src/main/java/org/session/libsignal/utilities/Snode.kt @@ -1,9 +1,21 @@ package org.session.libsignal.utilities -class Snode(val address: String, val port: Int, val publicKeySet: KeySet?, val version: String) { +import android.annotation.SuppressLint + +fun Snode(string: String): Snode? { + val components = string.split("-") + val address = components[0] + val port = components.getOrNull(1)?.toIntOrNull() ?: return null + val ed25519Key = components.getOrNull(2) ?: return null + val x25519Key = components.getOrNull(3) ?: return null + val version = components.getOrNull(4)?.let(Snode::Version) ?: Snode.Version.ZERO + return Snode(address, port, Snode.KeySet(ed25519Key, x25519Key), version) +} + +class Snode(val address: String, val port: Int, val publicKeySet: KeySet?, val version: Version) { val ip: String get() = address.removePrefix("https://") - public enum class Method(val rawValue: String) { + enum class Method(val rawValue: String) { GetSwarm("get_snodes_for_pubkey"), Retrieve("retrieve"), SendMessage("store"), @@ -32,4 +44,42 @@ class Snode(val address: String, val port: Int, val publicKeySet: KeySet?, val v } override fun toString(): String { return "$address:$port" } + + companion object { + private val CACHE = mutableMapOf() + + @SuppressLint("NotConstructor") + fun Version(value: String) = CACHE.getOrElse(value) { + Snode.Version(value) + } + } + + @JvmInline + value class Version(val value: ULong) { + companion object { + val ZERO = Version(0UL) + private const val MASK_BITS = 16 + private const val MASK = 0xFFFFUL + + private fun Sequence.foldToVersionAsULong() = take(4).foldIndexed(0UL) { i, acc, it -> + it and MASK shl (3 - i) * MASK_BITS or acc + } + } + + constructor(parts: List): this( + parts.asSequence() + .map { it.toByte().toULong() } + .foldToVersionAsULong() + ) + + constructor(value: Int): this(value.toULong()) + + internal constructor(value: String): this( + value.splitToSequence(".") + .map { it.toULongOrNull() ?: 0UL } + .foldToVersionAsULong() + ) + + operator fun compareTo(other: Version): Int = value.compareTo(other.value) + } } From 41ab9b98f820afc0435013683c475be613116b48 Mon Sep 17 00:00:00 2001 From: ThomasSession Date: Fri, 2 Aug 2024 16:00:24 +1000 Subject: [PATCH 16/50] Updated logic for hiding the input in conversation --- .../securesms/conversation/v2/ConversationViewModel.kt | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) 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) } From 9ce3ab4b8032b9fdfeaf6d0ac4066fe619389477 Mon Sep 17 00:00:00 2001 From: ThomasSession Date: Fri, 2 Aug 2024 16:31:24 +1000 Subject: [PATCH 17/50] Added a background to emoji variations popup --- .../components/emoji/EmojiVariationSelectorPopup.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) 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); } From 482f169df1f5fde73624624de86504f92ccd83a1 Mon Sep 17 00:00:00 2001 From: bemusementpark Date: Sat, 3 Aug 2024 02:46:42 +0930 Subject: [PATCH 18/50] Refactor SnodeApi --- .../messaging/jobs/ConfigurationSyncJob.kt | 1 - .../org/session/libsession/snode/SnodeAPI.kt | 728 +++++++----------- .../session/libsignal/utilities/Base64.java | 4 +- 3 files changed, 269 insertions(+), 464 deletions(-) diff --git a/libsession/src/main/java/org/session/libsession/messaging/jobs/ConfigurationSyncJob.kt b/libsession/src/main/java/org/session/libsession/messaging/jobs/ConfigurationSyncJob.kt index 4a3299d197c..a9f076b1064 100644 --- a/libsession/src/main/java/org/session/libsession/messaging/jobs/ConfigurationSyncJob.kt +++ b/libsession/src/main/java/org/session/libsession/messaging/jobs/ConfigurationSyncJob.kt @@ -63,7 +63,6 @@ data class ConfigurationSyncJob(val destination: Destination): Job { // return a list of batch request objects val snodeMessage = MessageSender.buildConfigMessageToSnode(destination.destinationPublicKey(), message) val authenticated = SnodeAPI.buildAuthenticatedStoreBatchInfo( - destination.destinationPublicKey(), config.configNamespace(), snodeMessage ) ?: return@map null // this entry will be null otherwise diff --git a/libsession/src/main/java/org/session/libsession/snode/SnodeAPI.kt b/libsession/src/main/java/org/session/libsession/snode/SnodeAPI.kt index 9ceefe8386c..8e19234b0d5 100644 --- a/libsession/src/main/java/org/session/libsession/snode/SnodeAPI.kt +++ b/libsession/src/main/java/org/session/libsession/snode/SnodeAPI.kt @@ -9,9 +9,9 @@ import com.goterl.lazysodium.interfaces.PwHash import com.goterl.lazysodium.interfaces.SecretBox import com.goterl.lazysodium.interfaces.Sign import com.goterl.lazysodium.utils.Key +import com.goterl.lazysodium.utils.KeyPair import nl.komponents.kovenant.Promise import nl.komponents.kovenant.all -import nl.komponents.kovenant.deferred import nl.komponents.kovenant.functional.bind import nl.komponents.kovenant.functional.map import nl.komponents.kovenant.task @@ -30,7 +30,6 @@ import org.session.libsignal.utilities.JsonUtil import org.session.libsignal.utilities.Log import org.session.libsignal.utilities.Namespace import org.session.libsignal.utilities.Snode -import org.session.libsignal.utilities.ThreadUtils import org.session.libsignal.utilities.prettifiedDescription import org.session.libsignal.utilities.retryIfNeeded import java.security.SecureRandom @@ -46,7 +45,7 @@ object SnodeAPI { private val broadcaster: Broadcaster get() = SnodeModule.shared.broadcaster - internal var snodeFailureCount: MutableMap = mutableMapOf() + private var snodeFailureCount: MutableMap = mutableMapOf() internal var snodePool: Set get() = database.getSnodePool() set(newValue) { database.setSnodePool(newValue) } @@ -57,7 +56,7 @@ object SnodeAPI { internal var clockOffset = 0L @JvmStatic - public val nowWithOffset + val nowWithOffset get() = System.currentTimeMillis() + clockOffset internal var forkInfo by observable(database.getForkInfo()) { _, oldValue, newValue -> @@ -68,32 +67,32 @@ object SnodeAPI { } // Settings - private val maxRetryCount = 6 - private val minimumSnodePoolCount = 12 - private val minimumSwarmSnodeCount = 3 + private const val maxRetryCount = 6 + private const val minimumSnodePoolCount = 12 + private const val minimumSwarmSnodeCount = 3 // Use port 4433 if the API level can handle the network security configuration and enforce pinned certificates private val seedNodePort = if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) 443 else 4443 - private val seedNodePool by lazy { - if (useTestnet) { - setOf( "http://public.loki.foundation:38157" ) - } else { - setOf( - "https://seed1.getsession.org:$seedNodePort", - "https://seed2.getsession.org:$seedNodePort", - "https://seed3.getsession.org:$seedNodePort", - ) - } - } + private const val snodeFailureThreshold = 3 private const val useOnionRequests = true - const val useTestnet = false + private const val useTestnet = false + + private val seedNodePool = if (useTestnet) { + setOf( "http://public.loki.foundation:38157" ) + } else { + setOf( + "https://seed1.getsession.org:$seedNodePort", + "https://seed2.getsession.org:$seedNodePort", + "https://seed3.getsession.org:$seedNodePort", + ) + } - const val KEY_IP = "public_ip" - const val KEY_PORT = "storage_port" - const val KEY_X25519 = "pubkey_x25519" - const val KEY_ED25519 = "pubkey_ed25519" - const val KEY_VERSION = "storage_server_version" + private const val KEY_IP = "public_ip" + private const val KEY_PORT = "storage_port" + private const val KEY_X25519 = "pubkey_x25519" + private const val KEY_ED25519 = "pubkey_ed25519" + private const val KEY_VERSION = "storage_server_version" // Error sealed class Error(val description: String) : Exception(description) { @@ -122,39 +121,28 @@ object SnodeAPI { parameters: Map, publicKey: String? = null, version: Version = Version.V3 - ): RawResponsePromise { - val url = "${snode.address}:${snode.port}/storage_rpc/v1" - val deferred = deferred, Exception>() - if (useOnionRequests) { - OnionRequestAPI.sendOnionRequest(method, parameters, snode, version, publicKey).map { - val body = it.body ?: throw Error.Generic - deferred.resolve(JsonUtil.fromJson(body, Map::class.java)) - }.fail { deferred.reject(it) } - } else { - ThreadUtils.queue { - val payload = mapOf( "method" to method.rawValue, "params" to parameters ) - try { - val response = HTTP.execute(HTTP.Verb.POST, url, payload).toString() - val json = JsonUtil.fromJson(response, Map::class.java) - deferred.resolve(json) - } catch (exception: Exception) { - val httpRequestFailedException = exception as? HTTP.HTTPRequestFailedException - if (httpRequestFailedException != null) { - val error = handleSnodeError(httpRequestFailedException.statusCode, httpRequestFailedException.json, snode, publicKey) - if (error != null) { return@queue deferred.reject(exception) } - } - Log.d("Loki", "Unhandled exception: $exception.") - deferred.reject(exception) + ): RawResponsePromise = if (useOnionRequests) OnionRequestAPI.sendOnionRequest(method, parameters, snode, version, publicKey).map { + val body = it.body ?: throw Error.Generic + JsonUtil.fromJson(body, Map::class.java) + } else task { + val payload = mapOf( "method" to method.rawValue, "params" to parameters ) + try { + val url = "${snode.address}:${snode.port}/storage_rpc/v1" + val response = HTTP.execute(HTTP.Verb.POST, url, payload).toString() + JsonUtil.fromJson(response, Map::class.java) + } catch (exception: Exception) { + (exception as? HTTP.HTTPRequestFailedException)?.run { + handleSnodeError(statusCode, json, snode, publicKey) + // TODO Check if we meant to throw the error returned by handleSnodeError + throw exception } + Log.d("Loki", "Unhandled exception: $exception.") + throw exception } } - return deferred.promise - } - internal fun getRandomSnode(): Promise { - val snodePool = this.snodePool - - if (snodePool.count() < minimumSnodePoolCount) { + internal fun getRandomSnode(): Promise = + snodePool.takeIf { it.size >= minimumSnodePoolCount }?.let { Promise.of(it.getRandomElement()) } ?: task { val target = seedNodePool.random() val url = "$target/json_rpc" Log.d("Loki", "Populating snode pool using: $target.") @@ -169,73 +157,48 @@ object SnodeAPI { ) ) ) - val deferred = deferred() - deferred() - ThreadUtils.queue { - try { - val response = HTTP.execute(HTTP.Verb.POST, url, parameters, useSeedNodeConnection = true) - val json = try { - JsonUtil.fromJson(response, Map::class.java) - } catch (exception: Exception) { - mapOf( "result" to response.toString()) - } - val intermediate = json["result"] as? Map<*, *> - val rawSnodes = intermediate?.get("service_node_states") as? List<*> - if (rawSnodes != null) { - val snodePool = rawSnodes.mapNotNull { rawSnode -> - val rawSnodeAsJSON = rawSnode as? Map<*, *> - val address = rawSnodeAsJSON?.get(KEY_IP) as? String - val port = rawSnodeAsJSON?.get(KEY_PORT) as? Int - val ed25519Key = rawSnodeAsJSON?.get(KEY_ED25519) as? String - val x25519Key = rawSnodeAsJSON?.get(KEY_X25519) as? String - val version = (rawSnodeAsJSON?.get(KEY_VERSION) as? ArrayList<*>) - ?.filterIsInstance() // get the array as Integers - ?.let(Snode::Version) // turn it int a version - - if (address != null && port != null && ed25519Key != null && x25519Key != null - && address != "0.0.0.0" && version != null) { - Snode( - address = "https://$address", - port = port, - publicKeySet = Snode.KeySet(ed25519Key, x25519Key), - version = version - ) - } else { - Log.d("Loki", "Failed to parse: ${rawSnode?.prettifiedDescription()}.") - null - } - }.toMutableSet() - Log.d("Loki", "Persisting snode pool to database.") - this.snodePool = snodePool - try { - deferred.resolve(snodePool.getRandomElement()) - } catch (exception: Exception) { - Log.d("Loki", "Got an empty snode pool from: $target.") - deferred.reject(SnodeAPI.Error.Generic) - } - } else { - Log.d("Loki", "Failed to update snode pool from: ${(rawSnodes as List<*>?)?.prettifiedDescription()}.") - deferred.reject(SnodeAPI.Error.Generic) - } - } catch (exception: Exception) { - deferred.reject(exception) - } + val response = HTTP.execute(HTTP.Verb.POST, url, parameters, useSeedNodeConnection = true) + val json = try { + JsonUtil.fromJson(response, Map::class.java) + } catch (exception: Exception) { + mapOf( "result" to response.toString()) } - return deferred.promise - } else { - return Promise.of(snodePool.getRandomElement()) - } - } - - private fun extractVersionString(jsonVersion: String): String{ - return jsonVersion.removeSurrounding("[", "]").split(", ").joinToString(separator = ".") + val intermediate = json["result"] as? Map<*, *> ?: throw Error.Generic + .also { Log.d("Loki", "Failed to update snode pool, intermediate was null.") } + val rawSnodes = intermediate["service_node_states"] as? List<*> ?: throw Error.Generic + .also { Log.d("Loki", "Failed to update snode pool, rawSnodes was null.") } + + rawSnodes.asSequence().mapNotNull { it as? Map<*, *> }.mapNotNull { rawSnode -> + createSnode( + address = rawSnode[KEY_IP] as? String, + port = rawSnode[KEY_PORT] as? Int, + ed25519Key = rawSnode[KEY_ED25519] as? String, + x25519Key = rawSnode[KEY_X25519] as? String, + version = (rawSnode[KEY_VERSION] as? List<*>) + ?.filterIsInstance() + ?.let(Snode::Version) + ).also { if (it == null) Log.d("Loki", "Failed to parse: ${rawSnode.prettifiedDescription()}.") } + }.toSet().also { + Log.d("Loki", "Persisting snode pool to database.") + this.snodePool = it + }.runCatching { getRandomElement() }.onFailure { + Log.d("Loki", "Got an empty snode pool from: $target.") + throw SnodeAPI.Error.Generic + }.getOrThrow() + } + + private fun createSnode(address: String?, port: Int?, ed25519Key: String?, x25519Key: String?, version: Snode.Version? = Snode.Version.ZERO): Snode? { + return Snode( + address?.takeUnless { it == "0.0.0.0" }?.let { "https://$it" } ?: return null, + port ?: return null, + Snode.KeySet(ed25519Key ?: return null, x25519Key ?: return null), + version ?: return null + ) } internal fun dropSnodeFromSwarmIfNeeded(snode: Snode, publicKey: String) { - val swarm = database.getSwarm(publicKey)?.toMutableSet() - if (swarm != null && swarm.contains(snode)) { - swarm.remove(snode) - database.setSwarm(publicKey, swarm) + database.getSwarm(publicKey)?.takeIf { snode in it }?.let { + database.setSwarm(publicKey, it - snode) } } @@ -246,8 +209,6 @@ object SnodeAPI { // Public API fun getAccountID(onsName: String): Promise { - val deferred = deferred() - val promise = deferred.promise val validationCount = 3 val accountIDByteCount = 33 // Hash the ONS name using BLAKE2b @@ -255,96 +216,79 @@ object SnodeAPI { val nameAsData = onsName.toByteArray() val nameHash = ByteArray(GenericHash.BYTES) if (!sodium.cryptoGenericHash(nameHash, nameHash.size, nameAsData, nameAsData.size.toLong())) { - deferred.reject(Error.HashingFailed) - return promise + throw Error.HashingFailed } val base64EncodedNameHash = Base64.encodeBytes(nameHash) // Ask 3 different snodes for the Account ID associated with the given name hash val parameters = mapOf( - "endpoint" to "ons_resolve", - "params" to mapOf( "type" to 0, "name_hash" to base64EncodedNameHash ) + "endpoint" to "ons_resolve", + "params" to mapOf( "type" to 0, "name_hash" to base64EncodedNameHash ) ) - val promises = (1..validationCount).map { + val promises = List(validationCount) { getRandomSnode().bind { snode -> retryIfNeeded(maxRetryCount) { invoke(Snode.Method.OxenDaemonRPCCall, snode, parameters) } } } - all(promises).success { results -> + return all(promises).map { results -> val accountIDs = mutableListOf() for (json in results) { val intermediate = json["result"] as? Map<*, *> - val hexEncodedCiphertext = intermediate?.get("encrypted_value") as? String - if (hexEncodedCiphertext != null) { - val ciphertext = Hex.fromStringCondensed(hexEncodedCiphertext) - val isArgon2Based = (intermediate["nonce"] == null) - if (isArgon2Based) { - // Handle old Argon2-based encryption used before HF16 - val salt = ByteArray(PwHash.SALTBYTES) - val key: ByteArray - val nonce = ByteArray(SecretBox.NONCEBYTES) - val accountIDAsData = ByteArray(accountIDByteCount) - try { - key = Key.fromHexString(sodium.cryptoPwHash(onsName, SecretBox.KEYBYTES, salt, PwHash.OPSLIMIT_MODERATE, PwHash.MEMLIMIT_MODERATE, PwHash.Alg.PWHASH_ALG_ARGON2ID13)).asBytes - } catch (e: SodiumException) { - deferred.reject(Error.HashingFailed) - return@success - } - if (!sodium.cryptoSecretBoxOpenEasy(accountIDAsData, ciphertext, ciphertext.size.toLong(), nonce, key)) { - deferred.reject(Error.DecryptionFailed) - return@success - } - accountIDs.add(Hex.toStringCondensed(accountIDAsData)) - } else { - val hexEncodedNonce = intermediate["nonce"] as? String - if (hexEncodedNonce == null) { - deferred.reject(Error.Generic) - return@success - } - val nonce = Hex.fromStringCondensed(hexEncodedNonce) - val key = ByteArray(GenericHash.BYTES) - if (!sodium.cryptoGenericHash(key, key.size, nameAsData, nameAsData.size.toLong(), nameHash, nameHash.size)) { - deferred.reject(Error.HashingFailed) - return@success - } - val accountIDAsData = ByteArray(accountIDByteCount) - if (!sodium.cryptoAeadXChaCha20Poly1305IetfDecrypt(accountIDAsData, null, null, ciphertext, ciphertext.size.toLong(), null, 0, nonce, key)) { - deferred.reject(Error.DecryptionFailed) - return@success - } - accountIDs.add(Hex.toStringCondensed(accountIDAsData)) + val hexEncodedCiphertext = intermediate?.get("encrypted_value") as? String ?: throw Error.Generic + val ciphertext = Hex.fromStringCondensed(hexEncodedCiphertext) + val isArgon2Based = (intermediate["nonce"] == null) + if (isArgon2Based) { + // Handle old Argon2-based encryption used before HF16 + val salt = ByteArray(PwHash.SALTBYTES) + val nonce = ByteArray(SecretBox.NONCEBYTES) + val accountIDAsData = ByteArray(accountIDByteCount) + val key = try { + Key.fromHexString(sodium.cryptoPwHash(onsName, SecretBox.KEYBYTES, salt, PwHash.OPSLIMIT_MODERATE, PwHash.MEMLIMIT_MODERATE, PwHash.Alg.PWHASH_ALG_ARGON2ID13)).asBytes + } catch (e: SodiumException) { + throw Error.HashingFailed + } + if (!sodium.cryptoSecretBoxOpenEasy(accountIDAsData, ciphertext, ciphertext.size.toLong(), nonce, key)) { + throw Error.DecryptionFailed } + accountIDs.add(Hex.toStringCondensed(accountIDAsData)) } else { - deferred.reject(Error.Generic) - return@success + val hexEncodedNonce = intermediate["nonce"] as? String ?: throw Error.Generic + val nonce = Hex.fromStringCondensed(hexEncodedNonce) + val key = ByteArray(GenericHash.BYTES) + if (!sodium.cryptoGenericHash(key, key.size, nameAsData, nameAsData.size.toLong(), nameHash, nameHash.size)) { + throw Error.HashingFailed + } + val accountIDAsData = ByteArray(accountIDByteCount) + if (!sodium.cryptoAeadXChaCha20Poly1305IetfDecrypt(accountIDAsData, null, null, ciphertext, ciphertext.size.toLong(), null, 0, nonce, key)) { + throw Error.DecryptionFailed + } + accountIDs.add(Hex.toStringCondensed(accountIDAsData)) } } - if (accountIDs.size == validationCount && accountIDs.toSet().size == 1) { - deferred.resolve(accountIDs.first()) - } else { - deferred.reject(Error.ValidationFailed) - } + accountIDs.takeIf { it.size == validationCount && it.toSet().size == 1 }?.first() + ?: throw Error.ValidationFailed } - return promise } - fun getSwarm(publicKey: String): Promise, Exception> { - val cachedSwarm = database.getSwarm(publicKey) - return if (cachedSwarm != null && cachedSwarm.size >= minimumSwarmSnodeCount) { - val cachedSwarmCopy = mutableSetOf() // Workaround for a Kotlin compiler issue - cachedSwarmCopy.addAll(cachedSwarm) - task { cachedSwarmCopy } - } else { - val parameters = mapOf( "pubKey" to publicKey ) - getRandomSnode().bind { - invoke(Snode.Method.GetSwarm, it, parameters, publicKey) + fun getSwarm(publicKey: String): Promise, Exception> = + database.getSwarm(publicKey)?.takeIf { it.size >= minimumSwarmSnodeCount }?.let(Promise.Companion::of) + ?: getRandomSnode().bind { + invoke(Snode.Method.GetSwarm, it, parameters = mapOf( "pubKey" to publicKey ), publicKey) }.map { parseSnodes(it).toSet() }.success { database.setSwarm(publicKey, it) } - } + + private fun signAndEncode(data: ByteArray, userED25519KeyPair: KeyPair) = sign(data, userED25519KeyPair).let(Base64::encodeBytes) + private fun sign(data: ByteArray, userED25519KeyPair: KeyPair): ByteArray = ByteArray(Sign.BYTES).also { + sodium.cryptoSignDetached( + it, + data, + data.size.toLong(), + userED25519KeyPair.secretKey.asBytes + ) } fun getRawMessages(snode: Snode, publicKey: String, requiresAuth: Boolean = true, namespace: Int = 0): RawResponsePromise { @@ -365,23 +309,19 @@ object SnodeAPI { } val timestamp = System.currentTimeMillis() + clockOffset val ed25519PublicKey = userED25519KeyPair.publicKey.asHexString - val signature = ByteArray(Sign.BYTES) - val verificationData = - if (namespace != 0) "retrieve$namespace$timestamp".toByteArray() - else "retrieve$timestamp".toByteArray() - try { - sodium.cryptoSignDetached( - signature, - verificationData, - verificationData.size.toLong(), - userED25519KeyPair.secretKey.asBytes - ) + val verificationData = buildString { + append("retrieve") + if (namespace != 0) append(namespace) + append(timestamp) + }.toByteArray() + val signature = try { + signAndEncode(verificationData, userED25519KeyPair) } catch (exception: Exception) { return Promise.ofFail(Error.SigningFailed) } parameters["timestamp"] = timestamp parameters["pubkey_ed25519"] = ed25519PublicKey - parameters["signature"] = Base64.encodeBytes(signature) + parameters["signature"] = signature } // If the namespace is default (0) here it will be implicitly read as 0 on the storage server @@ -394,42 +334,34 @@ object SnodeAPI { return invoke(Snode.Method.Retrieve, snode, parameters, publicKey) } - fun buildAuthenticatedStoreBatchInfo(publicKey: String, namespace: Int, message: SnodeMessage): SnodeBatchRequestInfo? { - val params = mutableMapOf() - // load the message data params into the sub request - // currently loads: - // pubKey - // data - // ttl - // timestamp - params.putAll(message.toJSON()) - params["namespace"] = namespace - + fun buildAuthenticatedStoreBatchInfo(namespace: Int, message: SnodeMessage): SnodeBatchRequestInfo? { // used for sig generation since it is also the value used in timestamp parameter val messageTimestamp = message.timestamp - val userEd25519KeyPair = try { - MessagingModuleConfiguration.shared.getUserED25519KeyPair() ?: return null - } catch (e: Exception) { - return null - } + val userED25519KeyPair = runCatching { MessagingModuleConfiguration.shared.getUserED25519KeyPair() }.getOrNull() ?: return null - val ed25519PublicKey = userEd25519KeyPair.publicKey.asHexString - val signature = ByteArray(Sign.BYTES) val verificationData = "store$namespace$messageTimestamp".toByteArray() - try { - sodium.cryptoSignDetached( - signature, - verificationData, - verificationData.size.toLong(), - userEd25519KeyPair.secretKey.asBytes - ) + val signature = try { + signAndEncode(verificationData, userED25519KeyPair) } catch (e: Exception) { Log.e("Loki", "Signing data failed with user secret key", e) + return null } - // timestamp already set - params["pubkey_ed25519"] = ed25519PublicKey - params["signature"] = Base64.encodeBytes(signature) + + val params = buildMap { + // load the message data params into the sub request + // currently loads: + // pubKey + // data + // ttl + // timestamp + putAll(message.toJSON()) + this["namespace"] = namespace + // timestamp already set + this["pubkey_ed25519"] = userED25519KeyPair.publicKey.asHexString + this["signature"] = signature + } + return SnodeBatchRequestInfo( Snode.Method.SendMessage.rawValue, params, @@ -444,32 +376,26 @@ object SnodeAPI { * @param required indicates that *at least one* message in the list is deleted from the server, otherwise it will return 404 */ fun buildAuthenticatedDeleteBatchInfo(publicKey: String, messageHashes: List, required: Boolean = false): SnodeBatchRequestInfo? { - val params = mutableMapOf( - "pubkey" to publicKey, - "required" to required, // could be omitted technically but explicit here - "messages" to messageHashes - ) val userEd25519KeyPair = try { MessagingModuleConfiguration.shared.getUserED25519KeyPair() ?: return null } catch (e: Exception) { return null } val ed25519PublicKey = userEd25519KeyPair.publicKey.asHexString - val signature = ByteArray(Sign.BYTES) - val verificationData = "delete${messageHashes.joinToString("")}".toByteArray() - try { - sodium.cryptoSignDetached( - signature, - verificationData, - verificationData.size.toLong(), - userEd25519KeyPair.secretKey.asBytes - ) + val verificationData = sequenceOf("delete").plus(messageHashes).toByteArray() + val signature = try { + signAndEncode(verificationData, userEd25519KeyPair) } catch (e: Exception) { Log.e("Loki", "Signing data failed with user secret key", e) return null } - params["pubkey_ed25519"] = ed25519PublicKey - params["signature"] = Base64.encodeBytes(signature) + val params = buildMap { + this["pubkey"] = publicKey + this["required"] = required // could be omitted technically but explicit here + this["messages"] = messageHashes + this["pubkey_ed25519"] = ed25519PublicKey + this["signature"] = signature + } return SnodeBatchRequestInfo( Snode.Method.DeleteMessage.rawValue, params, @@ -479,39 +405,25 @@ object SnodeAPI { fun buildAuthenticatedRetrieveBatchRequest(snode: Snode, publicKey: String, namespace: Int = 0, maxSize: Int? = null): SnodeBatchRequestInfo? { val lastHashValue = database.getLastMessageHashValue(snode, publicKey, namespace) ?: "" - val params = mutableMapOf( - "pubkey" to publicKey, - "last_hash" to lastHashValue, - ) - val userEd25519KeyPair = try { - MessagingModuleConfiguration.shared.getUserED25519KeyPair() ?: return null - } catch (e: Exception) { - return null - } + val userEd25519KeyPair = runCatching { MessagingModuleConfiguration.shared.getUserED25519KeyPair() }.getOrNull() ?: return null val ed25519PublicKey = userEd25519KeyPair.publicKey.asHexString val timestamp = System.currentTimeMillis() + clockOffset - val signature = ByteArray(Sign.BYTES) val verificationData = if (namespace == 0) "retrieve$timestamp".toByteArray() else "retrieve$namespace$timestamp".toByteArray() - try { - sodium.cryptoSignDetached( - signature, - verificationData, - verificationData.size.toLong(), - userEd25519KeyPair.secretKey.asBytes - ) + val signature = try { + signAndEncode(verificationData, userEd25519KeyPair) } catch (e: Exception) { Log.e("Loki", "Signing data failed with user secret key", e) return null } - params["timestamp"] = timestamp - params["pubkey_ed25519"] = ed25519PublicKey - params["signature"] = Base64.encodeBytes(signature) - if (namespace != 0) { - params["namespace"] = namespace - } - if (maxSize != null) { - params["max_size"] = maxSize + val params = buildMap { + this["pubkey"] = publicKey + this["last_hash"] = lastHashValue + this["timestamp"] = timestamp + this["pubkey_ed25519"] = ed25519PublicKey + this["signature"] = signature + if (namespace != 0) this["namespace"] = namespace + if (maxSize != null) this["max_size"] = maxSize } return SnodeBatchRequestInfo( Snode.Method.Retrieve.rawValue, @@ -535,13 +447,12 @@ object SnodeAPI { } fun getRawBatchResponse(snode: Snode, publicKey: String, requests: List, sequence: Boolean = false): RawResponsePromise { - val parameters = mutableMapOf( - "requests" to requests - ) + val parameters = buildMap { this["requests"] = requests } return invoke(if (sequence) Snode.Method.Sequence else Snode.Method.Batch, snode, parameters, publicKey).success { rawResponses -> - val responseList = (rawResponses["results"] as List) - responseList.forEachIndexed { index, response -> - if (response["code"] as? Int != 200) { + rawResponses["results"].let { it as List } + .asSequence() + .filter { it["code"] as? Int != 200 } + .forEach { response -> Log.w("Loki", "response code was not 200") handleSnodeError( response["code"] as? Int ?: 0, @@ -550,7 +461,6 @@ object SnodeAPI { publicKey ) } - } } } @@ -562,14 +472,8 @@ object SnodeAPI { val signData = "${Snode.Method.GetExpiries.rawValue}$timestamp${hashes.joinToString(separator = "")}".toByteArray() val ed25519PublicKey = userEd25519KeyPair.publicKey.asHexString - val signature = ByteArray(Sign.BYTES) - try { - sodium.cryptoSignDetached( - signature, - signData, - signData.size.toLong(), - userEd25519KeyPair.secretKey.asBytes - ) + val signature = try { + signAndEncode(signData, userEd25519KeyPair) } catch (e: Exception) { Log.e("Loki", "Signing data failed with user secret key", e) return@retryIfNeeded Promise.ofFail(e) @@ -579,7 +483,7 @@ object SnodeAPI { "messages" to hashes, "timestamp" to timestamp, "pubkey_ed25519" to ed25519PublicKey, - "signature" to Base64.encodeBytes(signature) + "signature" to signature ) getSingleTargetSnode(publicKey) bind { snode -> invoke(Snode.Method.GetExpiries, snode, params, publicKey) @@ -587,8 +491,8 @@ object SnodeAPI { } } - fun alterTtl(messageHashes: List, newExpiry: Long, publicKey: String, extend: Boolean = false, shorten: Boolean = false): RawResponsePromise { - return retryIfNeeded(maxRetryCount) { + fun alterTtl(messageHashes: List, newExpiry: Long, publicKey: String, extend: Boolean = false, shorten: Boolean = false): RawResponsePromise = + retryIfNeeded(maxRetryCount) { val params = buildAlterTtlParams(messageHashes, newExpiry, publicKey, extend, shorten) ?: return@retryIfNeeded Promise.ofFail( Exception("Couldn't build signed params for alterTtl request for newExpiry=$newExpiry, extend=$extend, shorten=$shorten") @@ -597,111 +501,96 @@ object SnodeAPI { invoke(Snode.Method.Expire, snode, params, publicKey) } } - } private fun buildAlterTtlParams( // TODO: in future this will probably need to use the closed group subkeys / admin keys for group swarms messageHashes: List, newExpiry: Long, publicKey: String, extend: Boolean = false, - shorten: Boolean = false): Map? { + shorten: Boolean = false + ): Map? { val userEd25519KeyPair = MessagingModuleConfiguration.shared.getUserED25519KeyPair() ?: return null - val params = mutableMapOf( - "expiry" to newExpiry, - "messages" to messageHashes, - ) - if (extend) { - params["extend"] = true - } else if (shorten) { - params["shorten"] = true - } + val shortenOrExtend = if (extend) "extend" else if (shorten) "shorten" else "" val signData = "${Snode.Method.Expire.rawValue}$shortenOrExtend$newExpiry${messageHashes.joinToString(separator = "")}".toByteArray() - val ed25519PublicKey = userEd25519KeyPair.publicKey.asHexString - val signature = ByteArray(Sign.BYTES) - try { - sodium.cryptoSignDetached( - signature, - signData, - signData.size.toLong(), - userEd25519KeyPair.secretKey.asBytes - ) + val signature = try { + signAndEncode(signData, userEd25519KeyPair) } catch (e: Exception) { Log.e("Loki", "Signing data failed with user secret key", e) return null } - params["pubkey"] = publicKey - params["pubkey_ed25519"] = ed25519PublicKey - params["signature"] = Base64.encodeBytes(signature) - return params + return buildMap { + this["expiry"] = newExpiry + this["messages"] = messageHashes + when { + extend -> this["extend"] = true + shorten -> this["shorten"] = true + } + this["pubkey"] = publicKey + this["pubkey_ed25519"] = userEd25519KeyPair.publicKey.asHexString + this["signature"] = signature + } } - fun getMessages(publicKey: String): MessageListPromise { - return retryIfNeeded(maxRetryCount) { - getSingleTargetSnode(publicKey).bind { snode -> - getRawMessages(snode, publicKey).map { parseRawMessagesResponse(it, snode, publicKey) } - } + fun getMessages(publicKey: String): MessageListPromise = retryIfNeeded(maxRetryCount) { + getSingleTargetSnode(publicKey).bind { snode -> + getRawMessages(snode, publicKey).map { parseRawMessagesResponse(it, snode, publicKey) } } } - private fun getNetworkTime(snode: Snode): Promise, Exception> { - return invoke(Snode.Method.Info, snode, emptyMap()).map { rawResponse -> + private fun getNetworkTime(snode: Snode): Promise, Exception> = + invoke(Snode.Method.Info, snode, emptyMap()).map { rawResponse -> val timestamp = rawResponse["timestamp"] as? Long ?: -1 snode to timestamp } - } - fun sendMessage(message: SnodeMessage, requiresAuth: Boolean = false, namespace: Int = 0): RawResponsePromise { - val destination = message.recipient - return retryIfNeeded(maxRetryCount) { + fun sendMessage(message: SnodeMessage, requiresAuth: Boolean = false, namespace: Int = 0): RawResponsePromise = + retryIfNeeded(maxRetryCount) { val module = MessagingModuleConfiguration.shared val userED25519KeyPair = module.getUserED25519KeyPair() ?: return@retryIfNeeded Promise.ofFail(Error.NoKeyPair) - val parameters = message.toJSON().toMutableMap() + val parameters = message.toJSON().toMutableMap() // Construct signature if (requiresAuth) { val sigTimestamp = nowWithOffset val ed25519PublicKey = userED25519KeyPair.publicKey.asHexString - val signature = ByteArray(Sign.BYTES) // assume namespace here is non-zero, as zero namespace doesn't require auth val verificationData = "store$namespace$sigTimestamp".toByteArray() - try { - sodium.cryptoSignDetached(signature, verificationData, verificationData.size.toLong(), userED25519KeyPair.secretKey.asBytes) + val signature = try { + signAndEncode(verificationData, userED25519KeyPair) } catch (exception: Exception) { return@retryIfNeeded Promise.ofFail(Error.SigningFailed) } parameters["sig_timestamp"] = sigTimestamp parameters["pubkey_ed25519"] = ed25519PublicKey - parameters["signature"] = Base64.encodeBytes(signature) + parameters["signature"] = signature } // If the namespace is default (0) here it will be implicitly read as 0 on the storage server // we only need to specify it explicitly if we want to (in future) or if it is non-zero if (namespace != 0) { parameters["namespace"] = namespace } + val destination = message.recipient getSingleTargetSnode(destination).bind { snode -> invoke(Snode.Method.SendMessage, snode, parameters, destination) } } - } - fun deleteMessage(publicKey: String, serverHashes: List): Promise, Exception> { - return retryIfNeeded(maxRetryCount) { + fun deleteMessage(publicKey: String, serverHashes: List): Promise, Exception> = + retryIfNeeded(maxRetryCount) { val module = MessagingModuleConfiguration.shared val userED25519KeyPair = module.getUserED25519KeyPair() ?: return@retryIfNeeded Promise.ofFail(Error.NoKeyPair) val userPublicKey = module.storage.getUserPublicKey() ?: return@retryIfNeeded Promise.ofFail(Error.NoKeyPair) getSingleTargetSnode(publicKey).bind { snode -> retryIfNeeded(maxRetryCount) { - val signature = ByteArray(Sign.BYTES) val verificationData = sequenceOf(Snode.Method.DeleteMessage.rawValue).plus(serverHashes).toByteArray() - sodium.cryptoSignDetached(signature, verificationData, verificationData.size.toLong(), userED25519KeyPair.secretKey.asBytes) val deleteMessageParams = mapOf( "pubkey" to userPublicKey, "pubkey_ed25519" to userED25519KeyPair.publicKey.asHexString, "messages" to serverHashes, - "signature" to Base64.encodeBytes(signature) + "signature" to signAndEncode(verificationData, userED25519KeyPair) ) invoke(Snode.Method.DeleteMessage, snode, deleteMessageParams, publicKey).map { rawResponse -> val swarms = rawResponse["swarm"] as? Map ?: return@map mapOf() @@ -729,44 +618,36 @@ object SnodeAPI { } } } - } // Parsing private fun parseSnodes(rawResponse: Any): List = (rawResponse as? Map<*, *>) ?.run { get("snodes") as? List<*> } - ?.mapNotNull { rawSnode -> - val rawSnodeAsJSON = rawSnode as? Map<*, *> - val address = rawSnodeAsJSON?.get("ip") as? String - val portAsString = rawSnodeAsJSON?.get("port") as? String - val port = portAsString?.toInt() - val ed25519Key = rawSnodeAsJSON?.get(KEY_ED25519) as? String - val x25519Key = rawSnodeAsJSON?.get(KEY_X25519) as? String - - if (address != null && port != null && ed25519Key != null && x25519Key != null && address != "0.0.0.0") { - Snode("https://$address", port, Snode.KeySet(ed25519Key, x25519Key), Snode.Version.ZERO) - } else { - Log.d("Loki", "Failed to parse snode from: ${rawSnode?.prettifiedDescription()}.") - null - } - } ?: listOf().also { Log.d("Loki", "Failed to parse snodes from: ${rawResponse.prettifiedDescription()}.") } - - fun deleteAllMessages(): Promise, Exception> { - return retryIfNeeded(maxRetryCount) { + ?.asSequence() + ?.mapNotNull { it as? Map<*, *> } + ?.mapNotNull { + createSnode( + address = it["ip"] as? String, + port = (it["port"] as? String)?.toInt(), + ed25519Key = it[KEY_ED25519] as? String, + x25519Key = it[KEY_X25519] as? String + ).apply { if (this == null) Log.d("Loki", "Failed to parse snode from: ${it.prettifiedDescription()}.") } + }?.toList() ?: listOf().also { Log.d("Loki", "Failed to parse snodes from: ${rawResponse.prettifiedDescription()}.") } + + fun deleteAllMessages(): Promise, Exception> = + retryIfNeeded(maxRetryCount) { val module = MessagingModuleConfiguration.shared val userED25519KeyPair = module.getUserED25519KeyPair() ?: return@retryIfNeeded Promise.ofFail(Error.NoKeyPair) val userPublicKey = module.storage.getUserPublicKey() ?: return@retryIfNeeded Promise.ofFail(Error.NoKeyPair) getSingleTargetSnode(userPublicKey).bind { snode -> retryIfNeeded(maxRetryCount) { getNetworkTime(snode).bind { (_, timestamp) -> - val signature = ByteArray(Sign.BYTES) val verificationData = (Snode.Method.DeleteAll.rawValue + Namespace.ALL + timestamp.toString()).toByteArray() - sodium.cryptoSignDetached(signature, verificationData, verificationData.size.toLong(), userED25519KeyPair.secretKey.asBytes) val deleteMessageParams = mapOf( "pubkey" to userPublicKey, "pubkey_ed25519" to userED25519KeyPair.publicKey.asHexString, "timestamp" to timestamp, - "signature" to Base64.encodeBytes(signature), + "signature" to signAndEncode(verificationData, userED25519KeyPair), "namespace" to Namespace.ALL, ) invoke(Snode.Method.DeleteAll, snode, deleteMessageParams, userPublicKey).map { @@ -778,75 +659,14 @@ object SnodeAPI { } } } - } - fun updateExpiry(updatedExpiryMs: Long, serverHashes: List): Promise, Long>>, Exception> { - return retryIfNeeded(maxRetryCount) { - val module = MessagingModuleConfiguration.shared - val userED25519KeyPair = module.getUserED25519KeyPair() ?: return@retryIfNeeded Promise.ofFail(Error.NoKeyPair) - val userPublicKey = module.storage.getUserPublicKey() ?: return@retryIfNeeded Promise.ofFail(Error.NoKeyPair) - val updatedExpiryMsWithNetworkOffset = updatedExpiryMs + clockOffset - getSingleTargetSnode(userPublicKey).bind { snode -> - retryIfNeeded(maxRetryCount) { - // "expire" || expiry || messages[0] || ... || messages[N] - val verificationData = sequenceOf(Snode.Method.Expire.rawValue, "$updatedExpiryMsWithNetworkOffset").plus(serverHashes).toByteArray() - val signature = ByteArray(Sign.BYTES) - sodium.cryptoSignDetached( - signature, - verificationData, - verificationData.size.toLong(), - userED25519KeyPair.secretKey.asBytes - ) - val params = mapOf( - "pubkey" to userPublicKey, - "pubkey_ed25519" to userED25519KeyPair.publicKey.asHexString, - "expiry" to updatedExpiryMs, - "messages" to serverHashes, - "signature" to Base64.encodeBytes(signature) - ) - invoke(Snode.Method.Expire, snode, params, userPublicKey).map { rawResponse -> - val swarms = rawResponse["swarm"] as? Map ?: return@map mapOf() - val result = swarms.mapNotNull { (hexSnodePublicKey, rawJSON) -> - val json = rawJSON as? Map ?: return@mapNotNull null - val isFailed = json["failed"] as? Boolean ?: false - val statusCode = json["code"] as? String - val reason = json["reason"] as? String - hexSnodePublicKey to if (isFailed) { - Log.e("Loki", "Failed to update expiry for: $hexSnodePublicKey due to error: $reason ($statusCode).") - listOf() to 0L - } else { - val hashes = json["updated"] as List - val expiryApplied = json["expiry"] as Long - val signature = json["signature"] as String - val snodePublicKey = Key.fromHexString(hexSnodePublicKey) - // The signature looks like ( PUBKEY_HEX || RMSG[0] || ... || RMSG[N] || DMSG[0] || ... || DMSG[M] ) - val message = sequenceOf(userPublicKey).plus(serverHashes).plus(hashes).toByteArray() - if (sodium.cryptoSignVerifyDetached(Base64.decode(signature), message, message.size, snodePublicKey.asBytes)) { - hashes to expiryApplied - } else listOf() to 0L - } - } - return@map result.toMap() - }.fail { e -> - Log.e("Loki", "Failed to update expiry", e) - } - } - } - } - } - - fun parseRawMessagesResponse(rawResponse: RawResponse, snode: Snode, publicKey: String, namespace: Int = 0, updateLatestHash: Boolean = true, updateStoredHashes: Boolean = true): List> { - val messages = rawResponse["messages"] as? List<*> - return if (messages != null) { + fun parseRawMessagesResponse(rawResponse: RawResponse, snode: Snode, publicKey: String, namespace: Int = 0, updateLatestHash: Boolean = true, updateStoredHashes: Boolean = true): List> = + (rawResponse["messages"] as? List<*>)?.let { messages -> if (updateLatestHash) { updateLastMessageHashValueIfPossible(snode, publicKey, messages, namespace) } - val newRawMessages = removeDuplicates(publicKey, messages, namespace, updateStoredHashes) - return parseEnvelopes(newRawMessages) - } else { - listOf() - } - } + removeDuplicates(publicKey, messages, namespace, updateStoredHashes).let(::parseEnvelopes) + } ?: listOf() fun updateLastMessageHashValueIfPossible(snode: Snode, publicKey: String, rawMessages: List<*>, namespace: Int) { val lastMessageAsJSON = rawMessages.lastOrNull() as? Map<*, *> @@ -859,34 +679,33 @@ object SnodeAPI { } fun removeDuplicates(publicKey: String, rawMessages: List<*>, namespace: Int, updateStoredHashes: Boolean): List<*> { - val originalMessageHashValues = database.getReceivedMessageHashValues(publicKey, namespace)?.toMutableSet() ?: mutableSetOf() + val originalMessageHashValues = database.getReceivedMessageHashValues(publicKey, namespace) ?: emptySet() val receivedMessageHashValues = originalMessageHashValues.toMutableSet() val result = rawMessages.filter { rawMessage -> - val rawMessageAsJSON = rawMessage as? Map<*, *> - val hashValue = rawMessageAsJSON?.get("hash") as? String - if (hashValue != null) { - val isDuplicate = receivedMessageHashValues.contains(hashValue) - receivedMessageHashValues.add(hashValue) - !isDuplicate - } else { - Log.d("Loki", "Missing hash value for message: ${rawMessage?.prettifiedDescription()}.") - false - } + (rawMessage as? Map<*, *>) + ?.let { it["hash"] as? String } + ?.let { receivedMessageHashValues.add(it) } + ?: false.also { Log.d("Loki", "Missing hash value for message: ${rawMessage?.prettifiedDescription()}.") } } - if (originalMessageHashValues != receivedMessageHashValues && updateStoredHashes) { + if (updateStoredHashes && originalMessageHashValues.containsAll(receivedMessageHashValues)) { database.setReceivedMessageHashValues(publicKey, receivedMessageHashValues, namespace) } return result } - private fun parseEnvelopes(rawMessages: List<*>): List> { - return rawMessages.mapNotNull { rawMessage -> + private fun parseEnvelopes(rawMessages: List<*>): List> = + rawMessages.mapNotNull { rawMessage -> val rawMessageAsJSON = rawMessage as? Map<*, *> val base64EncodedData = rawMessageAsJSON?.get("data") as? String val data = base64EncodedData?.let { Base64.decode(it) } + + data?.runCatching(MessageWrapper::unwrap) + ?.map { it to rawMessageAsJSON["hash"] as? String } + ?.onFailure { Log.d("Loki", "Failed to unwrap data for message: ${rawMessage.prettifiedDescription()}.") } + if (data != null) { try { - Pair(MessageWrapper.unwrap(data), rawMessageAsJSON.get("hash") as? String) + MessageWrapper.unwrap(data) to rawMessageAsJSON["hash"] as? String } catch (e: Exception) { Log.d("Loki", "Failed to unwrap data for message: ${rawMessage.prettifiedDescription()}.") null @@ -896,12 +715,11 @@ object SnodeAPI { null } } - } @Suppress("UNCHECKED_CAST") private fun parseDeletions(userPublicKey: String, timestamp: Long, rawResponse: RawResponse): Map { val swarms = rawResponse["swarm"] as? Map ?: return mapOf() - val result = swarms.mapNotNull { (hexSnodePublicKey, rawJSON) -> + return swarms.mapNotNull { (hexSnodePublicKey, rawJSON) -> val json = rawJSON as? Map ?: return@mapNotNull null val isFailed = json["failed"] as? Boolean ?: false val statusCode = json["code"] as? String @@ -917,14 +735,13 @@ object SnodeAPI { val message = sequenceOf(userPublicKey, "$timestamp").plus(hashes).toByteArray() sodium.cryptoSignVerifyDetached(Base64.decode(signature), message, message.size, snodePublicKey.asBytes) } - } - return result.toMap() + }.toMap() } // endregion // Error Handling - internal fun handleSnodeError(statusCode: Int, json: Map<*, *>?, snode: Snode, publicKey: String? = null): Exception? { + internal fun handleSnodeError(statusCode: Int, json: Map<*, *>?, snode: Snode, publicKey: String? = null): Throwable? = runCatching { fun handleBadSnode() { val oldFailureCount = snodeFailureCount[snode] ?: 0 val newFailureCount = oldFailureCount + 1 @@ -932,56 +749,43 @@ object SnodeAPI { Log.d("Loki", "Couldn't reach snode at $snode; setting failure count to $newFailureCount.") if (newFailureCount >= snodeFailureThreshold) { Log.d("Loki", "Failure threshold reached for: $snode; dropping it.") - if (publicKey != null) { - dropSnodeFromSwarmIfNeeded(snode, publicKey) - } - snodePool = snodePool.toMutableSet().minus(snode).toSet() + publicKey?.let { dropSnodeFromSwarmIfNeeded(snode, it) } + snodePool -= snode Log.d("Loki", "Snode pool count: ${snodePool.count()}.") snodeFailureCount[snode] = 0 } } when (statusCode) { - 400, 500, 502, 503 -> { // Usually indicates that the snode isn't up to date - handleBadSnode() - } + // Usually indicates that the snode isn't up to date + 400, 500, 502, 503 -> handleBadSnode() 406 -> { Log.d("Loki", "The user's clock is out of sync with the service node network.") broadcaster.broadcast("clockOutOfSync") - return Error.ClockOutOfSync + throw Error.ClockOutOfSync } 421 -> { // The snode isn't associated with the given public key anymore if (publicKey != null) { - fun invalidateSwarm() { - Log.d("Loki", "Invalidating swarm for: $publicKey.") - dropSnodeFromSwarmIfNeeded(snode, publicKey) - } - if (json != null) { - val snodes = parseSnodes(json) - if (snodes.isNotEmpty()) { - database.setSwarm(publicKey, snodes.toSet()) - } else { - invalidateSwarm() + json?.let(::parseSnodes) + ?.takeIf { it.isNotEmpty() } + ?.let { database.setSwarm(publicKey, it.toSet()) } + ?: run { + Log.d("Loki", "Invalidating swarm for: $publicKey.") + dropSnodeFromSwarmIfNeeded(snode, publicKey) } - } else { - invalidateSwarm() - } - } else { - Log.d("Loki", "Got a 421 without an associated public key.") - } + } else Log.d("Loki", "Got a 421 without an associated public key.") } 404 -> { Log.d("Loki", "404, probably no file found") - return Error.Generic + throw Error.Generic } else -> { handleBadSnode() Log.d("Loki", "Unhandled response code: ${statusCode}.") - return Error.Generic + throw Error.Generic } } - return null - } + }.exceptionOrNull() } // Type Aliases diff --git a/libsignal/src/main/java/org/session/libsignal/utilities/Base64.java b/libsignal/src/main/java/org/session/libsignal/utilities/Base64.java index 3ff38f76d3b..35ec22e0e09 100644 --- a/libsignal/src/main/java/org/session/libsignal/utilities/Base64.java +++ b/libsignal/src/main/java/org/session/libsignal/utilities/Base64.java @@ -1,5 +1,7 @@ package org.session.libsignal.utilities; +import androidx.annotation.NonNull; + /** *

Encodes and decodes to and from Base64 notation.

*

Homepage: http://iharder.net/base64.

@@ -714,7 +716,7 @@ public static String encodeObject( java.io.Serializable serializableObject, int * @throws NullPointerException if source array is null * @since 1.4 */ - public static String encodeBytes( byte[] source ) { + public static String encodeBytes(@NonNull byte[] source ) { // Since we're not going to have the GZIP encoding turned on, // we're not going to have an java.io.IOException thrown, so // we should not force the user to have to catch it. From 48554fada7676fa6d9899be540297bb9d7afa0da Mon Sep 17 00:00:00 2001 From: ThomasSession Date: Sat, 3 Aug 2024 09:42:02 +1000 Subject: [PATCH 19/50] [SES-2494] Applying proper styling for message request --- .../securesms/home/HomeAdapter.kt | 1 - .../layout/view_message_request_banner.xml | 28 ++++--------------- 2 files changed, 5 insertions(+), 24 deletions(-) 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 47b62521654..453d15050a5 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/home/HomeAdapter.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/home/HomeAdapter.kt @@ -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/res/layout/view_message_request_banner.xml b/app/src/main/res/layout/view_message_request_banner.xml index c03e9c1cdfb..e4b7d032321 100644 --- a/app/src/main/res/layout/view_message_request_banner.xml +++ b/app/src/main/res/layout/view_message_request_banner.xml @@ -4,7 +4,7 @@ xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="wrap_content" - android:background="@drawable/conversation_view_background" + android:background="@drawable/conversation_unread_background" android:contentDescription="@string/AccessibilityId_message_request_banner" android:gravity="center_vertical" android:orientation="horizontal" @@ -19,7 +19,8 @@ android:layout_marginStart="@dimen/medium_spacing" android:padding="10dp" android:src="@drawable/ic_outline_message_requests_24" - app:circleColor="#585858" + android:tint="?unreadIndicatorTextColor" + app:circleColor="?unreadIndicatorBackgroundColor" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> @@ -47,7 +48,7 @@ android:layout_height="20dp" android:layout_marginStart="4dp" android:background="@drawable/circle_tintable" - android:backgroundTint="#585858" + android:backgroundTint="?unreadIndicatorBackgroundColor" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintStart_toEndOf="@id/conversationViewDisplayNameTextView" app:layout_constraintTop_toTopOf="parent"> @@ -57,30 +58,11 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerInParent="true" - android:textColor="@color/white" + android:textColor="?unreadIndicatorTextColor" android:textSize="@dimen/very_small_font_size" android:textStyle="bold" tools:text="8" /> - - From c1d40cdbe73605c02a4718d0600cb419177cd5b5 Mon Sep 17 00:00:00 2001 From: bemusementpark Date: Sat, 3 Aug 2024 20:19:29 +0930 Subject: [PATCH 20/50] Optimise SnodesAPI --- .../org/session/libsession/snode/SnodeAPI.kt | 347 ++++++++---------- .../org/session/libsession/utilities/Util.kt | 21 ++ 2 files changed, 180 insertions(+), 188 deletions(-) diff --git a/libsession/src/main/java/org/session/libsession/snode/SnodeAPI.kt b/libsession/src/main/java/org/session/libsession/snode/SnodeAPI.kt index 8e19234b0d5..6d1ffaa5c0f 100644 --- a/libsession/src/main/java/org/session/libsession/snode/SnodeAPI.kt +++ b/libsession/src/main/java/org/session/libsession/snode/SnodeAPI.kt @@ -18,6 +18,8 @@ import nl.komponents.kovenant.task import org.session.libsession.messaging.MessagingModuleConfiguration import org.session.libsession.messaging.utilities.MessageWrapper import org.session.libsession.messaging.utilities.SodiumUtilities.sodium +import org.session.libsession.utilities.buildMutableMap +import org.session.libsession.utilities.mapValuesNotNull import org.session.libsession.utilities.toByteArray import org.session.libsignal.crypto.getRandomElement import org.session.libsignal.database.LokiAPIDatabaseProtocol @@ -73,20 +75,18 @@ object SnodeAPI { // Use port 4433 if the API level can handle the network security configuration and enforce pinned certificates private val seedNodePort = if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) 443 else 4443 - private const val snodeFailureThreshold = 3 - private const val useOnionRequests = true - private const val useTestnet = false - private val seedNodePool = if (useTestnet) { - setOf( "http://public.loki.foundation:38157" ) - } else { - setOf( - "https://seed1.getsession.org:$seedNodePort", - "https://seed2.getsession.org:$seedNodePort", - "https://seed3.getsession.org:$seedNodePort", - ) - } + private val seedNodePool = if (useTestnet) setOf( + "http://public.loki.foundation:38157" + ) else setOf( + "https://seed1.getsession.org:$seedNodePort", + "https://seed2.getsession.org:$seedNodePort", + "https://seed3.getsession.org:$seedNodePort", + ) + + private const val snodeFailureThreshold = 3 + private const val useOnionRequests = true private const val KEY_IP = "public_ip" private const val KEY_PORT = "storage_port" @@ -121,48 +121,45 @@ object SnodeAPI { parameters: Map, publicKey: String? = null, version: Version = Version.V3 - ): RawResponsePromise = if (useOnionRequests) OnionRequestAPI.sendOnionRequest(method, parameters, snode, version, publicKey).map { - val body = it.body ?: throw Error.Generic - JsonUtil.fromJson(body, Map::class.java) - } else task { - val payload = mapOf( "method" to method.rawValue, "params" to parameters ) - try { - val url = "${snode.address}:${snode.port}/storage_rpc/v1" - val response = HTTP.execute(HTTP.Verb.POST, url, payload).toString() - JsonUtil.fromJson(response, Map::class.java) - } catch (exception: Exception) { - (exception as? HTTP.HTTPRequestFailedException)?.run { - handleSnodeError(statusCode, json, snode, publicKey) - // TODO Check if we meant to throw the error returned by handleSnodeError - throw exception + ): RawResponsePromise = when { + useOnionRequests -> OnionRequestAPI.sendOnionRequest(method, parameters, snode, version, publicKey).map { + JsonUtil.fromJson(it.body ?: throw Error.Generic, Map::class.java) + } + else -> task { + HTTP.execute( + HTTP.Verb.POST, + url = "${snode.address}:${snode.port}/storage_rpc/v1", + parameters = buildMap { + this["method"] = method.rawValue + this["params"] = parameters } - Log.d("Loki", "Unhandled exception: $exception.") - throw exception + ).toString().let { + JsonUtil.fromJson(it, Map::class.java) + } + }.fail { e -> + when (e) { + is HTTP.HTTPRequestFailedException -> handleSnodeError(e.statusCode, e.json, snode, publicKey) + else -> Log.d("Loki", "Unhandled exception: $e.") } } + } + + private val GET_RANDOM_SNODE_PARAMS = buildMap { + this["method"] = "get_n_service_nodes" + this["params"] = buildMap { + this["active_only"] = true + this["fields"] = sequenceOf(KEY_IP, KEY_PORT, KEY_X25519, KEY_ED25519, KEY_VERSION).associateWith { true } + } + } internal fun getRandomSnode(): Promise = - snodePool.takeIf { it.size >= minimumSnodePoolCount }?.let { Promise.of(it.getRandomElement()) } ?: task { + snodePool.takeIf { it.size >= minimumSnodePoolCount }?.getRandomElement()?.let { Promise.of(it) } ?: task { val target = seedNodePool.random() - val url = "$target/json_rpc" Log.d("Loki", "Populating snode pool using: $target.") - val parameters = mapOf( - "method" to "get_n_service_nodes", - "params" to mapOf( - "active_only" to true, - "fields" to mapOf( - KEY_IP to true, KEY_PORT to true, - KEY_X25519 to true, KEY_ED25519 to true, - KEY_VERSION to true - ) - ) - ) - val response = HTTP.execute(HTTP.Verb.POST, url, parameters, useSeedNodeConnection = true) - val json = try { - JsonUtil.fromJson(response, Map::class.java) - } catch (exception: Exception) { - mapOf( "result" to response.toString()) - } + val url = "$target/json_rpc" + val response = HTTP.execute(HTTP.Verb.POST, url, GET_RANDOM_SNODE_PARAMS, useSeedNodeConnection = true) + val json = runCatching { JsonUtil.fromJson(response, Map::class.java) }.getOrNull() + ?: buildMap { this["result"] = response.toString() } val intermediate = json["result"] as? Map<*, *> ?: throw Error.Generic .also { Log.d("Loki", "Failed to update snode pool, intermediate was null.") } val rawSnodes = intermediate["service_node_states"] as? List<*> ?: throw Error.Generic @@ -180,7 +177,7 @@ object SnodeAPI { ).also { if (it == null) Log.d("Loki", "Failed to parse: ${rawSnode.prettifiedDescription()}.") } }.toSet().also { Log.d("Loki", "Persisting snode pool to database.") - this.snodePool = it + snodePool = it }.runCatching { getRandomElement() }.onFailure { Log.d("Loki", "Got an empty snode pool from: $target.") throw SnodeAPI.Error.Generic @@ -220,10 +217,13 @@ object SnodeAPI { } val base64EncodedNameHash = Base64.encodeBytes(nameHash) // Ask 3 different snodes for the Account ID associated with the given name hash - val parameters = mapOf( - "endpoint" to "ons_resolve", - "params" to mapOf( "type" to 0, "name_hash" to base64EncodedNameHash ) - ) + val parameters = buildMap { + this["endpoint"] = "ons_resolve" + this["params"] = buildMap { + this["type"] = 0 + this["name_hash"] = base64EncodedNameHash + } + } val promises = List(validationCount) { getRandomSnode().bind { snode -> retryIfNeeded(maxRetryCount) { @@ -232,10 +232,9 @@ object SnodeAPI { } } return all(promises).map { results -> - val accountIDs = mutableListOf() - for (json in results) { - val intermediate = json["result"] as? Map<*, *> - val hexEncodedCiphertext = intermediate?.get("encrypted_value") as? String ?: throw Error.Generic + results.map { json -> + val intermediate = json["result"] as? Map<*, *> ?: throw Error.Generic + val hexEncodedCiphertext = intermediate["encrypted_value"] as? String ?: throw Error.Generic val ciphertext = Hex.fromStringCondensed(hexEncodedCiphertext) val isArgon2Based = (intermediate["nonce"] == null) if (isArgon2Based) { @@ -251,7 +250,7 @@ object SnodeAPI { if (!sodium.cryptoSecretBoxOpenEasy(accountIDAsData, ciphertext, ciphertext.size.toLong(), nonce, key)) { throw Error.DecryptionFailed } - accountIDs.add(Hex.toStringCondensed(accountIDAsData)) + Hex.toStringCondensed(accountIDAsData) } else { val hexEncodedNonce = intermediate["nonce"] as? String ?: throw Error.Generic val nonce = Hex.fromStringCondensed(hexEncodedNonce) @@ -263,10 +262,9 @@ object SnodeAPI { if (!sodium.cryptoAeadXChaCha20Poly1305IetfDecrypt(accountIDAsData, null, null, ciphertext, ciphertext.size.toLong(), null, 0, nonce, key)) { throw Error.DecryptionFailed } - accountIDs.add(Hex.toStringCondensed(accountIDAsData)) + Hex.toStringCondensed(accountIDAsData) } - } - accountIDs.takeIf { it.size == validationCount && it.toSet().size == 1 }?.first() + }.takeIf { it.size == validationCount && it.toSet().size == 1 }?.first() ?: throw Error.ValidationFailed } } @@ -274,30 +272,31 @@ object SnodeAPI { fun getSwarm(publicKey: String): Promise, Exception> = database.getSwarm(publicKey)?.takeIf { it.size >= minimumSwarmSnodeCount }?.let(Promise.Companion::of) ?: getRandomSnode().bind { - invoke(Snode.Method.GetSwarm, it, parameters = mapOf( "pubKey" to publicKey ), publicKey) + invoke(Snode.Method.GetSwarm, it, parameters = buildMap { this["pubKey"] = publicKey }, publicKey) }.map { parseSnodes(it).toSet() }.success { database.setSwarm(publicKey, it) } - private fun signAndEncode(data: ByteArray, userED25519KeyPair: KeyPair) = sign(data, userED25519KeyPair).let(Base64::encodeBytes) + private fun signAndEncodeCatching(data: ByteArray, userED25519KeyPair: KeyPair): Result = + runCatching { signAndEncode(data, userED25519KeyPair) } + private fun signAndEncode(data: ByteArray, userED25519KeyPair: KeyPair): String = + sign(data, userED25519KeyPair).let(Base64::encodeBytes) private fun sign(data: ByteArray, userED25519KeyPair: KeyPair): ByteArray = ByteArray(Sign.BYTES).also { - sodium.cryptoSignDetached( - it, - data, - data.size.toLong(), - userED25519KeyPair.secretKey.asBytes - ) + sodium.cryptoSignDetached(it, data, data.size.toLong(), userED25519KeyPair.secretKey.asBytes) } fun getRawMessages(snode: Snode, publicKey: String, requiresAuth: Boolean = true, namespace: Int = 0): RawResponsePromise { // Get last message hash val lastHashValue = database.getLastMessageHashValue(snode, publicKey, namespace) ?: "" - val parameters = mutableMapOf( - "pubKey" to publicKey, - "last_hash" to lastHashValue, - ) + val parameters = buildMutableMap { + this["pubKey"] = publicKey + this["last_hash"] = lastHashValue + // If the namespace is default (0) here it will be implicitly read as 0 on the storage server + // we only need to specify it explicitly if we want to (in future) or if it is non-zero + namespace.takeIf { it != 0 }?.let { this["namespace"] = it } + } // Construct signature if (requiresAuth) { val userED25519KeyPair = try { @@ -311,23 +310,13 @@ object SnodeAPI { val ed25519PublicKey = userED25519KeyPair.publicKey.asHexString val verificationData = buildString { append("retrieve") - if (namespace != 0) append(namespace) + namespace.takeIf { it != 0 }?.let(::append) append(timestamp) }.toByteArray() - val signature = try { - signAndEncode(verificationData, userED25519KeyPair) - } catch (exception: Exception) { - return Promise.ofFail(Error.SigningFailed) - } + parameters["signature"] = signAndEncodeCatching(verificationData, userED25519KeyPair).getOrNull() + ?: return Promise.ofFail(Error.SigningFailed) parameters["timestamp"] = timestamp parameters["pubkey_ed25519"] = ed25519PublicKey - parameters["signature"] = signature - } - - // If the namespace is default (0) here it will be implicitly read as 0 on the storage server - // we only need to specify it explicitly if we want to (in future) or if it is non-zero - if (namespace != 0) { - parameters["namespace"] = namespace } // Make the request @@ -341,11 +330,8 @@ object SnodeAPI { val userED25519KeyPair = runCatching { MessagingModuleConfiguration.shared.getUserED25519KeyPair() }.getOrNull() ?: return null val verificationData = "store$namespace$messageTimestamp".toByteArray() - val signature = try { - signAndEncode(verificationData, userED25519KeyPair) - } catch (e: Exception) { - Log.e("Loki", "Signing data failed with user secret key", e) - return null + val signature = signAndEncodeCatching(verificationData, userED25519KeyPair).run { + getOrNull() ?: return null.also { Log.e("Loki", "Signing data failed with user secret key", exceptionOrNull()) } } val params = buildMap { @@ -478,13 +464,13 @@ object SnodeAPI { Log.e("Loki", "Signing data failed with user secret key", e) return@retryIfNeeded Promise.ofFail(e) } - val params = mapOf( - "pubkey" to publicKey, - "messages" to hashes, - "timestamp" to timestamp, - "pubkey_ed25519" to ed25519PublicKey, - "signature" to signature - ) + val params = buildMap { + this["pubkey"] = publicKey + this["messages"] = hashes + this["timestamp"] = timestamp + this["pubkey_ed25519"] = ed25519PublicKey + this["signature"] = signature + } getSingleTargetSnode(publicKey) bind { snode -> invoke(Snode.Method.GetExpiries, snode, params, publicKey) } @@ -578,7 +564,7 @@ object SnodeAPI { } } - fun deleteMessage(publicKey: String, serverHashes: List): Promise, Exception> = + fun deleteMessage(publicKey: String, serverHashes: List): Promise, Exception> = retryIfNeeded(maxRetryCount) { val module = MessagingModuleConfiguration.shared val userED25519KeyPair = module.getUserED25519KeyPair() ?: return@retryIfNeeded Promise.ofFail(Error.NoKeyPair) @@ -586,35 +572,40 @@ object SnodeAPI { getSingleTargetSnode(publicKey).bind { snode -> retryIfNeeded(maxRetryCount) { val verificationData = sequenceOf(Snode.Method.DeleteMessage.rawValue).plus(serverHashes).toByteArray() - val deleteMessageParams = mapOf( - "pubkey" to userPublicKey, - "pubkey_ed25519" to userED25519KeyPair.publicKey.asHexString, - "messages" to serverHashes, - "signature" to signAndEncode(verificationData, userED25519KeyPair) - ) + val deleteMessageParams = buildMap { + this["pubkey"] = userPublicKey + this["pubkey_ed25519"] = userED25519KeyPair.publicKey.asHexString + this["messages"] = serverHashes + this["signature"] = signAndEncode(verificationData, userED25519KeyPair) + } invoke(Snode.Method.DeleteMessage, snode, deleteMessageParams, publicKey).map { rawResponse -> val swarms = rawResponse["swarm"] as? Map ?: return@map mapOf() - val result = swarms.mapNotNull { (hexSnodePublicKey, rawJSON) -> - val json = rawJSON as? Map ?: return@mapNotNull null - val isFailed = json["failed"] as? Boolean ?: false - val statusCode = json["code"] as? String - val reason = json["reason"] as? String - hexSnodePublicKey to if (isFailed) { - Log.e("Loki", "Failed to delete messages from: $hexSnodePublicKey due to error: $reason ($statusCode).") - false - } else { - val hashes = json["deleted"] as List // Hashes of deleted messages - val signature = json["signature"] as String - val snodePublicKey = Key.fromHexString(hexSnodePublicKey) - // The signature looks like ( PUBKEY_HEX || RMSG[0] || ... || RMSG[N] || DMSG[0] || ... || DMSG[M] ) - val message = sequenceOf(userPublicKey).plus(serverHashes).plus(hashes).toByteArray() - sodium.cryptoSignVerifyDetached(Base64.decode(signature), message, message.size, snodePublicKey.asBytes) + swarms.mapValuesNotNull { (hexSnodePublicKey, rawJSON) -> + (rawJSON as? Map)?.let { json -> + val isFailed = json["failed"] as? Boolean ?: false + val statusCode = json["code"] as? String + val reason = json["reason"] as? String + + if (isFailed) { + Log.e("Loki", "Failed to delete messages from: $hexSnodePublicKey due to error: $reason ($statusCode).") + false + } else { + // Hashes of deleted messages + val hashes = json["deleted"] as List + val signature = json["signature"] as String + val snodePublicKey = Key.fromHexString(hexSnodePublicKey) + // The signature looks like ( PUBKEY_HEX || RMSG[0] || ... || RMSG[N] || DMSG[0] || ... || DMSG[M] ) + val message = sequenceOf(userPublicKey).plus(serverHashes).plus(hashes).toByteArray() + sodium.cryptoSignVerifyDetached( + Base64.decode(signature), + message, + message.size, + snodePublicKey.asBytes + ) + } } } - return@map result.toMap() - }.fail { e -> - Log.e("Loki", "Failed to delete messages", e) - } + }.fail { e -> Log.e("Loki", "Failed to delete messages", e) } } } } @@ -643,18 +634,16 @@ object SnodeAPI { retryIfNeeded(maxRetryCount) { getNetworkTime(snode).bind { (_, timestamp) -> val verificationData = (Snode.Method.DeleteAll.rawValue + Namespace.ALL + timestamp.toString()).toByteArray() - val deleteMessageParams = mapOf( - "pubkey" to userPublicKey, - "pubkey_ed25519" to userED25519KeyPair.publicKey.asHexString, - "timestamp" to timestamp, - "signature" to signAndEncode(verificationData, userED25519KeyPair), - "namespace" to Namespace.ALL, - ) - invoke(Snode.Method.DeleteAll, snode, deleteMessageParams, userPublicKey).map { - rawResponse -> parseDeletions(userPublicKey, timestamp, rawResponse) - }.fail { e -> - Log.e("Loki", "Failed to clear data", e) + val deleteMessageParams = buildMap { + this["pubkey"] = userPublicKey + this["pubkey_ed25519"] = userED25519KeyPair.publicKey.asHexString + this["timestamp"] = timestamp + this["signature"] = signAndEncode(verificationData, userED25519KeyPair) + this["namespace"] = Namespace.ALL } + invoke(Snode.Method.DeleteAll, snode, deleteMessageParams, userPublicKey) + .map { rawResponse -> parseDeletions(userPublicKey, timestamp, rawResponse) } + .fail { e -> Log.e("Loki", "Failed to clear data", e) } } } } @@ -662,69 +651,53 @@ object SnodeAPI { fun parseRawMessagesResponse(rawResponse: RawResponse, snode: Snode, publicKey: String, namespace: Int = 0, updateLatestHash: Boolean = true, updateStoredHashes: Boolean = true): List> = (rawResponse["messages"] as? List<*>)?.let { messages -> - if (updateLatestHash) { - updateLastMessageHashValueIfPossible(snode, publicKey, messages, namespace) - } + if (updateLatestHash) updateLastMessageHashValueIfPossible(snode, publicKey, messages, namespace) removeDuplicates(publicKey, messages, namespace, updateStoredHashes).let(::parseEnvelopes) } ?: listOf() fun updateLastMessageHashValueIfPossible(snode: Snode, publicKey: String, rawMessages: List<*>, namespace: Int) { val lastMessageAsJSON = rawMessages.lastOrNull() as? Map<*, *> val hashValue = lastMessageAsJSON?.get("hash") as? String - if (hashValue != null) { - database.setLastMessageHashValue(snode, publicKey, hashValue, namespace) - } else if (rawMessages.isNotEmpty()) { - Log.d("Loki", "Failed to update last message hash value from: ${rawMessages.prettifiedDescription()}.") + when { + hashValue != null -> database.setLastMessageHashValue(snode, publicKey, hashValue, namespace) + rawMessages.isNotEmpty() -> Log.d("Loki", "Failed to update last message hash value from: ${rawMessages.prettifiedDescription()}.") } } fun removeDuplicates(publicKey: String, rawMessages: List<*>, namespace: Int, updateStoredHashes: Boolean): List<*> { val originalMessageHashValues = database.getReceivedMessageHashValues(publicKey, namespace) ?: emptySet() val receivedMessageHashValues = originalMessageHashValues.toMutableSet() - val result = rawMessages.filter { rawMessage -> + return rawMessages.filter { rawMessage -> (rawMessage as? Map<*, *>) ?.let { it["hash"] as? String } ?.let { receivedMessageHashValues.add(it) } ?: false.also { Log.d("Loki", "Missing hash value for message: ${rawMessage?.prettifiedDescription()}.") } + }.also { + if (updateStoredHashes && originalMessageHashValues.containsAll(receivedMessageHashValues)) { + database.setReceivedMessageHashValues(publicKey, receivedMessageHashValues, namespace) + } } - if (updateStoredHashes && originalMessageHashValues.containsAll(receivedMessageHashValues)) { - database.setReceivedMessageHashValues(publicKey, receivedMessageHashValues, namespace) - } - return result } - private fun parseEnvelopes(rawMessages: List<*>): List> = - rawMessages.mapNotNull { rawMessage -> - val rawMessageAsJSON = rawMessage as? Map<*, *> - val base64EncodedData = rawMessageAsJSON?.get("data") as? String - val data = base64EncodedData?.let { Base64.decode(it) } - - data?.runCatching(MessageWrapper::unwrap) - ?.map { it to rawMessageAsJSON["hash"] as? String } - ?.onFailure { Log.d("Loki", "Failed to unwrap data for message: ${rawMessage.prettifiedDescription()}.") } - - if (data != null) { - try { - MessageWrapper.unwrap(data) to rawMessageAsJSON["hash"] as? String - } catch (e: Exception) { - Log.d("Loki", "Failed to unwrap data for message: ${rawMessage.prettifiedDescription()}.") - null - } - } else { - Log.d("Loki", "Failed to decode data for message: ${rawMessage?.prettifiedDescription()}.") - null - } - } + private fun parseEnvelopes(rawMessages: List<*>): List> = rawMessages.mapNotNull { rawMessage -> + val rawMessageAsJSON = rawMessage as? Map<*, *> + val base64EncodedData = rawMessageAsJSON?.get("data") as? String + val data = base64EncodedData?.let { Base64.decode(it) } + + data ?: Log.d("Loki", "Failed to decode data for message: ${rawMessage?.prettifiedDescription()}.") + + data?.runCatching { MessageWrapper.unwrap(this) to rawMessageAsJSON["hash"] as? String } + ?.onFailure { Log.d("Loki", "Failed to unwrap data for message: ${rawMessage.prettifiedDescription()}.") } + ?.getOrNull() + } @Suppress("UNCHECKED_CAST") - private fun parseDeletions(userPublicKey: String, timestamp: Long, rawResponse: RawResponse): Map { - val swarms = rawResponse["swarm"] as? Map ?: return mapOf() - return swarms.mapNotNull { (hexSnodePublicKey, rawJSON) -> - val json = rawJSON as? Map ?: return@mapNotNull null - val isFailed = json["failed"] as? Boolean ?: false - val statusCode = json["code"] as? String - val reason = json["reason"] as? String - hexSnodePublicKey to if (isFailed) { + private fun parseDeletions(userPublicKey: String, timestamp: Long, rawResponse: RawResponse): Map = + (rawResponse["swarm"] as? Map)?.mapValuesNotNull { (hexSnodePublicKey, rawJSON) -> + val json = rawJSON as? Map ?: return@mapValuesNotNull null + if (json["failed"] as? Boolean == true) { + val reason = json["reason"] as? String + val statusCode = json["code"] as? String Log.e("Loki", "Failed to delete all messages from: $hexSnodePublicKey due to error: $reason ($statusCode).") false } else { @@ -735,8 +708,7 @@ object SnodeAPI { val message = sequenceOf(userPublicKey, "$timestamp").plus(hashes).toByteArray() sodium.cryptoSignVerifyDetached(Base64.decode(signature), message, message.size, snodePublicKey.asBytes) } - }.toMap() - } + } ?: mapOf() // endregion @@ -752,7 +724,7 @@ object SnodeAPI { publicKey?.let { dropSnodeFromSwarmIfNeeded(snode, it) } snodePool -= snode Log.d("Loki", "Snode pool count: ${snodePool.count()}.") - snodeFailureCount[snode] = 0 + snodeFailureCount.remove(snode) } } when (statusCode) { @@ -765,15 +737,14 @@ object SnodeAPI { } 421 -> { // The snode isn't associated with the given public key anymore - if (publicKey != null) { - json?.let(::parseSnodes) - ?.takeIf { it.isNotEmpty() } - ?.let { database.setSwarm(publicKey, it.toSet()) } - ?: run { - Log.d("Loki", "Invalidating swarm for: $publicKey.") - dropSnodeFromSwarmIfNeeded(snode, publicKey) - } - } else Log.d("Loki", "Got a 421 without an associated public key.") + if (publicKey == null) Log.d("Loki", "Got a 421 without an associated public key.") + else json?.let(::parseSnodes) + ?.takeIf { it.isNotEmpty() } + ?.let { database.setSwarm(publicKey, it.toSet()) } + ?: run { + Log.d("Loki", "Invalidating swarm for: $publicKey.") + dropSnodeFromSwarmIfNeeded(snode, publicKey) + } } 404 -> { Log.d("Loki", "404, probably no file found") diff --git a/libsession/src/main/java/org/session/libsession/utilities/Util.kt b/libsession/src/main/java/org/session/libsession/utilities/Util.kt index d47754b7ed6..bc533d235fa 100644 --- a/libsession/src/main/java/org/session/libsession/utilities/Util.kt +++ b/libsession/src/main/java/org/session/libsession/utilities/Util.kt @@ -402,6 +402,12 @@ fun Iterable.associateByNotNull( for(e in this) { it[keySelector(e) ?: continue] = valueTransform(e) ?: continue } } +fun Map.mapValuesNotNull( + valueTransform: (Map.Entry) -> W? +): Map = mutableMapOf().also { + for(e in this) { it[e.key] = valueTransform(e) ?: continue } +} + /** * Groups elements of the original collection by the key returned by the given [keySelector] function * applied to each element and returns a map where each group key is associated with a list of @@ -413,6 +419,21 @@ inline fun Iterable.groupByNotNull(keySelector: (E) -> K?): Map keySelector(e)?.let { k -> it.getOrPut(k) { mutableListOf() } += e } } } +/** + * Analogous to [buildMap], this function creates a [MutableMap] and populates it using the given [action]. + */ +inline fun buildMutableMap(action: MutableMap.() -> Unit): MutableMap = + mutableMapOf().apply(action) + +/** + * Converts a list of Pairs into a Map, filtering out any Pairs where the value is null. + * + * @param pairs The list of Pairs to convert. + * @return A Map with non-null values. + */ +fun Iterable>.toMapNotNull(): Map = + associateByNotNull(Pair::first, Pair::second) + fun Sequence.toByteArray(): ByteArray = ByteArrayOutputStream().use { output -> forEach { it.byteInputStream().use { input -> input.copyTo(output) } } output.toByteArray() From 3c8302f7a4ce226096b2cac79b0c81d11fee949a Mon Sep 17 00:00:00 2001 From: bemusementpark Date: Sat, 3 Aug 2024 21:19:31 +0930 Subject: [PATCH 21/50] Optimise SnodeAPI further --- .../java/org/session/libsession/snode/SnodeAPI.kt | 11 +++++------ .../java/org/session/libsignal/utilities/Snode.kt | 6 +++--- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/libsession/src/main/java/org/session/libsession/snode/SnodeAPI.kt b/libsession/src/main/java/org/session/libsession/snode/SnodeAPI.kt index 6d1ffaa5c0f..4d59669d1ce 100644 --- a/libsession/src/main/java/org/session/libsession/snode/SnodeAPI.kt +++ b/libsession/src/main/java/org/session/libsession/snode/SnodeAPI.kt @@ -455,7 +455,7 @@ object SnodeAPI { val hashes = messageHashes.takeIf { it.size != 1 } ?: (messageHashes + "///////////////////////////////////////////") // TODO remove this when bug is fixed on nodes. return retryIfNeeded(maxRetryCount) { val timestamp = System.currentTimeMillis() + clockOffset - val signData = "${Snode.Method.GetExpiries.rawValue}$timestamp${hashes.joinToString(separator = "")}".toByteArray() + val signData = sequenceOf(Snode.Method.GetExpiries.rawValue).plus(timestamp.toString()).plus(hashes).toByteArray() val ed25519PublicKey = userEd25519KeyPair.publicKey.asHexString val signature = try { @@ -499,7 +499,7 @@ object SnodeAPI { val shortenOrExtend = if (extend) "extend" else if (shorten) "shorten" else "" - val signData = "${Snode.Method.Expire.rawValue}$shortenOrExtend$newExpiry${messageHashes.joinToString(separator = "")}".toByteArray() + val signData = sequenceOf(Snode.Method.Expire.rawValue).plus(shortenOrExtend).plus(newExpiry.toString()).plus(messageHashes).toByteArray() val signature = try { signAndEncode(signData, userEd25519KeyPair) @@ -633,7 +633,7 @@ object SnodeAPI { getSingleTargetSnode(userPublicKey).bind { snode -> retryIfNeeded(maxRetryCount) { getNetworkTime(snode).bind { (_, timestamp) -> - val verificationData = (Snode.Method.DeleteAll.rawValue + Namespace.ALL + timestamp.toString()).toByteArray() + val verificationData = sequenceOf(Snode.Method.DeleteAll.rawValue, Namespace.ALL, timestamp.toString()).toByteArray() val deleteMessageParams = buildMap { this["pubkey"] = userPublicKey this["pubkey_ed25519"] = userED25519KeyPair.publicKey.asHexString @@ -722,9 +722,8 @@ object SnodeAPI { if (newFailureCount >= snodeFailureThreshold) { Log.d("Loki", "Failure threshold reached for: $snode; dropping it.") publicKey?.let { dropSnodeFromSwarmIfNeeded(snode, it) } - snodePool -= snode - Log.d("Loki", "Snode pool count: ${snodePool.count()}.") - snodeFailureCount.remove(snode) + snodePool = (snodePool - snode).also { Log.d("Loki", "Snode pool count: ${it.count()}.") } + snodeFailureCount -= snode } } when (statusCode) { diff --git a/libsignal/src/main/java/org/session/libsignal/utilities/Snode.kt b/libsignal/src/main/java/org/session/libsignal/utilities/Snode.kt index cc123a8527c..f918dbbf730 100644 --- a/libsignal/src/main/java/org/session/libsignal/utilities/Snode.kt +++ b/libsignal/src/main/java/org/session/libsignal/utilities/Snode.kt @@ -52,6 +52,8 @@ class Snode(val address: String, val port: Int, val publicKeySet: KeySet?, val v fun Version(value: String) = CACHE.getOrElse(value) { Snode.Version(value) } + + fun Version(parts: List) = Version(parts.joinToString(".")) } @JvmInline @@ -66,14 +68,12 @@ class Snode(val address: String, val port: Int, val publicKeySet: KeySet?, val v } } - constructor(parts: List): this( + internal constructor(parts: List): this( parts.asSequence() .map { it.toByte().toULong() } .foldToVersionAsULong() ) - constructor(value: Int): this(value.toULong()) - internal constructor(value: String): this( value.splitToSequence(".") .map { it.toULongOrNull() ?: 0UL } From 6e1ed8cc117eb5706c8c4d97a33f1926af33777a Mon Sep 17 00:00:00 2001 From: bemusementpark Date: Sat, 3 Aug 2024 21:41:45 +0930 Subject: [PATCH 22/50] Add SnodeTest --- .../session/libsignal/utilities/SnodeTest.kt | 53 +++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 app/src/test/java/org/session/libsignal/utilities/SnodeTest.kt diff --git a/app/src/test/java/org/session/libsignal/utilities/SnodeTest.kt b/app/src/test/java/org/session/libsignal/utilities/SnodeTest.kt new file mode 100644 index 00000000000..d778db6519d --- /dev/null +++ b/app/src/test/java/org/session/libsignal/utilities/SnodeTest.kt @@ -0,0 +1,53 @@ +package org.session.libsignal.utilities + +import org.hamcrest.MatcherAssert.assertThat +import org.hamcrest.core.IsEqual.equalTo +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.Parameterized + +@RunWith(Parameterized::class) +class SnodeVersionTest( + private val v1: String, + private val v2: String, + private val expectedEqual: Boolean, + private val expectedLessThan: Boolean +) { + companion object { + @JvmStatic + @Parameterized.Parameters(name = "{index}: testVersion({0},{1}) = (equalTo: {2}, lessThan: {3})") + fun data(): Collection> = listOf( + arrayOf("1", "1", true, false), + arrayOf("1", "2", false, true), + arrayOf("2", "1", false, false), + arrayOf("1.0", "1", true, false), + arrayOf("1.0", "1.0.0", true, false), + arrayOf("1.0", "1.0.0.0", true, false), + arrayOf("1.0", "1.0.0.0.0.0", true, false), + arrayOf("2.0", "1.2", false, false), + arrayOf("1.0.0.0", "1.0.0.1", false, true), + // Snode.Version only considers the first 4 integers, so these are equal + arrayOf("1.0.0.0", "1.0.0.0.1", true, false), + arrayOf("1.0.0.1", "1.0.0.1", true, false), + arrayOf("12345.12345.12345.12345", "12345.12345.12345.12345", true, false), + arrayOf("11111.11111.11111.11111", "11111.11111.11111.99999", false, true), + arrayOf("11111.11111.11111.11111", "11111.11111.99999.99999", false, true), + arrayOf("11111.11111.11111.11111", "11111.99999.99999.99999", false, true), + arrayOf("11111.11111.11111.11111", "99999.99999.99999.99999", false, true), + ) + } + + @Test + fun testVersionEqual() { + val version1 = Snode.Version(v1) + val version2 = Snode.Version(v2) + assertThat(version1 == version2, equalTo(expectedEqual)) + } + + @Test + fun testVersionOnePartLessThan() { + val version1 = Snode.Version(v1) + val version2 = Snode.Version(v2) + assertThat(version1 < version2, equalTo(expectedLessThan)) + } +} \ No newline at end of file From 541766099680dbf80c60767813bf8fc2accf9dde Mon Sep 17 00:00:00 2001 From: bemusementpark Date: Sat, 3 Aug 2024 21:52:01 +0930 Subject: [PATCH 23/50] Improve Version to cap parts at 16-bits rather than masking them --- .../java/org/session/libsignal/utilities/SnodeTest.kt | 9 ++++----- .../main/java/org/session/libsignal/utilities/Snode.kt | 2 +- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/app/src/test/java/org/session/libsignal/utilities/SnodeTest.kt b/app/src/test/java/org/session/libsignal/utilities/SnodeTest.kt index d778db6519d..aed54fd0d31 100644 --- a/app/src/test/java/org/session/libsignal/utilities/SnodeTest.kt +++ b/app/src/test/java/org/session/libsignal/utilities/SnodeTest.kt @@ -29,11 +29,10 @@ class SnodeVersionTest( // Snode.Version only considers the first 4 integers, so these are equal arrayOf("1.0.0.0", "1.0.0.0.1", true, false), arrayOf("1.0.0.1", "1.0.0.1", true, false), - arrayOf("12345.12345.12345.12345", "12345.12345.12345.12345", true, false), - arrayOf("11111.11111.11111.11111", "11111.11111.11111.99999", false, true), - arrayOf("11111.11111.11111.11111", "11111.11111.99999.99999", false, true), - arrayOf("11111.11111.11111.11111", "11111.99999.99999.99999", false, true), - arrayOf("11111.11111.11111.11111", "99999.99999.99999.99999", false, true), + // parts can be up to 16 bits, around 65,535 + arrayOf("65535.65535.65535.65535", "65535.65535.65535.65535", true, false), + // values higher than this are coerced to 65535 (: + arrayOf("65535.65535.65535.65535", "65535.65535.65535.99999", true, false), ) } diff --git a/libsignal/src/main/java/org/session/libsignal/utilities/Snode.kt b/libsignal/src/main/java/org/session/libsignal/utilities/Snode.kt index f918dbbf730..86f3e26b645 100644 --- a/libsignal/src/main/java/org/session/libsignal/utilities/Snode.kt +++ b/libsignal/src/main/java/org/session/libsignal/utilities/Snode.kt @@ -64,7 +64,7 @@ class Snode(val address: String, val port: Int, val publicKeySet: KeySet?, val v private const val MASK = 0xFFFFUL private fun Sequence.foldToVersionAsULong() = take(4).foldIndexed(0UL) { i, acc, it -> - it and MASK shl (3 - i) * MASK_BITS or acc + it.coerceAtMost(MASK) shl (3 - i) * MASK_BITS or acc } } From 2125502e771e8ff35948e6f0e63a5cf402a3712c Mon Sep 17 00:00:00 2001 From: bemusementpark Date: Sat, 3 Aug 2024 22:08:17 +0930 Subject: [PATCH 24/50] Refactor a few MessagingModuleConfiguration function calls --- .../org/session/libsession/snode/SnodeAPI.kt | 34 ++++++++----------- 1 file changed, 15 insertions(+), 19 deletions(-) diff --git a/libsession/src/main/java/org/session/libsession/snode/SnodeAPI.kt b/libsession/src/main/java/org/session/libsession/snode/SnodeAPI.kt index 4d59669d1ce..4a2a9f2134f 100644 --- a/libsession/src/main/java/org/session/libsession/snode/SnodeAPI.kt +++ b/libsession/src/main/java/org/session/libsession/snode/SnodeAPI.kt @@ -287,6 +287,10 @@ object SnodeAPI { sodium.cryptoSignDetached(it, data, data.size.toLong(), userED25519KeyPair.secretKey.asBytes) } + private fun getUserED25519KeyPairCatchingOrNull() = runCatching { MessagingModuleConfiguration.shared.getUserED25519KeyPair() }.getOrNull() + private fun getUserED25519KeyPair(): KeyPair? = MessagingModuleConfiguration.shared.getUserED25519KeyPair() + private fun getUserPublicKey() = MessagingModuleConfiguration.shared.storage.getUserPublicKey() + fun getRawMessages(snode: Snode, publicKey: String, requiresAuth: Boolean = true, namespace: Int = 0): RawResponsePromise { // Get last message hash val lastHashValue = database.getLastMessageHashValue(snode, publicKey, namespace) ?: "" @@ -300,8 +304,7 @@ object SnodeAPI { // Construct signature if (requiresAuth) { val userED25519KeyPair = try { - MessagingModuleConfiguration.shared.getUserED25519KeyPair() - ?: return Promise.ofFail(Error.NoKeyPair) + getUserED25519KeyPair() ?: return Promise.ofFail(Error.NoKeyPair) } catch (e: Exception) { Log.e("Loki", "Error getting KeyPair", e) return Promise.ofFail(Error.NoKeyPair) @@ -327,7 +330,7 @@ object SnodeAPI { // used for sig generation since it is also the value used in timestamp parameter val messageTimestamp = message.timestamp - val userED25519KeyPair = runCatching { MessagingModuleConfiguration.shared.getUserED25519KeyPair() }.getOrNull() ?: return null + val userED25519KeyPair = getUserED25519KeyPairCatchingOrNull() ?: return null val verificationData = "store$namespace$messageTimestamp".toByteArray() val signature = signAndEncodeCatching(verificationData, userED25519KeyPair).run { @@ -362,11 +365,7 @@ object SnodeAPI { * @param required indicates that *at least one* message in the list is deleted from the server, otherwise it will return 404 */ fun buildAuthenticatedDeleteBatchInfo(publicKey: String, messageHashes: List, required: Boolean = false): SnodeBatchRequestInfo? { - val userEd25519KeyPair = try { - MessagingModuleConfiguration.shared.getUserED25519KeyPair() ?: return null - } catch (e: Exception) { - return null - } + val userEd25519KeyPair = getUserED25519KeyPairCatchingOrNull() ?: return null val ed25519PublicKey = userEd25519KeyPair.publicKey.asHexString val verificationData = sequenceOf("delete").plus(messageHashes).toByteArray() val signature = try { @@ -391,7 +390,7 @@ object SnodeAPI { fun buildAuthenticatedRetrieveBatchRequest(snode: Snode, publicKey: String, namespace: Int = 0, maxSize: Int? = null): SnodeBatchRequestInfo? { val lastHashValue = database.getLastMessageHashValue(snode, publicKey, namespace) ?: "" - val userEd25519KeyPair = runCatching { MessagingModuleConfiguration.shared.getUserED25519KeyPair() }.getOrNull() ?: return null + val userEd25519KeyPair = getUserED25519KeyPairCatchingOrNull() ?: return null val ed25519PublicKey = userEd25519KeyPair.publicKey.asHexString val timestamp = System.currentTimeMillis() + clockOffset val verificationData = if (namespace == 0) "retrieve$timestamp".toByteArray() @@ -451,7 +450,7 @@ object SnodeAPI { } fun getExpiries(messageHashes: List, publicKey: String) : RawResponsePromise { - val userEd25519KeyPair = MessagingModuleConfiguration.shared.getUserED25519KeyPair() ?: return Promise.ofFail(NullPointerException("No user key pair")) + val userEd25519KeyPair = getUserED25519KeyPairCatchingOrNull() ?: return Promise.ofFail(NullPointerException("No user key pair")) val hashes = messageHashes.takeIf { it.size != 1 } ?: (messageHashes + "///////////////////////////////////////////") // TODO remove this when bug is fixed on nodes. return retryIfNeeded(maxRetryCount) { val timestamp = System.currentTimeMillis() + clockOffset @@ -495,7 +494,7 @@ object SnodeAPI { extend: Boolean = false, shorten: Boolean = false ): Map? { - val userEd25519KeyPair = MessagingModuleConfiguration.shared.getUserED25519KeyPair() ?: return null + val userEd25519KeyPair = getUserED25519KeyPairCatchingOrNull() ?: return null val shortenOrExtend = if (extend) "extend" else if (shorten) "shorten" else "" @@ -535,8 +534,7 @@ object SnodeAPI { fun sendMessage(message: SnodeMessage, requiresAuth: Boolean = false, namespace: Int = 0): RawResponsePromise = retryIfNeeded(maxRetryCount) { - val module = MessagingModuleConfiguration.shared - val userED25519KeyPair = module.getUserED25519KeyPair() ?: return@retryIfNeeded Promise.ofFail(Error.NoKeyPair) + val userED25519KeyPair = getUserED25519KeyPair() ?: return@retryIfNeeded Promise.ofFail(Error.NoKeyPair) val parameters = message.toJSON().toMutableMap() // Construct signature if (requiresAuth) { @@ -566,9 +564,8 @@ object SnodeAPI { fun deleteMessage(publicKey: String, serverHashes: List): Promise, Exception> = retryIfNeeded(maxRetryCount) { - val module = MessagingModuleConfiguration.shared - val userED25519KeyPair = module.getUserED25519KeyPair() ?: return@retryIfNeeded Promise.ofFail(Error.NoKeyPair) - val userPublicKey = module.storage.getUserPublicKey() ?: return@retryIfNeeded Promise.ofFail(Error.NoKeyPair) + val userED25519KeyPair = getUserED25519KeyPair() ?: return@retryIfNeeded Promise.ofFail(Error.NoKeyPair) + val userPublicKey = getUserPublicKey() ?: return@retryIfNeeded Promise.ofFail(Error.NoKeyPair) getSingleTargetSnode(publicKey).bind { snode -> retryIfNeeded(maxRetryCount) { val verificationData = sequenceOf(Snode.Method.DeleteMessage.rawValue).plus(serverHashes).toByteArray() @@ -627,9 +624,8 @@ object SnodeAPI { fun deleteAllMessages(): Promise, Exception> = retryIfNeeded(maxRetryCount) { - val module = MessagingModuleConfiguration.shared - val userED25519KeyPair = module.getUserED25519KeyPair() ?: return@retryIfNeeded Promise.ofFail(Error.NoKeyPair) - val userPublicKey = module.storage.getUserPublicKey() ?: return@retryIfNeeded Promise.ofFail(Error.NoKeyPair) + val userED25519KeyPair = getUserED25519KeyPair() ?: return@retryIfNeeded Promise.ofFail(Error.NoKeyPair) + val userPublicKey = getUserPublicKey() ?: return@retryIfNeeded Promise.ofFail(Error.NoKeyPair) getSingleTargetSnode(userPublicKey).bind { snode -> retryIfNeeded(maxRetryCount) { getNetworkTime(snode).bind { (_, timestamp) -> From 61cb602e63be3ee7093c035c91adfa0ea03f184b Mon Sep 17 00:00:00 2001 From: bemusementpark Date: Sat, 3 Aug 2024 22:49:23 +0930 Subject: [PATCH 25/50] Simplify and document some functions --- .../org/session/libsession/snode/SnodeAPI.kt | 5 +-- .../org/session/libsignal/utilities/Snode.kt | 35 ++++++------------- 2 files changed, 12 insertions(+), 28 deletions(-) diff --git a/libsession/src/main/java/org/session/libsession/snode/SnodeAPI.kt b/libsession/src/main/java/org/session/libsession/snode/SnodeAPI.kt index 4a2a9f2134f..9c584ba0fb7 100644 --- a/libsession/src/main/java/org/session/libsession/snode/SnodeAPI.kt +++ b/libsession/src/main/java/org/session/libsession/snode/SnodeAPI.kt @@ -736,10 +736,7 @@ object SnodeAPI { else json?.let(::parseSnodes) ?.takeIf { it.isNotEmpty() } ?.let { database.setSwarm(publicKey, it.toSet()) } - ?: run { - Log.d("Loki", "Invalidating swarm for: $publicKey.") - dropSnodeFromSwarmIfNeeded(snode, publicKey) - } + ?: dropSnodeFromSwarmIfNeeded(snode, publicKey).also { Log.d("Loki", "Invalidating swarm for: $publicKey.") } } 404 -> { Log.d("Loki", "404, probably no file found") diff --git a/libsignal/src/main/java/org/session/libsignal/utilities/Snode.kt b/libsignal/src/main/java/org/session/libsignal/utilities/Snode.kt index 86f3e26b645..58dcbe8a374 100644 --- a/libsignal/src/main/java/org/session/libsignal/utilities/Snode.kt +++ b/libsignal/src/main/java/org/session/libsignal/utilities/Snode.kt @@ -2,6 +2,9 @@ package org.session.libsignal.utilities import android.annotation.SuppressLint +/** + * Create a Snode from a "-" delimited String if valid, null otherwise. + */ fun Snode(string: String): Snode? { val components = string.split("-") val address = components[0] @@ -31,25 +34,16 @@ class Snode(val address: String, val port: Int, val publicKeySet: KeySet?, val v data class KeySet(val ed25519Key: String, val x25519Key: String) - override fun equals(other: Any?): Boolean { - return if (other is Snode) { - address == other.address && port == other.port - } else { - false - } - } - - override fun hashCode(): Int { - return address.hashCode() xor port.hashCode() - } - - override fun toString(): String { return "$address:$port" } + override fun equals(other: Any?) = other is Snode && address == other.address && port == other.port + override fun hashCode(): Int = address.hashCode() xor port.hashCode() + override fun toString(): String = "$address:$port" companion object { private val CACHE = mutableMapOf() @SuppressLint("NotConstructor") fun Version(value: String) = CACHE.getOrElse(value) { + // internal constructor takes precedence Snode.Version(value) } @@ -62,22 +56,15 @@ class Snode(val address: String, val port: Int, val publicKeySet: KeySet?, val v val ZERO = Version(0UL) private const val MASK_BITS = 16 private const val MASK = 0xFFFFUL - - private fun Sequence.foldToVersionAsULong() = take(4).foldIndexed(0UL) { i, acc, it -> - it.coerceAtMost(MASK) shl (3 - i) * MASK_BITS or acc - } } - internal constructor(parts: List): this( - parts.asSequence() - .map { it.toByte().toULong() } - .foldToVersionAsULong() - ) - internal constructor(value: String): this( value.splitToSequence(".") + .take(4) .map { it.toULongOrNull() ?: 0UL } - .foldToVersionAsULong() + .foldIndexed(0UL) { i, acc, it -> + it.coerceAtMost(MASK) shl (3 - i) * MASK_BITS or acc + } ) operator fun compareTo(other: Version): Int = value.compareTo(other.value) From f5d1debc323a889067f44a7945159a54034ee4d9 Mon Sep 17 00:00:00 2001 From: bemusementpark Date: Sun, 4 Aug 2024 00:18:47 +0930 Subject: [PATCH 26/50] Add shared SecureRandom instance --- .../securesms/conversation/v2/Util.kt | 34 ++----------------- .../crypto/AttachmentSecretProvider.java | 7 ++-- .../crypto/DatabaseSecretProvider.java | 6 ++-- .../ModernEncryptingPartOutputStream.java | 5 +-- .../securesms/database/GroupDatabase.java | 11 ++---- .../securesms/database/MmsDatabase.kt | 5 ++- .../securesms/database/SmsDatabase.java | 5 +-- .../glide/PaddedHeadersInterceptor.java | 11 +++--- .../securesms/logging/LogFile.java | 4 +-- .../securesms/logging/LogSecretProvider.java | 6 ++-- .../org/thoughtcrime/securesms/mms/Slide.kt | 4 +-- .../securesms/net/ChunkedDataFetcher.java | 5 +-- .../securesms/permissions/Permissions.java | 11 ++---- .../securesms/preferences/SettingsActivity.kt | 4 +-- .../securesms/webrtc/PeerConnectionWrapper.kt | 7 ++-- .../jobs/RetrieveProfileAvatarJob.kt | 6 ++-- .../pollers/ClosedGroupPollerV2.kt | 4 +-- .../sending_receiving/pollers/Poller.kt | 6 ++-- .../messaging/utilities/MessageWrapper.kt | 20 +++++------ .../libsession/snode/OnionRequestAPI.kt | 15 ++++---- .../org/session/libsession/snode/SnodeAPI.kt | 12 ++++--- .../org/session/libsession/utilities/Util.kt | 20 ++++------- .../org/session/libsignal/crypto/Random.kt | 14 +++++--- .../streams/ProfileCipherOutputStream.java | 4 +-- .../org/session/libsignal/utilities/HTTP.kt | 7 ++-- .../org/session/libsignal/utilities/Util.java | 20 ++--------- 26 files changed, 98 insertions(+), 155 deletions(-) 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..71a0aae0fba 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 @@ -33,18 +33,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 network.loki.messenger.R +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) @@ -248,32 +246,6 @@ 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) 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..1602ab3fcfa 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. * @@ -81,9 +81,8 @@ private AttachmentSecret getEncryptedAttachmentSecret(@NonNull String serialized } 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); 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..44bb33161c2 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 { @@ -60,9 +61,8 @@ private DatabaseSecret getEncryptedDatabaseSecret(@NonNull String serializedEncr } 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); 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/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/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/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/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..763a2a430d4 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 { @@ -40,9 +41,8 @@ private static byte[] parseEncryptedSecret(String secret) { } 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); 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/permissions/Permissions.java b/app/src/main/java/org/thoughtcrime/securesms/permissions/Permissions.java index 88ee67cb4de..9b6950bf7ad 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); 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 fc98070ca40..dc1011d970a 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/preferences/SettingsActivity.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/preferences/SettingsActivity.kt @@ -62,6 +62,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 @@ -90,7 +91,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 @@ -294,7 +294,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/webrtc/PeerConnectionWrapper.kt b/app/src/main/java/org/thoughtcrime/securesms/webrtc/PeerConnectionWrapper.kt index b61edbb6d26..24ea2a390fd 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/libsession/src/main/java/org/session/libsession/messaging/jobs/RetrieveProfileAvatarJob.kt b/libsession/src/main/java/org/session/libsession/messaging/jobs/RetrieveProfileAvatarJob.kt index 54efa1c80bd..d82f29446dd 100644 --- a/libsession/src/main/java/org/session/libsession/messaging/jobs/RetrieveProfileAvatarJob.kt +++ b/libsession/src/main/java/org/session/libsession/messaging/jobs/RetrieveProfileAvatarJob.kt @@ -12,11 +12,11 @@ import org.session.libsession.utilities.Util.equals import org.session.libsession.utilities.recipients.Recipient import org.session.libsignal.streams.ProfileCipherInputStream import org.session.libsignal.utilities.Log +import org.session.libsignal.utilities.Util.SECURE_RANDOM import java.io.File import java.io.FileInputStream import java.io.FileOutputStream import java.io.InputStream -import java.security.SecureRandom import java.util.concurrent.ConcurrentSkipListSet class RetrieveProfileAvatarJob(private val profileAvatar: String?, val recipientAddress: Address): Job { @@ -64,7 +64,7 @@ class RetrieveProfileAvatarJob(private val profileAvatar: String?, val recipient Log.w(TAG, "Removing profile avatar for: " + recipient.address.serialize()) if (recipient.isLocalNumber) { - setProfileAvatarId(context, SecureRandom().nextInt()) + setProfileAvatarId(context, SECURE_RANDOM.nextInt()) setProfilePictureURL(context, null) } @@ -83,7 +83,7 @@ class RetrieveProfileAvatarJob(private val profileAvatar: String?, val recipient decryptDestination.renameTo(AvatarHelper.getAvatarFile(context, recipient.address)) if (recipient.isLocalNumber) { - setProfileAvatarId(context, SecureRandom().nextInt()) + setProfileAvatarId(context, SECURE_RANDOM.nextInt()) setProfilePictureURL(context, profileAvatar) } diff --git a/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/pollers/ClosedGroupPollerV2.kt b/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/pollers/ClosedGroupPollerV2.kt index 293fbc8d8ab..c9b7abefbc4 100644 --- a/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/pollers/ClosedGroupPollerV2.kt +++ b/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/pollers/ClosedGroupPollerV2.kt @@ -10,7 +10,7 @@ import org.session.libsession.messaging.jobs.JobQueue import org.session.libsession.messaging.jobs.MessageReceiveParameters import org.session.libsession.snode.SnodeAPI import org.session.libsession.utilities.GroupUtil -import org.session.libsignal.crypto.getRandomElementOrNull +import org.session.libsignal.crypto.secureRandomOrNull import org.session.libsignal.utilities.Log import org.session.libsignal.utilities.Namespace import org.session.libsignal.utilities.defaultRequiresAuth @@ -104,7 +104,7 @@ class ClosedGroupPollerV2 { fun poll(groupPublicKey: String): Promise { if (!isPolling(groupPublicKey)) { return Promise.of(Unit) } val promise = SnodeAPI.getSwarm(groupPublicKey).bind { swarm -> - val snode = swarm.getRandomElementOrNull() ?: throw InsufficientSnodesException() // Should be cryptographically secure + val snode = swarm.secureRandomOrNull() ?: throw InsufficientSnodesException() // Should be cryptographically secure if (!isPolling(groupPublicKey)) { throw PollingCanceledException() } val currentForkInfo = SnodeAPI.forkInfo when { diff --git a/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/pollers/Poller.kt b/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/pollers/Poller.kt index 2b7c8159eac..f57aa1f0f78 100644 --- a/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/pollers/Poller.kt +++ b/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/pollers/Poller.kt @@ -19,8 +19,6 @@ import org.session.libsession.messaging.MessagingModuleConfiguration import org.session.libsession.messaging.jobs.BatchMessageReceiveJob import org.session.libsession.messaging.jobs.JobQueue import org.session.libsession.messaging.jobs.MessageReceiveParameters -import org.session.libsession.messaging.messages.control.SharedConfigurationMessage -import org.session.libsession.messaging.sending_receiving.MessageReceiver import org.session.libsession.snode.RawResponse import org.session.libsession.snode.SnodeAPI import org.session.libsession.snode.SnodeModule @@ -29,7 +27,7 @@ import org.session.libsignal.utilities.Base64 import org.session.libsignal.utilities.Log import org.session.libsignal.utilities.Namespace import org.session.libsignal.utilities.Snode -import java.security.SecureRandom +import org.session.libsignal.utilities.Util.SECURE_RANDOM import java.util.Timer import java.util.TimerTask import kotlin.time.Duration.Companion.days @@ -106,7 +104,7 @@ class Poller(private val configFactory: ConfigFactoryProtocol, debounceTimer: Ti val swarm = SnodeModule.shared.storage.getSwarm(userPublicKey) ?: setOf() val unusedSnodes = swarm.subtract(usedSnodes) if (unusedSnodes.isNotEmpty()) { - val index = SecureRandom().nextInt(unusedSnodes.size) + val index = SECURE_RANDOM.nextInt(unusedSnodes.size) val nextSnode = unusedSnodes.elementAt(index) usedSnodes.add(nextSnode) Log.d(TAG, "Polling $nextSnode.") diff --git a/libsession/src/main/java/org/session/libsession/messaging/utilities/MessageWrapper.kt b/libsession/src/main/java/org/session/libsession/messaging/utilities/MessageWrapper.kt index 569b9c62c19..71cd587b450 100644 --- a/libsession/src/main/java/org/session/libsession/messaging/utilities/MessageWrapper.kt +++ b/libsession/src/main/java/org/session/libsession/messaging/utilities/MessageWrapper.kt @@ -28,7 +28,7 @@ object MessageWrapper { val webSocketMessage = createWebSocketMessage(envelope) return webSocketMessage.toByteArray() } catch (e: Exception) { - throw if (e is Error) { e } else { Error.FailedToWrapData } + throw if (e is Error) e else Error.FailedToWrapData } } @@ -49,15 +49,15 @@ object MessageWrapper { private fun createWebSocketMessage(envelope: Envelope): WebSocketMessage { try { - val requestBuilder = WebSocketRequestMessage.newBuilder() - requestBuilder.verb = "PUT" - requestBuilder.path = "/api/v1/message" - requestBuilder.id = SecureRandom.getInstance("SHA1PRNG").nextLong() - requestBuilder.body = envelope.toByteString() - val messageBuilder = WebSocketMessage.newBuilder() - messageBuilder.request = requestBuilder.build() - messageBuilder.type = WebSocketMessage.Type.REQUEST - return messageBuilder.build() + return WebSocketMessage.newBuilder().apply { + request = WebSocketRequestMessage.newBuilder().apply { + verb = "PUT" + path = "/api/v1/message" + id = SecureRandom.getInstance("SHA1PRNG").nextLong() + body = envelope.toByteString() + }.build() + type = WebSocketMessage.Type.REQUEST + }.build() } catch (e: Exception) { Log.d("Loki", "Failed to wrap envelope in web socket message: ${e.message}.") throw Error.FailedToWrapEnvelopeInWebSocketMessage diff --git a/libsession/src/main/java/org/session/libsession/snode/OnionRequestAPI.kt b/libsession/src/main/java/org/session/libsession/snode/OnionRequestAPI.kt index ead454eb446..9ff541a9d5c 100644 --- a/libsession/src/main/java/org/session/libsession/snode/OnionRequestAPI.kt +++ b/libsession/src/main/java/org/session/libsession/snode/OnionRequestAPI.kt @@ -10,11 +10,10 @@ import okhttp3.Request import org.session.libsession.messaging.file_server.FileServerApi import org.session.libsession.utilities.AESGCM import org.session.libsession.utilities.AESGCM.EncryptionResult -import org.session.libsession.utilities.Util import org.session.libsession.utilities.getBodyForOnionRequest import org.session.libsession.utilities.getHeadersForOnionRequest -import org.session.libsignal.crypto.getRandomElement -import org.session.libsignal.crypto.getRandomElementOrNull +import org.session.libsignal.crypto.secureRandom +import org.session.libsignal.crypto.secureRandomOrNull import org.session.libsignal.database.LokiAPIDatabaseProtocol import org.session.libsignal.utilities.Base64 import org.session.libsignal.utilities.Broadcaster @@ -149,7 +148,7 @@ object OnionRequestAPI { val reusableGuardSnodeCount = reusableGuardSnodes.count() if (unusedSnodes.count() < (targetGuardSnodeCount - reusableGuardSnodeCount)) { throw InsufficientSnodesException() } fun getGuardSnode(): Promise { - val candidate = unusedSnodes.getRandomElementOrNull() + val candidate = unusedSnodes.secureRandomOrNull() ?: return Promise.ofFail(InsufficientSnodesException()) unusedSnodes = unusedSnodes.minus(candidate) Log.d("Loki", "Testing guard snode: $candidate.") @@ -191,7 +190,7 @@ object OnionRequestAPI { // Don't test path snodes as this would reveal the user's IP to them guardSnodes.minus(reusableGuardSnodes).map { guardSnode -> val result = listOf( guardSnode ) + (0 until (pathSize - 1)).mapIndexed() { index, _ -> - var pathSnode = unusedSnodes.getRandomElement() + var pathSnode = unusedSnodes.secureRandom() // remove the snode from the unused list and return it unusedSnodes = unusedSnodes.minus(pathSnode) @@ -228,9 +227,9 @@ object OnionRequestAPI { OnionRequestAPI.guardSnodes = guardSnodes fun getPath(paths: List): Path { return if (snodeToExclude != null) { - paths.filter { !it.contains(snodeToExclude) }.getRandomElement() + paths.filter { !it.contains(snodeToExclude) }.secureRandom() } else { - paths.getRandomElement() + paths.secureRandom() } } when { @@ -273,7 +272,7 @@ object OnionRequestAPI { path.removeAt(snodeIndex) val unusedSnodes = SnodeAPI.snodePool.minus(oldPaths.flatten()) if (unusedSnodes.isEmpty()) { throw InsufficientSnodesException() } - path.add(unusedSnodes.getRandomElement()) + path.add(unusedSnodes.secureRandom()) // Don't test the new snode as this would reveal the user's IP oldPaths.removeAt(pathIndex) val newPaths = oldPaths + listOf( path ) diff --git a/libsession/src/main/java/org/session/libsession/snode/SnodeAPI.kt b/libsession/src/main/java/org/session/libsession/snode/SnodeAPI.kt index 034ef0e7d23..6a27312f7c3 100644 --- a/libsession/src/main/java/org/session/libsession/snode/SnodeAPI.kt +++ b/libsession/src/main/java/org/session/libsession/snode/SnodeAPI.kt @@ -18,7 +18,8 @@ import nl.komponents.kovenant.task import org.session.libsession.messaging.MessagingModuleConfiguration import org.session.libsession.messaging.utilities.MessageWrapper import org.session.libsession.messaging.utilities.SodiumUtilities.sodium -import org.session.libsignal.crypto.getRandomElement +import org.session.libsignal.crypto.secureRandom +import org.session.libsignal.crypto.shuffledRandom import org.session.libsignal.database.LokiAPIDatabaseProtocol import org.session.libsignal.protos.SignalServiceProtos import org.session.libsignal.utilities.Base64 @@ -30,6 +31,7 @@ import org.session.libsignal.utilities.Log import org.session.libsignal.utilities.Namespace import org.session.libsignal.utilities.Snode import org.session.libsignal.utilities.ThreadUtils +import org.session.libsignal.utilities.Util.SECURE_RANDOM import org.session.libsignal.utilities.prettifiedDescription import org.session.libsignal.utilities.retryIfNeeded import java.security.SecureRandom @@ -209,7 +211,7 @@ object SnodeAPI { Log.d("Loki", "Persisting snode pool to database.") this.snodePool = snodePool try { - deferred.resolve(snodePool.getRandomElement()) + deferred.resolve(snodePool.secureRandom()) } catch (exception: Exception) { Log.d("Loki", "Got an empty snode pool from: $target.") deferred.reject(SnodeAPI.Error.Generic) @@ -224,7 +226,7 @@ object SnodeAPI { } return deferred.promise } else { - return Promise.of(snodePool.getRandomElement()) + return Promise.of(snodePool.secureRandom()) } } @@ -241,8 +243,8 @@ object SnodeAPI { } internal fun getSingleTargetSnode(publicKey: String): Promise { - // SecureRandom() should be cryptographically secure - return getSwarm(publicKey).map { it.shuffled(SecureRandom()).random() } + // SecureRandom should be cryptographically secure + return getSwarm(publicKey).map { it.shuffledRandom().random() } } // Public API diff --git a/libsession/src/main/java/org/session/libsession/utilities/Util.kt b/libsession/src/main/java/org/session/libsession/utilities/Util.kt index 929f53e305e..1293547703e 100644 --- a/libsession/src/main/java/org/session/libsession/utilities/Util.kt +++ b/libsession/src/main/java/org/session/libsession/utilities/Util.kt @@ -13,6 +13,7 @@ import android.text.TextUtils import android.text.style.StyleSpan import org.session.libsignal.utilities.Log import org.session.libsignal.utilities.Base64 +import org.session.libsignal.utilities.Util.SECURE_RANDOM import java.io.* import java.nio.charset.StandardCharsets import java.security.SecureRandom @@ -292,15 +293,10 @@ object Util { @JvmStatic fun getSecretBytes(size: Int): ByteArray { val secret = ByteArray(size) - getSecureRandom().nextBytes(secret) + SECURE_RANDOM.nextBytes(secret) return secret } - @JvmStatic - fun getSecureRandom(): SecureRandom { - return SecureRandom() - } - @JvmStatic fun getFirstNonEmpty(vararg values: String?): String? { for (value in values) { @@ -317,18 +313,14 @@ object Util { } @JvmStatic - fun getRandomElement(elements: Array): T { - return elements[SecureRandom().nextInt(elements.size)] - } + fun getRandomElement(elements: Array): T = elements[SECURE_RANDOM.nextInt(elements.size)] @JvmStatic fun getBoldedString(value: String?): CharSequence { if (value.isNullOrEmpty()) { return "" } - val spanned = SpannableString(value) - spanned.setSpan(StyleSpan(Typeface.BOLD), 0, - spanned.length, - Spannable.SPAN_EXCLUSIVE_EXCLUSIVE) - return spanned + return SpannableString(value).also { + it.setSpan(StyleSpan(Typeface.BOLD), 0, it.length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE) + } } @JvmStatic diff --git a/libsignal/src/main/java/org/session/libsignal/crypto/Random.kt b/libsignal/src/main/java/org/session/libsignal/crypto/Random.kt index 4f7687307b7..c32eb78595b 100644 --- a/libsignal/src/main/java/org/session/libsignal/crypto/Random.kt +++ b/libsignal/src/main/java/org/session/libsignal/crypto/Random.kt @@ -1,19 +1,23 @@ package org.session.libsignal.crypto -import java.security.SecureRandom +import org.session.libsignal.utilities.Util.SECURE_RANDOM /** * Uses `SecureRandom` to pick an element from this collection. */ -fun Collection.getRandomElementOrNull(): T? { +fun Collection.secureRandomOrNull(): T? { if (isEmpty()) return null - val index = SecureRandom().nextInt(size) // SecureRandom() should be cryptographically secure + val index = SECURE_RANDOM.nextInt(size) // SecureRandom should be cryptographically secure return elementAtOrNull(index) } /** * Uses `SecureRandom` to pick an element from this collection. + * + * @throws [NullPointerException] if the [Collection] is empty */ -fun Collection.getRandomElement(): T { - return getRandomElementOrNull()!! +fun Collection.secureRandom(): T { + return secureRandomOrNull()!! } + +fun Collection.shuffledRandom(): List = shuffled(SECURE_RANDOM) diff --git a/libsignal/src/main/java/org/session/libsignal/streams/ProfileCipherOutputStream.java b/libsignal/src/main/java/org/session/libsignal/streams/ProfileCipherOutputStream.java index f47a5f72b69..43734605ae0 100644 --- a/libsignal/src/main/java/org/session/libsignal/streams/ProfileCipherOutputStream.java +++ b/libsignal/src/main/java/org/session/libsignal/streams/ProfileCipherOutputStream.java @@ -1,13 +1,13 @@ package org.session.libsignal.streams; import static org.session.libsignal.crypto.CipherUtil.CIPHER_LOCK; +import static org.session.libsignal.utilities.Util.SECURE_RANDOM; import java.io.IOException; import java.io.OutputStream; import java.security.InvalidAlgorithmParameterException; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; -import java.security.SecureRandom; import javax.crypto.BadPaddingException; import javax.crypto.Cipher; @@ -80,7 +80,7 @@ public void flush() throws IOException { private byte[] generateNonce() { byte[] nonce = new byte[12]; - new SecureRandom().nextBytes(nonce); + SECURE_RANDOM.nextBytes(nonce); return nonce; } diff --git a/libsignal/src/main/java/org/session/libsignal/utilities/HTTP.kt b/libsignal/src/main/java/org/session/libsignal/utilities/HTTP.kt index fd4a3f37028..4f8b20f7c6e 100644 --- a/libsignal/src/main/java/org/session/libsignal/utilities/HTTP.kt +++ b/libsignal/src/main/java/org/session/libsignal/utilities/HTTP.kt @@ -5,8 +5,7 @@ import okhttp3.MediaType.Companion.toMediaType import okhttp3.OkHttpClient import okhttp3.Request import okhttp3.RequestBody -import okhttp3.Response -import java.security.SecureRandom +import org.session.libsignal.utilities.Util.SECURE_RANDOM import java.security.cert.X509Certificate import java.util.concurrent.TimeUnit import javax.net.ssl.SSLContext @@ -35,7 +34,7 @@ object HTTP { override fun getAcceptedIssuers(): Array { return arrayOf() } } val sslContext = SSLContext.getInstance("SSL") - sslContext.init(null, arrayOf( trustManager ), SecureRandom()) + sslContext.init(null, arrayOf( trustManager ), SECURE_RANDOM) OkHttpClient().newBuilder() .sslSocketFactory(sslContext.socketFactory, trustManager) .hostnameVerifier { _, _ -> true } @@ -55,7 +54,7 @@ object HTTP { override fun getAcceptedIssuers(): Array { return arrayOf() } } val sslContext = SSLContext.getInstance("SSL") - sslContext.init(null, arrayOf( trustManager ), SecureRandom()) + sslContext.init(null, arrayOf( trustManager ), SECURE_RANDOM) return OkHttpClient().newBuilder() .sslSocketFactory(sslContext.socketFactory, trustManager) .hostnameVerifier { _, _ -> true } diff --git a/libsignal/src/main/java/org/session/libsignal/utilities/Util.java b/libsignal/src/main/java/org/session/libsignal/utilities/Util.java index 2c9436485ff..3b3b7aa5e6b 100644 --- a/libsignal/src/main/java/org/session/libsignal/utilities/Util.java +++ b/libsignal/src/main/java/org/session/libsignal/utilities/Util.java @@ -12,12 +12,10 @@ import java.io.OutputStream; import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; -import java.util.Arrays; import java.util.Collection; -import java.util.Collections; -import java.util.List; public class Util { + public static SecureRandom SECURE_RANDOM = new SecureRandom(); public static byte[] join(byte[]... input) { try { @@ -67,7 +65,7 @@ public static byte[] trim(byte[] input, int length) { } public static boolean isEmpty(String value) { - return value == null || value.trim().length() == 0; + return value == null || value.trim().isEmpty(); } public static byte[] getSecretBytes(int size) { @@ -80,13 +78,6 @@ public static byte[] getSecretBytes(int size) { } } - public static byte[] getRandomLengthBytes(int maxSize) { - SecureRandom secureRandom = new SecureRandom(); - byte[] result = new byte[secureRandom.nextInt(maxSize) + 1]; - secureRandom.nextBytes(result); - return result; - } - public static String readFully(InputStream in) throws IOException { ByteArrayOutputStream bout = new ByteArrayOutputStream(); byte[] buffer = new byte[4096]; @@ -98,7 +89,7 @@ public static String readFully(InputStream in) throws IOException { in.close(); - return new String(bout.toByteArray()); + return bout.toString(); } public static void readFully(InputStream in, byte[] buffer) throws IOException { @@ -146,9 +137,4 @@ public static int toIntExact(long value) { } return (int)value; } - - public static List immutableList(T... elements) { - return Collections.unmodifiableList(Arrays.asList(elements.clone())); - } - } From fa0abef243e5301c81ab564ff7076ffeeac18e5d Mon Sep 17 00:00:00 2001 From: bemusementpark Date: Mon, 5 Aug 2024 09:59:02 +0930 Subject: [PATCH 27/50] Fix Snode Version CACHE usage --- .../src/main/java/org/session/libsignal/utilities/Snode.kt | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/libsignal/src/main/java/org/session/libsignal/utilities/Snode.kt b/libsignal/src/main/java/org/session/libsignal/utilities/Snode.kt index 58dcbe8a374..288c3fcf1d5 100644 --- a/libsignal/src/main/java/org/session/libsignal/utilities/Snode.kt +++ b/libsignal/src/main/java/org/session/libsignal/utilities/Snode.kt @@ -42,10 +42,7 @@ class Snode(val address: String, val port: Int, val publicKeySet: KeySet?, val v private val CACHE = mutableMapOf() @SuppressLint("NotConstructor") - fun Version(value: String) = CACHE.getOrElse(value) { - // internal constructor takes precedence - Snode.Version(value) - } + fun Version(value: String) = CACHE[value] ?: Snode.Version(value).also { CACHE[value] = it } fun Version(parts: List) = Version(parts.joinToString(".")) } From f9ace6a9b92c8df4b0a7346f297c860327b31230 Mon Sep 17 00:00:00 2001 From: bemusementpark Date: Mon, 5 Aug 2024 10:26:52 +0930 Subject: [PATCH 28/50] Add LruCache and thread-safety --- .../src/main/java/org/session/libsignal/utilities/Snode.kt | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/libsignal/src/main/java/org/session/libsignal/utilities/Snode.kt b/libsignal/src/main/java/org/session/libsignal/utilities/Snode.kt index 288c3fcf1d5..675fe55f85c 100644 --- a/libsignal/src/main/java/org/session/libsignal/utilities/Snode.kt +++ b/libsignal/src/main/java/org/session/libsignal/utilities/Snode.kt @@ -1,6 +1,7 @@ package org.session.libsignal.utilities import android.annotation.SuppressLint +import android.util.LruCache /** * Create a Snode from a "-" delimited String if valid, null otherwise. @@ -39,10 +40,11 @@ class Snode(val address: String, val port: Int, val publicKeySet: KeySet?, val v override fun toString(): String = "$address:$port" companion object { - private val CACHE = mutableMapOf() + private val CACHE = LruCache(100) @SuppressLint("NotConstructor") - fun Version(value: String) = CACHE[value] ?: Snode.Version(value).also { CACHE[value] = it } + @Synchronized + fun Version(value: String) = CACHE[value] ?: Snode.Version(value).also { CACHE.put(value, it) } fun Version(parts: List) = Version(parts.joinToString(".")) } From 2960eddd8532d8c2387d7e3ef29f3624b5dca3b7 Mon Sep 17 00:00:00 2001 From: bemusementpark Date: Mon, 5 Aug 2024 13:46:55 +0930 Subject: [PATCH 29/50] Fix removeDuplicates --- .../sending_receiving/pollers/Poller.kt | 3 +- .../org/session/libsession/snode/SnodeAPI.kt | 36 ++++++++++--------- 2 files changed, 21 insertions(+), 18 deletions(-) diff --git a/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/pollers/Poller.kt b/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/pollers/Poller.kt index 2b7c8159eac..f0caa9d3142 100644 --- a/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/pollers/Poller.kt +++ b/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/pollers/Poller.kt @@ -142,8 +142,7 @@ class Poller(private val configFactory: ConfigFactoryProtocol, debounceTimer: Ti val messages = rawMessages["messages"] as? List<*> val processed = if (!messages.isNullOrEmpty()) { SnodeAPI.updateLastMessageHashValueIfPossible(snode, userPublicKey, messages, namespace) - SnodeAPI.removeDuplicates(userPublicKey, messages, namespace, true).mapNotNull { messageBody -> - val rawMessageAsJSON = messageBody as? Map<*, *> ?: return@mapNotNull null + SnodeAPI.removeDuplicates(userPublicKey, messages, namespace, true).mapNotNull { rawMessageAsJSON -> val hashValue = rawMessageAsJSON["hash"] as? String ?: return@mapNotNull null val b64EncodedBody = rawMessageAsJSON["data"] as? String ?: return@mapNotNull null val timestamp = rawMessageAsJSON["t"] as? Long ?: SnodeAPI.nowWithOffset diff --git a/libsession/src/main/java/org/session/libsession/snode/SnodeAPI.kt b/libsession/src/main/java/org/session/libsession/snode/SnodeAPI.kt index 9c584ba0fb7..72b144be03a 100644 --- a/libsession/src/main/java/org/session/libsession/snode/SnodeAPI.kt +++ b/libsession/src/main/java/org/session/libsession/snode/SnodeAPI.kt @@ -660,29 +660,33 @@ object SnodeAPI { } } - fun removeDuplicates(publicKey: String, rawMessages: List<*>, namespace: Int, updateStoredHashes: Boolean): List<*> { - val originalMessageHashValues = database.getReceivedMessageHashValues(publicKey, namespace) ?: emptySet() - val receivedMessageHashValues = originalMessageHashValues.toMutableSet() - return rawMessages.filter { rawMessage -> - (rawMessage as? Map<*, *>) - ?.let { it["hash"] as? String } - ?.let { receivedMessageHashValues.add(it) } - ?: false.also { Log.d("Loki", "Missing hash value for message: ${rawMessage?.prettifiedDescription()}.") } + /** + * + * + * TODO Use a db transaction, synchronizing is sufficient for now because + * database#setReceivedMessageHashValues is only called here. + */ + @Synchronized + fun removeDuplicates(publicKey: String, rawMessages: List<*>, namespace: Int, updateStoredHashes: Boolean): List> { + val hashValues = database.getReceivedMessageHashValues(publicKey, namespace)?.toMutableSet() ?: mutableSetOf() + return rawMessages.filterIsInstance>().filter { rawMessage -> + val hash = rawMessage["hash"] as? String + hash ?: Log.d("Loki", "Missing hash value for message: ${rawMessage.prettifiedDescription()}.") + hash?.let(hashValues::add) == true }.also { - if (updateStoredHashes && originalMessageHashValues.containsAll(receivedMessageHashValues)) { - database.setReceivedMessageHashValues(publicKey, receivedMessageHashValues, namespace) + if (updateStoredHashes && it.isNotEmpty()) { + database.setReceivedMessageHashValues(publicKey, hashValues, namespace) } } } - private fun parseEnvelopes(rawMessages: List<*>): List> = rawMessages.mapNotNull { rawMessage -> - val rawMessageAsJSON = rawMessage as? Map<*, *> - val base64EncodedData = rawMessageAsJSON?.get("data") as? String - val data = base64EncodedData?.let { Base64.decode(it) } + private fun parseEnvelopes(rawMessages: List>): List> = rawMessages.mapNotNull { rawMessage -> + val base64EncodedData = rawMessage["data"] as? String + val data = base64EncodedData?.let(Base64::decode) - data ?: Log.d("Loki", "Failed to decode data for message: ${rawMessage?.prettifiedDescription()}.") + data ?: Log.d("Loki", "Failed to decode data for message: ${rawMessage.prettifiedDescription()}.") - data?.runCatching { MessageWrapper.unwrap(this) to rawMessageAsJSON["hash"] as? String } + data?.runCatching { MessageWrapper.unwrap(this) to rawMessage["hash"] as? String } ?.onFailure { Log.d("Loki", "Failed to unwrap data for message: ${rawMessage.prettifiedDescription()}.") } ?.getOrNull() } From 8a9faa182d43322fa418e555461cc355b43280d7 Mon Sep 17 00:00:00 2001 From: bemusementpark Date: Mon, 5 Aug 2024 16:26:20 +0930 Subject: [PATCH 30/50] Fix SnodeAPI error thrown outside of Promise --- .../src/main/java/org/session/libsession/snode/SnodeAPI.kt | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/libsession/src/main/java/org/session/libsession/snode/SnodeAPI.kt b/libsession/src/main/java/org/session/libsession/snode/SnodeAPI.kt index 2f6cf141d1c..099b2541ab3 100644 --- a/libsession/src/main/java/org/session/libsession/snode/SnodeAPI.kt +++ b/libsession/src/main/java/org/session/libsession/snode/SnodeAPI.kt @@ -15,6 +15,7 @@ import nl.komponents.kovenant.all import nl.komponents.kovenant.functional.bind import nl.komponents.kovenant.functional.map import nl.komponents.kovenant.task +import nl.komponents.kovenant.unwrap import org.session.libsession.messaging.MessagingModuleConfiguration import org.session.libsession.messaging.utilities.MessageWrapper import org.session.libsession.messaging.utilities.SodiumUtilities.sodium @@ -202,7 +203,7 @@ object SnodeAPI { } // Public API - fun getAccountID(onsName: String): Promise { + fun getAccountID(onsName: String): Promise = task { val validationCount = 3 val accountIDByteCount = 33 // Hash the ONS name using BLAKE2b @@ -228,7 +229,7 @@ object SnodeAPI { } } } - return all(promises).map { results -> + all(promises).map { results -> results.map { json -> val intermediate = json["result"] as? Map<*, *> ?: throw Error.Generic val hexEncodedCiphertext = intermediate["encrypted_value"] as? String ?: throw Error.Generic @@ -264,7 +265,7 @@ object SnodeAPI { }.takeIf { it.size == validationCount && it.toSet().size == 1 }?.first() ?: throw Error.ValidationFailed } - } + }.unwrap() fun getSwarm(publicKey: String): Promise, Exception> = database.getSwarm(publicKey)?.takeIf { it.size >= minimumSwarmSnodeCount }?.let(Promise.Companion::of) From 93a28906fb364654c466543c0cc5993420209c8d Mon Sep 17 00:00:00 2001 From: ThomasSession Date: Thu, 15 Aug 2024 15:32:41 +1000 Subject: [PATCH 31/50] Removing unrequired APi checks (#1620) * Removing unrequired APi checks * Bumping min sdk to 26 and further cleaning version checks * More clean ups * Removed unused class --- app/src/main/AndroidManifest.xml | 4 - .../thoughtcrime/securesms/BaseActivity.java | 6 +- .../securesms/MediaOverviewActivity.java | 23 +- .../securesms/MediaPreviewActivity.java | 7 +- .../securesms/audio/AudioCodec.java | 5 +- .../securesms/audio/AudioSlidePlayer.java | 6 +- .../components/AttachmentTypeSelector.java | 43 +-- .../securesms/components/ComposeText.java | 8 +- .../components/ConversationItemAlertView.java | 3 - .../components/HidingLinearLayout.java | 6 +- .../securesms/components/InputPanel.java | 6 +- .../components/KeyboardAwareLinearLayout.java | 14 +- .../securesms/components/MediaView.java | 20 +- .../components/RecentPhotoViewRail.java | 22 +- .../securesms/components/SearchToolbar.java | 36 +-- .../components/SquareFrameLayout.java | 5 +- .../components/camera/CameraView.java | 48 +--- .../securesms/conversation/v2/Util.kt | 185 ------------ .../v2/input_bar/InputBarButton.kt | 7 +- .../v2/input_bar/InputBarEditText.kt | 3 +- .../crypto/AttachmentSecretProvider.java | 28 +- .../crypto/BiometricSecretProvider.kt | 5 +- .../crypto/DatabaseSecretProvider.java | 28 +- .../securesms/crypto/IdentityKeyUtil.java | 39 +-- .../database/AttachmentDatabase.java | 5 - .../database/helpers/SQLCipherOpenHelper.java | 12 +- .../securesms/home/PathActivity.kt | 6 +- .../securesms/home/PathStatusView.kt | 4 +- .../securesms/jobmanager/Constraint.java | 5 - .../jobmanager/impl/NetworkConstraint.java | 11 - .../securesms/logging/LogSecretProvider.java | 16 +- .../securesms/mediasend/MediaRepository.java | 3 - .../AbstractNotificationBuilder.java | 4 +- .../MultipleRecipientNotificationBuilder.java | 4 - .../notifications/NotificationChannels.java | 271 ------------------ .../notifications/NotificationState.java | 3 +- .../PendingMessageNotificationBuilder.java | 4 - .../SingleRecipientNotificationBuilder.java | 21 +- .../securesms/permissions/Permissions.java | 9 +- .../NotificationsPreferenceFragment.kt | 40 +-- .../PrivacySettingsPreferenceFragment.kt | 11 +- .../securesms/preferences/SettingsActivity.kt | 3 +- .../widgets/SignalListPreference.java | 10 +- .../widget/VerticalSlideColorPicker.java | 8 - .../securesms/util/DateUtils.java | 12 +- .../securesms/util/FileProviderUtil.java | 5 +- .../securesms/util/GeneralUtilities.kt | 11 - .../thoughtcrime/securesms/util/GlowView.kt | 25 -- .../securesms/util/LongClickCopySpan.java | 74 ----- .../util/StickyHeaderDecoration.java | 14 +- .../video/EncryptedMediaDataSource.java | 6 +- .../webrtc/audio/AudioManagerCompat.java | 49 +--- build.gradle | 2 +- .../org/session/libsession/snode/SnodeAPI.kt | 4 +- .../libsession/utilities/DecodedAudio.kt | 30 +- .../libsession/utilities/ServiceUtil.java | 9 - .../libsession/utilities/ViewUtil.java | 116 +------- .../utilities/recipients/Recipient.java | 2 +- 58 files changed, 201 insertions(+), 1165 deletions(-) delete mode 100644 app/src/main/java/org/thoughtcrime/securesms/util/LongClickCopySpan.java diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 0306252e16d..b6bcdf62939 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" /> - = Build.VERSION_CODES.LOLLIPOP) { - Window window = getActivity().getWindow(); - originalStatusBarColor = window.getStatusBarColor(); - window.setStatusBarColor(getResources().getColor(R.color.action_mode_status_bar)); - } + FragmentActivity activity = getActivity(); + if (activity == null) return false; + + Window window = activity.getWindow(); + originalStatusBarColor = window.getStatusBarColor(); + window.setStatusBarColor(ContextCompat.getColor(activity, R.color.action_mode_status_bar)); + return true; } @@ -455,11 +459,12 @@ public boolean onActionItemClicked(ActionMode mode, MenuItem menuItem) { 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); - } + MediaOverviewActivity activity = ((MediaOverviewActivity) getActivity()); + if(activity == null) return; + + activity.onExitMultiSelect(); + activity.getWindow().setStatusBarColor(originalStatusBarColor); } } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/MediaPreviewActivity.java b/app/src/main/java/org/thoughtcrime/securesms/MediaPreviewActivity.java index b609b54fc69..f0493d043ac 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; @@ -72,8 +73,6 @@ import org.thoughtcrime.securesms.database.model.MmsMessageRecord; import org.thoughtcrime.securesms.mediapreview.MediaPreviewViewModel; import org.thoughtcrime.securesms.mediapreview.MediaRailAdapter; -import com.bumptech.glide.Glide; -import com.bumptech.glide.RequestManager; import org.thoughtcrime.securesms.mms.Slide; import org.thoughtcrime.securesms.permissions.Permissions; import org.thoughtcrime.securesms.util.AttachmentUtil; 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/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 bb973c23ff2..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 androidx.annotation.NonNull; +import androidx.annotation.Nullable; + import com.bumptech.glide.RequestManager; -import org.thoughtcrime.securesms.mms.VideoSlide; -import org.thoughtcrime.securesms.video.VideoPlayer; 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); 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 e1d3236a967..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 com.bumptech.glide.Glide; -import org.session.libsession.utilities.ViewUtil; +import network.loki.messenger.R; public class RecentPhotoViewRail extends FrameLayout implements LoaderManager.LoaderCallbacks { @@ -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/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/conversation/v2/Util.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/Util.kt index 71a0aae0fba..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,22 +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 network.loki.messenger.R 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.util.Collections -import java.util.concurrent.TimeUnit -import kotlin.math.max -import kotlin.math.min 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) @@ -104,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 } @@ -133,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) @@ -225,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) @@ -251,26 +154,6 @@ object Util { 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. @@ -280,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/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/crypto/AttachmentSecretProvider.java b/app/src/main/java/org/thoughtcrime/securesms/crypto/AttachmentSecretProvider.java index 1602ab3fcfa..844ba975f15 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/crypto/AttachmentSecretProvider.java +++ b/app/src/main/java/org/thoughtcrime/securesms/crypto/AttachmentSecretProvider.java @@ -59,25 +59,17 @@ 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) { @@ -91,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 44bb33161c2..ab79f7ea55d 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/crypto/DatabaseSecretProvider.java +++ b/app/src/main/java/org/thoughtcrime/securesms/crypto/DatabaseSecretProvider.java @@ -36,28 +36,20 @@ 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) { @@ -66,12 +58,8 @@ private DatabaseSecret createAndStoreDatabaseSecret(@NonNull Context context) { 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/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/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/home/PathActivity.kt b/app/src/main/java/org/thoughtcrime/securesms/home/PathActivity.kt index db0c4d11cc5..e65f2d2f26c 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 @@ -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/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/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/LogSecretProvider.java b/app/src/main/java/org/thoughtcrime/securesms/logging/LogSecretProvider.java index 763a2a430d4..533e1a1d234 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/logging/LogSecretProvider.java +++ b/app/src/main/java/org/thoughtcrime/securesms/logging/LogSecretProvider.java @@ -32,24 +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) { byte[] secret = new byte[32]; 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/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= 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 fb7abb7bd51..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 com.bumptech.glide.Glide; 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) { @@ -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, 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 9b6950bf7ad..f38d4c8613d 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/permissions/Permissions.java +++ b/app/src/main/java/org/thoughtcrime/securesms/permissions/Permissions.java @@ -210,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/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..fb8b0ee3efa 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/preferences/PrivacySettingsPreferenceFragment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/preferences/PrivacySettingsPreferenceFragment.kt @@ -78,14 +78,9 @@ 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) } + 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) } 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 d8d8a4172d2..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 @@ -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 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/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/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/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/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/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/build.gradle b/build.gradle index 08efb9ca24e..ef9f081e968 100644 --- a/build.gradle +++ b/build.gradle @@ -69,7 +69,7 @@ allprojects { } project.ext { - androidMinimumSdkVersion = 23 + androidMinimumSdkVersion = 26 androidTargetSdkVersion = 34 androidCompileSdkVersion = 34 } diff --git a/libsession/src/main/java/org/session/libsession/snode/SnodeAPI.kt b/libsession/src/main/java/org/session/libsession/snode/SnodeAPI.kt index 099b2541ab3..c3335c5a99b 100644 --- a/libsession/src/main/java/org/session/libsession/snode/SnodeAPI.kt +++ b/libsession/src/main/java/org/session/libsession/snode/SnodeAPI.kt @@ -73,8 +73,8 @@ object SnodeAPI { private const val maxRetryCount = 6 private const val minimumSnodePoolCount = 12 private const val minimumSwarmSnodeCount = 3 - // Use port 4433 if the API level can handle the network security configuration and enforce pinned certificates - private val seedNodePort = if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) 443 else 4443 + // Use port 4433 to enforce pinned certificates + private val seedNodePort = 4443 private const val useTestnet = false diff --git a/libsession/src/main/java/org/session/libsession/utilities/DecodedAudio.kt b/libsession/src/main/java/org/session/libsession/utilities/DecodedAudio.kt index ef21abe4c03..a342ee1ae56 100644 --- a/libsession/src/main/java/org/session/libsession/utilities/DecodedAudio.kt +++ b/libsession/src/main/java/org/session/libsession/utilities/DecodedAudio.kt @@ -5,17 +5,12 @@ import android.media.MediaCodec import android.media.MediaDataSource import android.media.MediaExtractor import android.media.MediaFormat -import android.os.Build - -import androidx.annotation.RequiresApi - import java.io.FileDescriptor import java.io.IOException import java.io.InputStream import java.nio.ByteBuffer import java.nio.ByteOrder import java.nio.ShortBuffer -import kotlin.jvm.Throws import kotlin.math.ceil import kotlin.math.roundToInt import kotlin.math.sqrt @@ -44,7 +39,6 @@ class DecodedAudio { } @JvmStatic - @RequiresApi(api = Build.VERSION_CODES.M) @Throws(IOException::class) fun create(dataSource: MediaDataSource): DecodedAudio { val mediaExtractor = MediaExtractor().apply { setDataSource(dataSource) } @@ -69,15 +63,7 @@ class DecodedAudio { val samples: ShortBuffer get() { - return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && - Build.VERSION.SDK_INT <= Build.VERSION_CODES.N_MR1 - ) { - // Hack for Nougat where asReadOnlyBuffer fails to respect byte ordering. - // See https://code.google.com/p/android/issues/detail?id=223824 - decodedSamples - } else { - decodedSamples.asReadOnlyBuffer() - } + return decodedSamples.asReadOnlyBuffer() } /** @@ -128,15 +114,13 @@ class DecodedAudio { codec.start() // Check if the track is in PCM 16 bit encoding. - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { - try { - val pcmEncoding = codec.outputFormat.getInteger(MediaFormat.KEY_PCM_ENCODING) - if (pcmEncoding != AudioFormat.ENCODING_PCM_16BIT) { - throw IOException("Unsupported PCM encoding code: $pcmEncoding") - } - } catch (e: NullPointerException) { - // If KEY_PCM_ENCODING is not specified, means it's ENCODING_PCM_16BIT. + try { + val pcmEncoding = codec.outputFormat.getInteger(MediaFormat.KEY_PCM_ENCODING) + if (pcmEncoding != AudioFormat.ENCODING_PCM_16BIT) { + throw IOException("Unsupported PCM encoding code: $pcmEncoding") } + } catch (e: NullPointerException) { + // If KEY_PCM_ENCODING is not specified, means it's ENCODING_PCM_16BIT. } var decodedSamplesSize: Int = 0 // size of the output buffer containing decoded samples. diff --git a/libsession/src/main/java/org/session/libsession/utilities/ServiceUtil.java b/libsession/src/main/java/org/session/libsession/utilities/ServiceUtil.java index 394a8a0f31a..a66d5bf8405 100644 --- a/libsession/src/main/java/org/session/libsession/utilities/ServiceUtil.java +++ b/libsession/src/main/java/org/session/libsession/utilities/ServiceUtil.java @@ -6,13 +6,8 @@ import android.content.Context; import android.media.AudioManager; import android.net.ConnectivityManager; -import android.os.Build; import android.os.PowerManager; import android.os.Vibrator; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.annotation.RequiresApi; -import android.telephony.SubscriptionManager; import android.telephony.TelephonyManager; import android.view.WindowManager; import android.view.inputmethod.InputMethodManager; @@ -54,8 +49,4 @@ public static Vibrator getVibrator(Context context) { return (Vibrator)context.getSystemService(Context.VIBRATOR_SERVICE); } - @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP_MR1) - public static @Nullable SubscriptionManager getSubscriptionManager(@NonNull Context context) { - return (SubscriptionManager) context.getSystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE); - } } diff --git a/libsession/src/main/java/org/session/libsession/utilities/ViewUtil.java b/libsession/src/main/java/org/session/libsession/utilities/ViewUtil.java index 34a0212c639..f8ca27cfff4 100644 --- a/libsession/src/main/java/org/session/libsession/utilities/ViewUtil.java +++ b/libsession/src/main/java/org/session/libsession/utilities/ViewUtil.java @@ -19,21 +19,16 @@ import android.app.Activity; import android.content.Context; import android.graphics.drawable.Drawable; -import android.os.Build.VERSION; -import android.os.Build.VERSION_CODES; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; -import android.view.ViewStub; import android.view.animation.AlphaAnimation; import android.view.animation.Animation; -import android.widget.LinearLayout.LayoutParams; import androidx.annotation.IdRes; import androidx.annotation.LayoutRes; import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import androidx.core.view.ViewCompat; import androidx.interpolator.view.animation.FastOutSlowInInterpolator; import org.session.libsignal.utilities.ListenableFuture; @@ -42,58 +37,7 @@ public class ViewUtil { @SuppressWarnings("deprecation") public static void setBackground(final @NonNull View v, final @Nullable Drawable drawable) { - if (VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN) { - v.setBackground(drawable); - } else { - v.setBackgroundDrawable(drawable); - } - } - - public static void setY(final @NonNull View v, final int y) { - if (VERSION.SDK_INT >= 11) { - ViewCompat.setY(v, y); - } else { - ViewGroup.MarginLayoutParams params = (ViewGroup.MarginLayoutParams)v.getLayoutParams(); - params.topMargin = y; - v.setLayoutParams(params); - } - } - - public static float getY(final @NonNull View v) { - if (VERSION.SDK_INT >= 11) { - return ViewCompat.getY(v); - } else { - return ((ViewGroup.MarginLayoutParams)v.getLayoutParams()).topMargin; - } - } - - public static void setX(final @NonNull View v, final int x) { - if (VERSION.SDK_INT >= 11) { - ViewCompat.setX(v, x); - } else { - ViewGroup.MarginLayoutParams params = (ViewGroup.MarginLayoutParams)v.getLayoutParams(); - params.leftMargin = x; - v.setLayoutParams(params); - } - } - - public static float getX(final @NonNull View v) { - if (VERSION.SDK_INT >= 11) { - return ViewCompat.getX(v); - } else { - return ((LayoutParams)v.getLayoutParams()).leftMargin; - } - } - - public static void swapChildInPlace(ViewGroup parent, View toRemove, View toAdd, int defaultIndex) { - int childIndex = parent.indexOfChild(toRemove); - if (childIndex > -1) parent.removeView(toRemove); - parent.addView(toAdd, childIndex > -1 ? childIndex : defaultIndex); - } - - @SuppressWarnings("unchecked") - public static T inflateStub(@NonNull View parent, @IdRes int stubId) { - return (T)((ViewStub)parent.findViewById(stubId)).inflate(); + v.setBackground(drawable); } @SuppressWarnings("unchecked") @@ -106,10 +50,6 @@ public static T findById(@NonNull Activity parent, @IdRes int r return (T) parent.findViewById(resId); } - public static Stub findStubById(@NonNull Activity parent, @IdRes int resId) { - return new Stub((ViewStub)parent.findViewById(resId)); - } - private static Animation getAlphaAnimation(float from, float to, int duration) { final Animation anim = new AlphaAnimation(from, to); anim.setInterpolator(new FastOutSlowInInterpolator()); @@ -177,58 +117,4 @@ public static int dpToPx(Context context, int dp) { return (int)((dp * context.getResources().getDisplayMetrics().density) + 0.5); } - public static void updateLayoutParams(@NonNull View view, int width, int height) { - view.getLayoutParams().width = width; - view.getLayoutParams().height = height; - view.requestLayout(); - } - - public static int getLeftMargin(@NonNull View view) { - if (ViewCompat.getLayoutDirection(view) == ViewCompat.LAYOUT_DIRECTION_LTR) { - return ((ViewGroup.MarginLayoutParams) view.getLayoutParams()).leftMargin; - } - return ((ViewGroup.MarginLayoutParams) view.getLayoutParams()).rightMargin; - } - - public static int getRightMargin(@NonNull View view) { - if (ViewCompat.getLayoutDirection(view) == ViewCompat.LAYOUT_DIRECTION_LTR) { - return ((ViewGroup.MarginLayoutParams) view.getLayoutParams()).rightMargin; - } - return ((ViewGroup.MarginLayoutParams) view.getLayoutParams()).leftMargin; - } - - public static void setLeftMargin(@NonNull View view, int margin) { - if (ViewCompat.getLayoutDirection(view) == ViewCompat.LAYOUT_DIRECTION_LTR) { - ((ViewGroup.MarginLayoutParams) view.getLayoutParams()).leftMargin = margin; - } else { - ((ViewGroup.MarginLayoutParams) view.getLayoutParams()).rightMargin = margin; - } - view.forceLayout(); - view.requestLayout(); - } - - public static void setTopMargin(@NonNull View view, int margin) { - ((ViewGroup.MarginLayoutParams) view.getLayoutParams()).topMargin = margin; - view.requestLayout(); - } - - public static void setPaddingTop(@NonNull View view, int padding) { - view.setPadding(view.getPaddingLeft(), padding, view.getPaddingRight(), view.getPaddingBottom()); - } - - public static void setPaddingBottom(@NonNull View view, int padding) { - view.setPadding(view.getPaddingLeft(), view.getPaddingTop(), view.getPaddingRight(), padding); - } - - public static boolean isPointInsideView(@NonNull View view, float x, float y) { - int[] location = new int[2]; - - view.getLocationOnScreen(location); - - int viewX = location[0]; - int viewY = location[1]; - - return x > viewX && x < viewX + view.getWidth() && - y > viewY && y < viewY + view.getHeight(); - } } diff --git a/libsession/src/main/java/org/session/libsession/utilities/recipients/Recipient.java b/libsession/src/main/java/org/session/libsession/utilities/recipients/Recipient.java index 0aef1583872..ac0c9067c4f 100644 --- a/libsession/src/main/java/org/session/libsession/utilities/recipients/Recipient.java +++ b/libsession/src/main/java/org/session/libsession/utilities/recipients/Recipient.java @@ -718,7 +718,7 @@ public void setRegistered(@NonNull RegisteredState value) { } public synchronized @Nullable String getNotificationChannel() { - return !(Build.VERSION.SDK_INT >= 26) ? null : notificationChannel; + return notificationChannel; } public void setNotificationChannel(@Nullable String value) { From bc6718e9969cd6b77e0c6916f584d11b5ef1a024 Mon Sep 17 00:00:00 2001 From: ThomasSession Date: Mon, 19 Aug 2024 09:25:53 +1000 Subject: [PATCH 32/50] Feature/resource cleanup (#1623) * Removing unrequired APi checks * Bumping min sdk to 26 and further cleaning version checks * More clean ups * Removed unused class * Clean up * Clean up * Clean up * Clean up * Clean up * Clean up * Clean up * Clean up --- .../res/anim/camera_slide_from_bottom.xml | 8 - app/src/main/res/anim/fade_in.xml | 6 - app/src/main/res/anim/fade_out.xml | 6 - app/src/main/res/anim/fade_scale_in.xml | 17 -- app/src/main/res/anim/slide_from_top.xml | 11 -- app/src/main/res/anim/slide_to_top.xml | 7 - .../bottom_pause_to_play_animation.xml | 8 - .../bottom_play_to_pause_animation.xml | 8 - .../main/res/animator/rotate_90_animation.xml | 9 - .../animator/rotate_minus_90_animation.xml | 9 - .../upper_pause_to_play_animation.xml | 8 - .../upper_play_to_pause_animation.xml | 8 - app/src/main/res/color/icon_tab_selector.xml | 5 - .../drawable-hdpi/divet_lower_right_dark.png | Bin 117 -> 0 bytes .../res/drawable-hdpi/ic_account_box_dark.png | Bin 321 -> 0 bytes .../drawable-hdpi/ic_account_box_light.png | Bin 304 -> 0 bytes .../drawable-hdpi/ic_attach_grey600_24dp.png | Bin 3740 -> 0 bytes .../main/res/drawable-hdpi/ic_audio_light.png | Bin 1748 -> 0 bytes .../ic_circle_fill_white_48dp.png | Bin 1723 -> 0 bytes .../res/drawable-hdpi/ic_contact_picture.png | Bin 1128 -> 0 bytes .../drawable-hdpi/ic_document_small_light.png | Bin 988 -> 0 bytes .../main/res/drawable-hdpi/ic_image_light.png | Bin 287 -> 0 bytes .../drawable-hdpi/ic_movie_creation_light.png | Bin 204 -> 0 bytes .../res/drawable-hdpi/ic_note_to_self.png | Bin 274 -> 0 bytes .../drawable-hdpi/ic_photo_camera_light.png | Bin 407 -> 0 bytes .../drawable-hdpi/ic_unlocked_white_18dp.png | Bin 814 -> 0 bytes .../main/res/drawable-hdpi/ic_video_light.png | Bin 862 -> 0 bytes .../res/drawable-hdpi/ic_volume_up_light.png | Bin 367 -> 0 bytes .../res/drawable-hdpi/ic_warning_light.png | Bin 347 -> 0 bytes .../drawable-mdpi/divet_lower_right_dark.png | Bin 114 -> 0 bytes .../res/drawable-mdpi/ic_account_box_dark.png | Bin 253 -> 0 bytes .../drawable-mdpi/ic_account_box_light.png | Bin 236 -> 0 bytes .../drawable-mdpi/ic_attach_grey600_24dp.png | Bin 1943 -> 0 bytes .../main/res/drawable-mdpi/ic_audio_light.png | Bin 888 -> 0 bytes .../ic_circle_fill_white_48dp.png | Bin 1055 -> 0 bytes .../res/drawable-mdpi/ic_contact_picture.png | Bin 781 -> 0 bytes .../drawable-mdpi/ic_document_small_light.png | Bin 557 -> 0 bytes .../main/res/drawable-mdpi/ic_image_light.png | Bin 219 -> 0 bytes .../drawable-mdpi/ic_movie_creation_light.png | Bin 255 -> 0 bytes .../res/drawable-mdpi/ic_note_to_self.png | Bin 191 -> 0 bytes .../drawable-mdpi/ic_photo_camera_light.png | Bin 319 -> 0 bytes .../drawable-mdpi/ic_unlocked_white_18dp.png | Bin 543 -> 0 bytes .../main/res/drawable-mdpi/ic_video_light.png | Bin 542 -> 0 bytes .../res/drawable-mdpi/ic_volume_up_light.png | Bin 295 -> 0 bytes .../res/drawable-mdpi/ic_warning_light.png | Bin 290 -> 0 bytes .../drawable-xhdpi/divet_lower_right_dark.png | Bin 143 -> 0 bytes .../drawable-xhdpi/ic_account_box_dark.png | Bin 410 -> 0 bytes .../drawable-xhdpi/ic_account_box_light.png | Bin 379 -> 0 bytes .../ic_add_white_original_24dp.png | Bin 198 -> 0 bytes .../drawable-xhdpi/ic_attach_grey600_24dp.png | Bin 5950 -> 0 bytes .../res/drawable-xhdpi/ic_audio_light.png | Bin 2431 -> 0 bytes .../ic_circle_fill_white_48dp.png | Bin 2365 -> 0 bytes .../res/drawable-xhdpi/ic_contact_picture.png | Bin 1566 -> 0 bytes .../ic_document_small_light.png | Bin 1299 -> 0 bytes .../res/drawable-xhdpi/ic_image_light.png | Bin 363 -> 0 bytes .../ic_movie_creation_light.png | Bin 387 -> 0 bytes .../res/drawable-xhdpi/ic_note_to_self.png | Bin 282 -> 0 bytes .../drawable-xhdpi/ic_photo_camera_light.png | Bin 584 -> 0 bytes .../drawable-xhdpi/ic_unlocked_white_18dp.png | Bin 1112 -> 0 bytes .../res/drawable-xhdpi/ic_video_light.png | Bin 1075 -> 0 bytes .../res/drawable-xhdpi/ic_volume_up_light.png | Bin 538 -> 0 bytes .../res/drawable-xhdpi/ic_warning_light.png | Bin 504 -> 0 bytes .../divet_lower_right_dark.png | Bin 165 -> 0 bytes .../drawable-xxhdpi/ic_account_box_dark.png | Bin 585 -> 0 bytes .../drawable-xxhdpi/ic_account_box_light.png | Bin 540 -> 0 bytes .../ic_attach_grey600_24dp.png | Bin 3319 -> 0 bytes .../res/drawable-xxhdpi/ic_audio_light.png | Bin 3740 -> 0 bytes .../ic_circle_fill_white_48dp.png | Bin 1682 -> 0 bytes .../drawable-xxhdpi/ic_contact_picture.png | Bin 2111 -> 0 bytes .../ic_document_small_light.png | Bin 2107 -> 0 bytes .../res/drawable-xxhdpi/ic_image_light.png | Bin 494 -> 0 bytes .../ic_movie_creation_light.png | Bin 346 -> 0 bytes .../res/drawable-xxhdpi/ic_note_to_self.png | Bin 452 -> 0 bytes .../drawable-xxhdpi/ic_photo_camera_light.png | Bin 785 -> 0 bytes .../ic_unlocked_white_18dp.png | Bin 1679 -> 0 bytes .../res/drawable-xxhdpi/ic_video_light.png | Bin 1751 -> 0 bytes .../drawable-xxhdpi/ic_volume_up_light.png | Bin 720 -> 0 bytes .../res/drawable-xxhdpi/ic_warning_light.png | Bin 563 -> 0 bytes .../ic_circle_fill_white_48dp.png | Bin 2339 -> 0 bytes .../ic_document_small_light.png | Bin 3020 -> 0 bytes .../res/drawable-xxxhdpi/ic_note_to_self.png | Bin 545 -> 0 bytes .../ic_photo_camera_light.png | Bin 1851 -> 0 bytes .../ic_unlocked_white_18dp.png | Bin 650 -> 0 bytes .../res/drawable-xxxhdpi/oxen_dark_mode.png | Bin 14333 -> 0 bytes .../drawable/circle_tintable_4dp_inset.xml | 4 - .../colorpickerpreference_pref_swatch.xml | 13 -- .../res/drawable/compose_background_light.xml | 16 -- ...rsation_item_sent_indicator_text_shape.xml | 18 -- .../conversation_list_divider_shape.xml | 9 - .../conversation_list_item_background.xml | 10 - .../drawable/conversation_menu_gradient.xml | 8 - .../conversation_pinned_background.xml | 9 - app/src/main/res/drawable/cross.xml | 12 -- .../danger_dialog_button_background.xml | 11 -- .../drawable/default_session_background.xml | 12 -- ...ji_variation_selector_background_light.xml | 9 - app/src/main/res/drawable/ic_any_emoji_32.xml | 18 -- .../drawable/ic_baseline_arrow_forward_24.xml | 10 - .../res/drawable/ic_baseline_call_made_24.xml | 11 -- .../drawable/ic_baseline_call_missed_24.xml | 11 -- .../drawable/ic_baseline_call_received_24.xml | 11 -- .../main/res/drawable/ic_baseline_mic_24.xml | 10 - .../drawable/ic_baseline_night_mode_24.xml | 13 -- .../ic_baseline_pause_circle_filled_48.xml | 5 - .../ic_baseline_play_circle_filled_48.xml | 5 - .../drawable/ic_baseline_visibility_24.xml | 10 - .../ic_baseline_visibility_off_24.xml | 10 - .../drawable/ic_baseline_volume_mute_24.xml | 10 - app/src/main/res/drawable/ic_circle.xml | 12 -- .../drawable/ic_emoji_activity_light_20.xml | 9 - .../res/drawable/ic_emoji_animal_light_20.xml | 9 - .../drawable/ic_emoji_emoticon_light_20.xml | 9 - .../res/drawable/ic_emoji_flag_light_20.xml | 9 - .../res/drawable/ic_emoji_food_light_20.xml | 9 - .../res/drawable/ic_emoji_object_light_20.xml | 9 - .../drawable/ic_emoji_outline_keyboard.xml | 10 - .../res/drawable/ic_emoji_people_light_20.xml | 9 - .../res/drawable/ic_emoji_symbol_light_20.xml | 9 - .../res/drawable/ic_emoji_travel_light_20.xml | 9 - .../ic_launcher_foreground_monochrome.xml | 15 -- .../main/res/drawable/ic_outline_info_24.xml | 10 - .../res/drawable/ic_outline_keyboard_24.xml | 10 - .../res/drawable/ic_outline_mic_none_24.xml | 10 - .../res/drawable/ic_outline_mic_off_24.xml | 10 - .../res/drawable/ic_outline_night_mode_24.xml | 12 -- .../ic_outline_night_mode_auto_24.xml | 15 -- .../drawable/ic_outline_photo_camera_24.xml | 13 -- .../res/drawable/ic_outline_settings_24.xml | 10 - .../res/drawable/ic_outline_videocam_24.xml | 10 - .../drawable/ic_outline_videocam_off_24.xml | 10 - .../main/res/drawable/ic_recent_light_20.xml | 9 - .../drawable/ic_sticker_outline_keyboard.xml | 10 - app/src/main/res/drawable/ic_ui_mode_24.xml | 6 - ...message_bubble_background_received_end.xml | 14 -- ...sage_bubble_background_received_middle.xml | 14 -- ...ssage_bubble_background_received_start.xml | 14 -- .../message_bubble_background_sent_alone.xml | 10 - .../message_bubble_background_sent_end.xml | 14 -- .../message_bubble_background_sent_middle.xml | 14 -- .../message_bubble_background_sent_start.xml | 14 -- app/src/main/res/drawable/pause_icon.xml | 35 ---- .../res/drawable/pause_to_play_animation.xml | 18 -- app/src/main/res/drawable/play_icon.xml | 34 ---- .../res/drawable/play_to_pause_animation.xml | 18 -- .../res/drawable/pn_option_background.xml | 15 -- ..._option_background_deselect_transition.xml | 5 - ...pn_option_background_select_transition.xml | 5 - .../pn_option_background_selected.xml | 15 -- .../drawable/preference_single_no_padding.xml | 9 - ...utline_button_medium_background_accent.xml | 11 -- .../drawable/setting_button_background.xml | 9 - .../res/drawable/sticker_button_light.xml | 17 -- .../sticker_missing_background_light.xml | 9 - .../sticky_date_header_background_light.xml | 8 - .../unimportant_dialog_button_background.xml | 11 -- ...ortant_filled_button_medium_background.xml | 11 -- ...rtant_outline_button_medium_background.xml | 13 -- .../unread_count_background_light.xml | 14 -- .../res/layout/activity_join_public_chat.xml | 38 ---- .../main/res/layout/activity_link_device.xml | 20 -- .../res/layout/activity_message_detail.xml | 115 ----------- .../main/res/layout/backup_enable_dialog.xml | 90 --------- .../res/layout/contact_section_header.xml | 18 -- .../res/layout/dialog_list_preference.xml | 59 ------ app/src/main/res/layout/dialog_send_seed.xml | 56 ------ .../main/res/layout/emoji_display_item.xml | 38 ---- .../res/layout/fragment_call_bottom_sheet.xml | 77 -------- .../fragment_device_list_bottom_sheet.xml | 23 --- .../res/layout/fragment_enter_public_key.xml | 147 -------------- .../main/res/layout/fragment_new_message.xml | 91 --------- .../res/layout/fragment_view_my_qr_code.xml | 77 -------- .../layout/led_color_preference_widget.xml | 13 -- .../layout/preference_widget_color_swatch.xml | 7 - app/src/main/res/layout/quote_view.xml | 185 ------------------ .../react_with_any_emoji_status_fade.xml | 7 - .../recipient_preference_contact_widget.xml | 14 -- .../main/res/layout/view_profile_overflow.xml | 18 -- app/src/main/res/menu/menu_home.xml | 16 -- .../main/res/values-notnight-v21/colors.xml | 31 --- app/src/main/res/values-v26/values.xml | 2 +- app/src/main/res/values/arrays.xml | 66 ------- app/src/main/res/values/attrs.xml | 4 - app/src/main/res/values/colors.xml | 44 ----- app/src/main/res/values/dimens.xml | 31 --- app/src/main/res/values/ids.xml | 3 - app/src/main/res/values/integers.xml | 2 - app/src/main/res/values/styles.xml | 39 ---- app/src/main/res/values/text_styles.xml | 6 - app/src/main/res/values/themes.xml | 10 - app/src/main/res/values/values.xml | 1 - app/src/main/res/values/vector_paths.xml | 20 -- libsession/src/main/res/values/arrays.xml | 47 ----- libsession/src/main/res/values/attrs.xml | 2 - libsession/src/main/res/values/colors.xml | 16 -- libsession/src/main/res/values/dimens.xml | 27 --- libsession/src/main/res/values/ids.xml | 3 - libsession/src/main/res/values/integers.xml | 1 - .../src/main/res/values/text_styles.xml | 6 - libsession/src/main/res/values/values.xml | 1 - .../src/main/res/values/vector_paths.xml | 20 -- 200 files changed, 1 insertion(+), 2459 deletions(-) delete mode 100644 app/src/main/res/anim/camera_slide_from_bottom.xml delete mode 100644 app/src/main/res/anim/fade_in.xml delete mode 100644 app/src/main/res/anim/fade_out.xml delete mode 100644 app/src/main/res/anim/fade_scale_in.xml delete mode 100644 app/src/main/res/anim/slide_from_top.xml delete mode 100644 app/src/main/res/anim/slide_to_top.xml delete mode 100644 app/src/main/res/animator/bottom_pause_to_play_animation.xml delete mode 100644 app/src/main/res/animator/bottom_play_to_pause_animation.xml delete mode 100644 app/src/main/res/animator/rotate_90_animation.xml delete mode 100644 app/src/main/res/animator/rotate_minus_90_animation.xml delete mode 100644 app/src/main/res/animator/upper_pause_to_play_animation.xml delete mode 100644 app/src/main/res/animator/upper_play_to_pause_animation.xml delete mode 100644 app/src/main/res/color/icon_tab_selector.xml delete mode 100644 app/src/main/res/drawable-hdpi/divet_lower_right_dark.png delete mode 100644 app/src/main/res/drawable-hdpi/ic_account_box_dark.png delete mode 100644 app/src/main/res/drawable-hdpi/ic_account_box_light.png delete mode 100644 app/src/main/res/drawable-hdpi/ic_attach_grey600_24dp.png delete mode 100644 app/src/main/res/drawable-hdpi/ic_audio_light.png delete mode 100644 app/src/main/res/drawable-hdpi/ic_circle_fill_white_48dp.png delete mode 100644 app/src/main/res/drawable-hdpi/ic_contact_picture.png delete mode 100644 app/src/main/res/drawable-hdpi/ic_document_small_light.png delete mode 100644 app/src/main/res/drawable-hdpi/ic_image_light.png delete mode 100644 app/src/main/res/drawable-hdpi/ic_movie_creation_light.png delete mode 100644 app/src/main/res/drawable-hdpi/ic_note_to_self.png delete mode 100644 app/src/main/res/drawable-hdpi/ic_photo_camera_light.png delete mode 100644 app/src/main/res/drawable-hdpi/ic_unlocked_white_18dp.png delete mode 100644 app/src/main/res/drawable-hdpi/ic_video_light.png delete mode 100644 app/src/main/res/drawable-hdpi/ic_volume_up_light.png delete mode 100644 app/src/main/res/drawable-hdpi/ic_warning_light.png delete mode 100644 app/src/main/res/drawable-mdpi/divet_lower_right_dark.png delete mode 100644 app/src/main/res/drawable-mdpi/ic_account_box_dark.png delete mode 100644 app/src/main/res/drawable-mdpi/ic_account_box_light.png delete mode 100644 app/src/main/res/drawable-mdpi/ic_attach_grey600_24dp.png delete mode 100644 app/src/main/res/drawable-mdpi/ic_audio_light.png delete mode 100644 app/src/main/res/drawable-mdpi/ic_circle_fill_white_48dp.png delete mode 100644 app/src/main/res/drawable-mdpi/ic_contact_picture.png delete mode 100644 app/src/main/res/drawable-mdpi/ic_document_small_light.png delete mode 100644 app/src/main/res/drawable-mdpi/ic_image_light.png delete mode 100644 app/src/main/res/drawable-mdpi/ic_movie_creation_light.png delete mode 100644 app/src/main/res/drawable-mdpi/ic_note_to_self.png delete mode 100644 app/src/main/res/drawable-mdpi/ic_photo_camera_light.png delete mode 100644 app/src/main/res/drawable-mdpi/ic_unlocked_white_18dp.png delete mode 100644 app/src/main/res/drawable-mdpi/ic_video_light.png delete mode 100644 app/src/main/res/drawable-mdpi/ic_volume_up_light.png delete mode 100644 app/src/main/res/drawable-mdpi/ic_warning_light.png delete mode 100644 app/src/main/res/drawable-xhdpi/divet_lower_right_dark.png delete mode 100644 app/src/main/res/drawable-xhdpi/ic_account_box_dark.png delete mode 100644 app/src/main/res/drawable-xhdpi/ic_account_box_light.png delete mode 100644 app/src/main/res/drawable-xhdpi/ic_add_white_original_24dp.png delete mode 100644 app/src/main/res/drawable-xhdpi/ic_attach_grey600_24dp.png delete mode 100644 app/src/main/res/drawable-xhdpi/ic_audio_light.png delete mode 100644 app/src/main/res/drawable-xhdpi/ic_circle_fill_white_48dp.png delete mode 100644 app/src/main/res/drawable-xhdpi/ic_contact_picture.png delete mode 100644 app/src/main/res/drawable-xhdpi/ic_document_small_light.png delete mode 100644 app/src/main/res/drawable-xhdpi/ic_image_light.png delete mode 100644 app/src/main/res/drawable-xhdpi/ic_movie_creation_light.png delete mode 100644 app/src/main/res/drawable-xhdpi/ic_note_to_self.png delete mode 100644 app/src/main/res/drawable-xhdpi/ic_photo_camera_light.png delete mode 100644 app/src/main/res/drawable-xhdpi/ic_unlocked_white_18dp.png delete mode 100644 app/src/main/res/drawable-xhdpi/ic_video_light.png delete mode 100644 app/src/main/res/drawable-xhdpi/ic_volume_up_light.png delete mode 100644 app/src/main/res/drawable-xhdpi/ic_warning_light.png delete mode 100644 app/src/main/res/drawable-xxhdpi/divet_lower_right_dark.png delete mode 100644 app/src/main/res/drawable-xxhdpi/ic_account_box_dark.png delete mode 100644 app/src/main/res/drawable-xxhdpi/ic_account_box_light.png delete mode 100644 app/src/main/res/drawable-xxhdpi/ic_attach_grey600_24dp.png delete mode 100644 app/src/main/res/drawable-xxhdpi/ic_audio_light.png delete mode 100644 app/src/main/res/drawable-xxhdpi/ic_circle_fill_white_48dp.png delete mode 100644 app/src/main/res/drawable-xxhdpi/ic_contact_picture.png delete mode 100644 app/src/main/res/drawable-xxhdpi/ic_document_small_light.png delete mode 100644 app/src/main/res/drawable-xxhdpi/ic_image_light.png delete mode 100644 app/src/main/res/drawable-xxhdpi/ic_movie_creation_light.png delete mode 100644 app/src/main/res/drawable-xxhdpi/ic_note_to_self.png delete mode 100644 app/src/main/res/drawable-xxhdpi/ic_photo_camera_light.png delete mode 100644 app/src/main/res/drawable-xxhdpi/ic_unlocked_white_18dp.png delete mode 100644 app/src/main/res/drawable-xxhdpi/ic_video_light.png delete mode 100644 app/src/main/res/drawable-xxhdpi/ic_volume_up_light.png delete mode 100644 app/src/main/res/drawable-xxhdpi/ic_warning_light.png delete mode 100644 app/src/main/res/drawable-xxxhdpi/ic_circle_fill_white_48dp.png delete mode 100644 app/src/main/res/drawable-xxxhdpi/ic_document_small_light.png delete mode 100644 app/src/main/res/drawable-xxxhdpi/ic_note_to_self.png delete mode 100644 app/src/main/res/drawable-xxxhdpi/ic_photo_camera_light.png delete mode 100644 app/src/main/res/drawable-xxxhdpi/ic_unlocked_white_18dp.png delete mode 100644 app/src/main/res/drawable-xxxhdpi/oxen_dark_mode.png delete mode 100644 app/src/main/res/drawable/circle_tintable_4dp_inset.xml delete mode 100644 app/src/main/res/drawable/colorpickerpreference_pref_swatch.xml delete mode 100644 app/src/main/res/drawable/compose_background_light.xml delete mode 100644 app/src/main/res/drawable/conversation_item_sent_indicator_text_shape.xml delete mode 100644 app/src/main/res/drawable/conversation_list_divider_shape.xml delete mode 100644 app/src/main/res/drawable/conversation_list_item_background.xml delete mode 100644 app/src/main/res/drawable/conversation_menu_gradient.xml delete mode 100644 app/src/main/res/drawable/conversation_pinned_background.xml delete mode 100644 app/src/main/res/drawable/cross.xml delete mode 100644 app/src/main/res/drawable/danger_dialog_button_background.xml delete mode 100644 app/src/main/res/drawable/default_session_background.xml delete mode 100644 app/src/main/res/drawable/emoji_variation_selector_background_light.xml delete mode 100644 app/src/main/res/drawable/ic_any_emoji_32.xml delete mode 100644 app/src/main/res/drawable/ic_baseline_arrow_forward_24.xml delete mode 100644 app/src/main/res/drawable/ic_baseline_call_made_24.xml delete mode 100644 app/src/main/res/drawable/ic_baseline_call_missed_24.xml delete mode 100644 app/src/main/res/drawable/ic_baseline_call_received_24.xml delete mode 100644 app/src/main/res/drawable/ic_baseline_mic_24.xml delete mode 100644 app/src/main/res/drawable/ic_baseline_night_mode_24.xml delete mode 100644 app/src/main/res/drawable/ic_baseline_pause_circle_filled_48.xml delete mode 100644 app/src/main/res/drawable/ic_baseline_play_circle_filled_48.xml delete mode 100644 app/src/main/res/drawable/ic_baseline_visibility_24.xml delete mode 100644 app/src/main/res/drawable/ic_baseline_visibility_off_24.xml delete mode 100644 app/src/main/res/drawable/ic_baseline_volume_mute_24.xml delete mode 100644 app/src/main/res/drawable/ic_circle.xml delete mode 100644 app/src/main/res/drawable/ic_emoji_activity_light_20.xml delete mode 100644 app/src/main/res/drawable/ic_emoji_animal_light_20.xml delete mode 100644 app/src/main/res/drawable/ic_emoji_emoticon_light_20.xml delete mode 100644 app/src/main/res/drawable/ic_emoji_flag_light_20.xml delete mode 100644 app/src/main/res/drawable/ic_emoji_food_light_20.xml delete mode 100644 app/src/main/res/drawable/ic_emoji_object_light_20.xml delete mode 100644 app/src/main/res/drawable/ic_emoji_outline_keyboard.xml delete mode 100644 app/src/main/res/drawable/ic_emoji_people_light_20.xml delete mode 100644 app/src/main/res/drawable/ic_emoji_symbol_light_20.xml delete mode 100644 app/src/main/res/drawable/ic_emoji_travel_light_20.xml delete mode 100644 app/src/main/res/drawable/ic_launcher_foreground_monochrome.xml delete mode 100644 app/src/main/res/drawable/ic_outline_info_24.xml delete mode 100644 app/src/main/res/drawable/ic_outline_keyboard_24.xml delete mode 100644 app/src/main/res/drawable/ic_outline_mic_none_24.xml delete mode 100644 app/src/main/res/drawable/ic_outline_mic_off_24.xml delete mode 100644 app/src/main/res/drawable/ic_outline_night_mode_24.xml delete mode 100644 app/src/main/res/drawable/ic_outline_night_mode_auto_24.xml delete mode 100644 app/src/main/res/drawable/ic_outline_photo_camera_24.xml delete mode 100644 app/src/main/res/drawable/ic_outline_settings_24.xml delete mode 100644 app/src/main/res/drawable/ic_outline_videocam_24.xml delete mode 100644 app/src/main/res/drawable/ic_outline_videocam_off_24.xml delete mode 100644 app/src/main/res/drawable/ic_recent_light_20.xml delete mode 100644 app/src/main/res/drawable/ic_sticker_outline_keyboard.xml delete mode 100644 app/src/main/res/drawable/ic_ui_mode_24.xml delete mode 100644 app/src/main/res/drawable/message_bubble_background_received_end.xml delete mode 100644 app/src/main/res/drawable/message_bubble_background_received_middle.xml delete mode 100644 app/src/main/res/drawable/message_bubble_background_received_start.xml delete mode 100644 app/src/main/res/drawable/message_bubble_background_sent_alone.xml delete mode 100644 app/src/main/res/drawable/message_bubble_background_sent_end.xml delete mode 100644 app/src/main/res/drawable/message_bubble_background_sent_middle.xml delete mode 100644 app/src/main/res/drawable/message_bubble_background_sent_start.xml delete mode 100644 app/src/main/res/drawable/pause_icon.xml delete mode 100644 app/src/main/res/drawable/pause_to_play_animation.xml delete mode 100644 app/src/main/res/drawable/play_icon.xml delete mode 100644 app/src/main/res/drawable/play_to_pause_animation.xml delete mode 100644 app/src/main/res/drawable/pn_option_background.xml delete mode 100644 app/src/main/res/drawable/pn_option_background_deselect_transition.xml delete mode 100644 app/src/main/res/drawable/pn_option_background_select_transition.xml delete mode 100644 app/src/main/res/drawable/pn_option_background_selected.xml delete mode 100644 app/src/main/res/drawable/preference_single_no_padding.xml delete mode 100644 app/src/main/res/drawable/prominent_outline_button_medium_background_accent.xml delete mode 100644 app/src/main/res/drawable/setting_button_background.xml delete mode 100644 app/src/main/res/drawable/sticker_button_light.xml delete mode 100644 app/src/main/res/drawable/sticker_missing_background_light.xml delete mode 100644 app/src/main/res/drawable/sticky_date_header_background_light.xml delete mode 100644 app/src/main/res/drawable/unimportant_dialog_button_background.xml delete mode 100644 app/src/main/res/drawable/unimportant_filled_button_medium_background.xml delete mode 100644 app/src/main/res/drawable/unimportant_outline_button_medium_background.xml delete mode 100644 app/src/main/res/drawable/unread_count_background_light.xml delete mode 100644 app/src/main/res/layout/activity_join_public_chat.xml delete mode 100644 app/src/main/res/layout/activity_link_device.xml delete mode 100644 app/src/main/res/layout/activity_message_detail.xml delete mode 100644 app/src/main/res/layout/backup_enable_dialog.xml delete mode 100644 app/src/main/res/layout/contact_section_header.xml delete mode 100644 app/src/main/res/layout/dialog_list_preference.xml delete mode 100644 app/src/main/res/layout/dialog_send_seed.xml delete mode 100644 app/src/main/res/layout/emoji_display_item.xml delete mode 100644 app/src/main/res/layout/fragment_call_bottom_sheet.xml delete mode 100644 app/src/main/res/layout/fragment_device_list_bottom_sheet.xml delete mode 100644 app/src/main/res/layout/fragment_enter_public_key.xml delete mode 100644 app/src/main/res/layout/fragment_new_message.xml delete mode 100644 app/src/main/res/layout/fragment_view_my_qr_code.xml delete mode 100644 app/src/main/res/layout/led_color_preference_widget.xml delete mode 100644 app/src/main/res/layout/preference_widget_color_swatch.xml delete mode 100644 app/src/main/res/layout/quote_view.xml delete mode 100644 app/src/main/res/layout/react_with_any_emoji_status_fade.xml delete mode 100644 app/src/main/res/layout/recipient_preference_contact_widget.xml delete mode 100644 app/src/main/res/layout/view_profile_overflow.xml delete mode 100644 app/src/main/res/menu/menu_home.xml delete mode 100644 app/src/main/res/values/vector_paths.xml delete mode 100644 libsession/src/main/res/values/vector_paths.xml 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 d37eece35cec67c894448b5fe6fc253c1689ab8c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 117 zcmeAS@N?(olHy`uVBq!ia0vp^LLkh+0wn(&ce?|mY&=~YLn;`1PdRcj7z!LX5MlkI zljqIiAnios_lg$PKWFTXStjDW5d#U7cgknYLs&CRKC|&gx1Sjzsv?6(*(xEyPi`F5=K)$YuIqO}2 zfr9s)|KtOg+>tXObs_ecu)wzk_w4`v_$8aRB)0<-6y4f^l>~UP1ERJRsJNz2M#eEW ze5ip(dfz6Gcv1o-hd*X}yz79=*6Ax9&~KeS(E&TH)5(?t`X$o8Wc_EV-)Q=yA|{p{ z748L~Va}8(a~dsBQZQyf@?{SX7*kMg4$K&mTIW-S%>03xb0V`QA}*+XfQnP|$2g;M z2S&yrM(#l71nfEk#<9+U|8i8kq3G2PJaqm0ejvDjJd>}xLaqE4hdXj3E)@C%Ad(Ff T@}@sV00000NkvXXu0mjfp!ABu 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 042b0b8559805e0777f4389d5bbb388955ea4781..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 304 zcmV-00nh%4P)%uX92MrAByE1RJr-%4X}=Ey^WMD=lI9~p zoJ7J;I?jV9SyjA!aPu4iD7aG2s{!?x}|a95?)yTU>l#MGUL>Ko%7^ix0%2 zANU`m;#2$b`=Wh)zYutUykm|%p$;P!XN^IaMB)p$NFU_%4{p=|0000goSki9QWf zo8JVqlq*(^)qcyi%C~{mS{4FbT+cF4D=7W z5Bv@(9^`exDVTa+*BNGb>+`_h+YkjrwrfD7^8g~=8;FWd0g;>wYlgTlf~eTy?I15@ z^;Ij)nLvK!z2_V6i)Dgo3Y1D&z`u}ofgB-Z!>kNL7MSbvKS3p<)fZmmSySMLFYRq$ zRi3pTB)72mK#eQ9Z1AsPYzM(%FY=(e*?bfZukVln+*=(s0lB4EfNr_I?;C4DWrA4` z^b5rUFH2b<2MKvFJ4kPXMQK`aMD`1Czb2)@xXW6QNBv!&BMRW1NUb4mq^=Q6Y|P~X zpJw|zgxI8&aLgd~1U^?f0rESp_xsCRkZUl)IiOtb5b%$sQy{ky{ta{68{UV77ig%8 z?3Ll(d`g62eWXM<`H?sg^tz6nfD7*E2D)Hr2NW-8B*T%z?!Q9c&YlV2|A{3KjMk3+ zkW@goRUdUo1u9Be1$4#v{`{K?Mr;9;&Fuw#iL@2u_Cg8F3()fro5HSx5=_yVn~Ww(?k1HDJ#P~{T&gU6jw3@q+ksH%fj z3szd63Tr_V)Ix^RV5WfbH_-;)DW$=*goOe6L{Ng+2TCxf;cjIzL)bs1>u|oiDI46E z%ZDJoy@wT~Af*n9{a)f!@R`F+0ZO4ts^L-u$TrpwK53}?6_{fh&rhd^P!w79b8rqd z?3io4UOOOXFdNlER^c-sJGTq?aH%TDyT#tH&`*C3=G)wJrMq?wW z;r`-VxOIt(fKKzY!TC`KmI$21Tpd{KYX?*&7$a~sxVox7aW_HUEKY+(LHc-@oyN|C z5=;SPH=>C!^qKSsQXiNaf>$p`Fgg0)Ap9!5uOjP7Sie?V7flPZZ3P-F{Q&l(LOtlX zR5t?l8Vn7gTC~L<9&)S_^vjik;Nm-C72y6yDggF3mpn=bp;zu=&cU~|DnHXHtetDff$*#BI4Hp!fz_Lh-C^1_ z+XA5E(qKZMQ<`oN>H5|(nhHazOWAN~khl)K&T6yF`iF)B2=6El1Z4{m z2+PkK&q8#ZH5jOkbQbL4f(g1r=m_?$HGB?Lds-gCy{7VA=vQcthYQu5^^p#My|HKo ziZOo=^q=x}`Q=XYF4MJR@a5dKbp@H?ssO7megp9>4G$o?o}39vFgM}Ge<%#XI%w1N zm6_sZ@NQ1qV8g!*qhUn2HcKhN#Dm%9o(?lclxzboN=gCySV1jakLor;(saX9c>AU$ z19B5&wG4P{o(pN$gtx(s(?vrx$&cZ?&XNIg;ni}r0tro?4A;x!K?x=a%q1Sn z!E~`W9r#Dm&tM-cOoASN(;bGyCx+wTmuzheIdkm;p??qae#qz+!-4)F|22I5lXn7) zTk*UL{2H#!W*rAIPxzxIcMqtxwtyx7a=VzbsR^rqBDn(Kdr%$K10fU4oKXNJm@rU+ zxeK>^Spb9`bOvL}0G|ZPgiRX_k)Q;V1WGVFV0jC#cfk~2><#=MrNQ)s9(#4UkobvV z0r(|b-i4f__COf$+!;)y9&{o6_pqVZxCF+nC^3LqV@28yW$CK0jnP^-H`76KnbP@ZgytD5PHcGOapNOR2n8%fsM&-|A3)M z_NSl(^DV@Od(8uJY*AC-2THMEuOkFO54)~EBwjUC0lyC{C6JRKcYp!S%r_u?Rpi^C zuWdL2-&8Yx4r4;u0Z<0}`>@_!3V|sWM|CYiFwk!E6QH`Td6a2E;!L3XN^}ajnK&7| zJF`KXp$`en5P!ZvNYfKKs-}q1!bju z1(aY0K+pBMIN0^V&`jt@`O(G7}(eLA7R4+V>Zm`TsR4IC$!zUT_=12y}WhZAu&XKEWPDr znE=1nlI_s9mw78>*rM~nxLMx_5~^&3IzLBtpyr}k{hHZc|3s@+h3%@;S<`feu+T$ zAh1B&gRSiAz7Tw8mlVP6W%4-abI05bu74A=2;6V+4PZ+T<1HBeyS74Bf_V(fFH7?w zrnRX7@Kxp0z^a>T0=xRE!IWu1Tt3iK?Z#Bg(ULs1^AqcM77FwMor8i_T8mHyHTCzj zRNEc%g0vQ_&9!NCZ8dFL=YDsa0sR_K2Bh54Wx}^99xK3iw&en3{VaPz-?h$QqI1B5 z__nZRr*Q`;!4!fLOg=2H=L|;71U{HKn(&_urc6N03goM88I-?niE6v{9DP&H9q6et zDk%b=+Li+yBO|cd@=(@7u44=!IgAz<3m_L!ef|%Q;7Qk1hVZidFo4{`T*E=D z;#}aiXlK5~jy+1|KK)r>mDzA;bACk%&?R~TEP@*cN~E5^^x_Vn6tfN1U3oqkI-RB_ z&{?8v2>$ZXI_SMAehgeMiQWXBt8^1!+dAVp7}iSLt0}>xLfk%SJ%}evLBQp(?kdt| zLzerx!IUY^X-@19x}MI@#Yp&k=A2r<*o=`-yiN3i58o}l?jta`$1tF-F)e`xihF=R z`?BZ9yboS2O$OL~c#aJin{@#g6WhY#1J3Im!8{WV16SGkxigs84#ZK)Y&En^pfaAQ zgD5THDuH*pwsp8T$)g=K99}HLFL&r?=+#t;h6hDrE8st-W-9#c6x?9PvBhcX1Du!C z4s5(Q8oavELfBs4tu+kVXdea2f^sb^jMesXU(C~X|9oev2ke}b4@`DF>ix?Cau%^R z@O`BmkPnL`&}Wu)b0MT=*Ek7I!#3)%q}@qbk?&Qe}LT;y1whK4%8x7TI~d5aJQ5i4{( zVfAxk0&sJSA45?!@`1x`sR5+S*9Q5lD%x#*zM}%@-kdtYr%&95!P}$ln?Y$YN8zh{ z>F@AmHL)x3JE#iCA&v!X<-aV#0t%@IR~Ew1X}ssB!S=2JOsuSyh8t0tV@Q zK-r@$1m)*4P;R82850?sfbw}0P&PwWxINOo7#2S>H-pWG#2LWX(~eZ-gIbrQCT}X3 zmj>kg`ibL3 z#c+J0{VZ%xihm0CL&T-PWwBPOt>%w`V&8NyuUuSD+8hhXK<#{oUs>M2(~e$!rfEO@ z`e_GKRccJz2fBe~`oq(AE6@V^ zQ8i5)L%1f|)`h&Z11h$Nl>kLJ&V=t&)O%EP`QHEl0RR7=BFRK-8P;VLRHdr<2uhTE*u=-3!#g0LI zeoX(EAvM0FshDm%fGXoeS;2czGm+%015{3Vy~pPpX&h>WStv0c8dXD_K_QR6NorH?$nm`UxHRC^J zW)fdNZr-JgSV?92y7F!zT1n>`394W0H7+UjaYuHP;n@aCa#^&bD;xj4ZAYL0s(@jM zeb&QI)1$cYwqYW3J_)3NKzpL^U;AxiyZVxdFimz5!4?8>QwL?=l zRjzu_Wr@j(`J_3?tz~Y7ljLttp*&ZrM$ZE=bGCdKM!kUjAF(fc${xoP=kLm0+!CuLpMaZnIk z1{IMitu>Z~U~4Kw!Txcg$S$+@0;m?EH+hnXp4evx#TUZ8n+WnJwke3+`uWb_?SoqJ zY3kwRO+9o<;!Q!DR=n;U3^CnL(7v6Z^IHIrf1fx-t)laX0Q?|BoOG_z0L-$XOy4MI zyG@PY@52C=8!-|W7`_@F-jYXJ6h`Uo&x5zG<&qYQ_wYwCfQ=cxzX{s>1JK+9303MP zfLXp2>C02X7SEnH2$``s&I-)(r?Bn{Titrx%?}@C51js%ABe2plzFp|8*Z$RD^D(| z`1Ri7Fx*KiLl#a2dSP>M;6TaeSix#IzZcmx&VT>%$X$(QVw--x6VWUR!)R@mb0GM$ z^P@X4al_#)lC>+^D7_#A>e=z~;8g>Wv|CpOMaKOoSB~>?=kv3m7mxyMs`#1-YeJ;U zJ#m)M8}Ba{E_xNV6tx{tY;8Voj=EFX=NK&$o}itJn=!`4?sFzO49R1hm#?UIAu1sS z42|&v)^Hcv4}l6@N_wv!Iwmdwurf4vLLGrSauLb^`Yf^YCw6L9hclTJ4Dr}=wJnZK zMU@sqHYUE{Ktso`Z$=|fj;cB0ZkFuR9ydWU&5HZAj)uDl6w>TgUmTm^=7#9HoP9mL z<#O0JA-WjgkG{N%LUWa;@>7A!EHxwq==0LtI`1;OB_tF`T3YE;$E5QxYmPS6swoE^`i7y*3gxGjw(+ zlZ^I*o!N^tDNy%}4PC{$xXP0By63tW0>QjjMtPt4BHtXTf>j?BXdOjji2C_Vj=DBP z_p5%)>L;ugGtx~#yy4blt~fS8KD04`l@qRKRth75Q+v@Af3J~<)Bd;q$4#Ks$21F= z-Oe8v-IJCz$_99i^62nt&Z%xDclkV1J)XzJemDKZrt0h3-zBtqpE24J9gB8&mg?Y^ zTl`>$g;WW&rfyB|gr+BsE(5%$5iR|ZRq6%>hrpN~S-x%k^v?x@gc)8!$+7U$eHCe4 zq%f_OnYi)&$DH=S)PkTNxrot!b$LnmPTk?9)h?z#{6ibWd7AV&rb+nvyH#s>hK#!` zE_pfUD(c8{l4GEcUvfnRkyLL0+QJ}xzuvF50Lxcqfje0NqFl37t+#YQ?kJ{1ju3UE zfzbN}$yqvP@Eafg3vK&<$xkl%Qu490@0=&csNV2HX%NOgbEC7&Y3?<^y;<@{_aRUG z*%$q|21>aGQ~B*;H`yOIGhEn09*fH-X&vroSqG2BTBN8w|7M4gDQbd8sR5yI-tSr5 zThmKq61*1T^JUIo1>|EYrx06*ntry+B>?t+*fTJ^x_lU+avuD^?hx{=r7j3rdJlPY z;%Qx02zdi$%{L~FbeoKP6@U{apSPW5w=I%POC1T@*x7+y6E30@Em-V0#8@)f8Sl6y zR&t>RMw_)-^bTF~A)}2tlGKlL9UAfScTnvms-cr8P#S1u!Dm^jc7#R35BaHz9o#n% zs*`(&?hqZw)4U_UI<#H2)u)~%50FVtN{rkc2te{fcTQwfZVvL;yk+CaRg+D2^JZoA zWoXfokQ5mP-Th?JuJJ0sc>OzhR5(vcXE{+vi<2L^9!RYDfiCSw<#l8=vL7rgbf`bi mec3xsnKKqf_5WWwe`6C!yvxq=1JiDBT?`Hkr!`Sx|MM3s9thb0 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 a417946f2acf524ad1da066795fa619d57e8baf3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1723 zcmV;s21NOZP)OnzqLk|ursDlRu z$8}sl1zQ~4PHxZ&?lU;3pmtZM{~7Toe?~=QMlMyAnNtrQT|{R^{CN2zGUCV2nop91 zrU+wp(gw60Q-KOF6{rA{5(w?iF=ryKow3(W#MYTHP15KjIyeaYe}konu+Chy^kL3k zGjiicEf6}q?vx$JJd1f6^Caf+%+1VA%tP!oe>3+pe`c;Qzhr*STxR|d(MDq!l|Zi1 zr!cQ%E-}w!9$K;9KbapfUtzvTMz~O$ji70PFqCwKS1@lRBg{rsj%xz+6{M)L$R;HNrJpm&QBHrGPE9*pvs! zUBjGmH}fjdpN!l4o6c%8C*~ z;67wT<{iu(9ve%ejzH2d=9UMUH{0?xO6QBFg>m5ug?Vk>mOaY6oy=TSS){50 zNrOi-pJiT1>7*;eDWTB`<3hJ3-iw>Gn|U?!H>xO76@jFoW63-{PN(sDO$Z@x+QYn@ z`4d$Tsj3HIo_U@8;*47u7YX@Ax`es6Y-MHlH)wc)`8>5#96;Lw+hF4v%;(6=m!)hh zBhajE7!&dZ)J|~(K9MeBK0xWyHLjw_Atnu8$9$67Esln7xLwP9iX!Ku7=ffBY$`t? zv#HdJzqX1akPeE?*aR;!zoQ6|$_%mlnMY7|_1boE0I^dxv*^Qp(O)j`AW;}kfY->l zsa_p-K_Ea6HHd`i;bP{SRNzIiL+k_Q*^~+-aMk`bqCL(7Fb z)XpR0MMOC|SZT7P_o}D+uuv`C%On|*h zkMvkbpKuUxBAFAEA^1E<%5XFTab!a4!|)PFf6%wX$n+H$(j)IFwn+Zdhdx01gTA4U zi>f>WpFlDePj)Kn8+5;CKHiBb#2DJ=3<(*M}ceVm{L(l?~rcvsRP zfv}0&KMIRl3iQ>RtQ8UnL3vqv_iYs|(EUPR*L)Y{%|R=wl0Ht9n?Gb51(0-$;un(vmv#X_D%#D< zYV)o@c~{`u4M?AIZ*-ABQZce1kfg2OvgwisxtSe1nQu|VjV_Zwg}5zNoLx_#`kl-v z#`iH_jtQ8Fw;&_yHX2U#ZSzpmo-UtnB_mGMjg5^cO5!7O`yL|`;2uymF{y3v