forked from GitHub-Mirror/riotX-android
Get real push rules from server and evaluate them
This commit is contained in:
@ -43,51 +43,53 @@ class Action(val type: Type) {
|
||||
var stringValue: String? = null
|
||||
var boolValue: Boolean? = null
|
||||
|
||||
}
|
||||
|
||||
fun PushRule.domainActions(): List<Action>? {
|
||||
val actions = ArrayList<Action>()
|
||||
this.actions.forEach { actionStrOrObj ->
|
||||
if (actionStrOrObj is String) {
|
||||
val action = when (actionStrOrObj) {
|
||||
Action.Type.NOTIFY.value -> Action(Action.Type.NOTIFY)
|
||||
Action.Type.DONT_NOTIFY.value -> Action(Action.Type.DONT_NOTIFY)
|
||||
else -> {
|
||||
Timber.w("Unsupported action type ${actionStrOrObj}")
|
||||
null
|
||||
}
|
||||
}?.let {
|
||||
actions.add(it)
|
||||
}
|
||||
} else if (actionStrOrObj is Map<*, *>) {
|
||||
val tweakAction = actionStrOrObj["set_tweak"] as? String
|
||||
when (tweakAction) {
|
||||
"sound" -> {
|
||||
(actionStrOrObj["value"] as? String)?.let { stringValue ->
|
||||
Action(Action.Type.SET_TWEAK).also {
|
||||
it.tweak_action = "sound"
|
||||
it.stringValue = stringValue
|
||||
actions.add(it)
|
||||
companion object {
|
||||
fun mapFrom(pushRule: PushRule): List<Action>? {
|
||||
val actions = ArrayList<Action>()
|
||||
pushRule.actions.forEach { actionStrOrObj ->
|
||||
if (actionStrOrObj is String) {
|
||||
when (actionStrOrObj) {
|
||||
Action.Type.NOTIFY.value -> Action(Action.Type.NOTIFY)
|
||||
Action.Type.DONT_NOTIFY.value -> Action(Action.Type.DONT_NOTIFY)
|
||||
else -> {
|
||||
Timber.w("Unsupported action type ${actionStrOrObj}")
|
||||
null
|
||||
}
|
||||
}?.let {
|
||||
actions.add(it)
|
||||
}
|
||||
} else if (actionStrOrObj is Map<*, *>) {
|
||||
val tweakAction = actionStrOrObj["set_tweak"] as? String
|
||||
when (tweakAction) {
|
||||
"sound" -> {
|
||||
(actionStrOrObj["value"] as? String)?.let { stringValue ->
|
||||
Action(Action.Type.SET_TWEAK).also {
|
||||
it.tweak_action = "sound"
|
||||
it.stringValue = stringValue
|
||||
actions.add(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
"highlight" -> {
|
||||
(actionStrOrObj["value"] as? Boolean)?.let { boolValue ->
|
||||
Action(Action.Type.SET_TWEAK).also {
|
||||
it.tweak_action = "highlight"
|
||||
it.boolValue = boolValue
|
||||
actions.add(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
Timber.w("Unsupported action type ${actionStrOrObj}")
|
||||
}
|
||||
}
|
||||
}
|
||||
"highlight" -> {
|
||||
(actionStrOrObj["value"] as? Boolean)?.let { boolValue ->
|
||||
Action(Action.Type.SET_TWEAK).also {
|
||||
it.tweak_action = "highlight"
|
||||
it.boolValue = boolValue
|
||||
actions.add(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
} else {
|
||||
Timber.w("Unsupported action type ${actionStrOrObj}")
|
||||
return null
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Timber.w("Unsupported action type ${actionStrOrObj}")
|
||||
return null
|
||||
return if (actions.isEmpty()) null else actions
|
||||
}
|
||||
}
|
||||
return if (actions.isEmpty()) null else actions
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -15,25 +15,23 @@
|
||||
*/
|
||||
package im.vector.matrix.android.api.pushrules
|
||||
|
||||
import im.vector.matrix.android.api.session.events.model.Event
|
||||
|
||||
abstract class Condition(val kind: Kind) {
|
||||
|
||||
enum class Kind(val value: String) {
|
||||
EVENT_MATCH("event_match"),
|
||||
CONTAINS_DISPLAY_NAME("contains_display_name"),
|
||||
ROOM_MEMBER_COUNT("room_member_count"),
|
||||
SENDER_NOTIFICATION_PERMISSION("sender_notification_permission"),
|
||||
event_match("event_match"),
|
||||
contains_display_name("contains_display_name"),
|
||||
room_member_count("room_member_count"),
|
||||
sender_notification_permission("sender_notification_permission"),
|
||||
UNRECOGNIZE("");
|
||||
|
||||
companion object {
|
||||
|
||||
fun fromString(value: String): Kind {
|
||||
return when (value) {
|
||||
"event_match" -> EVENT_MATCH
|
||||
"contains_display_name" -> CONTAINS_DISPLAY_NAME
|
||||
"room_member_count" -> ROOM_MEMBER_COUNT
|
||||
"sender_notification_permission" -> SENDER_NOTIFICATION_PERMISSION
|
||||
"event_match" -> event_match
|
||||
"contains_display_name" -> contains_display_name
|
||||
"room_member_count" -> room_member_count
|
||||
"sender_notification_permission" -> sender_notification_permission
|
||||
else -> UNRECOGNIZE
|
||||
}
|
||||
}
|
||||
@ -42,10 +40,9 @@ abstract class Condition(val kind: Kind) {
|
||||
|
||||
}
|
||||
|
||||
abstract fun isSatisfied(event: Event): Boolean
|
||||
abstract fun isSatisfied(conditionResolver: ConditionResolver): Boolean
|
||||
|
||||
companion object {
|
||||
//TODO factory methods?
|
||||
open fun technicalDescription(): String {
|
||||
return "Kind: $kind"
|
||||
}
|
||||
|
||||
}
|
@ -15,11 +15,14 @@
|
||||
*/
|
||||
package im.vector.matrix.android.api.pushrules
|
||||
|
||||
import im.vector.matrix.android.api.pushrules.rest.PushRule
|
||||
/**
|
||||
* Acts like a visitor on Conditions.
|
||||
* This class as all required context needed to evaluate rules
|
||||
*/
|
||||
interface ConditionResolver {
|
||||
|
||||
interface PushRulesProvider {
|
||||
|
||||
fun getOrderedPushrules(): List<PushRule>
|
||||
|
||||
fun onRulesUpdate(newRules: List<PushRule>)
|
||||
fun resolveEventMatchCondition(eventMatchCondition: EventMatchCondition): Boolean
|
||||
fun resolveRoomMemberCountCondition(roomMemberCountCondition: RoomMemberCountCondition): Boolean
|
||||
fun resolveSenderNotificationPermissionCondition(senderNotificationPermissionCondition: SenderNotificationPermissionCondition): Boolean
|
||||
fun resolveContainsDisplayNameCondition(containsDisplayNameCondition: ContainsDisplayNameCondition) : Boolean
|
||||
}
|
@ -0,0 +1,80 @@
|
||||
/*
|
||||
* Copyright 2019 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.matrix.android.api.pushrules
|
||||
|
||||
import android.text.TextUtils
|
||||
import im.vector.matrix.android.api.session.events.model.Event
|
||||
import im.vector.matrix.android.api.session.events.model.EventType
|
||||
import im.vector.matrix.android.api.session.events.model.toModel
|
||||
import im.vector.matrix.android.api.session.room.model.message.MessageContent
|
||||
import im.vector.matrix.android.api.session.room.timeline.TimelineEvent
|
||||
import timber.log.Timber
|
||||
import java.util.regex.Pattern
|
||||
|
||||
class ContainsDisplayNameCondition : Condition(Kind.contains_display_name) {
|
||||
|
||||
override fun isSatisfied(conditionResolver: ConditionResolver): Boolean {
|
||||
return conditionResolver.resolveContainsDisplayNameCondition(this)
|
||||
}
|
||||
|
||||
override fun technicalDescription(): String {
|
||||
return "User is mentioned"
|
||||
}
|
||||
|
||||
fun isSatisfied(event: Event, displayName: String): Boolean {
|
||||
//TODO the spec says:
|
||||
// Matches any message whose content is unencrypted and contains the user's current display name
|
||||
var message = when (event.type) {
|
||||
EventType.MESSAGE -> {
|
||||
event.content.toModel<MessageContent>()
|
||||
}
|
||||
// EventType.ENCRYPTED -> {
|
||||
// event.root.getClearContent()?.toModel<MessageContent>()
|
||||
// }
|
||||
else -> null
|
||||
} ?: return false
|
||||
|
||||
return caseInsensitiveFind(displayName, message.body)
|
||||
}
|
||||
|
||||
|
||||
companion object {
|
||||
/**
|
||||
* Returns whether a string contains an occurrence of another, as a standalone word, regardless of case.
|
||||
*
|
||||
* @param subString the string to search for
|
||||
* @param longString the string to search in
|
||||
* @return whether a match was found
|
||||
*/
|
||||
fun caseInsensitiveFind(subString: String, longString: String): Boolean {
|
||||
// add sanity checks
|
||||
if (TextUtils.isEmpty(subString) || TextUtils.isEmpty(longString)) {
|
||||
return false
|
||||
}
|
||||
|
||||
var res = false
|
||||
|
||||
try {
|
||||
val pattern = Pattern.compile("(\\W|^)" + Pattern.quote(subString) + "(\\W|$)", Pattern.CASE_INSENSITIVE)
|
||||
res = pattern.matcher(longString).find()
|
||||
} catch (e: Exception) {
|
||||
Timber.e(e, "## caseInsensitiveFind() : failed")
|
||||
}
|
||||
|
||||
return res
|
||||
}
|
||||
}
|
||||
}
|
@ -19,9 +19,18 @@ import im.vector.matrix.android.api.session.events.model.Event
|
||||
import im.vector.matrix.android.internal.di.MoshiProvider
|
||||
import timber.log.Timber
|
||||
|
||||
class EventMatchCondition(val key: String, val pattern: String) : Condition(Kind.EVENT_MATCH) {
|
||||
class EventMatchCondition(val key: String, val pattern: String) : Condition(Kind.event_match) {
|
||||
|
||||
override fun isSatisfied(event: Event): Boolean {
|
||||
override fun isSatisfied(conditionResolver: ConditionResolver) : Boolean {
|
||||
return conditionResolver.resolveEventMatchCondition(this)
|
||||
}
|
||||
|
||||
override fun technicalDescription(): String {
|
||||
return "'$key' Matches '$pattern'"
|
||||
}
|
||||
|
||||
|
||||
fun isSatisfied(event: Event): Boolean {
|
||||
//TODO encrypted events?
|
||||
val rawJson = MoshiProvider.providesMoshi().adapter(Event::class.java).toJsonValue(event) as? Map<*, *>
|
||||
?: return false
|
||||
|
@ -15,12 +15,19 @@
|
||||
*/
|
||||
package im.vector.matrix.android.api.pushrules
|
||||
|
||||
import im.vector.matrix.android.api.pushrules.rest.PushRule
|
||||
import im.vector.matrix.android.api.session.events.model.Event
|
||||
|
||||
interface PushRuleService {
|
||||
|
||||
|
||||
/**
|
||||
* Fetch the push rules from the server
|
||||
*/
|
||||
fun fetchPushRules(scope: String = "global")
|
||||
|
||||
//TODO get push rule set
|
||||
fun getPushrules(scope: String = "global"): List<PushRule>
|
||||
|
||||
//TODO update rule
|
||||
|
||||
@ -28,6 +35,8 @@ interface PushRuleService {
|
||||
|
||||
fun removePushRuleListener(listener: PushRuleListener)
|
||||
|
||||
// fun fulfilledBingRule(event: Event, rules: List<PushRule>): PushRule?
|
||||
|
||||
interface PushRuleListener {
|
||||
fun onMatchRule(event: Event, actions: List<Action>)
|
||||
fun batchFinish()
|
||||
|
@ -0,0 +1,71 @@
|
||||
/*
|
||||
* Copyright 2019 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.matrix.android.api.pushrules
|
||||
|
||||
import im.vector.matrix.android.api.session.events.model.Event
|
||||
import im.vector.matrix.android.api.session.room.RoomService
|
||||
import timber.log.Timber
|
||||
import java.util.regex.Pattern
|
||||
|
||||
private val regex = Pattern.compile("^(==|<=|>=|<|>)?(\\d*)$")
|
||||
|
||||
class RoomMemberCountCondition(val `is`: String) : Condition(Kind.room_member_count) {
|
||||
|
||||
override fun isSatisfied(conditionResolver: ConditionResolver): Boolean {
|
||||
return conditionResolver.resolveRoomMemberCountCondition(this)
|
||||
}
|
||||
|
||||
override fun technicalDescription(): String {
|
||||
return "Room member count is $`is`"
|
||||
}
|
||||
|
||||
fun isSatisfied(event: Event, session: RoomService?): Boolean {
|
||||
// sanity check^
|
||||
val roomId = event.roomId ?: return false
|
||||
val room = session?.getRoom(roomId) ?: return false
|
||||
|
||||
// Parse the is field into prefix and number the first time
|
||||
val (prefix, count) = parseIsField() ?: return false
|
||||
|
||||
val numMembers = room.getNumberOfJoinedMembers()
|
||||
|
||||
return when (prefix) {
|
||||
"<" -> numMembers < count
|
||||
">" -> numMembers > count
|
||||
"<=" -> numMembers <= count
|
||||
">=" -> numMembers >= count
|
||||
else -> numMembers == count
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the is field to extract meaningful information.
|
||||
*/
|
||||
private fun parseIsField(): Pair<String?, Int>? {
|
||||
try {
|
||||
val match = regex.matcher(`is`)
|
||||
if (match.find()) {
|
||||
val prefix = match.group(1)
|
||||
val count = match.group(2).toInt()
|
||||
return prefix to count
|
||||
}
|
||||
} catch (t: Throwable) {
|
||||
Timber.d(t)
|
||||
}
|
||||
return null
|
||||
|
||||
}
|
||||
}
|
@ -0,0 +1,36 @@
|
||||
package im.vector.matrix.android.api.pushrules
|
||||
|
||||
/*
|
||||
* Copyright 2019 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.
|
||||
*/
|
||||
import im.vector.matrix.android.api.session.events.model.Event
|
||||
import im.vector.matrix.android.api.session.room.model.PowerLevels
|
||||
|
||||
|
||||
class SenderNotificationPermissionCondition(val key: String) : Condition(Kind.sender_notification_permission) {
|
||||
|
||||
override fun isSatisfied(conditionResolver: ConditionResolver): Boolean {
|
||||
return conditionResolver.resolveSenderNotificationPermissionCondition(this)
|
||||
}
|
||||
|
||||
override fun technicalDescription(): String {
|
||||
return "User power level <$key>"
|
||||
}
|
||||
|
||||
|
||||
fun isSatisfied(event: Event, powerLevels: PowerLevels): Boolean {
|
||||
return event.sender != null && powerLevels.getUserPowerLevel(event.sender) >= powerLevels.notificationLevel(key)
|
||||
}
|
||||
}
|
@ -17,8 +17,7 @@ package im.vector.matrix.android.api.pushrules.rest
|
||||
|
||||
import com.squareup.moshi.Json
|
||||
import com.squareup.moshi.JsonClass
|
||||
import im.vector.matrix.android.api.pushrules.Condition
|
||||
import im.vector.matrix.android.api.pushrules.EventMatchCondition
|
||||
import im.vector.matrix.android.api.pushrules.*
|
||||
import timber.log.Timber
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
@ -37,7 +36,7 @@ data class PushCondition(
|
||||
|
||||
fun asExecutableCondition(): Condition? {
|
||||
return when (Condition.Kind.fromString(this.kind)) {
|
||||
Condition.Kind.EVENT_MATCH -> {
|
||||
Condition.Kind.event_match -> {
|
||||
if (this.key != null && this.pattern != null) {
|
||||
EventMatchCondition(key, pattern)
|
||||
} else {
|
||||
@ -45,10 +44,24 @@ data class PushCondition(
|
||||
null
|
||||
}
|
||||
}
|
||||
Condition.Kind.CONTAINS_DISPLAY_NAME -> TODO()
|
||||
Condition.Kind.ROOM_MEMBER_COUNT -> TODO()
|
||||
Condition.Kind.SENDER_NOTIFICATION_PERMISSION -> TODO()
|
||||
Condition.Kind.UNRECOGNIZE -> null
|
||||
Condition.Kind.contains_display_name -> {
|
||||
ContainsDisplayNameCondition()
|
||||
}
|
||||
Condition.Kind.room_member_count -> {
|
||||
if (this.iz.isNullOrBlank()) {
|
||||
Timber.e("Malformed ROOM_MEMBER_COUNT condition")
|
||||
null
|
||||
} else {
|
||||
RoomMemberCountCondition(this.iz)
|
||||
}
|
||||
}
|
||||
Condition.Kind.sender_notification_permission -> {
|
||||
this.key?.let { SenderNotificationPermissionCondition(it) }
|
||||
}
|
||||
Condition.Kind.UNRECOGNIZE -> {
|
||||
Timber.e("Unknwon kind $kind")
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -25,7 +25,7 @@ data class PushRule(
|
||||
//Required. The domainActions to perform when this rule is matched.
|
||||
val actions: List<Any>,
|
||||
//Required. Whether this is a default rule, or has been set explicitly.
|
||||
val default: Boolean,
|
||||
val default: Boolean? = false,
|
||||
//Required. Whether the push rule is enabled or not.
|
||||
val enabled: Boolean,
|
||||
//Required. The ID of this rule.
|
||||
|
@ -22,7 +22,7 @@ import im.vector.matrix.android.api.pushrules.rest.Ruleset
|
||||
* All push rulesets for a user.
|
||||
*/
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class PushruleResponse(
|
||||
data class PushrulesResponse(
|
||||
//Global rules, account level applying to all devices
|
||||
val global: Ruleset,
|
||||
//Device specific rules, apply only to current device
|
@ -49,6 +49,8 @@ interface MembershipService {
|
||||
*/
|
||||
fun getRoomMemberIdsLive(): LiveData<List<String>>
|
||||
|
||||
fun getNumberOfJoinedMembers() : Int
|
||||
|
||||
/**
|
||||
* Invite a user in the room
|
||||
*/
|
||||
|
@ -0,0 +1,26 @@
|
||||
package im.vector.matrix.android.internal.database.mapper
|
||||
|
||||
import im.vector.matrix.android.api.pushrules.rest.PushCondition
|
||||
import im.vector.matrix.android.internal.database.model.PushConditionEntity
|
||||
|
||||
|
||||
internal object PushConditionMapper {
|
||||
|
||||
fun map(entity: PushConditionEntity): PushCondition {
|
||||
return PushCondition(
|
||||
kind = entity.kind,
|
||||
iz = entity.iz,
|
||||
key = entity.key,
|
||||
pattern = entity.pattern
|
||||
)
|
||||
}
|
||||
|
||||
fun map(domain: PushCondition): PushConditionEntity {
|
||||
return PushConditionEntity(
|
||||
kind = domain.kind,
|
||||
iz = domain.iz,
|
||||
key = domain.key,
|
||||
pattern = domain.pattern
|
||||
)
|
||||
}
|
||||
}
|
@ -0,0 +1,105 @@
|
||||
/*
|
||||
* Copyright 2019 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.matrix.android.internal.database.mapper
|
||||
|
||||
import com.squareup.moshi.Types
|
||||
import im.vector.matrix.android.api.pushrules.Condition
|
||||
import im.vector.matrix.android.api.pushrules.rest.PushCondition
|
||||
import im.vector.matrix.android.api.pushrules.rest.PushRule
|
||||
import im.vector.matrix.android.internal.database.model.PushRuleEntity
|
||||
import im.vector.matrix.android.internal.di.MoshiProvider
|
||||
import io.realm.RealmList
|
||||
import timber.log.Timber
|
||||
|
||||
|
||||
internal object PushRulesMapper {
|
||||
|
||||
private val moshiActionsAdapter = MoshiProvider.providesMoshi().adapter<List<Any>>(Types.newParameterizedType(List::class.java, Any::class.java))
|
||||
|
||||
// private val listOfAnyAdapter: JsonAdapter<List<Any>> =
|
||||
// moshi.adapter<List<Any>>(Types.newParameterizedType(List::class.java, Any::class.java), kotlin.collections.emptySet(), "actions")
|
||||
|
||||
fun mapContentRule(pushrule: PushRuleEntity): PushRule {
|
||||
return PushRule(
|
||||
actions = fromActionStr(pushrule.actionsStr),
|
||||
default = pushrule.default,
|
||||
enabled = pushrule.enabled,
|
||||
ruleId = pushrule.ruleId,
|
||||
conditions = listOf(
|
||||
PushCondition(Condition.Kind.event_match.name, "content.body", pushrule.pattern)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
private fun fromActionStr(actionsStr: String?): List<Any> {
|
||||
try {
|
||||
return actionsStr?.let { moshiActionsAdapter.fromJson(it) } ?: emptyList()
|
||||
} catch (e: Throwable) {
|
||||
Timber.e(e, "## failed to map push rule actions <$actionsStr>")
|
||||
return emptyList()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fun mapRoomRule(pushrule: PushRuleEntity): PushRule {
|
||||
return PushRule(
|
||||
actions = fromActionStr(pushrule.actionsStr),
|
||||
default = pushrule.default,
|
||||
enabled = pushrule.enabled,
|
||||
ruleId = pushrule.ruleId,
|
||||
conditions = listOf(
|
||||
PushCondition(Condition.Kind.event_match.name, "room_id", pushrule.ruleId)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
fun mapSenderRule(pushrule: PushRuleEntity): PushRule {
|
||||
return PushRule(
|
||||
actions = fromActionStr(pushrule.actionsStr),
|
||||
default = pushrule.default,
|
||||
enabled = pushrule.enabled,
|
||||
ruleId = pushrule.ruleId,
|
||||
conditions = listOf(
|
||||
PushCondition(Condition.Kind.event_match.name, "user_id", pushrule.ruleId)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
fun map(pushrule: PushRuleEntity): PushRule {
|
||||
return PushRule(
|
||||
actions = fromActionStr(pushrule.actionsStr),
|
||||
default = pushrule.default,
|
||||
enabled = pushrule.enabled,
|
||||
ruleId = pushrule.ruleId,
|
||||
conditions = pushrule.conditions?.map { PushConditionMapper.map(it) }
|
||||
)
|
||||
}
|
||||
|
||||
fun map(pushRule: PushRule): PushRuleEntity {
|
||||
return PushRuleEntity(
|
||||
actionsStr = moshiActionsAdapter.toJson(pushRule.actions),
|
||||
default = pushRule.default ?: false,
|
||||
enabled = pushRule.enabled,
|
||||
ruleId = pushRule.ruleId,
|
||||
pattern = pushRule.pattern,
|
||||
conditions = pushRule.conditions?.let {
|
||||
RealmList(*pushRule.conditions.map { PushConditionMapper.map(it) }.toTypedArray())
|
||||
} ?: RealmList()
|
||||
)
|
||||
}
|
||||
|
||||
}
|
@ -24,7 +24,7 @@ internal open class PushRulesEntity(
|
||||
@Index var userId: String = "",
|
||||
var scope: String = "",
|
||||
var rulesetKey: String = "",
|
||||
var pushRules: RealmList<PushRulesEntity> = RealmList()
|
||||
var pushRules: RealmList<PushRuleEntity> = RealmList()
|
||||
) : RealmObject() {
|
||||
companion object
|
||||
}
|
@ -15,8 +15,10 @@
|
||||
*/
|
||||
package im.vector.matrix.android.internal.database.query
|
||||
|
||||
import im.vector.matrix.android.api.pushrules.rest.PushRule
|
||||
import im.vector.matrix.android.internal.database.model.*
|
||||
import im.vector.matrix.android.internal.database.model.PushRulesEntity
|
||||
import im.vector.matrix.android.internal.database.model.PusherEntity
|
||||
import im.vector.matrix.android.internal.database.model.PusherEntityFields
|
||||
import io.realm.Realm
|
||||
import io.realm.RealmQuery
|
||||
import io.realm.kotlin.where
|
||||
@ -29,5 +31,11 @@ internal fun PusherEntity.Companion.where(realm: Realm, userId: String, pushKey:
|
||||
equalTo(PusherEntityFields.PUSH_KEY, pushKey)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
internal fun PushRulesEntity.Companion.where(realm: Realm, userId: String, scope: String, rulesetKey: String) : RealmQuery<PushRulesEntity> {
|
||||
return realm.where<PushRulesEntity>()
|
||||
.equalTo(PushRulesEntityFields.USER_ID,userId)
|
||||
.equalTo(PushRulesEntityFields.SCOPE,scope)
|
||||
.equalTo(PushRulesEntityFields.RULESET_KEY,rulesetKey)
|
||||
}
|
||||
|
@ -25,6 +25,7 @@ import im.vector.matrix.android.api.MatrixCallback
|
||||
import im.vector.matrix.android.api.auth.data.SessionParams
|
||||
import im.vector.matrix.android.api.listeners.ProgressListener
|
||||
import im.vector.matrix.android.api.pushrules.PushRuleService
|
||||
import im.vector.matrix.android.api.pushrules.rest.PushRule
|
||||
import im.vector.matrix.android.api.session.Session
|
||||
import im.vector.matrix.android.api.session.cache.CacheService
|
||||
import im.vector.matrix.android.api.session.content.ContentUploadStateTracker
|
||||
@ -82,6 +83,7 @@ import java.util.*
|
||||
|
||||
|
||||
internal class DefaultSession(override val sessionParams: SessionParams) : Session, MatrixKoinComponent {
|
||||
|
||||
companion object {
|
||||
const val SCOPE: String = "session"
|
||||
}
|
||||
@ -497,4 +499,13 @@ internal class DefaultSession(override val sessionParams: SessionParams) : Sessi
|
||||
return pushersService.livePushers()
|
||||
}
|
||||
|
||||
override fun getPushrules(scope: String): List<PushRule> {
|
||||
return pushRuleService.getPushrules(scope)
|
||||
}
|
||||
|
||||
override fun fetchPushRules(scope: String) {
|
||||
pushRuleService.fetchPushRules(scope)
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -20,7 +20,6 @@ import android.content.Context
|
||||
import com.zhuinden.monarchy.Monarchy
|
||||
import im.vector.matrix.android.api.auth.data.SessionParams
|
||||
import im.vector.matrix.android.api.pushrules.PushRuleService
|
||||
import im.vector.matrix.android.api.pushrules.PushRulesProvider
|
||||
import im.vector.matrix.android.api.session.cache.CacheService
|
||||
import im.vector.matrix.android.api.session.group.GroupService
|
||||
import im.vector.matrix.android.api.session.pushers.PushersService
|
||||
@ -38,11 +37,11 @@ import im.vector.matrix.android.internal.session.filter.*
|
||||
import im.vector.matrix.android.internal.session.group.DefaultGroupService
|
||||
import im.vector.matrix.android.internal.session.group.GroupSummaryUpdater
|
||||
import im.vector.matrix.android.internal.session.notification.BingRuleWatcher
|
||||
import im.vector.matrix.android.internal.session.notification.MockPushRuleProvider
|
||||
import im.vector.matrix.android.internal.session.notification.PushRulesManager
|
||||
import im.vector.matrix.android.internal.session.notification.DefaultPushRuleService
|
||||
import im.vector.matrix.android.internal.session.pushers.*
|
||||
import im.vector.matrix.android.internal.session.pushers.DefaultGetPusherTask
|
||||
import im.vector.matrix.android.internal.session.pushers.DefaultPusherService
|
||||
import im.vector.matrix.android.internal.session.pushers.GetPushRulesTask
|
||||
import im.vector.matrix.android.internal.session.pushers.GetPushersTask
|
||||
import im.vector.matrix.android.internal.session.pushers.PushersAPI
|
||||
import im.vector.matrix.android.internal.session.room.*
|
||||
@ -175,18 +174,25 @@ internal class SessionModule(private val sessionParams: SessionParams) {
|
||||
}
|
||||
|
||||
scope(DefaultSession.SCOPE) {
|
||||
MockPushRuleProvider() as PushRulesProvider
|
||||
val retrofit: Retrofit = get()
|
||||
retrofit.create(PushrulesApi::class.java)
|
||||
}
|
||||
|
||||
scope(DefaultSession.SCOPE) {
|
||||
get<PushRulesManager>() as PushRuleService
|
||||
}
|
||||
scope(DefaultSession.SCOPE) {
|
||||
PushRulesManager(get(), get())
|
||||
get<DefaultPushRuleService>() as PushRuleService
|
||||
}
|
||||
|
||||
scope(DefaultSession.SCOPE) {
|
||||
BingRuleWatcher(get(), get(), get(), get())
|
||||
DefaultPushRuleService(get(), get(), get(), get())
|
||||
}
|
||||
|
||||
scope(DefaultSession.SCOPE) {
|
||||
DefaultGetPushrulesTask(get()) as GetPushRulesTask
|
||||
}
|
||||
|
||||
|
||||
scope(DefaultSession.SCOPE) {
|
||||
BingRuleWatcher(get(), get())
|
||||
}
|
||||
|
||||
scope(DefaultSession.SCOPE) {
|
||||
|
@ -16,19 +16,19 @@
|
||||
package im.vector.matrix.android.internal.session.notification
|
||||
|
||||
import com.zhuinden.monarchy.Monarchy
|
||||
import im.vector.matrix.android.api.auth.data.Credentials
|
||||
import im.vector.matrix.android.api.pushrules.rest.PushRule
|
||||
import im.vector.matrix.android.api.session.events.model.Event
|
||||
import im.vector.matrix.android.api.session.events.model.EventType
|
||||
import im.vector.matrix.android.internal.database.RealmLiveEntityObserver
|
||||
import im.vector.matrix.android.internal.database.mapper.asDomain
|
||||
import im.vector.matrix.android.internal.database.model.EventEntity
|
||||
import im.vector.matrix.android.internal.database.query.types
|
||||
import im.vector.matrix.android.internal.task.TaskExecutor
|
||||
import im.vector.matrix.android.internal.session.pushers.DefaultConditionResolver
|
||||
import timber.log.Timber
|
||||
|
||||
|
||||
internal class BingRuleWatcher(monarchy: Monarchy,
|
||||
private val credentials: Credentials,
|
||||
private val taskExecutor: TaskExecutor,
|
||||
private val pushRulesManager: PushRulesManager) :
|
||||
private val defaultPushRuleService: DefaultPushRuleService) :
|
||||
RealmLiveEntityObserver<EventEntity>(monarchy) {
|
||||
|
||||
override val query = Monarchy.Query<EventEntity> {
|
||||
@ -41,9 +41,33 @@ internal class BingRuleWatcher(monarchy: Monarchy,
|
||||
|
||||
override fun processChanges(inserted: List<EventEntity>, updated: List<EventEntity>, deleted: List<EventEntity>) {
|
||||
//TODO task
|
||||
inserted.map { it.asDomain() }.let {
|
||||
pushRulesManager.processEvents(it)
|
||||
val rules = defaultPushRuleService.getPushrules("global")
|
||||
inserted.map { it.asDomain() }.let { events ->
|
||||
events.forEach { event ->
|
||||
fulfilledBingRule(event, rules)?.let {
|
||||
Timber.v("Rule $it match for event ${event.eventId}")
|
||||
defaultPushRuleService.dispatchBing(event, it)
|
||||
}
|
||||
}
|
||||
}
|
||||
defaultPushRuleService.dispatchFinish()
|
||||
}
|
||||
|
||||
private fun fulfilledBingRule(event: Event, rules: List<PushRule>): PushRule? {
|
||||
val conditionResolver = DefaultConditionResolver(event)
|
||||
rules.filter { it.enabled }.forEach { rule ->
|
||||
val isFullfilled = rule.conditions?.map {
|
||||
it.asExecutableCondition()?.isSatisfied(conditionResolver) ?: false
|
||||
}?.fold(true/*A rule with no conditions always matches*/, { acc, next ->
|
||||
//All conditions must hold true for an event in order to apply the action for the event.
|
||||
acc && next
|
||||
}) ?: false
|
||||
|
||||
if (isFullfilled) {
|
||||
return rule
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
|
||||
|
@ -0,0 +1,188 @@
|
||||
/*
|
||||
* Copyright 2019 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.matrix.android.internal.session.notification
|
||||
|
||||
import com.zhuinden.monarchy.Monarchy
|
||||
import im.vector.matrix.android.api.MatrixCallback
|
||||
import im.vector.matrix.android.api.auth.data.SessionParams
|
||||
import im.vector.matrix.android.api.pushrules.Action
|
||||
import im.vector.matrix.android.api.pushrules.PushRuleService
|
||||
import im.vector.matrix.android.api.pushrules.rest.PushRule
|
||||
import im.vector.matrix.android.api.pushrules.rest.PushrulesResponse
|
||||
import im.vector.matrix.android.api.session.events.model.Event
|
||||
import im.vector.matrix.android.internal.database.mapper.PushRulesMapper
|
||||
import im.vector.matrix.android.internal.database.model.PushRulesEntity
|
||||
import im.vector.matrix.android.internal.database.model.PusherEntityFields
|
||||
import im.vector.matrix.android.internal.database.query.where
|
||||
import im.vector.matrix.android.internal.session.pushers.GetPushRulesTask
|
||||
import im.vector.matrix.android.internal.task.TaskExecutor
|
||||
import im.vector.matrix.android.internal.task.configureWith
|
||||
|
||||
|
||||
internal class DefaultPushRuleService(
|
||||
private val sessionParams: SessionParams,
|
||||
private val pushRulesTask: GetPushRulesTask,
|
||||
private val taskExecutor: TaskExecutor,
|
||||
private val monarchy: Monarchy
|
||||
) : PushRuleService {
|
||||
|
||||
|
||||
private var listeners = ArrayList<PushRuleService.PushRuleListener>()
|
||||
|
||||
|
||||
override fun fetchPushRules(scope: String) {
|
||||
pushRulesTask
|
||||
.configureWith(Unit)
|
||||
.dispatchTo(object : MatrixCallback<PushrulesResponse> {
|
||||
override fun onSuccess(data: PushrulesResponse) {
|
||||
monarchy.runTransactionSync { realm ->
|
||||
//clear existings?
|
||||
//TODO
|
||||
realm.where(PushRulesEntity::class.java)
|
||||
.equalTo(PusherEntityFields.USER_ID, sessionParams.credentials.userId)
|
||||
.findAll().deleteAllFromRealm()
|
||||
|
||||
var content = PushRulesEntity(sessionParams.credentials.userId, scope, "content")
|
||||
data.global.content?.forEach { rule ->
|
||||
PushRulesMapper.map(rule).also {
|
||||
content.pushRules.add(it)
|
||||
}
|
||||
}
|
||||
realm.insertOrUpdate(content)
|
||||
|
||||
var override = PushRulesEntity(sessionParams.credentials.userId, scope, "override")
|
||||
data.global.override?.forEach { rule ->
|
||||
PushRulesMapper.map(rule).also {
|
||||
override.pushRules.add(it)
|
||||
}
|
||||
}
|
||||
realm.insertOrUpdate(override)
|
||||
|
||||
var rooms = PushRulesEntity(sessionParams.credentials.userId, scope, "room")
|
||||
data.global.room?.forEach { rule ->
|
||||
PushRulesMapper.map(rule).also {
|
||||
rooms.pushRules.add(it)
|
||||
}
|
||||
}
|
||||
realm.insertOrUpdate(rooms)
|
||||
|
||||
var senders = PushRulesEntity(sessionParams.credentials.userId, scope, "sender")
|
||||
data.global.sender?.forEach { rule ->
|
||||
PushRulesMapper.map(rule).also {
|
||||
senders.pushRules.add(it)
|
||||
}
|
||||
}
|
||||
realm.insertOrUpdate(senders)
|
||||
|
||||
var underrides = PushRulesEntity(sessionParams.credentials.userId, scope, "underride")
|
||||
data.global.underride?.forEach { rule ->
|
||||
PushRulesMapper.map(rule).also {
|
||||
underrides.pushRules.add(it)
|
||||
}
|
||||
}
|
||||
realm.insertOrUpdate(underrides)
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
})
|
||||
.executeBy(taskExecutor)
|
||||
}
|
||||
|
||||
override fun getPushrules(scope: String): List<PushRule> {
|
||||
|
||||
var contentRules: List<PushRule> = emptyList()
|
||||
monarchy.doWithRealm { realm ->
|
||||
PushRulesEntity.where(realm, sessionParams.credentials.userId, scope, "content").findFirst()?.let { re ->
|
||||
contentRules = re.pushRules.map { PushRulesMapper.mapContentRule(it) }
|
||||
}
|
||||
}
|
||||
|
||||
var overrideRules: List<PushRule> = emptyList()
|
||||
monarchy.doWithRealm { realm ->
|
||||
PushRulesEntity.where(realm, sessionParams.credentials.userId, scope, "override").findFirst()?.let { re ->
|
||||
overrideRules = re.pushRules.map { PushRulesMapper.map(it) }
|
||||
}
|
||||
}
|
||||
|
||||
var roomRules: List<PushRule> = emptyList()
|
||||
monarchy.doWithRealm { realm ->
|
||||
PushRulesEntity.where(realm, sessionParams.credentials.userId, scope, "room").findFirst()?.let { re ->
|
||||
roomRules = re.pushRules.map { PushRulesMapper.mapRoomRule(it) }
|
||||
}
|
||||
}
|
||||
|
||||
var senderRules: List<PushRule> = emptyList()
|
||||
monarchy.doWithRealm { realm ->
|
||||
PushRulesEntity.where(realm, sessionParams.credentials.userId, scope, "sender").findFirst()?.let { re ->
|
||||
senderRules = re.pushRules.map { PushRulesMapper.mapSenderRule(it) }
|
||||
}
|
||||
}
|
||||
|
||||
var underrideRules: List<PushRule> = emptyList()
|
||||
monarchy.doWithRealm { realm ->
|
||||
PushRulesEntity.where(realm, sessionParams.credentials.userId, scope, "underride").findFirst()?.let { re ->
|
||||
underrideRules = re.pushRules.map { PushRulesMapper.map(it) }
|
||||
}
|
||||
}
|
||||
|
||||
return contentRules + overrideRules + roomRules + senderRules + underrideRules
|
||||
}
|
||||
|
||||
override fun removePushRuleListener(listener: PushRuleService.PushRuleListener) {
|
||||
listeners.remove(listener)
|
||||
}
|
||||
|
||||
|
||||
override fun addPushRuleListener(listener: PushRuleService.PushRuleListener) {
|
||||
if (!listeners.contains(listener))
|
||||
listeners.add(listener)
|
||||
}
|
||||
|
||||
// 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) {
|
||||
try {
|
||||
listeners.forEach {
|
||||
it.onMatchRule(event, Action.mapFrom(rule) ?: emptyList())
|
||||
}
|
||||
} catch (e: Throwable) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
fun dispatchFinish() {
|
||||
try {
|
||||
listeners.forEach {
|
||||
it.batchFinish()
|
||||
}
|
||||
} catch (e: Throwable) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -1,41 +0,0 @@
|
||||
package im.vector.matrix.android.internal.session.notification
|
||||
|
||||
import im.vector.matrix.android.api.pushrules.PushRulesProvider
|
||||
import im.vector.matrix.android.api.pushrules.rest.PushRule
|
||||
import im.vector.matrix.android.internal.di.MoshiProvider
|
||||
|
||||
|
||||
class MockPushRuleProvider : PushRulesProvider {
|
||||
override fun getOrderedPushrules(): List<PushRule> {
|
||||
val raw = """
|
||||
{
|
||||
"actions": [
|
||||
"notify",
|
||||
{
|
||||
"set_tweak": "highlight",
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"conditions": [
|
||||
{
|
||||
"key": "type",
|
||||
"kind": "event_match",
|
||||
"pattern": "m.room.message"
|
||||
}
|
||||
],
|
||||
"default": true,
|
||||
"enabled": true,
|
||||
"rule_id": ".m.rule.message"
|
||||
}
|
||||
""".trimIndent()
|
||||
val pushRule = MoshiProvider.providesMoshi().adapter<PushRule>(PushRule::class.java).fromJson(raw)
|
||||
|
||||
return listOf<PushRule>(
|
||||
pushRule!!
|
||||
)
|
||||
}
|
||||
|
||||
override fun onRulesUpdate(newRules: List<PushRule>) {
|
||||
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
|
||||
}
|
||||
}
|
@ -1,85 +0,0 @@
|
||||
/*
|
||||
* Copyright 2019 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.matrix.android.internal.session.notification
|
||||
|
||||
import im.vector.matrix.android.api.auth.data.SessionParams
|
||||
import im.vector.matrix.android.api.pushrules.PushRuleService
|
||||
import im.vector.matrix.android.api.pushrules.PushRulesProvider
|
||||
import im.vector.matrix.android.api.pushrules.domainActions
|
||||
import im.vector.matrix.android.api.pushrules.rest.PushRule
|
||||
import im.vector.matrix.android.api.session.events.model.Event
|
||||
|
||||
|
||||
internal class PushRulesManager(
|
||||
private val sessionParams: SessionParams,
|
||||
private val pushRulesProvider: PushRulesProvider) : PushRuleService {
|
||||
|
||||
|
||||
private var listeners = ArrayList<PushRuleService.PushRuleListener>()
|
||||
|
||||
|
||||
override fun removePushRuleListener(listener: PushRuleService.PushRuleListener) {
|
||||
listeners.remove(listener)
|
||||
}
|
||||
|
||||
|
||||
override fun addPushRuleListener(listener: PushRuleService.PushRuleListener) {
|
||||
if (!listeners.contains(listener))
|
||||
listeners.add(listener)
|
||||
}
|
||||
|
||||
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) {
|
||||
try {
|
||||
listeners.forEach {
|
||||
it.onMatchRule(event, rule.domainActions() ?: emptyList())
|
||||
}
|
||||
} catch (e: Throwable) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
fun dispatchFinish() {
|
||||
try {
|
||||
listeners.forEach {
|
||||
it.batchFinish()
|
||||
}
|
||||
} catch (e: Throwable) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
fun fulfilledBingRule(event: Event): PushRule? {
|
||||
pushRulesProvider.getOrderedPushrules().forEach { rule ->
|
||||
rule.conditions?.mapNotNull { it.asExecutableCondition() }?.forEach {
|
||||
if (it.isSatisfied(event)) return rule
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,55 @@
|
||||
/*
|
||||
* Copyright 2019 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.matrix.android.internal.session.pushers
|
||||
|
||||
import im.vector.matrix.android.api.auth.data.SessionParams
|
||||
import im.vector.matrix.android.api.pushrules.*
|
||||
import im.vector.matrix.android.api.session.events.model.Event
|
||||
import im.vector.matrix.android.api.session.room.RoomService
|
||||
import im.vector.matrix.android.internal.di.MatrixKoinComponent
|
||||
import org.koin.standalone.inject
|
||||
import timber.log.Timber
|
||||
|
||||
internal class DefaultConditionResolver(val event: Event) : ConditionResolver, MatrixKoinComponent {
|
||||
|
||||
private val roomService by inject<RoomService>()
|
||||
|
||||
private val sessionParams by inject<SessionParams>()
|
||||
|
||||
override fun resolveEventMatchCondition(eventMatchCondition: EventMatchCondition): Boolean {
|
||||
return eventMatchCondition.isSatisfied(event)
|
||||
}
|
||||
|
||||
override fun resolveRoomMemberCountCondition(roomMemberCountCondition: RoomMemberCountCondition): Boolean {
|
||||
return roomMemberCountCondition.isSatisfied(event, roomService)
|
||||
}
|
||||
|
||||
override fun resolveSenderNotificationPermissionCondition(senderNotificationPermissionCondition: SenderNotificationPermissionCondition): Boolean {
|
||||
// val roomId = event.roomId ?: return false
|
||||
// val room = roomService.getRoom(roomId) ?: return false
|
||||
//TODO RoomState not yet managed
|
||||
Timber.e("POWER LEVELS STATE NOT YET MANAGED BY RIOTX")
|
||||
return false //senderNotificationPermissionCondition.isSatisfied(event, )
|
||||
}
|
||||
|
||||
override fun resolveContainsDisplayNameCondition(containsDisplayNameCondition: ContainsDisplayNameCondition): Boolean {
|
||||
val roomId = event.roomId ?: return false
|
||||
val room = roomService.getRoom(roomId) ?: return false
|
||||
val myDisplayName = room.getRoomMember(sessionParams.credentials.userId)?.displayName
|
||||
?: return false
|
||||
return containsDisplayNameCondition.isSatisfied(event, myDisplayName)
|
||||
}
|
||||
}
|
@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright 2019 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.matrix.android.internal.session.pushers
|
||||
|
||||
import arrow.core.Try
|
||||
import im.vector.matrix.android.api.pushrules.rest.PushrulesResponse
|
||||
import im.vector.matrix.android.internal.network.executeRequest
|
||||
import im.vector.matrix.android.internal.task.Task
|
||||
|
||||
|
||||
internal interface GetPushRulesTask : Task<Unit, PushrulesResponse>
|
||||
|
||||
internal class DefaultGetPushrulesTask(private val pushrulesApi: PushrulesApi) : GetPushRulesTask {
|
||||
|
||||
override suspend fun execute(params: Unit): Try<PushrulesResponse> {
|
||||
return executeRequest {
|
||||
apiCall = pushrulesApi.getAllRules()
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,72 @@
|
||||
/*
|
||||
* Copyright 2019 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.matrix.android.internal.session.pushers
|
||||
|
||||
import im.vector.matrix.android.api.pushrules.rest.PushRule
|
||||
import im.vector.matrix.android.api.pushrules.rest.PushrulesResponse
|
||||
import im.vector.matrix.android.internal.network.NetworkConstants
|
||||
import retrofit2.Call
|
||||
import retrofit2.http.*
|
||||
|
||||
|
||||
internal interface PushrulesApi {
|
||||
/**
|
||||
* Get all push rules
|
||||
*/
|
||||
@GET(NetworkConstants.URI_API_PREFIX_PATH_R0 + "pushrules/")
|
||||
fun getAllRules(): Call<PushrulesResponse>
|
||||
|
||||
/**
|
||||
* Update the ruleID enable status
|
||||
*
|
||||
* @param kind the notification kind (sender, room...)
|
||||
* @param ruleId the ruleId
|
||||
* @param enable the new enable status
|
||||
*/
|
||||
@PUT(NetworkConstants.URI_API_PREFIX_PATH_R0 + "pushrules/global/{kind}/{ruleId}/enabled")
|
||||
fun updateEnableRuleStatus(@Path("kind") kind: String, @Path("ruleId") ruleId: String, @Body enable: Boolean?): Call<Unit>
|
||||
|
||||
|
||||
/**
|
||||
* Update the ruleID enable status
|
||||
*
|
||||
* @param kind the notification kind (sender, room...)
|
||||
* @param ruleId the ruleId
|
||||
* @param actions the actions
|
||||
*/
|
||||
@PUT(NetworkConstants.URI_API_PREFIX_PATH_R0 + "pushrules/global/{kind}/{ruleId}/actions")
|
||||
fun updateRuleActions(@Path("kind") kind: String, @Path("ruleId") ruleId: String, @Body actions: Any): Call<Unit>
|
||||
|
||||
|
||||
/**
|
||||
* Update the ruleID enable status
|
||||
*
|
||||
* @param kind the notification kind (sender, room...)
|
||||
* @param ruleId the ruleId
|
||||
*/
|
||||
@DELETE(NetworkConstants.URI_API_PREFIX_PATH_R0 + "pushrules/global/{kind}/{ruleId}")
|
||||
fun deleteRule(@Path("kind") kind: String, @Path("ruleId") ruleId: String): Call<Unit>
|
||||
|
||||
/**
|
||||
* Add the ruleID enable status
|
||||
*
|
||||
* @param kind the notification kind (sender, room...)
|
||||
* @param ruleId the ruleId.
|
||||
* @param rule the rule to add.
|
||||
*/
|
||||
@PUT(NetworkConstants.URI_API_PREFIX_PATH_R0 + "pushrules/global/{kind}/{ruleId}")
|
||||
fun addRule(@Path("kind") kind: String, @Path("ruleId") ruleId: String, @Body rule: PushRule): Call<Unit>
|
||||
}
|
@ -66,6 +66,14 @@ internal class DefaultMembershipService(private val roomId: String,
|
||||
)
|
||||
}
|
||||
|
||||
override fun getNumberOfJoinedMembers(): Int {
|
||||
var result = 0
|
||||
monarchy.runTransactionSync {
|
||||
result = RoomMembers(it, roomId).getNumberOfJoinedMembers()
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
override fun invite(userId: String, callback: MatrixCallback<Unit>) {
|
||||
val params = InviteTask.Params(roomId, userId)
|
||||
inviteTask.configureWith(params)
|
||||
|
@ -1,10 +1,23 @@
|
||||
package im.vector.matrix.android.api.pushrules
|
||||
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import im.vector.matrix.android.api.MatrixCallback
|
||||
import im.vector.matrix.android.api.session.content.ContentAttachmentData
|
||||
import im.vector.matrix.android.api.session.events.model.Event
|
||||
import im.vector.matrix.android.api.session.events.model.toContent
|
||||
import im.vector.matrix.android.api.session.room.Room
|
||||
import im.vector.matrix.android.api.session.room.RoomService
|
||||
import im.vector.matrix.android.api.session.room.model.EventAnnotationsSummary
|
||||
import im.vector.matrix.android.api.session.room.model.Membership
|
||||
import im.vector.matrix.android.api.session.room.model.RoomMember
|
||||
import im.vector.matrix.android.api.session.room.model.RoomSummary
|
||||
import im.vector.matrix.android.api.session.room.model.create.CreateRoomParams
|
||||
import im.vector.matrix.android.api.session.room.model.message.MessageTextContent
|
||||
import im.vector.matrix.android.api.session.room.timeline.Timeline
|
||||
import im.vector.matrix.android.api.session.room.timeline.TimelineEvent
|
||||
import im.vector.matrix.android.api.util.Cancelable
|
||||
import org.junit.Assert
|
||||
import org.junit.Test
|
||||
|
||||
class PushrulesConditionTest {
|
||||
@ -100,4 +113,179 @@ class PushrulesConditionTest {
|
||||
|
||||
assert(condition.isSatisfied(simpleTextEvent))
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
fun test_roommember_condition() {
|
||||
|
||||
|
||||
val conditionEqual3 = RoomMemberCountCondition("3")
|
||||
val conditionEqual3Bis = RoomMemberCountCondition("==3")
|
||||
val conditionLessThan3 = RoomMemberCountCondition("<3")
|
||||
|
||||
val session = MockRoomService()
|
||||
|
||||
Event(
|
||||
type = "m.room.message",
|
||||
eventId = "mx0",
|
||||
content = MessageTextContent("m.text", "A").toContent(),
|
||||
originServerTs = 0,
|
||||
roomId = "2joined").also {
|
||||
Assert.assertFalse("This room does not have 3 members", conditionEqual3.isSatisfied(it, session))
|
||||
Assert.assertFalse("This room does not have 3 members", conditionEqual3Bis.isSatisfied(it, session))
|
||||
Assert.assertTrue("This room has less than 3 members", conditionLessThan3.isSatisfied(it, session))
|
||||
}
|
||||
|
||||
Event(
|
||||
type = "m.room.message",
|
||||
eventId = "mx0",
|
||||
content = MessageTextContent("m.text", "A").toContent(),
|
||||
originServerTs = 0,
|
||||
roomId = "3joined").also {
|
||||
Assert.assertTrue("This room has 3 members",conditionEqual3.isSatisfied(it, session))
|
||||
Assert.assertTrue("This room has 3 members",conditionEqual3Bis.isSatisfied(it, session))
|
||||
Assert.assertFalse("This room has more than 3 members",conditionLessThan3.isSatisfied(it, session))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class MockRoomService() : RoomService {
|
||||
override fun createRoom(createRoomParams: CreateRoomParams, callback: MatrixCallback<String>) {
|
||||
|
||||
}
|
||||
|
||||
override fun getRoom(roomId: String): Room? {
|
||||
return when (roomId) {
|
||||
"2joined" -> MockRoom(roomId, 2)
|
||||
"3joined" -> MockRoom(roomId, 3)
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
override fun liveRoomSummaries(): LiveData<List<RoomSummary>> {
|
||||
return MutableLiveData()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class MockRoom(override val roomId: String, val _numberOfJoinedMembers: Int) : Room {
|
||||
|
||||
|
||||
override fun getNumberOfJoinedMembers(): Int {
|
||||
return _numberOfJoinedMembers
|
||||
}
|
||||
|
||||
override val roomSummary: LiveData<RoomSummary>
|
||||
get() = TODO("not implemented") //To change initializer of created properties use File | Settings | File Templates.
|
||||
|
||||
override fun createTimeline(eventId: String?, allowedTypes: List<String>?): Timeline {
|
||||
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
override fun getTimeLineEvent(eventId: String): TimelineEvent? {
|
||||
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
override fun sendTextMessage(text: String, msgType: String, autoMarkdown: Boolean): Cancelable {
|
||||
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
override fun sendFormattedTextMessage(text: String, formattedText: String): Cancelable {
|
||||
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
override fun sendMedia(attachment: ContentAttachmentData): Cancelable {
|
||||
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
override fun sendMedias(attachments: List<ContentAttachmentData>): Cancelable {
|
||||
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
override fun redactEvent(event: Event, reason: String?): Cancelable {
|
||||
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
override fun markAllAsRead(callback: MatrixCallback<Unit>) {
|
||||
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
override fun setReadReceipt(eventId: String, callback: MatrixCallback<Unit>) {
|
||||
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
override fun setReadMarker(fullyReadEventId: String, callback: MatrixCallback<Unit>) {
|
||||
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
override fun isEventRead(eventId: String): Boolean {
|
||||
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
override fun loadRoomMembersIfNeeded(): Cancelable {
|
||||
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
override fun getRoomMember(userId: String): RoomMember? {
|
||||
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
override fun getRoomMemberIdsLive(): LiveData<List<String>> {
|
||||
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
override fun invite(userId: String, callback: MatrixCallback<Unit>) {
|
||||
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
override fun join(callback: MatrixCallback<Unit>) {
|
||||
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
override fun leave(callback: MatrixCallback<Unit>) {
|
||||
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
override fun updateTopic(topic: String, callback: MatrixCallback<Unit>) {
|
||||
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
override fun sendReaction(reaction: String, targetEventId: String): Cancelable {
|
||||
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
override fun undoReaction(reaction: String, targetEventId: String, myUserId: String) {
|
||||
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
override fun updateQuickReaction(reaction: String, oppositeReaction: String, targetEventId: String, myUserId: String) {
|
||||
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
override fun editTextMessage(targetEventId: String, newBodyText: String, newBodyAutoMarkdown: Boolean, compatibilityBodyText: String): Cancelable {
|
||||
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
override fun replyToMessage(eventReplied: Event, replyText: String): Cancelable? {
|
||||
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
override fun getEventSummaryLive(eventId: String): LiveData<List<EventAnnotationsSummary>> {
|
||||
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
override fun isEncrypted(): Boolean {
|
||||
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
override fun encryptionAlgorithm(): String? {
|
||||
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
override fun shouldEncryptForInvitedMembers(): Boolean {
|
||||
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
Reference in New Issue
Block a user