kopia lustrzana https://github.com/ryukoposting/Signal-Android
Add support for lower APIs to new notification system.
rodzic
ab44d608d2
commit
bd2a1d5574
|
@ -80,6 +80,10 @@ android {
|
|||
flavorDimensions 'distribution', 'environment'
|
||||
useLibrary 'org.apache.http.legacy'
|
||||
|
||||
kotlinOptions {
|
||||
freeCompilerArgs = ["-Xallow-result-return-type"]
|
||||
}
|
||||
|
||||
dexOptions {
|
||||
javaMaxHeapSize "4g"
|
||||
}
|
||||
|
|
|
@ -79,7 +79,7 @@ public final class NotificationCancellationHelper {
|
|||
}
|
||||
|
||||
public static void cancelMessageSummaryIfSoleNotification(@NonNull Context context) {
|
||||
if (Build.VERSION.SDK_INT >= 23) {
|
||||
if (Build.VERSION.SDK_INT > 23) {
|
||||
try {
|
||||
NotificationManager notifications = ServiceUtil.getNotificationManager(context);
|
||||
StatusBarNotification[] activeNotifications = notifications.getActiveNotifications();
|
||||
|
|
|
@ -14,6 +14,7 @@ import me.leolin.shortcutbadger.ShortcutBadger
|
|||
import org.signal.core.util.logging.Log
|
||||
import org.thoughtcrime.securesms.R
|
||||
import org.thoughtcrime.securesms.database.DatabaseFactory
|
||||
import org.thoughtcrime.securesms.database.MessageDatabase
|
||||
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies
|
||||
import org.thoughtcrime.securesms.messages.IncomingMessageObserver
|
||||
import org.thoughtcrime.securesms.notifications.DefaultMessageNotifier
|
||||
|
@ -29,6 +30,7 @@ import org.thoughtcrime.securesms.util.ServiceUtil
|
|||
import org.thoughtcrime.securesms.util.TextSecurePreferences
|
||||
import org.thoughtcrime.securesms.webrtc.CallNotificationBuilder
|
||||
import org.whispersystems.signalservice.internal.util.Util
|
||||
import java.lang.UnsupportedOperationException
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
import java.util.concurrent.Executor
|
||||
import java.util.concurrent.Executors
|
||||
|
@ -125,9 +127,26 @@ class MessageNotifierV2(context: Application) : MessageNotifier {
|
|||
}
|
||||
|
||||
Log.internal().i(TAG, "sticky thread: $stickyThreads")
|
||||
val state: NotificationStateV2 = NotificationStateProvider.constructNotificationState(context, stickyThreads)
|
||||
var state: NotificationStateV2 = NotificationStateProvider.constructNotificationState(context, stickyThreads)
|
||||
Log.internal().i(TAG, "state: $state")
|
||||
|
||||
val displayedNotifications: Set<Int>? = ServiceUtil.getNotificationManager(context).getDisplayedNotificationIds().getOrNull()
|
||||
if (displayedNotifications != null) {
|
||||
val cleanedUpThreadIds: MutableSet<Long> = mutableSetOf()
|
||||
state.conversations.filterNot { it.hasNewNotifications() || displayedNotifications.contains(it.notificationId) }
|
||||
.forEach { conversation ->
|
||||
cleanedUpThreadIds += conversation.threadId
|
||||
conversation.notificationItems.forEach { item ->
|
||||
val messageDatabase: MessageDatabase = if (item.isMms) DatabaseFactory.getMmsDatabase(context) else DatabaseFactory.getSmsDatabase(context)
|
||||
messageDatabase.markAsNotified(item.id)
|
||||
}
|
||||
}
|
||||
if (cleanedUpThreadIds.isNotEmpty()) {
|
||||
Log.i(TAG, "Cleaned up ${cleanedUpThreadIds.size} thread(s) with dangling notifications")
|
||||
state = NotificationStateV2(state.conversations.filterNot { cleanedUpThreadIds.contains(it.threadId) })
|
||||
}
|
||||
}
|
||||
|
||||
val retainStickyThreadIds: Set<Long> = state.getThreadsWithMostRecentNotificationFromSelf()
|
||||
stickyThreads.keys.retainAll { retainStickyThreadIds.contains(it) }
|
||||
|
||||
|
@ -148,7 +167,6 @@ class MessageNotifierV2(context: Application) : MessageNotifier {
|
|||
targetThreadId = threadId,
|
||||
defaultBubbleState = defaultBubbleState,
|
||||
lastAudibleNotification = lastAudibleNotification,
|
||||
notificationConfigurationChanged = notificationConfigurationChanged,
|
||||
alertOverrides = alertOverrides
|
||||
)
|
||||
|
||||
|
@ -238,8 +256,8 @@ class MessageNotifierV2(context: Application) : MessageNotifier {
|
|||
}
|
||||
|
||||
companion object {
|
||||
private val TAG = Log.tag(MessageNotifierV2::class.java)
|
||||
private val REMINDER_TIMEOUT = TimeUnit.MINUTES.toMillis(2)
|
||||
val TAG: String = Log.tag(MessageNotifierV2::class.java)
|
||||
private val REMINDER_TIMEOUT: Long = TimeUnit.MINUTES.toMillis(2)
|
||||
|
||||
private fun updateBadge(context: Context, count: Int) {
|
||||
try {
|
||||
|
@ -250,36 +268,51 @@ class MessageNotifierV2(context: Application) : MessageNotifier {
|
|||
}
|
||||
}
|
||||
|
||||
private fun NotificationManager.cancelOrphanedNotifications(context: Context, state: NotificationStateV2, stickyNotifications: Set<Int>) {
|
||||
if (Build.VERSION.SDK_INT < 23) {
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
for (notification: StatusBarNotification in activeNotifications) {
|
||||
if (notification.id != NotificationIds.MESSAGE_SUMMARY &&
|
||||
notification.id != KeyCachingService.SERVICE_RUNNING_ID &&
|
||||
notification.id != IncomingMessageObserver.FOREGROUND_ID &&
|
||||
notification.id != NotificationIds.PENDING_MESSAGES &&
|
||||
!CallNotificationBuilder.isWebRtcNotification(notification.id) &&
|
||||
!stickyNotifications.contains(notification.id)
|
||||
) {
|
||||
if (!state.notificationIds.contains(notification.id)) {
|
||||
Log.d(TAG, "Cancelling orphaned notification: ${notification.id}")
|
||||
NotificationCancellationHelper.cancel(context, notification.id)
|
||||
}
|
||||
}
|
||||
}
|
||||
NotificationCancellationHelper.cancelMessageSummaryIfSoleNotification(context)
|
||||
} catch (e: Throwable) {
|
||||
Log.w(TAG, e)
|
||||
}
|
||||
}
|
||||
|
||||
data class StickyThread(val threadId: Long, val notificationId: Int, val earliestTimestamp: Long)
|
||||
private data class Reminder(val lastNotified: Long, val count: Int = 0)
|
||||
}
|
||||
|
||||
private fun StatusBarNotification.isMessageNotification(): Boolean {
|
||||
return id != NotificationIds.MESSAGE_SUMMARY &&
|
||||
id != KeyCachingService.SERVICE_RUNNING_ID &&
|
||||
id != IncomingMessageObserver.FOREGROUND_ID &&
|
||||
id != NotificationIds.PENDING_MESSAGES &&
|
||||
!CallNotificationBuilder.isWebRtcNotification(id)
|
||||
}
|
||||
|
||||
private fun NotificationManager.getDisplayedNotificationIds(): Result<Set<Int>> {
|
||||
if (Build.VERSION.SDK_INT < 23) {
|
||||
return Result.failure(UnsupportedOperationException("SDK level too low"))
|
||||
}
|
||||
|
||||
return try {
|
||||
Result.success(activeNotifications.filter { it.isMessageNotification() }.map { it.id }.toSet())
|
||||
} catch (e: Throwable) {
|
||||
Log.w(MessageNotifierV2.TAG, e)
|
||||
Result.failure(e)
|
||||
}
|
||||
}
|
||||
|
||||
private fun NotificationManager.cancelOrphanedNotifications(context: Context, state: NotificationStateV2, stickyNotifications: Set<Int>) {
|
||||
if (Build.VERSION.SDK_INT < 23) {
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
activeNotifications.filter { it.isMessageNotification() && !stickyNotifications.contains(it.id) }
|
||||
.map { it.id }
|
||||
.filterNot { state.notificationIds.contains(it) }
|
||||
.forEach { id ->
|
||||
Log.d(MessageNotifierV2.TAG, "Cancelling orphaned notification: $id")
|
||||
NotificationCancellationHelper.cancel(context, id)
|
||||
}
|
||||
|
||||
NotificationCancellationHelper.cancelMessageSummaryIfSoleNotification(context)
|
||||
} catch (e: Throwable) {
|
||||
Log.w(MessageNotifierV2.TAG, e)
|
||||
}
|
||||
}
|
||||
|
||||
private class CancelableExecutor {
|
||||
private val executor: Executor = Executors.newSingleThreadExecutor()
|
||||
private val tasks: MutableSet<DelayedNotification> = mutableSetOf()
|
||||
|
|
|
@ -332,10 +332,18 @@ sealed class NotificationBuilder(protected val context: Context) {
|
|||
}
|
||||
|
||||
override fun setGroup(group: String) {
|
||||
if (Build.VERSION.SDK_INT < 23) {
|
||||
return
|
||||
}
|
||||
|
||||
builder.setGroup(group)
|
||||
}
|
||||
|
||||
override fun setGroupAlertBehavior(behavior: Int) {
|
||||
if (Build.VERSION.SDK_INT < 23) {
|
||||
return
|
||||
}
|
||||
|
||||
builder.setGroupAlertBehavior(behavior)
|
||||
}
|
||||
|
||||
|
@ -479,15 +487,6 @@ sealed class NotificationBuilder(protected val context: Context) {
|
|||
}
|
||||
|
||||
override fun addMessagesActual(conversation: NotificationConversation, includeShortcut: Boolean) {
|
||||
val bigPictureUri: Uri? = conversation.getSlideBigPictureUri(context)
|
||||
if (bigPictureUri != null) {
|
||||
builder.style = Notification.BigPictureStyle()
|
||||
.bigPicture(bigPictureUri.toBitmap(context, BIG_PICTURE_DIMEN))
|
||||
.setSummaryText(conversation.getContentText(context))
|
||||
.bigLargeIcon(null as Bitmap?)
|
||||
return
|
||||
}
|
||||
|
||||
val self: Person = Person.Builder()
|
||||
.setBot(false)
|
||||
.setName(Recipient.self().getDisplayName(context))
|
||||
|
|
|
@ -3,7 +3,6 @@ package org.thoughtcrime.securesms.notifications.v2
|
|||
import android.app.PendingIntent
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.net.Uri
|
||||
import android.text.SpannableStringBuilder
|
||||
|
@ -25,13 +24,11 @@ import org.thoughtcrime.securesms.service.KeyCachingService
|
|||
import org.thoughtcrime.securesms.util.TextSecurePreferences
|
||||
import org.thoughtcrime.securesms.util.Util
|
||||
|
||||
private const val LARGE_ICON_DIMEN = 250
|
||||
|
||||
/**
|
||||
* Encapsulate all the notifications for a given conversation (thread) and the top
|
||||
* level information about said conversation.
|
||||
*/
|
||||
class NotificationConversation(
|
||||
data class NotificationConversation(
|
||||
val recipient: Recipient,
|
||||
val threadId: Long,
|
||||
val notificationItems: List<NotificationItemV2>
|
||||
|
@ -51,18 +48,7 @@ class NotificationConversation(
|
|||
}
|
||||
}
|
||||
|
||||
fun getLargeIcon(context: Context): Bitmap? {
|
||||
if (TextSecurePreferences.getNotificationPrivacy(context).isDisplayMessage) {
|
||||
val largeIconUri: Uri? = getSlideLargeIcon()
|
||||
if (largeIconUri != null) {
|
||||
return largeIconUri.toBitmap(context, LARGE_ICON_DIMEN)
|
||||
}
|
||||
}
|
||||
|
||||
return getContactLargeIcon(context).toLargeBitmap(context)
|
||||
}
|
||||
|
||||
private fun getContactLargeIcon(context: Context): Drawable? {
|
||||
fun getContactLargeIcon(context: Context): Drawable? {
|
||||
return if (TextSecurePreferences.getNotificationPrivacy(context).isDisplayContact) {
|
||||
recipient.getContactDrawable(context)
|
||||
} else {
|
||||
|
@ -78,10 +64,6 @@ class NotificationConversation(
|
|||
}
|
||||
}
|
||||
|
||||
private fun getSlideLargeIcon(): Uri? {
|
||||
return if (notificationItems.size == 1) mostRecentNotification.getLargeIconUri() else null
|
||||
}
|
||||
|
||||
fun getSlideBigPictureUri(context: Context): Uri? {
|
||||
return if (notificationItems.size == 1 && TextSecurePreferences.getNotificationPrivacy(context).isDisplayMessage && !KeyCachingService.isLocked(context)) {
|
||||
mostRecentNotification.getBigPictureUri()
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package org.thoughtcrime.securesms.notifications.v2
|
||||
|
||||
import android.annotation.TargetApi
|
||||
import android.app.Notification
|
||||
import android.app.NotificationManager
|
||||
import android.app.PendingIntent
|
||||
|
@ -44,67 +45,114 @@ object NotificationFactory {
|
|||
targetThreadId: Long,
|
||||
defaultBubbleState: BubbleUtil.BubbleState,
|
||||
lastAudibleNotification: Long,
|
||||
notificationConfigurationChanged: Boolean,
|
||||
alertOverrides: Set<Long>
|
||||
): Set<Long> {
|
||||
if (state.isEmpty) {
|
||||
Log.d(TAG, "State is empty, bailing")
|
||||
return emptySet()
|
||||
}
|
||||
|
||||
val nonVisibleThreadCount = state.conversations.count { it.threadId != visibleThreadId }
|
||||
return if (Build.VERSION.SDK_INT < 23) {
|
||||
notify19(
|
||||
context = context,
|
||||
state = state,
|
||||
visibleThreadId = visibleThreadId,
|
||||
targetThreadId = targetThreadId,
|
||||
defaultBubbleState = defaultBubbleState,
|
||||
lastAudibleNotification = lastAudibleNotification,
|
||||
alertOverrides = alertOverrides,
|
||||
nonVisibleThreadCount = nonVisibleThreadCount
|
||||
)
|
||||
} else {
|
||||
notify23(
|
||||
context = context,
|
||||
state = state,
|
||||
visibleThreadId = visibleThreadId,
|
||||
targetThreadId = targetThreadId,
|
||||
defaultBubbleState = defaultBubbleState,
|
||||
lastAudibleNotification = lastAudibleNotification,
|
||||
alertOverrides = alertOverrides,
|
||||
nonVisibleThreadCount = nonVisibleThreadCount
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun notify19(
|
||||
context: Context,
|
||||
state: NotificationStateV2,
|
||||
visibleThreadId: Long,
|
||||
targetThreadId: Long,
|
||||
defaultBubbleState: BubbleUtil.BubbleState,
|
||||
lastAudibleNotification: Long,
|
||||
alertOverrides: Set<Long>,
|
||||
nonVisibleThreadCount: Int
|
||||
): Set<Long> {
|
||||
val threadsThatNewlyAlerted: MutableSet<Long> = mutableSetOf()
|
||||
|
||||
if (Build.VERSION.SDK_INT >= 23 || state.conversations.size == 1) {
|
||||
state.conversations.forEach { conversation ->
|
||||
if (conversation.threadId == visibleThreadId && conversation.hasNewNotifications()) {
|
||||
Log.internal().i(TAG, "Thread is visible, notifying in thread. notificationId: ${conversation.notificationId}")
|
||||
notifyInThread(context, conversation.recipient, lastAudibleNotification)
|
||||
} else if (notificationConfigurationChanged || conversation.hasNewNotifications() || alertOverrides.contains(conversation.threadId)) {
|
||||
|
||||
if (conversation.hasNewNotifications()) {
|
||||
threadsThatNewlyAlerted += conversation.threadId
|
||||
}
|
||||
|
||||
notifyForConversation(
|
||||
context = context,
|
||||
conversation = conversation,
|
||||
targetThreadId = targetThreadId,
|
||||
defaultBubbleState = defaultBubbleState,
|
||||
shouldAlert = (conversation.hasNewNotifications() || alertOverrides.contains(conversation.threadId)) && !conversation.mostRecentNotification.individualRecipient.isSelf
|
||||
)
|
||||
}
|
||||
state.conversations.find { it.threadId == visibleThreadId }?.let { conversation ->
|
||||
if (conversation.hasNewNotifications()) {
|
||||
Log.internal().i(TAG, "Thread is visible, notifying in thread. notificationId: ${conversation.notificationId}")
|
||||
notifyInThread(context, conversation.recipient, lastAudibleNotification)
|
||||
}
|
||||
}
|
||||
|
||||
if ((state.conversations.size > 1 && threadsThatNewlyAlerted.isNotEmpty()) || ServiceUtil.getNotificationManager(context).isDisplayingSummaryNotification()) {
|
||||
val builder: NotificationBuilder = NotificationBuilder.create(context)
|
||||
|
||||
builder.apply {
|
||||
setSmallIcon(R.drawable.ic_notification)
|
||||
setColor(ContextCompat.getColor(context, R.color.core_ultramarine))
|
||||
setCategory(NotificationCompat.CATEGORY_MESSAGE)
|
||||
setGroup(DefaultMessageNotifier.NOTIFICATION_GROUP)
|
||||
setGroupAlertBehavior(NotificationCompat.GROUP_ALERT_CHILDREN)
|
||||
setChannelId(NotificationChannels.getMessagesChannel(context))
|
||||
setContentTitle(context.getString(R.string.app_name))
|
||||
setContentIntent(PendingIntent.getActivity(context, 0, MainActivity.clearTop(context), 0))
|
||||
setGroupSummary(true)
|
||||
setSubText(context.getString(R.string.MessageNotifier_d_new_messages_in_d_conversations, state.messageCount, state.threadCount))
|
||||
setContentInfo(state.messageCount.toString())
|
||||
setNumber(state.messageCount)
|
||||
setSummaryContentText(state.mostRecentSender)
|
||||
setDeleteIntent(state.getDeleteIntent(context))
|
||||
setWhen(state.mostRecentNotification)
|
||||
addMarkAsReadAction(state)
|
||||
addMessages(state)
|
||||
setOnlyAlertOnce(!state.notificationItems.any { it.isNewNotification })
|
||||
setPriority(TextSecurePreferences.getNotificationPriority(context))
|
||||
setLights()
|
||||
setAlarms(state.mostRecentSender)
|
||||
setTicker(state.mostRecentNotification.getStyledPrimaryText(context, true))
|
||||
if (nonVisibleThreadCount == 1) {
|
||||
state.conversations.first { it.threadId != visibleThreadId }.let { conversation ->
|
||||
notifyForConversation(
|
||||
context = context,
|
||||
conversation = conversation,
|
||||
targetThreadId = targetThreadId,
|
||||
defaultBubbleState = defaultBubbleState,
|
||||
shouldAlert = (conversation.hasNewNotifications() || alertOverrides.contains(conversation.threadId)) && !conversation.mostRecentNotification.individualRecipient.isSelf
|
||||
)
|
||||
if (conversation.hasNewNotifications()) {
|
||||
threadsThatNewlyAlerted += conversation.threadId
|
||||
}
|
||||
}
|
||||
} else if (nonVisibleThreadCount > 1) {
|
||||
val nonVisibleConversations: List<NotificationConversation> = state.getNonVisibleConversation(visibleThreadId)
|
||||
threadsThatNewlyAlerted += nonVisibleConversations.filter { it.hasNewNotifications() }.map { it.threadId }
|
||||
notifySummary(context = context, state = state.copy(conversations = nonVisibleConversations))
|
||||
}
|
||||
|
||||
Log.d(TAG, "showing summary notification")
|
||||
NotificationManagerCompat.from(context).safelyNotify(context, null, NotificationIds.MESSAGE_SUMMARY, builder.build())
|
||||
return threadsThatNewlyAlerted
|
||||
}
|
||||
|
||||
@TargetApi(23)
|
||||
private fun notify23(
|
||||
context: Context,
|
||||
state: NotificationStateV2,
|
||||
visibleThreadId: Long,
|
||||
targetThreadId: Long,
|
||||
defaultBubbleState: BubbleUtil.BubbleState,
|
||||
lastAudibleNotification: Long,
|
||||
alertOverrides: Set<Long>,
|
||||
nonVisibleThreadCount: Int
|
||||
): Set<Long> {
|
||||
val threadsThatNewlyAlerted: MutableSet<Long> = mutableSetOf()
|
||||
|
||||
state.conversations.forEach { conversation ->
|
||||
if (conversation.threadId == visibleThreadId && conversation.hasNewNotifications()) {
|
||||
Log.internal().i(TAG, "Thread is visible, notifying in thread. notificationId: ${conversation.notificationId}")
|
||||
notifyInThread(context, conversation.recipient, lastAudibleNotification)
|
||||
} else {
|
||||
if (conversation.hasNewNotifications()) {
|
||||
threadsThatNewlyAlerted += conversation.threadId
|
||||
}
|
||||
|
||||
notifyForConversation(
|
||||
context = context,
|
||||
conversation = conversation,
|
||||
targetThreadId = targetThreadId,
|
||||
defaultBubbleState = defaultBubbleState,
|
||||
shouldAlert = (conversation.hasNewNotifications() || alertOverrides.contains(conversation.threadId)) && !conversation.mostRecentNotification.individualRecipient.isSelf
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
if (nonVisibleThreadCount > 1 || ServiceUtil.getNotificationManager(context).isDisplayingSummaryNotification()) {
|
||||
notifySummary(context = context, state = state.copy(conversations = state.getNonVisibleConversation(visibleThreadId)))
|
||||
}
|
||||
|
||||
return threadsThatNewlyAlerted
|
||||
|
@ -127,7 +175,7 @@ object NotificationFactory {
|
|||
setGroupAlertBehavior(NotificationCompat.GROUP_ALERT_CHILDREN)
|
||||
setChannelId(conversation.getChannelId(context))
|
||||
setContentTitle(conversation.getContentTitle(context))
|
||||
setLargeIcon(conversation.getLargeIcon(context))
|
||||
setLargeIcon(conversation.getContactLargeIcon(context).toLargeBitmap(context))
|
||||
addPerson(conversation.recipient)
|
||||
setShortcutId(ConversationUtil.getShortcutId(conversation.recipient))
|
||||
setContentInfo(conversation.messageCount.toString())
|
||||
|
@ -151,7 +199,41 @@ object NotificationFactory {
|
|||
builder.addTurnOffJoinedNotificationsAction(conversation.getTurnOffJoinedNotificationsIntent(context))
|
||||
}
|
||||
|
||||
NotificationManagerCompat.from(context).safelyNotify(context, conversation.recipient, conversation.notificationId, builder.build())
|
||||
val notificationId: Int = if (Build.VERSION.SDK_INT < 23) NotificationIds.MESSAGE_SUMMARY else conversation.notificationId
|
||||
|
||||
NotificationManagerCompat.from(context).safelyNotify(context, conversation.recipient, notificationId, builder.build())
|
||||
}
|
||||
|
||||
private fun notifySummary(context: Context, state: NotificationStateV2) {
|
||||
val builder: NotificationBuilder = NotificationBuilder.create(context)
|
||||
|
||||
builder.apply {
|
||||
setSmallIcon(R.drawable.ic_notification)
|
||||
setColor(ContextCompat.getColor(context, R.color.core_ultramarine))
|
||||
setCategory(NotificationCompat.CATEGORY_MESSAGE)
|
||||
setGroup(DefaultMessageNotifier.NOTIFICATION_GROUP)
|
||||
setGroupAlertBehavior(NotificationCompat.GROUP_ALERT_CHILDREN)
|
||||
setChannelId(NotificationChannels.getMessagesChannel(context))
|
||||
setContentTitle(context.getString(R.string.app_name))
|
||||
setContentIntent(PendingIntent.getActivity(context, 0, MainActivity.clearTop(context), 0))
|
||||
setGroupSummary(true)
|
||||
setSubText(context.getString(R.string.MessageNotifier_d_new_messages_in_d_conversations, state.messageCount, state.threadCount))
|
||||
setContentInfo(state.messageCount.toString())
|
||||
setNumber(state.messageCount)
|
||||
setSummaryContentText(state.mostRecentSender)
|
||||
setDeleteIntent(state.getDeleteIntent(context))
|
||||
setWhen(state.mostRecentNotification)
|
||||
addMarkAsReadAction(state)
|
||||
addMessages(state)
|
||||
setOnlyAlertOnce(!state.notificationItems.any { it.isNewNotification })
|
||||
setPriority(TextSecurePreferences.getNotificationPriority(context))
|
||||
setLights()
|
||||
setAlarms(state.mostRecentSender)
|
||||
setTicker(state.mostRecentNotification.getStyledPrimaryText(context, true))
|
||||
}
|
||||
|
||||
Log.d(TAG, "showing summary notification")
|
||||
NotificationManagerCompat.from(context).safelyNotify(context, null, NotificationIds.MESSAGE_SUMMARY, builder.build())
|
||||
}
|
||||
|
||||
private fun notifyInThread(context: Context, recipient: Recipient, lastAudibleNotification: Long) {
|
||||
|
|
|
@ -39,6 +39,10 @@ data class NotificationStateV2(val conversations: List<NotificationConversation>
|
|||
val mostRecentSender: Recipient
|
||||
get() = mostRecentNotification.individualRecipient
|
||||
|
||||
fun getNonVisibleConversation(visibleThreadId: Long): List<NotificationConversation> {
|
||||
return conversations.filterNot { it.threadId == visibleThreadId }
|
||||
}
|
||||
|
||||
fun getDeleteIntent(context: Context): PendingIntent? {
|
||||
val ids = LongArray(messageCount)
|
||||
val mms = BooleanArray(ids.size)
|
||||
|
|
|
@ -343,7 +343,7 @@ public final class FeatureFlags {
|
|||
|
||||
/** Whether or not to use the new notification system. */
|
||||
public static boolean useNewNotificationSystem() {
|
||||
return getBoolean(NOTIFICATION_REWRITE, false) && Build.VERSION.SDK_INT >= 26;
|
||||
return Build.VERSION.SDK_INT >= 26 || getBoolean(NOTIFICATION_REWRITE, false);
|
||||
}
|
||||
|
||||
public static boolean mp4GifSendSupport() {
|
||||
|
|
|
@ -15,6 +15,7 @@ import org.jetbrains.annotations.NotNull;
|
|||
import org.jetbrains.uast.UCallExpression;
|
||||
import org.jetbrains.uast.UExpression;
|
||||
import org.jetbrains.uast.java.JavaUSimpleNameReferenceExpression;
|
||||
import org.jetbrains.uast.kotlin.KotlinUQualifiedReferenceExpression;
|
||||
import org.jetbrains.uast.kotlin.KotlinUSimpleReferenceExpression;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
@ -74,7 +75,7 @@ public final class SignalLogDetector extends Detector implements Detector.UastSc
|
|||
if (evaluator.isMemberInClass(method, "org.signal.core.util.logging.Log")) {
|
||||
List<UExpression> arguments = call.getValueArguments();
|
||||
UExpression tag = arguments.get(0);
|
||||
if (!(tag instanceof JavaUSimpleNameReferenceExpression || tag instanceof KotlinUSimpleReferenceExpression)) {
|
||||
if (!(tag instanceof JavaUSimpleNameReferenceExpression || tag instanceof KotlinUSimpleReferenceExpression || tag instanceof KotlinUQualifiedReferenceExpression)) {
|
||||
context.report(INLINE_TAG, call, context.getLocation(call), "Not using a tag constant");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -152,6 +152,27 @@ public final class LogDetectorTest {
|
|||
.expectClean();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void log_uses_tag_companion_kotlin() {
|
||||
lint()
|
||||
.files(appLogStub,
|
||||
kotlin("package foo\n" +
|
||||
"import org.signal.core.util.logging.Log\n" +
|
||||
"class Example {\n" +
|
||||
" companion object { val TAG: String = Log.tag(Example::class.java) }\n" +
|
||||
" fun log() {\n" +
|
||||
" Log.d(TAG, \"msg\")\n" +
|
||||
" }\n" +
|
||||
"}\n"+
|
||||
"fun logOutsie() {\n" +
|
||||
" Log.d(Example.TAG, \"msg\")\n" +
|
||||
"}\n")
|
||||
)
|
||||
.issues(SignalLogDetector.INLINE_TAG)
|
||||
.run()
|
||||
.expectClean();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void log_uses_inline_tag() {
|
||||
lint()
|
||||
|
|
Ładowanie…
Reference in New Issue