From 96d5803823964ffa01ba539a5b08c634d51e7d36 Mon Sep 17 00:00:00 2001 From: ThomasSession Date: Tue, 10 Feb 2026 15:51:54 +1100 Subject: [PATCH 1/4] Removed annimon:streams dependency --- app/build.gradle.kts | 1 - .../libsession/avatars/AvatarHelper.java | 42 ------- .../emoji/RecentEmojiPageModel.java | 9 +- .../emoji/StaticEmojiPageModel.java | 6 - .../conversation/v2/ConversationActivityV2.kt | 18 ++- .../securesms/conversation/v2/Util.kt | 111 ----------------- .../securesms/database/GroupDatabase.java | 17 +-- .../securesms/database/MmsDatabase.kt | 35 ++---- .../securesms/database/RecipientDatabase.java | 7 -- .../securesms/database/SearchDatabase.java | 20 +-- .../securesms/database/SmsDatabase.java | 6 +- .../securesms/database/ThreadDatabase.java | 11 +- .../linkpreview/LinkPreviewUtil.java | 37 ++++-- .../securesms/mediasend/MediaRepository.java | 21 ++-- .../securesms/mediasend/MediaSendViewModel.kt | 48 +++----- .../thoughtcrime/securesms/mms/SlideDeck.java | 11 +- .../securesms/net/ChunkedDataFetcher.java | 114 ++++++++++-------- .../net/CompositeRequestController.java | 14 +-- .../securesms/permissions/Permissions.java | 35 ++++-- .../permissions/PermissionsRequest.java | 2 +- .../securesms/reactions/EmojiCount.java | 2 - .../reactions/ReactionsViewModel.java | 50 ++++---- .../any/ReactWithAnyEmojiRepository.java | 18 ++- .../any/ReactWithAnyEmojiViewModel.java | 17 +-- .../TypingStatusRepository.java | 14 ++- .../securesms/util/SearchUtil.java | 11 +- .../util/adapter/mapping/MappingModelList.kt | 38 +----- gradle/libs.versions.toml | 2 - 28 files changed, 276 insertions(+), 441 deletions(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 4bf38da30f..56a0d4e02d 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -395,7 +395,6 @@ dependencies { implementation(libs.subsampling.scale.image.view) { exclude(group = "com.android.support", module = "support-annotations") } - implementation(libs.stream) implementation(libs.androidx.sqlite.ktx) implementation(libs.sqlcipher.android) implementation(libs.kotlinx.serialization.json) diff --git a/app/src/main/java/org/session/libsession/avatars/AvatarHelper.java b/app/src/main/java/org/session/libsession/avatars/AvatarHelper.java index 77c66bb157..b446654875 100644 --- a/app/src/main/java/org/session/libsession/avatars/AvatarHelper.java +++ b/app/src/main/java/org/session/libsession/avatars/AvatarHelper.java @@ -3,25 +3,14 @@ import android.content.Context; import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -import com.annimon.stream.Stream; import org.session.libsession.utilities.Address; import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.util.LinkedList; -import java.util.List; /** * @deprecated We no longer use these address-based avatars. All avatars are now stored as sha256 of * urls encrypted locally. Look at {@link org.thoughtcrime.securesms.attachments.LocalEncryptedFileOutputStream}, - * {@link org.thoughtcrime.securesms.attachments.AvatarDownloadWorker}, * {@link org.thoughtcrime.securesms.glide.RecipientAvatarDownloadManager} for more information. * * Once the migration grace period is over, this class shall be removed. @@ -31,20 +20,6 @@ public class AvatarHelper { private static final String AVATAR_DIRECTORY = "avatars"; - public static InputStream getInputStreamFor(@NonNull Context context, @NonNull Address address) - throws FileNotFoundException - { - return new FileInputStream(getAvatarFile(context, address)); - } - - public static List getAvatarFiles(@NonNull Context context) { - File avatarDirectory = new File(context.getFilesDir(), AVATAR_DIRECTORY); - File[] results = avatarDirectory.listFiles(); - - if (results == null) return new LinkedList<>(); - else return Stream.of(results).toList(); - } - public static void delete(@NonNull Context context, @NonNull Address address) { getAvatarFile(context, address).delete(); } @@ -56,21 +31,4 @@ public static void delete(@NonNull Context context, @NonNull Address address) { return new File(avatarDirectory, new File(address.toString()).getName()); } - public static boolean avatarFileExists(@NonNull Context context , @NonNull Address address) { - File avatarFile = getAvatarFile(context, address); - return avatarFile.exists(); - } - - public static void setAvatar(@NonNull Context context, @NonNull Address address, @Nullable byte[] data) - throws IOException - { - if (data == null) { - delete(context, address); - } else { - try (FileOutputStream out = new FileOutputStream(getAvatarFile(context, address))) { - out.write(data); - } - } - } - } diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/emoji/RecentEmojiPageModel.java b/app/src/main/java/org/thoughtcrime/securesms/components/emoji/RecentEmojiPageModel.java index 0e2de9068c..04c38a2bdb 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/emoji/RecentEmojiPageModel.java +++ b/app/src/main/java/org/thoughtcrime/securesms/components/emoji/RecentEmojiPageModel.java @@ -7,8 +7,6 @@ import androidx.annotation.Nullable; -import com.annimon.stream.Stream; - import org.session.libsignal.utilities.JsonUtil; import org.session.libsignal.utilities.Log; @@ -16,6 +14,7 @@ import java.util.Arrays; import java.util.LinkedList; import java.util.List; +import java.util.stream.Collectors; import network.loki.messenger.R; @@ -64,9 +63,9 @@ public RecentEmojiPageModel(Context context) { return new ArrayList<>(recentlyUsed); } - @Override public List getDisplayEmoji() { - return Stream.of(getEmoji()).map(Emoji::new).toList(); - } + @Override public List getDisplayEmoji() { + return getEmoji().stream().map(Emoji::new).collect(Collectors.toList()); + } @Override public boolean hasSpriteMap() { return false; } diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/emoji/StaticEmojiPageModel.java b/app/src/main/java/org/thoughtcrime/securesms/components/emoji/StaticEmojiPageModel.java index 812b9d648c..900e2e9c25 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/emoji/StaticEmojiPageModel.java +++ b/app/src/main/java/org/thoughtcrime/securesms/components/emoji/StaticEmojiPageModel.java @@ -5,8 +5,6 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import com.annimon.stream.Collectors; -import com.annimon.stream.Stream; import org.thoughtcrime.securesms.emoji.EmojiCategory; @@ -19,10 +17,6 @@ public class StaticEmojiPageModel implements EmojiPageModel { private final @NonNull List emoji; private final @Nullable Uri sprite; - public StaticEmojiPageModel(@NonNull EmojiCategory category, @NonNull String[] strings, @Nullable Uri sprite) { - this(category, Stream.of(strings).map(s -> new Emoji(Collections.singletonList(s))).collect(Collectors.toList()), sprite); - } - public StaticEmojiPageModel(@NonNull EmojiCategory category, @NonNull List emoji, @Nullable Uri sprite) { this.category = category; this.emoji = Collections.unmodifiableList(emoji); 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 b352cec879..2348c50d4b 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 @@ -68,7 +68,6 @@ import androidx.loader.content.Loader import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearSmoothScroller import androidx.recyclerview.widget.RecyclerView -import com.annimon.stream.Stream import com.bumptech.glide.Glide import com.squareup.phrase.Phrase import dagger.hilt.android.AndroidEntryPoint @@ -2984,14 +2983,21 @@ class ConversationActivityV2 : ScreenLockActionBarActivity(), InputBarDelegate, } private fun saveAttachments(message: MmsMessageRecord) { - val attachments: List = Stream.of(message.slideDeck.slides) - .filter { s: Slide -> s.uri != null && (s.hasImage() || s.hasVideo() || s.hasAudio() || s.hasDocument()) } - .map { s: Slide -> SaveAttachmentTask.Attachment(s.uri!!, s.contentType, message.dateReceived, s.filename) } - .toList() + val attachments: List = + message.slideDeck.slides + .asSequence() + .filter { s -> + s.uri != null && (s.hasImage() || s.hasVideo() || s.hasAudio() || s.hasDocument()) + } + .map { s -> + SaveAttachmentTask.Attachment(s.uri!!, s.contentType, message.dateReceived, s.filename) + } + .toList() + if (attachments.isNotEmpty()) { val saveTask = SaveAttachmentTask(this) saveTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, *attachments.toTypedArray()) - if (!message.isOutgoing) { sendMediaSavedNotification() } + if (!message.isOutgoing) sendMediaSavedNotification() return } // Implied else that there were no attachment(s) 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 f2b19f539b..851b44dec1 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,117 +16,11 @@ */ package org.thoughtcrime.securesms.conversation.v2 -import android.content.Context -import android.net.Uri -import android.text.TextUtils -import android.view.View -import com.annimon.stream.Stream -import java.util.Collections import org.session.libsignal.utilities.Log object Util { private val TAG: String = Log.tag(Util::class.java) - fun asList(vararg elements: T): List { - val result = mutableListOf() // LinkedList() - Collections.addAll(result, *elements) - return result - } - - fun join(list: Array, delimiter: String?): String { - return join(listOf(*list), delimiter) - } - - fun join(list: Collection, delimiter: String?): String { - val result = StringBuilder() - var i = 0 - - for (item in list) { - result.append(item) - if (++i < list.size) result.append(delimiter) - } - - return result.toString() - } - - fun join(list: LongArray, delimeter: String?): String { - val boxed: MutableList = ArrayList(list.size) - - for (i in list.indices) { - boxed.add(list[i]) - } - - return join(boxed, delimeter) - } - - @SafeVarargs - fun join(vararg lists: List): List { - val totalSize = Stream.of(*lists).reduce(0) { sum: Int, list: List -> sum + list.size } - val joined: MutableList = ArrayList(totalSize) - - for (list in lists) { - joined.addAll(list) - } - - return joined - } - - fun join(list: List, delimeter: String?): String { - val sb = StringBuilder() - - for (j in list.indices) { - if (j != 0) sb.append(delimeter) - sb.append(list[j]) - } - - return sb.toString() - } - - fun wait(lock: Any, timeout: Long) { - try { - (lock as Object).wait(timeout) - } catch (ie: InterruptedException) { - throw AssertionError(ie) - } - } - - fun split(source: String, delimiter: String): List { - val results = mutableListOf() - - if (TextUtils.isEmpty(source)) { - return results - } - - val elements = source.split(delimiter.toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray() - Collections.addAll(results, *elements) - - return results - } - - fun split(input: ByteArray?, firstLength: Int, secondLength: Int): Array { - val parts = arrayOfNulls(2) - - parts[0] = ByteArray(firstLength) - System.arraycopy(input, 0, parts[0], 0, firstLength) - - parts[1] = ByteArray(secondLength) - System.arraycopy(input, firstLength, parts[1], 0, secondLength) - - return parts - } - - fun trim(input: ByteArray?, length: Int): ByteArray { - val result = ByteArray(length) - System.arraycopy(input, 0, result, 0, result.size) - - return result - } - - fun uri(uri: String?): Uri? { - return if (uri == null) null - else Uri.parse(uri) - } - /** * Returns half of the difference between the given length, and the length when scaled by the * given scale. @@ -136,9 +30,4 @@ object Util { return (length - scaledLength) / 2 } - // 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 - return config.layoutDirection == View.LAYOUT_DIRECTION_LTR - } } \ No newline at end of file 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 e8aff961b7..83d0d4e3dc 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/GroupDatabase.java +++ b/app/src/main/java/org/thoughtcrime/securesms/database/GroupDatabase.java @@ -6,32 +6,28 @@ import android.content.ContentValues; import android.content.Context; import android.database.Cursor; -import android.graphics.Bitmap; import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import com.annimon.stream.Stream; - import net.zetetic.database.sqlcipher.SQLiteDatabase; import org.session.libsession.utilities.Address; import org.session.libsession.utilities.AddressKt; import org.session.libsession.utilities.GroupRecord; -import org.session.libsession.utilities.TextSecurePreferences; -import org.session.libsession.utilities.Util; import org.session.libsignal.database.LokiOpenGroupDatabaseProtocol; import org.session.libsignal.messages.SignalServiceAttachmentPointer; import org.session.libsignal.utilities.guava.Optional; import org.thoughtcrime.securesms.auth.LoginStateRepository; import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper; -import org.thoughtcrime.securesms.util.BitmapUtil; import java.io.Closeable; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.LinkedList; import java.util.List; +import java.util.stream.Collectors; import javax.inject.Provider; @@ -100,7 +96,10 @@ public class GroupDatabase extends Database implements LokiOpenGroupDatabaseProt TIMESTAMP, ACTIVE, MMS, AVATAR_URL, ADMINS, UPDATED }; - static final List TYPED_GROUP_PROJECTION = Stream.of(GROUP_PROJECTION).map(columnName -> TABLE_NAME + "." + columnName).toList(); + static final List TYPED_GROUP_PROJECTION = + Arrays.stream(GROUP_PROJECTION) + .map(columnName -> TABLE_NAME + "." + columnName) + .collect(Collectors.toList()); public static String getCreateUpdatedTimestampCommand() { return "ALTER TABLE "+ TABLE_NAME + " " + @@ -275,10 +274,6 @@ public void updateTitle(String groupID, String newValue) { } } - public void updateProfilePicture(String groupID, Bitmap newValue) { - updateProfilePicture(groupID, BitmapUtil.toByteArray(newValue)); - } - @Override public void updateProfilePicture(String groupID, byte[] newValue) { long avatarId; 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 047af1b67c..450897f474 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/MmsDatabase.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/MmsDatabase.kt @@ -20,7 +20,6 @@ import android.content.ContentValues import android.content.Context import android.database.Cursor import androidx.sqlite.db.SupportSQLiteDatabase -import com.annimon.stream.Stream import dagger.Lazy import dagger.hilt.android.qualifiers.ApplicationContext import kotlinx.serialization.json.Json @@ -594,14 +593,8 @@ class MmsDatabase @Inject constructor( contentValues.put(ADDRESS, message.recipient.toString()) contentValues.put(PRO_PROFILE_FEATURES, message.proFeatures.toProProfileBitSetValue()) contentValues.put(PRO_MESSAGE_FEATURES, message.proFeatures.toProMessageBitSetValue()) - contentValues.put( - DELIVERY_RECEIPT_COUNT, - Stream.of(earlyDeliveryReceipts.values).mapToLong { obj: Long -> obj } - .sum()) - contentValues.put( - READ_RECEIPT_COUNT, - Stream.of(earlyReadReceipts.values).mapToLong { obj: Long -> obj } - .sum()) + contentValues.put(DELIVERY_RECEIPT_COUNT, earlyDeliveryReceipts.values.sum()) + contentValues.put(READ_RECEIPT_COUNT, earlyReadReceipts.values.sum()) val quoteAttachments: MutableList = LinkedList() if (message.outgoingQuote != null) { contentValues.put(QUOTE_ID, message.outgoingQuote.id) @@ -667,9 +660,9 @@ class MmsDatabase @Inject constructor( val allAttachments: MutableList = LinkedList() val thumbnailJobs: MutableList = ArrayList() // Collector for thumbnail jobs - val previewAttachments = - Stream.of(linkPreviews).filter { lp: LinkPreview -> lp.getThumbnail().isPresent } - .map { lp: LinkPreview -> lp.getThumbnail().get() } + val previewAttachments: List = + linkPreviews.asSequence() + .mapNotNull { lp -> lp.getThumbnail().orNull() } .toList() allAttachments.addAll(attachments) @@ -1066,11 +1059,10 @@ class MmsDatabase @Inject constructor( return recipientRepository.getRecipientSync(serialized.toAddress()) } - private fun getSlideDeck(attachments: List): SlideDeck? { - val messageAttachments: List? = Stream.of(attachments) - .filterNot { obj: DatabaseAttachment? -> obj!!.isQuote } - .toList() - return SlideDeck(context, messageAttachments!!) + private fun getSlideDeck(attachments: List): SlideDeck { + val messageAttachments: List = + attachments.filterNot { it?.isQuote == true } + return SlideDeck(context, messageAttachments) } private fun getQuote(cursor: Cursor): Quote? { @@ -1082,11 +1074,10 @@ class MmsDatabase @Inject constructor( val quoteText = retrievedQuote?.body val quoteMissing = retrievedQuote == null val quoteDeck = ( - (retrievedQuote as? MmsMessageRecord)?.slideDeck ?: - Stream.of(attachmentDatabase.getAttachment(cursor)) - .filter { obj: DatabaseAttachment? -> obj!!.isQuote } - .toList() - .let { SlideDeck(context, it) } + (retrievedQuote as? MmsMessageRecord)?.slideDeck + ?: attachmentDatabase.getAttachment(cursor) + .filter { it?.isQuote == true } + .let { SlideDeck(context, it) } ) return Quote( quoteId, diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/RecipientDatabase.java b/app/src/main/java/org/thoughtcrime/securesms/database/RecipientDatabase.java index ef8be8f1a4..50740685e1 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/RecipientDatabase.java +++ b/app/src/main/java/org/thoughtcrime/securesms/database/RecipientDatabase.java @@ -6,8 +6,6 @@ import android.content.Context; import android.database.Cursor; -import com.annimon.stream.Stream; - import net.zetetic.database.sqlcipher.SQLiteDatabase; import org.session.libsession.utilities.Address; @@ -21,7 +19,6 @@ import javax.inject.Provider; import kotlin.Triple; -import okhttp3.HttpUrl; /** * Note that you should not use this table anymore, use {@link RecipientSettingsDatabase} instead. @@ -88,10 +85,6 @@ public class RecipientDatabase extends Database { FORCE_SMS_SELECTION, NOTIFY_TYPE, DISAPPEARING_STATE, WRAPPER_HASH, BLOCKS_COMMUNITY_MESSAGE_REQUESTS, AUTO_DOWNLOAD, }; - static final List TYPED_RECIPIENT_PROJECTION = Stream.of(RECIPIENT_PROJECTION) - .map(columnName -> TABLE_NAME + "." + columnName) - .toList(); - public static final String CREATE_TABLE = "CREATE TABLE " + TABLE_NAME + " (" + ID + " INTEGER PRIMARY KEY, " + diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/SearchDatabase.java b/app/src/main/java/org/thoughtcrime/securesms/database/SearchDatabase.java index c5eb345eaf..60b8283eb9 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/SearchDatabase.java +++ b/app/src/main/java/org/thoughtcrime/securesms/database/SearchDatabase.java @@ -5,8 +5,6 @@ import androidx.annotation.NonNull; -import com.annimon.stream.Stream; - import net.zetetic.database.sqlcipher.SQLiteDatabase; import org.session.libsession.utilities.Address; @@ -14,8 +12,10 @@ import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.List; +import java.util.stream.Collectors; import javax.inject.Provider; @@ -189,12 +189,14 @@ public Cursor queryMessages(@NonNull String query, long threadId) { ); } - private String adjustQuery(@NonNull String query) { - List tokens = Stream.of(query.split(" ")).filter(s -> s.trim().length() > 0).toList(); - String prefixQuery = Util.join(tokens, "* "); - - prefixQuery += "*"; + private String adjustQuery(@NonNull String query) { + List tokens = Arrays.stream(query.split(" ")) + .map(String::trim) + .filter(s -> !s.isEmpty()) + .collect(Collectors.toList()); - return prefixQuery; - } + String prefixQuery = Util.join(tokens, "* "); + prefixQuery += "*"; + return prefixQuery; + } } \ No newline at end of file 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 48159a509a..d4fe2fa6e7 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/SmsDatabase.java +++ b/app/src/main/java/org/thoughtcrime/securesms/database/SmsDatabase.java @@ -26,8 +26,6 @@ import androidx.collection.ArraySet; import androidx.sqlite.db.SupportSQLiteDatabase; -import com.annimon.stream.Stream; - import net.zetetic.database.sqlcipher.SQLiteDatabase; import org.json.JSONArray; @@ -554,8 +552,8 @@ public long insertMessageOutbox(long threadId, OutgoingTextMessage message, contentValues.put(TYPE, type); contentValues.put(EXPIRES_IN, message.getExpiresInMillis()); contentValues.put(EXPIRE_STARTED, message.getExpireStartedAtMillis()); - contentValues.put(DELIVERY_RECEIPT_COUNT, Stream.of(earlyDeliveryReceipts.values()).mapToLong(Long::longValue).sum()); - contentValues.put(READ_RECEIPT_COUNT, Stream.of(earlyReadReceipts.values()).mapToLong(Long::longValue).sum()); + contentValues.put(DELIVERY_RECEIPT_COUNT, earlyDeliveryReceipts.values().stream().mapToLong(Long::longValue).sum()); + contentValues.put(READ_RECEIPT_COUNT, earlyReadReceipts.values().stream().mapToLong(Long::longValue).sum()); contentValues.put(PRO_MESSAGE_FEATURES, ProFeatureExtKt.toProMessageBitSetValue(message.getProFeatures())); contentValues.put(PRO_PROFILE_FEATURES, ProFeatureExtKt.toProProfileBitSetValue(message.getProFeatures())); diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/ThreadDatabase.java b/app/src/main/java/org/thoughtcrime/securesms/database/ThreadDatabase.java index ef127b10a0..0dc4ca500d 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/ThreadDatabase.java +++ b/app/src/main/java/org/thoughtcrime/securesms/database/ThreadDatabase.java @@ -25,8 +25,6 @@ import android.database.Cursor; import android.net.Uri; -import com.annimon.stream.Stream; - import net.zetetic.database.sqlcipher.SQLiteDatabase; import org.json.JSONArray; @@ -61,12 +59,14 @@ import java.io.Closeable; import java.time.ZonedDateTime; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; +import java.util.stream.Collectors; import javax.inject.Inject; import javax.inject.Provider; @@ -152,9 +152,10 @@ public class ThreadDatabase extends Database implements OnAppStartupComponent { SNIPPET_URI, ARCHIVED, STATUS, DELIVERY_RECEIPT_COUNT, EXPIRES_IN, LAST_SEEN, READ_RECEIPT_COUNT, IS_PINNED, SNIPPET_CONTENT, }; - private static final List TYPED_THREAD_PROJECTION = Stream.of(THREAD_PROJECTION) - .map(columnName -> TABLE_NAME + "." + columnName) - .toList(); + private static final List TYPED_THREAD_PROJECTION = + Arrays.stream(THREAD_PROJECTION) + .map(columnName -> TABLE_NAME + "." + columnName) + .collect(Collectors.toList()); private static final List COMBINED_THREAD_RECIPIENT_GROUP_PROJECTION = CollectionsKt.plus( diff --git a/app/src/main/java/org/thoughtcrime/securesms/linkpreview/LinkPreviewUtil.java b/app/src/main/java/org/thoughtcrime/securesms/linkpreview/LinkPreviewUtil.java index 6f44f7523c..fd571d6bef 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/linkpreview/LinkPreviewUtil.java +++ b/app/src/main/java/org/thoughtcrime/securesms/linkpreview/LinkPreviewUtil.java @@ -9,7 +9,6 @@ import android.text.util.Linkify; import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import com.annimon.stream.Stream; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Collections; @@ -47,10 +46,17 @@ public final class LinkPreviewUtil { return Collections.emptyList(); } - return Stream.of(spannable.getSpans(0, spannable.length(), URLSpan.class)) - .map(span -> new Link(span.getURL(), spannable.getSpanStart(span))) - .filter(link -> isValidLinkUrl(link.getUrl())) - .toList(); + URLSpan[] spans = spannable.getSpans(0, spannable.length(), URLSpan.class); + List links = new java.util.ArrayList<>(spans.length); + + for (URLSpan span : spans) { + Link link = new Link(span.getURL(), spannable.getSpanStart(span)); + if (isValidLinkUrl(link.getUrl())) { + links.add(link); + } + } + + return links; } /** @@ -215,14 +221,19 @@ private static long parseISO8601(String date) { @SuppressLint("ObsoleteSdkInt") public long getDate() { - return Stream.of(values.get(KEY_PUBLISHED_TIME_1), - values.get(KEY_PUBLISHED_TIME_2), - values.get(KEY_MODIFIED_TIME_1), - values.get(KEY_MODIFIED_TIME_2)) - .map(OpenGraph::parseISO8601) - .filter(time -> time > 0) - .findFirst() - .orElse(0L); + String[] candidates = new String[] { + values.get(KEY_PUBLISHED_TIME_1), + values.get(KEY_PUBLISHED_TIME_2), + values.get(KEY_MODIFIED_TIME_1), + values.get(KEY_MODIFIED_TIME_2) + }; + + for (String c : candidates) { + long t = parseISO8601(c); + if (t > 0) return t; + } + + return 0L; } } 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 66b491042d..be4c74ab26 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/mediasend/MediaRepository.java +++ b/app/src/main/java/org/thoughtcrime/securesms/mediasend/MediaRepository.java @@ -12,7 +12,6 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.WorkerThread; -import com.annimon.stream.Stream; import org.session.libsignal.utilities.guava.Optional; import org.thoughtcrime.securesms.mms.PartAuthority; import org.thoughtcrime.securesms.util.MediaUtil; @@ -49,7 +48,11 @@ void getMediaInBucket(@NonNull Context context, @NonNull String bucketId, @NonNu * much data as we have, like width/height. */ void getPopulatedMedia(@NonNull Context context, @NonNull List media, @NonNull Callback> callback) { - if (Stream.of(media).allMatch(this::isPopulated)) { + boolean allPopulated = true; + for (Media m : media) { + if (!isPopulated(m)) { allPopulated = false; break; } + } + if (allPopulated) { callback.onComplete(media); return; } @@ -207,19 +210,21 @@ void getPopulatedMedia(@NonNull Context context, @NonNull List media, @No } @WorkerThread private List getPopulatedMedia(@NonNull Context context, @NonNull List media) { - return Stream.of(media).map(m -> { + List result = new ArrayList<>(media.size()); + for (Media m : media) { try { if (isPopulated(m)) { - return m; + result.add(m); } else if (PartAuthority.isLocalUri(m.getUri())) { - return getLocallyPopulatedMedia(context, m); + result.add(getLocallyPopulatedMedia(context, m)); } else { - return getContentResolverPopulatedMedia(context, m); + result.add(getContentResolverPopulatedMedia(context, m)); } } catch (IOException e) { - return m; + result.add(m); } - }).toList(); + } + return result; } @SuppressWarnings("SuspiciousNameCombination") diff --git a/app/src/main/java/org/thoughtcrime/securesms/mediasend/MediaSendViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/mediasend/MediaSendViewModel.kt index 89709a8b6e..bf54a06121 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/mediasend/MediaSendViewModel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/mediasend/MediaSendViewModel.kt @@ -6,7 +6,6 @@ import android.net.Uri import android.os.Build import androidx.lifecycle.LiveData import androidx.lifecycle.asLiveData -import com.annimon.stream.Stream import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableStateFlow @@ -16,7 +15,6 @@ import kotlinx.coroutines.flow.asSharedFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.update -import org.session.libsession.utilities.Util.equals import org.session.libsession.utilities.Util.runOnMain import org.session.libsignal.utilities.Log import org.session.libsignal.utilities.guava.Optional @@ -138,22 +136,17 @@ class MediaSendViewModel @Inject constructor( _effects.tryEmit(MediaSendEffect.ShowError(Error.TOO_MANY_ITEMS)) } - val computedId: String = - if (filteredMedia.isNotEmpty()) { - Stream.of(filteredMedia) - .skip(1) - .reduce( - filteredMedia[0].bucketId ?: Media.ALL_MEDIA_BUCKET_ID - ) { id: String?, m: Media -> - if (equals(id, m.bucketId ?: Media.ALL_MEDIA_BUCKET_ID)) { - id - } else { - Media.ALL_MEDIA_BUCKET_ID - } - } - } else { - Media.ALL_MEDIA_BUCKET_ID - } + val computedId = run { + fun String?.normalize() = this?.ifEmpty { Media.ALL_MEDIA_BUCKET_ID } + ?: Media.ALL_MEDIA_BUCKET_ID + + filteredMedia + .asSequence() + .drop(1) + .fold(filteredMedia.firstOrNull()?.bucketId.normalize()) { acc, media -> + if (acc == media.bucketId.normalize()) acc else Media.ALL_MEDIA_BUCKET_ID + } + } val newVisibility = if (filteredMedia.isEmpty()) CountButtonState.Visibility.CONDITIONAL @@ -468,18 +461,13 @@ class MediaSendViewModel @Inject constructor( } override fun onCleared() { - if (!sentMedia) { - Stream.of(selectedMedia) - .map { obj: Media -> obj.uri } - .filter { uri: Uri? -> - BlobUtils.isAuthority( - uri!! - ) - } - .forEach { uri: Uri? -> - BlobUtils.getInstance().delete(context, uri!!) - } - } + if (sentMedia) return + + selectedMedia + .asSequence() + .map { it.uri } + .filter(BlobUtils::isAuthority) + .forEach { BlobUtils.getInstance().delete(context, it) } } enum class Error { diff --git a/app/src/main/java/org/thoughtcrime/securesms/mms/SlideDeck.java b/app/src/main/java/org/thoughtcrime/securesms/mms/SlideDeck.java index 7c91fdcce6..439d8f2c1b 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/mms/SlideDeck.java +++ b/app/src/main/java/org/thoughtcrime/securesms/mms/SlideDeck.java @@ -19,7 +19,6 @@ import android.content.Context; import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import com.annimon.stream.Stream; import org.session.libsession.messaging.sending_receiving.attachments.Attachment; import org.session.libsignal.utilities.guava.Optional; import org.thoughtcrime.securesms.util.MediaUtil; @@ -101,9 +100,13 @@ public boolean containsMediaSlide() { return null; } - public @NonNull List getThumbnailSlides() { - return Stream.of(slides).filter(Slide::hasImage).toList(); - } + public @NonNull List getThumbnailSlides() { + List result = new LinkedList<>(); + for (Slide slide : slides) { + if (slide.hasImage()) result.add(slide); + } + return result; + } public @Nullable AudioSlide getAudioSlide() { for (Slide slide : slides) { 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 e50846b1c9..aa0f955332 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/net/ChunkedDataFetcher.java +++ b/app/src/main/java/org/thoughtcrime/securesms/net/ChunkedDataFetcher.java @@ -5,7 +5,6 @@ import androidx.annotation.NonNull; import android.text.TextUtils; -import com.annimon.stream.Stream; import com.bumptech.glide.util.ContentLengthInputStream; import org.session.libsignal.utilities.Log; @@ -128,60 +127,69 @@ public void onResponse(@NonNull Call call, @NonNull Response response) { return compositeController; } - private void fetchChunks(@NonNull String url, - long contentLength, - Optional> firstChunk, - CompositeRequestController compositeController, - Callback callback) - { - List requestPattern; - try { - if (firstChunk.isPresent()) { - requestPattern = Stream.of(getRequestPattern(contentLength - firstChunk.get().second())) - .map(b -> new ByteRange(b.start + firstChunk.get().second(), - b.end + firstChunk.get().second(), - b.ignoreFirst)) - .toList(); - } else { - requestPattern = getRequestPattern(contentLength); - } - } catch (IOException e) { - callback.onFailure(e); - compositeController.cancel(); - return; - } - - SignalExecutors.UNBOUNDED.execute(() -> { - List controllers = Stream.of(requestPattern).map(range -> makeChunkRequest(client, url, range)).toList(); - List streams = new ArrayList<>(controllers.size() + (firstChunk.isPresent() ? 1 : 0)); - - if (firstChunk.isPresent()) { - streams.add(firstChunk.get().first()); - } - - Stream.of(controllers).forEach(compositeController::addController); - - for (CallRequestController controller : controllers) { - Optional stream = controller.getStream(); - - if (!stream.isPresent()) { - Log.w(TAG, "Stream was canceled."); - callback.onFailure(new IOException("Failure")); - compositeController.cancel(); - return; + private void fetchChunks(@NonNull String url, + long contentLength, + Optional> firstChunk, + CompositeRequestController compositeController, + Callback callback) + { + final List requestPattern; + try { + if (firstChunk.isPresent()) { + long offset = firstChunk.get().second(); + + List base = getRequestPattern(contentLength - offset); + List shifted = new ArrayList<>(base.size()); + for (ByteRange b : base) { + shifted.add(new ByteRange(b.start + offset, b.end + offset, b.ignoreFirst)); + } + requestPattern = shifted; + } else { + requestPattern = getRequestPattern(contentLength); + } + } catch (IOException e) { + callback.onFailure(e); + compositeController.cancel(); + return; } - streams.add(stream.get()); - } - - try { - callback.onSuccess(new InputStreamList(streams)); - } catch (IOException e) { - callback.onFailure(e); - compositeController.cancel(); - } - }); - } + SignalExecutors.UNBOUNDED.execute(() -> { + List controllers = new ArrayList<>(requestPattern.size()); + for (ByteRange range : requestPattern) { + controllers.add(makeChunkRequest(client, url, range)); + } + + List streams = new ArrayList<>(controllers.size() + (firstChunk.isPresent() ? 1 : 0)); + + if (firstChunk.isPresent()) { + streams.add(firstChunk.get().first()); + } + + for (CallRequestController c : controllers) { + compositeController.addController(c); + } + + for (CallRequestController controller : controllers) { + Optional stream = controller.getStream(); + + if (!stream.isPresent()) { + Log.w(TAG, "Stream was canceled."); + callback.onFailure(new IOException("Failure")); + compositeController.cancel(); + return; + } + + streams.add(stream.get()); + } + + try { + callback.onSuccess(new InputStreamList(streams)); + } catch (IOException e) { + callback.onFailure(e); + compositeController.cancel(); + } + }); + } private CallRequestController makeChunkRequest(@NonNull OkHttpClient client, @NonNull String url, @NonNull ByteRange range) { Request request = new Request.Builder() diff --git a/app/src/main/java/org/thoughtcrime/securesms/net/CompositeRequestController.java b/app/src/main/java/org/thoughtcrime/securesms/net/CompositeRequestController.java index 4c2e2fc49b..b13e587591 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/net/CompositeRequestController.java +++ b/app/src/main/java/org/thoughtcrime/securesms/net/CompositeRequestController.java @@ -2,8 +2,6 @@ import androidx.annotation.NonNull; -import com.annimon.stream.Stream; - import java.util.ArrayList; import java.util.List; @@ -20,11 +18,13 @@ public synchronized void addController(@NonNull RequestController controller) { } } - @Override - public synchronized void cancel() { - canceled = true; - Stream.of(controllers).forEach(RequestController::cancel); - } + @Override + public synchronized void cancel() { + canceled = true; + for (RequestController controller : controllers) { + controller.cancel(); + } + } public synchronized boolean isCanceled() { return canceled; 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 bdc90830fa..7e24a4f531 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/permissions/Permissions.java +++ b/app/src/main/java/org/thoughtcrime/securesms/permissions/Permissions.java @@ -19,8 +19,9 @@ import androidx.core.content.ContextCompat; import androidx.fragment.app.Fragment; -import com.annimon.stream.Stream; -import com.annimon.stream.function.Consumer; +import java.util.ArrayList; +import java.util.function.Consumer; +import java.util.stream.IntStream; import org.session.libsession.utilities.ServiceUtil; import org.thoughtcrime.securesms.util.LRUCache; @@ -186,7 +187,8 @@ private void executeNoPermissionsRequest(PermissionsRequest request) { } String[] permissions = filterNotGranted(permissionObject.getContext(), requestedPermissions); - int[] grantResults = Stream.of(permissions).mapToInt(permission -> PackageManager.PERMISSION_DENIED).toArray(); + int[] grantResults = new int[permissions.length]; + Arrays.fill(grantResults, PackageManager.PERMISSION_DENIED); boolean[] showDialog = new boolean[permissions.length]; Arrays.fill(showDialog, true); @@ -203,17 +205,24 @@ private static void requestPermissions(@NonNull Fragment fragment, int requestCo fragment.requestPermissions(filterNotGranted(fragment.getContext(), permissions), requestCode); } - private static String[] filterNotGranted(@NonNull Context context, String... permissions) { - return Stream.of(permissions) - .filter(permission -> ContextCompat.checkSelfPermission(context, permission) != PackageManager.PERMISSION_GRANTED) - .toList() - .toArray(new String[0]); - } - - public static boolean hasAll(@NonNull Context context, String... permissions) { - return Stream.of(permissions).allMatch(permission -> ContextCompat.checkSelfPermission(context, permission) == PackageManager.PERMISSION_GRANTED); + private static String[] filterNotGranted(@NonNull Context context, String... permissions) { + List notGranted = new ArrayList<>(permissions.length); + for (String permission : permissions) { + if (ContextCompat.checkSelfPermission(context, permission) != PackageManager.PERMISSION_GRANTED) { + notGranted.add(permission); + } + } + return notGranted.toArray(new String[0]); + } - } + public static boolean hasAll(@NonNull Context context, String... permissions) { + for (String permission : permissions) { + if (ContextCompat.checkSelfPermission(context, permission) != PackageManager.PERMISSION_GRANTED) { + return false; + } + } + return true; + } public static void onRequestPermissionsResult(Fragment fragment, int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { onRequestPermissionsResult(new FragmentPermissionObject(fragment), requestCode, permissions, grantResults); diff --git a/app/src/main/java/org/thoughtcrime/securesms/permissions/PermissionsRequest.java b/app/src/main/java/org/thoughtcrime/securesms/permissions/PermissionsRequest.java index 03942adfa5..a4cd3bf9f2 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/permissions/PermissionsRequest.java +++ b/app/src/main/java/org/thoughtcrime/securesms/permissions/PermissionsRequest.java @@ -4,7 +4,7 @@ import android.content.pm.PackageManager; import androidx.annotation.Nullable; -import com.annimon.stream.function.Consumer; +import java.util.function.Consumer; import java.util.ArrayList; import java.util.HashMap; diff --git a/app/src/main/java/org/thoughtcrime/securesms/reactions/EmojiCount.java b/app/src/main/java/org/thoughtcrime/securesms/reactions/EmojiCount.java index 566ed85f8f..903ab2731b 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/reactions/EmojiCount.java +++ b/app/src/main/java/org/thoughtcrime/securesms/reactions/EmojiCount.java @@ -2,8 +2,6 @@ import androidx.annotation.NonNull; -import com.annimon.stream.Stream; - import java.util.List; import kotlin.collections.CollectionsKt; diff --git a/app/src/main/java/org/thoughtcrime/securesms/reactions/ReactionsViewModel.java b/app/src/main/java/org/thoughtcrime/securesms/reactions/ReactionsViewModel.java index 9d85a0f2bd..20439b5cb5 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/reactions/ReactionsViewModel.java +++ b/app/src/main/java/org/thoughtcrime/securesms/reactions/ReactionsViewModel.java @@ -2,16 +2,13 @@ import androidx.annotation.NonNull; import androidx.lifecycle.ViewModel; -import androidx.lifecycle.ViewModelProvider; -import com.annimon.stream.Stream; - -import org.thoughtcrime.securesms.database.RecipientRepository; import org.thoughtcrime.securesms.database.model.MessageId; -import java.util.Comparator; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import java.util.stream.Collectors; import dagger.assisted.Assisted; import dagger.assisted.AssistedFactory; @@ -20,6 +17,8 @@ import io.reactivex.Observable; import io.reactivex.android.schedulers.AndroidSchedulers; +; + @HiltViewModel(assistedFactory = ReactionsViewModel.Factory.class) public class ReactionsViewModel extends ViewModel { @@ -38,17 +37,24 @@ public ReactionsViewModel(@Assisted @NonNull MessageId messageId, public @NonNull Observable> getEmojiCounts() { - return repository.getReactions(messageId) - .map(reactionList -> Stream.of(reactionList) - .groupBy(ReactionDetails::getBaseEmoji) - .sorted(this::compareReactions) - .map(entry -> new EmojiCount( - entry.getKey(), - getCountDisplayEmoji(entry.getValue()), - entry.getValue(), - !fromCommunityThread)) - .toList()) - .observeOn(AndroidSchedulers.mainThread()); + return repository.getReactions(messageId) + .map(reactionList -> reactionList.stream() + .collect(Collectors.groupingBy( + ReactionDetails::getBaseEmoji, + LinkedHashMap::new, + Collectors.toList() + )) + .entrySet().stream() + .sorted(this::compareReactions) + .map(entry -> new EmojiCount( + entry.getKey(), + getCountDisplayEmoji(entry.getValue()), + entry.getValue(), + !fromCommunityThread + )) + .collect(Collectors.toList()) + ) + .observeOn(AndroidSchedulers.mainThread()); } private int compareReactions(@NonNull Map.Entry> lhs, @NonNull Map.Entry> rhs) { @@ -61,12 +67,12 @@ private int compareReactions(@NonNull Map.Entry> l return -Long.compare(latestTimestampLhs, latestTimestampRhs); } - private long getLatestTimestamp(List reactions) { - return Stream.of(reactions) - .max(Comparator.comparingLong(ReactionDetails::getTimestamp)) - .map(ReactionDetails::getTimestamp) - .orElse(-1L); - } + private long getLatestTimestamp(List reactions) { + return reactions.stream() + .mapToLong(ReactionDetails::getTimestamp) + .max() + .orElse(-1L); + } private @NonNull String getCountDisplayEmoji(@NonNull List reactions) { for (ReactionDetails reaction : reactions) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/reactions/any/ReactWithAnyEmojiRepository.java b/app/src/main/java/org/thoughtcrime/securesms/reactions/any/ReactWithAnyEmojiRepository.java index 6c32e9ea10..3395f0c94a 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/reactions/any/ReactWithAnyEmojiRepository.java +++ b/app/src/main/java/org/thoughtcrime/securesms/reactions/any/ReactWithAnyEmojiRepository.java @@ -4,9 +4,8 @@ import androidx.annotation.NonNull; -import com.annimon.stream.Stream; - import org.session.libsignal.utilities.Log; +import org.thoughtcrime.securesms.components.emoji.EmojiPageModel; import org.thoughtcrime.securesms.components.emoji.RecentEmojiPageModel; import org.thoughtcrime.securesms.emoji.EmojiCategory; import org.thoughtcrime.securesms.emoji.EmojiSource; @@ -30,9 +29,18 @@ public final class ReactWithAnyEmojiRepository { this.recentEmojiPageModel = new RecentEmojiPageModel(context); this.emojiPages = new LinkedList<>(); - emojiPages.addAll(Stream.of(EmojiSource.getLatest().getDisplayPages()) - .map(page -> new ReactWithAnyEmojiPage(Collections.singletonList(new ReactWithAnyEmojiPageBlock(EmojiCategory.getCategoryLabel(page.getIconAttr()), page)))) - .toList()); + for (EmojiPageModel page : EmojiSource.getLatest().getDisplayPages()) { + emojiPages.add( + new ReactWithAnyEmojiPage( + Collections.singletonList( + new ReactWithAnyEmojiPageBlock( + EmojiCategory.getCategoryLabel(page.getIconAttr()), + page + ) + ) + ) + ); + } } List getEmojiPageModels() { diff --git a/app/src/main/java/org/thoughtcrime/securesms/reactions/any/ReactWithAnyEmojiViewModel.java b/app/src/main/java/org/thoughtcrime/securesms/reactions/any/ReactWithAnyEmojiViewModel.java index ad86278da5..d49aa2a26a 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/reactions/any/ReactWithAnyEmojiViewModel.java +++ b/app/src/main/java/org/thoughtcrime/securesms/reactions/any/ReactWithAnyEmojiViewModel.java @@ -6,8 +6,6 @@ import androidx.annotation.Nullable; import androidx.lifecycle.ViewModel; -import com.annimon.stream.Stream; - import org.thoughtcrime.securesms.components.emoji.EmojiPageModel; import org.thoughtcrime.securesms.components.emoji.EmojiPageViewGridAdapter; import org.thoughtcrime.securesms.database.model.MessageId; @@ -88,11 +86,16 @@ public void onQueryChanged(String query) { emojiSearchRepository.submitQuery(query, SEARCH_LIMIT, m -> searchResults.onNext(new EmojiSearchResult(query, m))); } - private static @NonNull MappingModelList toMappingModels(@NonNull EmojiPageModel model) { - return Stream.of(model.getDisplayEmoji()) - .map(e -> new EmojiPageViewGridAdapter.EmojiModel(model.getKey(), e)) - .collect(MappingModelList.collect()); - } + private static @NonNull MappingModelList toMappingModels(@NonNull EmojiPageModel model) { + MappingModelList out = new MappingModelList(); + String key = model.getKey(); + + for (var e : model.getDisplayEmoji()) { + out.add(new EmojiPageViewGridAdapter.EmojiModel(key, e)); + } + + return out; + } private static class EmojiSearchResult { private final String query; diff --git a/app/src/main/java/org/thoughtcrime/securesms/sskenvironment/TypingStatusRepository.java b/app/src/main/java/org/thoughtcrime/securesms/sskenvironment/TypingStatusRepository.java index 667453d09b..0caf64e424 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/sskenvironment/TypingStatusRepository.java +++ b/app/src/main/java/org/thoughtcrime/securesms/sskenvironment/TypingStatusRepository.java @@ -6,9 +6,6 @@ import androidx.lifecycle.LiveData; import androidx.lifecycle.MutableLiveData; -import com.annimon.stream.Collectors; -import com.annimon.stream.Stream; - import org.jetbrains.annotations.NotNull; import org.session.libsession.utilities.Address; import org.session.libsession.utilities.SSKEnvironment; @@ -20,6 +17,7 @@ import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; +import java.util.HashSet; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; @@ -155,8 +153,14 @@ private void notifyThread(long threadId, @NonNull Set typists, boolean i notifier.postValue(new TypingState(new ArrayList<>(uniqueTypists), isReplacedByIncomingMessage)); - Set activeThreads = Stream.of(typistMap.keySet()).filter(t -> !typistMap.get(t).isEmpty()).collect(Collectors.toSet()); - threadsNotifier.postValue(activeThreads); + Set activeThreads = new HashSet<>(); + for (Map.Entry> entry : typistMap.entrySet()) { + Set value = entry.getValue(); + if (value != null && !value.isEmpty()) { + activeThreads.add(entry.getKey()); + } + } + threadsNotifier.postValue(activeThreads); } public static class TypingState { diff --git a/app/src/main/java/org/thoughtcrime/securesms/util/SearchUtil.java b/app/src/main/java/org/thoughtcrime/securesms/util/SearchUtil.java index cf046b9a7e..72f08d52a5 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/util/SearchUtil.java +++ b/app/src/main/java/org/thoughtcrime/securesms/util/SearchUtil.java @@ -8,8 +8,6 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import com.annimon.stream.Stream; - import org.session.libsignal.utilities.Pair; import java.util.Collections; @@ -67,7 +65,14 @@ static List> getHighlightRanges(@NonNull Locale locale, String normalizedText = text.toLowerCase(locale); String normalizedHighlight = highlight.toLowerCase(locale); - List highlightTokens = Stream.of(normalizedHighlight.split("\\s")).filter(s -> s.trim().length() > 0).toList(); + + List highlightTokens = new LinkedList<>(); + + for (String token : normalizedHighlight.split("\\s+")) { + if (!token.isEmpty()) { + highlightTokens.add(token); + } + } List> ranges = new LinkedList<>(); diff --git a/app/src/main/java/org/thoughtcrime/securesms/util/adapter/mapping/MappingModelList.kt b/app/src/main/java/org/thoughtcrime/securesms/util/adapter/mapping/MappingModelList.kt index 5d6b8dc64e..082d3e4aaf 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/util/adapter/mapping/MappingModelList.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/util/adapter/mapping/MappingModelList.kt @@ -1,9 +1,6 @@ package org.thoughtcrime.securesms.util.adapter.mapping -import com.annimon.stream.Collector -import com.annimon.stream.function.BiConsumer -import com.annimon.stream.function.Function -import com.annimon.stream.function.Supplier + class MappingModelList : ArrayList?> { constructor() {} @@ -16,38 +13,5 @@ class MappingModelList : ArrayList?> { list.add(model) return list } - - @JvmStatic - fun collect(): Collector, MappingModelList, MappingModelList> { - return object : Collector, MappingModelList, MappingModelList> { - override fun supplier(): Supplier { - return Supplier { MappingModelList() } - } - - override fun accumulator(): BiConsumer> { - return BiConsumer { obj: MappingModelList, e: MappingModel<*> -> obj.add(e) } - } - - override fun finisher(): Function { - return Function { t -> t } - } - } - } - - /*fun toMappingModelList(): Collector, MappingModelList, MappingModelList> { - return object : Collector, MappingModelList, MappingModelList?> { - override fun supplier(): Supplier { - return Supplier { MappingModelList() } - } - - override fun accumulator(): BiConsumer> { - return BiConsumer { obj: MappingModelList, e: MappingModel<*> -> obj.add(e) } - } - - override fun finisher(): Function { - return Function { mappingModels: MappingModelList? -> mappingModels } - } - } - }*/ } } \ No newline at end of file diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 924369d912..a4ca0ea462 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -57,7 +57,6 @@ roundedimageviewVersion = "2.1.0" runnerVersion = "1.7.0" rxbindingVersion = "3.1.0" sqlcipherAndroidVersion = "4.13.0" -streamVersion = "1.1.8" sqliteKtxVersion = "2.6.2" subsamplingScaleImageViewVersion = "3.10.0" testCoreVersion = "1.7.0" @@ -161,7 +160,6 @@ robolectric = { module = "org.robolectric:robolectric", version.ref = "robolectr roundedimageview = { module = "com.makeramen:roundedimageview", version.ref = "roundedimageviewVersion" } rxbinding = { module = "com.jakewharton.rxbinding3:rxbinding", version.ref = "rxbindingVersion" } sqlcipher-android = { module = "net.zetetic:sqlcipher-android", version.ref = "sqlcipherAndroidVersion" } -stream = { module = "com.annimon:stream", version.ref = "streamVersion" } subsampling-scale-image-view = { module = "com.davemorrissey.labs:subsampling-scale-image-view", version.ref = "subsamplingScaleImageViewVersion" } truth = { module = "com.google.truth:truth", version.ref = "truthVersion" } turbine = { module = "app.cash.turbine:turbine", version.ref = "turbineVersion" } From e08c7ccaec737b24092ac4205d52bdfd4df43471 Mon Sep 17 00:00:00 2001 From: ThomasSession Date: Tue, 10 Feb 2026 15:53:31 +1100 Subject: [PATCH 2/4] cleanup --- .../java/org/thoughtcrime/securesms/permissions/Permissions.java | 1 - 1 file changed, 1 deletion(-) 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 7e24a4f531..eb6669a865 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/permissions/Permissions.java +++ b/app/src/main/java/org/thoughtcrime/securesms/permissions/Permissions.java @@ -21,7 +21,6 @@ import java.util.ArrayList; import java.util.function.Consumer; -import java.util.stream.IntStream; import org.session.libsession.utilities.ServiceUtil; import org.thoughtcrime.securesms.util.LRUCache; From b291a6795ba863d185828820b90878dbaae2eeb4 Mon Sep 17 00:00:00 2001 From: ThomasSession Date: Tue, 10 Feb 2026 16:35:44 +1100 Subject: [PATCH 3/4] Update app/src/main/java/org/thoughtcrime/securesms/database/MmsDatabase.kt Co-authored-by: SessionHero01 <180888785+SessionHero01@users.noreply.github.com> --- .../java/org/thoughtcrime/securesms/database/MmsDatabase.kt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) 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 450897f474..2518c14d07 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/MmsDatabase.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/MmsDatabase.kt @@ -661,9 +661,8 @@ class MmsDatabase @Inject constructor( val thumbnailJobs: MutableList = ArrayList() // Collector for thumbnail jobs val previewAttachments: List = - linkPreviews.asSequence() + linkPreviews .mapNotNull { lp -> lp.getThumbnail().orNull() } - .toList() allAttachments.addAll(attachments) allAttachments.addAll(previewAttachments) From dcaf92a43ab8d837b8a0e0b4c9cc1eaee346b670 Mon Sep 17 00:00:00 2001 From: ThomasSession Date: Tue, 10 Feb 2026 16:41:49 +1100 Subject: [PATCH 4/4] PR feedback --- .../main/java/org/thoughtcrime/securesms/mms/SlideDeck.java | 6 +++++- .../java/org/thoughtcrime/securesms/util/SearchUtil.java | 6 +++--- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/mms/SlideDeck.java b/app/src/main/java/org/thoughtcrime/securesms/mms/SlideDeck.java index 439d8f2c1b..2036ab3543 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/mms/SlideDeck.java +++ b/app/src/main/java/org/thoughtcrime/securesms/mms/SlideDeck.java @@ -17,11 +17,15 @@ package org.thoughtcrime.securesms.mms; import android.content.Context; + import androidx.annotation.NonNull; import androidx.annotation.Nullable; + import org.session.libsession.messaging.sending_receiving.attachments.Attachment; import org.session.libsignal.utilities.guava.Optional; import org.thoughtcrime.securesms.util.MediaUtil; + +import java.util.ArrayList; import java.util.LinkedList; import java.util.List; import java.util.Objects; @@ -101,7 +105,7 @@ public boolean containsMediaSlide() { } public @NonNull List getThumbnailSlides() { - List result = new LinkedList<>(); + List result = new ArrayList<>(); for (Slide slide : slides) { if (slide.hasImage()) result.add(slide); } diff --git a/app/src/main/java/org/thoughtcrime/securesms/util/SearchUtil.java b/app/src/main/java/org/thoughtcrime/securesms/util/SearchUtil.java index 72f08d52a5..6d649dd852 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/util/SearchUtil.java +++ b/app/src/main/java/org/thoughtcrime/securesms/util/SearchUtil.java @@ -10,8 +10,8 @@ import org.session.libsignal.utilities.Pair; +import java.util.ArrayList; import java.util.Collections; -import java.util.LinkedList; import java.util.List; import java.util.Locale; @@ -66,7 +66,7 @@ static List> getHighlightRanges(@NonNull Locale locale, String normalizedText = text.toLowerCase(locale); String normalizedHighlight = highlight.toLowerCase(locale); - List highlightTokens = new LinkedList<>(); + List highlightTokens = new ArrayList<>(); for (String token : normalizedHighlight.split("\\s+")) { if (!token.isEmpty()) { @@ -74,7 +74,7 @@ static List> getHighlightRanges(@NonNull Locale locale, } } - List> ranges = new LinkedList<>(); + List> ranges = new ArrayList<>(); int lastHighlightEndIndex = 0;