From 31d388e00b00e2208bc101a7879f1e7e3c56b019 Mon Sep 17 00:00:00 2001 From: charles Date: Tue, 25 Oct 2022 16:25:06 +1100 Subject: [PATCH 01/16] fix: Authenticate all Open Group API calls * Use unblinded authentication when we have `capabilities` data for the open group server we are sending the request to but don't have the `blind` capability * Use blinded authentication when we haven't gotten any `capabilities` for an open group server, or if we have `capabilities` and the server has the `blind` capability --- .../messaging/jobs/BackgroundGroupAddJob.kt | 2 +- .../messaging/open_groups/OpenGroupApi.kt | 140 +++++++++--------- 2 files changed, 68 insertions(+), 74 deletions(-) diff --git a/libsession/src/main/java/org/session/libsession/messaging/jobs/BackgroundGroupAddJob.kt b/libsession/src/main/java/org/session/libsession/messaging/jobs/BackgroundGroupAddJob.kt index aa37e0f0a8..288ae75f82 100644 --- a/libsession/src/main/java/org/session/libsession/messaging/jobs/BackgroundGroupAddJob.kt +++ b/libsession/src/main/java/org/session/libsession/messaging/jobs/BackgroundGroupAddJob.kt @@ -41,7 +41,7 @@ class BackgroundGroupAddJob(val joinUrl: String): Job { } // get image storage.setOpenGroupPublicKey(openGroup.server, openGroup.serverPublicKey) - val (capabilities, info) = OpenGroupApi.getCapabilitiesAndRoomInfo(openGroup.room, openGroup.server, false).get() + val (capabilities, info) = OpenGroupApi.getCapabilitiesAndRoomInfo(openGroup.room, openGroup.server).get() storage.setServerCapabilities(openGroup.server, capabilities.capabilities) val imageId = info.imageId storage.addOpenGroup(openGroup.joinUrl()) diff --git a/libsession/src/main/java/org/session/libsession/messaging/open_groups/OpenGroupApi.kt b/libsession/src/main/java/org/session/libsession/messaging/open_groups/OpenGroupApi.kt index daa735aa4c..dbb5d6dd76 100644 --- a/libsession/src/main/java/org/session/libsession/messaging/open_groups/OpenGroupApi.kt +++ b/libsession/src/main/java/org/session/libsession/messaging/open_groups/OpenGroupApi.kt @@ -255,7 +255,6 @@ object OpenGroupApi { val queryParameters: Map = mapOf(), val parameters: Any? = null, val headers: Map = mapOf(), - val isAuthRequired: Boolean = true, val body: ByteArray? = null, /** * Always `true` under normal circumstances. You might want to disable @@ -301,73 +300,71 @@ object OpenGroupApi { ?: return Promise.ofFail(Error.NoEd25519KeyPair) val urlRequest = urlBuilder.toString() val headers = request.headers.toMutableMap() - if (request.isAuthRequired) { - val nonce = sodium.nonce(16) - val timestamp = TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis()) - var pubKey = "" - var signature = ByteArray(Sign.BYTES) - var bodyHash = ByteArray(0) - if (request.parameters != null) { - val parameterBytes = JsonUtil.toJson(request.parameters).toByteArray() - val parameterHash = ByteArray(GenericHash.BYTES_MAX) - if (sodium.cryptoGenericHash( - parameterHash, - parameterHash.size, - parameterBytes, - parameterBytes.size.toLong() - ) - ) { - bodyHash = parameterHash - } - } else if (request.body != null) { - val byteHash = ByteArray(GenericHash.BYTES_MAX) - if (sodium.cryptoGenericHash( - byteHash, - byteHash.size, - request.body, - request.body.size.toLong() - ) - ) { - bodyHash = byteHash - } - } - val messageBytes = Hex.fromStringCondensed(publicKey) - .plus(nonce) - .plus("$timestamp".toByteArray(Charsets.US_ASCII)) - .plus(request.verb.rawValue.toByteArray()) - .plus("/${request.endpoint.value}".toByteArray()) - .plus(bodyHash) - if (serverCapabilities.contains(Capability.BLIND.name.lowercase())) { - SodiumUtilities.blindedKeyPair(publicKey, ed25519KeyPair)?.let { keyPair -> - pubKey = SessionId( - IdPrefix.BLINDED, - keyPair.publicKey.asBytes - ).hexString - - signature = SodiumUtilities.sogsSignature( - messageBytes, - ed25519KeyPair.secretKey.asBytes, - keyPair.secretKey.asBytes, - keyPair.publicKey.asBytes - ) ?: return Promise.ofFail(Error.SigningFailed) - } ?: return Promise.ofFail(Error.SigningFailed) - } else { - pubKey = SessionId( - IdPrefix.UN_BLINDED, - ed25519KeyPair.publicKey.asBytes - ).hexString - sodium.cryptoSignDetached( - signature, - messageBytes, - messageBytes.size.toLong(), - ed25519KeyPair.secretKey.asBytes + val nonce = sodium.nonce(16) + val timestamp = TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis()) + var pubKey = "" + var signature = ByteArray(Sign.BYTES) + var bodyHash = ByteArray(0) + if (request.parameters != null) { + val parameterBytes = JsonUtil.toJson(request.parameters).toByteArray() + val parameterHash = ByteArray(GenericHash.BYTES_MAX) + if (sodium.cryptoGenericHash( + parameterHash, + parameterHash.size, + parameterBytes, + parameterBytes.size.toLong() ) + ) { + bodyHash = parameterHash + } + } else if (request.body != null) { + val byteHash = ByteArray(GenericHash.BYTES_MAX) + if (sodium.cryptoGenericHash( + byteHash, + byteHash.size, + request.body, + request.body.size.toLong() + ) + ) { + bodyHash = byteHash } - headers["X-SOGS-Nonce"] = encodeBytes(nonce) - headers["X-SOGS-Timestamp"] = "$timestamp" - headers["X-SOGS-Pubkey"] = pubKey - headers["X-SOGS-Signature"] = encodeBytes(signature) } + val messageBytes = Hex.fromStringCondensed(publicKey) + .plus(nonce) + .plus("$timestamp".toByteArray(Charsets.US_ASCII)) + .plus(request.verb.rawValue.toByteArray()) + .plus("/${request.endpoint.value}".toByteArray()) + .plus(bodyHash) + if (serverCapabilities.isEmpty() || serverCapabilities.contains(Capability.BLIND.name.lowercase())) { + SodiumUtilities.blindedKeyPair(publicKey, ed25519KeyPair)?.let { keyPair -> + pubKey = SessionId( + IdPrefix.BLINDED, + keyPair.publicKey.asBytes + ).hexString + + signature = SodiumUtilities.sogsSignature( + messageBytes, + ed25519KeyPair.secretKey.asBytes, + keyPair.secretKey.asBytes, + keyPair.publicKey.asBytes + ) ?: return Promise.ofFail(Error.SigningFailed) + } ?: return Promise.ofFail(Error.SigningFailed) + } else { + pubKey = SessionId( + IdPrefix.UN_BLINDED, + ed25519KeyPair.publicKey.asBytes + ).hexString + sodium.cryptoSignDetached( + signature, + messageBytes, + messageBytes.size.toLong(), + ed25519KeyPair.secretKey.asBytes + ) + } + headers["X-SOGS-Nonce"] = encodeBytes(nonce) + headers["X-SOGS-Timestamp"] = "$timestamp" + headers["X-SOGS-Pubkey"] = pubKey + headers["X-SOGS-Signature"] = encodeBytes(signature) val requestBuilder = okhttp3.Request.Builder() .url(urlRequest) @@ -794,16 +791,14 @@ object OpenGroupApi { private fun sequentialBatch( server: String, - requests: MutableList>, - authRequired: Boolean = true + requests: MutableList> ): Promise>, Exception> { val request = Request( verb = POST, room = null, server = server, endpoint = Endpoint.Sequence, - parameters = requests.map { it.request }, - isAuthRequired = authRequired + parameters = requests.map { it.request } ) return getBatchResponseJson(request, requests) } @@ -904,7 +899,7 @@ object OpenGroupApi { } fun getCapabilities(server: String): Promise { - val request = Request(verb = GET, room = null, server = server, endpoint = Endpoint.Capabilities, isAuthRequired = false) + val request = Request(verb = GET, room = null, server = server, endpoint = Endpoint.Capabilities) return getResponseBody(request).map { response -> JsonUtil.fromJson(response, Capabilities::class.java) } @@ -912,8 +907,7 @@ object OpenGroupApi { fun getCapabilitiesAndRoomInfo( room: String, - server: String, - authRequired: Boolean = true + server: String ): Promise, Exception> { val requests = mutableListOf>( BatchRequestInfo( @@ -933,7 +927,7 @@ object OpenGroupApi { responseType = object : TypeReference(){} ) ) - return sequentialBatch(server, requests, authRequired).map { + return sequentialBatch(server, requests).map { val capabilities = it.firstOrNull()?.body as? Capabilities ?: throw Error.ParsingFailed val roomInfo = it.lastOrNull()?.body as? RoomInfo ?: throw Error.ParsingFailed capabilities to roomInfo From 0f04929bf9ac37f73ce90998d7b69a1aa6aad8db Mon Sep 17 00:00:00 2001 From: aaronkerckhoff Date: Sat, 30 Sep 2023 01:32:39 +0200 Subject: [PATCH 02/16] Fix bug displaying user ID when quoting own message --- .../securesms/conversation/v2/messages/QuoteView.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 4e91400430..06470f5b2a 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 @@ -72,7 +72,7 @@ class QuoteView @JvmOverloads constructor(context: Context, attrs: AttributeSet? // Author val author = contactDb.getContactWithSessionID(authorPublicKey) val localNumber = TextSecurePreferences.getLocalNumber(context) - val quoteIsLocalUser = localNumber != null && localNumber == author?.sessionID + val quoteIsLocalUser = localNumber != null && author == null val authorDisplayName = if (quoteIsLocalUser) context.getString(R.string.QuoteView_you) From 4313eee33bd149c27dccbd0b2e030ed79decaa1d Mon Sep 17 00:00:00 2001 From: andrew Date: Wed, 11 Oct 2023 01:57:33 +1030 Subject: [PATCH 03/16] Fix notification update for incoming unsend request --- .../messaging/sending_receiving/ReceivedMessageHandler.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/ReceivedMessageHandler.kt b/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/ReceivedMessageHandler.kt index 8745418e77..2b168a8120 100644 --- a/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/ReceivedMessageHandler.kt +++ b/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/ReceivedMessageHandler.kt @@ -238,7 +238,7 @@ fun MessageReceiver.handleUnsendRequest(message: UnsendRequest): Long? { SnodeAPI.deleteMessage(author, listOf(serverHash)) } val deletedMessageId = messageDataProvider.updateMessageAsDeleted(timestamp, author) - if (!messageDataProvider.isOutgoingMessage(messageIdToDelete)) { + if (!messageDataProvider.isOutgoingMessage(timestamp)) { SSKEnvironment.shared.notificationManager.updateNotification(context) } From e595cfb504f2e494cecfa8020dacc152e77b1372 Mon Sep 17 00:00:00 2001 From: aaronkerckhoff Date: Sun, 15 Oct 2023 14:19:41 +0200 Subject: [PATCH 04/16] Improve check if author is own user when quoting messages --- .../securesms/conversation/v2/messages/QuoteView.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 06470f5b2a..f6e111ad19 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 @@ -72,7 +72,7 @@ class QuoteView @JvmOverloads constructor(context: Context, attrs: AttributeSet? // Author val author = contactDb.getContactWithSessionID(authorPublicKey) val localNumber = TextSecurePreferences.getLocalNumber(context) - val quoteIsLocalUser = localNumber != null && author == null + val quoteIsLocalUser = localNumber != null && localNumber != null && authorPublicKey == localNumber val authorDisplayName = if (quoteIsLocalUser) context.getString(R.string.QuoteView_you) From ef157f99910bdafbf2f844b4fa271cff16400600 Mon Sep 17 00:00:00 2001 From: 0x330a <92654767+0x330a@users.noreply.github.com> Date: Wed, 27 Mar 2024 15:23:42 +1100 Subject: [PATCH 05/16] refactor: simplify comparison --- .../securesms/conversation/v2/messages/QuoteView.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 f6e111ad19..2e0dae6b0d 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 @@ -72,7 +72,7 @@ class QuoteView @JvmOverloads constructor(context: Context, attrs: AttributeSet? // Author val author = contactDb.getContactWithSessionID(authorPublicKey) val localNumber = TextSecurePreferences.getLocalNumber(context) - val quoteIsLocalUser = localNumber != null && localNumber != null && authorPublicKey == localNumber + val quoteIsLocalUser = localNumber != null && authorPublicKey == localNumber val authorDisplayName = if (quoteIsLocalUser) context.getString(R.string.QuoteView_you) From 1377e192a155cdd6f5262ef49c4070414adefea3 Mon Sep 17 00:00:00 2001 From: fanchao Date: Fri, 17 May 2024 14:02:34 +1000 Subject: [PATCH 06/16] Stop playing message if deleted --- .../securesms/audio/AudioSlidePlayer.java | 5 ++++ .../conversation/v2/ConversationActivityV2.kt | 1 + .../conversation/v2/ConversationViewModel.kt | 29 +++++++++++++++---- .../v2/messages/VoiceMessageView.kt | 2 +- 4 files changed, 30 insertions(+), 7 deletions(-) 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 61a92105aa..ef404bb070 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/audio/AudioSlidePlayer.java +++ b/app/src/main/java/org/thoughtcrime/securesms/audio/AudioSlidePlayer.java @@ -80,6 +80,11 @@ public class AudioSlidePlayer implements SensorEventListener { } } + @Nullable + public synchronized static AudioSlidePlayer getInstance() { + return playing.orNull(); + } + private AudioSlidePlayer(@NonNull Context context, @NonNull AudioSlide slide, @NonNull Listener listener) 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 84f43e014b..781cc2b4e7 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 @@ -103,6 +103,7 @@ import org.thoughtcrime.securesms.ApplicationContext import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity import org.thoughtcrime.securesms.attachments.ScreenshotObserver import org.thoughtcrime.securesms.audio.AudioRecorder +import org.thoughtcrime.securesms.audio.AudioSlidePlayer import org.thoughtcrime.securesms.components.emoji.RecentEmojiPageModel import org.thoughtcrime.securesms.contacts.SelectContactsActivity.Companion.selectedContactsKey import org.thoughtcrime.securesms.conversation.ConversationActionBarDelegate 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 80d6df87fe..1bfd19274c 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 @@ -27,10 +27,11 @@ import org.session.libsession.utilities.Address import org.session.libsession.utilities.recipients.Recipient import org.session.libsignal.utilities.IdPrefix import org.session.libsignal.utilities.Log -import org.thoughtcrime.securesms.database.MmsSmsDatabase +import org.thoughtcrime.securesms.audio.AudioSlidePlayer import org.thoughtcrime.securesms.database.Storage import org.thoughtcrime.securesms.database.model.MessageRecord +import org.thoughtcrime.securesms.database.model.MmsMessageRecord import org.thoughtcrime.securesms.repository.ConversationRepository import java.util.UUID @@ -95,14 +96,15 @@ class ConversationViewModel( init { viewModelScope.launch(Dispatchers.IO) { repository.recipientUpdateFlow(threadId) - .collect { recipient -> - if (recipient == null && _uiState.value.conversationExists) { - _uiState.update { it.copy(conversationExists = false) } - } - } } } + override fun onCleared() { + super.onCleared() + + AudioSlidePlayer.stopAll() + } + fun saveDraft(text: String) { GlobalScope.launch(Dispatchers.IO) { repository.saveDraft(threadId, text) @@ -142,10 +144,24 @@ class ConversationViewModel( } fun deleteLocally(message: MessageRecord) { + stopPlayingAudioMessage(message) val recipient = recipient ?: return Log.w("Loki", "Recipient was null for delete locally action") repository.deleteLocally(recipient, message) } + /** + * Stops audio player if its current playing is the one given in the message. + */ + private fun stopPlayingAudioMessage(message: MessageRecord) { + val player = AudioSlidePlayer.getInstance() + val audioSlide = player?.audioSlide + if (audioSlide != null && + message is MmsMessageRecord && + message.slideDeck.audioSlide == audioSlide) { + player.stop() + } + } + fun setRecipientApproved() { val recipient = recipient ?: return Log.w("Loki", "Recipient was null for set approved action") repository.setApproved(recipient, true) @@ -157,6 +173,7 @@ class ConversationViewModel( repository.deleteForEveryone(threadId, recipient, message) .onSuccess { Log.d("Loki", "Deleted message ${message.id} ") + stopPlayingAudioMessage(message) } .onFailure { Log.w("Loki", "FAILED TO delete message ${message.id} ") diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/VoiceMessageView.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/VoiceMessageView.kt index 2b829af152..06a5168a99 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/VoiceMessageView.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/VoiceMessageView.kt @@ -68,7 +68,7 @@ class VoiceMessageView : RelativeLayout, AudioSlidePlayer.Listener { return } - val player = AudioSlidePlayer.createFor(context, audio, this) + val player = AudioSlidePlayer.createFor(context.applicationContext, audio, this) this.player = player (audio.asAttachment() as? DatabaseAttachment)?.let { attachment -> From 580bf9ebb3ec9be2ad7286bb319bcbc648c99aff Mon Sep 17 00:00:00 2001 From: fanchao Date: Fri, 17 May 2024 14:07:52 +1000 Subject: [PATCH 07/16] Accidental change --- .../securesms/conversation/v2/ConversationViewModel.kt | 5 +++++ 1 file changed, 5 insertions(+) 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 1bfd19274c..2f22edeafe 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 @@ -96,6 +96,11 @@ class ConversationViewModel( init { viewModelScope.launch(Dispatchers.IO) { repository.recipientUpdateFlow(threadId) + .collect { recipient -> + if (recipient == null && _uiState.value.conversationExists) { + _uiState.update { it.copy(conversationExists = false) } + } + } } } From 23872afeb44237db402d3ef6e2ce343a8838f87e Mon Sep 17 00:00:00 2001 From: fanchao Date: Fri, 17 May 2024 14:09:10 +1000 Subject: [PATCH 08/16] Accidental change --- .../securesms/conversation/v2/ConversationViewModel.kt | 8 ++++---- 1 file changed, 4 insertions(+), 4 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 2f22edeafe..6e4b51cf39 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 @@ -96,11 +96,11 @@ class ConversationViewModel( init { viewModelScope.launch(Dispatchers.IO) { repository.recipientUpdateFlow(threadId) - .collect { recipient -> - if (recipient == null && _uiState.value.conversationExists) { - _uiState.update { it.copy(conversationExists = false) } - } + .collect { recipient -> + if (recipient == null && _uiState.value.conversationExists) { + _uiState.update { it.copy(conversationExists = false) } } + } } } From e49d017b0831da74a69c77a2ec75e44a06e280c6 Mon Sep 17 00:00:00 2001 From: fanchao Date: Fri, 17 May 2024 14:09:40 +1000 Subject: [PATCH 09/16] Comments --- .../securesms/conversation/v2/ConversationViewModel.kt | 1 + 1 file changed, 1 insertion(+) 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 6e4b51cf39..b64cb10556 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 @@ -107,6 +107,7 @@ class ConversationViewModel( override fun onCleared() { super.onCleared() + // Stop all voice message when existing the convo page AudioSlidePlayer.stopAll() } From 03893973b7bdcf22b3acd8f7eca73d48a10bebc8 Mon Sep 17 00:00:00 2001 From: fanchao Date: Mon, 20 May 2024 09:34:40 +1000 Subject: [PATCH 10/16] Feedback --- .../conversation/v2/ConversationViewModel.kt | 12 ++++-------- 1 file changed, 4 insertions(+), 8 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 b64cb10556..ce35a85910 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 @@ -107,7 +107,7 @@ class ConversationViewModel( override fun onCleared() { super.onCleared() - // Stop all voice message when existing the convo page + // Stop all voice message when exiting the convo page AudioSlidePlayer.stopAll() } @@ -159,13 +159,9 @@ class ConversationViewModel( * Stops audio player if its current playing is the one given in the message. */ private fun stopPlayingAudioMessage(message: MessageRecord) { - val player = AudioSlidePlayer.getInstance() - val audioSlide = player?.audioSlide - if (audioSlide != null && - message is MmsMessageRecord && - message.slideDeck.audioSlide == audioSlide) { - player.stop() - } + val mmsMessage = message as? MmsMessageRecord ?: return + val audioSlide = mmsMessage.slideDeck.audioSlide ?: return + AudioSlidePlayer.getInstance()?.takeIf { it.audioSlide == audioSlide }?.stop() } fun setRecipientApproved() { From a8cc9e24097945c4a77f71da8022b2e0173ad783 Mon Sep 17 00:00:00 2001 From: fanchao Date: Mon, 20 May 2024 09:50:29 +1000 Subject: [PATCH 11/16] Comments --- .../securesms/conversation/v2/ConversationViewModel.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 ce35a85910..764e97e10a 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 @@ -107,7 +107,7 @@ class ConversationViewModel( override fun onCleared() { super.onCleared() - // Stop all voice message when exiting the convo page + // Stop all voice message when exiting this page AudioSlidePlayer.stopAll() } From 10597f1f3096141394c536233b597021a915935e Mon Sep 17 00:00:00 2001 From: fanchao Date: Mon, 20 May 2024 09:53:44 +1000 Subject: [PATCH 12/16] Import --- .../securesms/conversation/v2/ConversationActivityV2.kt | 1 - 1 file changed, 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 781cc2b4e7..84f43e014b 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 @@ -103,7 +103,6 @@ import org.thoughtcrime.securesms.ApplicationContext import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity import org.thoughtcrime.securesms.attachments.ScreenshotObserver import org.thoughtcrime.securesms.audio.AudioRecorder -import org.thoughtcrime.securesms.audio.AudioSlidePlayer import org.thoughtcrime.securesms.components.emoji.RecentEmojiPageModel import org.thoughtcrime.securesms.contacts.SelectContactsActivity.Companion.selectedContactsKey import org.thoughtcrime.securesms.conversation.ConversationActionBarDelegate From 0d3a33e6c6e571d73089d0ea6286aad28a4b54de Mon Sep 17 00:00:00 2001 From: fanchao Date: Mon, 27 May 2024 13:25:56 +1000 Subject: [PATCH 13/16] Fix delete message for everyone doesn't stop the audio playing --- .../securesms/conversation/v2/ConversationViewModel.kt | 1 + 1 file changed, 1 insertion(+) 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 764e97e10a..1a036eee11 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 @@ -171,6 +171,7 @@ class ConversationViewModel( fun deleteForEveryone(message: MessageRecord) = viewModelScope.launch { val recipient = recipient ?: return@launch Log.w("Loki", "Recipient was null for delete for everyone - aborting delete operation.") + stopPlayingAudioMessage(message) repository.deleteForEveryone(threadId, recipient, message) .onSuccess { From 54bb84541a008781db9010b1a69f24431b3a5a3e Mon Sep 17 00:00:00 2001 From: fanchao Date: Thu, 6 Jun 2024 13:59:30 +1000 Subject: [PATCH 14/16] Optimise XML --- .../securesms/BaseActionBarActivity.java | 12 +++- .../conversation/v2/ConversationActivityV2.kt | 7 ++- .../conversation/v2/ConversationAdapter.kt | 13 ++-- .../v2/messages/ControlMessageView.kt | 11 ++-- .../v2/messages/VisibleMessageView.kt | 61 ++++++++++++------- .../securesms/dependencies/AppModule.kt | 2 +- .../securesms/dependencies/UiModule.kt | 19 ++++++ .../dependencies/ViewPoolQualifiers.kt | 7 +++ .../main/res/layout/view_visible_message.xml | 48 +++------------ ...wstub_visible_message_marker_container.xml | 38 ++++++++++++ ...wstub_visible_message_status_container.xml | 33 ++++++++++ 11 files changed, 167 insertions(+), 84 deletions(-) create mode 100644 app/src/main/java/org/thoughtcrime/securesms/dependencies/UiModule.kt create mode 100644 app/src/main/java/org/thoughtcrime/securesms/dependencies/ViewPoolQualifiers.kt create mode 100644 app/src/main/res/layout/viewstub_visible_message_marker_container.xml create mode 100644 app/src/main/res/layout/viewstub_visible_message_status_container.xml diff --git a/app/src/main/java/org/thoughtcrime/securesms/BaseActionBarActivity.java b/app/src/main/java/org/thoughtcrime/securesms/BaseActionBarActivity.java index c43d406575..a99fe83430 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/BaseActionBarActivity.java +++ b/app/src/main/java/org/thoughtcrime/securesms/BaseActionBarActivity.java @@ -30,13 +30,15 @@ public abstract class BaseActionBarActivity extends AppCompatActivity { private static final String TAG = BaseActionBarActivity.class.getSimpleName(); public ThemeState currentThemeState; + private Resources.Theme modifiedTheme; + private TextSecurePreferences getPreferences() { ApplicationContext appContext = (ApplicationContext) getApplicationContext(); return appContext.textSecurePreferences; } @StyleRes - public int getDesiredTheme() { + private int getDesiredTheme() { ThemeState themeState = ActivityUtilitiesKt.themeState(getPreferences()); int userSelectedTheme = themeState.getTheme(); @@ -58,7 +60,7 @@ public abstract class BaseActionBarActivity extends AppCompatActivity { } @StyleRes @Nullable - public Integer getAccentTheme() { + private Integer getAccentTheme() { if (!getPreferences().hasPreference(SELECTED_ACCENT_COLOR)) return null; ThemeState themeState = ActivityUtilitiesKt.themeState(getPreferences()); return themeState.getAccentStyle(); @@ -66,8 +68,12 @@ public abstract class BaseActionBarActivity extends AppCompatActivity { @Override public Resources.Theme getTheme() { + if (modifiedTheme != null) { + return modifiedTheme; + } + // New themes - Resources.Theme modifiedTheme = super.getTheme(); + modifiedTheme = super.getTheme(); modifiedTheme.applyStyle(getDesiredTheme(), true); Integer accentTheme = getAccentTheme(); if (accentTheme != null) { 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 76bf7b875f..62704f1b1c 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 @@ -61,7 +61,6 @@ import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import network.loki.messenger.R import network.loki.messenger.databinding.ActivityConversationV2Binding -import network.loki.messenger.databinding.ViewVisibleMessageBinding import network.loki.messenger.libsession_util.util.ExpiryMode import nl.komponents.kovenant.ui.successUi import org.session.libsession.messaging.MessagingModuleConfiguration @@ -146,6 +145,7 @@ import org.thoughtcrime.securesms.database.model.MessageId import org.thoughtcrime.securesms.database.model.MessageRecord import org.thoughtcrime.securesms.database.model.MmsMessageRecord import org.thoughtcrime.securesms.database.model.ReactionRecord +import org.thoughtcrime.securesms.dependencies.ConversationViewPool import org.thoughtcrime.securesms.giph.ui.GiphyActivity import org.thoughtcrime.securesms.groups.OpenGroupManager import org.thoughtcrime.securesms.linkpreview.LinkPreviewRepository @@ -216,6 +216,7 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe @Inject lateinit var storage: Storage @Inject lateinit var reactionDb: ReactionDatabase @Inject lateinit var viewModelFactory: ConversationViewModel.AssistedFactory + @Inject @ConversationViewPool lateinit var viewPool: RecyclerView.RecycledViewPool private val screenshotObserver by lazy { ScreenshotObserver(this, Handler(Looper.getMainLooper())) { @@ -568,6 +569,7 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe // called from onCreate private fun setUpRecyclerView() { binding!!.conversationRecyclerView.adapter = adapter + binding!!.conversationRecyclerView.setRecycledViewPool(viewPool) val layoutManager = LinearLayoutManager(this, LinearLayoutManager.VERTICAL, reverseMessageList) binding!!.conversationRecyclerView.layoutManager = layoutManager // Workaround for the fact that CursorRecyclerViewAdapter doesn't auto-update automatically (even though it says it will) @@ -1558,8 +1560,7 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe if (indexInAdapter < 0 || indexInAdapter >= adapter.itemCount) { return } val viewHolder = binding?.conversationRecyclerView?.findViewHolderForAdapterPosition(indexInAdapter) as? ConversationAdapter.VisibleMessageViewHolder ?: return - val visibleMessageView = ViewVisibleMessageBinding.bind(viewHolder.view).visibleMessageView - visibleMessageView.playVoiceMessage() + viewHolder.view.playVoiceMessage() } override fun sendMessage() { 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 7b01eba71e..d051d7d93c 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 @@ -5,9 +5,7 @@ import android.content.Intent import android.database.Cursor import android.util.SparseArray import android.util.SparseBooleanArray -import android.view.LayoutInflater import android.view.MotionEvent -import android.view.View import android.view.ViewGroup import androidx.annotation.WorkerThread import androidx.core.util.getOrDefault @@ -20,14 +18,11 @@ import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.isActive import kotlinx.coroutines.launch import network.loki.messenger.R -import network.loki.messenger.databinding.ViewVisibleMessageBinding import org.session.libsession.messaging.contacts.Contact -import org.session.libsession.utilities.TextSecurePreferences import org.thoughtcrime.securesms.conversation.v2.messages.ControlMessageView import org.thoughtcrime.securesms.conversation.v2.messages.VisibleMessageView import org.thoughtcrime.securesms.conversation.v2.messages.VisibleMessageViewDelegate import org.thoughtcrime.securesms.database.CursorRecyclerViewAdapter -import org.thoughtcrime.securesms.database.MmsSmsColumns import org.thoughtcrime.securesms.database.model.MessageRecord import org.thoughtcrime.securesms.dependencies.DatabaseComponent import org.thoughtcrime.securesms.mms.GlideRequests @@ -90,7 +85,7 @@ class ConversationAdapter( } } - class VisibleMessageViewHolder(val view: View) : ViewHolder(view) + class VisibleMessageViewHolder(val view: VisibleMessageView) : ViewHolder(view) class ControlMessageViewHolder(val view: ControlMessageView) : ViewHolder(view) override fun getItemViewType(cursor: Cursor): Int { @@ -103,7 +98,7 @@ class ConversationAdapter( @Suppress("NAME_SHADOWING") val viewType = ViewType.allValues[viewType] return when (viewType) { - ViewType.Visible -> VisibleMessageViewHolder(LayoutInflater.from(parent.context).inflate(R.layout.view_visible_message, parent, false)) + ViewType.Visible -> VisibleMessageViewHolder(VisibleMessageView(context)) ViewType.Control -> ControlMessageViewHolder(ControlMessageView(context)) else -> throw IllegalStateException("Unexpected view type: $viewType.") } @@ -115,7 +110,7 @@ class ConversationAdapter( val messageBefore = getMessageBefore(position, cursor) when (viewHolder) { is VisibleMessageViewHolder -> { - val visibleMessageView = ViewVisibleMessageBinding.bind(viewHolder.view).visibleMessageView + val visibleMessageView = viewHolder.view val isSelected = selectedItems.contains(message) visibleMessageView.snIsSelected = isSelected visibleMessageView.indexInAdapter = position @@ -181,7 +176,7 @@ class ConversationAdapter( override fun onItemViewRecycled(viewHolder: ViewHolder?) { when (viewHolder) { - is VisibleMessageViewHolder -> viewHolder.view.findViewById(R.id.visibleMessageView).recycle() + is VisibleMessageViewHolder -> viewHolder.view.recycle() is ControlMessageViewHolder -> viewHolder.view.recycle() } super.onItemViewRecycled(viewHolder) diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/ControlMessageView.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/ControlMessageView.kt index 88df4c4508..1177b4afc9 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/ControlMessageView.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/ControlMessageView.kt @@ -25,16 +25,15 @@ class ControlMessageView : LinearLayout { private val TAG = "ControlMessageView" - private lateinit var binding: ViewControlMessageBinding + private val binding = ViewControlMessageBinding.inflate(LayoutInflater.from(context), this, true) - constructor(context: Context) : super(context) { initialize() } - constructor(context: Context, attrs: AttributeSet) : super(context, attrs) { initialize() } - constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr) { initialize() } + constructor(context: Context) : super(context) + constructor(context: Context, attrs: AttributeSet) : super(context, attrs) + constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr) @Inject lateinit var disappearingMessages: DisappearingMessages - private fun initialize() { - binding = ViewControlMessageBinding.inflate(LayoutInflater.from(context), this, true) + init { layoutParams = RecyclerView.LayoutParams(RecyclerView.LayoutParams.MATCH_PARENT, RecyclerView.LayoutParams.WRAP_CONTENT) } 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 64017e2ad9..ec26e39986 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 @@ -11,8 +11,10 @@ import android.os.Looper import android.util.AttributeSet import android.view.Gravity import android.view.HapticFeedbackConstants +import android.view.LayoutInflater import android.view.MotionEvent import android.view.View +import android.view.ViewGroup import android.widget.FrameLayout import android.widget.LinearLayout import androidx.annotation.ColorInt @@ -26,17 +28,17 @@ import androidx.core.view.isVisible import androidx.core.view.marginBottom import dagger.hilt.android.AndroidEntryPoint import network.loki.messenger.R +import network.loki.messenger.databinding.ViewEmojiReactionsBinding import network.loki.messenger.databinding.ViewVisibleMessageBinding +import network.loki.messenger.databinding.ViewstubVisibleMessageMarkerContainerBinding import org.session.libsession.messaging.contacts.Contact import org.session.libsession.messaging.contacts.Contact.ContactContext import org.session.libsession.messaging.open_groups.OpenGroupApi import org.session.libsession.utilities.Address -import org.session.libsession.utilities.TextSecurePreferences import org.session.libsession.utilities.ViewUtil import org.session.libsession.utilities.getColorFromAttr import org.session.libsession.utilities.modifyLayoutParams import org.session.libsignal.utilities.IdPrefix -import org.session.libsignal.utilities.Log import org.thoughtcrime.securesms.conversation.v2.ConversationActivityV2 import org.thoughtcrime.securesms.database.LastSentTimestampCache import org.thoughtcrime.securesms.database.LokiAPIDatabase @@ -65,7 +67,7 @@ import kotlin.math.sqrt private const val TAG = "VisibleMessageView" @AndroidEntryPoint -class VisibleMessageView : LinearLayout { +class VisibleMessageView : FrameLayout { private var replyDisabled: Boolean = false @Inject lateinit var threadDb: ThreadDatabase @Inject lateinit var lokiThreadDb: LokiThreadDatabase @@ -75,7 +77,16 @@ class VisibleMessageView : LinearLayout { @Inject lateinit var mmsDb: MmsDatabase @Inject lateinit var lastSentTimestampCache: LastSentTimestampCache - private val binding by lazy { ViewVisibleMessageBinding.bind(this) } + private val binding = ViewVisibleMessageBinding.inflate(LayoutInflater.from(context), this, true) + + private val markerContainerBinding = lazy(LazyThreadSafetyMode.NONE) { + ViewstubVisibleMessageMarkerContainerBinding.bind(binding.unreadMarkerContainerStub.inflate()) + } + + private val emojiReactionsBinding = lazy(LazyThreadSafetyMode.NONE) { + ViewEmojiReactionsBinding.bind(binding.emojiReactionsView.inflate()) + } + private val swipeToReplyIcon = ContextCompat.getDrawable(context, R.drawable.ic_baseline_reply_24)!!.mutate() private val swipeToReplyIconRect = Rect() private var dx = 0.0f @@ -94,7 +105,7 @@ class VisibleMessageView : LinearLayout { var onPress: ((event: MotionEvent) -> Unit)? = null var onSwipeToReply: (() -> Unit)? = null var onLongPress: (() -> Unit)? = null - val messageContentView: VisibleMessageContentView by lazy { binding.messageContentView.root } + val messageContentView: VisibleMessageContentView get() = binding.messageContentView.root companion object { const val swipeToReplyThreshold = 64.0f // dp @@ -108,12 +119,7 @@ class VisibleMessageView : LinearLayout { constructor(context: Context, attrs: AttributeSet) : super(context, attrs) constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr) - override fun onFinishInflate() { - super.onFinishInflate() - initialize() - } - - private fun initialize() { + init { isHapticFeedbackEnabled = true setWillNotDraw(false) binding.root.disableClipping() @@ -121,7 +127,11 @@ class VisibleMessageView : LinearLayout { binding.messageInnerContainer.disableClipping() binding.messageInnerLayout.disableClipping() binding.messageContentView.root.disableClipping() + + // Default layout params + layoutParams = ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT) } + // endregion // region Updating @@ -203,7 +213,13 @@ class VisibleMessageView : LinearLayout { binding.senderNameTextView.text = contact?.displayName(contactContext) ?: senderSessionID // Unread marker - binding.unreadMarkerContainer.isVisible = lastSeen != -1L && message.timestamp > lastSeen && (previous == null || previous.timestamp <= lastSeen) && !message.isOutgoing + val shouldShowUnreadMarker = lastSeen != -1L && message.timestamp > lastSeen && (previous == null || previous.timestamp <= lastSeen) && !message.isOutgoing + if (shouldShowUnreadMarker) { + markerContainerBinding.value.root.isVisible = true + } else if (markerContainerBinding.isInitialized()) { + // Only need to hide the binding when the binding is inflated. (default is gone) + markerContainerBinding.value.root.isVisible = false + } // Date break val showDateBreak = isStartOfMessageCluster || snIsSelected @@ -214,21 +230,22 @@ class VisibleMessageView : LinearLayout { showStatusMessage(message) // Emoji Reactions - val emojiLayoutParams = binding.emojiReactionsView.root.layoutParams as ConstraintLayout.LayoutParams - emojiLayoutParams.horizontalBias = if (message.isOutgoing) 1f else 0f - binding.emojiReactionsView.root.layoutParams = emojiLayoutParams - if (message.reactions.isNotEmpty()) { val capabilities = lokiThreadDb.getOpenGroupChat(threadID)?.server?.let { lokiApiDb.getServerCapabilities(it) } if (capabilities.isNullOrEmpty() || capabilities.contains(OpenGroupApi.Capability.REACTIONS.name.lowercase())) { - binding.emojiReactionsView.root.setReactions(message.id, message.reactions, message.isOutgoing, delegate) - binding.emojiReactionsView.root.isVisible = true - } else { - binding.emojiReactionsView.root.isVisible = false + emojiReactionsBinding.value.root.let { root -> + root.setReactions(message.id, message.reactions, message.isOutgoing, delegate) + root.isVisible = true + (root.layoutParams as ConstraintLayout.LayoutParams).apply { + horizontalBias = if (message.isOutgoing) 1f else 0f + } + } + } else if (emojiReactionsBinding.isInitialized()) { + emojiReactionsBinding.value.root.isVisible = false } } - else { - binding.emojiReactionsView.root.isVisible = false + else if (emojiReactionsBinding.isInitialized()) { + emojiReactionsBinding.value.root.isVisible = false } // Populate content view diff --git a/app/src/main/java/org/thoughtcrime/securesms/dependencies/AppModule.kt b/app/src/main/java/org/thoughtcrime/securesms/dependencies/AppModule.kt index 936e4f287f..a9a72e7665 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/dependencies/AppModule.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/dependencies/AppModule.kt @@ -19,11 +19,11 @@ abstract class AppModule { @Binds abstract fun bindConversationRepository(repository: DefaultConversationRepository): ConversationRepository - } @EntryPoint @InstallIn(SingletonComponent::class) interface AppComponent { fun getPrefs(): TextSecurePreferences + } \ No newline at end of file diff --git a/app/src/main/java/org/thoughtcrime/securesms/dependencies/UiModule.kt b/app/src/main/java/org/thoughtcrime/securesms/dependencies/UiModule.kt new file mode 100644 index 0000000000..7a552c00a3 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/dependencies/UiModule.kt @@ -0,0 +1,19 @@ +package org.thoughtcrime.securesms.dependencies + +import androidx.recyclerview.widget.RecyclerView +import dagger.Module +import dagger.Provides +import dagger.hilt.InstallIn +import dagger.hilt.components.SingletonComponent +import javax.inject.Singleton + +@Module +@InstallIn(SingletonComponent::class) +class UiModule { + @Singleton + @Provides + @ConversationViewPool + fun provideConversationRecycledViewPool(): RecyclerView.RecycledViewPool { + return RecyclerView.RecycledViewPool() + } +} \ No newline at end of file diff --git a/app/src/main/java/org/thoughtcrime/securesms/dependencies/ViewPoolQualifiers.kt b/app/src/main/java/org/thoughtcrime/securesms/dependencies/ViewPoolQualifiers.kt new file mode 100644 index 0000000000..0d10cfd0df --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/dependencies/ViewPoolQualifiers.kt @@ -0,0 +1,7 @@ +package org.thoughtcrime.securesms.dependencies + +import javax.inject.Qualifier + +@Qualifier +@Retention(AnnotationRetention.SOURCE) +annotation class ConversationViewPool diff --git a/app/src/main/res/layout/view_visible_message.xml b/app/src/main/res/layout/view_visible_message.xml index 19f9b4f9ad..1e099e319e 100644 --- a/app/src/main/res/layout/view_visible_message.xml +++ b/app/src/main/res/layout/view_visible_message.xml @@ -1,5 +1,5 @@ - - - - - - + android:id="@+id/unreadMarkerContainerStub" + android:layout="@layout/viewstub_visible_message_marker_container" /> - - \ No newline at end of file + \ No newline at end of file diff --git a/app/src/main/res/layout/viewstub_visible_message_marker_container.xml b/app/src/main/res/layout/viewstub_visible_message_marker_container.xml new file mode 100644 index 0000000000..d9a1cc15d0 --- /dev/null +++ b/app/src/main/res/layout/viewstub_visible_message_marker_container.xml @@ -0,0 +1,38 @@ + + + + + + + + + diff --git a/app/src/main/res/layout/viewstub_visible_message_status_container.xml b/app/src/main/res/layout/viewstub_visible_message_status_container.xml new file mode 100644 index 0000000000..745b453cad --- /dev/null +++ b/app/src/main/res/layout/viewstub_visible_message_status_container.xml @@ -0,0 +1,33 @@ + + + + + + + + + + From 072accb1e1a47337ce2f144adb929c2b3f2d417b Mon Sep 17 00:00:00 2001 From: fanchao Date: Tue, 11 Jun 2024 14:10:56 +1000 Subject: [PATCH 15/16] Remove unused file --- ...wstub_visible_message_status_container.xml | 33 ------------------- 1 file changed, 33 deletions(-) delete mode 100644 app/src/main/res/layout/viewstub_visible_message_status_container.xml diff --git a/app/src/main/res/layout/viewstub_visible_message_status_container.xml b/app/src/main/res/layout/viewstub_visible_message_status_container.xml deleted file mode 100644 index 745b453cad..0000000000 --- a/app/src/main/res/layout/viewstub_visible_message_status_container.xml +++ /dev/null @@ -1,33 +0,0 @@ - - - - - - - - - - From d0e41480403b28be5e9f3b3cd50d2d46005ba9e8 Mon Sep 17 00:00:00 2001 From: fanchao Date: Tue, 11 Jun 2024 15:26:01 +1000 Subject: [PATCH 16/16] Remove view pools --- .../conversation/v2/ConversationActivityV2.kt | 3 --- .../securesms/dependencies/UiModule.kt | 19 ------------------- .../dependencies/ViewPoolQualifiers.kt | 7 ------- 3 files changed, 29 deletions(-) delete mode 100644 app/src/main/java/org/thoughtcrime/securesms/dependencies/UiModule.kt delete mode 100644 app/src/main/java/org/thoughtcrime/securesms/dependencies/ViewPoolQualifiers.kt 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 62704f1b1c..187ded770e 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 @@ -145,7 +145,6 @@ import org.thoughtcrime.securesms.database.model.MessageId import org.thoughtcrime.securesms.database.model.MessageRecord import org.thoughtcrime.securesms.database.model.MmsMessageRecord import org.thoughtcrime.securesms.database.model.ReactionRecord -import org.thoughtcrime.securesms.dependencies.ConversationViewPool import org.thoughtcrime.securesms.giph.ui.GiphyActivity import org.thoughtcrime.securesms.groups.OpenGroupManager import org.thoughtcrime.securesms.linkpreview.LinkPreviewRepository @@ -216,7 +215,6 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe @Inject lateinit var storage: Storage @Inject lateinit var reactionDb: ReactionDatabase @Inject lateinit var viewModelFactory: ConversationViewModel.AssistedFactory - @Inject @ConversationViewPool lateinit var viewPool: RecyclerView.RecycledViewPool private val screenshotObserver by lazy { ScreenshotObserver(this, Handler(Looper.getMainLooper())) { @@ -569,7 +567,6 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe // called from onCreate private fun setUpRecyclerView() { binding!!.conversationRecyclerView.adapter = adapter - binding!!.conversationRecyclerView.setRecycledViewPool(viewPool) val layoutManager = LinearLayoutManager(this, LinearLayoutManager.VERTICAL, reverseMessageList) binding!!.conversationRecyclerView.layoutManager = layoutManager // Workaround for the fact that CursorRecyclerViewAdapter doesn't auto-update automatically (even though it says it will) diff --git a/app/src/main/java/org/thoughtcrime/securesms/dependencies/UiModule.kt b/app/src/main/java/org/thoughtcrime/securesms/dependencies/UiModule.kt deleted file mode 100644 index 7a552c00a3..0000000000 --- a/app/src/main/java/org/thoughtcrime/securesms/dependencies/UiModule.kt +++ /dev/null @@ -1,19 +0,0 @@ -package org.thoughtcrime.securesms.dependencies - -import androidx.recyclerview.widget.RecyclerView -import dagger.Module -import dagger.Provides -import dagger.hilt.InstallIn -import dagger.hilt.components.SingletonComponent -import javax.inject.Singleton - -@Module -@InstallIn(SingletonComponent::class) -class UiModule { - @Singleton - @Provides - @ConversationViewPool - fun provideConversationRecycledViewPool(): RecyclerView.RecycledViewPool { - return RecyclerView.RecycledViewPool() - } -} \ No newline at end of file diff --git a/app/src/main/java/org/thoughtcrime/securesms/dependencies/ViewPoolQualifiers.kt b/app/src/main/java/org/thoughtcrime/securesms/dependencies/ViewPoolQualifiers.kt deleted file mode 100644 index 0d10cfd0df..0000000000 --- a/app/src/main/java/org/thoughtcrime/securesms/dependencies/ViewPoolQualifiers.kt +++ /dev/null @@ -1,7 +0,0 @@ -package org.thoughtcrime.securesms.dependencies - -import javax.inject.Qualifier - -@Qualifier -@Retention(AnnotationRetention.SOURCE) -annotation class ConversationViewPool