Skip to content

Commit bd2a1d5

Browse files
cody-signalalex-signal
authored andcommitted
Add support for lower APIs to new notification system.
1 parent ab44d60 commit bd2a1d5

File tree

10 files changed

+236
-110
lines changed

10 files changed

+236
-110
lines changed

app/build.gradle

+4
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,10 @@ android {
8080
flavorDimensions 'distribution', 'environment'
8181
useLibrary 'org.apache.http.legacy'
8282

83+
kotlinOptions {
84+
freeCompilerArgs = ["-Xallow-result-return-type"]
85+
}
86+
8387
dexOptions {
8488
javaMaxHeapSize "4g"
8589
}

app/src/main/java/org/thoughtcrime/securesms/notifications/NotificationCancellationHelper.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ public static void cancelAllMessageNotifications(@NonNull Context context, @NonN
7979
}
8080

8181
public static void cancelMessageSummaryIfSoleNotification(@NonNull Context context) {
82-
if (Build.VERSION.SDK_INT >= 23) {
82+
if (Build.VERSION.SDK_INT > 23) {
8383
try {
8484
NotificationManager notifications = ServiceUtil.getNotificationManager(context);
8585
StatusBarNotification[] activeNotifications = notifications.getActiveNotifications();

app/src/main/java/org/thoughtcrime/securesms/notifications/v2/MessageNotifierV2.kt

+62-29
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import me.leolin.shortcutbadger.ShortcutBadger
1414
import org.signal.core.util.logging.Log
1515
import org.thoughtcrime.securesms.R
1616
import org.thoughtcrime.securesms.database.DatabaseFactory
17+
import org.thoughtcrime.securesms.database.MessageDatabase
1718
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies
1819
import org.thoughtcrime.securesms.messages.IncomingMessageObserver
1920
import org.thoughtcrime.securesms.notifications.DefaultMessageNotifier
@@ -29,6 +30,7 @@ import org.thoughtcrime.securesms.util.ServiceUtil
2930
import org.thoughtcrime.securesms.util.TextSecurePreferences
3031
import org.thoughtcrime.securesms.webrtc.CallNotificationBuilder
3132
import org.whispersystems.signalservice.internal.util.Util
33+
import java.lang.UnsupportedOperationException
3234
import java.util.concurrent.ConcurrentHashMap
3335
import java.util.concurrent.Executor
3436
import java.util.concurrent.Executors
@@ -125,9 +127,26 @@ class MessageNotifierV2(context: Application) : MessageNotifier {
125127
}
126128

127129
Log.internal().i(TAG, "sticky thread: $stickyThreads")
128-
val state: NotificationStateV2 = NotificationStateProvider.constructNotificationState(context, stickyThreads)
130+
var state: NotificationStateV2 = NotificationStateProvider.constructNotificationState(context, stickyThreads)
129131
Log.internal().i(TAG, "state: $state")
130132

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+
131150
val retainStickyThreadIds: Set<Long> = state.getThreadsWithMostRecentNotificationFromSelf()
132151
stickyThreads.keys.retainAll { retainStickyThreadIds.contains(it) }
133152

@@ -148,7 +167,6 @@ class MessageNotifierV2(context: Application) : MessageNotifier {
148167
targetThreadId = threadId,
149168
defaultBubbleState = defaultBubbleState,
150169
lastAudibleNotification = lastAudibleNotification,
151-
notificationConfigurationChanged = notificationConfigurationChanged,
152170
alertOverrides = alertOverrides
153171
)
154172

@@ -238,8 +256,8 @@ class MessageNotifierV2(context: Application) : MessageNotifier {
238256
}
239257

240258
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)
243261

244262
private fun updateBadge(context: Context, count: Int) {
245263
try {
@@ -250,34 +268,49 @@ class MessageNotifierV2(context: Application) : MessageNotifier {
250268
}
251269
}
252270

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+
}
257274

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"))
277286
}
278287

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+
}
281314
}
282315

283316
private class CancelableExecutor {

app/src/main/java/org/thoughtcrime/securesms/notifications/v2/NotificationBuilder.kt

+8-9
Original file line numberDiff line numberDiff line change
@@ -332,10 +332,18 @@ sealed class NotificationBuilder(protected val context: Context) {
332332
}
333333

334334
override fun setGroup(group: String) {
335+
if (Build.VERSION.SDK_INT < 23) {
336+
return
337+
}
338+
335339
builder.setGroup(group)
336340
}
337341

338342
override fun setGroupAlertBehavior(behavior: Int) {
343+
if (Build.VERSION.SDK_INT < 23) {
344+
return
345+
}
346+
339347
builder.setGroupAlertBehavior(behavior)
340348
}
341349

@@ -479,15 +487,6 @@ sealed class NotificationBuilder(protected val context: Context) {
479487
}
480488

481489
override fun addMessagesActual(conversation: NotificationConversation, includeShortcut: Boolean) {
482-
val bigPictureUri: Uri? = conversation.getSlideBigPictureUri(context)
483-
if (bigPictureUri != null) {
484-
builder.style = Notification.BigPictureStyle()
485-
.bigPicture(bigPictureUri.toBitmap(context, BIG_PICTURE_DIMEN))
486-
.setSummaryText(conversation.getContentText(context))
487-
.bigLargeIcon(null as Bitmap?)
488-
return
489-
}
490-
491490
val self: Person = Person.Builder()
492491
.setBot(false)
493492
.setName(Recipient.self().getDisplayName(context))

app/src/main/java/org/thoughtcrime/securesms/notifications/v2/NotificationConversation.kt

+2-20
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ package org.thoughtcrime.securesms.notifications.v2
33
import android.app.PendingIntent
44
import android.content.Context
55
import android.content.Intent
6-
import android.graphics.Bitmap
76
import android.graphics.drawable.Drawable
87
import android.net.Uri
98
import android.text.SpannableStringBuilder
@@ -25,13 +24,11 @@ import org.thoughtcrime.securesms.service.KeyCachingService
2524
import org.thoughtcrime.securesms.util.TextSecurePreferences
2625
import org.thoughtcrime.securesms.util.Util
2726

28-
private const val LARGE_ICON_DIMEN = 250
29-
3027
/**
3128
* Encapsulate all the notifications for a given conversation (thread) and the top
3229
* level information about said conversation.
3330
*/
34-
class NotificationConversation(
31+
data class NotificationConversation(
3532
val recipient: Recipient,
3633
val threadId: Long,
3734
val notificationItems: List<NotificationItemV2>
@@ -51,18 +48,7 @@ class NotificationConversation(
5148
}
5249
}
5350

54-
fun getLargeIcon(context: Context): Bitmap? {
55-
if (TextSecurePreferences.getNotificationPrivacy(context).isDisplayMessage) {
56-
val largeIconUri: Uri? = getSlideLargeIcon()
57-
if (largeIconUri != null) {
58-
return largeIconUri.toBitmap(context, LARGE_ICON_DIMEN)
59-
}
60-
}
61-
62-
return getContactLargeIcon(context).toLargeBitmap(context)
63-
}
64-
65-
private fun getContactLargeIcon(context: Context): Drawable? {
51+
fun getContactLargeIcon(context: Context): Drawable? {
6652
return if (TextSecurePreferences.getNotificationPrivacy(context).isDisplayContact) {
6753
recipient.getContactDrawable(context)
6854
} else {
@@ -78,10 +64,6 @@ class NotificationConversation(
7864
}
7965
}
8066

81-
private fun getSlideLargeIcon(): Uri? {
82-
return if (notificationItems.size == 1) mostRecentNotification.getLargeIconUri() else null
83-
}
84-
8567
fun getSlideBigPictureUri(context: Context): Uri? {
8668
return if (notificationItems.size == 1 && TextSecurePreferences.getNotificationPrivacy(context).isDisplayMessage && !KeyCachingService.isLocked(context)) {
8769
mostRecentNotification.getBigPictureUri()

0 commit comments

Comments
 (0)