Get Event after a Push for a faster notification display in some conditions

This commit is contained in:
Benoit Marty 2021-03-24 18:48:25 +01:00 committed by Benoit Marty
parent af023669ba
commit 96153fe92a
14 changed files with 234 additions and 41 deletions

View File

@ -17,6 +17,7 @@ Improvements 🙌:
- Add better support for empty room name fallback (#3106) - Add better support for empty room name fallback (#3106)
- Room list improvements (paging) - Room list improvements (paging)
- Fix quick click action (#3127) - Fix quick click action (#3127)
- Get Event after a Push for a faster notification display in some conditions
Bugfix 🐛: Bugfix 🐛:
- Fix bad theme change for the MainActivity - Fix bad theme change for the MainActivity

View File

@ -30,6 +30,7 @@ import org.matrix.android.sdk.api.session.call.CallSignalingService
import org.matrix.android.sdk.api.session.content.ContentUploadStateTracker import org.matrix.android.sdk.api.session.content.ContentUploadStateTracker
import org.matrix.android.sdk.api.session.content.ContentUrlResolver import org.matrix.android.sdk.api.session.content.ContentUrlResolver
import org.matrix.android.sdk.api.session.crypto.CryptoService import org.matrix.android.sdk.api.session.crypto.CryptoService
import org.matrix.android.sdk.api.session.events.EventService
import org.matrix.android.sdk.api.session.file.ContentDownloadStateTracker import org.matrix.android.sdk.api.session.file.ContentDownloadStateTracker
import org.matrix.android.sdk.api.session.file.FileService import org.matrix.android.sdk.api.session.file.FileService
import org.matrix.android.sdk.api.session.group.GroupService import org.matrix.android.sdk.api.session.group.GroupService
@ -68,6 +69,7 @@ interface Session :
SignOutService, SignOutService,
FilterService, FilterService,
TermsService, TermsService,
EventService,
ProfileService, ProfileService,
PushRuleService, PushRuleService,
PushersService, PushersService,

View File

@ -0,0 +1,28 @@
/*
* Copyright (c) 2021 The Matrix.org Foundation C.I.C.
*
* 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 org.matrix.android.sdk.api.session.events
import org.matrix.android.sdk.api.session.events.model.Event
interface EventService {
/**
* Ask the homeserver for an event content. The SDK will try to decrypt it if it is possible
* The result will not be stored into cache
*/
suspend fun getEvent(roomId: String, eventId: String): Event
}

View File

@ -38,16 +38,21 @@ internal fun isEventRead(realmConfiguration: RealmConfiguration,
Realm.getInstance(realmConfiguration).use { realm -> Realm.getInstance(realmConfiguration).use { realm ->
val liveChunk = ChunkEntity.findLastForwardChunkOfRoom(realm, roomId) ?: return@use val liveChunk = ChunkEntity.findLastForwardChunkOfRoom(realm, roomId) ?: return@use
val eventToCheck = liveChunk.timelineEvents.find(eventId) val eventToCheck = liveChunk.timelineEvents.find(eventId)
isEventRead = if (eventToCheck == null || eventToCheck.root?.sender == userId) { isEventRead = when {
true eventToCheck == null -> {
} else { // This can happen in case of fast lane Event
val readReceipt = ReadReceiptEntity.where(realm, roomId, userId).findFirst() false
?: return@use }
val readReceiptIndex = liveChunk.timelineEvents.find(readReceipt.eventId)?.displayIndex eventToCheck.root?.sender == userId -> true
?: Int.MIN_VALUE else -> {
val eventToCheckIndex = eventToCheck.displayIndex val readReceipt = ReadReceiptEntity.where(realm, roomId, userId).findFirst()
?: return@use
val readReceiptIndex = liveChunk.timelineEvents.find(readReceipt.eventId)?.displayIndex
?: Int.MIN_VALUE
val eventToCheckIndex = eventToCheck.displayIndex
eventToCheckIndex <= readReceiptIndex eventToCheckIndex <= readReceiptIndex
}
} }
} }

View File

@ -33,6 +33,7 @@ import org.matrix.android.sdk.api.session.call.CallSignalingService
import org.matrix.android.sdk.api.session.content.ContentUploadStateTracker import org.matrix.android.sdk.api.session.content.ContentUploadStateTracker
import org.matrix.android.sdk.api.session.content.ContentUrlResolver import org.matrix.android.sdk.api.session.content.ContentUrlResolver
import org.matrix.android.sdk.api.session.crypto.CryptoService import org.matrix.android.sdk.api.session.crypto.CryptoService
import org.matrix.android.sdk.api.session.events.EventService
import org.matrix.android.sdk.api.session.file.ContentDownloadStateTracker import org.matrix.android.sdk.api.session.file.ContentDownloadStateTracker
import org.matrix.android.sdk.api.session.file.FileService import org.matrix.android.sdk.api.session.file.FileService
import org.matrix.android.sdk.api.session.group.GroupService import org.matrix.android.sdk.api.session.group.GroupService
@ -114,6 +115,7 @@ internal class DefaultSession @Inject constructor(
private val accountDataService: Lazy<AccountDataService>, private val accountDataService: Lazy<AccountDataService>,
private val _sharedSecretStorageService: Lazy<SharedSecretStorageService>, private val _sharedSecretStorageService: Lazy<SharedSecretStorageService>,
private val accountService: Lazy<AccountService>, private val accountService: Lazy<AccountService>,
private val eventService: Lazy<EventService>,
private val defaultIdentityService: DefaultIdentityService, private val defaultIdentityService: DefaultIdentityService,
private val integrationManagerService: IntegrationManagerService, private val integrationManagerService: IntegrationManagerService,
private val thirdPartyService: Lazy<ThirdPartyService>, private val thirdPartyService: Lazy<ThirdPartyService>,
@ -129,6 +131,7 @@ internal class DefaultSession @Inject constructor(
FilterService by filterService.get(), FilterService by filterService.get(),
PushRuleService by pushRuleService.get(), PushRuleService by pushRuleService.get(),
PushersService by pushersService.get(), PushersService by pushersService.get(),
EventService by eventService.get(),
TermsService by termsService.get(), TermsService by termsService.get(),
InitialSyncProgressService by initialSyncProgressService.get(), InitialSyncProgressService by initialSyncProgressService.get(),
SecureStorageService by secureStorageService.get(), SecureStorageService by secureStorageService.get(),

View File

@ -32,10 +32,11 @@ import org.matrix.android.sdk.api.auth.data.HomeServerConnectionConfig
import org.matrix.android.sdk.api.auth.data.SessionParams import org.matrix.android.sdk.api.auth.data.SessionParams
import org.matrix.android.sdk.api.auth.data.sessionId import org.matrix.android.sdk.api.auth.data.sessionId
import org.matrix.android.sdk.api.crypto.MXCryptoConfig import org.matrix.android.sdk.api.crypto.MXCryptoConfig
import org.matrix.android.sdk.api.session.initsync.InitialSyncProgressService
import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.Session
import org.matrix.android.sdk.api.session.accountdata.AccountDataService import org.matrix.android.sdk.api.session.accountdata.AccountDataService
import org.matrix.android.sdk.api.session.events.EventService
import org.matrix.android.sdk.api.session.homeserver.HomeServerCapabilitiesService import org.matrix.android.sdk.api.session.homeserver.HomeServerCapabilitiesService
import org.matrix.android.sdk.api.session.initsync.InitialSyncProgressService
import org.matrix.android.sdk.api.session.permalinks.PermalinkService import org.matrix.android.sdk.api.session.permalinks.PermalinkService
import org.matrix.android.sdk.api.session.securestorage.SecureStorageService import org.matrix.android.sdk.api.session.securestorage.SecureStorageService
import org.matrix.android.sdk.api.session.securestorage.SharedSecretStorageService import org.matrix.android.sdk.api.session.securestorage.SharedSecretStorageService
@ -75,6 +76,7 @@ import org.matrix.android.sdk.internal.network.token.AccessTokenProvider
import org.matrix.android.sdk.internal.network.token.HomeserverAccessTokenProvider import org.matrix.android.sdk.internal.network.token.HomeserverAccessTokenProvider
import org.matrix.android.sdk.internal.session.call.CallEventProcessor import org.matrix.android.sdk.internal.session.call.CallEventProcessor
import org.matrix.android.sdk.internal.session.download.DownloadProgressInterceptor import org.matrix.android.sdk.internal.session.download.DownloadProgressInterceptor
import org.matrix.android.sdk.internal.session.events.DefaultEventService
import org.matrix.android.sdk.internal.session.homeserver.DefaultHomeServerCapabilitiesService import org.matrix.android.sdk.internal.session.homeserver.DefaultHomeServerCapabilitiesService
import org.matrix.android.sdk.internal.session.identity.DefaultIdentityService import org.matrix.android.sdk.internal.session.identity.DefaultIdentityService
import org.matrix.android.sdk.internal.session.initsync.DefaultInitialSyncProgressService import org.matrix.android.sdk.internal.session.initsync.DefaultInitialSyncProgressService
@ -357,6 +359,9 @@ internal abstract class SessionModule {
@Binds @Binds
abstract fun bindAccountDataService(service: DefaultAccountDataService): AccountDataService abstract fun bindAccountDataService(service: DefaultAccountDataService): AccountDataService
@Binds
abstract fun bindEventService(service: DefaultEventService): EventService
@Binds @Binds
abstract fun bindSharedSecretStorageService(service: DefaultSharedSecretStorageService): SharedSecretStorageService abstract fun bindSharedSecretStorageService(service: DefaultSharedSecretStorageService): SharedSecretStorageService

View File

@ -21,9 +21,11 @@ import org.matrix.android.sdk.api.session.events.model.Event
import org.matrix.android.sdk.api.session.events.model.EventType import org.matrix.android.sdk.api.session.events.model.EventType
import org.matrix.android.sdk.internal.database.model.EventInsertType import org.matrix.android.sdk.internal.database.model.EventInsertType
import org.matrix.android.sdk.internal.session.EventInsertLiveProcessor import org.matrix.android.sdk.internal.session.EventInsertLiveProcessor
import org.matrix.android.sdk.internal.session.SessionScope
import timber.log.Timber import timber.log.Timber
import javax.inject.Inject import javax.inject.Inject
@SessionScope
internal class CallEventProcessor @Inject constructor(private val callSignalingHandler: CallSignalingHandler) internal class CallEventProcessor @Inject constructor(private val callSignalingHandler: CallSignalingHandler)
: EventInsertLiveProcessor { : EventInsertLiveProcessor {
@ -51,6 +53,15 @@ internal class CallEventProcessor @Inject constructor(private val callSignalingH
eventsToPostProcess.add(event) eventsToPostProcess.add(event)
} }
fun shouldProcessFastLane(eventType: String): Boolean {
return eventType == EventType.CALL_INVITE
}
suspend fun processFastLane(event: Event) {
eventsToPostProcess.add(event)
onPostProcess()
}
override suspend fun onPostProcess() { override suspend fun onPostProcess() {
eventsToPostProcess.forEach { eventsToPostProcess.forEach {
dispatchToCallSignalingHandlerIfNeeded(it) dispatchToCallSignalingHandlerIfNeeded(it)
@ -60,7 +71,7 @@ internal class CallEventProcessor @Inject constructor(private val callSignalingH
private fun dispatchToCallSignalingHandlerIfNeeded(event: Event) { private fun dispatchToCallSignalingHandlerIfNeeded(event: Event) {
val now = System.currentTimeMillis() val now = System.currentTimeMillis()
// TODO might check if an invite is not closed (hangup/answsered) in the same event batch? // TODO might check if an invite is not closed (hangup/answered) in the same event batch?
event.roomId ?: return Unit.also { event.roomId ?: return Unit.also {
Timber.w("Event with no room id ${event.eventId}") Timber.w("Event with no room id ${event.eventId}")
} }

View File

@ -56,25 +56,25 @@ internal class CallSignalingHandler @Inject constructor(private val activeCallHa
fun onCallEvent(event: Event) { fun onCallEvent(event: Event) {
when (event.getClearType()) { when (event.getClearType()) {
EventType.CALL_ANSWER -> { EventType.CALL_ANSWER -> {
handleCallAnswerEvent(event) handleCallAnswerEvent(event)
} }
EventType.CALL_INVITE -> { EventType.CALL_INVITE -> {
handleCallInviteEvent(event) handleCallInviteEvent(event)
} }
EventType.CALL_HANGUP -> { EventType.CALL_HANGUP -> {
handleCallHangupEvent(event) handleCallHangupEvent(event)
} }
EventType.CALL_REJECT -> { EventType.CALL_REJECT -> {
handleCallRejectEvent(event) handleCallRejectEvent(event)
} }
EventType.CALL_CANDIDATES -> { EventType.CALL_CANDIDATES -> {
handleCallCandidatesEvent(event) handleCallCandidatesEvent(event)
} }
EventType.CALL_SELECT_ANSWER -> { EventType.CALL_SELECT_ANSWER -> {
handleCallSelectAnswerEvent(event) handleCallSelectAnswerEvent(event)
} }
EventType.CALL_NEGOTIATE -> { EventType.CALL_NEGOTIATE -> {
handleCallNegotiateEvent(event) handleCallNegotiateEvent(event)
} }
} }
@ -168,6 +168,14 @@ internal class CallSignalingHandler @Inject constructor(private val activeCallHa
return return
} }
val content = event.getClearContent().toModel<CallInviteContent>() ?: return val content = event.getClearContent().toModel<CallInviteContent>() ?: return
content.callId ?: return
if (activeCallHandler.getCallWithId(content.callId) != null) {
// Call is already known, maybe due to fast lane. Ignore
Timber.d("Ignoring already known call invite")
return
}
val incomingCall = mxCallFactory.createIncomingCall( val incomingCall = mxCallFactory.createIncomingCall(
roomId = event.roomId, roomId = event.roomId,
opponentUserId = event.senderId, opponentUserId = event.senderId,

View File

@ -0,0 +1,40 @@
/*
* Copyright (c) 2021 The Matrix.org Foundation C.I.C.
*
* 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 org.matrix.android.sdk.internal.session.events
import org.matrix.android.sdk.api.session.events.EventService
import org.matrix.android.sdk.api.session.events.model.Event
import org.matrix.android.sdk.internal.session.call.CallEventProcessor
import org.matrix.android.sdk.internal.session.room.timeline.GetEventTask
import javax.inject.Inject
internal class DefaultEventService @Inject constructor(
private val getEventTask: GetEventTask,
private val callEventProcessor: CallEventProcessor
) : EventService {
override suspend fun getEvent(roomId: String, eventId: String): Event {
val event = getEventTask.execute(GetEventTask.Params(roomId, eventId))
// Fast lane to the call event processors: try to make the incoming call ring faster
if (callEventProcessor.shouldProcessFastLane(event.getClearType())) {
callEventProcessor.processFastLane(event)
}
return event
}
}

View File

@ -79,9 +79,11 @@ import org.matrix.android.sdk.internal.session.room.tags.DefaultDeleteTagFromRoo
import org.matrix.android.sdk.internal.session.room.tags.DeleteTagFromRoomTask import org.matrix.android.sdk.internal.session.room.tags.DeleteTagFromRoomTask
import org.matrix.android.sdk.internal.session.room.timeline.DefaultFetchTokenAndPaginateTask import org.matrix.android.sdk.internal.session.room.timeline.DefaultFetchTokenAndPaginateTask
import org.matrix.android.sdk.internal.session.room.timeline.DefaultGetContextOfEventTask import org.matrix.android.sdk.internal.session.room.timeline.DefaultGetContextOfEventTask
import org.matrix.android.sdk.internal.session.room.timeline.DefaultGetEventTask
import org.matrix.android.sdk.internal.session.room.timeline.DefaultPaginationTask import org.matrix.android.sdk.internal.session.room.timeline.DefaultPaginationTask
import org.matrix.android.sdk.internal.session.room.timeline.FetchTokenAndPaginateTask import org.matrix.android.sdk.internal.session.room.timeline.FetchTokenAndPaginateTask
import org.matrix.android.sdk.internal.session.room.timeline.GetContextOfEventTask import org.matrix.android.sdk.internal.session.room.timeline.GetContextOfEventTask
import org.matrix.android.sdk.internal.session.room.timeline.GetEventTask
import org.matrix.android.sdk.internal.session.room.timeline.PaginationTask import org.matrix.android.sdk.internal.session.room.timeline.PaginationTask
import org.matrix.android.sdk.internal.session.room.typing.DefaultSendTypingTask import org.matrix.android.sdk.internal.session.room.typing.DefaultSendTypingTask
import org.matrix.android.sdk.internal.session.room.typing.SendTypingTask import org.matrix.android.sdk.internal.session.room.typing.SendTypingTask
@ -228,4 +230,7 @@ internal abstract class RoomModule {
@Binds @Binds
abstract fun bindPeekRoomTask(task: DefaultPeekRoomTask): PeekRoomTask abstract fun bindPeekRoomTask(task: DefaultPeekRoomTask): PeekRoomTask
@Binds
abstract fun bindGetEventTask(task: DefaultGetEventTask): GetEventTask
} }

View File

@ -16,28 +16,49 @@
package org.matrix.android.sdk.internal.session.room.timeline package org.matrix.android.sdk.internal.session.room.timeline
import org.matrix.android.sdk.api.extensions.tryOrNull
import org.matrix.android.sdk.api.session.events.model.Event import org.matrix.android.sdk.api.session.events.model.Event
import org.matrix.android.sdk.internal.crypto.EventDecryptor
import org.matrix.android.sdk.internal.crypto.algorithms.olm.OlmDecryptionResult
import org.matrix.android.sdk.internal.network.GlobalErrorReceiver import org.matrix.android.sdk.internal.network.GlobalErrorReceiver
import org.matrix.android.sdk.internal.network.executeRequest import org.matrix.android.sdk.internal.network.executeRequest
import org.matrix.android.sdk.internal.session.room.RoomAPI import org.matrix.android.sdk.internal.session.room.RoomAPI
import org.matrix.android.sdk.internal.task.Task import org.matrix.android.sdk.internal.task.Task
import javax.inject.Inject import javax.inject.Inject
// TODO Add parent task internal interface GetEventTask : Task<GetEventTask.Params, Event> {
data class Params(
internal class GetEventTask @Inject constructor(
private val roomAPI: RoomAPI,
private val globalErrorReceiver: GlobalErrorReceiver
) : Task<GetEventTask.Params, Event> {
internal data class Params(
val roomId: String, val roomId: String,
val eventId: String val eventId: String,
) )
}
override suspend fun execute(params: Params): Event { internal class DefaultGetEventTask @Inject constructor(
return executeRequest(globalErrorReceiver) { private val roomAPI: RoomAPI,
private val globalErrorReceiver: GlobalErrorReceiver,
private val eventDecryptor: EventDecryptor
) : GetEventTask {
override suspend fun execute(params: GetEventTask.Params): Event {
val event = executeRequest(globalErrorReceiver) {
roomAPI.getEvent(params.roomId, params.eventId) roomAPI.getEvent(params.roomId, params.eventId)
} }
// Try to decrypt the Event
if (event.isEncrypted()) {
tryOrNull(message = "Unable to decrypt the event") {
eventDecryptor.decryptEvent(event, "")
}
?.let { result ->
event.mxDecryptionResult = OlmDecryptionResult(
payload = result.clearEvent,
senderKey = result.senderCurve25519Key,
keysClaimed = result.claimedEd25519Key?.let { mapOf("ed25519" to it) },
forwardingCurve25519KeyChain = result.forwardingCurve25519KeyChain
)
}
}
return event
} }
} }

View File

@ -40,6 +40,9 @@ import im.vector.app.features.notifications.NotificationUtils
import im.vector.app.features.notifications.SimpleNotifiableEvent import im.vector.app.features.notifications.SimpleNotifiableEvent
import im.vector.app.features.settings.VectorPreferences import im.vector.app.features.settings.VectorPreferences
import im.vector.app.push.fcm.FcmHelper import im.vector.app.push.fcm.FcmHelper
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.launch
import org.matrix.android.sdk.api.pushrules.Action import org.matrix.android.sdk.api.pushrules.Action
import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.Session
import org.matrix.android.sdk.api.session.events.model.Event import org.matrix.android.sdk.api.session.events.model.Event
@ -56,6 +59,8 @@ class VectorFirebaseMessagingService : FirebaseMessagingService() {
private lateinit var activeSessionHolder: ActiveSessionHolder private lateinit var activeSessionHolder: ActiveSessionHolder
private lateinit var vectorPreferences: VectorPreferences private lateinit var vectorPreferences: VectorPreferences
private val coroutineScope = CoroutineScope(SupervisorJob())
// UI handler // UI handler
private val mUIHandler by lazy { private val mUIHandler by lazy {
Handler(Looper.getMainLooper()) Handler(Looper.getMainLooper())
@ -78,6 +83,11 @@ class VectorFirebaseMessagingService : FirebaseMessagingService() {
* @param message the message * @param message the message
*/ */
override fun onMessageReceived(message: RemoteMessage) { override fun onMessageReceived(message: RemoteMessage) {
if (BuildConfig.LOW_PRIVACY_LOG_ENABLE) {
Timber.d("## onMessageReceived() %s", message.data.toString())
}
Timber.d("## onMessageReceived() from FCM with priority %s", message.priority)
// Diagnostic Push // Diagnostic Push
if (message.data["event_id"] == PushersManager.TEST_EVENT_ID) { if (message.data["event_id"] == PushersManager.TEST_EVENT_ID) {
val intent = Intent(NotificationUtils.PUSH_ACTION) val intent = Intent(NotificationUtils.PUSH_ACTION)
@ -90,14 +100,10 @@ class VectorFirebaseMessagingService : FirebaseMessagingService() {
return return
} }
if (BuildConfig.LOW_PRIVACY_LOG_ENABLE) {
Timber.i("## onMessageReceived() %s", message.data.toString())
Timber.i("## onMessageReceived() from FCM with priority %s", message.priority)
}
mUIHandler.post { mUIHandler.post {
if (ProcessLifecycleOwner.get().lifecycle.currentState.isAtLeast(Lifecycle.State.STARTED)) { if (ProcessLifecycleOwner.get().lifecycle.currentState.isAtLeast(Lifecycle.State.STARTED)) {
// we are in foreground, let the sync do the things? // we are in foreground, let the sync do the things?
Timber.v("PUSH received in a foreground state, ignore") Timber.d("PUSH received in a foreground state, ignore")
} else { } else {
onMessageReceivedInternal(message.data) onMessageReceivedInternal(message.data)
} }
@ -140,7 +146,9 @@ class VectorFirebaseMessagingService : FirebaseMessagingService() {
private fun onMessageReceivedInternal(data: Map<String, String>) { private fun onMessageReceivedInternal(data: Map<String, String>) {
try { try {
if (BuildConfig.LOW_PRIVACY_LOG_ENABLE) { if (BuildConfig.LOW_PRIVACY_LOG_ENABLE) {
Timber.i("## onMessageReceivedInternal() : $data") Timber.d("## onMessageReceivedInternal() : $data")
} else {
Timber.d("## onMessageReceivedInternal() : $data")
} }
// update the badge counter // update the badge counter
@ -156,9 +164,13 @@ class VectorFirebaseMessagingService : FirebaseMessagingService() {
val roomId = data["room_id"] val roomId = data["room_id"]
if (isEventAlreadyKnown(eventId, roomId)) { if (isEventAlreadyKnown(eventId, roomId)) {
Timber.i("Ignoring push, event already known") Timber.d("Ignoring push, event already known")
} else { } else {
Timber.v("Requesting background sync") // Try to get the Event content faster
Timber.d("Requesting event in fast lane")
getEventFastLane(session, roomId, eventId)
Timber.d("Requesting background sync")
session.requireBackgroundSync() session.requireBackgroundSync()
} }
} }
@ -167,6 +179,32 @@ class VectorFirebaseMessagingService : FirebaseMessagingService() {
} }
} }
private fun getEventFastLane(session: Session, roomId: String?, eventId: String?) {
roomId?.takeIf { it.isNotEmpty() } ?: return
eventId?.takeIf { it.isNotEmpty() } ?: return
// If the room is currently displayed, we will not show a notification, so no need to get the Event faster
if (notificationDrawerManager.shouldIgnoreMessageEventInRoom(roomId)) {
return
}
coroutineScope.launch {
Timber.d("Fast lane: start request")
val event = session.getEvent(roomId, eventId)
val resolvedEvent = notifiableEventResolver.resolveInMemoryEvent(session, event)
// TODO Test the Event against the push rules
resolvedEvent
?.also { Timber.d("Fast lane: notify drawer") }
?.let {
it.isPushGatewayEvent = true
notificationDrawerManager.onNotifiableEventReceived(it)
notificationDrawerManager.refreshNotificationDrawer()
}
}
}
// check if the event was not yet received // check if the event was not yet received
// a previous catchup might have already retrieved the notified event // a previous catchup might have already retrieved the notified event
private fun isEventAlreadyKnown(eventId: String?, roomId: String?): Boolean { private fun isEventAlreadyKnown(eventId: String?, roomId: String?): Boolean {

View File

@ -29,6 +29,7 @@ import org.matrix.android.sdk.api.session.events.model.EventType
import org.matrix.android.sdk.api.session.events.model.toModel import org.matrix.android.sdk.api.session.events.model.toModel
import org.matrix.android.sdk.api.session.room.model.Membership import org.matrix.android.sdk.api.session.room.model.Membership
import org.matrix.android.sdk.api.session.room.model.RoomMemberContent import org.matrix.android.sdk.api.session.room.model.RoomMemberContent
import org.matrix.android.sdk.api.session.room.sender.SenderInfo
import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent
import org.matrix.android.sdk.api.session.room.timeline.getEditedEventId import org.matrix.android.sdk.api.session.room.timeline.getEditedEventId
import org.matrix.android.sdk.internal.crypto.algorithms.olm.OlmDecryptionResult import org.matrix.android.sdk.internal.crypto.algorithms.olm.OlmDecryptionResult
@ -42,9 +43,10 @@ import javax.inject.Inject
* The NotifiableEventResolver is the only aware of session/store, the NotificationDrawerManager has no knowledge of that, * The NotifiableEventResolver is the only aware of session/store, the NotificationDrawerManager has no knowledge of that,
* this pattern allow decoupling between the object responsible of displaying notifications and the matrix sdk. * this pattern allow decoupling between the object responsible of displaying notifications and the matrix sdk.
*/ */
class NotifiableEventResolver @Inject constructor(private val stringProvider: StringProvider, class NotifiableEventResolver @Inject constructor(
private val noticeEventFormatter: NoticeEventFormatter, private val stringProvider: StringProvider,
private val displayableEventFormatter: DisplayableEventFormatter) { private val noticeEventFormatter: NoticeEventFormatter,
private val displayableEventFormatter: DisplayableEventFormatter) {
// private val eventDisplay = RiotEventDisplay(context) // private val eventDisplay = RiotEventDisplay(context)
@ -84,6 +86,28 @@ class NotifiableEventResolver @Inject constructor(private val stringProvider: St
} }
} }
fun resolveInMemoryEvent(session: Session, event: Event): NotifiableEvent? {
if (event.getClearType() != EventType.MESSAGE) return null
// TODO Ignore message edition
val user = session.getUser(event.senderId!!) ?: return null
val timelineEvent = TimelineEvent(
root = event,
localId = -1,
eventId = event.eventId!!,
displayIndex = 0,
senderInfo = SenderInfo(
userId = user.userId,
displayName = user.getBestName(),
isUniqueDisplayName = true,
avatarUrl = user.avatarUrl
)
)
return resolveMessageEvent(timelineEvent, session)
}
private fun resolveMessageEvent(event: TimelineEvent, session: Session): NotifiableEvent? { private fun resolveMessageEvent(event: TimelineEvent, session: Session): NotifiableEvent? {
// The event only contains an eventId, and roomId (type is m.room.*) , we need to get the displayable content (names, avatar, text, etc...) // 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*/) val room = session.getRoom(event.root.roomId!! /*roomID cannot be null*/)

View File

@ -89,7 +89,9 @@ class NotificationDrawerManager @Inject constructor(private val context: Context
// If we support multi session, event list should be per userId // If we support multi session, event list should be per userId
// Currently only manage single session // Currently only manage single session
if (BuildConfig.LOW_PRIVACY_LOG_ENABLE) { if (BuildConfig.LOW_PRIVACY_LOG_ENABLE) {
Timber.v("%%%%%%%% onNotifiableEventReceived $notifiableEvent") Timber.d("onNotifiableEventReceived(): $notifiableEvent")
} else {
Timber.d("onNotifiableEventReceived(): is push: ${notifiableEvent.isPushGatewayEvent}")
} }
synchronized(eventList) { synchronized(eventList) {
val existing = eventList.firstOrNull { it.eventId == notifiableEvent.eventId } val existing = eventList.firstOrNull { it.eventId == notifiableEvent.eventId }
@ -550,7 +552,7 @@ class NotificationDrawerManager @Inject constructor(private val context: Context
return bitmapLoader.getRoomBitmap(roomAvatarPath) return bitmapLoader.getRoomBitmap(roomAvatarPath)
} }
private fun shouldIgnoreMessageEventInRoom(roomId: String?): Boolean { fun shouldIgnoreMessageEventInRoom(roomId: String?): Boolean {
return currentRoomId != null && roomId == currentRoomId return currentRoomId != null && roomId == currentRoomId
} }