From 3013e311a46988418643dfab327d340c8201818c Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 28 Feb 2020 16:45:46 +0100 Subject: [PATCH] Restore push rule settings - WIP --- .../android/api/pushrules/rest/PushRule.kt | 115 ++++++++++++- .../android/api/pushrules/rest/RuleSet.kt | 40 +++++ .../core/preference/BingRulePreference.kt | 160 +++++++----------- ...sAdvancedNotificationPreferenceFragment.kt | 27 +-- vector/src/main/res/values/strings_riotX.xml | 2 +- ...ings_notification_advanced_preferences.xml | 49 +++--- .../res/xml/vector_settings_notifications.xml | 4 +- 7 files changed, 262 insertions(+), 135 deletions(-) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/pushrules/rest/PushRule.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/pushrules/rest/PushRule.kt index d746aed5d8..d60fe2e9c4 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/pushrules/rest/PushRule.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/pushrules/rest/PushRule.kt @@ -18,6 +18,9 @@ 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.Action +import im.vector.matrix.android.api.pushrules.getActions +import im.vector.matrix.android.api.pushrules.toJson /** * Ref: https://matrix.org/docs/spec/client_server/latest#get-matrix-client-r0-pushrules @@ -54,4 +57,114 @@ data class PushRule( */ @Json(name = "pattern") val pattern: String? = null -) +) { + /** + * Add the default notification sound. + */ + fun setNotificationSound(): PushRule { + return setNotificationSound(ACTION_VALUE_DEFAULT) + } + + fun getNotificationSound(): String? { + return (getActions().firstOrNull { it is Action.Sound } as? Action.Sound)?.sound + } + + /** + * Set the notification sound + * + * @param sound notification sound + */ + fun setNotificationSound(sound: String): PushRule { + return copy( + actions = (getActions().filter { it !is Action.Sound } + Action.Sound(sound)).toJson() + ) + } + + /** + * Remove the notification sound + */ + fun removeNotificationSound(): PushRule { + return copy( + actions = (getActions().filter { it !is Action.Sound }).toJson() + ) + } + + /** + * Set the highlight status. + * + * @param highlight the highlight status + */ + fun setHighlight(highlight: Boolean): PushRule { + return copy( + actions = (getActions().filter { it !is Action.Highlight } + Action.Highlight(highlight)).toJson() + ) + } + + /** + * Set the notification status. + * + * @param notify true to notify + */ + fun setNotify(notify: Boolean): PushRule { + val mutableActions = actions.toMutableList() + + mutableActions.remove(ACTION_DONT_NOTIFY) + mutableActions.remove(ACTION_NOTIFY) + + if (notify) { + mutableActions.add(ACTION_NOTIFY) + } else { + mutableActions.add(ACTION_DONT_NOTIFY) + } + + return copy(actions = mutableActions) + } + + /** + * Return true if the rule should highlight the event. + * + * @return true if the rule should play sound + */ + fun shouldNotify() = actions.contains(ACTION_NOTIFY) + + /** + * Return true if the rule should not highlight the event. + * + * @return true if the rule should not play sound + */ + fun shouldNotNotify() = actions.contains(ACTION_DONT_NOTIFY) + + companion object { + /* ========================================================================================== + * Rule id + * ========================================================================================== */ + + const val RULE_ID_DISABLE_ALL = ".m.rule.master" + const val RULE_ID_CONTAIN_USER_NAME = ".m.rule.contains_user_name" + const val RULE_ID_CONTAIN_DISPLAY_NAME = ".m.rule.contains_display_name" + const val RULE_ID_ONE_TO_ONE_ROOM = ".m.rule.room_one_to_one" + const val RULE_ID_INVITE_ME = ".m.rule.invite_for_me" + const val RULE_ID_PEOPLE_JOIN_LEAVE = ".m.rule.member_event" + const val RULE_ID_CALL = ".m.rule.call" + const val RULE_ID_SUPPRESS_BOTS_NOTIFICATIONS = ".m.rule.suppress_notices" + const val RULE_ID_ALL_OTHER_MESSAGES_ROOMS = ".m.rule.message" + const val RULE_ID_FALLBACK = ".m.rule.fallback" + + /* ========================================================================================== + * Actions + * ========================================================================================== */ + + const val ACTION_NOTIFY = "notify" + const val ACTION_DONT_NOTIFY = "dont_notify" + const val ACTION_COALESCE = "coalesce" + + const val ACTION_SET_TWEAK_SOUND_VALUE = "sound" + const val ACTION_SET_TWEAK_HIGHLIGHT_VALUE = "highlight" + + const val ACTION_PARAMETER_SET_TWEAK = "set_tweak" + const val ACTION_PARAMETER_VALUE = "value" + + const val ACTION_VALUE_DEFAULT = "default" + const val ACTION_VALUE_RING = "ring" + } +} diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/pushrules/rest/RuleSet.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/pushrules/rest/RuleSet.kt index 43b2599b52..28f680b12b 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/pushrules/rest/RuleSet.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/pushrules/rest/RuleSet.kt @@ -17,6 +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.RuleSetKey /** * Ref: https://matrix.org/docs/spec/client_server/latest#get-matrix-client-r0-pushrules @@ -38,4 +39,43 @@ data class RuleSet( // Ref. for the order: https://matrix.org/docs/spec/client_server/latest#push-rules return override.orEmpty() + content.orEmpty() + room.orEmpty() + sender.orEmpty() + underride.orEmpty() } + + /** + * Find a rule from its ruleID. + * + * @param ruleId a RULE_ID_XX value + * @return the matched bing rule or null it doesn't exist. + */ + fun findDefaultRule(ruleId: String?): PushRuleAndKind? { + var result: PushRuleAndKind? = null + // sanity check + if (null != ruleId) { + if (PushRule.RULE_ID_CONTAIN_USER_NAME == ruleId) { + result = findRule(content, ruleId)?.let { PushRuleAndKind(it, RuleSetKey.CONTENT) } + } else { + // assume that the ruleId is unique. + result = findRule(override, ruleId)?.let { PushRuleAndKind(it, RuleSetKey.OVERRIDE) } + if (null == result) { + result = findRule(underride, ruleId)?.let { PushRuleAndKind(it, RuleSetKey.UNDERRIDE) } + } + } + } + return result + } + + /** + * Find a rule from its rule Id. + * + * @param rules the rules list. + * @param ruleId the rule Id. + * @return the bing rule if it exists, else null. + */ + private fun findRule(rules: List?, ruleId: String): PushRule? { + return rules?.firstOrNull { it.ruleId == ruleId } + } } + +data class PushRuleAndKind( + val pushRule: PushRule, + val kind: RuleSetKey +) diff --git a/vector/src/main/java/im/vector/riotx/core/preference/BingRulePreference.kt b/vector/src/main/java/im/vector/riotx/core/preference/BingRulePreference.kt index 76df61dd33..2cb363621f 100755 --- a/vector/src/main/java/im/vector/riotx/core/preference/BingRulePreference.kt +++ b/vector/src/main/java/im/vector/riotx/core/preference/BingRulePreference.kt @@ -22,49 +22,17 @@ import android.view.View import android.widget.RadioGroup import android.widget.TextView import androidx.preference.PreferenceViewHolder +import im.vector.matrix.android.api.pushrules.RuleSetKey +import im.vector.matrix.android.api.pushrules.rest.PushRule +import im.vector.matrix.android.api.pushrules.rest.PushRuleAndKind import im.vector.riotx.R -// TODO Replace by real Bingrule class, then delete -@Suppress("UNUSED_PARAMETER") -class BingRule(rule: BingRule) { - fun shouldNotNotify() = false - fun shouldNotify() = false - fun setNotify(b: Boolean) { - } - - fun setHighlight(b: Boolean) { - } - - fun removeNotificationSound() { - } - - val ruleId: CharSequence? = null - var isEnabled = false - var notificationSound: String? = null - val kind: CharSequence? = null - - companion object { - const val RULE_ID_SUPPRESS_BOTS_NOTIFICATIONS = "TODO" - const val ACTION_VALUE_DEFAULT = "TODO" - const val KIND_UNDERRIDE = "TODO" - const val RULE_ID_INVITE_ME = "TODO" - const val RULE_ID_CALL = "TODO" - const val ACTION_VALUE_RING = "TODO" - const val RULE_ID_DISABLE_ALL = "TODO" - const val ACTION_DONT_NOTIFY = "TODO" - const val RULE_ID_CONTAIN_DISPLAY_NAME = "TODO" - const val RULE_ID_CONTAIN_USER_NAME = "TODO" - const val RULE_ID_ONE_TO_ONE_ROOM = "TODO" - const val RULE_ID_ALL_OTHER_MESSAGES_ROOMS = "TODO" - } -} - class BingRulePreference : VectorPreference { /** - * @return the selected bing rule + * @return the selected push rule and its kind */ - var rule: BingRule? = null + var ruleAndKind: PushRuleAndKind? = null private set constructor(context: Context) : super(context) @@ -80,29 +48,29 @@ class BingRulePreference : VectorPreference { /** * @return the bing rule status index */ - val ruleStatusIndex: Int + private val ruleStatusIndex: Int get() { - if (null != rule) { - if (rule!!.ruleId == BingRule.RULE_ID_SUPPRESS_BOTS_NOTIFICATIONS) { - if (rule!!.shouldNotNotify()) { - return if (rule!!.isEnabled) { - NOTIFICATION_OFF_INDEX - } else { - NOTIFICATION_SILENT_INDEX - } - } else if (rule!!.shouldNotify()) { - return NOTIFICATION_NOISY_INDEX - } - } + val safeRule = ruleAndKind?.pushRule ?: return NOTIFICATION_OFF_INDEX - if (rule!!.isEnabled) { - return if (rule!!.shouldNotNotify()) { + if (safeRule.ruleId == PushRule.RULE_ID_SUPPRESS_BOTS_NOTIFICATIONS) { + if (safeRule.shouldNotNotify()) { + return if (safeRule.enabled) { NOTIFICATION_OFF_INDEX - } else if (null != rule!!.notificationSound) { - NOTIFICATION_NOISY_INDEX } else { NOTIFICATION_SILENT_INDEX } + } else if (safeRule.shouldNotify()) { + return NOTIFICATION_NOISY_INDEX + } + } + + if (safeRule.enabled) { + return if (safeRule.shouldNotNotify()) { + NOTIFICATION_OFF_INDEX + } else if (null != safeRule.getNotificationSound()) { + NOTIFICATION_NOISY_INDEX + } else { + NOTIFICATION_SILENT_INDEX } } @@ -110,12 +78,12 @@ class BingRulePreference : VectorPreference { } /** - * Update the bing rule. + * Update the push rule. * - * @param aBingRule + * @param pushRule */ - fun setBingRule(aBingRule: BingRule) { - rule = aBingRule + fun setPushRule(pushRuleAndKind: PushRuleAndKind?) { + ruleAndKind = pushRuleAndKind refreshSummary() } @@ -131,63 +99,63 @@ class BingRulePreference : VectorPreference { } /** - * Create a bing rule with the updated required at index. + * Create a push rule with the updated required at index. * * @param index index - * @return a bing rule with the updated flags / null if there is no update + * @return a push rule with the updated flags / null if there is no update */ - fun createRule(index: Int): BingRule? { - var rule: BingRule? = null + fun createRule(index: Int): PushRule? { + val safeRule = ruleAndKind?.pushRule ?: return null + val safeKind = ruleAndKind?.kind ?: return null - if (null != this.rule && index != ruleStatusIndex) { - rule = BingRule(this.rule!!) - - if (rule.ruleId == BingRule.RULE_ID_SUPPRESS_BOTS_NOTIFICATIONS) { + return if (index != ruleStatusIndex) { + if (safeRule.ruleId == PushRule.RULE_ID_SUPPRESS_BOTS_NOTIFICATIONS) { when (index) { NOTIFICATION_OFF_INDEX -> { - rule.isEnabled = true - rule.setNotify(false) + safeRule.copy(enabled = true) + .setNotify(false) } NOTIFICATION_SILENT_INDEX -> { - rule.isEnabled = false - rule.setNotify(false) + safeRule.copy(enabled = false) + .setNotify(false) } NOTIFICATION_NOISY_INDEX -> { - rule.isEnabled = true - rule.setNotify(true) - rule.notificationSound = BingRule.ACTION_VALUE_DEFAULT + safeRule.copy(enabled = true) + .setNotify(true) + .setNotificationSound() } - } - - return rule - } - - if (NOTIFICATION_OFF_INDEX == index) { - if (this.rule!!.kind == BingRule.KIND_UNDERRIDE - || rule.ruleId == BingRule.RULE_ID_SUPPRESS_BOTS_NOTIFICATIONS) { - rule.setNotify(false) - } else { - rule.isEnabled = false + else -> safeRule } } else { - rule.isEnabled = true - rule.setNotify(true) - rule.setHighlight(this.rule!!.kind != BingRule.KIND_UNDERRIDE - && rule.ruleId != BingRule.RULE_ID_INVITE_ME - && NOTIFICATION_NOISY_INDEX == index) - if (NOTIFICATION_NOISY_INDEX == index) { - rule.notificationSound = if (rule.ruleId == BingRule.RULE_ID_CALL) { - BingRule.ACTION_VALUE_RING + if (NOTIFICATION_OFF_INDEX == index) { + if (safeKind == RuleSetKey.UNDERRIDE || safeRule.ruleId == PushRule.RULE_ID_SUPPRESS_BOTS_NOTIFICATIONS) { + safeRule.setNotify(false) } else { - BingRule.ACTION_VALUE_DEFAULT + safeRule.copy(enabled = false) } } else { - rule.removeNotificationSound() + val newRule = safeRule.copy(enabled = true) + .setNotify(true) + .setHighlight(safeKind != RuleSetKey.UNDERRIDE + && safeRule.ruleId != PushRule.RULE_ID_INVITE_ME + && NOTIFICATION_NOISY_INDEX == index) + + if (NOTIFICATION_NOISY_INDEX == index) { + newRule.setNotificationSound( + if (safeRule.ruleId == PushRule.RULE_ID_CALL) { + PushRule.ACTION_VALUE_RING + } else { + PushRule.ACTION_VALUE_DEFAULT + } + ) + } else { + newRule.removeNotificationSound() + } } } + } else { + safeRule } - - return rule } override fun onBindViewHolder(holder: PreferenceViewHolder) { diff --git a/vector/src/main/java/im/vector/riotx/features/settings/VectorSettingsAdvancedNotificationPreferenceFragment.kt b/vector/src/main/java/im/vector/riotx/features/settings/VectorSettingsAdvancedNotificationPreferenceFragment.kt index a8328fae52..3ecd08953c 100644 --- a/vector/src/main/java/im/vector/riotx/features/settings/VectorSettingsAdvancedNotificationPreferenceFragment.kt +++ b/vector/src/main/java/im/vector/riotx/features/settings/VectorSettingsAdvancedNotificationPreferenceFragment.kt @@ -23,8 +23,9 @@ import android.os.Parcelable import androidx.core.content.edit import androidx.preference.Preference import androidx.preference.PreferenceManager +import im.vector.matrix.android.api.pushrules.rest.PushRule +import im.vector.matrix.android.api.pushrules.rest.PushRuleAndKind import im.vector.riotx.R -import im.vector.riotx.core.preference.BingRule import im.vector.riotx.core.preference.BingRulePreference import im.vector.riotx.core.preference.VectorPreference import im.vector.riotx.features.notifications.NotificationUtils @@ -102,14 +103,14 @@ class VectorSettingsAdvancedNotificationPreferenceFragment @Inject constructor( val preference = findPreference(preferenceKey) if (preference is BingRulePreference) { // preference.isEnabled = null != rules && isConnected && pushManager.areDeviceNotificationsAllowed() - val rule: BingRule? = null // TODO session.dataHandler.pushRules()?.findDefaultRule(mPrefKeyToBingRuleId[preferenceKey]) + val ruleAndKind: PushRuleAndKind? = session.getPushRules().findDefaultRule(mPrefKeyToBingRuleId[preferenceKey]) - if (rule == null) { + if (ruleAndKind == null) { // The rule is not defined, hide the preference preference.isVisible = false } else { preference.isVisible = true - preference.setBingRule(rule) + preference.setPushRule(ruleAndKind) preference.onPreferenceChangeListener = Preference.OnPreferenceChangeListener { _, newValue -> val rule2 = preference.createRule(newValue as Int) if (null != rule2) { @@ -181,7 +182,7 @@ class VectorSettingsAdvancedNotificationPreferenceFragment @Inject constructor( val rule = it.findDefaultRule(ruleId) var isEnabled = null != rule && rule.isEnabled - if (TextUtils.equals(ruleId, BingRule.RULE_ID_DISABLE_ALL) || TextUtils.equals(ruleId, BingRule.RULE_ID_SUPPRESS_BOTS_NOTIFICATIONS)) { + if (TextUtils.equals(ruleId, PushRule.RULE_ID_DISABLE_ALL) || TextUtils.equals(ruleId, PushRule.RULE_ID_SUPPRESS_BOTS_NOTIFICATIONS)) { isEnabled = !isEnabled } else if (isEnabled) { val domainActions = rule!!.domainActions @@ -191,7 +192,7 @@ class VectorSettingsAdvancedNotificationPreferenceFragment @Inject constructor( isEnabled = false } else if (1 == domainActions.size) { try { - isEnabled = !TextUtils.equals(domainActions[0] as String, BingRule.ACTION_DONT_NOTIFY) + isEnabled = !TextUtils.equals(domainActions[0] as String, PushRule.ACTION_DONT_NOTIFY) } catch (e: Exception) { Timber.e(e, "## refreshPreferences failed") } @@ -216,13 +217,13 @@ class VectorSettingsAdvancedNotificationPreferenceFragment @Inject constructor( // preference name <-> rule Id private var mPrefKeyToBingRuleId = mapOf( - VectorPreferences.SETTINGS_CONTAINING_MY_DISPLAY_NAME_PREFERENCE_KEY to BingRule.RULE_ID_CONTAIN_DISPLAY_NAME, - VectorPreferences.SETTINGS_CONTAINING_MY_USER_NAME_PREFERENCE_KEY to BingRule.RULE_ID_CONTAIN_USER_NAME, - VectorPreferences.SETTINGS_MESSAGES_IN_ONE_TO_ONE_PREFERENCE_KEY to BingRule.RULE_ID_ONE_TO_ONE_ROOM, - VectorPreferences.SETTINGS_MESSAGES_IN_GROUP_CHAT_PREFERENCE_KEY to BingRule.RULE_ID_ALL_OTHER_MESSAGES_ROOMS, - VectorPreferences.SETTINGS_INVITED_TO_ROOM_PREFERENCE_KEY to BingRule.RULE_ID_INVITE_ME, - VectorPreferences.SETTINGS_CALL_INVITATIONS_PREFERENCE_KEY to BingRule.RULE_ID_CALL, - VectorPreferences.SETTINGS_MESSAGES_SENT_BY_BOT_PREFERENCE_KEY to BingRule.RULE_ID_SUPPRESS_BOTS_NOTIFICATIONS + VectorPreferences.SETTINGS_CONTAINING_MY_DISPLAY_NAME_PREFERENCE_KEY to PushRule.RULE_ID_CONTAIN_DISPLAY_NAME, + VectorPreferences.SETTINGS_CONTAINING_MY_USER_NAME_PREFERENCE_KEY to PushRule.RULE_ID_CONTAIN_USER_NAME, + VectorPreferences.SETTINGS_MESSAGES_IN_ONE_TO_ONE_PREFERENCE_KEY to PushRule.RULE_ID_ONE_TO_ONE_ROOM, + VectorPreferences.SETTINGS_MESSAGES_IN_GROUP_CHAT_PREFERENCE_KEY to PushRule.RULE_ID_ALL_OTHER_MESSAGES_ROOMS, + VectorPreferences.SETTINGS_INVITED_TO_ROOM_PREFERENCE_KEY to PushRule.RULE_ID_INVITE_ME, + VectorPreferences.SETTINGS_CALL_INVITATIONS_PREFERENCE_KEY to PushRule.RULE_ID_CALL, + VectorPreferences.SETTINGS_MESSAGES_SENT_BY_BOT_PREFERENCE_KEY to PushRule.RULE_ID_SUPPRESS_BOTS_NOTIFICATIONS ) } } diff --git a/vector/src/main/res/values/strings_riotX.xml b/vector/src/main/res/values/strings_riotX.xml index 00bf65e121..2d1ed60505 100644 --- a/vector/src/main/res/values/strings_riotX.xml +++ b/vector/src/main/res/values/strings_riotX.xml @@ -11,7 +11,7 @@ - + Notifications configuration diff --git a/vector/src/main/res/xml/vector_settings_notification_advanced_preferences.xml b/vector/src/main/res/xml/vector_settings_notification_advanced_preferences.xml index b5f01d98f6..8882a434eb 100644 --- a/vector/src/main/res/xml/vector_settings_notification_advanced_preferences.xml +++ b/vector/src/main/res/xml/vector_settings_notification_advanced_preferences.xml @@ -36,29 +36,36 @@ - - - - - + android:title="@string/settings_notification_configuration"> - + + - + + + + + + + + diff --git a/vector/src/main/res/xml/vector_settings_notifications.xml b/vector/src/main/res/xml/vector_settings_notifications.xml index 2114dba373..82c9cde759 100644 --- a/vector/src/main/res/xml/vector_settings_notifications.xml +++ b/vector/src/main/res/xml/vector_settings_notifications.xml @@ -24,13 +24,11 @@ + app:fragment="im.vector.riotx.features.settings.VectorSettingsAdvancedNotificationPreferenceFragment" />