From bb6644505bf6c91acf90b66c33436a93658f83ec Mon Sep 17 00:00:00 2001 From: Oscar Mira Date: Wed, 15 Nov 2023 18:35:40 +0100 Subject: [PATCH] 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.registration.PushChallengeRequest; 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 class FcmReceiveService extends FirebaseMessagingService { } @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 final class FeatureFlags { 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 @@ public class SecurePreferenceManager { 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 class TextSecurePreferences { 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 class TextSecurePreferences { 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 abstract class FirebaseMessagingService extends Service { 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);