Merge pull request #3055 from vector-im/feature/bma/get_event
Get event
This commit is contained in:
commit
e61a9e75e8
|
@ -17,6 +17,7 @@ Improvements 🙌:
|
|||
- Add better support for empty room name fallback (#3106)
|
||||
- Room list improvements (paging)
|
||||
- Fix quick click action (#3127)
|
||||
- Get Event after a Push for a faster notification display in some conditions
|
||||
|
||||
Bugfix 🐛:
|
||||
- Fix bad theme change for the MainActivity
|
||||
|
|
|
@ -39,6 +39,8 @@ interface PushRuleService {
|
|||
|
||||
fun removePushRuleListener(listener: PushRuleListener)
|
||||
|
||||
fun getActions(event: Event): List<Action>
|
||||
|
||||
// fun fulfilledBingRule(event: Event, rules: List<PushRule>): PushRule?
|
||||
|
||||
interface PushRuleListener {
|
||||
|
|
|
@ -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.ContentUrlResolver
|
||||
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.FileService
|
||||
import org.matrix.android.sdk.api.session.group.GroupService
|
||||
|
@ -68,6 +69,7 @@ interface Session :
|
|||
SignOutService,
|
||||
FilterService,
|
||||
TermsService,
|
||||
EventService,
|
||||
ProfileService,
|
||||
PushRuleService,
|
||||
PushersService,
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
/*
|
||||
* 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
|
||||
}
|
|
@ -289,3 +289,7 @@ fun Event.getRelationContent(): RelationDefaultContent? {
|
|||
fun Event.isReply(): Boolean {
|
||||
return getRelationContent()?.inReplyTo?.eventId != null
|
||||
}
|
||||
|
||||
fun Event.isEdition(): Boolean {
|
||||
return getRelationContent()?.takeIf { it.type == RelationType.REPLACE }?.eventId != null
|
||||
}
|
||||
|
|
|
@ -38,16 +38,21 @@ internal fun isEventRead(realmConfiguration: RealmConfiguration,
|
|||
Realm.getInstance(realmConfiguration).use { realm ->
|
||||
val liveChunk = ChunkEntity.findLastForwardChunkOfRoom(realm, roomId) ?: return@use
|
||||
val eventToCheck = liveChunk.timelineEvents.find(eventId)
|
||||
isEventRead = if (eventToCheck == null || eventToCheck.root?.sender == userId) {
|
||||
true
|
||||
} else {
|
||||
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
|
||||
isEventRead = when {
|
||||
eventToCheck == null -> {
|
||||
// This can happen in case of fast lane Event
|
||||
false
|
||||
}
|
||||
eventToCheck.root?.sender == userId -> true
|
||||
else -> {
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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.ContentUrlResolver
|
||||
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.FileService
|
||||
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 _sharedSecretStorageService: Lazy<SharedSecretStorageService>,
|
||||
private val accountService: Lazy<AccountService>,
|
||||
private val eventService: Lazy<EventService>,
|
||||
private val defaultIdentityService: DefaultIdentityService,
|
||||
private val integrationManagerService: IntegrationManagerService,
|
||||
private val thirdPartyService: Lazy<ThirdPartyService>,
|
||||
|
@ -129,6 +131,7 @@ internal class DefaultSession @Inject constructor(
|
|||
FilterService by filterService.get(),
|
||||
PushRuleService by pushRuleService.get(),
|
||||
PushersService by pushersService.get(),
|
||||
EventService by eventService.get(),
|
||||
TermsService by termsService.get(),
|
||||
InitialSyncProgressService by initialSyncProgressService.get(),
|
||||
SecureStorageService by secureStorageService.get(),
|
||||
|
|
|
@ -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.sessionId
|
||||
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.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.initsync.InitialSyncProgressService
|
||||
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.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.session.call.CallEventProcessor
|
||||
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.identity.DefaultIdentityService
|
||||
import org.matrix.android.sdk.internal.session.initsync.DefaultInitialSyncProgressService
|
||||
|
@ -357,6 +359,9 @@ internal abstract class SessionModule {
|
|||
@Binds
|
||||
abstract fun bindAccountDataService(service: DefaultAccountDataService): AccountDataService
|
||||
|
||||
@Binds
|
||||
abstract fun bindEventService(service: DefaultEventService): EventService
|
||||
|
||||
@Binds
|
||||
abstract fun bindSharedSecretStorageService(service: DefaultSharedSecretStorageService): SharedSecretStorageService
|
||||
|
||||
|
|
|
@ -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.internal.database.model.EventInsertType
|
||||
import org.matrix.android.sdk.internal.session.EventInsertLiveProcessor
|
||||
import org.matrix.android.sdk.internal.session.SessionScope
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
|
||||
@SessionScope
|
||||
internal class CallEventProcessor @Inject constructor(private val callSignalingHandler: CallSignalingHandler)
|
||||
: EventInsertLiveProcessor {
|
||||
|
||||
|
@ -51,6 +53,15 @@ internal class CallEventProcessor @Inject constructor(private val callSignalingH
|
|||
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() {
|
||||
eventsToPostProcess.forEach {
|
||||
dispatchToCallSignalingHandlerIfNeeded(it)
|
||||
|
@ -60,7 +71,7 @@ internal class CallEventProcessor @Inject constructor(private val callSignalingH
|
|||
|
||||
private fun dispatchToCallSignalingHandlerIfNeeded(event: Event) {
|
||||
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 {
|
||||
Timber.w("Event with no room id ${event.eventId}")
|
||||
}
|
||||
|
|
|
@ -56,25 +56,25 @@ internal class CallSignalingHandler @Inject constructor(private val activeCallHa
|
|||
|
||||
fun onCallEvent(event: Event) {
|
||||
when (event.getClearType()) {
|
||||
EventType.CALL_ANSWER -> {
|
||||
EventType.CALL_ANSWER -> {
|
||||
handleCallAnswerEvent(event)
|
||||
}
|
||||
EventType.CALL_INVITE -> {
|
||||
EventType.CALL_INVITE -> {
|
||||
handleCallInviteEvent(event)
|
||||
}
|
||||
EventType.CALL_HANGUP -> {
|
||||
EventType.CALL_HANGUP -> {
|
||||
handleCallHangupEvent(event)
|
||||
}
|
||||
EventType.CALL_REJECT -> {
|
||||
EventType.CALL_REJECT -> {
|
||||
handleCallRejectEvent(event)
|
||||
}
|
||||
EventType.CALL_CANDIDATES -> {
|
||||
EventType.CALL_CANDIDATES -> {
|
||||
handleCallCandidatesEvent(event)
|
||||
}
|
||||
EventType.CALL_SELECT_ANSWER -> {
|
||||
handleCallSelectAnswerEvent(event)
|
||||
}
|
||||
EventType.CALL_NEGOTIATE -> {
|
||||
EventType.CALL_NEGOTIATE -> {
|
||||
handleCallNegotiateEvent(event)
|
||||
}
|
||||
}
|
||||
|
@ -168,6 +168,14 @@ internal class CallSignalingHandler @Inject constructor(private val activeCallHa
|
|||
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(
|
||||
roomId = event.roomId,
|
||||
opponentUserId = event.senderId,
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -16,8 +16,10 @@
|
|||
package org.matrix.android.sdk.internal.session.notification
|
||||
|
||||
import com.zhuinden.monarchy.Monarchy
|
||||
import org.matrix.android.sdk.api.pushrules.Action
|
||||
import org.matrix.android.sdk.api.pushrules.PushRuleService
|
||||
import org.matrix.android.sdk.api.pushrules.RuleKind
|
||||
import org.matrix.android.sdk.api.pushrules.RuleScope
|
||||
import org.matrix.android.sdk.api.pushrules.RuleSetKey
|
||||
import org.matrix.android.sdk.api.pushrules.getActions
|
||||
import org.matrix.android.sdk.api.pushrules.rest.PushRule
|
||||
|
@ -45,6 +47,7 @@ internal class DefaultPushRuleService @Inject constructor(
|
|||
private val addPushRuleTask: AddPushRuleTask,
|
||||
private val updatePushRuleActionsTask: UpdatePushRuleActionsTask,
|
||||
private val removePushRuleTask: RemovePushRuleTask,
|
||||
private val pushRuleFinder: PushRuleFinder,
|
||||
private val taskExecutor: TaskExecutor,
|
||||
@SessionDatabase private val monarchy: Monarchy
|
||||
) : PushRuleService {
|
||||
|
@ -130,6 +133,12 @@ internal class DefaultPushRuleService @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
override fun getActions(event: Event): List<Action> {
|
||||
val rules = getPushRules(RuleScope.GLOBAL).getAllRules()
|
||||
|
||||
return pushRuleFinder.fulfilledBingRule(event, rules)?.getActions().orEmpty()
|
||||
}
|
||||
|
||||
// fun processEvents(events: List<Event>) {
|
||||
// var hasDoneSomething = false
|
||||
// events.forEach { event ->
|
||||
|
|
|
@ -16,9 +16,7 @@
|
|||
|
||||
package org.matrix.android.sdk.internal.session.notification
|
||||
|
||||
import org.matrix.android.sdk.api.pushrules.ConditionResolver
|
||||
import org.matrix.android.sdk.api.pushrules.rest.PushRule
|
||||
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.internal.di.UserId
|
||||
import org.matrix.android.sdk.internal.session.sync.model.RoomsSyncResponse
|
||||
|
@ -35,7 +33,7 @@ internal interface ProcessEventForPushTask : Task<ProcessEventForPushTask.Params
|
|||
|
||||
internal class DefaultProcessEventForPushTask @Inject constructor(
|
||||
private val defaultPushRuleService: DefaultPushRuleService,
|
||||
private val conditionResolver: ConditionResolver,
|
||||
private val pushRuleFinder: PushRuleFinder,
|
||||
@UserId private val userId: String
|
||||
) : ProcessEventForPushTask {
|
||||
|
||||
|
@ -72,7 +70,7 @@ internal class DefaultProcessEventForPushTask @Inject constructor(
|
|||
Timber.v("[PushRules] Found ${allEvents.size} out of ${(newJoinEvents + inviteEvents).size}" +
|
||||
" to check for push rules with ${params.rules.size} rules")
|
||||
allEvents.forEach { event ->
|
||||
fulfilledBingRule(event, params.rules)?.let {
|
||||
pushRuleFinder.fulfilledBingRule(event, params.rules)?.let {
|
||||
Timber.v("[PushRules] Rule $it match for event ${event.eventId}")
|
||||
defaultPushRuleService.dispatchBing(event, it)
|
||||
}
|
||||
|
@ -94,13 +92,4 @@ internal class DefaultProcessEventForPushTask @Inject constructor(
|
|||
|
||||
defaultPushRuleService.dispatchFinish()
|
||||
}
|
||||
|
||||
private fun fulfilledBingRule(event: Event, rules: List<PushRule>): PushRule? {
|
||||
return rules.firstOrNull { rule ->
|
||||
// All conditions must hold true for an event in order to apply the action for the event.
|
||||
rule.enabled && rule.conditions?.all {
|
||||
it.asExecutableCondition(rule)?.isSatisfied(event, conditionResolver) ?: false
|
||||
} ?: false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
/*
|
||||
* 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.notification
|
||||
|
||||
import org.matrix.android.sdk.api.pushrules.ConditionResolver
|
||||
import org.matrix.android.sdk.api.pushrules.rest.PushRule
|
||||
import org.matrix.android.sdk.api.session.events.model.Event
|
||||
import javax.inject.Inject
|
||||
|
||||
internal class PushRuleFinder @Inject constructor(
|
||||
private val conditionResolver: ConditionResolver
|
||||
) {
|
||||
fun fulfilledBingRule(event: Event, rules: List<PushRule>): PushRule? {
|
||||
return rules.firstOrNull { rule ->
|
||||
// All conditions must hold true for an event in order to apply the action for the event.
|
||||
rule.enabled && rule.conditions?.all {
|
||||
it.asExecutableCondition(rule)?.isSatisfied(event, conditionResolver) ?: false
|
||||
} ?: false
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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.timeline.DefaultFetchTokenAndPaginateTask
|
||||
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.FetchTokenAndPaginateTask
|
||||
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.typing.DefaultSendTypingTask
|
||||
import org.matrix.android.sdk.internal.session.room.typing.SendTypingTask
|
||||
|
@ -228,4 +230,7 @@ internal abstract class RoomModule {
|
|||
|
||||
@Binds
|
||||
abstract fun bindPeekRoomTask(task: DefaultPeekRoomTask): PeekRoomTask
|
||||
|
||||
@Binds
|
||||
abstract fun bindGetEventTask(task: DefaultGetEventTask): GetEventTask
|
||||
}
|
||||
|
|
|
@ -16,28 +16,49 @@
|
|||
|
||||
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.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.executeRequest
|
||||
import org.matrix.android.sdk.internal.session.room.RoomAPI
|
||||
import org.matrix.android.sdk.internal.task.Task
|
||||
import javax.inject.Inject
|
||||
|
||||
// TODO Add parent task
|
||||
|
||||
internal class GetEventTask @Inject constructor(
|
||||
private val roomAPI: RoomAPI,
|
||||
private val globalErrorReceiver: GlobalErrorReceiver
|
||||
) : Task<GetEventTask.Params, Event> {
|
||||
|
||||
internal data class Params(
|
||||
internal interface GetEventTask : Task<GetEventTask.Params, Event> {
|
||||
data class Params(
|
||||
val roomId: String,
|
||||
val eventId: String
|
||||
)
|
||||
}
|
||||
|
||||
override suspend fun execute(params: Params): Event {
|
||||
return executeRequest(globalErrorReceiver) {
|
||||
internal class DefaultGetEventTask @Inject constructor(
|
||||
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)
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -408,6 +408,7 @@ internal class RoomSyncHandler @Inject constructor(private val readReceiptHandle
|
|||
|
||||
private fun decryptIfNeeded(event: Event, roomId: String) {
|
||||
try {
|
||||
// Event from sync does not have roomId, so add it to the event first
|
||||
val result = cryptoService.decryptEvent(event.copy(roomId = roomId), "")
|
||||
event.mxDecryptionResult = OlmDecryptionResult(
|
||||
payload = result.clearEvent,
|
||||
|
|
|
@ -31,6 +31,7 @@ import im.vector.app.BuildConfig
|
|||
import im.vector.app.R
|
||||
import im.vector.app.core.di.ActiveSessionHolder
|
||||
import im.vector.app.core.extensions.vectorComponent
|
||||
import im.vector.app.core.network.WifiDetector
|
||||
import im.vector.app.core.pushers.PushersManager
|
||||
import im.vector.app.features.badge.BadgeProxy
|
||||
import im.vector.app.features.notifications.NotifiableEventResolver
|
||||
|
@ -40,6 +41,10 @@ import im.vector.app.features.notifications.NotificationUtils
|
|||
import im.vector.app.features.notifications.SimpleNotifiableEvent
|
||||
import im.vector.app.features.settings.VectorPreferences
|
||||
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.extensions.tryOrNull
|
||||
import org.matrix.android.sdk.api.pushrules.Action
|
||||
import org.matrix.android.sdk.api.session.Session
|
||||
import org.matrix.android.sdk.api.session.events.model.Event
|
||||
|
@ -55,6 +60,9 @@ class VectorFirebaseMessagingService : FirebaseMessagingService() {
|
|||
private lateinit var pusherManager: PushersManager
|
||||
private lateinit var activeSessionHolder: ActiveSessionHolder
|
||||
private lateinit var vectorPreferences: VectorPreferences
|
||||
private lateinit var wifiDetector: WifiDetector
|
||||
|
||||
private val coroutineScope = CoroutineScope(SupervisorJob())
|
||||
|
||||
// UI handler
|
||||
private val mUIHandler by lazy {
|
||||
|
@ -69,6 +77,7 @@ class VectorFirebaseMessagingService : FirebaseMessagingService() {
|
|||
pusherManager = pusherManager()
|
||||
activeSessionHolder = activeSessionHolder()
|
||||
vectorPreferences = vectorPreferences()
|
||||
wifiDetector = wifiDetector()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -78,6 +87,11 @@ class VectorFirebaseMessagingService : FirebaseMessagingService() {
|
|||
* @param message the message
|
||||
*/
|
||||
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
|
||||
if (message.data["event_id"] == PushersManager.TEST_EVENT_ID) {
|
||||
val intent = Intent(NotificationUtils.PUSH_ACTION)
|
||||
|
@ -90,14 +104,10 @@ class VectorFirebaseMessagingService : FirebaseMessagingService() {
|
|||
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 {
|
||||
if (ProcessLifecycleOwner.get().lifecycle.currentState.isAtLeast(Lifecycle.State.STARTED)) {
|
||||
// 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 {
|
||||
onMessageReceivedInternal(message.data)
|
||||
}
|
||||
|
@ -140,7 +150,9 @@ class VectorFirebaseMessagingService : FirebaseMessagingService() {
|
|||
private fun onMessageReceivedInternal(data: Map<String, String>) {
|
||||
try {
|
||||
if (BuildConfig.LOW_PRIVACY_LOG_ENABLE) {
|
||||
Timber.i("## onMessageReceivedInternal() : $data")
|
||||
Timber.d("## onMessageReceivedInternal() : $data")
|
||||
} else {
|
||||
Timber.d("## onMessageReceivedInternal() : $data")
|
||||
}
|
||||
|
||||
// update the badge counter
|
||||
|
@ -156,9 +168,13 @@ class VectorFirebaseMessagingService : FirebaseMessagingService() {
|
|||
val roomId = data["room_id"]
|
||||
|
||||
if (isEventAlreadyKnown(eventId, roomId)) {
|
||||
Timber.i("Ignoring push, event already known")
|
||||
Timber.d("Ignoring push, event already known")
|
||||
} 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()
|
||||
}
|
||||
}
|
||||
|
@ -167,6 +183,36 @@ 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
|
||||
}
|
||||
|
||||
if (wifiDetector.isConnectedToWifi().not()) {
|
||||
Timber.d("No WiFi network, do not get Event")
|
||||
return
|
||||
}
|
||||
|
||||
coroutineScope.launch {
|
||||
Timber.d("Fast lane: start request")
|
||||
val event = tryOrNull { session.getEvent(roomId, eventId) } ?: return@launch
|
||||
|
||||
val resolvedEvent = notifiableEventResolver.resolveInMemoryEvent(session, event)
|
||||
|
||||
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
|
||||
// a previous catchup might have already retrieved the notified event
|
||||
private fun isEventAlreadyKnown(eventId: String?, roomId: String?): Boolean {
|
||||
|
|
|
@ -26,6 +26,7 @@ import im.vector.app.EmojiCompatWrapper
|
|||
import im.vector.app.VectorApplication
|
||||
import im.vector.app.core.dialogs.UnrecognizedCertificateDialog
|
||||
import im.vector.app.core.error.ErrorFormatter
|
||||
import im.vector.app.core.network.WifiDetector
|
||||
import im.vector.app.core.pushers.PushersManager
|
||||
import im.vector.app.core.utils.AssetReader
|
||||
import im.vector.app.core.utils.DimensionConverter
|
||||
|
@ -140,6 +141,8 @@ interface VectorComponent {
|
|||
|
||||
fun vectorPreferences(): VectorPreferences
|
||||
|
||||
fun wifiDetector(): WifiDetector
|
||||
|
||||
fun vectorFileLogger(): VectorFileLogger
|
||||
|
||||
fun uiStateRepository(): UiStateRepository
|
||||
|
|
|
@ -39,7 +39,7 @@ inline fun <T> LiveData<LiveEvent<T>>.observeEventFirstThrottle(owner: Lifecycle
|
|||
val firstThrottler = FirstThrottler(minimumInterval)
|
||||
|
||||
this.observe(owner, EventObserver {
|
||||
if (firstThrottler.canHandle()) {
|
||||
if (firstThrottler.canHandle() is FirstThrottler.CanHandlerResult.Yes) {
|
||||
it.run(observer)
|
||||
}
|
||||
})
|
||||
|
|
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
* Copyright (c) 2021 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 im.vector.app.core.network
|
||||
|
||||
import android.content.Context
|
||||
import android.net.ConnectivityManager
|
||||
import android.net.NetworkCapabilities
|
||||
import android.os.Build
|
||||
import androidx.core.content.getSystemService
|
||||
import org.matrix.android.sdk.api.extensions.orFalse
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
|
||||
class WifiDetector @Inject constructor(
|
||||
context: Context
|
||||
) {
|
||||
private val connectivityManager = context.getSystemService<ConnectivityManager>()!!
|
||||
|
||||
fun isConnectedToWifi(): Boolean {
|
||||
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
connectivityManager.activeNetwork
|
||||
?.let { connectivityManager.getNetworkCapabilities(it) }
|
||||
?.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)
|
||||
.orFalse()
|
||||
} else {
|
||||
@Suppress("DEPRECATION")
|
||||
connectivityManager.activeNetworkInfo?.type == ConnectivityManager.TYPE_WIFI
|
||||
}
|
||||
.also { Timber.d("isConnected to WiFi: $it") }
|
||||
}
|
||||
}
|
|
@ -24,14 +24,27 @@ import android.os.SystemClock
|
|||
class FirstThrottler(private val minimumInterval: Long = 800) {
|
||||
private var lastDate = 0L
|
||||
|
||||
fun canHandle(): Boolean {
|
||||
sealed class CanHandlerResult {
|
||||
object Yes : CanHandlerResult()
|
||||
data class No(val shouldWaitMillis: Long) : CanHandlerResult()
|
||||
|
||||
fun waitMillis(): Long {
|
||||
return when (this) {
|
||||
Yes -> 0
|
||||
is No -> shouldWaitMillis
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun canHandle(): CanHandlerResult {
|
||||
val now = SystemClock.elapsedRealtime()
|
||||
if (now > lastDate + minimumInterval) {
|
||||
val delaySinceLast = now - lastDate
|
||||
if (delaySinceLast > minimumInterval) {
|
||||
lastDate = now
|
||||
return true
|
||||
return CanHandlerResult.Yes
|
||||
}
|
||||
|
||||
// Too soon
|
||||
return false
|
||||
return CanHandlerResult.No(minimumInterval - delaySinceLast)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,9 +26,11 @@ import org.matrix.android.sdk.api.session.content.ContentUrlResolver
|
|||
import org.matrix.android.sdk.api.session.crypto.MXCryptoError
|
||||
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.isEdition
|
||||
import org.matrix.android.sdk.api.session.events.model.toModel
|
||||
import org.matrix.android.sdk.api.session.room.model.Membership
|
||||
import org.matrix.android.sdk.api.session.room.model.RoomMemberContent
|
||||
import org.matrix.android.sdk.api.session.room.sender.SenderInfo
|
||||
import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent
|
||||
import org.matrix.android.sdk.api.session.room.timeline.getEditedEventId
|
||||
import org.matrix.android.sdk.internal.crypto.algorithms.olm.OlmDecryptionResult
|
||||
|
@ -42,9 +44,10 @@ import javax.inject.Inject
|
|||
* 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.
|
||||
*/
|
||||
class NotifiableEventResolver @Inject constructor(private val stringProvider: StringProvider,
|
||||
private val noticeEventFormatter: NoticeEventFormatter,
|
||||
private val displayableEventFormatter: DisplayableEventFormatter) {
|
||||
class NotifiableEventResolver @Inject constructor(
|
||||
private val stringProvider: StringProvider,
|
||||
private val noticeEventFormatter: NoticeEventFormatter,
|
||||
private val displayableEventFormatter: DisplayableEventFormatter) {
|
||||
|
||||
// private val eventDisplay = RiotEventDisplay(context)
|
||||
|
||||
|
@ -84,6 +87,47 @@ class NotifiableEventResolver @Inject constructor(private val stringProvider: St
|
|||
}
|
||||
}
|
||||
|
||||
fun resolveInMemoryEvent(session: Session, event: Event): NotifiableEvent? {
|
||||
if (event.getClearType() != EventType.MESSAGE) return null
|
||||
|
||||
// Ignore message edition
|
||||
if (event.isEdition()) return null
|
||||
|
||||
val actions = session.getActions(event)
|
||||
val notificationAction = actions.toNotificationAction()
|
||||
|
||||
return if (notificationAction.shouldNotify) {
|
||||
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
|
||||
)
|
||||
)
|
||||
|
||||
val notifiableEvent = resolveMessageEvent(timelineEvent, session)
|
||||
|
||||
if (notifiableEvent == null) {
|
||||
Timber.d("## Failed to resolve event")
|
||||
// TODO
|
||||
null
|
||||
} else {
|
||||
notifiableEvent.noisy = !notificationAction.soundName.isNullOrBlank()
|
||||
notifiableEvent
|
||||
}
|
||||
} else {
|
||||
Timber.d("Matched push rule is set to not notify")
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
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...)
|
||||
val room = session.getRoom(event.root.roomId!! /*roomID cannot be null*/)
|
||||
|
|
|
@ -26,6 +26,7 @@ import im.vector.app.ActiveSessionDataSource
|
|||
import im.vector.app.BuildConfig
|
||||
import im.vector.app.R
|
||||
import im.vector.app.core.resources.StringProvider
|
||||
import im.vector.app.core.utils.FirstThrottler
|
||||
import im.vector.app.features.settings.VectorPreferences
|
||||
import me.gujun.android.span.span
|
||||
import org.matrix.android.sdk.api.session.Session
|
||||
|
@ -88,7 +89,9 @@ class NotificationDrawerManager @Inject constructor(private val context: Context
|
|||
// If we support multi session, event list should be per userId
|
||||
// Currently only manage single session
|
||||
if (BuildConfig.LOW_PRIVACY_LOG_ENABLE) {
|
||||
Timber.v("%%%%%%%% onNotifiableEventReceived $notifiableEvent")
|
||||
Timber.d("onNotifiableEventReceived(): $notifiableEvent")
|
||||
} else {
|
||||
Timber.d("onNotifiableEventReceived(): is push: ${notifiableEvent.isPushGatewayEvent}")
|
||||
}
|
||||
synchronized(eventList) {
|
||||
val existing = eventList.firstOrNull { it.eventId == notifiableEvent.eventId }
|
||||
|
@ -194,10 +197,14 @@ class NotificationDrawerManager @Inject constructor(private val context: Context
|
|||
notificationUtils.cancelNotificationMessage(roomId, ROOM_INVITATION_NOTIFICATION_ID)
|
||||
}
|
||||
|
||||
private var firstThrottler = FirstThrottler(200)
|
||||
|
||||
fun refreshNotificationDrawer() {
|
||||
// Implement last throttler
|
||||
Timber.v("refreshNotificationDrawer()")
|
||||
val canHandle = firstThrottler.canHandle()
|
||||
Timber.v("refreshNotificationDrawer(), delay: ${canHandle.waitMillis()} ms")
|
||||
backgroundHandler.removeCallbacksAndMessages(null)
|
||||
|
||||
backgroundHandler.postDelayed(
|
||||
{
|
||||
try {
|
||||
|
@ -206,7 +213,8 @@ class NotificationDrawerManager @Inject constructor(private val context: Context
|
|||
// It can happen if for instance session has been destroyed. It's a bit ugly to try catch like this, but it's safer
|
||||
Timber.w(throwable, "refreshNotificationDrawerBg failure")
|
||||
}
|
||||
}, 200)
|
||||
},
|
||||
canHandle.waitMillis())
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
|
@ -544,7 +552,7 @@ class NotificationDrawerManager @Inject constructor(private val context: Context
|
|||
return bitmapLoader.getRoomBitmap(roomAvatarPath)
|
||||
}
|
||||
|
||||
private fun shouldIgnoreMessageEventInRoom(roomId: String?): Boolean {
|
||||
fun shouldIgnoreMessageEventInRoom(roomId: String?): Boolean {
|
||||
return currentRoomId != null && roomId == currentRoomId
|
||||
}
|
||||
|
||||
|
|
|
@ -101,7 +101,7 @@ class VectorSettingsHelpAboutFragment @Inject constructor(
|
|||
// third party notice
|
||||
findPreference<VectorPreference>(VectorPreferences.SETTINGS_THIRD_PARTY_NOTICES_PREFERENCE_KEY)!!
|
||||
.onPreferenceClickListener = Preference.OnPreferenceClickListener {
|
||||
if (firstThrottler.canHandle()) {
|
||||
if (firstThrottler.canHandle() is FirstThrottler.CanHandlerResult.Yes) {
|
||||
activity?.displayInWebView(VectorSettingsUrls.THIRD_PARTY_LICENSES)
|
||||
}
|
||||
false
|
||||
|
|
Loading…
Reference in New Issue