forked from GitHub-Mirror/riotX-android
Get real push rules from server and evaluate them
This commit is contained in:
parent
2e417a9143
commit
0584fc3666
@ -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.
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -137,8 +137,10 @@ class VectorApplication : Application() {
|
||||
} else {
|
||||
//TODO check if notifications are enabled for this device
|
||||
//We need to use alarm in this mode
|
||||
AlarmSyncBroadcastReceiver.scheduleAlarm(applicationContext,4_000L)
|
||||
Timber.i("Alarm scheduled to restart service")
|
||||
if (Matrix.getInstance().currentSession != null) {
|
||||
AlarmSyncBroadcastReceiver.scheduleAlarm(applicationContext, 4_000L)
|
||||
Timber.i("Alarm scheduled to restart service")
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@ -150,6 +152,7 @@ class VectorApplication : Application() {
|
||||
it.refreshPushers()
|
||||
//bind to the sync service
|
||||
get<PushRuleTriggerListener>().startWithSession(it)
|
||||
it.fetchPushRules()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -25,7 +25,7 @@ import android.widget.TextView
|
||||
import androidx.preference.PreferenceViewHolder
|
||||
import im.vector.riotredesign.R
|
||||
|
||||
// TODO Replace by real Bingrule class
|
||||
// TODO Replace by real Bingrule class, then delete
|
||||
class BingRule(rule: BingRule) {
|
||||
fun shouldNotNotify() = false
|
||||
fun shouldNotify() = false
|
||||
|
@ -0,0 +1,62 @@
|
||||
/*
|
||||
* 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.riotredesign.core.ui.list
|
||||
|
||||
import android.widget.TextView
|
||||
import com.airbnb.epoxy.EpoxyAttribute
|
||||
import com.airbnb.epoxy.EpoxyModelClass
|
||||
import im.vector.riotredesign.R
|
||||
import im.vector.riotredesign.core.epoxy.VectorEpoxyHolder
|
||||
import im.vector.riotredesign.core.epoxy.VectorEpoxyModel
|
||||
import im.vector.riotredesign.core.extensions.setTextOrHide
|
||||
|
||||
/**
|
||||
* A generic list item.
|
||||
* Displays an item with a title, and optional description.
|
||||
* Can display an accessory on the right, that can be an image or an indeterminate progress.
|
||||
* If provided with an action, will display a button at the bottom of the list item.
|
||||
*/
|
||||
@EpoxyModelClass(layout = R.layout.item_generic_footer)
|
||||
abstract class GenericFooterItem : VectorEpoxyModel<GenericFooterItem.Holder>() {
|
||||
|
||||
|
||||
@EpoxyAttribute
|
||||
var text: String? = null
|
||||
|
||||
@EpoxyAttribute
|
||||
var style: GenericItem.STYLE = GenericItem.STYLE.NORMAL_TEXT
|
||||
|
||||
@EpoxyAttribute
|
||||
var itemClickAction: GenericItem.Action? = null
|
||||
|
||||
override fun bind(holder: Holder) {
|
||||
|
||||
holder.text.setTextOrHide(text)
|
||||
when (style) {
|
||||
GenericItem.STYLE.BIG_TEXT -> holder.text.textSize = 18f
|
||||
GenericItem.STYLE.NORMAL_TEXT -> holder.text.textSize = 14f
|
||||
}
|
||||
|
||||
|
||||
holder.view.setOnClickListener {
|
||||
itemClickAction?.perform?.run()
|
||||
}
|
||||
}
|
||||
|
||||
class Holder : VectorEpoxyHolder() {
|
||||
val text by bind<TextView>(R.id.itemGenericFooterText)
|
||||
}
|
||||
}
|
@ -78,6 +78,7 @@ class LoginActivity : VectorBaseActivity() {
|
||||
data.setFilter(FilterService.FilterPreset.RiotFilter)
|
||||
data.startSync()
|
||||
get<PushRuleTriggerListener>().startWithSession(data)
|
||||
data.fetchPushRules()
|
||||
goToHome()
|
||||
}
|
||||
|
||||
|
@ -43,7 +43,7 @@ class NotifiableEventResolver(val context: Context) {
|
||||
|
||||
//private val eventDisplay = RiotEventDisplay(context)
|
||||
|
||||
fun resolveEvent(event: Event/*, roomState: RoomState?*/, bingRule: PushRule?, session: Session): NotifiableEvent? {
|
||||
fun resolveEvent(event: Event/*, roomState: RoomState?, bingRule: PushRule?*/, session: Session): NotifiableEvent? {
|
||||
|
||||
|
||||
// val store = session.dataHandler.store
|
||||
@ -55,7 +55,7 @@ class NotifiableEventResolver(val context: Context) {
|
||||
|
||||
when (event.getClearType()) {
|
||||
EventType.MESSAGE -> {
|
||||
return resolveMessageEvent(event, bingRule, session)
|
||||
return resolveMessageEvent(event, session)
|
||||
}
|
||||
// EventType.ENCRYPTED -> {
|
||||
// val messageEvent = resolveMessageEvent(event, bingRule, session, store)
|
||||
@ -88,12 +88,10 @@ class NotifiableEventResolver(val context: Context) {
|
||||
}
|
||||
|
||||
|
||||
private fun resolveMessageEvent(event: Event, pushRule: PushRule?, session: Session): NotifiableEvent? {
|
||||
private fun resolveMessageEvent(event: Event, session: Session): NotifiableEvent? {
|
||||
//If we are here, that means that the event should be notified to the user, we check now how it should be presented (sound)
|
||||
// val soundName = pushRule?.notificationSound
|
||||
|
||||
val noisy = true//pushRule?.notificationSound != null
|
||||
|
||||
//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.roomId!! /*roomID cannot be null (see Matrix SDK code)*/)
|
||||
|
||||
@ -109,7 +107,7 @@ class NotifiableEventResolver(val context: Context) {
|
||||
val notifiableEvent = NotifiableMessageEvent(
|
||||
eventId = event.eventId ?: "",
|
||||
timestamp = event.originServerTs ?: 0,
|
||||
noisy = noisy,
|
||||
noisy = false,//will be updated
|
||||
senderName = senderDisplayName,
|
||||
senderId = event.senderId,
|
||||
body = body,
|
||||
@ -130,7 +128,7 @@ class NotifiableEventResolver(val context: Context) {
|
||||
val notifiableEvent = NotifiableMessageEvent(
|
||||
eventId = event.eventId!!,
|
||||
timestamp = event.originServerTs ?: 0,
|
||||
noisy = noisy,
|
||||
noisy = false,//will be updated
|
||||
senderName = senderDisplayName,
|
||||
senderId = event.senderId,
|
||||
body = body,
|
||||
|
@ -0,0 +1,41 @@
|
||||
/*
|
||||
* 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.riotredesign.features.notifications
|
||||
|
||||
import im.vector.matrix.android.api.pushrules.Action
|
||||
|
||||
data class NotificationAction(
|
||||
val shouldNotify: Boolean,
|
||||
val highlight: Boolean = false,
|
||||
val soundName: String? = null
|
||||
) {
|
||||
companion object {
|
||||
fun extractFrom(ruleActions: List<Action>): NotificationAction {
|
||||
var shouldNotify = false
|
||||
var highlight = false
|
||||
var sound: String? = null
|
||||
ruleActions.forEach {
|
||||
if (it.type == Action.Type.NOTIFY) shouldNotify = true
|
||||
if (it.type == Action.Type.DONT_NOTIFY) shouldNotify = false
|
||||
if (it.type == Action.Type.SET_TWEAK) {
|
||||
if (it.tweak_action == "highlight") highlight = it.boolValue ?: false
|
||||
if (it.tweak_action == "sound") sound = it.stringValue
|
||||
}
|
||||
}
|
||||
return NotificationAction(shouldNotify, highlight, sound)
|
||||
}
|
||||
}
|
||||
}
|
@ -57,28 +57,6 @@ class NotificationDrawerManager(val context: Context, private val outdatedDetect
|
||||
}
|
||||
})
|
||||
|
||||
/**
|
||||
* No multi session support for now
|
||||
*/
|
||||
private fun initWithSession(session: Session?) {
|
||||
session?.let {
|
||||
/*
|
||||
myUserDisplayName = it.myUser?.displayname ?: it.myUserId
|
||||
|
||||
// User Avatar
|
||||
it.myUser?.avatarUrl?.let { avatarUrl ->
|
||||
val userAvatarUrlPath = it.mediaCache?.thumbnailCacheFile(avatarUrl, avatarSize)
|
||||
if (userAvatarUrlPath != null) {
|
||||
myUserAvatarUrl = userAvatarUrlPath.path
|
||||
} else {
|
||||
// prepare for the next time
|
||||
session.mediaCache?.loadAvatarThumbnail(session.homeServerConfig, ImageView(context), avatarUrl, avatarSize)
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Should be called as soon as a new event is ready to be displayed.
|
||||
The notification corresponding to this event will not be displayed until
|
||||
|
@ -16,12 +16,24 @@ class PushRuleTriggerListener(
|
||||
var session: Session? = null
|
||||
|
||||
override fun onMatchRule(event: Event, actions: List<Action>) {
|
||||
Timber.v("Push rule match for event ${event.eventId}")
|
||||
if (session == null) {
|
||||
Timber.e("Called without active session")
|
||||
return
|
||||
}
|
||||
resolver.resolveEvent(event,null,session!!)?.let {
|
||||
drawerManager.onNotifiableEventReceived(it)
|
||||
val notificationAction = NotificationAction.extractFrom(actions)
|
||||
if (notificationAction.shouldNotify) {
|
||||
val resolveEvent = resolver.resolveEvent(event, session!!)
|
||||
if (resolveEvent == null) {
|
||||
Timber.v("## Failed to resolve event")
|
||||
//TODO
|
||||
} else {
|
||||
resolveEvent.noisy = !notificationAction.soundName.isNullOrBlank()
|
||||
Timber.v("New event to notify $resolveEvent tweaks:$notificationAction")
|
||||
drawerManager.onNotifiableEventReceived(resolveEvent)
|
||||
}
|
||||
} else {
|
||||
Timber.v("Matched push rule is set to not notify")
|
||||
}
|
||||
}
|
||||
|
||||
@ -43,4 +55,5 @@ class PushRuleTriggerListener(
|
||||
drawerManager.clearAllEvents()
|
||||
drawerManager.refreshNotificationDrawer()
|
||||
}
|
||||
|
||||
}
|
@ -1671,15 +1671,15 @@ class VectorSettingsPreferencesFragment : VectorPreferenceFragment(), SharedPref
|
||||
|
||||
var index = 0
|
||||
|
||||
for (pusher in mDisplayedPushers) {
|
||||
if (null != pusher.lang) {
|
||||
val isThisDeviceTarget = TextUtils.equals(pushManager.currentRegistrationToken, pusher.pushkey)
|
||||
for (pushRule in mDisplayedPushers) {
|
||||
if (null != pushRule.lang) {
|
||||
val isThisDeviceTarget = TextUtils.equals(pushManager.currentRegistrationToken, pushRule.pushkey)
|
||||
|
||||
val preference = VectorPreference(activity).apply {
|
||||
mTypeface = if (isThisDeviceTarget) Typeface.BOLD else Typeface.NORMAL
|
||||
}
|
||||
preference.title = pusher.deviceDisplayName
|
||||
preference.summary = pusher.appDisplayName
|
||||
preference.title = pushRule.deviceDisplayName
|
||||
preference.summary = pushRule.appDisplayName
|
||||
preference.key = PUSHER_PREFERENCE_KEY_BASE + index
|
||||
index++
|
||||
mPushersSettingsCategory.addPreference(preference)
|
||||
@ -1694,7 +1694,7 @@ class VectorSettingsPreferencesFragment : VectorPreferenceFragment(), SharedPref
|
||||
.setPositiveButton(R.string.remove)
|
||||
{ _, _ ->
|
||||
displayLoadingView()
|
||||
pushManager.unregister(session, pusher, object : MatrixCallback<Unit> {
|
||||
pushManager.unregister(session, pushRule, object : MatrixCallback<Unit> {
|
||||
override fun onSuccess(info: Void?) {
|
||||
refreshPushersList()
|
||||
onCommonDone(null)
|
||||
|
@ -17,7 +17,6 @@
|
||||
package im.vector.riotredesign.features.settings.push
|
||||
|
||||
import android.os.Bundle
|
||||
import android.widget.LinearLayout
|
||||
import androidx.recyclerview.widget.DividerItemDecoration
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
@ -27,19 +26,21 @@ import com.airbnb.mvrx.withState
|
||||
import im.vector.riotredesign.R
|
||||
import im.vector.riotredesign.core.platform.VectorBaseActivity
|
||||
import im.vector.riotredesign.core.platform.VectorBaseFragment
|
||||
import kotlinx.android.synthetic.main.fragment_settings_pushgateways.*
|
||||
import im.vector.riotredesign.core.resources.StringProvider
|
||||
import im.vector.riotredesign.core.ui.list.genericFooterItem
|
||||
import kotlinx.android.synthetic.main.fragment_generic_recycler_epoxy.*
|
||||
|
||||
class PushGatewaysFragment : VectorBaseFragment() {
|
||||
|
||||
override fun getLayoutResId(): Int = R.layout.fragment_settings_pushgateways
|
||||
override fun getLayoutResId(): Int = R.layout.fragment_generic_recycler_epoxy
|
||||
|
||||
private val viewModel: PushGatewaysViewModel by fragmentViewModel(PushGatewaysViewModel::class)
|
||||
|
||||
private val epoxyController by lazy { PushGateWayController() }
|
||||
private val epoxyController by lazy { PushGateWayController(StringProvider(requireContext().resources)) }
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
(activity as? VectorBaseActivity)?.supportActionBar?.setTitle(R.string.settings_push_gateways)
|
||||
(activity as? VectorBaseActivity)?.supportActionBar?.setTitle(R.string.settings_notifications_targets)
|
||||
}
|
||||
|
||||
override fun onActivityCreated(savedInstanceState: Bundle?) {
|
||||
@ -56,13 +57,26 @@ class PushGatewaysFragment : VectorBaseFragment() {
|
||||
epoxyController.setData(it)
|
||||
}
|
||||
|
||||
class PushGateWayController : TypedEpoxyController<PushGatewayViewState>() {
|
||||
class PushGateWayController(private val stringProvider: StringProvider) : TypedEpoxyController<PushGatewayViewState>() {
|
||||
override fun buildModels(data: PushGatewayViewState?) {
|
||||
val pushers = data?.pushgateways?.invoke() ?: return
|
||||
pushers.forEach {
|
||||
pushGatewayItem {
|
||||
id("${it.pushKey}_${it.appId}")
|
||||
pusher(it)
|
||||
data?.pushgateways?.invoke()?.let { pushers ->
|
||||
if (pushers.isEmpty()) {
|
||||
genericFooterItem {
|
||||
id("footer")
|
||||
text(stringProvider.getString(R.string.settings_push_gateway_no_pushers))
|
||||
}
|
||||
} else {
|
||||
pushers.forEach {
|
||||
pushGatewayItem {
|
||||
id("${it.pushKey}_${it.appId}")
|
||||
pusher(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
} ?: run {
|
||||
genericFooterItem {
|
||||
id("footer")
|
||||
text(stringProvider.getString(R.string.loading))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,71 @@
|
||||
package im.vector.riotredesign.features.settings.push
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.graphics.Color
|
||||
import android.widget.ImageView
|
||||
import android.widget.TextView
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.view.isInvisible
|
||||
import androidx.core.view.isVisible
|
||||
import com.airbnb.epoxy.EpoxyAttribute
|
||||
import com.airbnb.epoxy.EpoxyModelClass
|
||||
import com.airbnb.epoxy.EpoxyModelWithHolder
|
||||
import im.vector.matrix.android.api.pushrules.Action
|
||||
import im.vector.matrix.android.api.pushrules.rest.PushRule
|
||||
import im.vector.riotredesign.R
|
||||
import im.vector.riotredesign.core.epoxy.VectorEpoxyHolder
|
||||
import im.vector.riotredesign.features.notifications.NotificationAction
|
||||
|
||||
|
||||
@EpoxyModelClass(layout = R.layout.item_pushrule_raw)
|
||||
abstract class PushRuleItem : EpoxyModelWithHolder<PushRuleItem.Holder>() {
|
||||
|
||||
@EpoxyAttribute
|
||||
lateinit var pushRule: PushRule
|
||||
|
||||
@SuppressLint("SetTextI18n")
|
||||
override fun bind(holder: Holder) {
|
||||
val context = holder.view.context
|
||||
if (pushRule.enabled) {
|
||||
holder.view.setBackgroundColor(Color.TRANSPARENT)
|
||||
holder.ruleId.text = pushRule.ruleId
|
||||
} else {
|
||||
holder.view.setBackgroundColor(ContextCompat.getColor(context, R.color.vector_silver_color))
|
||||
holder.ruleId.text = "[Disabled] ${pushRule.ruleId}"
|
||||
}
|
||||
val actions = Action.mapFrom(pushRule)
|
||||
if (actions.isNullOrEmpty()) {
|
||||
holder.actionIcon.isInvisible = true
|
||||
} else {
|
||||
holder.actionIcon.isVisible = true
|
||||
val notifAction = NotificationAction.extractFrom(actions)
|
||||
|
||||
if (notifAction.shouldNotify && !notifAction.soundName.isNullOrBlank()) {
|
||||
holder.actionIcon.setImageDrawable(ContextCompat.getDrawable(context, R.drawable.ic_action_notify_noisy))
|
||||
} else if (notifAction.shouldNotify) {
|
||||
holder.actionIcon.setImageDrawable(ContextCompat.getDrawable(context, R.drawable.ic_action_notify_silent))
|
||||
} else {
|
||||
holder.actionIcon.setImageDrawable(ContextCompat.getDrawable(context, R.drawable.ic_action_dont_notify))
|
||||
}
|
||||
|
||||
var description = StringBuffer()
|
||||
pushRule.conditions?.forEachIndexed { i, condition ->
|
||||
if (i > 0) description.append("\n")
|
||||
description.append(condition.asExecutableCondition()?.technicalDescription()
|
||||
?: "UNSUPPORTED")
|
||||
}
|
||||
if (description.isBlank()) {
|
||||
holder.description.text = "No Conditions"
|
||||
} else {
|
||||
holder.description.text = description
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
class Holder : VectorEpoxyHolder() {
|
||||
val ruleId by bind<TextView>(R.id.pushRuleId)
|
||||
val description by bind<TextView>(R.id.pushRuleDescription)
|
||||
val actionIcon by bind<ImageView>(R.id.pushRuleActionIcon)
|
||||
}
|
||||
}
|
@ -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.riotredesign.features.settings.push
|
||||
|
||||
import android.os.Bundle
|
||||
import androidx.recyclerview.widget.DividerItemDecoration
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.airbnb.epoxy.TypedEpoxyController
|
||||
import com.airbnb.mvrx.fragmentViewModel
|
||||
import com.airbnb.mvrx.withState
|
||||
import im.vector.riotredesign.R
|
||||
import im.vector.riotredesign.core.platform.VectorBaseActivity
|
||||
import im.vector.riotredesign.core.platform.VectorBaseFragment
|
||||
import im.vector.riotredesign.core.resources.StringProvider
|
||||
import im.vector.riotredesign.core.ui.list.genericFooterItem
|
||||
import kotlinx.android.synthetic.main.fragment_generic_recycler_epoxy.*
|
||||
|
||||
|
||||
class PushRulesFragment : VectorBaseFragment() {
|
||||
|
||||
override fun getLayoutResId(): Int = R.layout.fragment_generic_recycler_epoxy
|
||||
|
||||
private val viewModel: PushRulesViewModel by fragmentViewModel(PushRulesViewModel::class)
|
||||
|
||||
private val epoxyController by lazy { PushRulesFragment.PushRulesController(StringProvider(requireContext().resources)) }
|
||||
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
(activity as? VectorBaseActivity)?.supportActionBar?.setTitle(R.string.settings_push_rules)
|
||||
}
|
||||
|
||||
override fun onActivityCreated(savedInstanceState: Bundle?) {
|
||||
super.onActivityCreated(savedInstanceState)
|
||||
val lmgr = LinearLayoutManager(context, RecyclerView.VERTICAL, false)
|
||||
epoxyRecyclerView.layoutManager = lmgr
|
||||
val dividerItemDecoration = DividerItemDecoration(epoxyRecyclerView.context,
|
||||
lmgr.orientation)
|
||||
epoxyRecyclerView.addItemDecoration(dividerItemDecoration)
|
||||
epoxyRecyclerView.adapter = epoxyController.adapter
|
||||
}
|
||||
|
||||
override fun invalidate() = withState(viewModel) {
|
||||
epoxyController.setData(it)
|
||||
}
|
||||
|
||||
class PushRulesController(private val stringProvider: StringProvider) : TypedEpoxyController<PushRulesViewState>() {
|
||||
|
||||
override fun buildModels(data: PushRulesViewState?) {
|
||||
data?.let {
|
||||
it.rules.forEach {
|
||||
pushRuleItem {
|
||||
id(it.ruleId)
|
||||
pushRule(it)
|
||||
}
|
||||
}
|
||||
} ?: run {
|
||||
genericFooterItem {
|
||||
id("footer")
|
||||
text(stringProvider.getString(R.string.settings_push_rules_no_rules))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -0,0 +1,43 @@
|
||||
/*
|
||||
* 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.riotredesign.features.settings.push
|
||||
|
||||
import com.airbnb.mvrx.MvRxState
|
||||
import com.airbnb.mvrx.MvRxViewModelFactory
|
||||
import com.airbnb.mvrx.ViewModelContext
|
||||
import im.vector.matrix.android.api.pushrules.rest.PushRule
|
||||
import im.vector.matrix.android.api.session.Session
|
||||
import im.vector.riotredesign.core.platform.VectorViewModel
|
||||
import org.koin.android.ext.android.get
|
||||
|
||||
data class PushRulesViewState(
|
||||
val rules: List<PushRule> = emptyList())
|
||||
: MvRxState
|
||||
|
||||
|
||||
class PushRulesViewModel(initialState: PushRulesViewState) : VectorViewModel<PushRulesViewState>(initialState) {
|
||||
|
||||
companion object : MvRxViewModelFactory<PushRulesViewModel, PushRulesViewState> {
|
||||
|
||||
override fun initialState(viewModelContext: ViewModelContext): PushRulesViewState? {
|
||||
val session = viewModelContext.activity.get<Session>()
|
||||
val rules = session.getPushrules()
|
||||
|
||||
return PushRulesViewState(rules)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
25
vector/src/main/res/drawable/ic_action_dont_notify.xml
Normal file
25
vector/src/main/res/drawable/ic_action_dont_notify.xml
Normal file
@ -0,0 +1,25 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="34dp"
|
||||
android:height="34dp"
|
||||
android:viewportWidth="34"
|
||||
android:viewportHeight="34">
|
||||
<path
|
||||
android:pathData="M9.7169,15L9.7169,20.2316L7.5734,23.7393C7.5254,23.8178 7.5,23.908 7.5,24C7.5,24.2761 7.7239,24.5 8,24.5L26.4339,24.5C26.5259,24.5 26.6161,24.4746 26.6946,24.4266C26.9302,24.2826 27.0045,23.9749 26.8605,23.7393L24.7169,20.2316L24.7169,15C24.7169,11.558 22.3785,8.582 19.0923,7.7363L18.7169,7.6396L18.7169,6C18.7169,5.1716 18.0454,4.5 17.2169,4.5C16.3885,4.5 15.7169,5.1716 15.7169,6L15.7169,7.6396L15.3416,7.7363C12.0554,8.582 9.7169,11.558 9.7169,15ZM14.797,26.5C15.0741,27.3506 16.0402,28 17.2169,28C18.3937,28 19.3598,27.3506 19.6369,26.5L14.797,26.5Z"
|
||||
android:strokeWidth="1"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#224955"
|
||||
android:fillType="evenOdd"/>
|
||||
<path
|
||||
android:pathData="M17,17m-16,0a16,16 0,1 1,32 0a16,16 0,1 1,-32 0"
|
||||
android:strokeWidth="1"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#224955"
|
||||
android:fillType="evenOdd"/>
|
||||
<path
|
||||
android:pathData="M4.5,27.25L28.2169,6"
|
||||
android:strokeWidth="1"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#224955"
|
||||
android:fillType="evenOdd"
|
||||
android:strokeLineCap="square"/>
|
||||
</vector>
|
18
vector/src/main/res/drawable/ic_action_notify_noisy.xml
Normal file
18
vector/src/main/res/drawable/ic_action_notify_noisy.xml
Normal file
@ -0,0 +1,18 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="21dp"
|
||||
android:height="25dp"
|
||||
android:viewportWidth="21"
|
||||
android:viewportHeight="25">
|
||||
<path
|
||||
android:pathData="M2.7169,11L2.7169,16.2316L0.5734,19.7393C0.5254,19.8178 0.5,19.908 0.5,20C0.5,20.2761 0.7239,20.5 1,20.5L19.4339,20.5C19.5259,20.5 19.6161,20.4746 19.6946,20.4266C19.9302,20.2826 20.0045,19.9749 19.8605,19.7393L17.7169,16.2316L17.7169,11C17.7169,7.558 15.3785,4.582 12.0923,3.7363L11.7169,3.6396L11.7169,2C11.7169,1.1716 11.0454,0.5 10.2169,0.5C9.3885,0.5 8.7169,1.1716 8.7169,2L8.7169,3.6396L8.3416,3.7363C5.0554,4.582 2.7169,7.558 2.7169,11ZM7.797,22.5C8.0741,23.3506 9.0402,24 10.2169,24C11.3937,24 12.3598,23.3506 12.6369,22.5L7.797,22.5Z"
|
||||
android:strokeWidth="1"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#224955"
|
||||
android:fillType="evenOdd"/>
|
||||
<path
|
||||
android:pathData="M16,7m-4,0a4,4 0,1 1,8 0a4,4 0,1 1,-8 0"
|
||||
android:strokeWidth="1"
|
||||
android:fillColor="#E8707B"
|
||||
android:fillType="evenOdd"
|
||||
android:strokeColor="#00000000"/>
|
||||
</vector>
|
24
vector/src/main/res/drawable/ic_action_notify_silent.xml
Normal file
24
vector/src/main/res/drawable/ic_action_notify_silent.xml
Normal file
@ -0,0 +1,24 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="23dp"
|
||||
android:height="26dp"
|
||||
android:viewportWidth="23"
|
||||
android:viewportHeight="26">
|
||||
<path
|
||||
android:pathData="M2.7169,12L2.7169,17.2316L0.5734,20.7393C0.5254,20.8178 0.5,20.908 0.5,21C0.5,21.2761 0.7239,21.5 1,21.5L19.4339,21.5C19.5259,21.5 19.6161,21.4746 19.6946,21.4266C19.9302,21.2826 20.0045,20.9749 19.8605,20.7393L17.7169,17.2316L17.7169,12C17.7169,8.558 15.3785,5.582 12.0923,4.7363L11.7169,4.6396L11.7169,3C11.7169,2.1716 11.0454,1.5 10.2169,1.5C9.3885,1.5 8.7169,2.1716 8.7169,3L8.7169,4.6396L8.3416,4.7363C5.0554,5.582 2.7169,8.558 2.7169,12ZM7.797,23.5C8.0741,24.3506 9.0402,25 10.2169,25C11.3937,25 12.3598,24.3506 12.6369,23.5L7.797,23.5Z"
|
||||
android:strokeWidth="1"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#224955"
|
||||
android:fillType="evenOdd"/>
|
||||
<path
|
||||
android:pathData="M17.5522,4.7158l1.1133,0l0,0.2842l-1.5366,0l0,-0.2227l1.0708,-1.6245l-1.062,0l0,-0.2856l1.4912,0l0,0.2139z"
|
||||
android:strokeWidth="1"
|
||||
android:fillColor="#4A4A4A"
|
||||
android:fillType="evenOdd"
|
||||
android:strokeColor="#00000000"/>
|
||||
<path
|
||||
android:pathData="M20.1045,4.4316l2.2266,0l0,0.5684l-3.0732,0l0,-0.4453l2.1416,-3.249l-2.124,0l0,-0.5713l2.9824,0l0,0.4277z"
|
||||
android:strokeWidth="1"
|
||||
android:fillColor="#4A4A4A"
|
||||
android:fillType="evenOdd"
|
||||
android:strokeColor="#00000000"/>
|
||||
</vector>
|
16
vector/src/main/res/layout/item_generic_footer.xml
Normal file
16
vector/src/main/res/layout/item_generic_footer.xml
Normal file
@ -0,0 +1,16 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:padding="16dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/itemGenericFooterText"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center"
|
||||
tools:text="Empty list, nothing here" />
|
||||
|
||||
</LinearLayout>
|
43
vector/src/main/res/layout/item_pushrule_raw.xml
Normal file
43
vector/src/main/res/layout/item_pushrule_raw.xml
Normal file
@ -0,0 +1,43 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:padding="8dp">
|
||||
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:layout_marginRight="8dp"
|
||||
android:layout_weight="1"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/pushRuleId"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="4dp"
|
||||
tools:text=".m.rule.contains_user_name"
|
||||
android:textStyle="bold" />
|
||||
|
||||
|
||||
<TextView
|
||||
android:id="@+id/pushRuleDescription"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="16dp"
|
||||
android:textStyle=""
|
||||
tools:text="content matches valere" />
|
||||
</LinearLayout>
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/pushRuleActionIcon"
|
||||
android:layout_width="30dp"
|
||||
android:layout_height="30dp"
|
||||
android:layout_gravity="center_vertical"
|
||||
tools:src="@drawable/ic_action_dont_notify" />
|
||||
|
||||
</LinearLayout>
|
@ -17,6 +17,8 @@
|
||||
<string name="settings_preferences">Preferences</string>
|
||||
<string name="settings_security_and_privacy">Security & Privacy</string>
|
||||
<string name="settings_expert">Expert</string>
|
||||
<string name="settings_push_gateways">Push Gateways</string>
|
||||
<string name="settings_push_rules">Push Rules</string>
|
||||
<string name="settings_push_rules_no_rules">No push rules defined</string>
|
||||
<string name="settings_push_gateway_no_pushers">No registered push gateways</string>
|
||||
|
||||
</resources>
|
@ -2,14 +2,15 @@
|
||||
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
|
||||
<SwitchPreference
|
||||
android:key="SETTINGS_ENABLE_ALL_NOTIF_PREFERENCE_KEY"
|
||||
android:title="@string/settings_enable_all_notif" />
|
||||
<!--<SwitchPreference-->
|
||||
<!--android:key="SETTINGS_ENABLE_ALL_NOTIF_PREFERENCE_KEY"-->
|
||||
<!--android:title="@string/settings_enable_all_notif" />-->
|
||||
|
||||
<SwitchPreference
|
||||
android:dependency="SETTINGS_ENABLE_ALL_NOTIF_PREFERENCE_KEY"
|
||||
|
||||
android:key="SETTINGS_ENABLE_THIS_DEVICE_PREFERENCE_KEY"
|
||||
android:title="@string/settings_enable_this_device" />
|
||||
<!--android:dependency="SETTINGS_ENABLE_ALL_NOTIF_PREFERENCE_KEY"-->
|
||||
|
||||
<!--<im.vector.riotredesign.core.preference.VectorSwitchPreference-->
|
||||
<!--android:dependency="SETTINGS_ENABLE_ALL_NOTIF_PREFERENCE_KEY"-->
|
||||
@ -27,6 +28,7 @@
|
||||
android:persistent="false"
|
||||
android:summary="@string/settings_notification_advanced_summary"
|
||||
android:title="@string/settings_notification_advanced"
|
||||
android:enabled="false"
|
||||
app:fragment="im.vector.fragments.VectorSettingsNotificationsAdvancedFragment" />
|
||||
|
||||
<Preference
|
||||
@ -38,8 +40,15 @@
|
||||
|
||||
<Preference
|
||||
android:layout_width="match_parent"
|
||||
android:title="@string/settings_push_gateways"
|
||||
android:title="@string/settings_notifications_targets"
|
||||
android:persistent="false"
|
||||
app:fragment="im.vector.riotredesign.features.settings.push.PushGatewaysFragment" />
|
||||
|
||||
<Preference
|
||||
android:layout_width="match_parent"
|
||||
android:title="@string/settings_push_rules"
|
||||
android:persistent="false"
|
||||
app:fragment="im.vector.riotredesign.features.settings.push.PushRulesFragment" />
|
||||
|
||||
</PreferenceCategory>
|
||||
</PreferenceScreen>
|
Loading…
Reference in New Issue
Block a user