From 7da9cafcc2cd5773e5dea13b31dcefbdf353cf40 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 18 Sep 2019 16:26:25 +0200 Subject: [PATCH] Remove any notification of a redacted event (#563) Also do some cleanup and kotlinification on the code --- CHANGES.md | 1 + .../android/api/pushrules/PushRuleService.kt | 1 + .../notification/DefaultPushRuleService.kt | 10 ++++ .../notification/ProcessEventForPushTask.kt | 19 ++++++++ .../fcm/VectorFirebaseMessagingService.kt | 7 --- .../notifications/InviteNotifiableEvent.kt | 1 + .../features/notifications/NotifiableEvent.kt | 1 + .../notifications/NotifiableMessageEvent.kt | 1 + .../NotificationDrawerManager.kt | 47 ++++++++++--------- .../notifications/PushRuleTriggerListener.kt | 4 ++ .../notifications/SimpleNotifiableEvent.kt | 1 + 11 files changed, 63 insertions(+), 30 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 412d32a0a1..75b11bdbd4 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -7,6 +7,7 @@ Features: Improvements: - Add unread indent on room list (#485) - Message Editing: Update notifications (#128) + - Remove any notification of a redacted event (#563) Other changes: - diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/pushrules/PushRuleService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/pushrules/PushRuleService.kt index 28fcff0c76..05396cb219 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/pushrules/PushRuleService.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/pushrules/PushRuleService.kt @@ -43,6 +43,7 @@ interface PushRuleService { interface PushRuleListener { fun onMatchRule(event: Event, actions: List) fun onRoomLeft(roomId: String) + fun onEventRedacted(redactedEventId: String) fun batchFinish() } } \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/notification/DefaultPushRuleService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/notification/DefaultPushRuleService.kt index 83b89701b3..7cccd0dc3e 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/notification/DefaultPushRuleService.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/notification/DefaultPushRuleService.kt @@ -132,6 +132,16 @@ internal class DefaultPushRuleService @Inject constructor( } } + fun dispatchRedactedEventId(redactedEventId: String) { + try { + listeners.forEach { + it.onEventRedacted(redactedEventId) + } + } catch (e: Throwable) { + Timber.e(e, "Error while dispatching room left") + } + } + fun dispatchFinish() { try { listeners.forEach { diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/notification/ProcessEventForPushTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/notification/ProcessEventForPushTask.kt index a434c6e950..064f1e3f46 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/notification/ProcessEventForPushTask.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/notification/ProcessEventForPushTask.kt @@ -78,6 +78,25 @@ internal class DefaultProcessEventForPushTask @Inject constructor( defaultPushRuleService.dispatchBing(event, it) } } + + val allRedactedEvents = params.syncResponse.join + .map { entries -> + entries.value.timeline?.events?.filter { + it.type == EventType.REDACTION + } + .orEmpty() + .mapNotNull { it.redacts } + } + .fold(emptyList(), { acc, next -> + acc + next + }) + + Timber.v("[PushRules] Found ${allRedactedEvents.size} redacted events") + + allRedactedEvents.forEach { redactedEventId -> + defaultPushRuleService.dispatchRedactedEventId(redactedEventId) + } + defaultPushRuleService.dispatchFinish() } diff --git a/vector/src/gplay/java/im/vector/riotx/gplay/push/fcm/VectorFirebaseMessagingService.kt b/vector/src/gplay/java/im/vector/riotx/gplay/push/fcm/VectorFirebaseMessagingService.kt index dcb228cfbd..cde848ea41 100755 --- a/vector/src/gplay/java/im/vector/riotx/gplay/push/fcm/VectorFirebaseMessagingService.kt +++ b/vector/src/gplay/java/im/vector/riotx/gplay/push/fcm/VectorFirebaseMessagingService.kt @@ -196,7 +196,6 @@ class VectorFirebaseMessagingService : FirebaseMessagingService() { // This ID can and should be used to detect duplicate notification requests. val eventId = data["event_id"] ?: return //Just ignore - val eventType = data["type"] if (eventType == null) { //Just add a generic unknown event @@ -214,10 +213,7 @@ class VectorFirebaseMessagingService : FirebaseMessagingService() { ) notificationDrawerManager.onNotifiableEventReceived(simpleNotifiableEvent) notificationDrawerManager.refreshNotificationDrawer() - - return } else { - val event = parseEvent(data) ?: return val notifiableEvent = notifiableEventResolver.resolveEvent(event, session) @@ -228,8 +224,6 @@ class VectorFirebaseMessagingService : FirebaseMessagingService() { Timber.e("--> ${event}") } } else { - - if (notifiableEvent is NotifiableMessageEvent) { if (TextUtils.isEmpty(notifiableEvent.senderName)) { notifiableEvent.senderName = data["sender_display_name"] @@ -246,7 +240,6 @@ class VectorFirebaseMessagingService : FirebaseMessagingService() { notificationDrawerManager.refreshNotificationDrawer() } } - } private fun findRoomNameBestEffort(data: Map, session: Session?): String? { diff --git a/vector/src/main/java/im/vector/riotx/features/notifications/InviteNotifiableEvent.kt b/vector/src/main/java/im/vector/riotx/features/notifications/InviteNotifiableEvent.kt index 10add07a66..5ecc66ffd4 100644 --- a/vector/src/main/java/im/vector/riotx/features/notifications/InviteNotifiableEvent.kt +++ b/vector/src/main/java/im/vector/riotx/features/notifications/InviteNotifiableEvent.kt @@ -32,6 +32,7 @@ data class InviteNotifiableEvent( override var isPushGatewayEvent: Boolean = false) : NotifiableEvent { override var hasBeenDisplayed: Boolean = false + override var isRedacted: Boolean = false override var lockScreenVisibility = NotificationCompat.VISIBILITY_PUBLIC } \ No newline at end of file diff --git a/vector/src/main/java/im/vector/riotx/features/notifications/NotifiableEvent.kt b/vector/src/main/java/im/vector/riotx/features/notifications/NotifiableEvent.kt index bc9c711d67..1603ea5f00 100644 --- a/vector/src/main/java/im/vector/riotx/features/notifications/NotifiableEvent.kt +++ b/vector/src/main/java/im/vector/riotx/features/notifications/NotifiableEvent.kt @@ -31,6 +31,7 @@ interface NotifiableEvent : Serializable { // Compat: Only for android <7, for newer version the sound is defined in the channel var soundName: String? var hasBeenDisplayed: Boolean + var isRedacted: Boolean //Used to know if event should be replaced with the one coming from eventstream var isPushGatewayEvent: Boolean } diff --git a/vector/src/main/java/im/vector/riotx/features/notifications/NotifiableMessageEvent.kt b/vector/src/main/java/im/vector/riotx/features/notifications/NotifiableMessageEvent.kt index 36af131130..5a3e989f18 100644 --- a/vector/src/main/java/im/vector/riotx/features/notifications/NotifiableMessageEvent.kt +++ b/vector/src/main/java/im/vector/riotx/features/notifications/NotifiableMessageEvent.kt @@ -36,6 +36,7 @@ data class NotifiableMessageEvent( override var soundName: String? = null override var lockScreenVisibility = NotificationCompat.VISIBILITY_PUBLIC override var hasBeenDisplayed: Boolean = false + override var isRedacted: Boolean = false var roomAvatarPath: String? = null var senderAvatarPath: String? = null diff --git a/vector/src/main/java/im/vector/riotx/features/notifications/NotificationDrawerManager.kt b/vector/src/main/java/im/vector/riotx/features/notifications/NotificationDrawerManager.kt index 3ea4c970a0..da2fd5c91a 100644 --- a/vector/src/main/java/im/vector/riotx/features/notifications/NotificationDrawerManager.kt +++ b/vector/src/main/java/im/vector/riotx/features/notifications/NotificationDrawerManager.kt @@ -15,7 +15,6 @@ */ package im.vector.riotx.features.notifications -import android.app.Notification import android.content.Context import android.graphics.Bitmap import android.os.Handler @@ -96,7 +95,6 @@ class NotificationDrawerManager @Inject constructor(private val context: Context notifiableEvent.noisy = false eventList.remove(existing) eventList.add(notifiableEvent) - } else { //keep the existing one, do not replace } @@ -127,6 +125,15 @@ class NotificationDrawerManager @Inject constructor(private val context: Context } } + fun onEventRedacted(eventId: String) { + synchronized(eventList) { + eventList.filter { it.eventId == eventId }.map { notifiableEvent -> + notifiableEvent.isRedacted = true + notifiableEvent.hasBeenDisplayed = false + } + } + } + /** Clear all known events and refresh the notification drawer */ @@ -215,20 +222,15 @@ class NotificationDrawerManager @Inject constructor(private val context: Context val summaryInboxStyle = NotificationCompat.InboxStyle() //group events by room to create a single MessagingStyle notif - val roomIdToEventMap: MutableMap> = HashMap() - val simpleEvents: ArrayList = ArrayList() - val notifications: ArrayList = ArrayList() + val roomIdToEventMap: MutableMap> = LinkedHashMap() + val simpleEvents: MutableList = ArrayList() val eventIterator = eventList.listIterator() while (eventIterator.hasNext()) { val event = eventIterator.next() if (event is NotifiableMessageEvent) { val roomId = event.roomId - var roomEvents = roomIdToEventMap[roomId] - if (roomEvents == null) { - roomEvents = ArrayList() - roomIdToEventMap[roomId] = roomEvents - } + val roomEvents = roomIdToEventMap.getOrPut(roomId) { ArrayList() } if (shouldIgnoreMessageEventInRoom(roomId) || outdatedDetector?.isMessageOutdated(event) == true) { //forget this event @@ -246,10 +248,10 @@ class NotificationDrawerManager @Inject constructor(private val context: Context var globalLastMessageTimestamp = 0L - //events have been grouped + //events have been grouped by roomId for ((roomId, events) in roomIdToEventMap) { - - if (events.isEmpty()) { + // Build the notification for the room + if (events.isEmpty() || events.all { it.isRedacted }) { //Just clear this notification Timber.v("%%%%%%%% REFRESH NOTIFICATION DRAWER $roomId has no more events") NotificationUtils.cancelNotificationMessage(context, roomId, ROOM_MESSAGES_NOTIFICATION_ID) @@ -280,7 +282,7 @@ class NotificationDrawerManager @Inject constructor(private val context: Context for (event in events) { //if all events in this room have already been displayed there is no need to update it - if (!event.hasBeenDisplayed) { + if (!event.hasBeenDisplayed && !event.isRedacted) { roomEventGroupInfo.shouldBing = roomEventGroupInfo.shouldBing || event.noisy roomEventGroupInfo.customSound = event.soundName } @@ -296,7 +298,9 @@ class NotificationDrawerManager @Inject constructor(private val context: Context style.addMessage(context.getString(R.string.notification_inline_reply_failed), event.timestamp, senderPerson) roomEventGroupInfo.hasSmartReplyError = true } else { - style.addMessage(event.body, event.timestamp, senderPerson) + if (!event.isRedacted) { + style.addMessage(event.body, event.timestamp, senderPerson) + } } event.hasBeenDisplayed = true //we can consider it as displayed @@ -356,7 +360,6 @@ class NotificationDrawerManager @Inject constructor(private val context: Context myUserDisplayName) ?.let { //is there an id for this room? - notifications.add(it) NotificationUtils.showNotificationMessage(context, roomId, ROOM_MESSAGES_NOTIFICATION_ID, it) } hasNewEvent = true @@ -372,7 +375,6 @@ class NotificationDrawerManager @Inject constructor(private val context: Context //We build a simple event if (firstTime || !event.hasBeenDisplayed) { NotificationUtils.buildSimpleEventNotification(context, vectorPreferences, event, null, session.myUserId)?.let { - notifications.add(it) NotificationUtils.showNotificationMessage(context, event.eventId, ROOM_EVENT_NOTIFICATION_ID, it) event.hasBeenDisplayed = true //we can consider it as displayed hasNewEvent = true @@ -396,7 +398,7 @@ class NotificationDrawerManager @Inject constructor(private val context: Context // To ensure the best experience on all devices and versions, always include a group summary when you create a group // https://developer.android.com/training/notify-user/group - if (eventList.isEmpty()) { + if (eventList.isEmpty() || eventList.all { it.isRedacted }) { NotificationUtils.cancelNotificationMessage(context, null, SUMMARY_NOTIFICATION_ID) } else { val nbEvents = roomIdToEventMap.size + simpleEvents.size @@ -443,12 +445,11 @@ class NotificationDrawerManager @Inject constructor(private val context: Context } } - private fun getRoomBitmap(events: ArrayList): Bitmap? { + private fun getRoomBitmap(events: List): Bitmap? { if (events.isEmpty()) return null //Use the last event (most recent?) - val roomAvatarPath = events.last().roomAvatarPath - ?: events.last().senderAvatarPath + val roomAvatarPath = events.last().roomAvatarPath ?: events.last().senderAvatarPath return bitmapLoader.getRoomBitmap(roomAvatarPath) } @@ -476,14 +477,14 @@ class NotificationDrawerManager @Inject constructor(private val context: Context } } - private fun loadEventInfo(): ArrayList { + private fun loadEventInfo(): MutableList { try { val file = File(context.applicationContext.cacheDir, ROOMS_NOTIFICATIONS_FILE_NAME) if (file.exists()) { FileInputStream(file).use { val events: ArrayList? = activeSessionHolder.getSafeActiveSession()?.loadSecureSecret(it, KEY_ALIAS_SECRET_STORAGE) if (events != null) { - return ArrayList(events.mapNotNull { it as? NotifiableEvent }) + return events.toMutableList() } } } diff --git a/vector/src/main/java/im/vector/riotx/features/notifications/PushRuleTriggerListener.kt b/vector/src/main/java/im/vector/riotx/features/notifications/PushRuleTriggerListener.kt index 555c8737c5..6711d57a22 100644 --- a/vector/src/main/java/im/vector/riotx/features/notifications/PushRuleTriggerListener.kt +++ b/vector/src/main/java/im/vector/riotx/features/notifications/PushRuleTriggerListener.kt @@ -60,6 +60,10 @@ class PushRuleTriggerListener @Inject constructor( notificationDrawerManager.clearMessageEventOfRoom(roomId) } + override fun onEventRedacted(redactedEventId: String) { + notificationDrawerManager.onEventRedacted(redactedEventId) + } + override fun batchFinish() { notificationDrawerManager.refreshNotificationDrawer() } diff --git a/vector/src/main/java/im/vector/riotx/features/notifications/SimpleNotifiableEvent.kt b/vector/src/main/java/im/vector/riotx/features/notifications/SimpleNotifiableEvent.kt index abd7b706bc..66779cfe9a 100644 --- a/vector/src/main/java/im/vector/riotx/features/notifications/SimpleNotifiableEvent.kt +++ b/vector/src/main/java/im/vector/riotx/features/notifications/SimpleNotifiableEvent.kt @@ -30,6 +30,7 @@ data class SimpleNotifiableEvent( override var isPushGatewayEvent: Boolean = false) : NotifiableEvent { override var hasBeenDisplayed: Boolean = false + override var isRedacted: Boolean = false override var lockScreenVisibility = NotificationCompat.VISIBILITY_PUBLIC }