From 947f741d7540cb22f59093ec34bb2398b5dfdb12 Mon Sep 17 00:00:00 2001 From: sim Date: Sat, 11 Nov 2023 16:32:08 +0100 Subject: [PATCH 1/4] Display MayHaveMessagesNotification on push with locked db --- .../java/org/thoughtcrime/securesms/gcm/FcmFetchManager.kt | 5 +++-- .../org/thoughtcrime/securesms/gcm/FcmReceiveService.java | 4 ++++ .../securesms/notifications/NotificationChannels.java | 2 +- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/gcm/FcmFetchManager.kt b/app/src/main/java/org/thoughtcrime/securesms/gcm/FcmFetchManager.kt index 58a57aee13..c260afcbd3 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/gcm/FcmFetchManager.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/gcm/FcmFetchManager.kt @@ -70,12 +70,13 @@ object FcmFetchManager { } } - private fun postMayHaveMessagesNotification(context: Context) { + @JvmStatic + fun postMayHaveMessagesNotification(context: Context) { if (FeatureFlags.fcmMayHaveMessagesNotificationKillSwitch()) { Log.w(TAG, "May have messages notification kill switch") return } - val mayHaveMessagesNotification: Notification = NotificationCompat.Builder(context, NotificationChannels.getInstance().ADDITIONAL_MESSAGE_NOTIFICATIONS) + val mayHaveMessagesNotification: Notification = NotificationCompat.Builder(context, NotificationChannels.ADDITIONAL_MESSAGE_NOTIFICATIONS) .setSmallIcon(R.drawable.ic_notification) .setContentTitle(context.getString(R.string.FcmFetchManager__you_may_have_messages)) .setCategory(NotificationCompat.CATEGORY_MESSAGE) diff --git a/app/src/main/java/org/thoughtcrime/securesms/gcm/FcmReceiveService.java b/app/src/main/java/org/thoughtcrime/securesms/gcm/FcmReceiveService.java index de58878c3d..09a2e2ec3c 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/gcm/FcmReceiveService.java +++ b/app/src/main/java/org/thoughtcrime/securesms/gcm/FcmReceiveService.java @@ -28,6 +28,10 @@ public class FcmReceiveService extends FirebaseMessagingService { @Override public void onMessageReceived(RemoteMessage remoteMessage) { if (KeyCachingService.isLocked()) { + if (remoteMessage != null && remoteMessage.getPriority() == RemoteMessage.PRIORITY_HIGH) { + Log.d(TAG, "New urgent message received while the db is locked"); + FcmFetchManager.postMayHaveMessagesNotification(this); + } return; } diff --git a/app/src/main/java/org/thoughtcrime/securesms/notifications/NotificationChannels.java b/app/src/main/java/org/thoughtcrime/securesms/notifications/NotificationChannels.java index 5b2fdad258..1caa2b58ac 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/notifications/NotificationChannels.java +++ b/app/src/main/java/org/thoughtcrime/securesms/notifications/NotificationChannels.java @@ -80,7 +80,7 @@ private static class Version { public final String BACKGROUND = "background_connection"; public final String CALL_STATUS = "call_status"; public final String APP_ALERTS = "app_alerts"; - public final String ADDITIONAL_MESSAGE_NOTIFICATIONS = "additional_message_notifications"; + public static String ADDITIONAL_MESSAGE_NOTIFICATIONS = "additional_message_notifications"; private static volatile NotificationChannels instance; From 8268817216a8048c98b25931171f6c67c90b623e Mon Sep 17 00:00:00 2001 From: Oscar Mira Date: Thu, 16 Nov 2023 09:03:26 +0100 Subject: [PATCH 2/4] Fix MayHaveMessagesNotification color --- .../main/java/org/thoughtcrime/securesms/gcm/FcmFetchManager.kt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/src/main/java/org/thoughtcrime/securesms/gcm/FcmFetchManager.kt b/app/src/main/java/org/thoughtcrime/securesms/gcm/FcmFetchManager.kt index c260afcbd3..cb5312724c 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/gcm/FcmFetchManager.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/gcm/FcmFetchManager.kt @@ -7,6 +7,7 @@ import android.content.Intent import android.os.Build import androidx.core.app.NotificationCompat import androidx.core.app.NotificationManagerCompat +import androidx.core.content.ContextCompat import org.signal.core.util.PendingIntentFlags.mutable import org.signal.core.util.concurrent.SignalExecutors import org.signal.core.util.logging.Log @@ -78,6 +79,7 @@ object FcmFetchManager { } val mayHaveMessagesNotification: Notification = NotificationCompat.Builder(context, NotificationChannels.ADDITIONAL_MESSAGE_NOTIFICATIONS) .setSmallIcon(R.drawable.ic_notification) + .setColor(ContextCompat.getColor(context, R.color.core_ultramarine)) .setContentTitle(context.getString(R.string.FcmFetchManager__you_may_have_messages)) .setCategory(NotificationCompat.CATEGORY_MESSAGE) .setContentIntent(PendingIntent.getActivity(context, 0, MainActivity.clearTop(context), mutable())) From bb6644505bf6c91acf90b66c33436a93658f83ec Mon Sep 17 00:00:00 2001 From: Oscar Mira Date: Wed, 15 Nov 2023 18:35:40 +0100 Subject: [PATCH 3/4] Add switch preference for notifications while locked --- .../NotificationsSettingsFragment.kt | 20 +++++++++++++++++++ .../NotificationsSettingsState.kt | 2 ++ .../NotificationsSettingsViewModel.kt | 12 ++++++++++- .../securesms/gcm/FcmFetchManager.kt | 9 ++++++--- .../securesms/gcm/FcmReceiveService.java | 10 ++++++---- .../securesms/keyvalue/AccountValues.kt | 4 ++++ .../securesms/util/FeatureFlags.java | 7 ------- .../util/SecurePreferenceManager.java | 1 + .../securesms/util/TextSecurePreferences.java | 19 +++++++++++++----- app/src/main/res/values/strings2.xml | 4 ++++ .../messaging/FirebaseMessagingService.java | 4 ++-- 11 files changed, 70 insertions(+), 22 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/notifications/NotificationsSettingsFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/notifications/NotificationsSettingsFragment.kt index f648cfa7fb..6fd7be9775 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/notifications/NotificationsSettingsFragment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/notifications/NotificationsSettingsFragment.kt @@ -18,6 +18,7 @@ import androidx.annotation.RequiresApi import androidx.core.content.ContextCompat import androidx.lifecycle.ViewModelProvider import androidx.navigation.fragment.findNavController +import com.google.android.material.dialog.MaterialAlertDialogBuilder import org.signal.core.util.getParcelableExtraCompat import org.signal.core.util.logging.Log import org.thoughtcrime.securesms.R @@ -276,6 +277,25 @@ class NotificationsSettingsFragment : DSLSettingsFragment(R.string.preferences__ sectionHeaderPref(R.string.NotificationsSettingsFragment__notify_when) + switchPref( + title = DSLSettingsText.from(R.string.NotificationsSettingsFragment__new_activity_while_locked), + summary = DSLSettingsText.from(R.string.NotificationsSettingsFragment__receive_notifications_for_messages_or_missed_calls_when_the_app_is_locked), + isChecked = state.notifyWhileLocked, + onToggle = { isChecked -> + if (isChecked && !state.canEnableNotifyWhileLocked) { + MaterialAlertDialogBuilder(requireContext()) + .setTitle(R.string.NotificationsSettingsFragment__push_notifications_unavailable) + .setMessage(R.string.NotificationsSettingsFragment__sorry_this_feature_requires_push_notifications_delivered_via_fcm_or_unifiedpush) + .setPositiveButton(android.R.string.ok, null) + .show() + false + } else { + viewModel.setNotifyWhileLocked(isChecked) + true + } + } + ) + switchPref( title = DSLSettingsText.from(R.string.NotificationsSettingsFragment__contact_joins_signal), isChecked = state.notifyWhenContactJoinsSignal, diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/notifications/NotificationsSettingsState.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/notifications/NotificationsSettingsState.kt index 97b6595cd1..f4a18d7d9a 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/notifications/NotificationsSettingsState.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/notifications/NotificationsSettingsState.kt @@ -5,6 +5,8 @@ import android.net.Uri data class NotificationsSettingsState( val messageNotificationsState: MessageNotificationsState, val callNotificationsState: CallNotificationsState, + val notifyWhileLocked: Boolean, + val canEnableNotifyWhileLocked: Boolean, val notifyWhenContactJoinsSignal: Boolean ) diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/notifications/NotificationsSettingsViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/notifications/NotificationsSettingsViewModel.kt index caf8d925cf..2840341443 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/notifications/NotificationsSettingsViewModel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/notifications/NotificationsSettingsViewModel.kt @@ -1,5 +1,6 @@ package org.thoughtcrime.securesms.components.settings.app.notifications +import android.app.Application import android.content.SharedPreferences import android.net.Uri import android.os.Build @@ -17,6 +18,8 @@ class NotificationsSettingsViewModel(private val sharedPreferences: SharedPrefer private val store = Store(getState()) + private val application: Application = ApplicationDependencies.getApplication() + val state: LiveData = store.stateLiveData init { @@ -96,6 +99,11 @@ class NotificationsSettingsViewModel(private val sharedPreferences: SharedPrefer refresh() } + fun setNotifyWhileLocked(enabled: Boolean) { + TextSecurePreferences.setPassphraseLockNotificationsEnabled(application, enabled) + refresh() + } + fun setNotifyWhenContactJoinsSignal(enabled: Boolean) { SignalStore.settings().isNotifyWhenContactJoinsSignal = enabled refresh() @@ -117,7 +125,7 @@ class NotificationsSettingsViewModel(private val sharedPreferences: SharedPrefer inChatSoundsEnabled = SignalStore.settings().isMessageNotificationsInChatSoundsEnabled, repeatAlerts = SignalStore.settings().messageNotificationsRepeatAlerts, messagePrivacy = SignalStore.settings().messageNotificationsPrivacy.toString(), - priority = TextSecurePreferences.getNotificationPriority(ApplicationDependencies.getApplication()), + priority = TextSecurePreferences.getNotificationPriority(application), ), callNotificationsState = CallNotificationsState( notificationsEnabled = SignalStore.settings().isCallNotificationsEnabled && canEnableNotifications(), @@ -125,6 +133,8 @@ class NotificationsSettingsViewModel(private val sharedPreferences: SharedPrefer ringtone = SignalStore.settings().callRingtone, vibrateEnabled = SignalStore.settings().isCallVibrateEnabled ), + notifyWhileLocked = TextSecurePreferences.isPassphraseLockNotificationsEnabled(application) && SignalStore.account().pushAvailable, + canEnableNotifyWhileLocked = SignalStore.account().pushAvailable, notifyWhenContactJoinsSignal = SignalStore.settings().isNotifyWhenContactJoinsSignal ) diff --git a/app/src/main/java/org/thoughtcrime/securesms/gcm/FcmFetchManager.kt b/app/src/main/java/org/thoughtcrime/securesms/gcm/FcmFetchManager.kt index cb5312724c..124ecadab6 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/gcm/FcmFetchManager.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/gcm/FcmFetchManager.kt @@ -73,10 +73,13 @@ object FcmFetchManager { @JvmStatic fun postMayHaveMessagesNotification(context: Context) { - if (FeatureFlags.fcmMayHaveMessagesNotificationKillSwitch()) { - Log.w(TAG, "May have messages notification kill switch") + val notificationManager = NotificationManagerCompat.from(context) + + if (notificationManager.getNotificationChannel(NotificationChannels.ADDITIONAL_MESSAGE_NOTIFICATIONS) == null) { + Log.e(TAG, "Notification channel for MAY_HAVE_MESSAGES_NOTIFICATION does not exist.") return } + val mayHaveMessagesNotification: Notification = NotificationCompat.Builder(context, NotificationChannels.ADDITIONAL_MESSAGE_NOTIFICATIONS) .setSmallIcon(R.drawable.ic_notification) .setColor(ContextCompat.getColor(context, R.color.core_ultramarine)) @@ -87,7 +90,7 @@ object FcmFetchManager { .setOnlyAlertOnce(true) .build() - NotificationManagerCompat.from(context) + notificationManager .notify(NotificationIds.MAY_HAVE_MESSAGES_NOTIFICATION_ID, mayHaveMessagesNotification) } diff --git a/app/src/main/java/org/thoughtcrime/securesms/gcm/FcmReceiveService.java b/app/src/main/java/org/thoughtcrime/securesms/gcm/FcmReceiveService.java index 09a2e2ec3c..206b912446 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/gcm/FcmReceiveService.java +++ b/app/src/main/java/org/thoughtcrime/securesms/gcm/FcmReceiveService.java @@ -18,6 +18,7 @@ import org.thoughtcrime.securesms.service.KeyCachingService; import org.thoughtcrime.securesms.util.NetworkUtil; import org.thoughtcrime.securesms.util.SignalLocalMetrics; +import org.thoughtcrime.securesms.util.TextSecurePreferences; import java.util.Locale; @@ -26,10 +27,11 @@ public class FcmReceiveService extends FirebaseMessagingService { private static final String TAG = Log.tag(FcmReceiveService.class); @Override - public void onMessageReceived(RemoteMessage remoteMessage) { + public void onMessageReceived(@NonNull RemoteMessage remoteMessage) { if (KeyCachingService.isLocked()) { - if (remoteMessage != null && remoteMessage.getPriority() == RemoteMessage.PRIORITY_HIGH) { - Log.d(TAG, "New urgent message received while the db is locked"); + if (remoteMessage.getPriority() == RemoteMessage.PRIORITY_HIGH && + TextSecurePreferences.isPassphraseLockNotificationsEnabled(this)) { + Log.d(TAG, "New urgent message received while app is locked."); FcmFetchManager.postMayHaveMessagesNotification(this); } return; @@ -67,7 +69,7 @@ public void onDeletedMessages() { } @Override - public void onNewToken(String token) { + public void onNewToken(@NonNull String token) { Log.i(TAG, "onNewToken()"); if (KeyCachingService.isLocked()) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/keyvalue/AccountValues.kt b/app/src/main/java/org/thoughtcrime/securesms/keyvalue/AccountValues.kt index 658b6e7cd2..08aee7b99b 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/keyvalue/AccountValues.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/keyvalue/AccountValues.kt @@ -291,6 +291,10 @@ internal class AccountValues internal constructor(store: KeyValueStore) : Signal @get:JvmName("isFcmEnabled") var fcmEnabled: Boolean by booleanValue(KEY_FCM_ENABLED, false) + @get:JvmName("isPushAvailable") + val pushAvailable: Boolean + get() = fcmEnabled + /** The FCM token, which allows the server to send us FCM messages. */ var fcmToken: String? get() { diff --git a/app/src/main/java/org/thoughtcrime/securesms/util/FeatureFlags.java b/app/src/main/java/org/thoughtcrime/securesms/util/FeatureFlags.java index 4a4be10011..52339f7921 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/util/FeatureFlags.java +++ b/app/src/main/java/org/thoughtcrime/securesms/util/FeatureFlags.java @@ -569,13 +569,6 @@ public static boolean callingFieldTrialAnyAddressPortsKillSwitch() { return getBoolean(ANY_ADDRESS_PORTS_KILL_SWITCH, false); } - /** - * Enable/disable for notification when we cannot fetch messages despite receiving an urgent push. - */ - public static boolean fcmMayHaveMessagesNotificationKillSwitch() { - return getBoolean(FCM_MAY_HAVE_MESSAGES_KILL_SWITCH, false); - } - public static boolean editMessageSending() { return getBoolean(EDIT_MESSAGE_SEND, false); } diff --git a/app/src/main/java/org/thoughtcrime/securesms/util/SecurePreferenceManager.java b/app/src/main/java/org/thoughtcrime/securesms/util/SecurePreferenceManager.java index 6536a63f90..9f64c41ce9 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/util/SecurePreferenceManager.java +++ b/app/src/main/java/org/thoughtcrime/securesms/util/SecurePreferenceManager.java @@ -29,6 +29,7 @@ private static SharedPreferences createSecurePreferences(Context context) { case TextSecurePreferences.THEME_PREF: case TextSecurePreferences.LANGUAGE_PREF: case TextSecurePreferences.PASSPHRASE_LOCK: + case TextSecurePreferences.PASSPHRASE_LOCK_NOTIFICATIONS: case TextSecurePreferences.BIOMETRIC_SCREEN_LOCK: case TextSecurePreferences.FIRST_INSTALL_VERSION: case TextSecurePreferences.SYSTEM_EMOJI_PREF: diff --git a/app/src/main/java/org/thoughtcrime/securesms/util/TextSecurePreferences.java b/app/src/main/java/org/thoughtcrime/securesms/util/TextSecurePreferences.java index 7fef3fd5da..4518808187 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/util/TextSecurePreferences.java +++ b/app/src/main/java/org/thoughtcrime/securesms/util/TextSecurePreferences.java @@ -47,7 +47,6 @@ public class TextSecurePreferences { private static final String TAG = Log.tag(TextSecurePreferences.class); - public static final String CHANGE_PASSPHRASE_PREF = "pref_change_passphrase"; public static final String THEME_PREF = "pref_theme"; public static final String LANGUAGE_PREF = "pref_language"; @@ -109,10 +108,11 @@ public class TextSecurePreferences { public static final String TRANSFER = "pref_transfer"; - public static final String PASSPHRASE_LOCK = "pref_passphrase_lock"; - public static final String PASSPHRASE_LOCK_TIMEOUT = "pref_passphrase_lock_timeout"; - public static final String PASSPHRASE_LOCK_TRIGGER = "pref_passphrase_lock_trigger"; - public static final String BIOMETRIC_SCREEN_LOCK = "pref_biometric_screen_lock"; + public static final String PASSPHRASE_LOCK = "pref_passphrase_lock"; + public static final String PASSPHRASE_LOCK_TIMEOUT = "pref_passphrase_lock_timeout"; + public static final String PASSPHRASE_LOCK_TRIGGER = "pref_passphrase_lock_trigger"; + public static final String PASSPHRASE_LOCK_NOTIFICATIONS = "pref_passphrase_lock_notifications"; + public static final String BIOMETRIC_SCREEN_LOCK = "pref_biometric_screen_lock"; private static final String NETWORK_CONFIG_SEEN = "pref_network_config_seen"; @@ -220,6 +220,7 @@ public static void setGoogleMapType(Context context, String value) { UPDATE_APK_INCLUDE_BETA, BLOCK_UNKNOWN, BIOMETRIC_SCREEN_LOCK, + PASSPHRASE_LOCK_NOTIFICATIONS, }; private static final String[] stringSetPreferencesToBackupMolly = {PASSPHRASE_LOCK_TRIGGER}; @@ -372,6 +373,14 @@ public static long getPassphraseLockTimeout(@NonNull Context context) { return getLongPreference(context, PASSPHRASE_LOCK_TIMEOUT, 0); } + public static boolean isPassphraseLockNotificationsEnabled(@NonNull Context context) { + return getBooleanPreference(context, PASSPHRASE_LOCK_NOTIFICATIONS, true); + } + + public static void setPassphraseLockNotificationsEnabled(@NonNull Context context, boolean value) { + setBooleanPreference(context, PASSPHRASE_LOCK_NOTIFICATIONS, value); + } + public static void setHasSeenNetworkConfig(Context context, boolean value) { setBooleanPreference(context, NETWORK_CONFIG_SEEN, value); } diff --git a/app/src/main/res/values/strings2.xml b/app/src/main/res/values/strings2.xml index 146ea57be3..ade1580fc5 100644 --- a/app/src/main/res/values/strings2.xml +++ b/app/src/main/res/values/strings2.xml @@ -108,4 +108,8 @@ Timed out waiting for the primary device. Ready your device to scan the QR code, then try again Retry linking The provider has declined to send you a verification code, likely due to fraud prevention rules. + New activity while locked + Receive notifications for messages or missed calls when the app is locked. + Push notifications unavailable + Sorry, this feature requires push notifications delivered via FCM or UnifiedPush, which are currently unavailable. diff --git a/libfakegms/src/main/java/com/google/firebase/messaging/FirebaseMessagingService.java b/libfakegms/src/main/java/com/google/firebase/messaging/FirebaseMessagingService.java index 453fbfe975..54dd5f94c5 100644 --- a/libfakegms/src/main/java/com/google/firebase/messaging/FirebaseMessagingService.java +++ b/libfakegms/src/main/java/com/google/firebase/messaging/FirebaseMessagingService.java @@ -14,11 +14,11 @@ public IBinder onBind(Intent intent) { return null; } - public abstract void onMessageReceived(RemoteMessage remoteMessage); + public abstract void onMessageReceived(@NonNull RemoteMessage remoteMessage); public abstract void onDeletedMessages(); - public abstract void onNewToken(String token); + public abstract void onNewToken(@NonNull String token); public abstract void onMessageSent(@NonNull String s); From 7fba50a6c9894a7fdb41d5196dbe428f5b394976 Mon Sep 17 00:00:00 2001 From: Oscar Mira Date: Thu, 16 Nov 2023 13:19:19 +0100 Subject: [PATCH 4/4] Handle active notifications on passphrase lock timeout --- .../securesms/ApplicationContext.java | 20 ++++++++--- .../notifications/MessageNotifier.java | 2 +- .../OptimizedMessageNotifier.java | 4 +-- .../v2/DefaultMessageNotifier.kt | 10 ++++-- .../securesms/service/KeyCachingService.java | 35 +++++++------------ .../securesms/service/WipeMemoryService.java | 17 ++------- 6 files changed, 41 insertions(+), 47 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/ApplicationContext.java b/app/src/main/java/org/thoughtcrime/securesms/ApplicationContext.java index 558663bb0b..b92842452f 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/ApplicationContext.java +++ b/app/src/main/java/org/thoughtcrime/securesms/ApplicationContext.java @@ -26,6 +26,7 @@ import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; import androidx.annotation.WorkerThread; +import androidx.core.content.ContextCompat; import androidx.multidex.MultiDexApplication; import com.google.android.gms.security.ProviderInstaller; @@ -84,6 +85,7 @@ import org.thoughtcrime.securesms.mms.SignalGlideComponents; import org.thoughtcrime.securesms.mms.SignalGlideModule; import org.thoughtcrime.securesms.net.NetworkManager; +import org.thoughtcrime.securesms.notifications.MessageNotifier; import org.thoughtcrime.securesms.providers.BlobProvider; import org.thoughtcrime.securesms.ratelimit.RateLimitUtil; import org.thoughtcrime.securesms.recipients.Recipient; @@ -310,7 +312,7 @@ public void onUnlock() { } @MainThread - public void onLock() { + public void onLock(boolean keyExpired) { Log.i(TAG, "onLock()"); stopService(new Intent(this, WebRtcCallService.class)); @@ -319,6 +321,16 @@ public void onLock() { finalizeMessageRetrieval(); unregisterKeyEventReceiver(); + MessageNotifier messageNotifier = ApplicationDependencies.getMessageNotifier(); + messageNotifier.cancelDelayedNotifications(); + boolean hadActiveNotifications = messageNotifier.clearNotifications(this); + + if (hadActiveNotifications && keyExpired && SignalStore.account().isPushAvailable() && + TextSecurePreferences.isPassphraseLockNotificationsEnabled(this) ) { + Log.d(TAG, "Replacing active notifications with may-have-messages notification"); + FcmFetchManager.postMayHaveMessagesNotification(this); + } + ThreadUtil.runOnMainDelayed(() -> { ApplicationDependencies.getJobManager().shutdown(TimeUnit.SECONDS.toMillis(10)); KeyCachingService.clearMasterSecret(); @@ -628,15 +640,15 @@ public void e(@NonNull String tag, @NonNull String message, @Nullable Throwable private final BroadcastReceiver keyEventReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { - onLock(); + boolean keyExpired = intent.getBooleanExtra(KeyCachingService.EXTRA_KEY_EXPIRED, false); + onLock(keyExpired); } }; private void registerKeyEventReceiver() { IntentFilter filter = new IntentFilter(); filter.addAction(KeyCachingService.CLEAR_KEY_EVENT); - - registerReceiver(keyEventReceiver, filter, KeyCachingService.KEY_PERMISSION, null); + ContextCompat.registerReceiver(this, keyEventReceiver, filter, KeyCachingService.KEY_PERMISSION, null, ContextCompat.RECEIVER_NOT_EXPORTED); } private void unregisterKeyEventReceiver() { diff --git a/app/src/main/java/org/thoughtcrime/securesms/notifications/MessageNotifier.java b/app/src/main/java/org/thoughtcrime/securesms/notifications/MessageNotifier.java index 5e64c9edaa..8fe32177ba 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/notifications/MessageNotifier.java +++ b/app/src/main/java/org/thoughtcrime/securesms/notifications/MessageNotifier.java @@ -29,7 +29,7 @@ public interface MessageNotifier { void updateNotification(@NonNull Context context, @NonNull ConversationId conversationId, @NonNull BubbleUtil.BubbleState defaultBubbleState); void updateNotification(@NonNull Context context, @NonNull ConversationId conversationId, boolean signal); void updateNotification(@NonNull Context context, @Nullable ConversationId conversationId, boolean signal, int reminderCount, @NonNull BubbleUtil.BubbleState defaultBubbleState); - void clearNotifications(@NonNull Context context); + boolean clearNotifications(@NonNull Context context); void clearReminder(@NonNull Context context); void addStickyThread(@NonNull ConversationId conversationId, long earliestTimestamp); void removeStickyThread(@NonNull ConversationId conversationId); diff --git a/app/src/main/java/org/thoughtcrime/securesms/notifications/OptimizedMessageNotifier.java b/app/src/main/java/org/thoughtcrime/securesms/notifications/OptimizedMessageNotifier.java index a649d40ddb..bf35b0f944 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/notifications/OptimizedMessageNotifier.java +++ b/app/src/main/java/org/thoughtcrime/securesms/notifications/OptimizedMessageNotifier.java @@ -122,8 +122,8 @@ public void updateNotification(@NonNull Context context, @Nullable ConversationI } @Override - public void clearNotifications(@NonNull Context context) { - getNotifier().clearNotifications(context); + public boolean clearNotifications(@NonNull Context context) { + return getNotifier().clearNotifications(context); } @Override diff --git a/app/src/main/java/org/thoughtcrime/securesms/notifications/v2/DefaultMessageNotifier.kt b/app/src/main/java/org/thoughtcrime/securesms/notifications/v2/DefaultMessageNotifier.kt index fc7f37eeba..e7da3a9588 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/notifications/v2/DefaultMessageNotifier.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/notifications/v2/DefaultMessageNotifier.kt @@ -241,19 +241,23 @@ class DefaultMessageNotifier(context: Application) : MessageNotifier { if (Build.VERSION.SDK_INT >= 24) { val ids = state.conversations.filter { it.thread != visibleThread }.map { it.notificationId } + stickyThreads.map { (_, stickyThread) -> stickyThread.notificationId } - val notShown = ids - ServiceUtil.getNotificationManager(context).getDisplayedNotificationIds().getOrDefault(emptySet()) + val notShown = ids - getDisplayedNotificationIds(context) if (notShown.isNotEmpty()) { Log.e(TAG, "Notifications should be showing but are not for ${notShown.size} threads") } } } - override fun clearNotifications(context: Context) { - NotificationCancellationHelper.cancelAllMessageNotifications(context, stickyThreads.map { it.value.notificationId }.toSet()) + override fun clearNotifications(context: Context): Boolean { + val activeNotifications = getDisplayedNotificationIds(context) + NotificationCancellationHelper.cancelAllMessageNotifications(context) updateBadge(context, 0) clearReminderInternal(context) + return activeNotifications.isNotEmpty() } + private fun getDisplayedNotificationIds(context: Context) = ServiceUtil.getNotificationManager(context).getDisplayedNotificationIds().getOrDefault(emptySet()) + override fun clearReminder(context: Context) { // Intentionally left blank } diff --git a/app/src/main/java/org/thoughtcrime/securesms/service/KeyCachingService.java b/app/src/main/java/org/thoughtcrime/securesms/service/KeyCachingService.java index 7a0d46f1ee..a0d52db376 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/service/KeyCachingService.java +++ b/app/src/main/java/org/thoughtcrime/securesms/service/KeyCachingService.java @@ -40,7 +40,6 @@ import org.thoughtcrime.securesms.crypto.MasterSecret; import org.thoughtcrime.securesms.dependencies.ApplicationDependencies; import org.thoughtcrime.securesms.migrations.ApplicationMigrations; -import org.thoughtcrime.securesms.notifications.MessageNotifier; import org.thoughtcrime.securesms.notifications.NotificationChannels; import org.thoughtcrime.securesms.util.DynamicLanguage; import org.thoughtcrime.securesms.util.ServiceUtil; @@ -66,6 +65,8 @@ public class KeyCachingService extends Service { public static final String CLEAR_KEY_ACTION = BuildConfig.APPLICATION_ID + ".service.action.CLEAR_KEY"; public static final String LOCALE_CHANGE_EVENT = BuildConfig.APPLICATION_ID + ".service.action.LOCALE_CHANGE_EVENT"; + public static final String EXTRA_KEY_EXPIRED = "extra.key_expired"; + private DynamicLanguage dynamicLanguage = new DynamicLanguage(); private final IBinder binder = new KeySetBinder(); @@ -110,9 +111,9 @@ public int onStartCommand(Intent intent, int flags, int startId) { if (intent.getAction() != null) { switch (intent.getAction()) { - case CLEAR_KEY_ACTION: handleClearKey(); break; - case PASSPHRASE_EXPIRED_EVENT: handleClearKey(); break; - case LOCALE_CHANGE_EVENT: handleLocaleChanged(); break; + case CLEAR_KEY_ACTION -> handleClearKey(false); + case PASSPHRASE_EXPIRED_EVENT -> handleClearKey(true); + case LOCALE_CHANGE_EVENT -> handleLocaleChanged(); } } else { handleCacheKey(); @@ -165,8 +166,8 @@ private void handleCacheKey() { }); } - private void handleClearKey() { - Log.i(TAG, "handleClearKey"); + private void handleClearKey(boolean keyExpired) { + Log.d(TAG, "handleClearKey() keyExpired: " + keyExpired); cancelTimeout(); @@ -178,13 +179,12 @@ private void handleClearKey() { KeyCachingService.locking = true; - sendPackageBroadcast(CLEAR_KEY_EVENT); + Log.i(TAG, "Broadcasting " + CLEAR_KEY_EVENT); - SignalExecutors.BOUNDED.execute(() -> { - MessageNotifier messageNotifier = ApplicationDependencies.getMessageNotifier(); - messageNotifier.cancelDelayedNotifications(); - messageNotifier.clearNotifications(KeyCachingService.this); - }); + Intent intent = new Intent(CLEAR_KEY_EVENT); + intent.putExtra(EXTRA_KEY_EXPIRED, keyExpired); + intent.setPackage(getPackageName()); + sendBroadcast(intent, KEY_PERMISSION); } private void handleLocaleChanged() { @@ -252,18 +252,9 @@ private void foregroundService() { startForeground(SERVICE_RUNNING_ID, builder.build()); } - private void sendPackageBroadcast(String action) { - Log.i(TAG, "Broadcasting " + action); - - Intent intent = new Intent(action); - intent.setPackage(getApplicationContext().getPackageName()); - - sendBroadcast(intent, KEY_PERMISSION); - } - private PendingIntent buildLockIntent() { Intent intent = new Intent(this, KeyCachingService.class); - intent.setAction(PASSPHRASE_EXPIRED_EVENT); + intent.setAction(CLEAR_KEY_ACTION); return PendingIntent.getService(getApplicationContext(), 0, intent, getPendingIntentFlags()); } diff --git a/app/src/main/java/org/thoughtcrime/securesms/service/WipeMemoryService.java b/app/src/main/java/org/thoughtcrime/securesms/service/WipeMemoryService.java index 696086c51b..17a23ff6d1 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/service/WipeMemoryService.java +++ b/app/src/main/java/org/thoughtcrime/securesms/service/WipeMemoryService.java @@ -5,7 +5,6 @@ import android.app.PendingIntent; import android.content.Context; import android.content.Intent; -import android.os.Build; import android.os.PowerManager; import androidx.annotation.Nullable; @@ -16,8 +15,8 @@ import org.signal.core.util.logging.Log; import org.thoughtcrime.securesms.MainActivity; import org.thoughtcrime.securesms.R; +import org.thoughtcrime.securesms.gcm.FcmFetchManager; import org.thoughtcrime.securesms.jobs.ForegroundServiceUtil; -import org.thoughtcrime.securesms.jobs.UnableToStartException; import org.thoughtcrime.securesms.notifications.NotificationChannels; import org.thoughtcrime.securesms.util.ServiceUtil; import org.thoughtcrime.securesms.util.WakeLockUtil; @@ -64,20 +63,8 @@ public class WipeMemoryService extends IntentService { public static void run(Context context, boolean restartApp) { restart = restartApp; - startForegroundService(context); - } - - private static void startForegroundService(Context context) { Intent intent = new Intent(context, WipeMemoryService.class); - if (Build.VERSION.SDK_INT >= 26) { - try { - ForegroundServiceUtil.startWhenCapable(context, intent); - } catch (UnableToStartException e) { - Log.e(TAG, "Unable to start foreground service", e); - } - } else { - context.startService(intent); - } + ForegroundServiceUtil.startWhenCapableOrThrow(context, intent); } public WipeMemoryService() {