mirror of
https://github.com/signalapp/Signal-Android.git
synced 2025-12-23 12:38:33 +00:00
Squelch notifications in noisy groups and during large initial message processing.
This commit is contained in:
committed by
Greyson Parrelli
parent
6b6e2490e7
commit
1b82d10b39
@@ -29,7 +29,7 @@ public class OptimizedMessageNotifier implements MessageNotifier {
|
||||
|
||||
@MainThread
|
||||
public OptimizedMessageNotifier(@NonNull Application context) {
|
||||
this.limiter = new LeakyBucketLimiter(5, 1000, new Handler(SignalExecutors.getAndStartHandlerThread("signal-notifier", ThreadUtil.PRIORITY_IMPORTANT_BACKGROUND_THREAD).getLooper()));
|
||||
this.limiter = new LeakyBucketLimiter(3, 1000, new Handler(SignalExecutors.getAndStartHandlerThread("signal-notifier", ThreadUtil.PRIORITY_IMPORTANT_BACKGROUND_THREAD).getLooper()));
|
||||
this.defaultMessageNotifier = new DefaultMessageNotifier(context);
|
||||
}
|
||||
|
||||
|
||||
@@ -61,12 +61,16 @@ class DefaultMessageNotifier(context: Application) : MessageNotifier {
|
||||
|
||||
private val threadReminders: MutableMap<ConversationId, Reminder> = ConcurrentHashMap()
|
||||
private val stickyThreads: MutableMap<ConversationId, StickyThread> = mutableMapOf()
|
||||
private val lastThreadNotification: MutableMap<ConversationId, Long> = ConcurrentHashMap()
|
||||
|
||||
private val executor = CancelableExecutor()
|
||||
|
||||
override fun setVisibleThread(conversationId: ConversationId?) {
|
||||
visibleThread = conversationId
|
||||
stickyThreads.remove(conversationId)
|
||||
if (conversationId != null) {
|
||||
lastThreadNotification.remove(conversationId)
|
||||
}
|
||||
}
|
||||
|
||||
override fun getVisibleThread(): Optional<ConversationId> {
|
||||
@@ -209,7 +213,8 @@ class DefaultMessageNotifier(context: Application) : MessageNotifier {
|
||||
lastAudibleNotification = lastAudibleNotification,
|
||||
notificationConfigurationChanged = notificationConfigurationChanged,
|
||||
alertOverrides = alertOverrides,
|
||||
previousState = previousState
|
||||
previousState = previousState,
|
||||
lastThreadNotification = lastThreadNotification
|
||||
)
|
||||
|
||||
previousState = state
|
||||
|
||||
@@ -25,6 +25,7 @@ import org.thoughtcrime.securesms.conversation.ConversationIntents
|
||||
import org.thoughtcrime.securesms.conversation.colors.AvatarColor
|
||||
import org.thoughtcrime.securesms.database.SignalDatabase
|
||||
import org.thoughtcrime.securesms.database.model.InMemoryMessageRecord
|
||||
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies
|
||||
import org.thoughtcrime.securesms.keyvalue.SignalStore
|
||||
import org.thoughtcrime.securesms.notifications.NotificationChannels
|
||||
import org.thoughtcrime.securesms.notifications.NotificationIds
|
||||
@@ -34,6 +35,8 @@ import org.thoughtcrime.securesms.util.BubbleUtil
|
||||
import org.thoughtcrime.securesms.util.ConversationUtil
|
||||
import org.thoughtcrime.securesms.util.ServiceUtil
|
||||
import org.thoughtcrime.securesms.util.TextSecurePreferences
|
||||
import kotlin.time.Duration
|
||||
import kotlin.time.Duration.Companion.seconds
|
||||
|
||||
/**
|
||||
* Given a notification state consisting of conversations of messages, show appropriate system notifications.
|
||||
@@ -42,6 +45,9 @@ object NotificationFactory {
|
||||
|
||||
val TAG: String = Log.tag(NotificationFactory::class.java)
|
||||
|
||||
private val STILL_DECRYPTING_INDIVIDUAL_THROTTLE: Duration = 5.seconds
|
||||
private val GROUP_THROTTLE: Duration = 20.seconds
|
||||
|
||||
fun notify(
|
||||
context: Context,
|
||||
state: NotificationState,
|
||||
@@ -51,7 +57,8 @@ object NotificationFactory {
|
||||
lastAudibleNotification: Long,
|
||||
notificationConfigurationChanged: Boolean,
|
||||
alertOverrides: Set<ConversationId>,
|
||||
previousState: NotificationState
|
||||
previousState: NotificationState,
|
||||
lastThreadNotification: MutableMap<ConversationId, Long>
|
||||
): Set<ConversationId> {
|
||||
if (state.isEmpty) {
|
||||
Log.d(TAG, "State is empty, bailing")
|
||||
@@ -68,7 +75,8 @@ object NotificationFactory {
|
||||
defaultBubbleState = defaultBubbleState,
|
||||
lastAudibleNotification = lastAudibleNotification,
|
||||
alertOverrides = alertOverrides,
|
||||
nonVisibleThreadCount = nonVisibleThreadCount
|
||||
nonVisibleThreadCount = nonVisibleThreadCount,
|
||||
lastThreadNotification = lastThreadNotification
|
||||
)
|
||||
} else {
|
||||
notify24(
|
||||
@@ -81,7 +89,8 @@ object NotificationFactory {
|
||||
notificationConfigurationChanged = notificationConfigurationChanged,
|
||||
alertOverrides = alertOverrides,
|
||||
nonVisibleThreadCount = nonVisibleThreadCount,
|
||||
previousState = previousState
|
||||
previousState = previousState,
|
||||
lastThreadNotification = lastThreadNotification
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -94,7 +103,8 @@ object NotificationFactory {
|
||||
defaultBubbleState: BubbleUtil.BubbleState,
|
||||
lastAudibleNotification: Long,
|
||||
alertOverrides: Set<ConversationId>,
|
||||
nonVisibleThreadCount: Int
|
||||
nonVisibleThreadCount: Int,
|
||||
lastThreadNotification: MutableMap<ConversationId, Long>
|
||||
): Set<ConversationId> {
|
||||
val threadsThatNewlyAlerted: MutableSet<ConversationId> = mutableSetOf()
|
||||
|
||||
@@ -107,12 +117,17 @@ object NotificationFactory {
|
||||
|
||||
if (nonVisibleThreadCount == 1) {
|
||||
state.conversations.first { it.thread != visibleThread }.let { conversation ->
|
||||
val shouldAlert = shouldAlert(conversation, lastThreadNotification.getOrDefault(conversation.thread, 0), alertOverrides.contains(conversation.thread))
|
||||
if (shouldAlert) {
|
||||
lastThreadNotification[conversation.thread] = System.currentTimeMillis()
|
||||
}
|
||||
|
||||
notifyForConversation(
|
||||
context = context,
|
||||
conversation = conversation,
|
||||
targetThread = targetThread,
|
||||
defaultBubbleState = defaultBubbleState,
|
||||
shouldAlert = (conversation.hasNewNotifications() || alertOverrides.contains(conversation.thread)) && !conversation.mostRecentNotification.authorRecipient.isSelf
|
||||
shouldAlert = shouldAlert
|
||||
)
|
||||
if (conversation.hasNewNotifications()) {
|
||||
threadsThatNewlyAlerted += conversation.thread
|
||||
@@ -138,7 +153,8 @@ object NotificationFactory {
|
||||
notificationConfigurationChanged: Boolean,
|
||||
alertOverrides: Set<ConversationId>,
|
||||
nonVisibleThreadCount: Int,
|
||||
previousState: NotificationState
|
||||
previousState: NotificationState,
|
||||
lastThreadNotification: MutableMap<ConversationId, Long>
|
||||
): Set<ConversationId> {
|
||||
val threadsThatNewlyAlerted: MutableSet<ConversationId> = mutableSetOf()
|
||||
|
||||
@@ -152,12 +168,22 @@ object NotificationFactory {
|
||||
}
|
||||
|
||||
try {
|
||||
val shouldAlert = shouldAlert(
|
||||
conversation = conversation,
|
||||
lastNotificationTimestamp = lastThreadNotification.getOrDefault(conversation.thread, 0),
|
||||
alertOverride = alertOverrides.contains(conversation.thread)
|
||||
)
|
||||
|
||||
if (shouldAlert) {
|
||||
lastThreadNotification[conversation.thread] = System.currentTimeMillis()
|
||||
}
|
||||
|
||||
notifyForConversation(
|
||||
context = context,
|
||||
conversation = conversation,
|
||||
targetThread = targetThread,
|
||||
defaultBubbleState = defaultBubbleState,
|
||||
shouldAlert = (conversation.hasNewNotifications() || alertOverrides.contains(conversation.thread)) && !conversation.mostRecentNotification.authorRecipient.isSelf
|
||||
shouldAlert = shouldAlert
|
||||
)
|
||||
} catch (e: SecurityException) {
|
||||
Log.w(TAG, "Too many pending intents device quirk", e)
|
||||
@@ -172,6 +198,17 @@ object NotificationFactory {
|
||||
return threadsThatNewlyAlerted
|
||||
}
|
||||
|
||||
private fun shouldAlert(conversation: NotificationConversation, lastNotificationTimestamp: Long, alertOverride: Boolean): Boolean {
|
||||
val throttle: Duration = when {
|
||||
conversation.recipient.isGroup && (conversation.mostRecentNotification as? MessageNotification)?.hasSelfMention == false -> GROUP_THROTTLE
|
||||
ApplicationDependencies.getIncomingMessageObserver().decryptionDrained -> STILL_DECRYPTING_INDIVIDUAL_THROTTLE
|
||||
else -> 0.seconds
|
||||
}
|
||||
val canAlertBasedOnTime: Boolean = lastNotificationTimestamp < System.currentTimeMillis() - throttle.inWholeMilliseconds || lastNotificationTimestamp > System.currentTimeMillis()
|
||||
|
||||
return ((conversation.hasNewNotifications() && canAlertBasedOnTime) || alertOverride) && !conversation.mostRecentNotification.authorRecipient.isSelf
|
||||
}
|
||||
|
||||
private fun notifyForConversation(
|
||||
context: Context,
|
||||
conversation: NotificationConversation,
|
||||
|
||||
@@ -205,6 +205,7 @@ class MessageNotification(threadRecipient: Recipient, record: MessageRecord) : N
|
||||
override val timestamp: Long = record.timestamp
|
||||
override val authorRecipient: Recipient = record.fromRecipient.resolve()
|
||||
override val isNewNotification: Boolean = notifiedTimestamp == 0L
|
||||
val hasSelfMention = record.hasSelfMention()
|
||||
|
||||
private var thumbnailInfo: ThumbnailInfo? = null
|
||||
|
||||
|
||||
Reference in New Issue
Block a user