From 31d388e00b00e2208bc101a7879f1e7e3c56b019 Mon Sep 17 00:00:00 2001 From: charles Date: Tue, 25 Oct 2022 16:25:06 +1100 Subject: [PATCH 01/23] 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 | 136 +++++++++--------- 2 files changed, 66 insertions(+), 72 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 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 } - 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 { + } 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.isEmpty() || serverCapabilities.contains(Capability.BLIND.name.lowercase())) { + SodiumUtilities.blindedKeyPair(publicKey, ed25519KeyPair)?.let { keyPair -> pubKey = SessionId( - IdPrefix.UN_BLINDED, - ed25519KeyPair.publicKey.asBytes + IdPrefix.BLINDED, + keyPair.publicKey.asBytes ).hexString - sodium.cryptoSignDetached( - signature, + + signature = SodiumUtilities.sogsSignature( 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) + 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 60ea5ba3a7fec595df8e62eaba859b071bf23b16 Mon Sep 17 00:00:00 2001 From: andrew Date: Wed, 9 Aug 2023 20:02:51 +0930 Subject: [PATCH 02/23] Hide send button when message contains only whitespace --- .../securesms/conversation/v2/input_bar/InputBar.kt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/input_bar/InputBar.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/input_bar/InputBar.kt index 73e2d571c0..ffdc425c5b 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/input_bar/InputBar.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/input_bar/InputBar.kt @@ -13,6 +13,7 @@ import android.view.MotionEvent import android.view.inputmethod.EditorInfo import android.widget.RelativeLayout import android.widget.TextView +import androidx.core.view.isGone import androidx.core.view.isVisible import network.loki.messenger.R import network.loki.messenger.databinding.ViewInputBarBinding @@ -118,8 +119,8 @@ class InputBar : RelativeLayout, InputBarEditTextDelegate, QuoteViewDelegate, Li // region Updating override fun inputBarEditTextContentChanged(text: CharSequence) { - sendButton.isVisible = text.isNotEmpty() - microphoneButton.isVisible = text.isEmpty() + microphoneButton.isVisible = text.all { it.isWhitespace() } + sendButton.isVisible = microphoneButton.isGone delegate?.inputBarEditTextContentChanged(text) } From 0f04929bf9ac37f73ce90998d7b69a1aa6aad8db Mon Sep 17 00:00:00 2001 From: aaronkerckhoff Date: Sat, 30 Sep 2023 01:32:39 +0200 Subject: [PATCH 03/23] 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 04/23] 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 05/23] 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 c647bab35ea78659d17b9e03c6f63584432230c4 Mon Sep 17 00:00:00 2001 From: Rugved Darwhekar Date: Sun, 29 Oct 2023 15:58:54 -0700 Subject: [PATCH 06/23] Fixed video call auto rotate, when auto rotate is disabled --- .../securesms/calls/WebRtcCallActivity.kt | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/calls/WebRtcCallActivity.kt b/app/src/main/java/org/thoughtcrime/securesms/calls/WebRtcCallActivity.kt index b87eac12c4..1567531cd2 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/calls/WebRtcCallActivity.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/calls/WebRtcCallActivity.kt @@ -21,6 +21,7 @@ import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.Job import kotlinx.coroutines.delay import kotlinx.coroutines.isActive +import android.provider.Settings import kotlinx.coroutines.launch import network.loki.messenger.R import network.loki.messenger.databinding.ActivityWebrtcBinding @@ -99,7 +100,14 @@ class WebRtcCallActivity : PassphraseRequiredActionBarActivity() { override fun onCreate(savedInstanceState: Bundle?, ready: Boolean) { super.onCreate(savedInstanceState, ready) - rotationListener.enable() + + // Only enable auto-rotate if system auto-rotate is enabled + if (isAutoRotateOn()) { + rotationListener.enable() + } else { + rotationListener.disable() + } + binding = ActivityWebrtcBinding.inflate(layoutInflater) setContentView(binding.root) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1) { @@ -183,6 +191,14 @@ class WebRtcCallActivity : PassphraseRequiredActionBarActivity() { } + //Function to check if Android System Auto-rotate is on or off + private fun isAutoRotateOn(): Boolean { + return Settings.System.getInt( + contentResolver, + Settings.System.ACCELEROMETER_ROTATION, 0 + ) == 1 + } + override fun onDestroy() { super.onDestroy() hangupReceiver?.let { receiver -> 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 07/23] 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 08/23] 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 09/23] 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 10/23] 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 11/23] 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 12/23] 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 13/23] 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 14/23] 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 15/23] 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 172edde628f6d982d3c7396a19415d78faf8e6d3 Mon Sep 17 00:00:00 2001 From: fanchao Date: Tue, 4 Jun 2024 10:41:15 +1000 Subject: [PATCH 16/23] Correct the usage of flowOn --- .../thoughtcrime/securesms/home/HomeViewModel.kt | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/home/HomeViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/home/HomeViewModel.kt index fa18a995b6..6819054b31 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/home/HomeViewModel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/home/HomeViewModel.kt @@ -2,7 +2,6 @@ package org.thoughtcrime.securesms.home import android.content.ContentResolver import android.content.Context -import android.util.Log import androidx.lifecycle.ViewModel import androidx.lifecycle.asFlow import androidx.lifecycle.viewModelScope @@ -22,9 +21,9 @@ import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.mapLatest import kotlinx.coroutines.flow.merge -import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onStart import kotlinx.coroutines.flow.stateIn +import kotlinx.coroutines.withContext import org.session.libsession.utilities.TextSecurePreferences import org.thoughtcrime.securesms.ApplicationContext import org.thoughtcrime.securesms.database.DatabaseContentProviders @@ -60,12 +59,10 @@ class HomeViewModel @Inject constructor( observeTypingStatus(), messageRequests(), ::Data - ) - .stateIn(viewModelScope, SharingStarted.Eagerly, null) + ).stateIn(viewModelScope, SharingStarted.Eagerly, null) private fun hasHiddenMessageRequests() = TextSecurePreferences.events .filter { it == TextSecurePreferences.HAS_HIDDEN_MESSAGE_REQUESTS } - .flowOn(Dispatchers.IO) .map { prefs.hasHiddenMessageRequests() } .onStart { emit(prefs.hasHiddenMessageRequests()) } @@ -81,7 +78,7 @@ class HomeViewModel @Inject constructor( hasHiddenMessageRequests(), latestUnapprovedConversationTimestamp(), ::createMessageRequests - ) + ).flowOn(Dispatchers.IO) private fun unapprovedConversationCount() = reloadTriggersAndContentChanges() .map { threadDb.unapprovedConversationCount } @@ -96,13 +93,13 @@ class HomeViewModel @Inject constructor( threadDb.readerFor(openCursor).run { generateSequence { next }.toList() } } } + .flowOn(Dispatchers.IO) @OptIn(FlowPreview::class) private fun reloadTriggersAndContentChanges() = merge( manualReloadTrigger, contentResolver.observeChanges(DatabaseContentProviders.ConversationList.CONTENT_URI) ) - .flowOn(Dispatchers.IO) .debounce(CHANGE_NOTIFICATION_DEBOUNCE_MILLS) .onStart { emit(Unit) } @@ -114,7 +111,7 @@ class HomeViewModel @Inject constructor( val messageRequests: MessageRequests? = null ) - fun createMessageRequests( + private fun createMessageRequests( count: Int, hidden: Boolean, timestamp: Long From 6e24df0547cb6dabe560939bb7fed8b22ce2e988 Mon Sep 17 00:00:00 2001 From: fanchao Date: Tue, 4 Jun 2024 10:46:02 +1000 Subject: [PATCH 17/23] Import --- .../main/java/org/thoughtcrime/securesms/home/HomeViewModel.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/home/HomeViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/home/HomeViewModel.kt index 6819054b31..dd6d24cd00 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/home/HomeViewModel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/home/HomeViewModel.kt @@ -23,7 +23,6 @@ import kotlinx.coroutines.flow.mapLatest import kotlinx.coroutines.flow.merge import kotlinx.coroutines.flow.onStart import kotlinx.coroutines.flow.stateIn -import kotlinx.coroutines.withContext import org.session.libsession.utilities.TextSecurePreferences import org.thoughtcrime.securesms.ApplicationContext import org.thoughtcrime.securesms.database.DatabaseContentProviders From 54bb84541a008781db9010b1a69f24431b3a5a3e Mon Sep 17 00:00:00 2001 From: fanchao Date: Thu, 6 Jun 2024 13:59:30 +1000 Subject: [PATCH 18/23] 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 19/23] 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 20/23] 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 From 0547dde554774741dd612b5eb7301d1fc69cc062 Mon Sep 17 00:00:00 2001 From: fanchao Date: Mon, 24 Jun 2024 14:05:06 +1000 Subject: [PATCH 21/23] Remove the use of executor in ThreadUtils --- .../org/thoughtcrime/securesms/AppContext.kt | 4 ++- .../libsignal/utilities/ThreadUtils.kt | 29 +++---------------- 2 files changed, 7 insertions(+), 26 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/AppContext.kt b/app/src/main/java/org/thoughtcrime/securesms/AppContext.kt index 2588618b72..b9183939cf 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/AppContext.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/AppContext.kt @@ -1,5 +1,7 @@ package org.thoughtcrime.securesms +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.asExecutor import nl.komponents.kovenant.Kovenant import nl.komponents.kovenant.jvm.asDispatcher import org.session.libsignal.utilities.Log @@ -11,7 +13,7 @@ object AppContext { fun configureKovenant() { Kovenant.context { callbackContext.dispatcher = Executors.newSingleThreadExecutor().asDispatcher() - workerContext.dispatcher = ThreadUtils.executorPool.asDispatcher() + workerContext.dispatcher = Dispatchers.IO.asExecutor().asDispatcher() multipleCompletion = { v1, v2 -> Log.d("Loki", "Promise resolved more than once (first with $v1, then with $v2); ignoring $v2.") } diff --git a/libsignal/src/main/java/org/session/libsignal/utilities/ThreadUtils.kt b/libsignal/src/main/java/org/session/libsignal/utilities/ThreadUtils.kt index e920d85b47..6485babe80 100644 --- a/libsignal/src/main/java/org/session/libsignal/utilities/ThreadUtils.kt +++ b/libsignal/src/main/java/org/session/libsignal/utilities/ThreadUtils.kt @@ -1,11 +1,13 @@ package org.session.libsignal.utilities import android.os.Process +import kotlinx.coroutines.Dispatchers import java.util.concurrent.ExecutorService import java.util.concurrent.LinkedBlockingQueue import java.util.concurrent.SynchronousQueue import java.util.concurrent.ThreadPoolExecutor import java.util.concurrent.TimeUnit +import kotlin.coroutines.EmptyCoroutineContext object ThreadUtils { @@ -13,39 +15,16 @@ object ThreadUtils { const val PRIORITY_IMPORTANT_BACKGROUND_THREAD = Process.THREAD_PRIORITY_DEFAULT + Process.THREAD_PRIORITY_LESS_FAVORABLE - // Paraphrased from: https://www.baeldung.com/kotlin/create-thread-pool - // "A cached thread pool such as one created via: - // `val executorPool: ExecutorService = Executors.newCachedThreadPool()` - // will utilize resources according to the requirements of submitted tasks. It will try to reuse - // existing threads for submitted tasks but will create as many threads as it needs if new tasks - // keep pouring in (with a memory usage of at least 1MB per created thread). These threads will - // live for up to 60 seconds of idle time before terminating by default. As such, it presents a - // very sharp tool that doesn't include any backpressure mechanism - and a sudden peak in load - // can bring the system down with an OutOfMemory error. We can achieve a similar effect but with - // better control by creating a ThreadPoolExecutor manually." - - private val corePoolSize = Runtime.getRuntime().availableProcessors() // Default thread pool size is our CPU core count - private val maxPoolSize = corePoolSize * 4 // Allow a maximum pool size of up to 4 threads per core - private val keepAliveTimeSecs = 100L // How long to keep idle threads in the pool before they are terminated - private val workQueue = SynchronousQueue() - val executorPool: ExecutorService = ThreadPoolExecutor(corePoolSize, maxPoolSize, keepAliveTimeSecs, TimeUnit.SECONDS, workQueue) - // Note: To see how many threads are running in our app at any given time we can use: // val threadCount = getAllStackTraces().size @JvmStatic fun queue(target: Runnable) { - executorPool.execute { - try { - target.run() - } catch (e: Exception) { - Log.e(TAG, e) - } - } + queue(target::run) } fun queue(target: () -> Unit) { - executorPool.execute { + Dispatchers.IO.dispatch(EmptyCoroutineContext) { try { target() } catch (e: Exception) { From 031a18061dd273b988db15ab63deeecd6c6ce022 Mon Sep 17 00:00:00 2001 From: ThomasSession Date: Mon, 24 Jun 2024 17:26:36 +1000 Subject: [PATCH 22/23] Using trim and empty to capture semantic concept of nothing being in there --- .../securesms/conversation/v2/input_bar/InputBar.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/input_bar/InputBar.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/input_bar/InputBar.kt index f183cff13f..c036ed38b4 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/input_bar/InputBar.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/input_bar/InputBar.kt @@ -120,7 +120,7 @@ class InputBar : RelativeLayout, InputBarEditTextDelegate, QuoteViewDelegate, Li // region Updating override fun inputBarEditTextContentChanged(text: CharSequence) { - microphoneButton.isVisible = text.all { it.isWhitespace() } + microphoneButton.isVisible = text.trim().isEmpty() sendButton.isVisible = microphoneButton.isGone delegate?.inputBarEditTextContentChanged(text) } From d22cb1e2c250dae262c62fd9bc6df1a29d422d96 Mon Sep 17 00:00:00 2001 From: ThomasSession Date: Tue, 25 Jun 2024 09:25:46 +1000 Subject: [PATCH 23/23] Remove config checks (PR 1294) Refactor: remove checks for whether new config is enabled throughout config factory generation. First commit from PR 1294. --- .../securesms/dependencies/ConfigFactory.kt | 9 --- .../securesms/home/HomeActivity.kt | 4 +- .../util/ConfigurationMessageUtilities.kt | 57 ++----------------- .../loki/messenger/libsession_util/Config.kt | 6 -- .../messaging/jobs/ConfigurationSyncJob.kt | 8 +-- .../ReceivedMessageHandler.kt | 10 ++-- 6 files changed, 11 insertions(+), 83 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/dependencies/ConfigFactory.kt b/app/src/main/java/org/thoughtcrime/securesms/dependencies/ConfigFactory.kt index 8379e1a23b..505a7939a8 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/dependencies/ConfigFactory.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/dependencies/ConfigFactory.kt @@ -7,7 +7,6 @@ import network.loki.messenger.libsession_util.Contacts import network.loki.messenger.libsession_util.ConversationVolatileConfig import network.loki.messenger.libsession_util.UserGroupsConfig import network.loki.messenger.libsession_util.UserProfile -import org.session.libsession.snode.SnodeAPI import org.session.libsession.utilities.ConfigFactoryProtocol import org.session.libsession.utilities.ConfigFactoryUpdateListener import org.session.libsession.utilities.TextSecurePreferences @@ -72,7 +71,6 @@ class ConfigFactory( override val user: UserProfile? get() = synchronizedWithLog(userLock) { - if (!ConfigBase.isNewConfigEnabled(isConfigForcedOn, SnodeAPI.nowWithOffset)) return null if (_userConfig == null) { val (secretKey, publicKey) = maybeGetUserInfo() ?: return null val userDump = configDatabase.retrieveConfigAndHashes( @@ -92,7 +90,6 @@ class ConfigFactory( override val contacts: Contacts? get() = synchronizedWithLog(contactsLock) { - if (!ConfigBase.isNewConfigEnabled(isConfigForcedOn, SnodeAPI.nowWithOffset)) return null if (_contacts == null) { val (secretKey, publicKey) = maybeGetUserInfo() ?: return null val contactsDump = configDatabase.retrieveConfigAndHashes( @@ -112,7 +109,6 @@ class ConfigFactory( override val convoVolatile: ConversationVolatileConfig? get() = synchronizedWithLog(convoVolatileLock) { - if (!ConfigBase.isNewConfigEnabled(isConfigForcedOn, SnodeAPI.nowWithOffset)) return null if (_convoVolatileConfig == null) { val (secretKey, publicKey) = maybeGetUserInfo() ?: return null val convoDump = configDatabase.retrieveConfigAndHashes( @@ -133,7 +129,6 @@ class ConfigFactory( override val userGroups: UserGroupsConfig? get() = synchronizedWithLog(userGroupsLock) { - if (!ConfigBase.isNewConfigEnabled(isConfigForcedOn, SnodeAPI.nowWithOffset)) return null if (_userGroups == null) { val (secretKey, publicKey) = maybeGetUserInfo() ?: return null val userGroupsDump = configDatabase.retrieveConfigAndHashes( @@ -207,8 +202,6 @@ class ConfigFactory( openGroupId: String?, visibleOnly: Boolean ): Boolean { - if (!ConfigBase.isNewConfigEnabled(isConfigForcedOn, SnodeAPI.nowWithOffset)) return true - val (_, userPublicKey) = maybeGetUserInfo() ?: return true if (openGroupId != null) { @@ -241,8 +234,6 @@ class ConfigFactory( } override fun canPerformChange(variant: String, publicKey: String, changeTimestampMs: Long): Boolean { - if (!ConfigBase.isNewConfigEnabled(isConfigForcedOn, SnodeAPI.nowWithOffset)) return true - val lastUpdateTimestampMs = configDatabase.retrieveConfigLastUpdateTimestamp(variant, publicKey) // Ensure the change occurred after the last config message was handled (minus the buffer period) diff --git a/app/src/main/java/org/thoughtcrime/securesms/home/HomeActivity.kt b/app/src/main/java/org/thoughtcrime/securesms/home/HomeActivity.kt index c063f30538..4b0cf60c3c 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/home/HomeActivity.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/home/HomeActivity.kt @@ -27,7 +27,6 @@ import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import network.loki.messenger.R import network.loki.messenger.databinding.ActivityHomeBinding -import network.loki.messenger.libsession_util.ConfigBase import org.greenrobot.eventbus.EventBus import org.greenrobot.eventbus.Subscribe import org.greenrobot.eventbus.ThreadMode @@ -336,8 +335,7 @@ class HomeActivity : PassphraseRequiredActionBarActivity(), } private fun updateLegacyConfigView() { - binding.configOutdatedView.isVisible = ConfigBase.isNewConfigEnabled(textSecurePreferences.hasForcedNewConfig(), SnodeAPI.nowWithOffset) - && textSecurePreferences.getHasLegacyConfig() + binding.configOutdatedView.isVisible = textSecurePreferences.getHasLegacyConfig() } override fun onResume() { diff --git a/app/src/main/java/org/thoughtcrime/securesms/util/ConfigurationMessageUtilities.kt b/app/src/main/java/org/thoughtcrime/securesms/util/ConfigurationMessageUtilities.kt index 9d10cfdab5..e0f0492445 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/util/ConfigurationMessageUtilities.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/util/ConfigurationMessageUtilities.kt @@ -17,8 +17,6 @@ import org.session.libsession.messaging.jobs.ConfigurationSyncJob import org.session.libsession.messaging.jobs.JobQueue import org.session.libsession.messaging.messages.Destination import org.session.libsession.messaging.messages.control.ConfigurationMessage -import org.session.libsession.messaging.sending_receiving.MessageSender -import org.session.libsession.snode.SnodeAPI import org.session.libsession.utilities.Address import org.session.libsession.utilities.GroupUtil import org.session.libsession.utilities.TextSecurePreferences @@ -55,61 +53,16 @@ object ConfigurationMessageUtilities { fun syncConfigurationIfNeeded(context: Context) { // add if check here to schedule new config job process and return early val userPublicKey = TextSecurePreferences.getLocalNumber(context) ?: return - val forcedConfig = TextSecurePreferences.hasForcedNewConfig(context) - val currentTime = SnodeAPI.nowWithOffset - if (ConfigBase.isNewConfigEnabled(forcedConfig, currentTime)) { - scheduleConfigSync(userPublicKey) - return - } - val lastSyncTime = TextSecurePreferences.getLastConfigurationSyncTime(context) - val now = System.currentTimeMillis() - if (now - lastSyncTime < 7 * 24 * 60 * 60 * 1000) return - val contacts = ContactUtilities.getAllContacts(context).filter { recipient -> - !recipient.name.isNullOrEmpty() && !recipient.isLocalNumber && recipient.address.serialize().isNotEmpty() - }.map { recipient -> - ConfigurationMessage.Contact( - publicKey = recipient.address.serialize(), - name = recipient.name!!, - profilePicture = recipient.profileAvatar, - profileKey = recipient.profileKey, - isApproved = recipient.isApproved, - isBlocked = recipient.isBlocked, - didApproveMe = recipient.hasApprovedMe() - ) - } - val configurationMessage = ConfigurationMessage.getCurrent(contacts) ?: return - MessageSender.send(configurationMessage, Address.fromSerialized(userPublicKey)) - TextSecurePreferences.setLastConfigurationSyncTime(context, now) + scheduleConfigSync(userPublicKey) } fun forceSyncConfigurationNowIfNeeded(context: Context): Promise { // add if check here to schedule new config job process and return early val userPublicKey = TextSecurePreferences.getLocalNumber(context) ?: return Promise.ofFail(NullPointerException("User Public Key is null")) - val forcedConfig = TextSecurePreferences.hasForcedNewConfig(context) - val currentTime = SnodeAPI.nowWithOffset - if (ConfigBase.isNewConfigEnabled(forcedConfig, currentTime)) { - // schedule job if none exist - // don't schedule job if we already have one - scheduleConfigSync(userPublicKey) - return Promise.ofSuccess(Unit) - } - val contacts = ContactUtilities.getAllContacts(context).filter { recipient -> - !recipient.isGroupRecipient && !recipient.name.isNullOrEmpty() && !recipient.isLocalNumber && recipient.address.serialize().isNotEmpty() - }.map { recipient -> - ConfigurationMessage.Contact( - publicKey = recipient.address.serialize(), - name = recipient.name!!, - profilePicture = recipient.profileAvatar, - profileKey = recipient.profileKey, - isApproved = recipient.isApproved, - isBlocked = recipient.isBlocked, - didApproveMe = recipient.hasApprovedMe() - ) - } - val configurationMessage = ConfigurationMessage.getCurrent(contacts) ?: return Promise.ofSuccess(Unit) - val promise = MessageSender.send(configurationMessage, Destination.from(Address.fromSerialized(userPublicKey)), isSyncMessage = true) - TextSecurePreferences.setLastConfigurationSyncTime(context, System.currentTimeMillis()) - return promise + // schedule job if none exist + // don't schedule job if we already have one + scheduleConfigSync(userPublicKey) + return Promise.ofSuccess(Unit) } private fun maybeUserSecretKey() = MessagingModuleConfiguration.shared.getUserED25519KeyPair()?.secretKey?.asBytes diff --git a/libsession-util/src/main/java/network/loki/messenger/libsession_util/Config.kt b/libsession-util/src/main/java/network/loki/messenger/libsession_util/Config.kt index befd0d6d43..03af4a3968 100644 --- a/libsession-util/src/main/java/network/loki/messenger/libsession_util/Config.kt +++ b/libsession-util/src/main/java/network/loki/messenger/libsession_util/Config.kt @@ -27,12 +27,6 @@ sealed class ConfigBase(protected val /* yucky */ pointer: Long) { is UserGroupsConfig -> Kind.GROUPS } - // TODO: time in future to activate (hardcoded to 1st jan 2024 for testing, change before release) - private const val ACTIVATE_TIME = 1690761600000 - - fun isNewConfigEnabled(forced: Boolean, currentTime: Long) = - forced || currentTime >= ACTIVATE_TIME - const val PRIORITY_HIDDEN = -1 const val PRIORITY_VISIBLE = 0 const val PRIORITY_PINNED = 1 diff --git a/libsession/src/main/java/org/session/libsession/messaging/jobs/ConfigurationSyncJob.kt b/libsession/src/main/java/org/session/libsession/messaging/jobs/ConfigurationSyncJob.kt index ec8de44163..241853df55 100644 --- a/libsession/src/main/java/org/session/libsession/messaging/jobs/ConfigurationSyncJob.kt +++ b/libsession/src/main/java/org/session/libsession/messaging/jobs/ConfigurationSyncJob.kt @@ -1,6 +1,5 @@ package org.session.libsession.messaging.jobs -import network.loki.messenger.libsession_util.ConfigBase import network.loki.messenger.libsession_util.ConfigBase.Companion.protoKindFor import nl.komponents.kovenant.functional.bind import org.session.libsession.messaging.MessagingModuleConfiguration @@ -10,7 +9,6 @@ import org.session.libsession.messaging.sending_receiving.MessageSender import org.session.libsession.messaging.utilities.Data import org.session.libsession.snode.RawResponse import org.session.libsession.snode.SnodeAPI -import org.session.libsession.utilities.TextSecurePreferences import org.session.libsignal.utilities.Log import java.util.concurrent.atomic.AtomicBoolean @@ -26,14 +24,10 @@ data class ConfigurationSyncJob(val destination: Destination): Job { override suspend fun execute(dispatcherName: String) { val storage = MessagingModuleConfiguration.shared.storage - val forcedConfig = TextSecurePreferences.hasForcedNewConfig(MessagingModuleConfiguration.shared.context) - val currentTime = SnodeAPI.nowWithOffset val userEdKeyPair = MessagingModuleConfiguration.shared.getUserED25519KeyPair() val userPublicKey = storage.getUserPublicKey() val delegate = delegate - if (destination is Destination.ClosedGroup // TODO: closed group configs will be handled in closed group feature - // if we haven't enabled the new configs don't run - || !ConfigBase.isNewConfigEnabled(forcedConfig, currentTime) + if (destination is Destination.ClosedGroup // if we don't have a user ed key pair for signing updates || userEdKeyPair == null // this will be useful to not handle null delegate cases 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 6450d927a3..dfd40f5025 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 @@ -203,12 +203,10 @@ private fun handleConfigurationMessage(message: ConfigurationMessage) { TextSecurePreferences.setConfigurationMessageSynced(context, true) TextSecurePreferences.setLastProfileUpdateTime(context, message.sentTimestamp!!) - val isForceSync = TextSecurePreferences.hasForcedNewConfig(context) - val currentTime = SnodeAPI.nowWithOffset - if (ConfigBase.isNewConfigEnabled(isForceSync, currentTime)) { - TextSecurePreferences.setHasLegacyConfig(context, true) - if (!firstTimeSync) return - } + + TextSecurePreferences.setHasLegacyConfig(context, true) + if (!firstTimeSync) return + val allClosedGroupPublicKeys = storage.getAllClosedGroupPublicKeys() for (closedGroup in message.closedGroups) { if (allClosedGroupPublicKeys.contains(closedGroup.publicKey)) {