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)
|
- 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
|
||||||
|
@ -39,6 +39,8 @@ interface PushRuleService {
|
|||||||
|
|
||||||
fun removePushRuleListener(listener: PushRuleListener)
|
fun removePushRuleListener(listener: PushRuleListener)
|
||||||
|
|
||||||
|
fun getActions(event: Event): List<Action>
|
||||||
|
|
||||||
// fun fulfilledBingRule(event: Event, rules: List<PushRule>): PushRule?
|
// fun fulfilledBingRule(event: Event, rules: List<PushRule>): PushRule?
|
||||||
|
|
||||||
interface PushRuleListener {
|
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.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,
|
||||||
|
@ -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 {
|
fun Event.isReply(): Boolean {
|
||||||
return getRelationContent()?.inReplyTo?.eventId != null
|
return getRelationContent()?.inReplyTo?.eventId != null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun Event.isEdition(): Boolean {
|
||||||
|
return getRelationContent()?.takeIf { it.type == RelationType.REPLACE }?.eventId != null
|
||||||
|
}
|
||||||
|
@ -38,9 +38,13 @@ 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
|
||||||
|
false
|
||||||
|
}
|
||||||
|
eventToCheck.root?.sender == userId -> true
|
||||||
|
else -> {
|
||||||
val readReceipt = ReadReceiptEntity.where(realm, roomId, userId).findFirst()
|
val readReceipt = ReadReceiptEntity.where(realm, roomId, userId).findFirst()
|
||||||
?: return@use
|
?: return@use
|
||||||
val readReceiptIndex = liveChunk.timelineEvents.find(readReceipt.eventId)?.displayIndex
|
val readReceiptIndex = liveChunk.timelineEvents.find(readReceipt.eventId)?.displayIndex
|
||||||
@ -50,6 +54,7 @@ internal fun isEventRead(realmConfiguration: RealmConfiguration,
|
|||||||
eventToCheckIndex <= readReceiptIndex
|
eventToCheckIndex <= readReceiptIndex
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return isEventRead
|
return isEventRead
|
||||||
}
|
}
|
||||||
|
@ -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(),
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
@ -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}")
|
||||||
}
|
}
|
||||||
|
@ -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,
|
||||||
|
@ -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
|
package org.matrix.android.sdk.internal.session.notification
|
||||||
|
|
||||||
import com.zhuinden.monarchy.Monarchy
|
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.PushRuleService
|
||||||
import org.matrix.android.sdk.api.pushrules.RuleKind
|
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.RuleSetKey
|
||||||
import org.matrix.android.sdk.api.pushrules.getActions
|
import org.matrix.android.sdk.api.pushrules.getActions
|
||||||
import org.matrix.android.sdk.api.pushrules.rest.PushRule
|
import org.matrix.android.sdk.api.pushrules.rest.PushRule
|
||||||
@ -45,6 +47,7 @@ internal class DefaultPushRuleService @Inject constructor(
|
|||||||
private val addPushRuleTask: AddPushRuleTask,
|
private val addPushRuleTask: AddPushRuleTask,
|
||||||
private val updatePushRuleActionsTask: UpdatePushRuleActionsTask,
|
private val updatePushRuleActionsTask: UpdatePushRuleActionsTask,
|
||||||
private val removePushRuleTask: RemovePushRuleTask,
|
private val removePushRuleTask: RemovePushRuleTask,
|
||||||
|
private val pushRuleFinder: PushRuleFinder,
|
||||||
private val taskExecutor: TaskExecutor,
|
private val taskExecutor: TaskExecutor,
|
||||||
@SessionDatabase private val monarchy: Monarchy
|
@SessionDatabase private val monarchy: Monarchy
|
||||||
) : PushRuleService {
|
) : 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>) {
|
// fun processEvents(events: List<Event>) {
|
||||||
// var hasDoneSomething = false
|
// var hasDoneSomething = false
|
||||||
// events.forEach { event ->
|
// events.forEach { event ->
|
||||||
|
@ -16,9 +16,7 @@
|
|||||||
|
|
||||||
package org.matrix.android.sdk.internal.session.notification
|
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.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.api.session.events.model.EventType
|
||||||
import org.matrix.android.sdk.internal.di.UserId
|
import org.matrix.android.sdk.internal.di.UserId
|
||||||
import org.matrix.android.sdk.internal.session.sync.model.RoomsSyncResponse
|
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(
|
internal class DefaultProcessEventForPushTask @Inject constructor(
|
||||||
private val defaultPushRuleService: DefaultPushRuleService,
|
private val defaultPushRuleService: DefaultPushRuleService,
|
||||||
private val conditionResolver: ConditionResolver,
|
private val pushRuleFinder: PushRuleFinder,
|
||||||
@UserId private val userId: String
|
@UserId private val userId: String
|
||||||
) : ProcessEventForPushTask {
|
) : ProcessEventForPushTask {
|
||||||
|
|
||||||
@ -72,7 +70,7 @@ internal class DefaultProcessEventForPushTask @Inject constructor(
|
|||||||
Timber.v("[PushRules] Found ${allEvents.size} out of ${(newJoinEvents + inviteEvents).size}" +
|
Timber.v("[PushRules] Found ${allEvents.size} out of ${(newJoinEvents + inviteEvents).size}" +
|
||||||
" to check for push rules with ${params.rules.size} rules")
|
" to check for push rules with ${params.rules.size} rules")
|
||||||
allEvents.forEach { event ->
|
allEvents.forEach { event ->
|
||||||
fulfilledBingRule(event, params.rules)?.let {
|
pushRuleFinder.fulfilledBingRule(event, params.rules)?.let {
|
||||||
Timber.v("[PushRules] Rule $it match for event ${event.eventId}")
|
Timber.v("[PushRules] Rule $it match for event ${event.eventId}")
|
||||||
defaultPushRuleService.dispatchBing(event, it)
|
defaultPushRuleService.dispatchBing(event, it)
|
||||||
}
|
}
|
||||||
@ -94,13 +92,4 @@ internal class DefaultProcessEventForPushTask @Inject constructor(
|
|||||||
|
|
||||||
defaultPushRuleService.dispatchFinish()
|
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.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
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -408,6 +408,7 @@ internal class RoomSyncHandler @Inject constructor(private val readReceiptHandle
|
|||||||
|
|
||||||
private fun decryptIfNeeded(event: Event, roomId: String) {
|
private fun decryptIfNeeded(event: Event, roomId: String) {
|
||||||
try {
|
try {
|
||||||
|
// Event from sync does not have roomId, so add it to the event first
|
||||||
val result = cryptoService.decryptEvent(event.copy(roomId = roomId), "")
|
val result = cryptoService.decryptEvent(event.copy(roomId = roomId), "")
|
||||||
event.mxDecryptionResult = OlmDecryptionResult(
|
event.mxDecryptionResult = OlmDecryptionResult(
|
||||||
payload = result.clearEvent,
|
payload = result.clearEvent,
|
||||||
|
@ -31,6 +31,7 @@ import im.vector.app.BuildConfig
|
|||||||
import im.vector.app.R
|
import im.vector.app.R
|
||||||
import im.vector.app.core.di.ActiveSessionHolder
|
import im.vector.app.core.di.ActiveSessionHolder
|
||||||
import im.vector.app.core.extensions.vectorComponent
|
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.core.pushers.PushersManager
|
||||||
import im.vector.app.features.badge.BadgeProxy
|
import im.vector.app.features.badge.BadgeProxy
|
||||||
import im.vector.app.features.notifications.NotifiableEventResolver
|
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.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.extensions.tryOrNull
|
||||||
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
|
||||||
@ -55,6 +60,9 @@ class VectorFirebaseMessagingService : FirebaseMessagingService() {
|
|||||||
private lateinit var pusherManager: PushersManager
|
private lateinit var pusherManager: PushersManager
|
||||||
private lateinit var activeSessionHolder: ActiveSessionHolder
|
private lateinit var activeSessionHolder: ActiveSessionHolder
|
||||||
private lateinit var vectorPreferences: VectorPreferences
|
private lateinit var vectorPreferences: VectorPreferences
|
||||||
|
private lateinit var wifiDetector: WifiDetector
|
||||||
|
|
||||||
|
private val coroutineScope = CoroutineScope(SupervisorJob())
|
||||||
|
|
||||||
// UI handler
|
// UI handler
|
||||||
private val mUIHandler by lazy {
|
private val mUIHandler by lazy {
|
||||||
@ -69,6 +77,7 @@ class VectorFirebaseMessagingService : FirebaseMessagingService() {
|
|||||||
pusherManager = pusherManager()
|
pusherManager = pusherManager()
|
||||||
activeSessionHolder = activeSessionHolder()
|
activeSessionHolder = activeSessionHolder()
|
||||||
vectorPreferences = vectorPreferences()
|
vectorPreferences = vectorPreferences()
|
||||||
|
wifiDetector = wifiDetector()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -78,6 +87,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 +104,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 +150,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 +168,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 +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
|
// 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 {
|
||||||
|
@ -26,6 +26,7 @@ import im.vector.app.EmojiCompatWrapper
|
|||||||
import im.vector.app.VectorApplication
|
import im.vector.app.VectorApplication
|
||||||
import im.vector.app.core.dialogs.UnrecognizedCertificateDialog
|
import im.vector.app.core.dialogs.UnrecognizedCertificateDialog
|
||||||
import im.vector.app.core.error.ErrorFormatter
|
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.pushers.PushersManager
|
||||||
import im.vector.app.core.utils.AssetReader
|
import im.vector.app.core.utils.AssetReader
|
||||||
import im.vector.app.core.utils.DimensionConverter
|
import im.vector.app.core.utils.DimensionConverter
|
||||||
@ -140,6 +141,8 @@ interface VectorComponent {
|
|||||||
|
|
||||||
fun vectorPreferences(): VectorPreferences
|
fun vectorPreferences(): VectorPreferences
|
||||||
|
|
||||||
|
fun wifiDetector(): WifiDetector
|
||||||
|
|
||||||
fun vectorFileLogger(): VectorFileLogger
|
fun vectorFileLogger(): VectorFileLogger
|
||||||
|
|
||||||
fun uiStateRepository(): UiStateRepository
|
fun uiStateRepository(): UiStateRepository
|
||||||
|
@ -39,7 +39,7 @@ inline fun <T> LiveData<LiveEvent<T>>.observeEventFirstThrottle(owner: Lifecycle
|
|||||||
val firstThrottler = FirstThrottler(minimumInterval)
|
val firstThrottler = FirstThrottler(minimumInterval)
|
||||||
|
|
||||||
this.observe(owner, EventObserver {
|
this.observe(owner, EventObserver {
|
||||||
if (firstThrottler.canHandle()) {
|
if (firstThrottler.canHandle() is FirstThrottler.CanHandlerResult.Yes) {
|
||||||
it.run(observer)
|
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) {
|
class FirstThrottler(private val minimumInterval: Long = 800) {
|
||||||
private var lastDate = 0L
|
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()
|
val now = SystemClock.elapsedRealtime()
|
||||||
if (now > lastDate + minimumInterval) {
|
val delaySinceLast = now - lastDate
|
||||||
|
if (delaySinceLast > minimumInterval) {
|
||||||
lastDate = now
|
lastDate = now
|
||||||
return true
|
return CanHandlerResult.Yes
|
||||||
}
|
}
|
||||||
|
|
||||||
// Too soon
|
// 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.crypto.MXCryptoError
|
||||||
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.api.session.events.model.EventType
|
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.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,7 +44,8 @@ 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 stringProvider: StringProvider,
|
||||||
private val noticeEventFormatter: NoticeEventFormatter,
|
private val noticeEventFormatter: NoticeEventFormatter,
|
||||||
private val displayableEventFormatter: DisplayableEventFormatter) {
|
private val displayableEventFormatter: DisplayableEventFormatter) {
|
||||||
|
|
||||||
@ -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? {
|
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*/)
|
||||||
|
@ -26,6 +26,7 @@ import im.vector.app.ActiveSessionDataSource
|
|||||||
import im.vector.app.BuildConfig
|
import im.vector.app.BuildConfig
|
||||||
import im.vector.app.R
|
import im.vector.app.R
|
||||||
import im.vector.app.core.resources.StringProvider
|
import im.vector.app.core.resources.StringProvider
|
||||||
|
import im.vector.app.core.utils.FirstThrottler
|
||||||
import im.vector.app.features.settings.VectorPreferences
|
import im.vector.app.features.settings.VectorPreferences
|
||||||
import me.gujun.android.span.span
|
import me.gujun.android.span.span
|
||||||
import org.matrix.android.sdk.api.session.Session
|
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
|
// 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 }
|
||||||
@ -194,10 +197,14 @@ class NotificationDrawerManager @Inject constructor(private val context: Context
|
|||||||
notificationUtils.cancelNotificationMessage(roomId, ROOM_INVITATION_NOTIFICATION_ID)
|
notificationUtils.cancelNotificationMessage(roomId, ROOM_INVITATION_NOTIFICATION_ID)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private var firstThrottler = FirstThrottler(200)
|
||||||
|
|
||||||
fun refreshNotificationDrawer() {
|
fun refreshNotificationDrawer() {
|
||||||
// Implement last throttler
|
// Implement last throttler
|
||||||
Timber.v("refreshNotificationDrawer()")
|
val canHandle = firstThrottler.canHandle()
|
||||||
|
Timber.v("refreshNotificationDrawer(), delay: ${canHandle.waitMillis()} ms")
|
||||||
backgroundHandler.removeCallbacksAndMessages(null)
|
backgroundHandler.removeCallbacksAndMessages(null)
|
||||||
|
|
||||||
backgroundHandler.postDelayed(
|
backgroundHandler.postDelayed(
|
||||||
{
|
{
|
||||||
try {
|
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
|
// 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")
|
Timber.w(throwable, "refreshNotificationDrawerBg failure")
|
||||||
}
|
}
|
||||||
}, 200)
|
},
|
||||||
|
canHandle.waitMillis())
|
||||||
}
|
}
|
||||||
|
|
||||||
@WorkerThread
|
@WorkerThread
|
||||||
@ -544,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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -101,7 +101,7 @@ class VectorSettingsHelpAboutFragment @Inject constructor(
|
|||||||
// third party notice
|
// third party notice
|
||||||
findPreference<VectorPreference>(VectorPreferences.SETTINGS_THIRD_PARTY_NOTICES_PREFERENCE_KEY)!!
|
findPreference<VectorPreference>(VectorPreferences.SETTINGS_THIRD_PARTY_NOTICES_PREFERENCE_KEY)!!
|
||||||
.onPreferenceClickListener = Preference.OnPreferenceClickListener {
|
.onPreferenceClickListener = Preference.OnPreferenceClickListener {
|
||||||
if (firstThrottler.canHandle()) {
|
if (firstThrottler.canHandle() is FirstThrottler.CanHandlerResult.Yes) {
|
||||||
activity?.displayInWebView(VectorSettingsUrls.THIRD_PARTY_LICENSES)
|
activity?.displayInWebView(VectorSettingsUrls.THIRD_PARTY_LICENSES)
|
||||||
}
|
}
|
||||||
false
|
false
|
||||||
|
Loading…
Reference in New Issue
Block a user