supporting images in the room notifications 15:40:32

- downloads and exports any images whilst resolving the notification event
This commit is contained in:
Adam Brown 2021-11-03 15:42:35 +00:00
parent 4597cb3816
commit 8cc68e16d2
6 changed files with 64 additions and 15 deletions

View file

@ -66,3 +66,7 @@ fun String?.insertBeforeLast(insert: String, delimiter: String = "."): String {
replaceRange(idx, idx, insert)
}
}
inline fun <reified R> Any?.takeAs(): R? {
return takeIf { it is R } as R?
}

View file

@ -15,8 +15,10 @@
*/
package im.vector.app.features.notifications
import android.net.Uri
import im.vector.app.BuildConfig
import im.vector.app.R
import im.vector.app.core.extensions.takeAs
import im.vector.app.core.resources.StringProvider
import im.vector.app.features.displayname.getBestName
import im.vector.app.features.home.room.detail.timeline.format.DisplayableEventFormatter
@ -31,9 +33,11 @@ import org.matrix.android.sdk.api.session.events.model.isEdition
import org.matrix.android.sdk.api.session.events.model.toModel
import org.matrix.android.sdk.api.session.room.model.Membership
import org.matrix.android.sdk.api.session.room.model.RoomMemberContent
import org.matrix.android.sdk.api.session.room.model.message.MessageWithAttachmentContent
import org.matrix.android.sdk.api.session.room.sender.SenderInfo
import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent
import org.matrix.android.sdk.api.session.room.timeline.getEditedEventId
import org.matrix.android.sdk.api.session.room.timeline.getLastMessageContent
import org.matrix.android.sdk.api.util.toMatrixItem
import org.matrix.android.sdk.internal.crypto.algorithms.olm.OlmDecryptionResult
import timber.log.Timber
@ -49,11 +53,12 @@ import javax.inject.Inject
class NotifiableEventResolver @Inject constructor(
private val stringProvider: StringProvider,
private val noticeEventFormatter: NoticeEventFormatter,
private val displayableEventFormatter: DisplayableEventFormatter) {
private val displayableEventFormatter: DisplayableEventFormatter
) {
// private val eventDisplay = RiotEventDisplay(context)
fun resolveEvent(event: Event/*, roomState: RoomState?, bingRule: PushRule?*/, session: Session, isNoisy: Boolean): NotifiableEvent? {
suspend fun resolveEvent(event: Event/*, roomState: RoomState?, bingRule: PushRule?*/, session: Session, isNoisy: Boolean): NotifiableEvent? {
val roomID = event.roomId ?: return null
val eventId = event.eventId ?: return null
if (event.getClearType() == EventType.STATE_ROOM_MEMBER) {
@ -89,7 +94,7 @@ class NotifiableEventResolver @Inject constructor(
}
}
fun resolveInMemoryEvent(session: Session, event: Event, canBeReplaced: Boolean): NotifiableEvent? {
suspend fun resolveInMemoryEvent(session: Session, event: Event, canBeReplaced: Boolean): NotifiableEvent? {
if (event.getClearType() != EventType.MESSAGE) return null
// Ignore message edition
@ -120,7 +125,7 @@ class NotifiableEventResolver @Inject constructor(
}
}
private fun resolveMessageEvent(event: TimelineEvent, session: Session, canBeReplaced: Boolean, isNoisy: Boolean): NotifiableEvent {
private suspend fun resolveMessageEvent(event: TimelineEvent, session: Session, canBeReplaced: Boolean, isNoisy: Boolean): NotifiableEvent {
// The event only contains an eventId, and roomId (type is m.room.*) , we need to get the displayable content (names, avatar, text, etc...)
val room = session.getRoom(event.root.roomId!! /*roomID cannot be null*/)
@ -140,6 +145,7 @@ class NotifiableEventResolver @Inject constructor(
senderName = senderDisplayName,
senderId = event.root.senderId,
body = body.toString(),
imageUri = event.fetchImageIfPresent(session),
roomId = event.root.roomId!!,
roomName = roomName,
matrixID = session.myUserId
@ -173,6 +179,7 @@ class NotifiableEventResolver @Inject constructor(
senderName = senderDisplayName,
senderId = event.root.senderId,
body = body,
imageUri = event.fetchImageIfPresent(session),
roomId = event.root.roomId!!,
roomName = roomName,
roomIsDirect = room.roomSummary()?.isDirect ?: false,
@ -192,6 +199,22 @@ class NotifiableEventResolver @Inject constructor(
}
}
private suspend fun TimelineEvent.fetchImageIfPresent(session: Session): Uri? {
return when {
root.isEncrypted() && root.mxDecryptionResult == null -> null
root.getClearType() == EventType.MESSAGE -> downloadAndExportImage(session)
else -> null
}
}
private suspend fun TimelineEvent.downloadAndExportImage(session: Session): Uri? {
return getLastMessageContent()?.takeAs<MessageWithAttachmentContent>()?.let { imageMessage ->
val fileService = session.fileService()
fileService.downloadFile(imageMessage)
fileService.getTemporarySharableURI(imageMessage)
}
}
private fun resolveStateRoomEvent(event: Event, session: Session, canBeReplaced: Boolean, isNoisy: Boolean): NotifiableEvent? {
val content = event.content?.toModel<RoomMemberContent>() ?: return null
val roomId = event.roomId ?: return null
@ -224,3 +247,4 @@ class NotifiableEventResolver @Inject constructor(
return null
}
}

View file

@ -15,6 +15,7 @@
*/
package im.vector.app.features.notifications
import android.net.Uri
import org.matrix.android.sdk.api.session.events.model.EventType
data class NotifiableMessageEvent(
@ -26,6 +27,7 @@ data class NotifiableMessageEvent(
val senderName: String?,
val senderId: String?,
val body: String?,
val imageUri: Uri?,
val roomId: String,
val roomName: String?,
val roomIsDirect: Boolean = false,

View file

@ -138,6 +138,7 @@ class NotificationBroadcastReceiver : BroadcastReceiver() {
?: context?.getString(R.string.notification_sender_me),
senderId = session.myUserId,
body = message,
imageUri = null,
roomId = room.roomId,
roomName = room.roomSummary()?.displayName ?: room.roomId,
roomIsDirect = room.roomSummary()?.isDirect == true,

View file

@ -16,6 +16,11 @@
package im.vector.app.features.notifications
import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.cancelChildren
import kotlinx.coroutines.launch
import org.matrix.android.sdk.api.pushrules.PushEvents
import org.matrix.android.sdk.api.pushrules.PushRuleService
import org.matrix.android.sdk.api.pushrules.getActions
@ -31,21 +36,24 @@ class PushRuleTriggerListener @Inject constructor(
) : PushRuleService.PushRuleListener {
private var session: Session? = null
private val scope: CoroutineScope = CoroutineScope(SupervisorJob())
override fun onEvents(pushEvents: PushEvents) {
session?.let { session ->
val notifiableEvents = createNotifiableEvents(pushEvents, session)
notificationDrawerManager.updateEvents { queuedEvents ->
notifiableEvents.forEach { notifiableEvent ->
queuedEvents.onNotifiableEventReceived(notifiableEvent)
scope.launch {
session?.let { session ->
val notifiableEvents = createNotifiableEvents(pushEvents, session)
notificationDrawerManager.updateEvents { queuedEvents ->
notifiableEvents.forEach { notifiableEvent ->
queuedEvents.onNotifiableEventReceived(notifiableEvent)
}
queuedEvents.syncRoomEvents(roomsLeft = pushEvents.roomsLeft, roomsJoined = pushEvents.roomsJoined)
queuedEvents.markRedacted(pushEvents.redactedEventIds)
}
queuedEvents.syncRoomEvents(roomsLeft = pushEvents.roomsLeft, roomsJoined = pushEvents.roomsJoined)
queuedEvents.markRedacted(pushEvents.redactedEventIds)
}
} ?: Timber.e("Called without active session")
} ?: Timber.e("Called without active session")
}
}
private fun createNotifiableEvents(pushEvents: PushEvents, session: Session): List<NotifiableEvent> {
private suspend fun createNotifiableEvents(pushEvents: PushEvents, session: Session): List<NotifiableEvent> {
return pushEvents.matchedEvents.mapNotNull { (event, pushRule) ->
Timber.v("Push rule match for event ${event.eventId}")
val action = pushRule.getActions().toNotificationAction()
@ -67,6 +75,7 @@ class PushRuleTriggerListener @Inject constructor(
}
fun stop() {
scope.coroutineContext.cancelChildren(CancellationException("PushRuleTriggerListener stopping"))
session?.removePushRuleListener(this)
session = null
notificationDrawerManager.clearAllEvents()

View file

@ -19,6 +19,7 @@ package im.vector.app.features.notifications
import android.content.Context
import android.graphics.Bitmap
import android.os.Build
import android.util.Log
import androidx.core.app.NotificationCompat
import androidx.core.app.Person
import androidx.core.content.pm.ShortcutInfoCompat
@ -103,6 +104,7 @@ class RoomGroupMessageCreator @Inject constructor(
private fun NotificationCompat.MessagingStyle.addMessagesFromEvents(events: List<NotifiableMessageEvent>) {
events.forEach { event ->
Log.e("!!!", "event: $event")
val senderPerson = if (event.outGoingMessage) {
null
} else {
@ -114,7 +116,14 @@ class RoomGroupMessageCreator @Inject constructor(
}
when {
event.isSmartReplyError() -> addMessage(stringProvider.getString(R.string.notification_inline_reply_failed), event.timestamp, senderPerson)
else -> addMessage(event.body, event.timestamp, senderPerson)
else -> {
val message = NotificationCompat.MessagingStyle.Message(event.body, event.timestamp, senderPerson).also { message ->
event.imageUri?.let {
message.setData("image/", it)
}
}
addMessage(message)
}
}
}
}