Add you may have messages notification.

This commit is contained in:
Clark
2023-07-19 16:05:00 -04:00
committed by Nicholas
parent 5242b9af39
commit 0fde404da8
7 changed files with 123 additions and 53 deletions

View File

@@ -48,6 +48,7 @@ import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
import org.thoughtcrime.securesms.dependencies.ApplicationDependencyProvider;
import org.thoughtcrime.securesms.emoji.EmojiSource;
import org.thoughtcrime.securesms.emoji.JumboEmoji;
import org.thoughtcrime.securesms.gcm.FcmFetchManager;
import org.thoughtcrime.securesms.gcm.FcmJobService;
import org.thoughtcrime.securesms.jobs.AccountConsistencyWorkerJob;
import org.thoughtcrime.securesms.jobs.CheckServiceReachabilityJob;
@@ -230,6 +231,7 @@ public class ApplicationContext extends MultiDexApplication implements AppForegr
ApplicationDependencies.getMegaphoneRepository().onAppForegrounded();
ApplicationDependencies.getDeadlockDetector().start();
SubscriptionKeepAliveJob.enqueueAndTrackTimeIfNecessary();
FcmFetchManager.onForeground(this);
SignalExecutors.BOUNDED.execute(() -> {
FeatureFlags.refreshIfNecessary();

View File

@@ -1,13 +1,23 @@
package org.thoughtcrime.securesms.gcm
import android.app.Notification
import android.app.PendingIntent
import android.content.Context
import android.content.Intent
import android.os.Build
import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationManagerCompat
import org.signal.core.util.PendingIntentFlags.mutable
import org.signal.core.util.concurrent.SignalExecutors
import org.signal.core.util.logging.Log
import org.thoughtcrime.securesms.MainActivity
import org.thoughtcrime.securesms.R
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies
import org.thoughtcrime.securesms.jobs.PushNotificationReceiveJob
import org.thoughtcrime.securesms.messages.WebSocketStrategy
import org.thoughtcrime.securesms.notifications.NotificationChannels
import org.thoughtcrime.securesms.notifications.NotificationIds
import org.thoughtcrime.securesms.util.FeatureFlags
import org.thoughtcrime.securesms.util.SignalLocalMetrics
import org.thoughtcrime.securesms.util.concurrent.SerialMonoLifoExecutor
import kotlin.time.Duration.Companion.minutes
@@ -38,6 +48,9 @@ object FcmFetchManager {
@Volatile
private var activeCount = 0
@Volatile
private var highPriority = false
/**
* @return True if a service was successfully started, otherwise false.
*/
@@ -56,13 +69,41 @@ object FcmFetchManager {
FcmFetchForegroundService.startServiceIfNecessary(context)
}
private 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)
.setSmallIcon(R.drawable.ic_notification)
.setContentTitle(context.getString(R.string.FcmFetchManager__you_may_have_messages))
.setCategory(NotificationCompat.CATEGORY_MESSAGE)
.setContentIntent(PendingIntent.getActivity(context, 0, MainActivity.clearTop(context), mutable()))
.setVibrate(longArrayOf(0))
.setOnlyAlertOnce(true)
.build()
NotificationManagerCompat.from(context)
.notify(NotificationIds.MAY_HAVE_MESSAGES_NOTIFICATION_ID, mayHaveMessagesNotification)
}
private fun cancelMayHaveMessagesNotification(context: Context) {
NotificationManagerCompat.from(context).cancel(NotificationIds.MAY_HAVE_MESSAGES_NOTIFICATION_ID)
}
private fun fetch(context: Context) {
val hasHighPriorityContext = highPriority
val metricId = SignalLocalMetrics.PushWebsocketFetch.startFetch()
val success = retrieveMessages(context)
if (!success) {
SignalLocalMetrics.PushWebsocketFetch.onTimedOut(metricId)
} else {
if (success) {
SignalLocalMetrics.PushWebsocketFetch.onDrained(metricId)
cancelMayHaveMessagesNotification(context)
} else {
SignalLocalMetrics.PushWebsocketFetch.onTimedOut(metricId)
if (hasHighPriorityContext) {
postMayHaveMessagesNotification(context)
}
}
synchronized(this) {
@@ -72,13 +113,22 @@ object FcmFetchManager {
Log.i(TAG, "No more active. Stopping.")
context.stopService(Intent(context, FcmFetchBackgroundService::class.java))
FcmFetchForegroundService.stopServiceIfNecessary(context)
highPriority = false
}
}
}
@JvmStatic
fun enqueueFetch(context: Context) {
fun onForeground(context: Context) {
cancelMayHaveMessagesNotification(context)
}
@JvmStatic
fun enqueueFetch(context: Context, highPriority: Boolean) {
synchronized(this) {
if (highPriority) {
this.highPriority = true
}
val performedReplace = EXECUTOR.enqueue { fetch(context) }
if (performedReplace) {
Log.i(TAG, "Already have one running and one enqueued. Ignoring.")

View File

@@ -74,9 +74,8 @@ public class FcmReceiveService extends FirebaseMessagingService {
}
private static void handleReceivedNotification(Context context, @Nullable RemoteMessage remoteMessage) {
boolean highPriority = remoteMessage != null && remoteMessage.getPriority() == RemoteMessage.PRIORITY_HIGH;
try {
boolean highPriority = remoteMessage != null && remoteMessage.getPriority() == RemoteMessage.PRIORITY_HIGH;
Log.d(TAG, String.format(Locale.US, "[handleReceivedNotification] API: %s, RemoteMessagePriority: %s", Build.VERSION.SDK_INT, remoteMessage != null ? remoteMessage.getPriority() : "n/a"));
if (highPriority) {
@@ -88,7 +87,7 @@ public class FcmReceiveService extends FirebaseMessagingService {
Log.w(TAG, "Failed to start service.", e);
}
FcmFetchManager.enqueueFetch(context);
FcmFetchManager.enqueueFetch(context, highPriority);
}
private static void handleRegistrationPushChallenge(@NonNull String challenge) {

View File

@@ -68,17 +68,18 @@ public class NotificationChannels {
private static final String CONTACT_PREFIX = "contact_";
private static final String MESSAGES_PREFIX = "messages_";
public final String CALLS = "calls_v3";
public final String FAILURES = "failures";
public final String APP_UPDATES = "app_updates";
public final String BACKUPS = "backups_v2";
public final String LOCKED_STATUS = "locked_status_v2";
public final String OTHER = "other_v3";
public final String VOICE_NOTES = "voice_notes";
public final String JOIN_EVENTS = "join_events";
public final String BACKGROUND = "background_connection";
public final String CALL_STATUS = "call_status";
public final String APP_ALERTS = "app_alerts";
public final String CALLS = "calls_v3";
public final String FAILURES = "failures";
public final String APP_UPDATES = "app_updates";
public final String BACKUPS = "backups_v2";
public final String LOCKED_STATUS = "locked_status_v2";
public final String OTHER = "other_v3";
public final String VOICE_NOTES = "voice_notes";
public final String JOIN_EVENTS = "join_events";
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";
private static volatile NotificationChannels instance;
@@ -621,17 +622,18 @@ public class NotificationChannels {
NotificationChannelGroup messagesGroup = new NotificationChannelGroup(CATEGORY_MESSAGES, context.getResources().getString(R.string.NotificationChannel_group_chats));
notificationManager.createNotificationChannelGroup(messagesGroup);
NotificationChannel messages = new NotificationChannel(getMessagesChannel(), context.getString(R.string.NotificationChannel_channel_messages), NotificationManager.IMPORTANCE_HIGH);
NotificationChannel calls = new NotificationChannel(CALLS, context.getString(R.string.NotificationChannel_calls), NotificationManager.IMPORTANCE_HIGH);
NotificationChannel failures = new NotificationChannel(FAILURES, context.getString(R.string.NotificationChannel_failures), NotificationManager.IMPORTANCE_HIGH);
NotificationChannel backups = new NotificationChannel(BACKUPS, context.getString(R.string.NotificationChannel_backups), NotificationManager.IMPORTANCE_LOW);
NotificationChannel lockedStatus = new NotificationChannel(LOCKED_STATUS, context.getString(R.string.NotificationChannel_locked_status), NotificationManager.IMPORTANCE_LOW);
NotificationChannel other = new NotificationChannel(OTHER, context.getString(R.string.NotificationChannel_other), NotificationManager.IMPORTANCE_LOW);
NotificationChannel voiceNotes = new NotificationChannel(VOICE_NOTES, context.getString(R.string.NotificationChannel_voice_notes), NotificationManager.IMPORTANCE_LOW);
NotificationChannel joinEvents = new NotificationChannel(JOIN_EVENTS, context.getString(R.string.NotificationChannel_contact_joined_signal), NotificationManager.IMPORTANCE_DEFAULT);
NotificationChannel background = new NotificationChannel(BACKGROUND, context.getString(R.string.NotificationChannel_background_connection), getDefaultBackgroundChannelImportance(notificationManager));
NotificationChannel callStatus = new NotificationChannel(CALL_STATUS, context.getString(R.string.NotificationChannel_call_status), NotificationManager.IMPORTANCE_LOW);
NotificationChannel appAlerts = new NotificationChannel(APP_ALERTS, context.getString(R.string.NotificationChannel_critical_app_alerts), NotificationManager.IMPORTANCE_HIGH);
NotificationChannel messages = new NotificationChannel(getMessagesChannel(), context.getString(R.string.NotificationChannel_channel_messages), NotificationManager.IMPORTANCE_HIGH);
NotificationChannel calls = new NotificationChannel(CALLS, context.getString(R.string.NotificationChannel_calls), NotificationManager.IMPORTANCE_HIGH);
NotificationChannel failures = new NotificationChannel(FAILURES, context.getString(R.string.NotificationChannel_failures), NotificationManager.IMPORTANCE_HIGH);
NotificationChannel backups = new NotificationChannel(BACKUPS, context.getString(R.string.NotificationChannel_backups), NotificationManager.IMPORTANCE_LOW);
NotificationChannel lockedStatus = new NotificationChannel(LOCKED_STATUS, context.getString(R.string.NotificationChannel_locked_status), NotificationManager.IMPORTANCE_LOW);
NotificationChannel other = new NotificationChannel(OTHER, context.getString(R.string.NotificationChannel_other), NotificationManager.IMPORTANCE_LOW);
NotificationChannel voiceNotes = new NotificationChannel(VOICE_NOTES, context.getString(R.string.NotificationChannel_voice_notes), NotificationManager.IMPORTANCE_LOW);
NotificationChannel joinEvents = new NotificationChannel(JOIN_EVENTS, context.getString(R.string.NotificationChannel_contact_joined_signal), NotificationManager.IMPORTANCE_DEFAULT);
NotificationChannel background = new NotificationChannel(BACKGROUND, context.getString(R.string.NotificationChannel_background_connection), getDefaultBackgroundChannelImportance(notificationManager));
NotificationChannel callStatus = new NotificationChannel(CALL_STATUS, context.getString(R.string.NotificationChannel_call_status), NotificationManager.IMPORTANCE_LOW);
NotificationChannel appAlerts = new NotificationChannel(APP_ALERTS, context.getString(R.string.NotificationChannel_critical_app_alerts), NotificationManager.IMPORTANCE_HIGH);
NotificationChannel additionalMessageNotifications = new NotificationChannel(ADDITIONAL_MESSAGE_NOTIFICATIONS, context.getString(R.string.NotificationChannel_additional_message_notifications), NotificationManager.IMPORTANCE_HIGH);
messages.setGroup(CATEGORY_MESSAGES);
setVibrationEnabled(messages, SignalStore.settings().isMessageVibrateEnabled());
@@ -649,7 +651,7 @@ public class NotificationChannels {
callStatus.setShowBadge(false);
appAlerts.setShowBadge(false);
notificationManager.createNotificationChannels(Arrays.asList(messages, calls, failures, backups, lockedStatus, other, voiceNotes, joinEvents, background, callStatus, appAlerts));
notificationManager.createNotificationChannels(Arrays.asList(messages, calls, failures, backups, lockedStatus, other, voiceNotes, joinEvents, background, callStatus, appAlerts, additionalMessageNotifications));
if (BuildConfig.PLAY_STORE_DISABLED) {
NotificationChannel appUpdates = new NotificationChannel(APP_UPDATES, context.getString(R.string.NotificationChannel_app_updates), NotificationManager.IMPORTANCE_DEFAULT);

View File

@@ -6,25 +6,26 @@ import org.thoughtcrime.securesms.notifications.v2.ConversationId;
public final class NotificationIds {
public static final int FCM_FAILURE = 12;
public static final int PENDING_MESSAGES = 1111;
public static final int MESSAGE_SUMMARY = 1338;
public static final int APPLICATION_MIGRATION = 4242;
public static final int SMS_IMPORT_COMPLETE = 31337;
public static final int PRE_REGISTRATION_SMS = 5050;
public static final int THREAD = 50000;
public static final int INTERNAL_ERROR = 258069;
public static final int LEGACY_SQLCIPHER_MIGRATION = 494949;
public static final int USER_NOTIFICATION_MIGRATION = 525600;
public static final int DEVICE_TRANSFER = 625420;
public static final int DONOR_BADGE_FAILURE = 630001;
public static final int FCM_FETCH = 630002;
public static final int SMS_EXPORT_SERVICE = 630003;
public static final int SMS_EXPORT_COMPLETE = 630004;
public static final int STORY_THREAD = 700000;
public static final int MESSAGE_DELIVERY_FAILURE = 800000;
public static final int STORY_MESSAGE_DELIVERY_FAILURE = 900000;
public static final int UNREGISTERED_NOTIFICATION_ID = 20230102;
public static final int FCM_FAILURE = 12;
public static final int PENDING_MESSAGES = 1111;
public static final int MESSAGE_SUMMARY = 1338;
public static final int APPLICATION_MIGRATION = 4242;
public static final int SMS_IMPORT_COMPLETE = 31337;
public static final int MAY_HAVE_MESSAGES_NOTIFICATION_ID = 31365;
public static final int PRE_REGISTRATION_SMS = 5050;
public static final int THREAD = 50000;
public static final int INTERNAL_ERROR = 258069;
public static final int LEGACY_SQLCIPHER_MIGRATION = 494949;
public static final int USER_NOTIFICATION_MIGRATION = 525600;
public static final int DEVICE_TRANSFER = 625420;
public static final int DONOR_BADGE_FAILURE = 630001;
public static final int FCM_FETCH = 630002;
public static final int SMS_EXPORT_SERVICE = 630003;
public static final int SMS_EXPORT_COMPLETE = 630004;
public static final int STORY_THREAD = 700000;
public static final int MESSAGE_DELIVERY_FAILURE = 800000;
public static final int STORY_MESSAGE_DELIVERY_FAILURE = 900000;
public static final int UNREGISTERED_NOTIFICATION_ID = 20230102;
private NotificationIds() { }

View File

@@ -108,7 +108,7 @@ public final class FeatureFlags {
private static final String SVR2_KILLSWITCH = "android.svr2.killSwitch";
private static final String CDS_COMPAT_MODE = "global.cds.return_acis_without_uaks";
private static final String CONVERSATION_FRAGMENT_V2 = "android.conversationFragmentV2.2";
private static final String FCM_MAY_HAVE_MESSAGES_KILL_SWITCH = "android.fcmNotificationFallbackKillSwitch";
private static final String SAFETY_NUMBER_ACI = "global.safetyNumberAci";
/**
* We will only store remote values for flags in this set. If you want a flag to be controllable
@@ -169,7 +169,8 @@ public final class FeatureFlags {
SVR2_KILLSWITCH,
CDS_COMPAT_MODE,
CONVERSATION_FRAGMENT_V2,
SAFETY_NUMBER_ACI
SAFETY_NUMBER_ACI,
FCM_MAY_HAVE_MESSAGES_KILL_SWITCH
);
@VisibleForTesting
@@ -236,7 +237,8 @@ public final class FeatureFlags {
SVR2_KILLSWITCH,
CDS_COMPAT_MODE,
CONVERSATION_FRAGMENT_V2,
SAFETY_NUMBER_ACI
SAFETY_NUMBER_ACI,
FCM_MAY_HAVE_MESSAGES_KILL_SWITCH
);
/**
@@ -245,7 +247,8 @@ public final class FeatureFlags {
@VisibleForTesting
static final Set<String> STICKY = SetUtil.newHashSet(
VERIFY_V2,
SVR2_KILLSWITCH
SVR2_KILLSWITCH,
FCM_MAY_HAVE_MESSAGES_KILL_SWITCH
);
/**
@@ -573,6 +576,13 @@ 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);
}

View File

@@ -106,6 +106,10 @@
<!-- BackgroundMessageRetriever -->
<string name="BackgroundMessageRetriever_checking_for_messages">Checking for messages…</string>
<!-- Fcm notifications -->
<!-- Notification we show when there may be messages for you, but we cannot connect to the server to check -->
<string name="FcmFetchManager__you_may_have_messages">You may have new messages</string>
<!-- BlockedUsersActivity -->
<string name="BlockedUsersActivity__blocked_users">Blocked users</string>
<string name="BlockedUsersActivity__add_blocked_user">Add blocked user</string>
@@ -2267,6 +2271,8 @@
<string name="NotificationChannel_call_status">Call status</string>
<!-- Notification channel name for occasional alerts to the user. Will appear in the system notification settings as the title of this notification channel. -->
<string name="NotificationChannel_critical_app_alerts">Critical app alerts</string>
<!-- Notification channel name for other notifications related to messages. Will appear in the system notification settings as the title of this notification channel. -->
<string name="NotificationChannel_additional_message_notifications">Additional message notifications</string>
<!-- ProfileEditNameFragment -->