@@ -14,6 +14,7 @@ import me.leolin.shortcutbadger.ShortcutBadger
14
14
import org.signal.core.util.logging.Log
15
15
import org.thoughtcrime.securesms.R
16
16
import org.thoughtcrime.securesms.database.DatabaseFactory
17
+ import org.thoughtcrime.securesms.database.MessageDatabase
17
18
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies
18
19
import org.thoughtcrime.securesms.messages.IncomingMessageObserver
19
20
import org.thoughtcrime.securesms.notifications.DefaultMessageNotifier
@@ -29,6 +30,7 @@ import org.thoughtcrime.securesms.util.ServiceUtil
29
30
import org.thoughtcrime.securesms.util.TextSecurePreferences
30
31
import org.thoughtcrime.securesms.webrtc.CallNotificationBuilder
31
32
import org.whispersystems.signalservice.internal.util.Util
33
+ import java.lang.UnsupportedOperationException
32
34
import java.util.concurrent.ConcurrentHashMap
33
35
import java.util.concurrent.Executor
34
36
import java.util.concurrent.Executors
@@ -125,9 +127,26 @@ class MessageNotifierV2(context: Application) : MessageNotifier {
125
127
}
126
128
127
129
Log .internal().i(TAG , " sticky thread: $stickyThreads " )
128
- val state: NotificationStateV2 = NotificationStateProvider .constructNotificationState(context, stickyThreads)
130
+ var state: NotificationStateV2 = NotificationStateProvider .constructNotificationState(context, stickyThreads)
129
131
Log .internal().i(TAG , " state: $state " )
130
132
133
+ val displayedNotifications: Set <Int >? = ServiceUtil .getNotificationManager(context).getDisplayedNotificationIds().getOrNull()
134
+ if (displayedNotifications != null ) {
135
+ val cleanedUpThreadIds: MutableSet <Long > = mutableSetOf ()
136
+ state.conversations.filterNot { it.hasNewNotifications() || displayedNotifications.contains(it.notificationId) }
137
+ .forEach { conversation ->
138
+ cleanedUpThreadIds + = conversation.threadId
139
+ conversation.notificationItems.forEach { item ->
140
+ val messageDatabase: MessageDatabase = if (item.isMms) DatabaseFactory .getMmsDatabase(context) else DatabaseFactory .getSmsDatabase(context)
141
+ messageDatabase.markAsNotified(item.id)
142
+ }
143
+ }
144
+ if (cleanedUpThreadIds.isNotEmpty()) {
145
+ Log .i(TAG , " Cleaned up ${cleanedUpThreadIds.size} thread(s) with dangling notifications" )
146
+ state = NotificationStateV2 (state.conversations.filterNot { cleanedUpThreadIds.contains(it.threadId) })
147
+ }
148
+ }
149
+
131
150
val retainStickyThreadIds: Set <Long > = state.getThreadsWithMostRecentNotificationFromSelf()
132
151
stickyThreads.keys.retainAll { retainStickyThreadIds.contains(it) }
133
152
@@ -148,7 +167,6 @@ class MessageNotifierV2(context: Application) : MessageNotifier {
148
167
targetThreadId = threadId,
149
168
defaultBubbleState = defaultBubbleState,
150
169
lastAudibleNotification = lastAudibleNotification,
151
- notificationConfigurationChanged = notificationConfigurationChanged,
152
170
alertOverrides = alertOverrides
153
171
)
154
172
@@ -238,8 +256,8 @@ class MessageNotifierV2(context: Application) : MessageNotifier {
238
256
}
239
257
240
258
companion object {
241
- private val TAG = Log .tag(MessageNotifierV2 ::class .java)
242
- private val REMINDER_TIMEOUT = TimeUnit .MINUTES .toMillis(2 )
259
+ val TAG : String = Log .tag(MessageNotifierV2 ::class .java)
260
+ private val REMINDER_TIMEOUT : Long = TimeUnit .MINUTES .toMillis(2 )
243
261
244
262
private fun updateBadge (context : Context , count : Int ) {
245
263
try {
@@ -250,34 +268,49 @@ class MessageNotifierV2(context: Application) : MessageNotifier {
250
268
}
251
269
}
252
270
253
- private fun NotificationManager.cancelOrphanedNotifications (context : Context , state : NotificationStateV2 , stickyNotifications : Set <Int >) {
254
- if (Build .VERSION .SDK_INT < 23 ) {
255
- return
256
- }
271
+ data class StickyThread (val threadId : Long , val notificationId : Int , val earliestTimestamp : Long )
272
+ private data class Reminder (val lastNotified : Long , val count : Int = 0 )
273
+ }
257
274
258
- try {
259
- for (notification: StatusBarNotification in activeNotifications) {
260
- if (notification.id != NotificationIds .MESSAGE_SUMMARY &&
261
- notification.id != KeyCachingService .SERVICE_RUNNING_ID &&
262
- notification.id != IncomingMessageObserver .FOREGROUND_ID &&
263
- notification.id != NotificationIds .PENDING_MESSAGES &&
264
- ! CallNotificationBuilder .isWebRtcNotification(notification.id) &&
265
- ! stickyNotifications.contains(notification.id)
266
- ) {
267
- if (! state.notificationIds.contains(notification.id)) {
268
- Log .d(TAG , " Cancelling orphaned notification: ${notification.id} " )
269
- NotificationCancellationHelper .cancel(context, notification.id)
270
- }
271
- }
272
- }
273
- NotificationCancellationHelper .cancelMessageSummaryIfSoleNotification(context)
274
- } catch (e: Throwable ) {
275
- Log .w(TAG , e)
276
- }
275
+ private fun StatusBarNotification.isMessageNotification (): Boolean {
276
+ return id != NotificationIds .MESSAGE_SUMMARY &&
277
+ id != KeyCachingService .SERVICE_RUNNING_ID &&
278
+ id != IncomingMessageObserver .FOREGROUND_ID &&
279
+ id != NotificationIds .PENDING_MESSAGES &&
280
+ ! CallNotificationBuilder .isWebRtcNotification(id)
281
+ }
282
+
283
+ private fun NotificationManager.getDisplayedNotificationIds (): Result <Set <Int >> {
284
+ if (Build .VERSION .SDK_INT < 23 ) {
285
+ return Result .failure(UnsupportedOperationException (" SDK level too low" ))
277
286
}
278
287
279
- data class StickyThread (val threadId : Long , val notificationId : Int , val earliestTimestamp : Long )
280
- private data class Reminder (val lastNotified : Long , val count : Int = 0 )
288
+ return try {
289
+ Result .success(activeNotifications.filter { it.isMessageNotification() }.map { it.id }.toSet())
290
+ } catch (e: Throwable ) {
291
+ Log .w(MessageNotifierV2 .TAG , e)
292
+ Result .failure(e)
293
+ }
294
+ }
295
+
296
+ private fun NotificationManager.cancelOrphanedNotifications (context : Context , state : NotificationStateV2 , stickyNotifications : Set <Int >) {
297
+ if (Build .VERSION .SDK_INT < 23 ) {
298
+ return
299
+ }
300
+
301
+ try {
302
+ activeNotifications.filter { it.isMessageNotification() && ! stickyNotifications.contains(it.id) }
303
+ .map { it.id }
304
+ .filterNot { state.notificationIds.contains(it) }
305
+ .forEach { id ->
306
+ Log .d(MessageNotifierV2 .TAG , " Cancelling orphaned notification: $id " )
307
+ NotificationCancellationHelper .cancel(context, id)
308
+ }
309
+
310
+ NotificationCancellationHelper .cancelMessageSummaryIfSoleNotification(context)
311
+ } catch (e: Throwable ) {
312
+ Log .w(MessageNotifierV2 .TAG , e)
313
+ }
281
314
}
282
315
283
316
private class CancelableExecutor {
0 commit comments