replacing separated push listener callbacks with a single onEvents callback

- simplifies the handling of notifications, will allow us to reduce redundant synchronisations and suspend the entire notification update (will be needed for supporting images)
This commit is contained in:
Adam Brown 2021-11-03 11:12:48 +00:00
parent 2b58c0e5ab
commit 5190ef4280
5 changed files with 73 additions and 131 deletions

View File

@ -0,0 +1,27 @@
/*
* 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 org.matrix.android.sdk.api.pushrules
import org.matrix.android.sdk.api.pushrules.rest.PushRule
import org.matrix.android.sdk.api.session.events.model.Event
data class PushEvents(
val matchedEvents: List<Pair<Event, PushRule>>,
val roomsJoined: Collection<String>,
val roomsLeft: Collection<String>,
val redactedEventIds: List<String>
)

View File

@ -51,11 +51,7 @@ interface PushRuleService {
// fun fulfilledBingRule(event: Event, rules: List<PushRule>): PushRule?
interface PushRuleListener {
fun onMatchRule(event: Event, actions: List<Action>)
fun onRoomJoined(roomId: String)
fun onRoomLeft(roomId: String)
fun onEventRedacted(redactedEventId: String)
fun batchFinish()
fun onEvents(pushEvents: PushEvents)
}
fun getKeywords(): LiveData<Set<String>>

View File

@ -19,6 +19,7 @@ import androidx.lifecycle.LiveData
import androidx.lifecycle.Transformations
import com.zhuinden.monarchy.Monarchy
import org.matrix.android.sdk.api.pushrules.Action
import org.matrix.android.sdk.api.pushrules.PushEvents
import org.matrix.android.sdk.api.pushrules.PushRuleService
import org.matrix.android.sdk.api.pushrules.RuleKind
import org.matrix.android.sdk.api.pushrules.RuleScope
@ -40,7 +41,6 @@ import org.matrix.android.sdk.internal.session.pushers.UpdatePushRuleActionsTask
import org.matrix.android.sdk.internal.session.pushers.UpdatePushRuleEnableStatusTask
import org.matrix.android.sdk.internal.task.TaskExecutor
import org.matrix.android.sdk.internal.task.configureWith
import timber.log.Timber
import javax.inject.Inject
@SessionScope
@ -142,79 +142,6 @@ internal class DefaultPushRuleService @Inject constructor(
return pushRuleFinder.fulfilledBingRule(event, rules)?.getActions().orEmpty()
}
// fun processEvents(events: List<Event>) {
// var hasDoneSomething = false
// events.forEach { event ->
// fulfilledBingRule(event)?.let {
// hasDoneSomething = true
// dispatchBing(event, it)
// }
// }
// if (hasDoneSomething)
// dispatchFinish()
// }
fun dispatchBing(event: Event, rule: PushRule) {
synchronized(listeners) {
val actionsList = rule.getActions()
listeners.forEach {
try {
it.onMatchRule(event, actionsList)
} catch (e: Throwable) {
Timber.e(e, "Error while dispatching bing")
}
}
}
}
fun dispatchRoomJoined(roomId: String) {
synchronized(listeners) {
listeners.forEach {
try {
it.onRoomJoined(roomId)
} catch (e: Throwable) {
Timber.e(e, "Error while dispatching room joined")
}
}
}
}
fun dispatchRoomLeft(roomId: String) {
synchronized(listeners) {
listeners.forEach {
try {
it.onRoomLeft(roomId)
} catch (e: Throwable) {
Timber.e(e, "Error while dispatching room left")
}
}
}
}
fun dispatchRedactedEventId(redactedEventId: String) {
synchronized(listeners) {
listeners.forEach {
try {
it.onEventRedacted(redactedEventId)
} catch (e: Throwable) {
Timber.e(e, "Error while dispatching redacted event")
}
}
}
}
fun dispatchFinish() {
synchronized(listeners) {
listeners.forEach {
try {
it.batchFinish()
} catch (e: Throwable) {
Timber.e(e, "Error while dispatching finish")
}
}
}
}
override fun getKeywords(): LiveData<Set<String>> {
// Keywords are all content rules that don't start with '.'
val liveData = monarchy.findAllMappedWithChanges(
@ -229,4 +156,12 @@ internal class DefaultPushRuleService @Inject constructor(
results.firstOrNull().orEmpty().toSet()
}
}
fun dispatchEvents(pushEvents: PushEvents) {
synchronized(listeners) {
listeners.forEach {
it.onEvents(pushEvents)
}
}
}
}

View File

@ -16,6 +16,7 @@
package org.matrix.android.sdk.internal.session.notification
import org.matrix.android.sdk.api.pushrules.PushEvents
import org.matrix.android.sdk.api.pushrules.rest.PushRule
import org.matrix.android.sdk.api.session.events.model.EventType
import org.matrix.android.sdk.api.session.events.model.isInvitation
@ -39,14 +40,6 @@ internal class DefaultProcessEventForPushTask @Inject constructor(
) : ProcessEventForPushTask {
override suspend fun execute(params: ProcessEventForPushTask.Params) {
// Handle left rooms
params.syncResponse.leave.keys.forEach {
defaultPushRuleService.dispatchRoomLeft(it)
}
// Handle joined rooms
params.syncResponse.join.keys.forEach {
defaultPushRuleService.dispatchRoomJoined(it)
}
val newJoinEvents = params.syncResponse.join
.mapNotNull { (key, value) ->
value.timeline?.events?.mapNotNull {
@ -74,10 +67,10 @@ 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 ->
val matchedEvents = allEvents.mapNotNull { event ->
pushRuleFinder.fulfilledBingRule(event, params.rules)?.let {
Timber.v("[PushRules] Rule $it match for event ${event.eventId}")
defaultPushRuleService.dispatchBing(event, it)
event to it
}
}
@ -91,10 +84,13 @@ internal class DefaultProcessEventForPushTask @Inject constructor(
Timber.v("[PushRules] Found ${allRedactedEvents.size} redacted events")
allRedactedEvents.forEach { redactedEventId ->
defaultPushRuleService.dispatchRedactedEventId(redactedEventId)
}
defaultPushRuleService.dispatchFinish()
defaultPushRuleService.dispatchEvents(
PushEvents(
matchedEvents = matchedEvents,
roomsJoined = params.syncResponse.join.keys,
roomsLeft = params.syncResponse.leave.keys,
redactedEventIds = allRedactedEvents
)
)
}
}

View File

@ -16,10 +16,10 @@
package im.vector.app.features.notifications
import org.matrix.android.sdk.api.pushrules.Action
import org.matrix.android.sdk.api.pushrules.PushEvents
import org.matrix.android.sdk.api.pushrules.PushRuleService
import org.matrix.android.sdk.api.pushrules.getActions
import org.matrix.android.sdk.api.session.Session
import org.matrix.android.sdk.api.session.events.model.Event
import timber.log.Timber
import javax.inject.Inject
import javax.inject.Singleton
@ -32,42 +32,30 @@ class PushRuleTriggerListener @Inject constructor(
private var session: Session? = null
override fun onMatchRule(event: Event, actions: List<Action>) {
Timber.v("Push rule match for event ${event.eventId}")
val safeSession = session ?: return Unit.also {
Timber.e("Called without active session")
}
val notificationAction = actions.toNotificationAction()
if (notificationAction.shouldNotify) {
val notifiableEvent = resolver.resolveEvent(event, safeSession, isNoisy = !notificationAction.soundName.isNullOrBlank())
if (notifiableEvent == null) {
Timber.v("## Failed to resolve event")
// TODO
} else {
Timber.v("New event to notify")
notificationDrawerManager.onNotifiableEventReceived(notifiableEvent)
override fun onEvents(pushEvents: PushEvents) {
session?.let { session ->
pushEvents.matchedEvents.mapNotNull { (event, pushRule) ->
Timber.v("Push rule match for event ${event.eventId}")
val action = pushRule.getActions().toNotificationAction()
if (action.shouldNotify) {
resolver.resolveEvent(event, session, isNoisy = !action.soundName.isNullOrBlank())
} else {
Timber.v("Matched push rule is set to not notify")
null
}
}.forEach { notificationDrawerManager.onNotifiableEventReceived(it) }
pushEvents.roomsLeft.forEach { roomId ->
notificationDrawerManager.clearMessageEventOfRoom(roomId)
notificationDrawerManager.clearMemberShipNotificationForRoom(roomId)
}
} else {
Timber.v("Matched push rule is set to not notify")
}
}
override fun onRoomLeft(roomId: String) {
notificationDrawerManager.clearMessageEventOfRoom(roomId)
notificationDrawerManager.clearMemberShipNotificationForRoom(roomId)
}
override fun onRoomJoined(roomId: String) {
notificationDrawerManager.clearMemberShipNotificationForRoom(roomId)
}
override fun onEventRedacted(redactedEventId: String) {
notificationDrawerManager.onEventRedacted(redactedEventId)
}
override fun batchFinish() {
notificationDrawerManager.refreshNotificationDrawer()
pushEvents.roomsJoined.forEach { roomId ->
notificationDrawerManager.clearMemberShipNotificationForRoom(roomId)
}
pushEvents.redactedEventIds.forEach {
notificationDrawerManager.onEventRedacted(it)
}
notificationDrawerManager.refreshNotificationDrawer()
} ?: Timber.e("Called without active session")
}
fun startWithSession(session: Session) {