diff --git a/vector/build.gradle b/vector/build.gradle index 8e8e5bc3dc..7003ab1834 100644 --- a/vector/build.gradle +++ b/vector/build.gradle @@ -142,6 +142,9 @@ android { resValue "bool", "useLoginV1", "true" resValue "bool", "useLoginV2", "false" + resValue "bool", "useNotificationSettingsV1", "true" + resValue "bool", "useNotificationSettingsV2", "false" + buildConfigField "im.vector.app.features.crypto.keysrequest.OutboundSessionKeySharingStrategy", "outboundSessionKeySharingStrategy", "im.vector.app.features.crypto.keysrequest.OutboundSessionKeySharingStrategy.WhenTyping" // If set, MSC3086 asserted identity messages sent on VoIP calls will cause the call to appear in the room corresponding to the asserted identity. diff --git a/vector/src/main/java/im/vector/app/core/di/FragmentModule.kt b/vector/src/main/java/im/vector/app/core/di/FragmentModule.kt index 7d50dbdcb5..7da26a3ea1 100644 --- a/vector/src/main/java/im/vector/app/core/di/FragmentModule.kt +++ b/vector/src/main/java/im/vector/app/core/di/FragmentModule.kt @@ -111,6 +111,7 @@ import im.vector.app.features.roomprofile.settings.RoomSettingsFragment import im.vector.app.features.roomprofile.uploads.RoomUploadsFragment import im.vector.app.features.roomprofile.uploads.files.RoomUploadsFilesFragment import im.vector.app.features.roomprofile.uploads.media.RoomUploadsMediaFragment +import im.vector.app.features.settings.VectorSettingsAdvancedNotificationPreferenceFragment import im.vector.app.features.settings.VectorSettingsGeneralFragment import im.vector.app.features.settings.VectorSettingsHelpAboutFragment import im.vector.app.features.settings.VectorSettingsLabsFragment @@ -384,6 +385,11 @@ interface FragmentModule { @FragmentKey(PushGatewaysFragment::class) fun bindPushGatewaysFragment(fragment: PushGatewaysFragment): Fragment + @Binds + @IntoMap + @FragmentKey(VectorSettingsAdvancedNotificationPreferenceFragment::class) + fun bindVectorSettingsAdvancedNotificationPreferenceFragment(fragment: VectorSettingsAdvancedNotificationPreferenceFragment): Fragment + @Binds @IntoMap @FragmentKey(VectorSettingsNotificationsTroubleshootFragment::class) diff --git a/vector/src/main/java/im/vector/app/core/preference/PushRulePreference.kt b/vector/src/main/java/im/vector/app/core/preference/PushRulePreference.kt new file mode 100644 index 0000000000..c3e324b64a --- /dev/null +++ b/vector/src/main/java/im/vector/app/core/preference/PushRulePreference.kt @@ -0,0 +1,207 @@ +/* + * Copyright 2018 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.app.core.preference + +import android.content.Context +import android.util.AttributeSet +import android.view.View +import android.widget.RadioGroup +import androidx.preference.PreferenceViewHolder +import im.vector.app.R +import org.matrix.android.sdk.api.pushrules.Action +import org.matrix.android.sdk.api.pushrules.RuleIds +import org.matrix.android.sdk.api.pushrules.RuleSetKey +import org.matrix.android.sdk.api.pushrules.rest.PushRule +import org.matrix.android.sdk.api.pushrules.rest.PushRuleAndKind + +class PushRulePreference : VectorPreference { + + /** + * @return the selected push rule and its kind + */ + var ruleAndKind: PushRuleAndKind? = null + private set + + constructor(context: Context) : super(context) + + constructor(context: Context, attrs: AttributeSet) : super(context, attrs) + + constructor(context: Context, attrs: AttributeSet, defStyle: Int) : super(context, attrs, defStyle) + + init { + layoutResource = R.layout.vector_preference_push_rule + } + + /** + * @return the bing rule status index + */ + private val ruleStatusIndex: Int + get() { + val safeRule = ruleAndKind?.pushRule ?: return NOTIFICATION_OFF_INDEX + + if (safeRule.ruleId == RuleIds.RULE_ID_SUPPRESS_BOTS_NOTIFICATIONS) { + if (safeRule.shouldNotNotify()) { + return if (safeRule.enabled) { + NOTIFICATION_OFF_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 (safeRule.getNotificationSound() != null) { + NOTIFICATION_NOISY_INDEX + } else { + NOTIFICATION_SILENT_INDEX + } + } + + return NOTIFICATION_OFF_INDEX + } + + /** + * Update the push rule. + * + * @param pushRule + */ + fun setPushRule(pushRuleAndKind: PushRuleAndKind?) { + ruleAndKind = pushRuleAndKind + refreshSummary() + } + + /** + * Refresh the summary + */ + private fun refreshSummary() { + summary = context.getString(when (ruleStatusIndex) { + NOTIFICATION_OFF_INDEX -> R.string.notification_off + NOTIFICATION_SILENT_INDEX -> R.string.notification_silent + else -> R.string.notification_noisy + }) + } + + /** + * Create a push rule with the updated required at index. + * + * @param index index + * @return a push rule with the updated flags / null if there is no update + */ + fun createNewRule(index: Int): PushRule? { + val safeRule = ruleAndKind?.pushRule ?: return null + val safeKind = ruleAndKind?.kind ?: return null + + return if (index != ruleStatusIndex) { + if (safeRule.ruleId == RuleIds.RULE_ID_SUPPRESS_BOTS_NOTIFICATIONS) { + when (index) { + NOTIFICATION_OFF_INDEX -> { + safeRule.copy(enabled = true) + .setNotify(false) + .removeNotificationSound() + } + NOTIFICATION_SILENT_INDEX -> { + safeRule.copy(enabled = false) + .setNotify(false) + } + NOTIFICATION_NOISY_INDEX -> { + safeRule.copy(enabled = true) + .setNotify(true) + .setNotificationSound() + } + else -> safeRule + } + } else { + if (NOTIFICATION_OFF_INDEX == index) { + if (safeKind == RuleSetKey.UNDERRIDE || safeRule.ruleId == RuleIds.RULE_ID_SUPPRESS_BOTS_NOTIFICATIONS) { + safeRule.setNotify(false) + } else { + safeRule.copy(enabled = false) + } + } else { + val newRule = safeRule.copy(enabled = true) + .setNotify(true) + .setHighlight(safeKind != RuleSetKey.UNDERRIDE + && safeRule.ruleId != RuleIds.RULE_ID_INVITE_ME + && NOTIFICATION_NOISY_INDEX == index) + + if (NOTIFICATION_NOISY_INDEX == index) { + newRule.setNotificationSound( + if (safeRule.ruleId == RuleIds.RULE_ID_CALL) { + Action.ACTION_OBJECT_VALUE_VALUE_RING + } else { + Action.ACTION_OBJECT_VALUE_VALUE_DEFAULT + } + ) + } else { + newRule.removeNotificationSound() + } + } + } + } else { + safeRule + } + } + + override fun onBindViewHolder(holder: PreferenceViewHolder) { + super.onBindViewHolder(holder) + + holder.findViewById(android.R.id.summary)?.visibility = View.GONE + holder.itemView.setOnClickListener(null) + holder.itemView.setOnLongClickListener(null) + + val radioGroup = holder.findViewById(R.id.bingPreferenceRadioGroup) as? RadioGroup + radioGroup?.setOnCheckedChangeListener(null) + + when (ruleStatusIndex) { + NOTIFICATION_OFF_INDEX -> { + radioGroup?.check(R.id.bingPreferenceRadioBingRuleOff) + } + NOTIFICATION_SILENT_INDEX -> { + radioGroup?.check(R.id.bingPreferenceRadioBingRuleSilent) + } + else -> { + radioGroup?.check(R.id.bingPreferenceRadioBingRuleNoisy) + } + } + + radioGroup?.setOnCheckedChangeListener { _, checkedId -> + when (checkedId) { + R.id.bingPreferenceRadioBingRuleOff -> { + onPreferenceChangeListener?.onPreferenceChange(this, NOTIFICATION_OFF_INDEX) + } + R.id.bingPreferenceRadioBingRuleSilent -> { + onPreferenceChangeListener?.onPreferenceChange(this, NOTIFICATION_SILENT_INDEX) + } + R.id.bingPreferenceRadioBingRuleNoisy -> { + onPreferenceChangeListener?.onPreferenceChange(this, NOTIFICATION_NOISY_INDEX) + } + } + } + } + + companion object { + + // index in mRuleStatuses + private const val NOTIFICATION_OFF_INDEX = 0 + private const val NOTIFICATION_SILENT_INDEX = 1 + private const val NOTIFICATION_NOISY_INDEX = 2 + } +} diff --git a/vector/src/main/java/im/vector/app/features/settings/VectorSettingsAdvancedNotificationPreferenceFragment.kt b/vector/src/main/java/im/vector/app/features/settings/VectorSettingsAdvancedNotificationPreferenceFragment.kt new file mode 100644 index 0000000000..8d9f8d7170 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/settings/VectorSettingsAdvancedNotificationPreferenceFragment.kt @@ -0,0 +1,105 @@ +/* + * Copyright 2018 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package im.vector.app.features.settings + +import androidx.lifecycle.lifecycleScope +import androidx.preference.Preference +import im.vector.app.R +import im.vector.app.core.preference.PushRulePreference +import im.vector.app.core.preference.VectorPreference +import im.vector.app.core.utils.toast +import kotlinx.coroutines.launch +import org.matrix.android.sdk.api.pushrules.RuleIds +import org.matrix.android.sdk.api.pushrules.rest.PushRuleAndKind +import javax.inject.Inject + +class VectorSettingsAdvancedNotificationPreferenceFragment @Inject constructor() + : VectorSettingsBaseFragment() { + + override var titleRes: Int = R.string.settings_notification_advanced + + override val preferenceXmlRes = R.xml.vector_settings_notification_advanced_preferences + + override fun bindPref() { + for (preferenceKey in prefKeyToPushRuleId.keys) { + val preference = findPreference(preferenceKey) + if (preference is PushRulePreference) { + // preference.isEnabled = null != rules && isConnected && pushManager.areDeviceNotificationsAllowed() + val ruleAndKind: PushRuleAndKind? = session.getPushRules().findDefaultRule(prefKeyToPushRuleId[preferenceKey]) + + if (ruleAndKind == null) { + // The rule is not defined, hide the preference + preference.isVisible = false + } else { + preference.isVisible = true + preference.setPushRule(ruleAndKind) + preference.onPreferenceChangeListener = Preference.OnPreferenceChangeListener { _, newValue -> + val newRule = preference.createNewRule(newValue as Int) + if (newRule != null) { + displayLoadingView() + + lifecycleScope.launch { + val result = runCatching { + session.updatePushRuleActions(ruleAndKind.kind, + preference.ruleAndKind?.pushRule ?: ruleAndKind.pushRule, + newRule) + } + if (!isAdded) { + return@launch + } + hideLoadingView() + result.onSuccess { + preference.setPushRule(ruleAndKind.copy(pushRule = newRule)) + } + result.onFailure { failure -> + // Restore the previous value + refreshDisplay() + activity?.toast(errorFormatter.toHumanReadable(failure)) + } + } + } + false + } + } + } + } + } + + private fun refreshDisplay() { + listView?.adapter?.notifyDataSetChanged() + } + + /* ========================================================================================== + * Companion + * ========================================================================================== */ + + companion object { + // preference name <-> rule Id + private val prefKeyToPushRuleId = mapOf( + "SETTINGS_PUSH_RULE_CONTAINING_MY_DISPLAY_NAME_PREFERENCE_KEY" to RuleIds.RULE_ID_CONTAIN_DISPLAY_NAME, + "SETTINGS_PUSH_RULE_CONTAINING_MY_USER_NAME_PREFERENCE_KEY" to RuleIds.RULE_ID_CONTAIN_USER_NAME, + "SETTINGS_PUSH_RULE_MESSAGES_IN_ONE_TO_ONE_PREFERENCE_KEY" to RuleIds.RULE_ID_ONE_TO_ONE_ROOM, + "SETTINGS_PUSH_RULE_MESSAGES_IN_GROUP_CHAT_PREFERENCE_KEY" to RuleIds.RULE_ID_ALL_OTHER_MESSAGES_ROOMS, + "SETTINGS_PUSH_RULE_INVITED_TO_ROOM_PREFERENCE_KEY" to RuleIds.RULE_ID_INVITE_ME, + "SETTINGS_PUSH_RULE_CALL_INVITATIONS_PREFERENCE_KEY" to RuleIds.RULE_ID_CALL, + "SETTINGS_PUSH_RULE_MESSAGES_SENT_BY_BOT_PREFERENCE_KEY" to RuleIds.RULE_ID_SUPPRESS_BOTS_NOTIFICATIONS, + "SETTINGS_PUSH_RULE_MESSAGES_CONTAINING_AT_ROOM_PREFERENCE_KEY" to RuleIds.RULE_ID_ROOM_NOTIF, + "SETTINGS_PUSH_RULE_MESSAGES_IN_E2E_ONE_ONE_CHAT_PREFERENCE_KEY" to RuleIds.RULE_ID_ONE_TO_ONE_ENCRYPTED_ROOM, + "SETTINGS_PUSH_RULE_MESSAGES_IN_E2E_GROUP_CHAT_PREFERENCE_KEY" to RuleIds.RULE_ID_ENCRYPTED, + "SETTINGS_PUSH_RULE_ROOMS_UPGRADED_KEY" to RuleIds.RULE_ID_TOMBSTONE + ) + } +} diff --git a/vector/src/main/java/im/vector/app/features/settings/VectorSettingsPushRuleNotificationPreferenceFragment.kt b/vector/src/main/java/im/vector/app/features/settings/VectorSettingsPushRuleNotificationPreferenceFragment.kt index 2f274ce904..6683cf3038 100644 --- a/vector/src/main/java/im/vector/app/features/settings/VectorSettingsPushRuleNotificationPreferenceFragment.kt +++ b/vector/src/main/java/im/vector/app/features/settings/VectorSettingsPushRuleNotificationPreferenceFragment.kt @@ -30,105 +30,58 @@ import org.matrix.android.sdk.api.pushrules.rest.PushRuleAndKind abstract class VectorSettingsPushRuleNotificationPreferenceFragment : VectorSettingsBaseFragment() { - - fun indexFromBooleanPreference(pushRuleOn: Boolean): Int { - return if (pushRuleOn) { - NOTIFICATION_NOISY_INDEX - } else { - NOTIFICATION_OFF_INDEX - } - } - - fun booleanPreferenceFromIndex(index: Int): Boolean { - return index != NOTIFICATION_OFF_INDEX - } - /** - * @return the bing rule status index + * @return the bing rule status boolean */ - fun ruleStatusIndexFor(ruleAndKind: PushRuleAndKind? ): Int { - val safeRule = ruleAndKind?.pushRule ?: return NOTIFICATION_OFF_INDEX - - if (safeRule.ruleId == RuleIds.RULE_ID_SUPPRESS_BOTS_NOTIFICATIONS) { - if (safeRule.shouldNotNotify()) { - return if (safeRule.enabled) { - NOTIFICATION_OFF_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 (safeRule.getNotificationSound() != null) { - NOTIFICATION_NOISY_INDEX - } else { - NOTIFICATION_SILENT_INDEX - } - } - - return NOTIFICATION_OFF_INDEX + fun ruleStatusIndexFor(ruleAndKind: PushRuleAndKind): Boolean { + val rule = ruleAndKind.pushRule + if (rule.ruleId == RuleIds.RULE_ID_SUPPRESS_BOTS_NOTIFICATIONS) { + return rule.shouldNotify() || rule.shouldNotNotify() && !rule.enabled } + return rule.enabled && !rule.shouldNotNotify() + } /** * Create a push rule with the updated required at index. * - * @param index index - * @return a push rule with the updated flags / null if there is no update + * @param status boolean checkbox status + * @return a push rule with the updated flags */ - fun createNewRule(ruleAndKind: PushRuleAndKind?, index: Int): PushRule? { - val safeRule = ruleAndKind?.pushRule ?: return null + fun createNewRule(ruleAndKind: PushRuleAndKind, status: Boolean): PushRule { + val safeRule = ruleAndKind.pushRule val safeKind = ruleAndKind.kind val ruleStatusIndex = ruleStatusIndexFor(ruleAndKind) - return if (index != ruleStatusIndex) { + return if (status != ruleStatusIndex) { if (safeRule.ruleId == RuleIds.RULE_ID_SUPPRESS_BOTS_NOTIFICATIONS) { - when (index) { - NOTIFICATION_OFF_INDEX -> { - safeRule.copy(enabled = true) - .setNotify(false) - .removeNotificationSound() - } - NOTIFICATION_SILENT_INDEX -> { - safeRule.copy(enabled = false) - .setNotify(false) - } - NOTIFICATION_NOISY_INDEX -> { - safeRule.copy(enabled = true) - .setNotify(true) - .setNotificationSound() - } - else -> safeRule + if (status) { + safeRule.copy(enabled = true) + .setNotify(true) + .setNotificationSound() + } else { + safeRule.copy(enabled = true) + .setNotify(false) + .removeNotificationSound() } } else { - if (NOTIFICATION_OFF_INDEX == index) { + if (status) { + safeRule.copy(enabled = true) + .setNotify(true) + .setHighlight(safeKind != RuleSetKey.UNDERRIDE + && safeRule.ruleId != RuleIds.RULE_ID_INVITE_ME) + .setNotificationSound( + if (safeRule.ruleId == RuleIds.RULE_ID_CALL) { + Action.ACTION_OBJECT_VALUE_VALUE_RING + } else { + Action.ACTION_OBJECT_VALUE_VALUE_DEFAULT + } + ) + } else { if (safeKind == RuleSetKey.UNDERRIDE || safeRule.ruleId == RuleIds.RULE_ID_SUPPRESS_BOTS_NOTIFICATIONS) { safeRule.setNotify(false) } else { safeRule.copy(enabled = false) } - } else { - val newRule = safeRule.copy(enabled = true) - .setNotify(true) - .setHighlight(safeKind != RuleSetKey.UNDERRIDE - && safeRule.ruleId != RuleIds.RULE_ID_INVITE_ME - && NOTIFICATION_NOISY_INDEX == index) - - if (NOTIFICATION_NOISY_INDEX == index) { - newRule.setNotificationSound( - if (safeRule.ruleId == RuleIds.RULE_ID_CALL) { - Action.ACTION_OBJECT_VALUE_VALUE_RING - } else { - Action.ACTION_OBJECT_VALUE_VALUE_DEFAULT - } - ) - } else { - newRule.removeNotificationSound() - } } } } else { @@ -136,51 +89,44 @@ abstract class VectorSettingsPushRuleNotificationPreferenceFragment } } - override fun bindPref() { for (preferenceKey in prefKeyToPushRuleId.keys) { val preference = findPreference(preferenceKey)!! - // preference.isEnabled = null != rules && isConnected && pushManager.areDeviceNotificationsAllowed() val ruleAndKind: PushRuleAndKind? = session.getPushRules().findDefaultRule(prefKeyToPushRuleId[preferenceKey]) - if (ruleAndKind == null) { // The rule is not defined, hide the preference preference.isVisible = false } else { + var oldRuleAndKind: PushRuleAndKind = ruleAndKind preference.isVisible = true - - val index = ruleStatusIndexFor(ruleAndKind) - val boolPreference = booleanPreferenceFromIndex(index) - preference.isChecked = boolPreference + preference.isChecked = ruleStatusIndexFor(ruleAndKind) preference.onPreferenceChangeListener = Preference.OnPreferenceChangeListener { _, newValue -> - val newIndex = indexFromBooleanPreference(newValue as Boolean) - val newRule = createNewRule(ruleAndKind, newIndex) + val newRule = createNewRule(ruleAndKind, newValue as Boolean) + displayLoadingView() - if (newRule != null) { - displayLoadingView() - - lifecycleScope.launch { - val result = runCatching { - session.updatePushRuleActions( - ruleAndKind.kind, - ruleAndKind.pushRule, - newRule - ) - } - if (!isAdded) { - return@launch - } - hideLoadingView() - result.onSuccess { - preference.isChecked = newValue - } - result.onFailure { failure -> - // Restore the previous value - refreshDisplay() - activity?.toast(errorFormatter.toHumanReadable(failure)) - } + lifecycleScope.launch { + val result = runCatching { + session.updatePushRuleActions( + oldRuleAndKind.kind, + oldRuleAndKind.pushRule, + newRule + ) + } + if (!isAdded) { + return@launch + } + hideLoadingView() + result.onSuccess { + oldRuleAndKind = oldRuleAndKind.copy(pushRule = newRule) + preference.isChecked = newValue + } + result.onFailure { failure -> + // Restore the previous value + refreshDisplay() + activity?.toast(errorFormatter.toHumanReadable(failure)) } } + false } } @@ -191,17 +137,6 @@ abstract class VectorSettingsPushRuleNotificationPreferenceFragment listView?.adapter?.notifyDataSetChanged() } - /* ========================================================================================== - * Companion - * ========================================================================================== */ - abstract val prefKeyToPushRuleId: Map - companion object { - - // index in mRuleStatuses - private const val NOTIFICATION_OFF_INDEX = 0 - private const val NOTIFICATION_SILENT_INDEX = 1 - private const val NOTIFICATION_NOISY_INDEX = 2 - } } diff --git a/vector/src/main/res/layout/vector_preference_push_rule.xml b/vector/src/main/res/layout/vector_preference_push_rule.xml new file mode 100644 index 0000000000..3da5c81410 --- /dev/null +++ b/vector/src/main/res/layout/vector_preference_push_rule.xml @@ -0,0 +1,78 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/vector/src/main/res/values/strings.xml b/vector/src/main/res/values/strings.xml index a80ed06186..7f93cd3957 100644 --- a/vector/src/main/res/values/strings.xml +++ b/vector/src/main/res/values/strings.xml @@ -1189,6 +1189,15 @@ Configure Silent Notifications Choose LED color, vibration, sound… + Encrypted messages in one-to-one chats + Encrypted messages in group chats + When rooms are upgraded + Msgs containing my display name + Msgs containing my user name + Msgs in one-to-one chats + Msgs in group chats + When I’m invited to a room + Messages sent by bot Messages containing my display name Messages containing my username 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 new file mode 100644 index 0000000000..436858ac05 --- /dev/null +++ b/vector/src/main/res/xml/vector_settings_notification_advanced_preferences.xml @@ -0,0 +1,67 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/vector/src/main/res/xml/vector_settings_notifications.xml b/vector/src/main/res/xml/vector_settings_notifications.xml index d19945cd46..c9668bcd4d 100644 --- a/vector/src/main/res/xml/vector_settings_notifications.xml +++ b/vector/src/main/res/xml/vector_settings_notifications.xml @@ -20,26 +20,38 @@ + + + app:fragment="im.vector.app.features.settings.VectorSettingsGlobalNotificationPreferenceFragment" + app:isPreferenceVisible="@bool/useNotificationSettingsV2"/> + app:fragment="im.vector.app.features.settings.VectorSettingsKeywordAndMentionsNotificationPreferenceFragment" + app:isPreferenceVisible="@bool/useNotificationSettingsV2"/> + app:fragment="im.vector.app.features.settings.VectorSettingsOtherNotificationPreferenceFragment" + app:isPreferenceVisible="@bool/useNotificationSettingsV2"/>