Address API23 notification issues and update when conversation content changes.

This commit is contained in:
Cody Henthorne
2021-05-04 20:31:29 -04:00
committed by GitHub
parent efc3e7b25d
commit 29a0b86411
8 changed files with 78 additions and 51 deletions

View File

@@ -48,6 +48,7 @@ class MessageNotifierV2(context: Application) : MessageNotifier {
@Volatile private var lastScheduledReminder: Long = 0 @Volatile private var lastScheduledReminder: Long = 0
@Volatile private var previousLockedStatus: Boolean = KeyCachingService.isLocked(context) @Volatile private var previousLockedStatus: Boolean = KeyCachingService.isLocked(context)
@Volatile private var previousPrivacyPreference: NotificationPrivacyPreference = TextSecurePreferences.getNotificationPrivacy(context) @Volatile private var previousPrivacyPreference: NotificationPrivacyPreference = TextSecurePreferences.getNotificationPrivacy(context)
@Volatile private var previousState: NotificationStateV2 = NotificationStateV2.EMPTY
private val threadReminders: MutableMap<Long, Reminder> = ConcurrentHashMap() private val threadReminders: MutableMap<Long, Reminder> = ConcurrentHashMap()
private val stickyThreads: MutableMap<Long, StickyThread> = mutableMapOf() private val stickyThreads: MutableMap<Long, StickyThread> = mutableMapOf()
@@ -167,9 +168,11 @@ class MessageNotifierV2(context: Application) : MessageNotifier {
defaultBubbleState = defaultBubbleState, defaultBubbleState = defaultBubbleState,
lastAudibleNotification = lastAudibleNotification, lastAudibleNotification = lastAudibleNotification,
notificationConfigurationChanged = notificationConfigurationChanged, notificationConfigurationChanged = notificationConfigurationChanged,
alertOverrides = alertOverrides alertOverrides = alertOverrides,
previousState = previousState
) )
previousState = state
lastAudibleNotification = System.currentTimeMillis() lastAudibleNotification = System.currentTimeMillis()
updateReminderTimestamps(context, alertOverrides, threadsThatAlerted) updateReminderTimestamps(context, alertOverrides, threadsThatAlerted)
@@ -281,7 +284,7 @@ private fun StatusBarNotification.isMessageNotification(): Boolean {
} }
private fun NotificationManager.getDisplayedNotificationIds(): Result<Set<Int>> { private fun NotificationManager.getDisplayedNotificationIds(): Result<Set<Int>> {
if (Build.VERSION.SDK_INT < 23) { if (Build.VERSION.SDK_INT < 24) {
return Result.failure(UnsupportedOperationException("SDK level too low")) return Result.failure(UnsupportedOperationException("SDK level too low"))
} }
@@ -294,7 +297,7 @@ private fun NotificationManager.getDisplayedNotificationIds(): Result<Set<Int>>
} }
private fun NotificationManager.cancelOrphanedNotifications(context: Context, state: NotificationStateV2, stickyNotifications: Set<Int>) { private fun NotificationManager.cancelOrphanedNotifications(context: Context, state: NotificationStateV2, stickyNotifications: Set<Int>) {
if (Build.VERSION.SDK_INT < 23) { if (Build.VERSION.SDK_INT < 24) {
return return
} }

View File

@@ -68,7 +68,7 @@ sealed class NotificationBuilder(protected val context: Context) {
abstract fun addMarkAsReadActionActual(state: NotificationStateV2) abstract fun addMarkAsReadActionActual(state: NotificationStateV2)
abstract fun setPriority(priority: Int) abstract fun setPriority(priority: Int)
abstract fun setAlarms(recipient: Recipient?) abstract fun setAlarms(recipient: Recipient?)
abstract fun setTicker(ticker: CharSequence) abstract fun setTicker(ticker: CharSequence?)
abstract fun addTurnOffJoinedNotificationsAction(pendingIntent: PendingIntent) abstract fun addTurnOffJoinedNotificationsAction(pendingIntent: PendingIntent)
abstract fun setAutoCancel(autoCancel: Boolean) abstract fun setAutoCancel(autoCancel: Boolean)
abstract fun build(): Notification abstract fun build(): Notification
@@ -100,8 +100,8 @@ sealed class NotificationBuilder(protected val context: Context) {
} }
} }
fun setWhen(notificationItem: NotificationItemV2) { fun setWhen(notificationItem: NotificationItemV2?) {
if (notificationItem.timestamp != 0L) { if (notificationItem != null && notificationItem.timestamp != 0L) {
setWhen(notificationItem.timestamp) setWhen(notificationItem.timestamp)
} }
} }
@@ -136,12 +136,12 @@ sealed class NotificationBuilder(protected val context: Context) {
} }
} }
fun setSummaryContentText(recipient: Recipient) { fun setSummaryContentText(recipient: Recipient?) {
if (privacy.isDisplayContact) { if (privacy.isDisplayContact && recipient != null) {
setContentText(context.getString(R.string.MessageNotifier_most_recent_from_s, recipient.getDisplayName(context))) setContentText(context.getString(R.string.MessageNotifier_most_recent_from_s, recipient.getDisplayName(context)))
} }
recipient.notificationChannel?.let { channel -> setChannelId(channel) } recipient?.notificationChannel?.let { channel -> setChannelId(channel) }
} }
fun setLights() { fun setLights() {
@@ -244,8 +244,8 @@ sealed class NotificationBuilder(protected val context: Context) {
val self: PersonCompat = PersonCompat.Builder() val self: PersonCompat = PersonCompat.Builder()
.setBot(false) .setBot(false)
.setName(Recipient.self().getDisplayName(context)) .setName(if (includeShortcut) Recipient.self().getDisplayName(context) else context.getString(R.string.SingleRecipientNotificationBuilder_you))
.setIcon(Recipient.self().getContactDrawable(context).toLargeBitmap(context).toIconCompat()) .setIcon(if (includeShortcut) Recipient.self().getContactDrawable(context).toLargeBitmap(context).toIconCompat() else null)
.build() .build()
val messagingStyle: NotificationCompat.MessagingStyle = NotificationCompat.MessagingStyle(self) val messagingStyle: NotificationCompat.MessagingStyle = NotificationCompat.MessagingStyle(self)
@@ -332,7 +332,7 @@ sealed class NotificationBuilder(protected val context: Context) {
} }
override fun setGroup(group: String) { override fun setGroup(group: String) {
if (Build.VERSION.SDK_INT < 23) { if (Build.VERSION.SDK_INT < 24) {
return return
} }
@@ -340,7 +340,7 @@ sealed class NotificationBuilder(protected val context: Context) {
} }
override fun setGroupAlertBehavior(behavior: Int) { override fun setGroupAlertBehavior(behavior: Int) {
if (Build.VERSION.SDK_INT < 23) { if (Build.VERSION.SDK_INT < 24) {
return return
} }
@@ -375,7 +375,7 @@ sealed class NotificationBuilder(protected val context: Context) {
builder.setContentText(contentText) builder.setContentText(contentText)
} }
override fun setTicker(ticker: CharSequence) { override fun setTicker(ticker: CharSequence?) {
builder.setTicker(ticker) builder.setTicker(ticker)
} }
@@ -489,8 +489,8 @@ sealed class NotificationBuilder(protected val context: Context) {
override fun addMessagesActual(conversation: NotificationConversation, includeShortcut: Boolean) { override fun addMessagesActual(conversation: NotificationConversation, includeShortcut: Boolean) {
val self: Person = Person.Builder() val self: Person = Person.Builder()
.setBot(false) .setBot(false)
.setName(Recipient.self().getDisplayName(context)) .setName(if (includeShortcut) Recipient.self().getDisplayName(context) else context.getString(R.string.SingleRecipientNotificationBuilder_you))
.setIcon(Recipient.self().getContactDrawable(context).toLargeBitmap(context).toIcon()) .setIcon(if (includeShortcut) Recipient.self().getContactDrawable(context).toLargeBitmap(context).toIcon() else null)
.build() .build()
val messagingStyle: Notification.MessagingStyle = Notification.MessagingStyle(self) val messagingStyle: Notification.MessagingStyle = Notification.MessagingStyle(self)
@@ -598,7 +598,7 @@ sealed class NotificationBuilder(protected val context: Context) {
builder.setContentText(contentText) builder.setContentText(contentText)
} }
override fun setTicker(ticker: CharSequence) { override fun setTicker(ticker: CharSequence?) {
builder.setTicker(ticker) builder.setTicker(ticker)
} }

View File

@@ -88,14 +88,10 @@ data class NotificationConversation(
} }
fun getConversationTitle(context: Context): CharSequence? { fun getConversationTitle(context: Context): CharSequence? {
if (isGroup) { if (TextSecurePreferences.getNotificationPrivacy(context).isDisplayContact) {
return if (TextSecurePreferences.getNotificationPrivacy(context).isDisplayContact) { return if (isGroup) recipient.getDisplayName(context) else null
recipient.getDisplayName(context)
} else {
context.getString(R.string.SingleRecipientNotificationBuilder_signal)
} }
} return context.getString(R.string.SingleRecipientNotificationBuilder_signal)
return null
} }
fun getWhen(): Long { fun getWhen(): Long {
@@ -114,6 +110,14 @@ data class NotificationConversation(
} }
} }
fun hasSameContent(other: NotificationConversation?): Boolean {
if (other == null) {
return false
}
return messageCount == other.messageCount && notificationItems.zip(other.notificationItems).all { (item, otherItem) -> item.hasSameContent(otherItem) }
}
fun getPendingIntent(context: Context): PendingIntent { fun getPendingIntent(context: Context): PendingIntent {
val intent: Intent = ConversationIntents.createBuilder(context, recipient.id, threadId) val intent: Intent = ConversationIntents.createBuilder(context, recipient.id, threadId)
.withStartingPosition(mostRecentNotification.getStartingPosition(context)) .withStartingPosition(mostRecentNotification.getStartingPosition(context))

View File

@@ -1,10 +1,12 @@
package org.thoughtcrime.securesms.notifications.v2 package org.thoughtcrime.securesms.notifications.v2
import android.app.NotificationManager
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.graphics.Bitmap import android.graphics.Bitmap
import android.graphics.drawable.Drawable import android.graphics.drawable.Drawable
import android.net.Uri import android.net.Uri
import android.os.Build
import com.bumptech.glide.load.MultiTransformation import com.bumptech.glide.load.MultiTransformation
import com.bumptech.glide.load.Transformation import com.bumptech.glide.load.Transformation
import com.bumptech.glide.load.engine.DiskCacheStrategy import com.bumptech.glide.load.engine.DiskCacheStrategy
@@ -17,6 +19,7 @@ import org.thoughtcrime.securesms.contacts.avatars.ProfileContactPhoto
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies import org.thoughtcrime.securesms.dependencies.ApplicationDependencies
import org.thoughtcrime.securesms.mms.DecryptableStreamUriLoader.DecryptableUri import org.thoughtcrime.securesms.mms.DecryptableStreamUriLoader.DecryptableUri
import org.thoughtcrime.securesms.mms.GlideApp import org.thoughtcrime.securesms.mms.GlideApp
import org.thoughtcrime.securesms.notifications.NotificationIds
import org.thoughtcrime.securesms.recipients.Recipient import org.thoughtcrime.securesms.recipients.Recipient
import org.thoughtcrime.securesms.util.BitmapUtil import org.thoughtcrime.securesms.util.BitmapUtil
import org.thoughtcrime.securesms.util.BlurTransformation import org.thoughtcrime.securesms.util.BlurTransformation
@@ -84,3 +87,13 @@ fun Intent.makeUniqueToPreventMerging(): Intent {
fun Recipient.getFallback(context: Context): FallbackContactPhoto { fun Recipient.getFallback(context: Context): FallbackContactPhoto {
return GeneratedContactPhoto(getDisplayName(context), R.drawable.ic_profile_outline_40) return GeneratedContactPhoto(getDisplayName(context), R.drawable.ic_profile_outline_40)
} }
fun NotificationManager.isDisplayingSummaryNotification(): Boolean {
if (Build.VERSION.SDK_INT > 23) {
try {
return activeNotifications.any { notification -> notification.id == NotificationIds.MESSAGE_SUMMARY }
} catch (e: Throwable) {
}
}
return false
}

View File

@@ -2,7 +2,6 @@ package org.thoughtcrime.securesms.notifications.v2
import android.annotation.TargetApi import android.annotation.TargetApi
import android.app.Notification import android.app.Notification
import android.app.NotificationManager
import android.app.PendingIntent import android.app.PendingIntent
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
@@ -46,15 +45,16 @@ object NotificationFactory {
defaultBubbleState: BubbleUtil.BubbleState, defaultBubbleState: BubbleUtil.BubbleState,
lastAudibleNotification: Long, lastAudibleNotification: Long,
notificationConfigurationChanged: Boolean, notificationConfigurationChanged: Boolean,
alertOverrides: Set<Long> alertOverrides: Set<Long>,
previousState: NotificationStateV2
): Set<Long> { ): Set<Long> {
if (state.isEmpty) { if (state.isEmpty) {
Log.d(TAG, "State is empty, bailing") Log.d(TAG, "State is empty, bailing")
return emptySet() return emptySet()
} }
val nonVisibleThreadCount = state.conversations.count { it.threadId != visibleThreadId } val nonVisibleThreadCount: Int = state.conversations.count { it.threadId != visibleThreadId }
return if (Build.VERSION.SDK_INT < 23) { return if (Build.VERSION.SDK_INT < 24) {
notify19( notify19(
context = context, context = context,
state = state, state = state,
@@ -66,7 +66,7 @@ object NotificationFactory {
nonVisibleThreadCount = nonVisibleThreadCount nonVisibleThreadCount = nonVisibleThreadCount
) )
} else { } else {
notify23( notify24(
context = context, context = context,
state = state, state = state,
visibleThreadId = visibleThreadId, visibleThreadId = visibleThreadId,
@@ -75,7 +75,8 @@ object NotificationFactory {
lastAudibleNotification = lastAudibleNotification, lastAudibleNotification = lastAudibleNotification,
notificationConfigurationChanged = notificationConfigurationChanged, notificationConfigurationChanged = notificationConfigurationChanged,
alertOverrides = alertOverrides, alertOverrides = alertOverrides,
nonVisibleThreadCount = nonVisibleThreadCount nonVisibleThreadCount = nonVisibleThreadCount,
previousState = previousState
) )
} }
} }
@@ -121,8 +122,8 @@ object NotificationFactory {
return threadsThatNewlyAlerted return threadsThatNewlyAlerted
} }
@TargetApi(23) @TargetApi(24)
private fun notify23( private fun notify24(
context: Context, context: Context,
state: NotificationStateV2, state: NotificationStateV2,
visibleThreadId: Long, visibleThreadId: Long,
@@ -131,7 +132,8 @@ object NotificationFactory {
lastAudibleNotification: Long, lastAudibleNotification: Long,
notificationConfigurationChanged: Boolean, notificationConfigurationChanged: Boolean,
alertOverrides: Set<Long>, alertOverrides: Set<Long>,
nonVisibleThreadCount: Int nonVisibleThreadCount: Int,
previousState: NotificationStateV2
): Set<Long> { ): Set<Long> {
val threadsThatNewlyAlerted: MutableSet<Long> = mutableSetOf() val threadsThatNewlyAlerted: MutableSet<Long> = mutableSetOf()
@@ -139,7 +141,7 @@ object NotificationFactory {
if (conversation.threadId == visibleThreadId && conversation.hasNewNotifications()) { if (conversation.threadId == visibleThreadId && conversation.hasNewNotifications()) {
Log.internal().i(TAG, "Thread is visible, notifying in thread. notificationId: ${conversation.notificationId}") Log.internal().i(TAG, "Thread is visible, notifying in thread. notificationId: ${conversation.notificationId}")
notifyInThread(context, conversation.recipient, lastAudibleNotification) notifyInThread(context, conversation.recipient, lastAudibleNotification)
} else if (notificationConfigurationChanged || conversation.hasNewNotifications() || alertOverrides.contains(conversation.threadId)) { } else if (notificationConfigurationChanged || conversation.hasNewNotifications() || alertOverrides.contains(conversation.threadId) || !conversation.hasSameContent(previousState.getConversation(conversation.threadId))) {
if (conversation.hasNewNotifications()) { if (conversation.hasNewNotifications()) {
threadsThatNewlyAlerted += conversation.threadId threadsThatNewlyAlerted += conversation.threadId
} }
@@ -206,7 +208,7 @@ object NotificationFactory {
builder.addTurnOffJoinedNotificationsAction(conversation.getTurnOffJoinedNotificationsIntent(context)) builder.addTurnOffJoinedNotificationsAction(conversation.getTurnOffJoinedNotificationsIntent(context))
} }
val notificationId: Int = if (Build.VERSION.SDK_INT < 23) NotificationIds.MESSAGE_SUMMARY else conversation.notificationId val notificationId: Int = if (Build.VERSION.SDK_INT < 24) NotificationIds.MESSAGE_SUMMARY else conversation.notificationId
NotificationManagerCompat.from(context).safelyNotify(context, conversation.recipient, notificationId, builder.build()) NotificationManagerCompat.from(context).safelyNotify(context, conversation.recipient, notificationId, builder.build())
} }
@@ -240,7 +242,7 @@ object NotificationFactory {
setPriority(TextSecurePreferences.getNotificationPriority(context)) setPriority(TextSecurePreferences.getNotificationPriority(context))
setLights() setLights()
setAlarms(state.mostRecentSender) setAlarms(state.mostRecentSender)
setTicker(state.mostRecentNotification.getStyledPrimaryText(context, true)) setTicker(state.mostRecentNotification?.getStyledPrimaryText(context, true))
} }
Log.d(TAG, "showing summary notification") Log.d(TAG, "showing summary notification")
@@ -313,16 +315,6 @@ object NotificationFactory {
NotificationManagerCompat.from(context).safelyNotify(context, recipient, threadId.toInt(), builder.build()) NotificationManagerCompat.from(context).safelyNotify(context, recipient, threadId.toInt(), builder.build())
} }
private fun NotificationManager.isDisplayingSummaryNotification(): Boolean {
if (Build.VERSION.SDK_INT >= 23) {
try {
return activeNotifications.any { notification -> notification.id == NotificationIds.MESSAGE_SUMMARY }
} catch (e: Throwable) {
}
}
return false
}
private fun NotificationManagerCompat.safelyNotify(context: Context, threadRecipient: Recipient?, notificationId: Int, notification: Notification) { private fun NotificationManagerCompat.safelyNotify(context: Context, threadRecipient: Recipient?, notificationId: Int, notification: Notification) {
try { try {
notify(notificationId, notification) notify(notificationId, notification)

View File

@@ -90,7 +90,7 @@ sealed class NotificationItemV2(val threadRecipient: Recipient, protected val re
return if (TextSecurePreferences.getNotificationPrivacy(context).isDisplayContact) { return if (TextSecurePreferences.getNotificationPrivacy(context).isDisplayContact) {
individualRecipient.getDisplayName(context) individualRecipient.getDisplayName(context)
} else { } else {
"" context.getString(R.string.SingleRecipientNotificationBuilder_signal)
} }
} }
@@ -133,6 +133,16 @@ sealed class NotificationItemV2(val threadRecipient: Recipient, protected val re
} }
} }
fun hasSameContent(other: NotificationItemV2): Boolean {
return timestamp == other.timestamp &&
id == other.id &&
isMms == other.isMms &&
individualRecipient == other.individualRecipient &&
individualRecipient.hasSameContent(other.individualRecipient) &&
slideDeck?.thumbnailSlide?.isInProgress == other.slideDeck?.thumbnailSlide?.isInProgress &&
record.isRemoteDelete == other.record.isRemoteDelete
}
private fun CharSequence?.trimToDisplayLength(): CharSequence { private fun CharSequence?.trimToDisplayLength(): CharSequence {
val text: CharSequence = this ?: "" val text: CharSequence = this ?: ""
return if (text.length <= AbstractNotificationBuilder.MAX_DISPLAY_LENGTH) { return if (text.length <= AbstractNotificationBuilder.MAX_DISPLAY_LENGTH) {

View File

@@ -33,16 +33,20 @@ data class NotificationStateV2(val conversations: List<NotificationConversation>
.toSet() .toSet()
} }
val mostRecentNotification: NotificationItemV2 val mostRecentNotification: NotificationItemV2?
get() = notificationItems.last() get() = notificationItems.lastOrNull()
val mostRecentSender: Recipient val mostRecentSender: Recipient?
get() = mostRecentNotification.individualRecipient get() = mostRecentNotification?.individualRecipient
fun getNonVisibleConversation(visibleThreadId: Long): List<NotificationConversation> { fun getNonVisibleConversation(visibleThreadId: Long): List<NotificationConversation> {
return conversations.filterNot { it.threadId == visibleThreadId } return conversations.filterNot { it.threadId == visibleThreadId }
} }
fun getConversation(threadId: Long): NotificationConversation? {
return conversations.firstOrNull { it.threadId == threadId }
}
fun getDeleteIntent(context: Context): PendingIntent? { fun getDeleteIntent(context: Context): PendingIntent? {
val ids = LongArray(messageCount) val ids = LongArray(messageCount)
val mms = BooleanArray(ids.size) val mms = BooleanArray(ids.size)

View File

@@ -1751,6 +1751,7 @@
<string name="SingleRecipientNotificationBuilder_signal">Signal</string> <string name="SingleRecipientNotificationBuilder_signal">Signal</string>
<string name="SingleRecipientNotificationBuilder_new_message">New message</string> <string name="SingleRecipientNotificationBuilder_new_message">New message</string>
<string name="SingleRecipientNotificationBuilder_message_request">Message request</string> <string name="SingleRecipientNotificationBuilder_message_request">Message request</string>
<string name="SingleRecipientNotificationBuilder_you">You</string>
<!-- ThumbnailView --> <!-- ThumbnailView -->
<string name="ThumbnailView_Play_video_description">Play video</string> <string name="ThumbnailView_Play_video_description">Play video</string>