mirror of
https://github.com/signalapp/Signal-Android.git
synced 2025-12-24 13:08:46 +00:00
Add you may have messages notification.
This commit is contained in:
@@ -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();
|
||||
|
||||
@@ -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.")
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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() { }
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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 -->
|
||||
|
||||
|
||||
Reference in New Issue
Block a user