Remove any notification of a redacted event ()

Also do some cleanup and kotlinification on the code
This commit is contained in:
Benoit Marty 2019-09-18 16:26:25 +02:00
parent 6f09eea248
commit 7da9cafcc2
11 changed files with 63 additions and 30 deletions
CHANGES.md
matrix-sdk-android/src/main/java/im/vector/matrix/android
vector/src

View file

@ -7,6 +7,7 @@ Features:
Improvements: Improvements:
- Add unread indent on room list (#485) - Add unread indent on room list (#485)
- Message Editing: Update notifications (#128) - Message Editing: Update notifications (#128)
- Remove any notification of a redacted event (#563)
Other changes: Other changes:
- -

View file

@ -43,6 +43,7 @@ interface PushRuleService {
interface PushRuleListener { interface PushRuleListener {
fun onMatchRule(event: Event, actions: List<Action>) fun onMatchRule(event: Event, actions: List<Action>)
fun onRoomLeft(roomId: String) fun onRoomLeft(roomId: String)
fun onEventRedacted(redactedEventId: String)
fun batchFinish() fun batchFinish()
} }
} }

View file

@ -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() { fun dispatchFinish() {
try { try {
listeners.forEach { listeners.forEach {

View file

@ -78,6 +78,25 @@ internal class DefaultProcessEventForPushTask @Inject constructor(
defaultPushRuleService.dispatchBing(event, it) 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<String>(), { acc, next ->
acc + next
})
Timber.v("[PushRules] Found ${allRedactedEvents.size} redacted events")
allRedactedEvents.forEach { redactedEventId ->
defaultPushRuleService.dispatchRedactedEventId(redactedEventId)
}
defaultPushRuleService.dispatchFinish() defaultPushRuleService.dispatchFinish()
} }

View file

@ -196,7 +196,6 @@ class VectorFirebaseMessagingService : FirebaseMessagingService() {
// This ID can and should be used to detect duplicate notification requests. // This ID can and should be used to detect duplicate notification requests.
val eventId = data["event_id"] ?: return //Just ignore val eventId = data["event_id"] ?: return //Just ignore
val eventType = data["type"] val eventType = data["type"]
if (eventType == null) { if (eventType == null) {
//Just add a generic unknown event //Just add a generic unknown event
@ -214,10 +213,7 @@ class VectorFirebaseMessagingService : FirebaseMessagingService() {
) )
notificationDrawerManager.onNotifiableEventReceived(simpleNotifiableEvent) notificationDrawerManager.onNotifiableEventReceived(simpleNotifiableEvent)
notificationDrawerManager.refreshNotificationDrawer() notificationDrawerManager.refreshNotificationDrawer()
return
} else { } else {
val event = parseEvent(data) ?: return val event = parseEvent(data) ?: return
val notifiableEvent = notifiableEventResolver.resolveEvent(event, session) val notifiableEvent = notifiableEventResolver.resolveEvent(event, session)
@ -228,8 +224,6 @@ class VectorFirebaseMessagingService : FirebaseMessagingService() {
Timber.e("--> ${event}") Timber.e("--> ${event}")
} }
} else { } else {
if (notifiableEvent is NotifiableMessageEvent) { if (notifiableEvent is NotifiableMessageEvent) {
if (TextUtils.isEmpty(notifiableEvent.senderName)) { if (TextUtils.isEmpty(notifiableEvent.senderName)) {
notifiableEvent.senderName = data["sender_display_name"] notifiableEvent.senderName = data["sender_display_name"]
@ -246,7 +240,6 @@ class VectorFirebaseMessagingService : FirebaseMessagingService() {
notificationDrawerManager.refreshNotificationDrawer() notificationDrawerManager.refreshNotificationDrawer()
} }
} }
} }
private fun findRoomNameBestEffort(data: Map<String, String>, session: Session?): String? { private fun findRoomNameBestEffort(data: Map<String, String>, session: Session?): String? {

View file

@ -32,6 +32,7 @@ data class InviteNotifiableEvent(
override var isPushGatewayEvent: Boolean = false) : NotifiableEvent { override var isPushGatewayEvent: Boolean = false) : NotifiableEvent {
override var hasBeenDisplayed: Boolean = false override var hasBeenDisplayed: Boolean = false
override var isRedacted: Boolean = false
override var lockScreenVisibility = NotificationCompat.VISIBILITY_PUBLIC override var lockScreenVisibility = NotificationCompat.VISIBILITY_PUBLIC
} }

View file

@ -31,6 +31,7 @@ interface NotifiableEvent : Serializable {
// Compat: Only for android <7, for newer version the sound is defined in the channel // Compat: Only for android <7, for newer version the sound is defined in the channel
var soundName: String? var soundName: String?
var hasBeenDisplayed: Boolean var hasBeenDisplayed: Boolean
var isRedacted: Boolean
//Used to know if event should be replaced with the one coming from eventstream //Used to know if event should be replaced with the one coming from eventstream
var isPushGatewayEvent: Boolean var isPushGatewayEvent: Boolean
} }

View file

@ -36,6 +36,7 @@ data class NotifiableMessageEvent(
override var soundName: String? = null override var soundName: String? = null
override var lockScreenVisibility = NotificationCompat.VISIBILITY_PUBLIC override var lockScreenVisibility = NotificationCompat.VISIBILITY_PUBLIC
override var hasBeenDisplayed: Boolean = false override var hasBeenDisplayed: Boolean = false
override var isRedacted: Boolean = false
var roomAvatarPath: String? = null var roomAvatarPath: String? = null
var senderAvatarPath: String? = null var senderAvatarPath: String? = null

View file

@ -15,7 +15,6 @@
*/ */
package im.vector.riotx.features.notifications package im.vector.riotx.features.notifications
import android.app.Notification
import android.content.Context import android.content.Context
import android.graphics.Bitmap import android.graphics.Bitmap
import android.os.Handler import android.os.Handler
@ -96,7 +95,6 @@ class NotificationDrawerManager @Inject constructor(private val context: Context
notifiableEvent.noisy = false notifiableEvent.noisy = false
eventList.remove(existing) eventList.remove(existing)
eventList.add(notifiableEvent) eventList.add(notifiableEvent)
} else { } else {
//keep the existing one, do not replace //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 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() val summaryInboxStyle = NotificationCompat.InboxStyle()
//group events by room to create a single MessagingStyle notif //group events by room to create a single MessagingStyle notif
val roomIdToEventMap: MutableMap<String, ArrayList<NotifiableMessageEvent>> = HashMap() val roomIdToEventMap: MutableMap<String, MutableList<NotifiableMessageEvent>> = LinkedHashMap()
val simpleEvents: ArrayList<NotifiableEvent> = ArrayList() val simpleEvents: MutableList<NotifiableEvent> = ArrayList()
val notifications: ArrayList<Notification> = ArrayList()
val eventIterator = eventList.listIterator() val eventIterator = eventList.listIterator()
while (eventIterator.hasNext()) { while (eventIterator.hasNext()) {
val event = eventIterator.next() val event = eventIterator.next()
if (event is NotifiableMessageEvent) { if (event is NotifiableMessageEvent) {
val roomId = event.roomId val roomId = event.roomId
var roomEvents = roomIdToEventMap[roomId] val roomEvents = roomIdToEventMap.getOrPut(roomId) { ArrayList() }
if (roomEvents == null) {
roomEvents = ArrayList()
roomIdToEventMap[roomId] = roomEvents
}
if (shouldIgnoreMessageEventInRoom(roomId) || outdatedDetector?.isMessageOutdated(event) == true) { if (shouldIgnoreMessageEventInRoom(roomId) || outdatedDetector?.isMessageOutdated(event) == true) {
//forget this event //forget this event
@ -246,10 +248,10 @@ class NotificationDrawerManager @Inject constructor(private val context: Context
var globalLastMessageTimestamp = 0L var globalLastMessageTimestamp = 0L
//events have been grouped //events have been grouped by roomId
for ((roomId, events) in roomIdToEventMap) { for ((roomId, events) in roomIdToEventMap) {
// Build the notification for the room
if (events.isEmpty()) { if (events.isEmpty() || events.all { it.isRedacted }) {
//Just clear this notification //Just clear this notification
Timber.v("%%%%%%%% REFRESH NOTIFICATION DRAWER $roomId has no more events") Timber.v("%%%%%%%% REFRESH NOTIFICATION DRAWER $roomId has no more events")
NotificationUtils.cancelNotificationMessage(context, roomId, ROOM_MESSAGES_NOTIFICATION_ID) NotificationUtils.cancelNotificationMessage(context, roomId, ROOM_MESSAGES_NOTIFICATION_ID)
@ -280,7 +282,7 @@ class NotificationDrawerManager @Inject constructor(private val context: Context
for (event in events) { for (event in events) {
//if all events in this room have already been displayed there is no need to update it //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.shouldBing = roomEventGroupInfo.shouldBing || event.noisy
roomEventGroupInfo.customSound = event.soundName 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) style.addMessage(context.getString(R.string.notification_inline_reply_failed), event.timestamp, senderPerson)
roomEventGroupInfo.hasSmartReplyError = true roomEventGroupInfo.hasSmartReplyError = true
} else { } 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 event.hasBeenDisplayed = true //we can consider it as displayed
@ -356,7 +360,6 @@ class NotificationDrawerManager @Inject constructor(private val context: Context
myUserDisplayName) myUserDisplayName)
?.let { ?.let {
//is there an id for this room? //is there an id for this room?
notifications.add(it)
NotificationUtils.showNotificationMessage(context, roomId, ROOM_MESSAGES_NOTIFICATION_ID, it) NotificationUtils.showNotificationMessage(context, roomId, ROOM_MESSAGES_NOTIFICATION_ID, it)
} }
hasNewEvent = true hasNewEvent = true
@ -372,7 +375,6 @@ class NotificationDrawerManager @Inject constructor(private val context: Context
//We build a simple event //We build a simple event
if (firstTime || !event.hasBeenDisplayed) { if (firstTime || !event.hasBeenDisplayed) {
NotificationUtils.buildSimpleEventNotification(context, vectorPreferences, event, null, session.myUserId)?.let { NotificationUtils.buildSimpleEventNotification(context, vectorPreferences, event, null, session.myUserId)?.let {
notifications.add(it)
NotificationUtils.showNotificationMessage(context, event.eventId, ROOM_EVENT_NOTIFICATION_ID, it) NotificationUtils.showNotificationMessage(context, event.eventId, ROOM_EVENT_NOTIFICATION_ID, it)
event.hasBeenDisplayed = true //we can consider it as displayed event.hasBeenDisplayed = true //we can consider it as displayed
hasNewEvent = true 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 // 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 // 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) NotificationUtils.cancelNotificationMessage(context, null, SUMMARY_NOTIFICATION_ID)
} else { } else {
val nbEvents = roomIdToEventMap.size + simpleEvents.size val nbEvents = roomIdToEventMap.size + simpleEvents.size
@ -443,12 +445,11 @@ class NotificationDrawerManager @Inject constructor(private val context: Context
} }
} }
private fun getRoomBitmap(events: ArrayList<NotifiableMessageEvent>): Bitmap? { private fun getRoomBitmap(events: List<NotifiableMessageEvent>): Bitmap? {
if (events.isEmpty()) return null if (events.isEmpty()) return null
//Use the last event (most recent?) //Use the last event (most recent?)
val roomAvatarPath = events.last().roomAvatarPath val roomAvatarPath = events.last().roomAvatarPath ?: events.last().senderAvatarPath
?: events.last().senderAvatarPath
return bitmapLoader.getRoomBitmap(roomAvatarPath) return bitmapLoader.getRoomBitmap(roomAvatarPath)
} }
@ -476,14 +477,14 @@ class NotificationDrawerManager @Inject constructor(private val context: Context
} }
} }
private fun loadEventInfo(): ArrayList<NotifiableEvent> { private fun loadEventInfo(): MutableList<NotifiableEvent> {
try { try {
val file = File(context.applicationContext.cacheDir, ROOMS_NOTIFICATIONS_FILE_NAME) val file = File(context.applicationContext.cacheDir, ROOMS_NOTIFICATIONS_FILE_NAME)
if (file.exists()) { if (file.exists()) {
FileInputStream(file).use { FileInputStream(file).use {
val events: ArrayList<NotifiableEvent>? = activeSessionHolder.getSafeActiveSession()?.loadSecureSecret(it, KEY_ALIAS_SECRET_STORAGE) val events: ArrayList<NotifiableEvent>? = activeSessionHolder.getSafeActiveSession()?.loadSecureSecret(it, KEY_ALIAS_SECRET_STORAGE)
if (events != null) { if (events != null) {
return ArrayList(events.mapNotNull { it as? NotifiableEvent }) return events.toMutableList()
} }
} }
} }

View file

@ -60,6 +60,10 @@ class PushRuleTriggerListener @Inject constructor(
notificationDrawerManager.clearMessageEventOfRoom(roomId) notificationDrawerManager.clearMessageEventOfRoom(roomId)
} }
override fun onEventRedacted(redactedEventId: String) {
notificationDrawerManager.onEventRedacted(redactedEventId)
}
override fun batchFinish() { override fun batchFinish() {
notificationDrawerManager.refreshNotificationDrawer() notificationDrawerManager.refreshNotificationDrawer()
} }

View file

@ -30,6 +30,7 @@ data class SimpleNotifiableEvent(
override var isPushGatewayEvent: Boolean = false) : NotifiableEvent { override var isPushGatewayEvent: Boolean = false) : NotifiableEvent {
override var hasBeenDisplayed: Boolean = false override var hasBeenDisplayed: Boolean = false
override var isRedacted: Boolean = false
override var lockScreenVisibility = NotificationCompat.VISIBILITY_PUBLIC override var lockScreenVisibility = NotificationCompat.VISIBILITY_PUBLIC
} }