From b44ea31d8e3205e50a798b118e4a5ef5c3774e07 Mon Sep 17 00:00:00 2001 From: SessionHero01 <180888785+SessionHero01@users.noreply.github.com> Date: Thu, 27 Feb 2025 15:38:21 +1100 Subject: [PATCH] [SES-3434] - Fix receiving left groups' notification (#986) * Fix still receiving generic notification for groups * Imports * Fixes tests * Fixes compile issues --- .../securesms/ApplicationContext.java | 32 +--------- .../securesms/debugmenu/DebugMenuViewModel.kt | 34 +++++----- .../securesms/notifications/PushReceiver.kt | 28 ++++++--- .../MessageNotificationsViewModel.kt | 13 ++-- .../preferences/ClearAllDataDialog.kt | 30 +++++---- .../securesms/util/ClearDataUtils.kt | 63 +++++++++++++++++++ .../notifications/FirebaseTokenFetcher.kt | 3 - .../v2/ConversationViewModelTest.kt | 1 + 8 files changed, 125 insertions(+), 79 deletions(-) create mode 100644 app/src/main/java/org/thoughtcrime/securesms/util/ClearDataUtils.kt diff --git a/app/src/main/java/org/thoughtcrime/securesms/ApplicationContext.java b/app/src/main/java/org/thoughtcrime/securesms/ApplicationContext.java index 270b30fe43..3bcf748788 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/ApplicationContext.java +++ b/app/src/main/java/org/thoughtcrime/securesms/ApplicationContext.java @@ -40,6 +40,7 @@ import androidx.lifecycle.LifecycleOwner; import androidx.lifecycle.ProcessLifecycleOwner; import androidx.work.Configuration; +import com.google.firebase.messaging.FirebaseMessaging; import com.squareup.phrase.Phrase; import org.conscrypt.Conscrypt; @@ -499,37 +500,6 @@ public class ApplicationContext extends Application implements DefaultLifecycleO } }); } - - // Method to clear the local data - returns true on success otherwise false - @SuppressLint("ApplySharedPref") - public boolean clearAllData() { - TextSecurePreferences.clearAll(this); - getSharedPreferences(PREFERENCES_NAME, 0).edit().clear().commit(); - if (!deleteDatabase(SQLCipherOpenHelper.DATABASE_NAME)) { - Log.d("Loki", "Failed to delete database."); - return false; - } - configFactory.clearAll(); - return true; - } - - /** - * Clear all local profile data and message history then restart the app after a brief delay. - * @return true on success, false otherwise. - */ - @SuppressLint("ApplySharedPref") - public boolean clearAllDataAndRestart() { - clearAllData(); - Util.runOnMain(() -> new Handler().postDelayed(ApplicationContext.this::restartApplication, 200)); - return true; - } - - public void restartApplication() { - Intent intent = new Intent(this, HomeActivity.class); - startActivity(Intent.makeRestartActivityTask(intent.getComponent())); - Runtime.getRuntime().exit(0); - } - // endregion // Method to wake up the screen and dismiss the keyguard diff --git a/app/src/main/java/org/thoughtcrime/securesms/debugmenu/DebugMenuViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/debugmenu/DebugMenuViewModel.kt index 68020da575..0f5484dd74 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/debugmenu/DebugMenuViewModel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/debugmenu/DebugMenuViewModel.kt @@ -16,6 +16,7 @@ import org.session.libsignal.utilities.Log import org.thoughtcrime.securesms.ApplicationContext import org.thoughtcrime.securesms.dependencies.ConfigFactory import org.session.libsession.messaging.groups.LegacyGroupDeprecationManager +import org.thoughtcrime.securesms.util.ClearDataUtils import java.time.ZonedDateTime import javax.inject.Inject @@ -25,6 +26,7 @@ class DebugMenuViewModel @Inject constructor( private val textSecurePreferences: TextSecurePreferences, private val configFactory: ConfigFactory, private val deprecationManager: LegacyGroupDeprecationManager, + private val clearDataUtils: ClearDataUtils ) : ViewModel() { private val TAG = "DebugMenu" @@ -86,7 +88,7 @@ class DebugMenuViewModel @Inject constructor( // restart app viewModelScope.launch { delay(500) // giving time to save data - ApplicationContext.getInstance(application).restartApplication() + clearDataUtils.restartApplication() } } @@ -128,21 +130,21 @@ class DebugMenuViewModel @Inject constructor( // clear remote and local data, then restart the app viewModelScope.launch { - ApplicationContext.getInstance(application).clearAllData().let { success -> - if(success){ - // save the environment - textSecurePreferences.setEnvironment(env) - delay(500) - ApplicationContext.getInstance(application).restartApplication() - } else { - _uiState.value = _uiState.value.copy( - showEnvironmentWarningDialog = false, - showLoadingDialog = false - ) - Log.e(TAG, "Failed to force sync when deleting data") - _uiState.value = _uiState.value.copy(snackMessage = "Sorry, something went wrong...") - return@launch - } + val success = runCatching { clearDataUtils.clearAllData() } .isSuccess + + if(success){ + // save the environment + textSecurePreferences.setEnvironment(env) + delay(500) + clearDataUtils.restartApplication() + } else { + _uiState.value = _uiState.value.copy( + showEnvironmentWarningDialog = false, + showLoadingDialog = false + ) + Log.e(TAG, "Failed to force sync when deleting data") + _uiState.value = _uiState.value.copy(snackMessage = "Sorry, something went wrong...") + return@launch } } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/notifications/PushReceiver.kt b/app/src/main/java/org/thoughtcrime/securesms/notifications/PushReceiver.kt index ccccb2cfe3..b8474b05b8 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/notifications/PushReceiver.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/notifications/PushReceiver.kt @@ -26,6 +26,7 @@ import org.session.libsession.utilities.ConfigMessage import org.session.libsession.utilities.bencode.Bencode import org.session.libsession.utilities.bencode.BencodeList import org.session.libsession.utilities.bencode.BencodeString +import org.session.libsession.utilities.getGroup import org.session.libsignal.protos.SignalServiceProtos.Envelope import org.session.libsignal.utilities.AccountId import org.session.libsignal.utilities.Base64 @@ -62,14 +63,8 @@ class PushReceiver @Inject constructor( } private fun addMessageReceiveJob(pushData: PushData?) { - // send a generic notification if we have no data - if (pushData?.data == null) { - sendGenericNotification() - return - } - try { - val namespace = pushData.metadata?.namespace + val namespace = pushData?.metadata?.namespace val params = when { namespace == Namespace.GROUP_MESSAGES() || namespace == Namespace.REVOKED_GROUP_MESSAGES() || @@ -80,6 +75,17 @@ class PushReceiver @Inject constructor( "Received a closed group message push notification without an account ID" }) + if (configFactory.getGroup(groupId)?.shouldPoll != true) { + Log.d(TAG, "Received a push notification for a group that isn't active") + return + } + + // send a generic notification if we have no data + if (pushData.data == null) { + sendGenericNotification() + return + } + if (namespace == Namespace.GROUP_MESSAGES()) { val envelope = checkNotNull(tryDecryptGroupEnvelope(groupId, pushData.data)) { "Unable to decrypt closed group message" @@ -125,7 +131,13 @@ class PushReceiver @Inject constructor( } } - namespace == Namespace.DEFAULT() || pushData.metadata == null -> { + namespace == Namespace.DEFAULT() || pushData?.metadata == null -> { + // send a generic notification if we have no data + if (pushData?.data == null) { + sendGenericNotification() + return + } + val envelopeAsData = MessageWrapper.unwrap(pushData.data).toByteArray() MessageReceiveParameters( data = envelopeAsData, diff --git a/app/src/main/java/org/thoughtcrime/securesms/onboarding/messagenotifications/MessageNotificationsViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/onboarding/messagenotifications/MessageNotificationsViewModel.kt index 0508e97946..8ea92dffc1 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/onboarding/messagenotifications/MessageNotificationsViewModel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/onboarding/messagenotifications/MessageNotificationsViewModel.kt @@ -16,14 +16,15 @@ import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch import network.loki.messenger.R import org.session.libsession.utilities.TextSecurePreferences -import org.thoughtcrime.securesms.ApplicationContext import org.thoughtcrime.securesms.onboarding.manager.CreateAccountManager +import org.thoughtcrime.securesms.util.ClearDataUtils internal class MessageNotificationsViewModel( private val state: State, private val application: Application, private val prefs: TextSecurePreferences, - private val createAccountManager: CreateAccountManager + private val createAccountManager: CreateAccountManager, + private val clearDataUtils: ClearDataUtils, ): AndroidViewModel(application) { private val _uiStates = MutableStateFlow(UiState()) val uiStates = _uiStates.asStateFlow() @@ -71,8 +72,8 @@ internal class MessageNotificationsViewModel( fun quit() { _uiStates.update { it.copy(clearData = true) } - viewModelScope.launch(Dispatchers.IO) { - ApplicationContext.getInstance(application).clearAllDataAndRestart() + viewModelScope.launch { + clearDataUtils.clearAllDataAndRestart() } } @@ -100,6 +101,7 @@ internal class MessageNotificationsViewModel( private val application: Application, private val prefs: TextSecurePreferences, private val createAccountManager: CreateAccountManager, + private val clearDataUtils: ClearDataUtils, ) : ViewModelProvider.Factory { override fun create(modelClass: Class): T { @@ -107,7 +109,8 @@ internal class MessageNotificationsViewModel( state = profileName?.let(State::CreateAccount) ?: State.LoadAccount, application = application, prefs = prefs, - createAccountManager = createAccountManager + createAccountManager = createAccountManager, + clearDataUtils = clearDataUtils, ) as T } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/preferences/ClearAllDataDialog.kt b/app/src/main/java/org/thoughtcrime/securesms/preferences/ClearAllDataDialog.kt index c2546ea306..743b60109a 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/preferences/ClearAllDataDialog.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/preferences/ClearAllDataDialog.kt @@ -25,6 +25,7 @@ import org.session.libsignal.utilities.Log import org.thoughtcrime.securesms.ApplicationContext import org.thoughtcrime.securesms.createSessionDialog import org.thoughtcrime.securesms.dependencies.DatabaseComponent +import org.thoughtcrime.securesms.util.ClearDataUtils import org.thoughtcrime.securesms.util.ConfigurationMessageUtilities import javax.inject.Inject @@ -37,6 +38,9 @@ class ClearAllDataDialog : DialogFragment() { @Inject lateinit var storage: StorageProtocol + @Inject + lateinit var clearDataUtils: ClearDataUtils + private enum class Steps { INFO_PROMPT, NETWORK_PROMPT, @@ -123,13 +127,15 @@ class ClearAllDataDialog : DialogFragment() { } private suspend fun performDeleteLocalDataOnlyStep() { - ApplicationContext.getInstance(context).clearAllDataAndRestart().let { success -> - withContext(Main) { - if (success) { - dismissAllowingStateLoss() - } else { - Toast.makeText(ApplicationContext.getInstance(requireContext()), R.string.errorUnknown, Toast.LENGTH_LONG).show() - } + val result = runCatching { + clearDataUtils.clearAllDataAndRestart() + } + + withContext(Main) { + if (result.isSuccess) { + dismissAllowingStateLoss() + } else { + Toast.makeText(ApplicationContext.getInstance(requireContext()), R.string.errorUnknown, Toast.LENGTH_LONG).show() } } } @@ -160,15 +166,7 @@ class ClearAllDataDialog : DialogFragment() { } else if (deletionResultMap.values.all { it }) { // ..otherwise if the network data deletion was successful proceed to delete the local data as well. - ApplicationContext.getInstance(context).clearAllDataAndRestart().let { success -> - withContext(Main) { - if (success) { - dismissAllowingStateLoss() - } else { - Toast.makeText(ApplicationContext.getInstance(requireContext()), R.string.errorUnknown, Toast.LENGTH_LONG).show() - } - } - } + performDeleteLocalDataOnlyStep() } } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/util/ClearDataUtils.kt b/app/src/main/java/org/thoughtcrime/securesms/util/ClearDataUtils.kt new file mode 100644 index 0000000000..fb582a1089 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/util/ClearDataUtils.kt @@ -0,0 +1,63 @@ +package org.thoughtcrime.securesms.util + +import android.annotation.SuppressLint +import android.app.Application +import android.content.Intent +import com.google.firebase.messaging.FirebaseMessaging +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.delay +import kotlinx.coroutines.withContext +import org.session.libsession.utilities.TextSecurePreferences +import org.session.libsignal.utilities.Log +import org.thoughtcrime.securesms.ApplicationContext +import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper +import org.thoughtcrime.securesms.dependencies.ConfigFactory +import org.thoughtcrime.securesms.home.HomeActivity +import javax.inject.Inject +import androidx.core.content.edit +import kotlinx.coroutines.tasks.await + +class ClearDataUtils @Inject constructor( + private val application: Application, + private val configFactory: ConfigFactory, +) { + // Method to clear the local data - returns true on success otherwise false + @SuppressLint("ApplySharedPref") + suspend fun clearAllData() { + return withContext(Dispatchers.Default) { + // Should not proceed if we can't delete db + check(application.deleteDatabase(SQLCipherOpenHelper.DATABASE_NAME)) { + "Failed to delete database" + } + + TextSecurePreferences.clearAll(application) + application.getSharedPreferences(ApplicationContext.PREFERENCES_NAME, 0).edit(commit = true) { clear() } + configFactory.clearAll() + + // The token deletion is nice but not critical, so don't let it block the rest of the process + runCatching { + FirebaseMessaging.getInstance().deleteToken().await() + }.onFailure { e -> + Log.w("ClearDataUtils", "Failed to delete Firebase token: ${e.message}", e) + } + } + } + + /** + * Clear all local profile data and message history then restart the app after a brief delay. + * @return true on success, false otherwise. + */ + @SuppressLint("ApplySharedPref") + suspend fun clearAllDataAndRestart() { + clearAllData() + delay(200) + restartApplication() + } + + fun restartApplication() { + val intent = Intent(application, HomeActivity::class.java) + application.startActivity(Intent.makeRestartActivityTask(intent.component)) + Runtime.getRuntime().exit(0) + } + +} \ No newline at end of file diff --git a/app/src/play/kotlin/org/thoughtcrime/securesms/notifications/FirebaseTokenFetcher.kt b/app/src/play/kotlin/org/thoughtcrime/securesms/notifications/FirebaseTokenFetcher.kt index d324015609..2c4746bc2a 100644 --- a/app/src/play/kotlin/org/thoughtcrime/securesms/notifications/FirebaseTokenFetcher.kt +++ b/app/src/play/kotlin/org/thoughtcrime/securesms/notifications/FirebaseTokenFetcher.kt @@ -2,9 +2,6 @@ package org.thoughtcrime.securesms.notifications import com.google.firebase.messaging.FirebaseMessaging import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.StateFlow - -import kotlinx.coroutines.tasks.await import org.session.libsession.messaging.notifications.TokenFetcher import javax.inject.Inject import javax.inject.Singleton diff --git a/app/src/test/java/org/thoughtcrime/securesms/conversation/v2/ConversationViewModelTest.kt b/app/src/test/java/org/thoughtcrime/securesms/conversation/v2/ConversationViewModelTest.kt index a43d49d130..2c705d0616 100644 --- a/app/src/test/java/org/thoughtcrime/securesms/conversation/v2/ConversationViewModelTest.kt +++ b/app/src/test/java/org/thoughtcrime/securesms/conversation/v2/ConversationViewModelTest.kt @@ -49,6 +49,7 @@ class ConversationViewModelTest: BaseViewModelTest() { configFactory = mock(), groupManagerV2 = mock(), legacyGroupDeprecationManager = mock(), + expiredGroupManager = mock() ) }