Making progress on notification for multi account.
This commit is contained in:
parent
3d1823064d
commit
0c0f7dff40
28 changed files with 483 additions and 620 deletions
|
|
@ -33,4 +33,9 @@ class IntentProviderImpl @Inject constructor(
|
||||||
override fun getMainIntent(): Intent {
|
override fun getMainIntent(): Intent {
|
||||||
return Intent(context, MainActivity::class.java)
|
return Intent(context, MainActivity::class.java)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun getIntent(sessionId: String, roomId: String?, threadId: String?): Intent {
|
||||||
|
// TODO Handle deeplink or pass parameters
|
||||||
|
return Intent(context, MainActivity::class.java)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -19,12 +19,6 @@ package io.element.android.libraries.push.api
|
||||||
import io.element.android.libraries.matrix.api.MatrixClient
|
import io.element.android.libraries.matrix.api.MatrixClient
|
||||||
|
|
||||||
interface PushService {
|
interface PushService {
|
||||||
// TODO EAx remove
|
|
||||||
fun setCurrentRoom(roomId: String?)
|
|
||||||
|
|
||||||
// TODO EAx remove
|
|
||||||
fun setCurrentThread(threadId: String?)
|
|
||||||
|
|
||||||
fun notificationStyleChanged()
|
fun notificationStyleChanged()
|
||||||
|
|
||||||
// Ensure pusher is registered
|
// Ensure pusher is registered
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,7 @@ import kotlinx.coroutines.flow.Flow
|
||||||
interface PushDataStore {
|
interface PushDataStore {
|
||||||
val pushCounterFlow: Flow<Int>
|
val pushCounterFlow: Flow<Int>
|
||||||
|
|
||||||
|
// TODO Move all those settings to the per user store...
|
||||||
fun areNotificationEnabledForDevice(): Boolean
|
fun areNotificationEnabledForDevice(): Boolean
|
||||||
fun setNotificationEnabledForDevice(enabled: Boolean)
|
fun setNotificationEnabledForDevice(enabled: Boolean)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -46,6 +46,7 @@ dependencies {
|
||||||
api(projects.libraries.push.api)
|
api(projects.libraries.push.api)
|
||||||
|
|
||||||
implementation(projects.services.analytics.api)
|
implementation(projects.services.analytics.api)
|
||||||
|
implementation(projects.services.appnavstate.api)
|
||||||
implementation(projects.services.toolbox.api)
|
implementation(projects.services.toolbox.api)
|
||||||
|
|
||||||
api("me.gujun.android:span:1.7") {
|
api("me.gujun.android:span:1.7") {
|
||||||
|
|
|
||||||
|
|
@ -60,5 +60,15 @@
|
||||||
<action android:name="org.unifiedpush.android.distributor.REGISTER" />
|
<action android:name="org.unifiedpush.android.distributor.REGISTER" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</receiver>
|
</receiver>
|
||||||
|
|
||||||
|
<receiver
|
||||||
|
android:name=".notifications.TestNotificationReceiver"
|
||||||
|
android:exported="false" />
|
||||||
|
|
||||||
|
<receiver
|
||||||
|
android:name=".notifications.NotificationBroadcastReceiver"
|
||||||
|
android:enabled="true"
|
||||||
|
android:exported="false" />
|
||||||
|
|
||||||
</application>
|
</application>
|
||||||
</manifest>
|
</manifest>
|
||||||
|
|
|
||||||
|
|
@ -32,14 +32,6 @@ class DefaultPushService @Inject constructor(
|
||||||
private val pushersManager: PushersManager,
|
private val pushersManager: PushersManager,
|
||||||
private val fcmHelper: FcmHelper,
|
private val fcmHelper: FcmHelper,
|
||||||
) : PushService {
|
) : PushService {
|
||||||
override fun setCurrentRoom(roomId: String?) {
|
|
||||||
notificationDrawerManager.setCurrentRoom(roomId)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun setCurrentThread(threadId: String?) {
|
|
||||||
notificationDrawerManager.setCurrentThread(threadId)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun notificationStyleChanged() {
|
override fun notificationStyleChanged() {
|
||||||
notificationDrawerManager.notificationStyleChanged()
|
notificationDrawerManager.notificationStyleChanged()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,13 @@ import android.content.Intent
|
||||||
|
|
||||||
interface IntentProvider {
|
interface IntentProvider {
|
||||||
/**
|
/**
|
||||||
* Provide an intent to start the application
|
* Provide an intent to start the application.
|
||||||
*/
|
*/
|
||||||
fun getMainIntent(): Intent
|
fun getMainIntent(): Intent
|
||||||
|
|
||||||
|
fun getIntent(
|
||||||
|
sessionId: String,
|
||||||
|
roomId: String?,
|
||||||
|
threadId: String?,
|
||||||
|
): Intent
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -19,3 +19,4 @@ package io.element.android.libraries.push.impl.log
|
||||||
import io.element.android.libraries.core.log.logger.LoggerTag
|
import io.element.android.libraries.core.log.logger.LoggerTag
|
||||||
|
|
||||||
internal val pushLoggerTag = LoggerTag("Push")
|
internal val pushLoggerTag = LoggerTag("Push")
|
||||||
|
internal val notificationLoggerTag = LoggerTag("Notification", pushLoggerTag)
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@
|
||||||
package io.element.android.libraries.push.impl.notifications
|
package io.element.android.libraries.push.impl.notifications
|
||||||
|
|
||||||
import io.element.android.libraries.push.impl.notifications.model.*
|
import io.element.android.libraries.push.impl.notifications.model.*
|
||||||
|
import io.element.android.services.appnavstate.api.AppNavigationState
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
|
@ -26,12 +27,16 @@ class NotifiableEventProcessor @Inject constructor(
|
||||||
private val outdatedDetector: OutdatedEventDetector,
|
private val outdatedDetector: OutdatedEventDetector,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
fun process(queuedEvents: List<NotifiableEvent>, currentRoomId: String?, currentThreadId: String?, renderedEvents: ProcessedEvents): ProcessedEvents {
|
fun process(
|
||||||
|
queuedEvents: List<NotifiableEvent>,
|
||||||
|
appNavigationState: AppNavigationState?,
|
||||||
|
renderedEvents: ProcessedEvents,
|
||||||
|
): ProcessedEvents {
|
||||||
val processedEvents = queuedEvents.map {
|
val processedEvents = queuedEvents.map {
|
||||||
val type = when (it) {
|
val type = when (it) {
|
||||||
is InviteNotifiableEvent -> ProcessedEvent.Type.KEEP
|
is InviteNotifiableEvent -> ProcessedEvent.Type.KEEP
|
||||||
is NotifiableMessageEvent -> when {
|
is NotifiableMessageEvent -> when {
|
||||||
it.shouldIgnoreMessageEventInRoom(currentRoomId, currentThreadId) -> {
|
it.shouldIgnoreMessageEventInRoom(appNavigationState) -> {
|
||||||
ProcessedEvent.Type.REMOVE
|
ProcessedEvent.Type.REMOVE
|
||||||
.also { Timber.d("notification message removed due to currently viewing the same room or thread") }
|
.also { Timber.d("notification message removed due to currently viewing the same room or thread") }
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -15,13 +15,28 @@
|
||||||
*/
|
*/
|
||||||
package io.element.android.libraries.push.impl.notifications
|
package io.element.android.libraries.push.impl.notifications
|
||||||
|
|
||||||
|
import io.element.android.libraries.core.log.logger.LoggerTag
|
||||||
import io.element.android.libraries.core.meta.BuildMeta
|
import io.element.android.libraries.core.meta.BuildMeta
|
||||||
|
import io.element.android.libraries.matrix.api.auth.MatrixAuthenticationService
|
||||||
|
import io.element.android.libraries.matrix.api.core.EventId
|
||||||
|
import io.element.android.libraries.matrix.api.core.SessionId
|
||||||
|
import io.element.android.libraries.matrix.api.core.UserId
|
||||||
|
import io.element.android.libraries.matrix.api.notification.NotificationData
|
||||||
|
import io.element.android.libraries.matrix.api.timeline.MatrixTimelineItem
|
||||||
|
import io.element.android.libraries.matrix.api.timeline.item.event.EventTimelineItem
|
||||||
|
import io.element.android.libraries.matrix.api.timeline.item.event.MessageContent
|
||||||
|
import io.element.android.libraries.matrix.api.timeline.item.event.ProfileTimelineDetails
|
||||||
|
import io.element.android.libraries.matrix.api.timeline.item.event.TextMessageType
|
||||||
|
import io.element.android.libraries.push.impl.log.pushLoggerTag
|
||||||
import io.element.android.libraries.push.impl.notifications.model.NotifiableEvent
|
import io.element.android.libraries.push.impl.notifications.model.NotifiableEvent
|
||||||
import io.element.android.libraries.push.impl.notifications.model.NotifiableMessageEvent
|
import io.element.android.libraries.push.impl.notifications.model.NotifiableMessageEvent
|
||||||
import io.element.android.services.toolbox.api.strings.StringProvider
|
import io.element.android.services.toolbox.api.strings.StringProvider
|
||||||
import io.element.android.services.toolbox.api.systemclock.SystemClock
|
import io.element.android.services.toolbox.api.systemclock.SystemClock
|
||||||
|
import timber.log.Timber
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
private val loggerTag = LoggerTag("NotifiableEventResolver", pushLoggerTag)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The notifiable event resolver is able to create a NotifiableEvent (view model for notifications) from an sdk Event.
|
* The notifiable event resolver is able to create a NotifiableEvent (view model for notifications) from an sdk Event.
|
||||||
* It is used as a bridge between the Event Thread and the NotificationDrawerManager.
|
* It is used as a bridge between the Event Thread and the NotificationDrawerManager.
|
||||||
|
|
@ -33,232 +48,92 @@ class NotifiableEventResolver @Inject constructor(
|
||||||
// private val noticeEventFormatter: NoticeEventFormatter,
|
// private val noticeEventFormatter: NoticeEventFormatter,
|
||||||
// private val displayableEventFormatter: DisplayableEventFormatter,
|
// private val displayableEventFormatter: DisplayableEventFormatter,
|
||||||
private val clock: SystemClock,
|
private val clock: SystemClock,
|
||||||
|
private val matrixAuthenticationService: MatrixAuthenticationService,
|
||||||
private val buildMeta: BuildMeta,
|
private val buildMeta: BuildMeta,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
suspend fun resolveEvent(/*event: Event, session: Session, isNoisy: Boolean*/): NotifiableEvent? {
|
suspend fun resolveEvent(userId: String, roomId: String, eventId: String): NotifiableEvent? {
|
||||||
return TODO()
|
// Restore session
|
||||||
/*
|
val session = matrixAuthenticationService.restoreSession(SessionId(userId)).getOrNull() ?: return null
|
||||||
val roomID = event.roomId ?: return null
|
// TODO EAx, no need for a session?
|
||||||
val eventId = event.eventId ?: return null
|
val notificationData = session.let {// TODO Use make the app crashes
|
||||||
if (event.getClearType() == EventType.STATE_ROOM_MEMBER) {
|
it.notificationService().getNotification(
|
||||||
return resolveStateRoomEvent(event, session, canBeReplaced = false, isNoisy = isNoisy)
|
userId = userId,
|
||||||
}
|
roomId = roomId,
|
||||||
val timelineEvent = session.getRoom(roomID)?.getTimelineEvent(eventId) ?: return null
|
eventId = eventId,
|
||||||
return when {
|
|
||||||
event.supportsNotification() || event.type == EventType.ENCRYPTED -> {
|
|
||||||
resolveMessageEvent(timelineEvent, session, canBeReplaced = false, isNoisy = isNoisy)
|
|
||||||
}
|
|
||||||
else -> {
|
|
||||||
// If the event can be displayed, display it as is
|
|
||||||
Timber.w("NotifiableEventResolver Received an unsupported event matching a bing rule")
|
|
||||||
// TODO Better event text display
|
|
||||||
val bodyPreview = event.type ?: EventType.MISSING_TYPE
|
|
||||||
|
|
||||||
SimpleNotifiableEvent(
|
|
||||||
session.myUserId,
|
|
||||||
eventId = event.eventId!!,
|
|
||||||
editedEventId = timelineEvent.getEditedEventId(),
|
|
||||||
noisy = false, // will be updated
|
|
||||||
timestamp = event.originServerTs ?: clock.epochMillis(),
|
|
||||||
description = bodyPreview,
|
|
||||||
title = stringProvider.getString(StringR.string.notification_unknown_new_event),
|
|
||||||
soundName = null,
|
|
||||||
type = event.type,
|
|
||||||
canBeReplaced = false
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
|
|
||||||
suspend fun resolveInMemoryEvent(/*session: Session, event: Event, canBeReplaced: Boolean*/): NotifiableEvent? {
|
|
||||||
TODO()
|
|
||||||
/*
|
|
||||||
if (!event.supportsNotification()) return null
|
|
||||||
|
|
||||||
// Ignore message edition
|
|
||||||
if (event.isEdition()) return null
|
|
||||||
|
|
||||||
val actions = session.pushRuleService().getActions(event)
|
|
||||||
val notificationAction = actions.toNotificationAction()
|
|
||||||
|
|
||||||
return if (notificationAction.shouldNotify) {
|
|
||||||
val user = session.getUserOrDefault(event.senderId!!)
|
|
||||||
|
|
||||||
val timelineEvent = TimelineEvent(
|
|
||||||
root = event,
|
|
||||||
localId = -1,
|
|
||||||
eventId = event.eventId!!,
|
|
||||||
displayIndex = 0,
|
|
||||||
senderInfo = SenderInfo(
|
|
||||||
userId = user.userId,
|
|
||||||
displayName = user.toMatrixItem().getBestName(),
|
|
||||||
isUniqueDisplayName = true,
|
|
||||||
avatarUrl = user.avatarUrl
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
resolveMessageEvent(timelineEvent, session, canBeReplaced = canBeReplaced, isNoisy = !notificationAction.soundName.isNullOrBlank())
|
}.fold(
|
||||||
} else {
|
{
|
||||||
Timber.d("Matched push rule is set to not notify")
|
it
|
||||||
null
|
},
|
||||||
}
|
{
|
||||||
|
Timber.tag(loggerTag.value).e(it, "Unable to resolve event.")
|
||||||
*/
|
null
|
||||||
}
|
|
||||||
|
|
||||||
private suspend fun resolveMessageEvent(/*event: TimelineEvent, session: Session, canBeReplaced: Boolean, isNoisy: Boolean*/): NotifiableMessageEvent? {
|
|
||||||
TODO()
|
|
||||||
/*
|
|
||||||
// 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*/)
|
|
||||||
|
|
||||||
return if (room == null) {
|
|
||||||
Timber.e("## Unable to resolve room for eventId [$event]")
|
|
||||||
// Ok room is not known in store, but we can still display something
|
|
||||||
val body = displayableEventFormatter.format(event, isDm = false, appendAuthor = false)
|
|
||||||
val roomName = stringProvider.getString(StringR.string.notification_unknown_room_name)
|
|
||||||
val senderDisplayName = event.senderInfo.disambiguatedDisplayName
|
|
||||||
|
|
||||||
NotifiableMessageEvent(
|
|
||||||
eventId = event.root.eventId!!,
|
|
||||||
editedEventId = event.getEditedEventId(),
|
|
||||||
canBeReplaced = canBeReplaced,
|
|
||||||
timestamp = event.root.originServerTs ?: 0,
|
|
||||||
noisy = isNoisy,
|
|
||||||
senderName = senderDisplayName,
|
|
||||||
senderId = event.root.senderId,
|
|
||||||
body = body.toString(),
|
|
||||||
imageUriString = event.fetchImageIfPresent(session)?.toString(),
|
|
||||||
roomId = event.root.roomId!!,
|
|
||||||
threadId = event.root.getRootThreadEventId(),
|
|
||||||
roomName = roomName,
|
|
||||||
matrixID = session.myUserId
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
event.attemptToDecryptIfNeeded(session)
|
|
||||||
// only convert encrypted messages to NotifiableMessageEvents
|
|
||||||
when {
|
|
||||||
event.root.supportsNotification() -> {
|
|
||||||
val body = displayableEventFormatter.format(event, isDm = room.roomSummary()?.isDirect.orFalse(), appendAuthor = false).toString()
|
|
||||||
val roomName = room.roomSummary()?.displayName ?: ""
|
|
||||||
val senderDisplayName = event.senderInfo.disambiguatedDisplayName
|
|
||||||
|
|
||||||
NotifiableMessageEvent(
|
|
||||||
eventId = event.root.eventId!!,
|
|
||||||
editedEventId = event.getEditedEventId(),
|
|
||||||
canBeReplaced = canBeReplaced,
|
|
||||||
timestamp = event.root.originServerTs ?: 0,
|
|
||||||
noisy = isNoisy,
|
|
||||||
senderName = senderDisplayName,
|
|
||||||
senderId = event.root.senderId,
|
|
||||||
body = body,
|
|
||||||
imageUriString = event.fetchImageIfPresent(session)?.toString(),
|
|
||||||
roomId = event.root.roomId!!,
|
|
||||||
threadId = event.root.getRootThreadEventId(),
|
|
||||||
roomName = roomName,
|
|
||||||
roomIsDirect = room.roomSummary()?.isDirect ?: false,
|
|
||||||
roomAvatarPath = session.contentUrlResolver()
|
|
||||||
.resolveThumbnail(
|
|
||||||
room.roomSummary()?.avatarUrl,
|
|
||||||
250,
|
|
||||||
250,
|
|
||||||
ContentUrlResolver.ThumbnailMethod.SCALE
|
|
||||||
),
|
|
||||||
senderAvatarPath = session.contentUrlResolver()
|
|
||||||
.resolveThumbnail(
|
|
||||||
event.senderInfo.avatarUrl,
|
|
||||||
250,
|
|
||||||
250,
|
|
||||||
ContentUrlResolver.ThumbnailMethod.SCALE
|
|
||||||
),
|
|
||||||
matrixID = session.myUserId,
|
|
||||||
soundName = null
|
|
||||||
)
|
|
||||||
}
|
|
||||||
else -> null
|
|
||||||
}
|
}
|
||||||
}
|
).orDefault(roomId, eventId)
|
||||||
|
|
||||||
*/
|
return notificationData.asNotifiableEvent(userId, roomId, eventId)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
/*
|
|
||||||
private suspend fun TimelineEvent.attemptToDecryptIfNeeded(session: Session) {
|
private fun NotificationData.asNotifiableEvent(userId: String, roomId: String, eventId: String): NotifiableEvent {
|
||||||
if (root.isEncrypted() && root.mxDecryptionResult == null) {
|
return NotifiableMessageEvent(
|
||||||
// TODO use a global event decryptor? attache to session and that listen to new sessionId?
|
sessionId = userId,
|
||||||
// for now decrypt sync
|
roomId = roomId,
|
||||||
try {
|
eventId = eventId,
|
||||||
val result = session.cryptoService().decryptEvent(root, root.roomId + UUID.randomUUID().toString())
|
editedEventId = null,
|
||||||
root.mxDecryptionResult = OlmDecryptionResult(
|
canBeReplaced = true,
|
||||||
payload = result.clearEvent,
|
noisy = false,
|
||||||
senderKey = result.senderCurve25519Key,
|
timestamp = System.currentTimeMillis(),
|
||||||
keysClaimed = result.claimedEd25519Key?.let { mapOf("ed25519" to it) },
|
senderName = null,
|
||||||
forwardingCurve25519KeyChain = result.forwardingCurve25519KeyChain,
|
senderId = null,
|
||||||
isSafe = result.isSafe
|
body = "$eventId in $roomId",
|
||||||
)
|
imageUriString = null,
|
||||||
} catch (ignore: MXCryptoError) {
|
threadId = null,
|
||||||
}
|
roomName = null,
|
||||||
}
|
roomIsDirect = false,
|
||||||
}
|
roomAvatarPath = null,
|
||||||
*/
|
senderAvatarPath = null,
|
||||||
|
soundName = null,
|
||||||
/*
|
outGoingMessage = false,
|
||||||
private suspend fun TimelineEvent.fetchImageIfPresent(session: Session): Uri? {
|
outGoingMessageFailed = false,
|
||||||
return when {
|
isRedacted = false,
|
||||||
root.isEncrypted() && root.mxDecryptionResult == null -> null
|
isUpdated = false
|
||||||
root.isImageMessage() -> downloadAndExportImage(session)
|
)
|
||||||
else -> null
|
}
|
||||||
}
|
|
||||||
}
|
/**
|
||||||
*/
|
* TODO This is a temporary method for EAx
|
||||||
|
*/
|
||||||
/*
|
private fun NotificationData?.orDefault(roomId: String, eventId: String): NotificationData {
|
||||||
private suspend fun TimelineEvent.downloadAndExportImage(session: Session): Uri? {
|
return this ?: NotificationData(
|
||||||
return kotlin.runCatching {
|
item = MatrixTimelineItem.Event(
|
||||||
getVectorLastMessageContent()?.takeAs<MessageWithAttachmentContent>()?.let { imageMessage ->
|
event = EventTimelineItem(
|
||||||
val fileService = session.fileService()
|
uniqueIdentifier = eventId,
|
||||||
fileService.downloadFile(imageMessage)
|
eventId = EventId(eventId),
|
||||||
fileService.getTemporarySharableURI(imageMessage)
|
isEditable = false,
|
||||||
}
|
isLocal = false,
|
||||||
}.onFailure {
|
isOwn = false,
|
||||||
Timber.e(it, "Failed to download and export image for notification")
|
isRemote = false,
|
||||||
}.getOrNull()
|
localSendState = null,
|
||||||
}
|
reactions = emptyList(),
|
||||||
*/
|
sender = UserId(""),
|
||||||
|
senderProfile = ProfileTimelineDetails.Unavailable,
|
||||||
/*
|
timestamp = System.currentTimeMillis(),
|
||||||
private fun resolveStateRoomEvent(event: Event, session: Session, canBeReplaced: Boolean, isNoisy: Boolean): NotifiableEvent? {
|
content = MessageContent(
|
||||||
val content = event.content?.toModel<RoomMemberContent>() ?: return null
|
body = eventId,
|
||||||
val roomId = event.roomId ?: return null
|
inReplyTo = null,
|
||||||
val dName = event.senderId?.let { session.roomService().getRoomMember(it, roomId)?.displayName }
|
isEdited = false,
|
||||||
if (Membership.INVITE == content.membership) {
|
type = TextMessageType(
|
||||||
val roomSummary = session.getRoomSummary(roomId)
|
body = eventId,
|
||||||
val body = noticeEventFormatter.format(event, dName, isDm = roomSummary?.isDirect.orFalse())
|
formatted = null
|
||||||
?: stringProvider.getString(StringR.string.notification_new_invitation)
|
)
|
||||||
return InviteNotifiableEvent(
|
)
|
||||||
session.myUserId,
|
),
|
||||||
eventId = event.eventId!!,
|
),
|
||||||
editedEventId = null,
|
title = roomId,
|
||||||
canBeReplaced = canBeReplaced,
|
subtitle = eventId,
|
||||||
roomId = roomId,
|
isNoisy = false,
|
||||||
roomName = roomSummary?.displayName,
|
avatarUrl = null,
|
||||||
timestamp = event.originServerTs ?: 0,
|
)
|
||||||
noisy = isNoisy,
|
|
||||||
title = stringProvider.getString(StringR.string.notification_new_invitation),
|
|
||||||
description = body.toString(),
|
|
||||||
soundName = null, // will be set later
|
|
||||||
type = event.getClearType()
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
Timber.e("## unsupported notifiable event for eventId [${event.eventId}]")
|
|
||||||
if (buildMeta.lowPrivacyLoggingEnabled) {
|
|
||||||
Timber.e("## unsupported notifiable event for event [$event]")
|
|
||||||
}
|
|
||||||
// TODO generic handling?
|
|
||||||
}
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,6 @@ import javax.inject.Inject
|
||||||
data class NotificationActionIds @Inject constructor(
|
data class NotificationActionIds @Inject constructor(
|
||||||
private val buildMeta: BuildMeta,
|
private val buildMeta: BuildMeta,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
val join = "${buildMeta.applicationId}.NotificationActions.JOIN_ACTION"
|
val join = "${buildMeta.applicationId}.NotificationActions.JOIN_ACTION"
|
||||||
val reject = "${buildMeta.applicationId}.NotificationActions.REJECT_ACTION"
|
val reject = "${buildMeta.applicationId}.NotificationActions.REJECT_ACTION"
|
||||||
val quickLaunch = "${buildMeta.applicationId}.NotificationActions.QUICK_LAUNCH_ACTION"
|
val quickLaunch = "${buildMeta.applicationId}.NotificationActions.QUICK_LAUNCH_ACTION"
|
||||||
|
|
|
||||||
|
|
@ -21,11 +21,15 @@ import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import androidx.core.app.RemoteInput
|
import androidx.core.app.RemoteInput
|
||||||
import io.element.android.libraries.architecture.bindings
|
import io.element.android.libraries.architecture.bindings
|
||||||
|
import io.element.android.libraries.core.log.logger.LoggerTag
|
||||||
|
import io.element.android.libraries.push.impl.log.notificationLoggerTag
|
||||||
import io.element.android.services.analytics.api.AnalyticsTracker
|
import io.element.android.services.analytics.api.AnalyticsTracker
|
||||||
import io.element.android.services.toolbox.api.systemclock.SystemClock
|
import io.element.android.services.toolbox.api.systemclock.SystemClock
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
private val loggerTag = LoggerTag("NotificationBroadcastReceiver", notificationLoggerTag)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Receives actions broadcast by notification (on click, on dismiss, inline replies, etc.).
|
* Receives actions broadcast by notification (on click, on dismiss, inline replies, etc.).
|
||||||
*/
|
*/
|
||||||
|
|
@ -41,37 +45,38 @@ class NotificationBroadcastReceiver : BroadcastReceiver() {
|
||||||
override fun onReceive(context: Context?, intent: Intent?) {
|
override fun onReceive(context: Context?, intent: Intent?) {
|
||||||
if (intent == null || context == null) return
|
if (intent == null || context == null) return
|
||||||
context.bindings<NotificationBroadcastReceiverBindings>().inject(this)
|
context.bindings<NotificationBroadcastReceiverBindings>().inject(this)
|
||||||
Timber.v("NotificationBroadcastReceiver received : $intent")
|
Timber.tag(loggerTag.value).v("NotificationBroadcastReceiver received : $intent")
|
||||||
|
val sessionId = intent.extras?.getString(KEY_SESSION_ID) ?: return
|
||||||
when (intent.action) {
|
when (intent.action) {
|
||||||
actionIds.smartReply ->
|
actionIds.smartReply ->
|
||||||
handleSmartReply(intent, context)
|
handleSmartReply(intent, context)
|
||||||
actionIds.dismissRoom ->
|
actionIds.dismissRoom ->
|
||||||
intent.getStringExtra(KEY_ROOM_ID)?.let { roomId ->
|
intent.getStringExtra(KEY_ROOM_ID)?.let { roomId ->
|
||||||
notificationDrawerManager.updateEvents { it.clearMessagesForRoom(roomId) }
|
notificationDrawerManager.updateEvents { it.clearMessagesForRoom(sessionId, roomId) }
|
||||||
}
|
}
|
||||||
actionIds.dismissSummary ->
|
actionIds.dismissSummary ->
|
||||||
notificationDrawerManager.clearAllEvents()
|
notificationDrawerManager.clearAllEvents(sessionId)
|
||||||
actionIds.markRoomRead ->
|
actionIds.markRoomRead ->
|
||||||
intent.getStringExtra(KEY_ROOM_ID)?.let { roomId ->
|
intent.getStringExtra(KEY_ROOM_ID)?.let { roomId ->
|
||||||
notificationDrawerManager.updateEvents { it.clearMessagesForRoom(roomId) }
|
notificationDrawerManager.updateEvents { it.clearMessagesForRoom(sessionId, roomId) }
|
||||||
handleMarkAsRead(roomId)
|
handleMarkAsRead(sessionId, roomId)
|
||||||
}
|
}
|
||||||
actionIds.join -> {
|
actionIds.join -> {
|
||||||
intent.getStringExtra(KEY_ROOM_ID)?.let { roomId ->
|
intent.getStringExtra(KEY_ROOM_ID)?.let { roomId ->
|
||||||
notificationDrawerManager.updateEvents { it.clearMemberShipNotificationForRoom(roomId) }
|
notificationDrawerManager.updateEvents { it.clearMemberShipNotificationForRoom(sessionId, roomId) }
|
||||||
handleJoinRoom(roomId)
|
handleJoinRoom(sessionId, roomId)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
actionIds.reject -> {
|
actionIds.reject -> {
|
||||||
intent.getStringExtra(KEY_ROOM_ID)?.let { roomId ->
|
intent.getStringExtra(KEY_ROOM_ID)?.let { roomId ->
|
||||||
notificationDrawerManager.updateEvents { it.clearMemberShipNotificationForRoom(roomId) }
|
notificationDrawerManager.updateEvents { it.clearMemberShipNotificationForRoom(sessionId, roomId) }
|
||||||
handleRejectRoom(roomId)
|
handleRejectRoom(sessionId, roomId)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun handleJoinRoom(roomId: String) {
|
private fun handleJoinRoom(sessionId: String, roomId: String) {
|
||||||
/*
|
/*
|
||||||
activeSessionHolder.getSafeActiveSession()?.let { session ->
|
activeSessionHolder.getSafeActiveSession()?.let { session ->
|
||||||
val room = session.getRoom(roomId)
|
val room = session.getRoom(roomId)
|
||||||
|
|
@ -88,7 +93,7 @@ class NotificationBroadcastReceiver : BroadcastReceiver() {
|
||||||
*/
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun handleRejectRoom(roomId: String) {
|
private fun handleRejectRoom(sessionId: String, roomId: String) {
|
||||||
/*
|
/*
|
||||||
activeSessionHolder.getSafeActiveSession()?.let { session ->
|
activeSessionHolder.getSafeActiveSession()?.let { session ->
|
||||||
session.coroutineScope.launch {
|
session.coroutineScope.launch {
|
||||||
|
|
@ -99,7 +104,7 @@ class NotificationBroadcastReceiver : BroadcastReceiver() {
|
||||||
*/
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun handleMarkAsRead(roomId: String) {
|
private fun handleMarkAsRead(sessionId: String, roomId: String) {
|
||||||
/*
|
/*
|
||||||
activeSessionHolder.getActiveSession().let { session ->
|
activeSessionHolder.getActiveSession().let { session ->
|
||||||
val room = session.getRoom(roomId)
|
val room = session.getRoom(roomId)
|
||||||
|
|
@ -115,6 +120,7 @@ class NotificationBroadcastReceiver : BroadcastReceiver() {
|
||||||
|
|
||||||
private fun handleSmartReply(intent: Intent, context: Context) {
|
private fun handleSmartReply(intent: Intent, context: Context) {
|
||||||
val message = getReplyMessage(intent)
|
val message = getReplyMessage(intent)
|
||||||
|
val sessionId = intent.getStringExtra(KEY_SESSION_ID)
|
||||||
val roomId = intent.getStringExtra(KEY_ROOM_ID)
|
val roomId = intent.getStringExtra(KEY_ROOM_ID)
|
||||||
val threadId = intent.getStringExtra(KEY_THREAD_ID)
|
val threadId = intent.getStringExtra(KEY_THREAD_ID)
|
||||||
|
|
||||||
|
|
@ -234,6 +240,7 @@ class NotificationBroadcastReceiver : BroadcastReceiver() {
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
const val KEY_SESSION_ID = "sessionID"
|
||||||
const val KEY_ROOM_ID = "roomID"
|
const val KEY_ROOM_ID = "roomID"
|
||||||
const val KEY_THREAD_ID = "threadID"
|
const val KEY_THREAD_ID = "threadID"
|
||||||
const val KEY_TEXT_REPLY = "key_text_reply"
|
const val KEY_TEXT_REPLY = "key_text_reply"
|
||||||
|
|
|
||||||
|
|
@ -26,8 +26,6 @@ import io.element.android.libraries.di.ApplicationContext
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
const val TEMPORARY_ID = 101
|
|
||||||
|
|
||||||
class NotificationDisplayer @Inject constructor(
|
class NotificationDisplayer @Inject constructor(
|
||||||
@ApplicationContext private val context: Context,
|
@ApplicationContext private val context: Context,
|
||||||
) {
|
) {
|
||||||
|
|
|
||||||
|
|
@ -30,6 +30,10 @@ import io.element.android.libraries.push.impl.R
|
||||||
import io.element.android.libraries.push.impl.notifications.model.NotifiableEvent
|
import io.element.android.libraries.push.impl.notifications.model.NotifiableEvent
|
||||||
import io.element.android.libraries.push.impl.notifications.model.NotifiableMessageEvent
|
import io.element.android.libraries.push.impl.notifications.model.NotifiableMessageEvent
|
||||||
import io.element.android.libraries.push.impl.notifications.model.shouldIgnoreMessageEventInRoom
|
import io.element.android.libraries.push.impl.notifications.model.shouldIgnoreMessageEventInRoom
|
||||||
|
import io.element.android.services.appnavstate.api.AppNavigationState
|
||||||
|
import io.element.android.services.appnavstate.api.AppNavigationStateService
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
|
@ -42,31 +46,24 @@ import javax.inject.Inject
|
||||||
class NotificationDrawerManager @Inject constructor(
|
class NotificationDrawerManager @Inject constructor(
|
||||||
@ApplicationContext context: Context,
|
@ApplicationContext context: Context,
|
||||||
private val pushDataStore: PushDataStore,
|
private val pushDataStore: PushDataStore,
|
||||||
// private val activeSessionDataSource: ActiveSessionDataSource,
|
|
||||||
private val notifiableEventProcessor: NotifiableEventProcessor,
|
private val notifiableEventProcessor: NotifiableEventProcessor,
|
||||||
private val notificationRenderer: NotificationRenderer,
|
private val notificationRenderer: NotificationRenderer,
|
||||||
private val notificationEventPersistence: NotificationEventPersistence,
|
private val notificationEventPersistence: NotificationEventPersistence,
|
||||||
private val filteredEventDetector: FilteredEventDetector,
|
private val filteredEventDetector: FilteredEventDetector,
|
||||||
|
private val appNavigationStateService: AppNavigationStateService,
|
||||||
|
private val coroutineScope: CoroutineScope,
|
||||||
private val buildMeta: BuildMeta,
|
private val buildMeta: BuildMeta,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
private val handlerThread: HandlerThread = HandlerThread("NotificationDrawerManager", Thread.MIN_PRIORITY)
|
private val handlerThread: HandlerThread = HandlerThread("NotificationDrawerManager", Thread.MIN_PRIORITY)
|
||||||
private var backgroundHandler: Handler
|
private var backgroundHandler: Handler
|
||||||
|
|
||||||
// TODO Multi-session: this will have to be improved
|
|
||||||
/*
|
|
||||||
private val currentSession: Session?
|
|
||||||
get() = activeSessionDataSource.currentValue?.orNull()
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Lazily initializes the NotificationState as we rely on having a current session in order to fetch the persisted queue of events.
|
* Lazily initializes the NotificationState as we rely on having a current session in order to fetch the persisted queue of events.
|
||||||
*/
|
*/
|
||||||
private val notificationState by lazy { createInitialNotificationState() }
|
private val notificationState by lazy { createInitialNotificationState() }
|
||||||
private val avatarSize = context.resources.getDimensionPixelSize(R.dimen.profile_avatar_size)
|
private val avatarSize = context.resources.getDimensionPixelSize(R.dimen.profile_avatar_size)
|
||||||
private var currentRoomId: String? = null
|
private var currentAppNavigationState: AppNavigationState? = null
|
||||||
private var currentThreadId: String? = null
|
|
||||||
private val firstThrottler = FirstThrottler(200)
|
private val firstThrottler = FirstThrottler(200)
|
||||||
|
|
||||||
private var useCompleteNotificationFormat = pushDataStore.useCompleteNotificationFormat()
|
private var useCompleteNotificationFormat = pushDataStore.useCompleteNotificationFormat()
|
||||||
|
|
@ -74,6 +71,31 @@ class NotificationDrawerManager @Inject constructor(
|
||||||
init {
|
init {
|
||||||
handlerThread.start()
|
handlerThread.start()
|
||||||
backgroundHandler = Handler(handlerThread.looper)
|
backgroundHandler = Handler(handlerThread.looper)
|
||||||
|
// Observe application state
|
||||||
|
coroutineScope.launch {
|
||||||
|
appNavigationStateService.appNavigationStateFlow
|
||||||
|
.collect { onAppNavigationStateChange(it) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun onAppNavigationStateChange(appNavigationState: AppNavigationState) {
|
||||||
|
currentAppNavigationState = appNavigationState
|
||||||
|
when (appNavigationState) {
|
||||||
|
AppNavigationState.Root -> {}
|
||||||
|
is AppNavigationState.Session -> {}
|
||||||
|
is AppNavigationState.Space -> {}
|
||||||
|
is AppNavigationState.Room -> {
|
||||||
|
// Cleanup notification for current room
|
||||||
|
onEnteringRoom(appNavigationState.parentSpace.parentSession.sessionId.value, appNavigationState.roomId.value)
|
||||||
|
}
|
||||||
|
is AppNavigationState.Thread -> {
|
||||||
|
onEnteringThread(
|
||||||
|
appNavigationState.parentRoom.parentSpace.parentSession.sessionId.value,
|
||||||
|
appNavigationState.parentRoom.roomId.value,
|
||||||
|
appNavigationState.threadId.value
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun createInitialNotificationState(): NotificationState {
|
private fun createInitialNotificationState(): NotificationState {
|
||||||
|
|
@ -114,21 +136,17 @@ class NotificationDrawerManager @Inject constructor(
|
||||||
/**
|
/**
|
||||||
* Clear all known events and refresh the notification drawer.
|
* Clear all known events and refresh the notification drawer.
|
||||||
*/
|
*/
|
||||||
fun clearAllEvents() {
|
fun clearAllEvents(sessionId: String) {
|
||||||
updateEvents { it.clear() }
|
updateEvents { it.clearMessagesForSession(sessionId) }
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Should be called when the application is currently opened and showing timeline for the given roomId.
|
* Should be called when the application is currently opened and showing timeline for the given roomId.
|
||||||
* Used to ignore events related to that room (no need to display notification) and clean any existing notification on this room.
|
* Used to ignore events related to that room (no need to display notification) and clean any existing notification on this room.
|
||||||
*/
|
*/
|
||||||
fun setCurrentRoom(roomId: String?) {
|
private fun onEnteringRoom(sessionId: String, roomId: String) {
|
||||||
updateEvents {
|
updateEvents {
|
||||||
val hasChanged = roomId != currentRoomId
|
it.clearMessagesForRoom(sessionId, roomId)
|
||||||
currentRoomId = roomId
|
|
||||||
if (hasChanged && roomId != null) {
|
|
||||||
it.clearMessagesForRoom(roomId)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -136,18 +154,13 @@ class NotificationDrawerManager @Inject constructor(
|
||||||
* Should be called when the application is currently opened and showing timeline for the given threadId.
|
* Should be called when the application is currently opened and showing timeline for the given threadId.
|
||||||
* Used to ignore events related to that thread (no need to display notification) and clean any existing notification on this room.
|
* Used to ignore events related to that thread (no need to display notification) and clean any existing notification on this room.
|
||||||
*/
|
*/
|
||||||
fun setCurrentThread(threadId: String?) {
|
private fun onEnteringThread(sessionId: String, roomId: String, threadId: String) {
|
||||||
updateEvents {
|
updateEvents {
|
||||||
val hasChanged = threadId != currentThreadId
|
it.clearMessagesForThread(sessionId, roomId, threadId)
|
||||||
currentThreadId = threadId
|
|
||||||
currentRoomId?.let { roomId ->
|
|
||||||
if (hasChanged && threadId != null) {
|
|
||||||
it.clearMessagesForThread(roomId, threadId)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO EAx Must be per account
|
||||||
fun notificationStyleChanged() {
|
fun notificationStyleChanged() {
|
||||||
updateEvents {
|
updateEvents {
|
||||||
val newSettings = pushDataStore.useCompleteNotificationFormat()
|
val newSettings = pushDataStore.useCompleteNotificationFormat()
|
||||||
|
|
@ -189,7 +202,7 @@ class NotificationDrawerManager @Inject constructor(
|
||||||
private fun refreshNotificationDrawerBg() {
|
private fun refreshNotificationDrawerBg() {
|
||||||
Timber.v("refreshNotificationDrawerBg()")
|
Timber.v("refreshNotificationDrawerBg()")
|
||||||
val eventsToRender = notificationState.updateQueuedEvents(this) { queuedEvents, renderedEvents ->
|
val eventsToRender = notificationState.updateQueuedEvents(this) { queuedEvents, renderedEvents ->
|
||||||
notifiableEventProcessor.process(queuedEvents.rawEvents(), currentRoomId, currentThreadId, renderedEvents).also {
|
notifiableEventProcessor.process(queuedEvents.rawEvents(), currentAppNavigationState, renderedEvents).also {
|
||||||
queuedEvents.clearAndAdd(it.onlyKeptEvents())
|
queuedEvents.clearAndAdd(it.onlyKeptEvents())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -198,9 +211,7 @@ class NotificationDrawerManager @Inject constructor(
|
||||||
Timber.d("Skipping notification update due to event list not changing")
|
Timber.d("Skipping notification update due to event list not changing")
|
||||||
} else {
|
} else {
|
||||||
notificationState.clearAndAddRenderedEvents(eventsToRender)
|
notificationState.clearAndAddRenderedEvents(eventsToRender)
|
||||||
// TODO EAx
|
renderEvents(eventsToRender)
|
||||||
//val session = currentSession ?: return
|
|
||||||
//renderEvents(session, eventsToRender)
|
|
||||||
persistEvents()
|
persistEvents()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -211,37 +222,28 @@ class NotificationDrawerManager @Inject constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun renderEvents(/*session: Session, eventsToRender: List<ProcessedEvent<NotifiableEvent>>*/) {
|
private fun renderEvents(eventsToRender: List<ProcessedEvent<NotifiableEvent>>) {
|
||||||
/* TODO EAx
|
// Group by sessionId
|
||||||
val user = session.getUserOrDefault(session.myUserId)
|
val eventsForSessions = eventsToRender.groupBy {
|
||||||
// myUserDisplayName cannot be empty else NotificationCompat.MessagingStyle() will crash
|
it.event.sessionId
|
||||||
val myUserDisplayName = user.toMatrixItem().getBestName()
|
}
|
||||||
val myUserAvatarUrl = session.contentUrlResolver().resolveThumbnail(
|
|
||||||
contentUrl = user.avatarUrl,
|
|
||||||
width = avatarSize,
|
|
||||||
height = avatarSize,
|
|
||||||
method = ContentUrlResolver.ThumbnailMethod.SCALE
|
|
||||||
)
|
|
||||||
notificationRenderer.render(session.myUserId, myUserDisplayName, myUserAvatarUrl, useCompleteNotificationFormat, eventsToRender)
|
|
||||||
|
|
||||||
*/
|
eventsForSessions.forEach { (sessionId, notifiableEvents) ->
|
||||||
|
// TODO EAx val user = session.getUserOrDefault(session.myUserId)
|
||||||
|
// myUserDisplayName cannot be empty else NotificationCompat.MessagingStyle() will crash
|
||||||
|
val myUserDisplayName = "Todo display name" // user.toMatrixItem().getBestName()
|
||||||
|
// TODO EAx avatar URL
|
||||||
|
val myUserAvatarUrl = null // session.contentUrlResolver().resolveThumbnail(
|
||||||
|
// contentUrl = user.avatarUrl,
|
||||||
|
// width = avatarSize,
|
||||||
|
// height = avatarSize,
|
||||||
|
// method = ContentUrlResolver.ThumbnailMethod.SCALE
|
||||||
|
//)
|
||||||
|
notificationRenderer.render(sessionId, myUserDisplayName, myUserAvatarUrl, useCompleteNotificationFormat, notifiableEvents)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun shouldIgnoreMessageEventInRoom(resolvedEvent: NotifiableMessageEvent): Boolean {
|
fun shouldIgnoreMessageEventInRoom(resolvedEvent: NotifiableMessageEvent): Boolean {
|
||||||
return resolvedEvent.shouldIgnoreMessageEventInRoom(currentRoomId, currentThreadId)
|
return resolvedEvent.shouldIgnoreMessageEventInRoom(currentAppNavigationState)
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Temporary notification for EAx
|
|
||||||
*/
|
|
||||||
fun displayTemporaryNotification() {
|
|
||||||
notificationRenderer.displayTemporaryNotification()
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
const val SUMMARY_NOTIFICATION_ID = 0
|
|
||||||
const val ROOM_MESSAGES_NOTIFICATION_ID = 1
|
|
||||||
const val ROOM_EVENT_NOTIFICATION_ID = 2
|
|
||||||
const val ROOM_INVITATION_NOTIFICATION_ID = 3
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,7 @@ import io.element.android.libraries.push.impl.notifications.model.NotifiableMess
|
||||||
import io.element.android.libraries.push.impl.notifications.model.SimpleNotifiableEvent
|
import io.element.android.libraries.push.impl.notifications.model.SimpleNotifiableEvent
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
|
|
||||||
data class NotificationEventQueue(
|
data class NotificationEventQueue constructor(
|
||||||
private val queue: MutableList<NotifiableEvent>,
|
private val queue: MutableList<NotifiableEvent>,
|
||||||
/**
|
/**
|
||||||
* An in memory FIFO cache of the seen events.
|
* An in memory FIFO cache of the seen events.
|
||||||
|
|
@ -103,7 +103,7 @@ data class NotificationEventQueue(
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun findExistingById(notifiableEvent: NotifiableEvent): NotifiableEvent? {
|
private fun findExistingById(notifiableEvent: NotifiableEvent): NotifiableEvent? {
|
||||||
return queue.firstOrNull { it.eventId == notifiableEvent.eventId }
|
return queue.firstOrNull { it.sessionId == notifiableEvent.sessionId && it.eventId == notifiableEvent.eventId }
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun findEdited(notifiableEvent: NotifiableEvent): NotifiableEvent? {
|
private fun findEdited(notifiableEvent: NotifiableEvent): NotifiableEvent? {
|
||||||
|
|
@ -125,19 +125,24 @@ data class NotificationEventQueue(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun clearMemberShipNotificationForRoom(roomId: String) {
|
fun clearMemberShipNotificationForRoom(sessionId: String, roomId: String) {
|
||||||
Timber.d("clearMemberShipOfRoom $roomId")
|
Timber.d("clearMemberShipOfRoom $sessionId, $roomId")
|
||||||
queue.removeAll { it is InviteNotifiableEvent && it.roomId == roomId }
|
queue.removeAll { it is InviteNotifiableEvent && it.sessionId == sessionId && it.roomId == roomId }
|
||||||
}
|
}
|
||||||
|
|
||||||
fun clearMessagesForRoom(roomId: String) {
|
fun clearMessagesForSession(sessionId: String) {
|
||||||
Timber.d("clearMessageEventOfRoom $roomId")
|
Timber.d("clearMessagesForSession $sessionId")
|
||||||
queue.removeAll { it is NotifiableMessageEvent && it.roomId == roomId }
|
queue.removeAll { it is NotifiableMessageEvent && it.sessionId == sessionId}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun clearMessagesForThread(roomId: String, threadId: String) {
|
fun clearMessagesForRoom(sessionId: String, roomId: String) {
|
||||||
Timber.d("clearMessageEventOfThread $roomId, $threadId")
|
Timber.d("clearMessageEventOfRoom $sessionId, $roomId")
|
||||||
queue.removeAll { it is NotifiableMessageEvent && it.roomId == roomId && it.threadId == threadId }
|
queue.removeAll { it is NotifiableMessageEvent && it.sessionId == sessionId && it.roomId == roomId }
|
||||||
|
}
|
||||||
|
|
||||||
|
fun clearMessagesForThread(sessionId: String, roomId: String, threadId: String) {
|
||||||
|
Timber.d("clearMessageEventOfThread $sessionId, $roomId, $threadId")
|
||||||
|
queue.removeAll { it is NotifiableMessageEvent && it.sessionId == sessionId && it.roomId == roomId && it.threadId == threadId }
|
||||||
}
|
}
|
||||||
|
|
||||||
fun rawEvents(): List<NotifiableEvent> = queue
|
fun rawEvents(): List<NotifiableEvent> = queue
|
||||||
|
|
|
||||||
|
|
@ -25,18 +25,28 @@ import javax.inject.Inject
|
||||||
private typealias ProcessedMessageEvents = List<ProcessedEvent<NotifiableMessageEvent>>
|
private typealias ProcessedMessageEvents = List<ProcessedEvent<NotifiableMessageEvent>>
|
||||||
|
|
||||||
class NotificationFactory @Inject constructor(
|
class NotificationFactory @Inject constructor(
|
||||||
private val notificationUtils: NotificationUtils,
|
private val notificationUtils: NotificationUtils,
|
||||||
private val roomGroupMessageCreator: RoomGroupMessageCreator,
|
private val roomGroupMessageCreator: RoomGroupMessageCreator,
|
||||||
private val summaryGroupMessageCreator: SummaryGroupMessageCreator
|
private val summaryGroupMessageCreator: SummaryGroupMessageCreator
|
||||||
) {
|
) {
|
||||||
|
|
||||||
fun Map<String, ProcessedMessageEvents>.toNotifications(myUserDisplayName: String, myUserAvatarUrl: String?): List<RoomNotification> {
|
fun Map<String, ProcessedMessageEvents>.toNotifications(
|
||||||
|
sessionId: String,
|
||||||
|
myUserDisplayName: String,
|
||||||
|
myUserAvatarUrl: String?
|
||||||
|
): List<RoomNotification> {
|
||||||
return map { (roomId, events) ->
|
return map { (roomId, events) ->
|
||||||
when {
|
when {
|
||||||
events.hasNoEventsToDisplay() -> RoomNotification.Removed(roomId)
|
events.hasNoEventsToDisplay() -> RoomNotification.Removed(roomId)
|
||||||
else -> {
|
else -> {
|
||||||
val messageEvents = events.onlyKeptEvents().filterNot { it.isRedacted }
|
val messageEvents = events.onlyKeptEvents().filterNot { it.isRedacted }
|
||||||
roomGroupMessageCreator.createRoomMessage(messageEvents, roomId, myUserDisplayName, myUserAvatarUrl)
|
roomGroupMessageCreator.createRoomMessage(
|
||||||
|
sessionId = sessionId,
|
||||||
|
events = messageEvents,
|
||||||
|
roomId = roomId,
|
||||||
|
userDisplayName = myUserDisplayName,
|
||||||
|
userAvatarUrl = myUserAvatarUrl
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -49,46 +59,47 @@ class NotificationFactory @Inject constructor(
|
||||||
private fun NotifiableMessageEvent.canNotBeDisplayed() = isRedacted
|
private fun NotifiableMessageEvent.canNotBeDisplayed() = isRedacted
|
||||||
|
|
||||||
@JvmName("toNotificationsInviteNotifiableEvent")
|
@JvmName("toNotificationsInviteNotifiableEvent")
|
||||||
fun List<ProcessedEvent<InviteNotifiableEvent>>.toNotifications(myUserId: String): List<OneShotNotification> {
|
fun List<ProcessedEvent<InviteNotifiableEvent>>.toNotifications(): List<OneShotNotification> {
|
||||||
return map { (processed, event) ->
|
return map { (processed, event) ->
|
||||||
when (processed) {
|
when (processed) {
|
||||||
ProcessedEvent.Type.REMOVE -> OneShotNotification.Removed(key = event.roomId)
|
ProcessedEvent.Type.REMOVE -> OneShotNotification.Removed(key = event.roomId)
|
||||||
ProcessedEvent.Type.KEEP -> OneShotNotification.Append(
|
ProcessedEvent.Type.KEEP -> OneShotNotification.Append(
|
||||||
notificationUtils.buildRoomInvitationNotification(event, myUserId),
|
notificationUtils.buildRoomInvitationNotification(event),
|
||||||
OneShotNotification.Append.Meta(
|
OneShotNotification.Append.Meta(
|
||||||
key = event.roomId,
|
key = event.roomId,
|
||||||
summaryLine = event.description,
|
summaryLine = event.description,
|
||||||
isNoisy = event.noisy,
|
isNoisy = event.noisy,
|
||||||
timestamp = event.timestamp
|
timestamp = event.timestamp
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@JvmName("toNotificationsSimpleNotifiableEvent")
|
@JvmName("toNotificationsSimpleNotifiableEvent")
|
||||||
fun List<ProcessedEvent<SimpleNotifiableEvent>>.toNotifications(myUserId: String): List<OneShotNotification> {
|
fun List<ProcessedEvent<SimpleNotifiableEvent>>.toNotifications(): List<OneShotNotification> {
|
||||||
return map { (processed, event) ->
|
return map { (processed, event) ->
|
||||||
when (processed) {
|
when (processed) {
|
||||||
ProcessedEvent.Type.REMOVE -> OneShotNotification.Removed(key = event.eventId)
|
ProcessedEvent.Type.REMOVE -> OneShotNotification.Removed(key = event.eventId)
|
||||||
ProcessedEvent.Type.KEEP -> OneShotNotification.Append(
|
ProcessedEvent.Type.KEEP -> OneShotNotification.Append(
|
||||||
notificationUtils.buildSimpleEventNotification(event, myUserId),
|
notificationUtils.buildSimpleEventNotification(event),
|
||||||
OneShotNotification.Append.Meta(
|
OneShotNotification.Append.Meta(
|
||||||
key = event.eventId,
|
key = event.eventId,
|
||||||
summaryLine = event.description,
|
summaryLine = event.description,
|
||||||
isNoisy = event.noisy,
|
isNoisy = event.noisy,
|
||||||
timestamp = event.timestamp
|
timestamp = event.timestamp
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun createSummaryNotification(
|
fun createSummaryNotification(
|
||||||
roomNotifications: List<RoomNotification>,
|
sessionId: String,
|
||||||
invitationNotifications: List<OneShotNotification>,
|
roomNotifications: List<RoomNotification>,
|
||||||
simpleNotifications: List<OneShotNotification>,
|
invitationNotifications: List<OneShotNotification>,
|
||||||
useCompleteNotificationFormat: Boolean
|
simpleNotifications: List<OneShotNotification>,
|
||||||
|
useCompleteNotificationFormat: Boolean
|
||||||
): SummaryNotification {
|
): SummaryNotification {
|
||||||
val roomMeta = roomNotifications.filterIsInstance<RoomNotification.Message>().map { it.meta }
|
val roomMeta = roomNotifications.filterIsInstance<RoomNotification.Message>().map { it.meta }
|
||||||
val invitationMeta = invitationNotifications.filterIsInstance<OneShotNotification.Append>().map { it.meta }
|
val invitationMeta = invitationNotifications.filterIsInstance<OneShotNotification.Append>().map { it.meta }
|
||||||
|
|
@ -96,30 +107,27 @@ class NotificationFactory @Inject constructor(
|
||||||
return when {
|
return when {
|
||||||
roomMeta.isEmpty() && invitationMeta.isEmpty() && simpleMeta.isEmpty() -> SummaryNotification.Removed
|
roomMeta.isEmpty() && invitationMeta.isEmpty() && simpleMeta.isEmpty() -> SummaryNotification.Removed
|
||||||
else -> SummaryNotification.Update(
|
else -> SummaryNotification.Update(
|
||||||
summaryGroupMessageCreator.createSummaryNotification(
|
summaryGroupMessageCreator.createSummaryNotification(
|
||||||
roomNotifications = roomMeta,
|
sessionId = sessionId,
|
||||||
invitationNotifications = invitationMeta,
|
roomNotifications = roomMeta,
|
||||||
simpleNotifications = simpleMeta,
|
invitationNotifications = invitationMeta,
|
||||||
useCompleteNotificationFormat = useCompleteNotificationFormat
|
simpleNotifications = simpleMeta,
|
||||||
)
|
useCompleteNotificationFormat = useCompleteNotificationFormat
|
||||||
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun createTemporaryNotification(): Notification {
|
|
||||||
return notificationUtils.createTemporaryNotification()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sealed interface RoomNotification {
|
sealed interface RoomNotification {
|
||||||
data class Removed(val roomId: String) : RoomNotification
|
data class Removed(val roomId: String) : RoomNotification
|
||||||
data class Message(val notification: Notification, val meta: Meta) : RoomNotification {
|
data class Message(val notification: Notification, val meta: Meta) : RoomNotification {
|
||||||
data class Meta(
|
data class Meta(
|
||||||
val summaryLine: CharSequence,
|
val roomId: String,
|
||||||
val messageCount: Int,
|
val summaryLine: CharSequence,
|
||||||
val latestTimestamp: Long,
|
val messageCount: Int,
|
||||||
val roomId: String,
|
val latestTimestamp: Long,
|
||||||
val shouldBing: Boolean
|
val shouldBing: Boolean
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -128,10 +136,10 @@ sealed interface OneShotNotification {
|
||||||
data class Removed(val key: String) : OneShotNotification
|
data class Removed(val key: String) : OneShotNotification
|
||||||
data class Append(val notification: Notification, val meta: Meta) : OneShotNotification {
|
data class Append(val notification: Notification, val meta: Meta) : OneShotNotification {
|
||||||
data class Meta(
|
data class Meta(
|
||||||
val key: String,
|
val key: String,
|
||||||
val summaryLine: CharSequence,
|
val summaryLine: CharSequence,
|
||||||
val isNoisy: Boolean,
|
val isNoisy: Boolean,
|
||||||
val timestamp: Long,
|
val timestamp: Long,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,52 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2023 New Vector Ltd
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.element.android.libraries.push.impl.notifications
|
||||||
|
|
||||||
|
import io.element.android.libraries.di.AppScope
|
||||||
|
import io.element.android.libraries.di.SingleIn
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
@SingleIn(AppScope::class)
|
||||||
|
class NotificationIdProvider @Inject constructor() {
|
||||||
|
fun getSummaryNotificationId(sessionId: String): Int {
|
||||||
|
return getOffset(sessionId) + SUMMARY_NOTIFICATION_ID
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getRoomMessagesNotificationId(sessionId: String): Int {
|
||||||
|
return getOffset(sessionId) + ROOM_MESSAGES_NOTIFICATION_ID
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getRoomEventNotificationId(sessionId: String): Int {
|
||||||
|
return getOffset(sessionId) + ROOM_EVENT_NOTIFICATION_ID
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getRoomInvitationNotificationId(sessionId: String): Int {
|
||||||
|
return getOffset(sessionId) + ROOM_INVITATION_NOTIFICATION_ID
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getOffset(sessionId: String): Int {
|
||||||
|
// TODO EAx multi account: return different value for users and persist data
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private const val SUMMARY_NOTIFICATION_ID = 0
|
||||||
|
private const val ROOM_MESSAGES_NOTIFICATION_ID = 1
|
||||||
|
private const val ROOM_EVENT_NOTIFICATION_ID = 2
|
||||||
|
private const val ROOM_INVITATION_NOTIFICATION_ID = 3
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -16,10 +16,6 @@
|
||||||
package io.element.android.libraries.push.impl.notifications
|
package io.element.android.libraries.push.impl.notifications
|
||||||
|
|
||||||
import androidx.annotation.WorkerThread
|
import androidx.annotation.WorkerThread
|
||||||
import io.element.android.libraries.push.impl.notifications.NotificationDrawerManager.Companion.ROOM_EVENT_NOTIFICATION_ID
|
|
||||||
import io.element.android.libraries.push.impl.notifications.NotificationDrawerManager.Companion.ROOM_INVITATION_NOTIFICATION_ID
|
|
||||||
import io.element.android.libraries.push.impl.notifications.NotificationDrawerManager.Companion.ROOM_MESSAGES_NOTIFICATION_ID
|
|
||||||
import io.element.android.libraries.push.impl.notifications.NotificationDrawerManager.Companion.SUMMARY_NOTIFICATION_ID
|
|
||||||
import io.element.android.libraries.push.impl.notifications.model.InviteNotifiableEvent
|
import io.element.android.libraries.push.impl.notifications.model.InviteNotifiableEvent
|
||||||
import io.element.android.libraries.push.impl.notifications.model.NotifiableEvent
|
import io.element.android.libraries.push.impl.notifications.model.NotifiableEvent
|
||||||
import io.element.android.libraries.push.impl.notifications.model.NotifiableMessageEvent
|
import io.element.android.libraries.push.impl.notifications.model.NotifiableMessageEvent
|
||||||
|
|
@ -28,13 +24,14 @@ import timber.log.Timber
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
class NotificationRenderer @Inject constructor(
|
class NotificationRenderer @Inject constructor(
|
||||||
|
private val notificationIdProvider: NotificationIdProvider,
|
||||||
private val notificationDisplayer: NotificationDisplayer,
|
private val notificationDisplayer: NotificationDisplayer,
|
||||||
private val notificationFactory: NotificationFactory,
|
private val notificationFactory: NotificationFactory,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
@WorkerThread
|
@WorkerThread
|
||||||
fun render(
|
fun render(
|
||||||
myUserId: String,
|
sessionId: String,
|
||||||
myUserDisplayName: String,
|
myUserDisplayName: String,
|
||||||
myUserAvatarUrl: String?,
|
myUserAvatarUrl: String?,
|
||||||
useCompleteNotificationFormat: Boolean,
|
useCompleteNotificationFormat: Boolean,
|
||||||
|
|
@ -42,10 +39,11 @@ class NotificationRenderer @Inject constructor(
|
||||||
) {
|
) {
|
||||||
val (roomEvents, simpleEvents, invitationEvents) = eventsToProcess.groupByType()
|
val (roomEvents, simpleEvents, invitationEvents) = eventsToProcess.groupByType()
|
||||||
with(notificationFactory) {
|
with(notificationFactory) {
|
||||||
val roomNotifications = roomEvents.toNotifications(myUserDisplayName, myUserAvatarUrl)
|
val roomNotifications = roomEvents.toNotifications(sessionId, myUserDisplayName, myUserAvatarUrl)
|
||||||
val invitationNotifications = invitationEvents.toNotifications(myUserId)
|
val invitationNotifications = invitationEvents.toNotifications()
|
||||||
val simpleNotifications = simpleEvents.toNotifications(myUserId)
|
val simpleNotifications = simpleEvents.toNotifications()
|
||||||
val summaryNotification = createSummaryNotification(
|
val summaryNotification = createSummaryNotification(
|
||||||
|
sessionId = sessionId,
|
||||||
roomNotifications = roomNotifications,
|
roomNotifications = roomNotifications,
|
||||||
invitationNotifications = invitationNotifications,
|
invitationNotifications = invitationNotifications,
|
||||||
simpleNotifications = simpleNotifications,
|
simpleNotifications = simpleNotifications,
|
||||||
|
|
@ -55,18 +53,22 @@ class NotificationRenderer @Inject constructor(
|
||||||
// Remove summary first to avoid briefly displaying it after dismissing the last notification
|
// Remove summary first to avoid briefly displaying it after dismissing the last notification
|
||||||
if (summaryNotification == SummaryNotification.Removed) {
|
if (summaryNotification == SummaryNotification.Removed) {
|
||||||
Timber.d("Removing summary notification")
|
Timber.d("Removing summary notification")
|
||||||
notificationDisplayer.cancelNotificationMessage(null, SUMMARY_NOTIFICATION_ID)
|
notificationDisplayer.cancelNotificationMessage(null, notificationIdProvider.getSummaryNotificationId(sessionId))
|
||||||
}
|
}
|
||||||
|
|
||||||
roomNotifications.forEach { wrapper ->
|
roomNotifications.forEach { wrapper ->
|
||||||
when (wrapper) {
|
when (wrapper) {
|
||||||
is RoomNotification.Removed -> {
|
is RoomNotification.Removed -> {
|
||||||
Timber.d("Removing room messages notification ${wrapper.roomId}")
|
Timber.d("Removing room messages notification ${wrapper.roomId}")
|
||||||
notificationDisplayer.cancelNotificationMessage(wrapper.roomId, ROOM_MESSAGES_NOTIFICATION_ID)
|
notificationDisplayer.cancelNotificationMessage(wrapper.roomId, notificationIdProvider.getRoomMessagesNotificationId(sessionId))
|
||||||
}
|
}
|
||||||
is RoomNotification.Message -> if (useCompleteNotificationFormat) {
|
is RoomNotification.Message -> if (useCompleteNotificationFormat) {
|
||||||
Timber.d("Updating room messages notification ${wrapper.meta.roomId}")
|
Timber.d("Updating room messages notification ${wrapper.meta.roomId}")
|
||||||
notificationDisplayer.showNotificationMessage(wrapper.meta.roomId, ROOM_MESSAGES_NOTIFICATION_ID, wrapper.notification)
|
notificationDisplayer.showNotificationMessage(
|
||||||
|
wrapper.meta.roomId,
|
||||||
|
notificationIdProvider.getRoomMessagesNotificationId(sessionId),
|
||||||
|
wrapper.notification
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -75,11 +77,15 @@ class NotificationRenderer @Inject constructor(
|
||||||
when (wrapper) {
|
when (wrapper) {
|
||||||
is OneShotNotification.Removed -> {
|
is OneShotNotification.Removed -> {
|
||||||
Timber.d("Removing invitation notification ${wrapper.key}")
|
Timber.d("Removing invitation notification ${wrapper.key}")
|
||||||
notificationDisplayer.cancelNotificationMessage(wrapper.key, ROOM_INVITATION_NOTIFICATION_ID)
|
notificationDisplayer.cancelNotificationMessage(wrapper.key, notificationIdProvider.getRoomInvitationNotificationId(sessionId))
|
||||||
}
|
}
|
||||||
is OneShotNotification.Append -> if (useCompleteNotificationFormat) {
|
is OneShotNotification.Append -> if (useCompleteNotificationFormat) {
|
||||||
Timber.d("Updating invitation notification ${wrapper.meta.key}")
|
Timber.d("Updating invitation notification ${wrapper.meta.key}")
|
||||||
notificationDisplayer.showNotificationMessage(wrapper.meta.key, ROOM_INVITATION_NOTIFICATION_ID, wrapper.notification)
|
notificationDisplayer.showNotificationMessage(
|
||||||
|
wrapper.meta.key,
|
||||||
|
notificationIdProvider.getRoomInvitationNotificationId(sessionId),
|
||||||
|
wrapper.notification
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -88,11 +94,15 @@ class NotificationRenderer @Inject constructor(
|
||||||
when (wrapper) {
|
when (wrapper) {
|
||||||
is OneShotNotification.Removed -> {
|
is OneShotNotification.Removed -> {
|
||||||
Timber.d("Removing simple notification ${wrapper.key}")
|
Timber.d("Removing simple notification ${wrapper.key}")
|
||||||
notificationDisplayer.cancelNotificationMessage(wrapper.key, ROOM_EVENT_NOTIFICATION_ID)
|
notificationDisplayer.cancelNotificationMessage(wrapper.key, notificationIdProvider.getRoomEventNotificationId(sessionId))
|
||||||
}
|
}
|
||||||
is OneShotNotification.Append -> if (useCompleteNotificationFormat) {
|
is OneShotNotification.Append -> if (useCompleteNotificationFormat) {
|
||||||
Timber.d("Updating simple notification ${wrapper.meta.key}")
|
Timber.d("Updating simple notification ${wrapper.meta.key}")
|
||||||
notificationDisplayer.showNotificationMessage(wrapper.meta.key, ROOM_EVENT_NOTIFICATION_ID, wrapper.notification)
|
notificationDisplayer.showNotificationMessage(
|
||||||
|
wrapper.meta.key,
|
||||||
|
notificationIdProvider.getRoomEventNotificationId(sessionId),
|
||||||
|
wrapper.notification
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -100,7 +110,11 @@ class NotificationRenderer @Inject constructor(
|
||||||
// Update summary last to avoid briefly displaying it before other notifications
|
// Update summary last to avoid briefly displaying it before other notifications
|
||||||
if (summaryNotification is SummaryNotification.Update) {
|
if (summaryNotification is SummaryNotification.Update) {
|
||||||
Timber.d("Updating summary notification")
|
Timber.d("Updating summary notification")
|
||||||
notificationDisplayer.showNotificationMessage(null, SUMMARY_NOTIFICATION_ID, summaryNotification.notification)
|
notificationDisplayer.showNotificationMessage(
|
||||||
|
null,
|
||||||
|
notificationIdProvider.getSummaryNotificationId(sessionId),
|
||||||
|
summaryNotification.notification
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -108,11 +122,6 @@ class NotificationRenderer @Inject constructor(
|
||||||
fun cancelAllNotifications() {
|
fun cancelAllNotifications() {
|
||||||
notificationDisplayer.cancelAllNotifications()
|
notificationDisplayer.cancelAllNotifications()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun displayTemporaryNotification() {
|
|
||||||
val notification = notificationFactory.createTemporaryNotification()
|
|
||||||
notificationDisplayer.showNotificationMessage(null, TEMPORARY_ID, notification)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun List<ProcessedEvent<NotifiableEvent>>.groupByType(): GroupedNotificationEvents {
|
private fun List<ProcessedEvent<NotifiableEvent>>.groupByType(): GroupedNotificationEvents {
|
||||||
|
|
|
||||||
|
|
@ -228,7 +228,7 @@ class NotificationUtils @Inject constructor(
|
||||||
true
|
true
|
||||||
/** TODO EAx vectorPreferences.areThreadMessagesEnabled() */
|
/** TODO EAx vectorPreferences.areThreadMessagesEnabled() */
|
||||||
-> buildOpenThreadIntent(roomInfo, threadId)
|
-> buildOpenThreadIntent(roomInfo, threadId)
|
||||||
else -> buildOpenRoomIntent(roomInfo.roomId)
|
else -> buildOpenRoomIntent(roomInfo.sessionId, roomInfo.roomId)
|
||||||
}
|
}
|
||||||
|
|
||||||
val smallIcon = R.drawable.ic_notification
|
val smallIcon = R.drawable.ic_notification
|
||||||
|
|
@ -259,8 +259,7 @@ class NotificationUtils @Inject constructor(
|
||||||
)
|
)
|
||||||
// Auto-bundling is enabled for 4 or more notifications on API 24+ (N+)
|
// Auto-bundling is enabled for 4 or more notifications on API 24+ (N+)
|
||||||
// devices and all Wear devices. But we want a custom grouping, so we specify the groupID
|
// devices and all Wear devices. But we want a custom grouping, so we specify the groupID
|
||||||
// TODO Group should be current user display name
|
.setGroup(roomInfo.sessionId)
|
||||||
.setGroup(buildMeta.applicationName)
|
|
||||||
// In order to avoid notification making sound twice (due to the summary notification)
|
// In order to avoid notification making sound twice (due to the summary notification)
|
||||||
.setGroupAlertBehavior(NotificationCompat.GROUP_ALERT_ALL)
|
.setGroupAlertBehavior(NotificationCompat.GROUP_ALERT_ALL)
|
||||||
.setSmallIcon(smallIcon)
|
.setSmallIcon(smallIcon)
|
||||||
|
|
@ -287,7 +286,8 @@ class NotificationUtils @Inject constructor(
|
||||||
// Mark room as read
|
// Mark room as read
|
||||||
val markRoomReadIntent = Intent(context, NotificationBroadcastReceiver::class.java)
|
val markRoomReadIntent = Intent(context, NotificationBroadcastReceiver::class.java)
|
||||||
markRoomReadIntent.action = actionIds.markRoomRead
|
markRoomReadIntent.action = actionIds.markRoomRead
|
||||||
markRoomReadIntent.data = createIgnoredUri(roomInfo.roomId)
|
markRoomReadIntent.data = createIgnoredUri("markRead?${roomInfo.sessionId}&$${roomInfo.roomId}")
|
||||||
|
markRoomReadIntent.putExtra(NotificationBroadcastReceiver.KEY_SESSION_ID, roomInfo.sessionId)
|
||||||
markRoomReadIntent.putExtra(NotificationBroadcastReceiver.KEY_ROOM_ID, roomInfo.roomId)
|
markRoomReadIntent.putExtra(NotificationBroadcastReceiver.KEY_ROOM_ID, roomInfo.roomId)
|
||||||
val markRoomReadPendingIntent = PendingIntent.getBroadcast(
|
val markRoomReadPendingIntent = PendingIntent.getBroadcast(
|
||||||
context,
|
context,
|
||||||
|
|
@ -307,7 +307,7 @@ class NotificationUtils @Inject constructor(
|
||||||
|
|
||||||
// Quick reply
|
// Quick reply
|
||||||
if (!roomInfo.hasSmartReplyError) {
|
if (!roomInfo.hasSmartReplyError) {
|
||||||
buildQuickReplyIntent(roomInfo.roomId, threadId, senderDisplayNameForReplyCompat)?.let { replyPendingIntent ->
|
buildQuickReplyIntent(roomInfo.sessionId, roomInfo.roomId, threadId, senderDisplayNameForReplyCompat)?.let { replyPendingIntent ->
|
||||||
val remoteInput = RemoteInput.Builder(NotificationBroadcastReceiver.KEY_TEXT_REPLY)
|
val remoteInput = RemoteInput.Builder(NotificationBroadcastReceiver.KEY_TEXT_REPLY)
|
||||||
.setLabel(stringProvider.getString(StringR.string.action_quick_reply))
|
.setLabel(stringProvider.getString(StringR.string.action_quick_reply))
|
||||||
.build()
|
.build()
|
||||||
|
|
@ -332,8 +332,9 @@ class NotificationUtils @Inject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
val intent = Intent(context, NotificationBroadcastReceiver::class.java)
|
val intent = Intent(context, NotificationBroadcastReceiver::class.java)
|
||||||
intent.putExtra(NotificationBroadcastReceiver.KEY_ROOM_ID, roomInfo.roomId)
|
|
||||||
intent.action = actionIds.dismissRoom
|
intent.action = actionIds.dismissRoom
|
||||||
|
intent.putExtra(NotificationBroadcastReceiver.KEY_SESSION_ID, roomInfo.sessionId)
|
||||||
|
intent.putExtra(NotificationBroadcastReceiver.KEY_ROOM_ID, roomInfo.roomId)
|
||||||
val pendingIntent = PendingIntent.getBroadcast(
|
val pendingIntent = PendingIntent.getBroadcast(
|
||||||
context.applicationContext,
|
context.applicationContext,
|
||||||
clock.epochMillis().toInt(),
|
clock.epochMillis().toInt(),
|
||||||
|
|
@ -347,8 +348,7 @@ class NotificationUtils @Inject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
fun buildRoomInvitationNotification(
|
fun buildRoomInvitationNotification(
|
||||||
inviteNotifiableEvent: InviteNotifiableEvent,
|
inviteNotifiableEvent: InviteNotifiableEvent
|
||||||
matrixId: String
|
|
||||||
): Notification {
|
): Notification {
|
||||||
val accentColor = ContextCompat.getColor(context, R.color.notification_accent_color)
|
val accentColor = ContextCompat.getColor(context, R.color.notification_accent_color)
|
||||||
// Build the pending intent for when the notification is clicked
|
// Build the pending intent for when the notification is clicked
|
||||||
|
|
@ -359,7 +359,7 @@ class NotificationUtils @Inject constructor(
|
||||||
.setOnlyAlertOnce(true)
|
.setOnlyAlertOnce(true)
|
||||||
.setContentTitle(inviteNotifiableEvent.roomName ?: buildMeta.applicationName)
|
.setContentTitle(inviteNotifiableEvent.roomName ?: buildMeta.applicationName)
|
||||||
.setContentText(inviteNotifiableEvent.description)
|
.setContentText(inviteNotifiableEvent.description)
|
||||||
.setGroup(buildMeta.applicationName)
|
.setGroup(inviteNotifiableEvent.sessionId)
|
||||||
.setGroupAlertBehavior(NotificationCompat.GROUP_ALERT_ALL)
|
.setGroupAlertBehavior(NotificationCompat.GROUP_ALERT_ALL)
|
||||||
.setSmallIcon(smallIcon)
|
.setSmallIcon(smallIcon)
|
||||||
.setColor(accentColor)
|
.setColor(accentColor)
|
||||||
|
|
@ -368,7 +368,8 @@ class NotificationUtils @Inject constructor(
|
||||||
// offer to type a quick reject button
|
// offer to type a quick reject button
|
||||||
val rejectIntent = Intent(context, NotificationBroadcastReceiver::class.java)
|
val rejectIntent = Intent(context, NotificationBroadcastReceiver::class.java)
|
||||||
rejectIntent.action = actionIds.reject
|
rejectIntent.action = actionIds.reject
|
||||||
rejectIntent.data = createIgnoredUri("$roomId&$matrixId")
|
rejectIntent.data = createIgnoredUri("rejectInvite?${inviteNotifiableEvent.sessionId}&$roomId")
|
||||||
|
rejectIntent.putExtra(NotificationBroadcastReceiver.KEY_SESSION_ID, inviteNotifiableEvent.sessionId)
|
||||||
rejectIntent.putExtra(NotificationBroadcastReceiver.KEY_ROOM_ID, roomId)
|
rejectIntent.putExtra(NotificationBroadcastReceiver.KEY_ROOM_ID, roomId)
|
||||||
val rejectIntentPendingIntent = PendingIntent.getBroadcast(
|
val rejectIntentPendingIntent = PendingIntent.getBroadcast(
|
||||||
context,
|
context,
|
||||||
|
|
@ -386,7 +387,8 @@ class NotificationUtils @Inject constructor(
|
||||||
// offer to type a quick accept button
|
// offer to type a quick accept button
|
||||||
val joinIntent = Intent(context, NotificationBroadcastReceiver::class.java)
|
val joinIntent = Intent(context, NotificationBroadcastReceiver::class.java)
|
||||||
joinIntent.action = actionIds.join
|
joinIntent.action = actionIds.join
|
||||||
joinIntent.data = createIgnoredUri("$roomId&$matrixId")
|
joinIntent.data = createIgnoredUri("acceptInvite?${inviteNotifiableEvent.sessionId}&$roomId")
|
||||||
|
joinIntent.putExtra(NotificationBroadcastReceiver.KEY_SESSION_ID, inviteNotifiableEvent.sessionId)
|
||||||
joinIntent.putExtra(NotificationBroadcastReceiver.KEY_ROOM_ID, roomId)
|
joinIntent.putExtra(NotificationBroadcastReceiver.KEY_ROOM_ID, roomId)
|
||||||
val joinIntentPendingIntent = PendingIntent.getBroadcast(
|
val joinIntentPendingIntent = PendingIntent.getBroadcast(
|
||||||
context,
|
context,
|
||||||
|
|
@ -433,7 +435,6 @@ class NotificationUtils @Inject constructor(
|
||||||
|
|
||||||
fun buildSimpleEventNotification(
|
fun buildSimpleEventNotification(
|
||||||
simpleNotifiableEvent: SimpleNotifiableEvent,
|
simpleNotifiableEvent: SimpleNotifiableEvent,
|
||||||
matrixId: String
|
|
||||||
): Notification {
|
): Notification {
|
||||||
val accentColor = ContextCompat.getColor(context, R.color.notification_accent_color)
|
val accentColor = ContextCompat.getColor(context, R.color.notification_accent_color)
|
||||||
// Build the pending intent for when the notification is clicked
|
// Build the pending intent for when the notification is clicked
|
||||||
|
|
@ -445,7 +446,7 @@ class NotificationUtils @Inject constructor(
|
||||||
.setOnlyAlertOnce(true)
|
.setOnlyAlertOnce(true)
|
||||||
.setContentTitle(buildMeta.applicationName)
|
.setContentTitle(buildMeta.applicationName)
|
||||||
.setContentText(simpleNotifiableEvent.description)
|
.setContentText(simpleNotifiableEvent.description)
|
||||||
.setGroup(buildMeta.applicationName)
|
.setGroup(simpleNotifiableEvent.sessionId)
|
||||||
.setGroupAlertBehavior(NotificationCompat.GROUP_ALERT_ALL)
|
.setGroupAlertBehavior(NotificationCompat.GROUP_ALERT_ALL)
|
||||||
.setSmallIcon(smallIcon)
|
.setSmallIcon(smallIcon)
|
||||||
.setColor(accentColor)
|
.setColor(accentColor)
|
||||||
|
|
@ -476,81 +477,45 @@ class NotificationUtils @Inject constructor(
|
||||||
.build()
|
.build()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun buildOpenRoomIntent(roomId: String): PendingIntent? {
|
private fun buildOpenRoomIntent(sessionId: String, roomId: String): PendingIntent? {
|
||||||
return null
|
val roomIntent = intentProvider.getIntent(sessionId = sessionId, roomId = roomId, threadId = null)
|
||||||
/*
|
roomIntent.action = actionIds.tapToView
|
||||||
val roomIntentTap = RoomDetailActivity.newIntent(context, TimelineArgs(roomId = roomId, switchToParentSpace = true), true)
|
|
||||||
roomIntentTap.action = actionIds.tapToView
|
|
||||||
// pending intent get reused by system, this will mess up the extra params, so put unique info to avoid that
|
// pending intent get reused by system, this will mess up the extra params, so put unique info to avoid that
|
||||||
roomIntentTap.data = createIgnoredUri("openRoom?$roomId")
|
roomIntent.data = createIgnoredUri("openRoom?$sessionId&$roomId")
|
||||||
|
|
||||||
// Recreate the back stack
|
return PendingIntent.getActivity(
|
||||||
return TaskStackBuilder.create(context)
|
context,
|
||||||
.addNextIntentWithParentStack(HomeActivity.newIntent(context, firstStartMainActivity = false))
|
clock.epochMillis().toInt(),
|
||||||
.addNextIntent(roomIntentTap)
|
roomIntent,
|
||||||
.getPendingIntent(
|
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntentCompat.FLAG_IMMUTABLE
|
||||||
clock.epochMillis().toInt(),
|
)
|
||||||
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntentCompat.FLAG_IMMUTABLE
|
|
||||||
)
|
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun buildOpenThreadIntent(roomInfo: RoomEventGroupInfo, threadId: String?): PendingIntent? {
|
private fun buildOpenThreadIntent(roomInfo: RoomEventGroupInfo, threadId: String?): PendingIntent? {
|
||||||
return null
|
val sessionId = roomInfo.sessionId
|
||||||
/*
|
val roomId = roomInfo.roomId
|
||||||
val threadTimelineArgs = ThreadTimelineArgs(
|
val threadIntentTap = intentProvider.getIntent(sessionId = sessionId, roomId = roomId, threadId = threadId)
|
||||||
startsThread = false,
|
|
||||||
roomId = roomInfo.roomId,
|
|
||||||
rootThreadEventId = threadId,
|
|
||||||
showKeyboard = false,
|
|
||||||
displayName = roomInfo.roomDisplayName,
|
|
||||||
avatarUrl = null,
|
|
||||||
roomEncryptionTrustLevel = null,
|
|
||||||
)
|
|
||||||
val threadIntentTap = ThreadsActivity.newIntent(
|
|
||||||
context = context,
|
|
||||||
threadTimelineArgs = threadTimelineArgs,
|
|
||||||
threadListArgs = null,
|
|
||||||
firstStartMainActivity = true,
|
|
||||||
)
|
|
||||||
threadIntentTap.action = actionIds.tapToView
|
threadIntentTap.action = actionIds.tapToView
|
||||||
// pending intent get reused by system, this will mess up the extra params, so put unique info to avoid that
|
// pending intent get reused by system, this will mess up the extra params, so put unique info to avoid that
|
||||||
threadIntentTap.data = createIgnoredUri("openThread?$threadId")
|
threadIntentTap.data = createIgnoredUri("openThread?$sessionId&$roomId&$threadId")
|
||||||
|
|
||||||
val roomIntent = RoomDetailActivity.newIntent(
|
|
||||||
context = context,
|
|
||||||
timelineArgs = TimelineArgs(
|
|
||||||
roomId = roomInfo.roomId,
|
|
||||||
switchToParentSpace = true
|
|
||||||
),
|
|
||||||
firstStartMainActivity = false
|
|
||||||
)
|
|
||||||
// Recreate the back stack
|
|
||||||
return TaskStackBuilder.create(context)
|
|
||||||
.addNextIntentWithParentStack(HomeActivity.newIntent(context, firstStartMainActivity = false))
|
|
||||||
.addNextIntentWithParentStack(roomIntent)
|
|
||||||
.addNextIntent(threadIntentTap)
|
|
||||||
.getPendingIntent(
|
|
||||||
clock.epochMillis().toInt(),
|
|
||||||
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntentCompat.FLAG_IMMUTABLE
|
|
||||||
)
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun buildOpenHomePendingIntentForSummary(): PendingIntent {
|
|
||||||
TODO()
|
|
||||||
/*
|
|
||||||
val intent = HomeActivity.newIntent(context, firstStartMainActivity = false, clearNotification = true)
|
|
||||||
intent.flags = Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP
|
|
||||||
intent.data = createIgnoredUri("tapSummary")
|
|
||||||
val mainIntent = MainActivity.getIntentWithNextIntent(context, intent)
|
|
||||||
return PendingIntent.getActivity(
|
return PendingIntent.getActivity(
|
||||||
context,
|
context,
|
||||||
Random.nextInt(1000),
|
clock.epochMillis().toInt(),
|
||||||
mainIntent,
|
threadIntentTap,
|
||||||
|
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntentCompat.FLAG_IMMUTABLE
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun buildOpenHomePendingIntentForSummary(sessionId: String): PendingIntent {
|
||||||
|
val intent = intentProvider.getIntent(sessionId = sessionId, roomId = null, threadId = null)
|
||||||
|
intent.data = createIgnoredUri("tapSummary?$sessionId")
|
||||||
|
return PendingIntent.getActivity(
|
||||||
|
context,
|
||||||
|
clock.epochMillis().toInt(),
|
||||||
|
intent,
|
||||||
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntentCompat.FLAG_IMMUTABLE
|
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntentCompat.FLAG_IMMUTABLE
|
||||||
)
|
)
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
@ -560,12 +525,18 @@ class NotificationUtils @Inject constructor(
|
||||||
However, for Android devices running Marshmallow and below (API level 23 and below),
|
However, for Android devices running Marshmallow and below (API level 23 and below),
|
||||||
it will be more appropriate to use an activity. Since you have to provide your own UI.
|
it will be more appropriate to use an activity. Since you have to provide your own UI.
|
||||||
*/
|
*/
|
||||||
private fun buildQuickReplyIntent(roomId: String, threadId: String?, senderName: String?): PendingIntent? {
|
private fun buildQuickReplyIntent(
|
||||||
|
sessionId: String,
|
||||||
|
roomId: String,
|
||||||
|
threadId: String?,
|
||||||
|
senderName: String?
|
||||||
|
): PendingIntent? {
|
||||||
val intent: Intent
|
val intent: Intent
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||||
intent = Intent(context, NotificationBroadcastReceiver::class.java)
|
intent = Intent(context, NotificationBroadcastReceiver::class.java)
|
||||||
intent.action = actionIds.smartReply
|
intent.action = actionIds.smartReply
|
||||||
intent.data = createIgnoredUri(roomId)
|
intent.data = createIgnoredUri("quickReply?$sessionId&$roomId")
|
||||||
|
intent.putExtra(NotificationBroadcastReceiver.KEY_SESSION_ID, sessionId)
|
||||||
intent.putExtra(NotificationBroadcastReceiver.KEY_ROOM_ID, roomId)
|
intent.putExtra(NotificationBroadcastReceiver.KEY_ROOM_ID, roomId)
|
||||||
threadId?.let {
|
threadId?.let {
|
||||||
intent.putExtra(NotificationBroadcastReceiver.KEY_THREAD_ID, it)
|
intent.putExtra(NotificationBroadcastReceiver.KEY_THREAD_ID, it)
|
||||||
|
|
@ -602,6 +573,7 @@ class NotificationUtils @Inject constructor(
|
||||||
* Build the summary notification.
|
* Build the summary notification.
|
||||||
*/
|
*/
|
||||||
fun buildSummaryListNotification(
|
fun buildSummaryListNotification(
|
||||||
|
sessionId: String,
|
||||||
style: NotificationCompat.InboxStyle?,
|
style: NotificationCompat.InboxStyle?,
|
||||||
compatSummary: String,
|
compatSummary: String,
|
||||||
noisy: Boolean,
|
noisy: Boolean,
|
||||||
|
|
@ -615,12 +587,12 @@ class NotificationUtils @Inject constructor(
|
||||||
// used in compat < N, after summary is built based on child notifications
|
// used in compat < N, after summary is built based on child notifications
|
||||||
.setWhen(lastMessageTimestamp)
|
.setWhen(lastMessageTimestamp)
|
||||||
.setStyle(style)
|
.setStyle(style)
|
||||||
.setContentTitle(buildMeta.applicationName)
|
.setContentTitle(sessionId)
|
||||||
.setCategory(NotificationCompat.CATEGORY_MESSAGE)
|
.setCategory(NotificationCompat.CATEGORY_MESSAGE)
|
||||||
.setSmallIcon(smallIcon)
|
.setSmallIcon(smallIcon)
|
||||||
// set content text to support devices running API level < 24
|
// set content text to support devices running API level < 24
|
||||||
.setContentText(compatSummary)
|
.setContentText(compatSummary)
|
||||||
.setGroup(buildMeta.applicationName)
|
.setGroup(sessionId)
|
||||||
// set this notification as the summary for the group
|
// set this notification as the summary for the group
|
||||||
.setGroupSummary(true)
|
.setGroupSummary(true)
|
||||||
.setColor(accentColor)
|
.setColor(accentColor)
|
||||||
|
|
@ -639,15 +611,16 @@ class NotificationUtils @Inject constructor(
|
||||||
priority = NotificationCompat.PRIORITY_LOW
|
priority = NotificationCompat.PRIORITY_LOW
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.setContentIntent(buildOpenHomePendingIntentForSummary())
|
.setContentIntent(buildOpenHomePendingIntentForSummary(sessionId))
|
||||||
.setDeleteIntent(getDismissSummaryPendingIntent())
|
.setDeleteIntent(getDismissSummaryPendingIntent(sessionId))
|
||||||
.build()
|
.build()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getDismissSummaryPendingIntent(): PendingIntent {
|
private fun getDismissSummaryPendingIntent(sessionId: String): PendingIntent {
|
||||||
val intent = Intent(context, NotificationBroadcastReceiver::class.java)
|
val intent = Intent(context, NotificationBroadcastReceiver::class.java)
|
||||||
intent.action = actionIds.dismissSummary
|
intent.action = actionIds.dismissSummary
|
||||||
intent.data = createIgnoredUri("deleteSummary")
|
intent.data = createIgnoredUri("deleteSummary?$sessionId")
|
||||||
|
intent.putExtra(NotificationBroadcastReceiver.KEY_SESSION_ID, sessionId)
|
||||||
return PendingIntent.getBroadcast(
|
return PendingIntent.getBroadcast(
|
||||||
context.applicationContext,
|
context.applicationContext,
|
||||||
0,
|
0,
|
||||||
|
|
@ -703,23 +676,6 @@ class NotificationUtils @Inject constructor(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun createTemporaryNotification(): Notification {
|
|
||||||
val contentIntent = intentProvider.getMainIntent()
|
|
||||||
val pendingIntent = PendingIntent.getActivity(context, 0, contentIntent, PendingIntentCompat.FLAG_IMMUTABLE)
|
|
||||||
|
|
||||||
return NotificationCompat.Builder(context, NOISY_NOTIFICATION_CHANNEL_ID)
|
|
||||||
.setContentTitle(buildMeta.applicationName)
|
|
||||||
.setContentText(stringProvider.getString(R.string.notification_new_messages_temporary))
|
|
||||||
.setSmallIcon(R.drawable.ic_notification)
|
|
||||||
.setLargeIcon(getBitmap(context, R.drawable.element_logo_green))
|
|
||||||
.setColor(ContextCompat.getColor(context, R.color.notification_accent_color))
|
|
||||||
.setPriority(NotificationCompat.PRIORITY_MAX)
|
|
||||||
.setCategory(NotificationCompat.CATEGORY_STATUS)
|
|
||||||
.setAutoCancel(true)
|
|
||||||
.setContentIntent(pendingIntent)
|
|
||||||
.build()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun getBitmap(context: Context, @DrawableRes drawableRes: Int): Bitmap? {
|
private fun getBitmap(context: Context, @DrawableRes drawableRes: Int): Bitmap? {
|
||||||
val drawable = ResourcesCompat.getDrawable(context.resources, drawableRes, null) ?: return null
|
val drawable = ResourcesCompat.getDrawable(context.resources, drawableRes, null) ?: return null
|
||||||
val canvas = Canvas()
|
val canvas = Canvas()
|
||||||
|
|
|
||||||
|
|
@ -20,8 +20,9 @@ package io.element.android.libraries.push.impl.notifications
|
||||||
* Data class to hold information about a group of notifications for a room.
|
* Data class to hold information about a group of notifications for a room.
|
||||||
*/
|
*/
|
||||||
data class RoomEventGroupInfo(
|
data class RoomEventGroupInfo(
|
||||||
|
val sessionId: String,
|
||||||
val roomId: String,
|
val roomId: String,
|
||||||
val roomDisplayName: String = "",
|
val roomDisplayName: String,
|
||||||
val isDirect: Boolean = false
|
val isDirect: Boolean = false
|
||||||
) {
|
) {
|
||||||
// An event in the list has not yet been display
|
// An event in the list has not yet been display
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,13 @@ class RoomGroupMessageCreator @Inject constructor(
|
||||||
private val notificationUtils: NotificationUtils
|
private val notificationUtils: NotificationUtils
|
||||||
) {
|
) {
|
||||||
|
|
||||||
fun createRoomMessage(events: List<NotifiableMessageEvent>, roomId: String, userDisplayName: String, userAvatarUrl: String?): RoomNotification.Message {
|
fun createRoomMessage(
|
||||||
|
sessionId: String,
|
||||||
|
events: List<NotifiableMessageEvent>,
|
||||||
|
roomId: String,
|
||||||
|
userDisplayName: String,
|
||||||
|
userAvatarUrl: String?
|
||||||
|
): RoomNotification.Message {
|
||||||
val lastKnownRoomEvent = events.last()
|
val lastKnownRoomEvent = events.last()
|
||||||
val roomName = lastKnownRoomEvent.roomName ?: lastKnownRoomEvent.senderName ?: ""
|
val roomName = lastKnownRoomEvent.roomName ?: lastKnownRoomEvent.senderName ?: ""
|
||||||
val roomIsGroup = !lastKnownRoomEvent.roomIsDirect
|
val roomIsGroup = !lastKnownRoomEvent.roomIsDirect
|
||||||
|
|
@ -41,7 +47,7 @@ class RoomGroupMessageCreator @Inject constructor(
|
||||||
Person.Builder()
|
Person.Builder()
|
||||||
.setName(userDisplayName)
|
.setName(userDisplayName)
|
||||||
.setIcon(bitmapLoader.getUserIcon(userAvatarUrl))
|
.setIcon(bitmapLoader.getUserIcon(userAvatarUrl))
|
||||||
.setKey(lastKnownRoomEvent.matrixID)
|
.setKey(lastKnownRoomEvent.sessionId)
|
||||||
.build()
|
.build()
|
||||||
).also {
|
).also {
|
||||||
it.conversationTitle = roomName.takeIf { roomIsGroup }
|
it.conversationTitle = roomName.takeIf { roomIsGroup }
|
||||||
|
|
@ -70,7 +76,12 @@ class RoomGroupMessageCreator @Inject constructor(
|
||||||
return RoomNotification.Message(
|
return RoomNotification.Message(
|
||||||
notificationUtils.buildMessagesListNotification(
|
notificationUtils.buildMessagesListNotification(
|
||||||
style,
|
style,
|
||||||
RoomEventGroupInfo(roomId, roomName, isDirect = !roomIsGroup).also {
|
RoomEventGroupInfo(
|
||||||
|
sessionId = sessionId,
|
||||||
|
roomId = roomId,
|
||||||
|
roomDisplayName = roomName,
|
||||||
|
isDirect = !roomIsGroup,
|
||||||
|
).also {
|
||||||
it.hasSmartReplyError = smartReplyErrors.isNotEmpty()
|
it.hasSmartReplyError = smartReplyErrors.isNotEmpty()
|
||||||
it.shouldBing = meta.shouldBing
|
it.shouldBing = meta.shouldBing
|
||||||
it.customSound = events.last().soundName
|
it.customSound = events.last().soundName
|
||||||
|
|
|
||||||
|
|
@ -42,6 +42,7 @@ class SummaryGroupMessageCreator @Inject constructor(
|
||||||
) {
|
) {
|
||||||
|
|
||||||
fun createSummaryNotification(
|
fun createSummaryNotification(
|
||||||
|
sessionId: String,
|
||||||
roomNotifications: List<RoomNotification.Message.Meta>,
|
roomNotifications: List<RoomNotification.Message.Meta>,
|
||||||
invitationNotifications: List<OneShotNotification.Append.Meta>,
|
invitationNotifications: List<OneShotNotification.Append.Meta>,
|
||||||
simpleNotifications: List<OneShotNotification.Append.Meta>,
|
simpleNotifications: List<OneShotNotification.Append.Meta>,
|
||||||
|
|
@ -71,6 +72,7 @@ class SummaryGroupMessageCreator @Inject constructor(
|
||||||
.setSummaryText(stringProvider.getQuantityString(R.plurals.notification_unread_notified_messages, nbEvents, nbEvents))
|
.setSummaryText(stringProvider.getQuantityString(R.plurals.notification_unread_notified_messages, nbEvents, nbEvents))
|
||||||
return if (useCompleteNotificationFormat) {
|
return if (useCompleteNotificationFormat) {
|
||||||
notificationUtils.buildSummaryListNotification(
|
notificationUtils.buildSummaryListNotification(
|
||||||
|
sessionId,
|
||||||
summaryInboxStyle,
|
summaryInboxStyle,
|
||||||
sumTitle,
|
sumTitle,
|
||||||
noisy = summaryIsNoisy,
|
noisy = summaryIsNoisy,
|
||||||
|
|
@ -78,6 +80,7 @@ class SummaryGroupMessageCreator @Inject constructor(
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
processSimpleGroupSummary(
|
processSimpleGroupSummary(
|
||||||
|
sessionId,
|
||||||
summaryIsNoisy,
|
summaryIsNoisy,
|
||||||
messageCount,
|
messageCount,
|
||||||
simpleNotifications.size,
|
simpleNotifications.size,
|
||||||
|
|
@ -89,6 +92,7 @@ class SummaryGroupMessageCreator @Inject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun processSimpleGroupSummary(
|
private fun processSimpleGroupSummary(
|
||||||
|
sessionId: String,
|
||||||
summaryIsNoisy: Boolean,
|
summaryIsNoisy: Boolean,
|
||||||
messageEventsCount: Int,
|
messageEventsCount: Int,
|
||||||
simpleEventsCount: Int,
|
simpleEventsCount: Int,
|
||||||
|
|
@ -147,6 +151,7 @@ class SummaryGroupMessageCreator @Inject constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return notificationUtils.buildSummaryListNotification(
|
return notificationUtils.buildSummaryListNotification(
|
||||||
|
sessionId = sessionId,
|
||||||
style = null,
|
style = null,
|
||||||
compatSummary = privacyTitle,
|
compatSummary = privacyTitle,
|
||||||
noisy = summaryIsNoisy,
|
noisy = summaryIsNoisy,
|
||||||
|
|
|
||||||
|
|
@ -16,18 +16,18 @@
|
||||||
package io.element.android.libraries.push.impl.notifications.model
|
package io.element.android.libraries.push.impl.notifications.model
|
||||||
|
|
||||||
data class InviteNotifiableEvent(
|
data class InviteNotifiableEvent(
|
||||||
val matrixID: String?,
|
override val sessionId: String,
|
||||||
override val eventId: String,
|
override val roomId: String,
|
||||||
override val editedEventId: String?,
|
override val eventId: String,
|
||||||
override val canBeReplaced: Boolean,
|
override val editedEventId: String?,
|
||||||
val roomId: String,
|
override val canBeReplaced: Boolean,
|
||||||
val roomName: String?,
|
val roomName: String?,
|
||||||
val noisy: Boolean,
|
val noisy: Boolean,
|
||||||
val title: String,
|
val title: String,
|
||||||
val description: String,
|
val description: String,
|
||||||
val type: String?,
|
val type: String?,
|
||||||
val timestamp: Long,
|
val timestamp: Long,
|
||||||
val soundName: String?,
|
val soundName: String?,
|
||||||
override val isRedacted: Boolean = false,
|
override val isRedacted: Boolean = false,
|
||||||
override val isUpdated: Boolean = false
|
override val isUpdated: Boolean = false
|
||||||
) : NotifiableEvent
|
) : NotifiableEvent
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,8 @@ import java.io.Serializable
|
||||||
* Parent interface for all events which can be displayed as a Notification.
|
* Parent interface for all events which can be displayed as a Notification.
|
||||||
*/
|
*/
|
||||||
sealed interface NotifiableEvent : Serializable {
|
sealed interface NotifiableEvent : Serializable {
|
||||||
|
val sessionId: String
|
||||||
|
val roomId: String
|
||||||
val eventId: String
|
val eventId: String
|
||||||
val editedEventId: String?
|
val editedEventId: String?
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -16,8 +16,14 @@
|
||||||
package io.element.android.libraries.push.impl.notifications.model
|
package io.element.android.libraries.push.impl.notifications.model
|
||||||
|
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
|
import io.element.android.services.appnavstate.api.AppNavigationState
|
||||||
|
import io.element.android.services.appnavstate.api.currentRoomId
|
||||||
|
import io.element.android.services.appnavstate.api.currentSessionId
|
||||||
|
import io.element.android.services.appnavstate.api.currentThreadId
|
||||||
|
|
||||||
data class NotifiableMessageEvent(
|
data class NotifiableMessageEvent(
|
||||||
|
override val sessionId: String,
|
||||||
|
override val roomId: String,
|
||||||
override val eventId: String,
|
override val eventId: String,
|
||||||
override val editedEventId: String?,
|
override val editedEventId: String?,
|
||||||
override val canBeReplaced: Boolean,
|
override val canBeReplaced: Boolean,
|
||||||
|
|
@ -29,13 +35,11 @@ data class NotifiableMessageEvent(
|
||||||
// We cannot use Uri? type here, as that could trigger a
|
// We cannot use Uri? type here, as that could trigger a
|
||||||
// NotSerializableException when persisting this to storage
|
// NotSerializableException when persisting this to storage
|
||||||
val imageUriString: String?,
|
val imageUriString: String?,
|
||||||
val roomId: String,
|
|
||||||
val threadId: String?,
|
val threadId: String?,
|
||||||
val roomName: String?,
|
val roomName: String?,
|
||||||
val roomIsDirect: Boolean = false,
|
val roomIsDirect: Boolean = false,
|
||||||
val roomAvatarPath: String? = null,
|
val roomAvatarPath: String? = null,
|
||||||
val senderAvatarPath: String? = null,
|
val senderAvatarPath: String? = null,
|
||||||
val matrixID: String? = null,
|
|
||||||
val soundName: String? = null,
|
val soundName: String? = null,
|
||||||
// This is used for >N notification, as the result of a smart reply
|
// This is used for >N notification, as the result of a smart reply
|
||||||
val outGoingMessage: Boolean = false,
|
val outGoingMessage: Boolean = false,
|
||||||
|
|
@ -52,9 +56,12 @@ data class NotifiableMessageEvent(
|
||||||
get() = imageUriString?.let { Uri.parse(it) }
|
get() = imageUriString?.let { Uri.parse(it) }
|
||||||
}
|
}
|
||||||
|
|
||||||
fun NotifiableMessageEvent.shouldIgnoreMessageEventInRoom(currentRoomId: String?, currentThreadId: String?): Boolean {
|
fun NotifiableMessageEvent.shouldIgnoreMessageEventInRoom(
|
||||||
return when (currentRoomId) {
|
appNavigationState: AppNavigationState?
|
||||||
|
): Boolean {
|
||||||
|
val currentSessionId = appNavigationState?.currentSessionId()?.value ?: return false
|
||||||
|
return when (val currentRoomId = appNavigationState.currentRoomId()?.value) {
|
||||||
null -> false
|
null -> false
|
||||||
else -> roomId == currentRoomId && threadId == currentThreadId
|
else -> sessionId == currentSessionId && roomId == currentRoomId && threadId == appNavigationState.currentThreadId()?.value
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -16,16 +16,17 @@
|
||||||
package io.element.android.libraries.push.impl.notifications.model
|
package io.element.android.libraries.push.impl.notifications.model
|
||||||
|
|
||||||
data class SimpleNotifiableEvent(
|
data class SimpleNotifiableEvent(
|
||||||
val matrixID: String?,
|
override val sessionId: String,
|
||||||
override val eventId: String,
|
override val roomId: String,
|
||||||
override val editedEventId: String?,
|
override val eventId: String,
|
||||||
val noisy: Boolean,
|
override val editedEventId: String?,
|
||||||
val title: String,
|
val noisy: Boolean,
|
||||||
val description: String,
|
val title: String,
|
||||||
val type: String?,
|
val description: String,
|
||||||
val timestamp: Long,
|
val type: String?,
|
||||||
val soundName: String?,
|
val timestamp: Long,
|
||||||
override var canBeReplaced: Boolean,
|
val soundName: String?,
|
||||||
override val isRedacted: Boolean = false,
|
override var canBeReplaced: Boolean,
|
||||||
override val isUpdated: Boolean = false
|
override val isRedacted: Boolean = false,
|
||||||
|
override val isUpdated: Boolean = false
|
||||||
) : NotifiableEvent
|
) : NotifiableEvent
|
||||||
|
|
|
||||||
|
|
@ -20,15 +20,12 @@ import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.os.Handler
|
import android.os.Handler
|
||||||
import android.os.Looper
|
import android.os.Looper
|
||||||
import androidx.lifecycle.Lifecycle
|
|
||||||
import androidx.lifecycle.ProcessLifecycleOwner
|
|
||||||
import androidx.localbroadcastmanager.content.LocalBroadcastManager
|
import androidx.localbroadcastmanager.content.LocalBroadcastManager
|
||||||
import io.element.android.libraries.androidutils.network.WifiDetector
|
import io.element.android.libraries.androidutils.network.WifiDetector
|
||||||
import io.element.android.libraries.core.log.logger.LoggerTag
|
import io.element.android.libraries.core.log.logger.LoggerTag
|
||||||
import io.element.android.libraries.core.meta.BuildMeta
|
import io.element.android.libraries.core.meta.BuildMeta
|
||||||
import io.element.android.libraries.di.ApplicationContext
|
import io.element.android.libraries.di.ApplicationContext
|
||||||
import io.element.android.libraries.matrix.api.auth.MatrixAuthenticationService
|
import io.element.android.libraries.matrix.api.auth.MatrixAuthenticationService
|
||||||
import io.element.android.libraries.matrix.api.core.SessionId
|
|
||||||
import io.element.android.libraries.push.api.store.PushDataStore
|
import io.element.android.libraries.push.api.store.PushDataStore
|
||||||
import io.element.android.libraries.push.impl.PushersManager
|
import io.element.android.libraries.push.impl.PushersManager
|
||||||
import io.element.android.libraries.push.impl.clientsecret.PushClientSecret
|
import io.element.android.libraries.push.impl.clientsecret.PushClientSecret
|
||||||
|
|
@ -49,7 +46,6 @@ private val loggerTag = LoggerTag("PushHandler", pushLoggerTag)
|
||||||
class PushHandler @Inject constructor(
|
class PushHandler @Inject constructor(
|
||||||
private val notificationDrawerManager: NotificationDrawerManager,
|
private val notificationDrawerManager: NotificationDrawerManager,
|
||||||
private val notifiableEventResolver: NotifiableEventResolver,
|
private val notifiableEventResolver: NotifiableEventResolver,
|
||||||
// private val activeSessionHolder: ActiveSessionHolder,
|
|
||||||
private val pushDataStore: PushDataStore,
|
private val pushDataStore: PushDataStore,
|
||||||
private val defaultPushDataStore: DefaultPushDataStore,
|
private val defaultPushDataStore: DefaultPushDataStore,
|
||||||
private val pushClientSecret: PushClientSecret,
|
private val pushClientSecret: PushClientSecret,
|
||||||
|
|
@ -95,12 +91,7 @@ class PushHandler @Inject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
mUIHandler.post {
|
mUIHandler.post {
|
||||||
if (ProcessLifecycleOwner.get().lifecycle.currentState.isAtLeast(Lifecycle.State.STARTED)) {
|
coroutineScope.launch(Dispatchers.IO) { handleInternal(pushData) }
|
||||||
// we are in foreground, let the sync do the things?
|
|
||||||
Timber.tag(loggerTag.value).d("PUSH received in a foreground state, ignore")
|
|
||||||
} else {
|
|
||||||
coroutineScope.launch(Dispatchers.IO) { handleInternal(pushData) }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -136,96 +127,16 @@ class PushHandler @Inject constructor(
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Restore session
|
val notificationData = notifiableEventResolver.resolveEvent(userId, pushData.roomId, pushData.eventId)
|
||||||
val session = matrixAuthenticationService.restoreSession(SessionId(userId)).getOrNull() ?: return
|
|
||||||
// TODO EAx, no need for a session?
|
if (notificationData == null) {
|
||||||
val notificationData = session.let {// TODO Use make the app crashes
|
Timber.w("Unable to get a notification data")
|
||||||
it.notificationService().getNotification(
|
return
|
||||||
userId = userId,
|
|
||||||
roomId = pushData.roomId,
|
|
||||||
eventId = pushData.eventId,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO Remove
|
notificationDrawerManager.updateEvents { it.onNotifiableEventReceived(notificationData) }
|
||||||
Timber.w("Notification: $notificationData")
|
|
||||||
// TODO Display notification
|
|
||||||
|
|
||||||
notificationDrawerManager.displayTemporaryNotification()
|
|
||||||
|
|
||||||
/* TODO EAx
|
|
||||||
- get the event
|
|
||||||
- display the notif
|
|
||||||
|
|
||||||
val session = activeSessionHolder.getOrInitializeSession()
|
|
||||||
|
|
||||||
if (session == null) {
|
|
||||||
Timber.tag(loggerTag.value).w("## Can't sync from push, no current session")
|
|
||||||
} else {
|
|
||||||
if (isEventAlreadyKnown(pushData)) {
|
|
||||||
Timber.tag(loggerTag.value).d("Ignoring push, event already known")
|
|
||||||
} else {
|
|
||||||
// Try to get the Event content faster
|
|
||||||
Timber.tag(loggerTag.value).d("Requesting event in fast lane")
|
|
||||||
getEventFastLane(session, pushData)
|
|
||||||
|
|
||||||
Timber.tag(loggerTag.value).d("Requesting background sync")
|
|
||||||
session.syncService().requireBackgroundSync()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
*/
|
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Timber.tag(loggerTag.value).e(e, "## handleInternal() failed")
|
Timber.tag(loggerTag.value).e(e, "## handleInternal() failed")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* TODO EAx
|
|
||||||
private suspend fun getEventFastLane(session: Session, pushData: PushData) {
|
|
||||||
pushData.roomId ?: return
|
|
||||||
pushData.eventId ?: return
|
|
||||||
|
|
||||||
if (wifiDetector.isConnectedToWifi().not()) {
|
|
||||||
Timber.tag(loggerTag.value).d("No WiFi network, do not get Event")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
Timber.tag(loggerTag.value).d("Fast lane: start request")
|
|
||||||
val event = tryOrNull { session.eventService().getEvent(pushData.roomId, pushData.eventId) } ?: return
|
|
||||||
|
|
||||||
val resolvedEvent = notifiableEventResolver.resolveInMemoryEvent(session, event, canBeReplaced = true)
|
|
||||||
|
|
||||||
if (resolvedEvent is NotifiableMessageEvent) {
|
|
||||||
// If the room is currently displayed, we will not show a notification, so no need to get the Event faster
|
|
||||||
if (notificationDrawerManager.shouldIgnoreMessageEventInRoom(resolvedEvent)) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
resolvedEvent
|
|
||||||
?.also { Timber.tag(loggerTag.value).d("Fast lane: notify drawer") }
|
|
||||||
?.let {
|
|
||||||
notificationDrawerManager.updateEvents { it.onNotifiableEventReceived(resolvedEvent) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
// check if the event was not yet received
|
|
||||||
// a previous catchup might have already retrieved the notified event
|
|
||||||
private fun isEventAlreadyKnown(pushData: PushData): Boolean {
|
|
||||||
/* TODO EAx
|
|
||||||
if (pushData.eventId != null && pushData.roomId != null) {
|
|
||||||
try {
|
|
||||||
val session = activeSessionHolder.getSafeActiveSession() ?: return false
|
|
||||||
val room = session.getRoom(pushData.roomId) ?: return false
|
|
||||||
return room.getTimelineEvent(pushData.eventId) != null
|
|
||||||
} catch (e: Exception) {
|
|
||||||
Timber.tag(loggerTag.value).e(e, "## isEventAlreadyKnown() : failed to check if the event was already defined")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
*/
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,6 @@
|
||||||
<string name="notification_silent_notifications">Silent notifications</string>
|
<string name="notification_silent_notifications">Silent notifications</string>
|
||||||
<string name="call">Call</string>
|
<string name="call">Call</string>
|
||||||
<string name="notification_new_messages">New Messages</string>
|
<string name="notification_new_messages">New Messages</string>
|
||||||
<string name="notification_new_messages_temporary">You have new message(s)</string>
|
|
||||||
<string name="action_mark_room_read">Mark as read</string>
|
<string name="action_mark_room_read">Mark as read</string>
|
||||||
<string name="action_join">Join</string>
|
<string name="action_join">Join</string>
|
||||||
<string name="action_reject">Reject</string>
|
<string name="action_reject">Reject</string>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue