Merge pull request #3825 from vector-im/feature/dla/room_notification_settings
Room Notification Settings V2
This commit is contained in:
commit
f843712d7e
|
@ -143,8 +143,10 @@ android {
|
||||||
resValue "bool", "useLoginV2", "false"
|
resValue "bool", "useLoginV2", "false"
|
||||||
|
|
||||||
// NotificationSettingsV2 is disabled. To be released in conjunction with iOS/Web
|
// NotificationSettingsV2 is disabled. To be released in conjunction with iOS/Web
|
||||||
resValue "bool", "useNotificationSettingsV1", "true"
|
def useNotificationSettingsV2 = false
|
||||||
resValue "bool", "useNotificationSettingsV2", "false"
|
buildConfigField "Boolean", "USE_NOTIFICATION_SETTINGS_V2", "${useNotificationSettingsV2}"
|
||||||
|
resValue "bool", "useNotificationSettingsV1", "${!useNotificationSettingsV2}"
|
||||||
|
resValue "bool", "useNotificationSettingsV2", "${useNotificationSettingsV2}"
|
||||||
|
|
||||||
buildConfigField "im.vector.app.features.crypto.keysrequest.OutboundSessionKeySharingStrategy", "outboundSessionKeySharingStrategy", "im.vector.app.features.crypto.keysrequest.OutboundSessionKeySharingStrategy.WhenTyping"
|
buildConfigField "im.vector.app.features.crypto.keysrequest.OutboundSessionKeySharingStrategy", "outboundSessionKeySharingStrategy", "im.vector.app.features.crypto.keysrequest.OutboundSessionKeySharingStrategy.WhenTyping"
|
||||||
|
|
||||||
|
|
|
@ -106,6 +106,7 @@ import im.vector.app.features.roomprofile.RoomProfileFragment
|
||||||
import im.vector.app.features.roomprofile.alias.RoomAliasFragment
|
import im.vector.app.features.roomprofile.alias.RoomAliasFragment
|
||||||
import im.vector.app.features.roomprofile.banned.RoomBannedMemberListFragment
|
import im.vector.app.features.roomprofile.banned.RoomBannedMemberListFragment
|
||||||
import im.vector.app.features.roomprofile.members.RoomMemberListFragment
|
import im.vector.app.features.roomprofile.members.RoomMemberListFragment
|
||||||
|
import im.vector.app.features.roomprofile.notifications.RoomNotificationSettingsFragment
|
||||||
import im.vector.app.features.roomprofile.permissions.RoomPermissionsFragment
|
import im.vector.app.features.roomprofile.permissions.RoomPermissionsFragment
|
||||||
import im.vector.app.features.roomprofile.settings.RoomSettingsFragment
|
import im.vector.app.features.roomprofile.settings.RoomSettingsFragment
|
||||||
import im.vector.app.features.roomprofile.settings.joinrule.RoomJoinRuleChooseRestrictedFragment
|
import im.vector.app.features.roomprofile.settings.joinrule.RoomJoinRuleChooseRestrictedFragment
|
||||||
|
@ -717,6 +718,11 @@ interface FragmentModule {
|
||||||
@FragmentKey(RoomBannedMemberListFragment::class)
|
@FragmentKey(RoomBannedMemberListFragment::class)
|
||||||
fun bindRoomBannedMemberListFragment(fragment: RoomBannedMemberListFragment): Fragment
|
fun bindRoomBannedMemberListFragment(fragment: RoomBannedMemberListFragment): Fragment
|
||||||
|
|
||||||
|
@Binds
|
||||||
|
@IntoMap
|
||||||
|
@FragmentKey(RoomNotificationSettingsFragment::class)
|
||||||
|
fun bindRoomNotificationSettingsFragment(fragment: RoomNotificationSettingsFragment): Fragment
|
||||||
|
|
||||||
@Binds
|
@Binds
|
||||||
@IntoMap
|
@IntoMap
|
||||||
@FragmentKey(SearchFragment::class)
|
@FragmentKey(SearchFragment::class)
|
||||||
|
|
|
@ -0,0 +1,62 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2021 New Vector Ltd
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package im.vector.app.core.epoxy.profiles.notifications
|
||||||
|
|
||||||
|
import android.widget.TextView
|
||||||
|
import com.airbnb.epoxy.EpoxyAttribute
|
||||||
|
import com.airbnb.epoxy.EpoxyModelClass
|
||||||
|
import im.vector.app.R
|
||||||
|
import im.vector.app.core.epoxy.ClickListener
|
||||||
|
import im.vector.app.core.epoxy.VectorEpoxyHolder
|
||||||
|
import im.vector.app.core.epoxy.VectorEpoxyModel
|
||||||
|
import im.vector.app.core.extensions.setTextWithColoredPart
|
||||||
|
|
||||||
|
@EpoxyModelClass(layout = R.layout.item_notifications_footer)
|
||||||
|
abstract class NotificationSettingsFooterItem : VectorEpoxyModel<NotificationSettingsFooterItem.Holder>() {
|
||||||
|
|
||||||
|
@EpoxyAttribute
|
||||||
|
var encrypted: Boolean = false
|
||||||
|
|
||||||
|
@EpoxyAttribute(EpoxyAttribute.Option.DoNotHash)
|
||||||
|
var clickListener: ClickListener? = null
|
||||||
|
|
||||||
|
override fun bind(holder: Holder) {
|
||||||
|
super.bind(holder)
|
||||||
|
val accountSettingsString = holder.view.context.getString(R.string.room_settings_room_notifications_account_settings)
|
||||||
|
val manageNotificationsString = holder.view.context.getString(
|
||||||
|
R.string.room_settings_room_notifications_manage_notifications,
|
||||||
|
accountSettingsString
|
||||||
|
)
|
||||||
|
val manageNotificationsBuilder = StringBuilder(manageNotificationsString)
|
||||||
|
if (encrypted) {
|
||||||
|
val encryptionNotice = holder.view.context.getString(R.string.room_settings_room_notifications_encryption_notice)
|
||||||
|
manageNotificationsBuilder.appendLine().append(encryptionNotice)
|
||||||
|
}
|
||||||
|
|
||||||
|
holder.textView.setTextWithColoredPart(
|
||||||
|
manageNotificationsBuilder.toString(),
|
||||||
|
accountSettingsString,
|
||||||
|
underline = true
|
||||||
|
) {
|
||||||
|
clickListener?.invoke(holder.textView)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Holder : VectorEpoxyHolder() {
|
||||||
|
val textView by bind<TextView>(R.id.footerText)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,69 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2021 New Vector Ltd
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package im.vector.app.core.epoxy.profiles.notifications
|
||||||
|
|
||||||
|
import android.widget.ImageView
|
||||||
|
import android.widget.TextView
|
||||||
|
import androidx.annotation.StringRes
|
||||||
|
import androidx.core.content.ContextCompat
|
||||||
|
import com.airbnb.epoxy.EpoxyAttribute
|
||||||
|
import com.airbnb.epoxy.EpoxyModelClass
|
||||||
|
import im.vector.app.R
|
||||||
|
import im.vector.app.core.epoxy.ClickListener
|
||||||
|
import im.vector.app.core.epoxy.VectorEpoxyHolder
|
||||||
|
import im.vector.app.core.epoxy.VectorEpoxyModel
|
||||||
|
import im.vector.app.core.epoxy.onClick
|
||||||
|
|
||||||
|
@EpoxyModelClass(layout = R.layout.item_radio)
|
||||||
|
abstract class RadioButtonItem : VectorEpoxyModel<RadioButtonItem.Holder>() {
|
||||||
|
|
||||||
|
@EpoxyAttribute
|
||||||
|
var title: CharSequence? = null
|
||||||
|
|
||||||
|
@StringRes
|
||||||
|
@EpoxyAttribute
|
||||||
|
var titleRes: Int? = null
|
||||||
|
|
||||||
|
@EpoxyAttribute
|
||||||
|
var selected = false
|
||||||
|
|
||||||
|
@EpoxyAttribute(EpoxyAttribute.Option.DoNotHash)
|
||||||
|
lateinit var listener: ClickListener
|
||||||
|
|
||||||
|
override fun bind(holder: Holder) {
|
||||||
|
super.bind(holder)
|
||||||
|
holder.view.onClick(listener)
|
||||||
|
if (titleRes != null) {
|
||||||
|
holder.titleText.setText(titleRes!!)
|
||||||
|
} else {
|
||||||
|
holder.titleText.text = title
|
||||||
|
}
|
||||||
|
|
||||||
|
if (selected) {
|
||||||
|
holder.radioImage.setImageDrawable(ContextCompat.getDrawable(holder.view.context, R.drawable.ic_radio_on))
|
||||||
|
holder.radioImage.contentDescription = holder.view.context.getString(R.string.a11y_checked)
|
||||||
|
} else {
|
||||||
|
holder.radioImage.setImageDrawable(ContextCompat.getDrawable(holder.view.context, R.drawable.ic_radio_off))
|
||||||
|
holder.radioImage.contentDescription = holder.view.context.getString(R.string.a11y_unchecked)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Holder : VectorEpoxyHolder() {
|
||||||
|
val titleText by bind<TextView>(R.id.actionTitle)
|
||||||
|
val radioImage by bind<ImageView>(R.id.radioIcon)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,50 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2021 New Vector Ltd
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package im.vector.app.core.epoxy.profiles.notifications
|
||||||
|
|
||||||
|
import android.widget.TextView
|
||||||
|
import androidx.annotation.StringRes
|
||||||
|
import com.airbnb.epoxy.EpoxyAttribute
|
||||||
|
import com.airbnb.epoxy.EpoxyModelClass
|
||||||
|
import im.vector.app.R
|
||||||
|
import im.vector.app.core.epoxy.VectorEpoxyHolder
|
||||||
|
import im.vector.app.core.epoxy.VectorEpoxyModel
|
||||||
|
|
||||||
|
@EpoxyModelClass(layout = R.layout.item_text_header)
|
||||||
|
abstract class TextHeaderItem : VectorEpoxyModel<TextHeaderItem.Holder>() {
|
||||||
|
|
||||||
|
@EpoxyAttribute
|
||||||
|
var text: String? = null
|
||||||
|
|
||||||
|
@StringRes
|
||||||
|
@EpoxyAttribute
|
||||||
|
var textRes: Int? = null
|
||||||
|
|
||||||
|
override fun bind(holder: Holder) {
|
||||||
|
super.bind(holder)
|
||||||
|
val textResource = textRes
|
||||||
|
if (textResource != null) {
|
||||||
|
holder.textView.setText(textResource)
|
||||||
|
} else {
|
||||||
|
holder.textView.text = text
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Holder : VectorEpoxyHolder() {
|
||||||
|
val textView by bind<TextView>(R.id.headerText)
|
||||||
|
}
|
||||||
|
}
|
|
@ -65,6 +65,23 @@ fun TextView.setTextWithColoredPart(@StringRes fullTextRes: Int,
|
||||||
val coloredPart = resources.getString(coloredTextRes)
|
val coloredPart = resources.getString(coloredTextRes)
|
||||||
// Insert colored part into the full text
|
// Insert colored part into the full text
|
||||||
val fullText = resources.getString(fullTextRes, coloredPart)
|
val fullText = resources.getString(fullTextRes, coloredPart)
|
||||||
|
|
||||||
|
setTextWithColoredPart(fullText, coloredPart, colorAttribute, underline, onClick)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set text with a colored part
|
||||||
|
* @param fullText The full text.
|
||||||
|
* @param coloredPart The colored part of the text
|
||||||
|
* @param colorAttribute attribute of the color. Default to colorPrimary
|
||||||
|
* @param underline true to also underline the text. Default to false
|
||||||
|
* @param onClick attributes to handle click on the colored part if needed
|
||||||
|
*/
|
||||||
|
fun TextView.setTextWithColoredPart(fullText: String,
|
||||||
|
coloredPart: String,
|
||||||
|
@AttrRes colorAttribute: Int = R.attr.colorPrimary,
|
||||||
|
underline: Boolean = false,
|
||||||
|
onClick: (() -> Unit)? = null) {
|
||||||
val color = ThemeUtils.getColor(context, colorAttribute)
|
val color = ThemeUtils.getColor(context, colorAttribute)
|
||||||
|
|
||||||
val foregroundSpan = ForegroundColorSpan(color)
|
val foregroundSpan = ForegroundColorSpan(color)
|
||||||
|
|
|
@ -143,6 +143,8 @@ class HomeActivity :
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun getCoordinatorLayout() = views.coordinatorLayout
|
||||||
|
|
||||||
override fun getBinding() = ActivityHomeBinding.inflate(layoutInflater)
|
override fun getBinding() = ActivityHomeBinding.inflate(layoutInflater)
|
||||||
|
|
||||||
override fun injectWith(injector: ScreenComponent) {
|
override fun injectWith(injector: ScreenComponent) {
|
||||||
|
|
|
@ -0,0 +1,24 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2021 New Vector Ltd
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package im.vector.app.features.home.room.list.actions
|
||||||
|
|
||||||
|
import im.vector.app.features.roomprofile.notifications.RoomNotificationSettingsViewState
|
||||||
|
|
||||||
|
data class RoomListQuickActionViewState(
|
||||||
|
val roomListActionsArgs: RoomListActionsArgs,
|
||||||
|
val notificationSettingsViewState: RoomNotificationSettingsViewState
|
||||||
|
)
|
|
@ -22,15 +22,23 @@ import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
import com.airbnb.mvrx.args
|
||||||
import com.airbnb.mvrx.fragmentViewModel
|
import com.airbnb.mvrx.fragmentViewModel
|
||||||
import com.airbnb.mvrx.withState
|
import com.airbnb.mvrx.withState
|
||||||
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||||
|
import im.vector.app.R
|
||||||
import im.vector.app.core.di.ScreenComponent
|
import im.vector.app.core.di.ScreenComponent
|
||||||
|
import im.vector.app.core.error.ErrorFormatter
|
||||||
import im.vector.app.core.extensions.cleanup
|
import im.vector.app.core.extensions.cleanup
|
||||||
import im.vector.app.core.extensions.configureWith
|
import im.vector.app.core.extensions.configureWith
|
||||||
import im.vector.app.core.platform.VectorBaseBottomSheetDialogFragment
|
import im.vector.app.core.platform.VectorBaseBottomSheetDialogFragment
|
||||||
import im.vector.app.databinding.BottomSheetGenericListBinding
|
import im.vector.app.databinding.BottomSheetGenericListBinding
|
||||||
import im.vector.app.features.navigation.Navigator
|
import im.vector.app.features.navigation.Navigator
|
||||||
|
import im.vector.app.features.roomprofile.notifications.RoomNotificationSettingsAction
|
||||||
|
import im.vector.app.features.roomprofile.notifications.RoomNotificationSettingsViewEvents
|
||||||
|
import im.vector.app.features.roomprofile.notifications.RoomNotificationSettingsViewModel
|
||||||
import kotlinx.parcelize.Parcelize
|
import kotlinx.parcelize.Parcelize
|
||||||
|
import org.matrix.android.sdk.api.session.room.notification.RoomNotificationState
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
@Parcelize
|
@Parcelize
|
||||||
|
@ -54,11 +62,13 @@ class RoomListQuickActionsBottomSheet :
|
||||||
|
|
||||||
private lateinit var sharedActionViewModel: RoomListQuickActionsSharedActionViewModel
|
private lateinit var sharedActionViewModel: RoomListQuickActionsSharedActionViewModel
|
||||||
@Inject lateinit var sharedViewPool: RecyclerView.RecycledViewPool
|
@Inject lateinit var sharedViewPool: RecyclerView.RecycledViewPool
|
||||||
@Inject lateinit var roomListActionsViewModelFactory: RoomListQuickActionsViewModel.Factory
|
@Inject lateinit var roomNotificationSettingsViewModelFactory: RoomNotificationSettingsViewModel.Factory
|
||||||
@Inject lateinit var roomListActionsEpoxyController: RoomListQuickActionsEpoxyController
|
@Inject lateinit var roomListActionsEpoxyController: RoomListQuickActionsEpoxyController
|
||||||
@Inject lateinit var navigator: Navigator
|
@Inject lateinit var navigator: Navigator
|
||||||
|
@Inject lateinit var errorFormatter: ErrorFormatter
|
||||||
|
|
||||||
private val viewModel: RoomListQuickActionsViewModel by fragmentViewModel(RoomListQuickActionsViewModel::class)
|
private val roomListActionsArgs: RoomListActionsArgs by args()
|
||||||
|
private val viewModel: RoomNotificationSettingsViewModel by fragmentViewModel(RoomNotificationSettingsViewModel::class)
|
||||||
|
|
||||||
override val showExpanded = true
|
override val showExpanded = true
|
||||||
|
|
||||||
|
@ -80,6 +90,12 @@ class RoomListQuickActionsBottomSheet :
|
||||||
disableItemAnimation = true
|
disableItemAnimation = true
|
||||||
)
|
)
|
||||||
roomListActionsEpoxyController.listener = this
|
roomListActionsEpoxyController.listener = this
|
||||||
|
|
||||||
|
viewModel.observeViewEvents {
|
||||||
|
when (it) {
|
||||||
|
is RoomNotificationSettingsViewEvents.Failure -> displayErrorDialog(it.throwable)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onDestroyView() {
|
override fun onDestroyView() {
|
||||||
|
@ -89,7 +105,11 @@ class RoomListQuickActionsBottomSheet :
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun invalidate() = withState(viewModel) {
|
override fun invalidate() = withState(viewModel) {
|
||||||
roomListActionsEpoxyController.setData(it)
|
val roomListViewState = RoomListQuickActionViewState(
|
||||||
|
roomListActionsArgs,
|
||||||
|
it
|
||||||
|
)
|
||||||
|
roomListActionsEpoxyController.setData(roomListViewState)
|
||||||
super.invalidate()
|
super.invalidate()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -103,6 +123,10 @@ class RoomListQuickActionsBottomSheet :
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun didSelectRoomNotificationState(roomNotificationState: RoomNotificationState) {
|
||||||
|
viewModel.handle(RoomNotificationSettingsAction.SelectNotificationState(roomNotificationState))
|
||||||
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
fun newInstance(roomId: String, mode: RoomListActionsArgs.Mode): RoomListQuickActionsBottomSheet {
|
fun newInstance(roomId: String, mode: RoomListActionsArgs.Mode): RoomListQuickActionsBottomSheet {
|
||||||
return RoomListQuickActionsBottomSheet().apply {
|
return RoomListQuickActionsBottomSheet().apply {
|
||||||
|
@ -110,4 +134,12 @@ class RoomListQuickActionsBottomSheet :
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun displayErrorDialog(throwable: Throwable) {
|
||||||
|
MaterialAlertDialogBuilder(requireActivity())
|
||||||
|
.setTitle(R.string.dialog_title_error)
|
||||||
|
.setMessage(errorFormatter.toHumanReadable(throwable))
|
||||||
|
.setPositiveButton(R.string.ok, null)
|
||||||
|
.show()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,13 +15,19 @@
|
||||||
*/
|
*/
|
||||||
package im.vector.app.features.home.room.list.actions
|
package im.vector.app.features.home.room.list.actions
|
||||||
|
|
||||||
|
import androidx.annotation.StringRes
|
||||||
import com.airbnb.epoxy.TypedEpoxyController
|
import com.airbnb.epoxy.TypedEpoxyController
|
||||||
|
import im.vector.app.BuildConfig
|
||||||
|
import im.vector.app.R
|
||||||
import im.vector.app.core.epoxy.bottomSheetDividerItem
|
import im.vector.app.core.epoxy.bottomSheetDividerItem
|
||||||
import im.vector.app.core.epoxy.bottomsheet.bottomSheetActionItem
|
import im.vector.app.core.epoxy.bottomsheet.bottomSheetActionItem
|
||||||
import im.vector.app.core.epoxy.bottomsheet.bottomSheetRoomPreviewItem
|
import im.vector.app.core.epoxy.bottomsheet.bottomSheetRoomPreviewItem
|
||||||
|
import im.vector.app.core.epoxy.profiles.notifications.radioButtonItem
|
||||||
import im.vector.app.core.resources.ColorProvider
|
import im.vector.app.core.resources.ColorProvider
|
||||||
import im.vector.app.core.resources.StringProvider
|
import im.vector.app.core.resources.StringProvider
|
||||||
import im.vector.app.features.home.AvatarRenderer
|
import im.vector.app.features.home.AvatarRenderer
|
||||||
|
import im.vector.app.features.roomprofile.notifications.notificationOptions
|
||||||
|
import im.vector.app.features.roomprofile.notifications.notificationStateMapped
|
||||||
import org.matrix.android.sdk.api.session.room.notification.RoomNotificationState
|
import org.matrix.android.sdk.api.session.room.notification.RoomNotificationState
|
||||||
import org.matrix.android.sdk.api.util.toMatrixItem
|
import org.matrix.android.sdk.api.util.toMatrixItem
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
@ -33,16 +39,19 @@ class RoomListQuickActionsEpoxyController @Inject constructor(
|
||||||
private val avatarRenderer: AvatarRenderer,
|
private val avatarRenderer: AvatarRenderer,
|
||||||
private val colorProvider: ColorProvider,
|
private val colorProvider: ColorProvider,
|
||||||
private val stringProvider: StringProvider
|
private val stringProvider: StringProvider
|
||||||
) : TypedEpoxyController<RoomListQuickActionsState>() {
|
) : TypedEpoxyController<RoomListQuickActionViewState>() {
|
||||||
|
|
||||||
var listener: Listener? = null
|
var listener: Listener? = null
|
||||||
|
|
||||||
override fun buildModels(state: RoomListQuickActionsState) {
|
override fun buildModels(state: RoomListQuickActionViewState) {
|
||||||
val roomSummary = state.roomSummary() ?: return
|
val notificationViewState = state.notificationSettingsViewState
|
||||||
|
val roomSummary = notificationViewState.roomSummary() ?: return
|
||||||
val host = this
|
val host = this
|
||||||
val showAll = state.mode == RoomListActionsArgs.Mode.FULL
|
val isV2 = BuildConfig.USE_NOTIFICATION_SETTINGS_V2
|
||||||
|
// V2 always shows full details as we no longer display the sheet from RoomProfile > Notifications
|
||||||
|
val showFull = state.roomListActionsArgs.mode == RoomListActionsArgs.Mode.FULL || isV2
|
||||||
|
|
||||||
if (showAll) {
|
if (showFull) {
|
||||||
// Preview, favorite, settings
|
// Preview, favorite, settings
|
||||||
bottomSheetRoomPreviewItem {
|
bottomSheetRoomPreviewItem {
|
||||||
id("room_preview")
|
id("room_preview")
|
||||||
|
@ -63,17 +72,38 @@ class RoomListQuickActionsEpoxyController @Inject constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val selectedRoomState = state.roomNotificationState()
|
if (isV2) {
|
||||||
RoomListQuickActionsSharedAction.NotificationsAllNoisy(roomSummary.roomId).toBottomSheetItem(0, selectedRoomState)
|
notificationViewState.notificationOptions.forEach { notificationState ->
|
||||||
RoomListQuickActionsSharedAction.NotificationsAll(roomSummary.roomId).toBottomSheetItem(1, selectedRoomState)
|
val title = titleForNotificationState(notificationState)
|
||||||
RoomListQuickActionsSharedAction.NotificationsMentionsOnly(roomSummary.roomId).toBottomSheetItem(2, selectedRoomState)
|
radioButtonItem {
|
||||||
RoomListQuickActionsSharedAction.NotificationsMute(roomSummary.roomId).toBottomSheetItem(3, selectedRoomState)
|
id(notificationState.name)
|
||||||
|
titleRes(title)
|
||||||
|
selected(notificationViewState.notificationStateMapped() == notificationState)
|
||||||
|
listener {
|
||||||
|
host.listener?.didSelectRoomNotificationState(notificationState)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
val selectedRoomState = notificationViewState.notificationState()
|
||||||
|
RoomListQuickActionsSharedAction.NotificationsAllNoisy(roomSummary.roomId).toBottomSheetItem(0, selectedRoomState)
|
||||||
|
RoomListQuickActionsSharedAction.NotificationsAll(roomSummary.roomId).toBottomSheetItem(1, selectedRoomState)
|
||||||
|
RoomListQuickActionsSharedAction.NotificationsMentionsOnly(roomSummary.roomId).toBottomSheetItem(2, selectedRoomState)
|
||||||
|
RoomListQuickActionsSharedAction.NotificationsMute(roomSummary.roomId).toBottomSheetItem(3, selectedRoomState)
|
||||||
|
}
|
||||||
|
|
||||||
if (showAll) {
|
if (showFull) {
|
||||||
RoomListQuickActionsSharedAction.Leave(roomSummary.roomId).toBottomSheetItem(5)
|
RoomListQuickActionsSharedAction.Leave(roomSummary.roomId, showIcon = !isV2).toBottomSheetItem(5)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@StringRes
|
||||||
|
private fun titleForNotificationState(notificationState: RoomNotificationState): Int? = when (notificationState) {
|
||||||
|
RoomNotificationState.ALL_MESSAGES_NOISY -> R.string.room_settings_all_messages
|
||||||
|
RoomNotificationState.MENTIONS_ONLY -> R.string.room_settings_mention_and_keyword_only
|
||||||
|
RoomNotificationState.MUTE -> R.string.room_settings_none
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
private fun RoomListQuickActionsSharedAction.toBottomSheetItem(index: Int, roomNotificationState: RoomNotificationState? = null) {
|
private fun RoomListQuickActionsSharedAction.toBottomSheetItem(index: Int, roomNotificationState: RoomNotificationState? = null) {
|
||||||
val host = this@RoomListQuickActionsEpoxyController
|
val host = this@RoomListQuickActionsEpoxyController
|
||||||
val selected = when (this) {
|
val selected = when (this) {
|
||||||
|
@ -86,7 +116,11 @@ class RoomListQuickActionsEpoxyController @Inject constructor(
|
||||||
return bottomSheetActionItem {
|
return bottomSheetActionItem {
|
||||||
id("action_$index")
|
id("action_$index")
|
||||||
selected(selected)
|
selected(selected)
|
||||||
iconRes(iconResId)
|
if (iconResId != null) {
|
||||||
|
iconRes(iconResId)
|
||||||
|
} else {
|
||||||
|
showIcon(false)
|
||||||
|
}
|
||||||
textRes(titleRes)
|
textRes(titleRes)
|
||||||
destructive(this@toBottomSheetItem.destructive)
|
destructive(this@toBottomSheetItem.destructive)
|
||||||
listener { host.listener?.didSelectMenuAction(this@toBottomSheetItem) }
|
listener { host.listener?.didSelectMenuAction(this@toBottomSheetItem) }
|
||||||
|
@ -95,5 +129,6 @@ class RoomListQuickActionsEpoxyController @Inject constructor(
|
||||||
|
|
||||||
interface Listener {
|
interface Listener {
|
||||||
fun didSelectMenuAction(quickAction: RoomListQuickActionsSharedAction)
|
fun didSelectMenuAction(quickAction: RoomListQuickActionsSharedAction)
|
||||||
|
fun didSelectRoomNotificationState(roomNotificationState: RoomNotificationState)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,7 +23,7 @@ import im.vector.app.core.platform.VectorSharedAction
|
||||||
|
|
||||||
sealed class RoomListQuickActionsSharedAction(
|
sealed class RoomListQuickActionsSharedAction(
|
||||||
@StringRes val titleRes: Int,
|
@StringRes val titleRes: Int,
|
||||||
@DrawableRes val iconResId: Int,
|
@DrawableRes val iconResId: Int?,
|
||||||
val destructive: Boolean = false)
|
val destructive: Boolean = false)
|
||||||
: VectorSharedAction {
|
: VectorSharedAction {
|
||||||
|
|
||||||
|
@ -60,9 +60,9 @@ sealed class RoomListQuickActionsSharedAction(
|
||||||
R.string.room_list_quick_actions_favorite_add,
|
R.string.room_list_quick_actions_favorite_add,
|
||||||
R.drawable.ic_star_24dp)
|
R.drawable.ic_star_24dp)
|
||||||
|
|
||||||
data class Leave(val roomId: String) : RoomListQuickActionsSharedAction(
|
data class Leave(val roomId: String, val showIcon: Boolean = true) : RoomListQuickActionsSharedAction(
|
||||||
R.string.room_list_quick_actions_leave,
|
R.string.room_list_quick_actions_leave,
|
||||||
R.drawable.ic_room_actions_leave,
|
if (showIcon) R.drawable.ic_room_actions_leave else null,
|
||||||
true
|
true
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,33 +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.app.features.home.room.list.actions
|
|
||||||
|
|
||||||
import com.airbnb.mvrx.Async
|
|
||||||
import com.airbnb.mvrx.MvRxState
|
|
||||||
import com.airbnb.mvrx.Uninitialized
|
|
||||||
import org.matrix.android.sdk.api.session.room.model.RoomSummary
|
|
||||||
import org.matrix.android.sdk.api.session.room.notification.RoomNotificationState
|
|
||||||
|
|
||||||
data class RoomListQuickActionsState(
|
|
||||||
val roomId: String,
|
|
||||||
val mode: RoomListActionsArgs.Mode,
|
|
||||||
val roomSummary: Async<RoomSummary> = Uninitialized,
|
|
||||||
val roomNotificationState: Async<RoomNotificationState> = Uninitialized
|
|
||||||
) : MvRxState {
|
|
||||||
|
|
||||||
constructor(args: RoomListActionsArgs) : this(roomId = args.roomId, mode = args.mode)
|
|
||||||
}
|
|
|
@ -1,78 +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.app.features.home.room.list.actions
|
|
||||||
|
|
||||||
import com.airbnb.mvrx.FragmentViewModelContext
|
|
||||||
import com.airbnb.mvrx.MvRxViewModelFactory
|
|
||||||
import com.airbnb.mvrx.ViewModelContext
|
|
||||||
import dagger.assisted.Assisted
|
|
||||||
import dagger.assisted.AssistedInject
|
|
||||||
import dagger.assisted.AssistedFactory
|
|
||||||
import im.vector.app.core.platform.EmptyAction
|
|
||||||
import im.vector.app.core.platform.EmptyViewEvents
|
|
||||||
import im.vector.app.core.platform.VectorViewModel
|
|
||||||
import org.matrix.android.sdk.api.session.Session
|
|
||||||
import org.matrix.android.sdk.rx.rx
|
|
||||||
import org.matrix.android.sdk.rx.unwrap
|
|
||||||
|
|
||||||
class RoomListQuickActionsViewModel @AssistedInject constructor(@Assisted initialState: RoomListQuickActionsState,
|
|
||||||
session: Session
|
|
||||||
) : VectorViewModel<RoomListQuickActionsState, EmptyAction, EmptyViewEvents>(initialState) {
|
|
||||||
|
|
||||||
@AssistedFactory
|
|
||||||
interface Factory {
|
|
||||||
fun create(initialState: RoomListQuickActionsState): RoomListQuickActionsViewModel
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object : MvRxViewModelFactory<RoomListQuickActionsViewModel, RoomListQuickActionsState> {
|
|
||||||
|
|
||||||
@JvmStatic
|
|
||||||
override fun create(viewModelContext: ViewModelContext, state: RoomListQuickActionsState): RoomListQuickActionsViewModel? {
|
|
||||||
val fragment: RoomListQuickActionsBottomSheet = (viewModelContext as FragmentViewModelContext).fragment()
|
|
||||||
return fragment.roomListActionsViewModelFactory.create(state)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private val room = session.getRoom(initialState.roomId)!!
|
|
||||||
|
|
||||||
init {
|
|
||||||
observeRoomSummary()
|
|
||||||
observeNotificationState()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun observeNotificationState() {
|
|
||||||
room
|
|
||||||
.rx()
|
|
||||||
.liveNotificationState()
|
|
||||||
.execute {
|
|
||||||
copy(roomNotificationState = it)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun observeRoomSummary() {
|
|
||||||
room
|
|
||||||
.rx()
|
|
||||||
.liveRoomSummary()
|
|
||||||
.unwrap()
|
|
||||||
.execute {
|
|
||||||
copy(roomSummary = it)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun handle(action: EmptyAction) {
|
|
||||||
// No op
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -39,6 +39,7 @@ import im.vector.app.features.roomprofile.banned.RoomBannedMemberListFragment
|
||||||
import im.vector.app.features.roomprofile.members.RoomMemberListFragment
|
import im.vector.app.features.roomprofile.members.RoomMemberListFragment
|
||||||
import im.vector.app.features.roomprofile.settings.RoomSettingsFragment
|
import im.vector.app.features.roomprofile.settings.RoomSettingsFragment
|
||||||
import im.vector.app.features.roomprofile.alias.RoomAliasFragment
|
import im.vector.app.features.roomprofile.alias.RoomAliasFragment
|
||||||
|
import im.vector.app.features.roomprofile.notifications.RoomNotificationSettingsFragment
|
||||||
import im.vector.app.features.roomprofile.permissions.RoomPermissionsFragment
|
import im.vector.app.features.roomprofile.permissions.RoomPermissionsFragment
|
||||||
import im.vector.app.features.roomprofile.uploads.RoomUploadsFragment
|
import im.vector.app.features.roomprofile.uploads.RoomUploadsFragment
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
@ -107,12 +108,13 @@ class RoomProfileActivity :
|
||||||
.observe()
|
.observe()
|
||||||
.subscribe { sharedAction ->
|
.subscribe { sharedAction ->
|
||||||
when (sharedAction) {
|
when (sharedAction) {
|
||||||
RoomProfileSharedAction.OpenRoomMembers -> openRoomMembers()
|
RoomProfileSharedAction.OpenRoomMembers -> openRoomMembers()
|
||||||
RoomProfileSharedAction.OpenRoomSettings -> openRoomSettings()
|
RoomProfileSharedAction.OpenRoomSettings -> openRoomSettings()
|
||||||
RoomProfileSharedAction.OpenRoomAliasesSettings -> openRoomAlias()
|
RoomProfileSharedAction.OpenRoomAliasesSettings -> openRoomAlias()
|
||||||
RoomProfileSharedAction.OpenRoomPermissionsSettings -> openRoomPermissions()
|
RoomProfileSharedAction.OpenRoomPermissionsSettings -> openRoomPermissions()
|
||||||
RoomProfileSharedAction.OpenRoomUploads -> openRoomUploads()
|
RoomProfileSharedAction.OpenRoomUploads -> openRoomUploads()
|
||||||
RoomProfileSharedAction.OpenBannedRoomMembers -> openBannedRoomMembers()
|
RoomProfileSharedAction.OpenBannedRoomMembers -> openBannedRoomMembers()
|
||||||
|
RoomProfileSharedAction.OpenRoomNotificationSettings -> openRoomNotificationSettings()
|
||||||
}.exhaustive
|
}.exhaustive
|
||||||
}
|
}
|
||||||
.disposeOnDestroy()
|
.disposeOnDestroy()
|
||||||
|
@ -162,6 +164,10 @@ class RoomProfileActivity :
|
||||||
addFragmentToBackstack(R.id.simpleFragmentContainer, RoomBannedMemberListFragment::class.java, roomProfileArgs)
|
addFragmentToBackstack(R.id.simpleFragmentContainer, RoomBannedMemberListFragment::class.java, roomProfileArgs)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun openRoomNotificationSettings() {
|
||||||
|
addFragmentToBackstack(R.id.simpleFragmentContainer, RoomNotificationSettingsFragment::class.java, roomProfileArgs)
|
||||||
|
}
|
||||||
|
|
||||||
override fun configure(toolbar: MaterialToolbar) {
|
override fun configure(toolbar: MaterialToolbar) {
|
||||||
configureToolbar(toolbar)
|
configureToolbar(toolbar)
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,6 +30,7 @@ import com.airbnb.mvrx.args
|
||||||
import com.airbnb.mvrx.fragmentViewModel
|
import com.airbnb.mvrx.fragmentViewModel
|
||||||
import com.airbnb.mvrx.withState
|
import com.airbnb.mvrx.withState
|
||||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||||
|
import im.vector.app.BuildConfig
|
||||||
import im.vector.app.R
|
import im.vector.app.R
|
||||||
import im.vector.app.core.animations.AppBarStateChangeListener
|
import im.vector.app.core.animations.AppBarStateChangeListener
|
||||||
import im.vector.app.core.animations.MatrixItemAppBarStateChangeListener
|
import im.vector.app.core.animations.MatrixItemAppBarStateChangeListener
|
||||||
|
@ -253,9 +254,13 @@ class RoomProfileFragment @Inject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onNotificationsClicked() {
|
override fun onNotificationsClicked() {
|
||||||
RoomListQuickActionsBottomSheet
|
if (BuildConfig.USE_NOTIFICATION_SETTINGS_V2) {
|
||||||
.newInstance(roomProfileArgs.roomId, RoomListActionsArgs.Mode.NOTIFICATIONS)
|
roomProfileSharedActionViewModel.post(RoomProfileSharedAction.OpenRoomNotificationSettings)
|
||||||
.show(childFragmentManager, "ROOM_PROFILE_NOTIFICATIONS")
|
} else {
|
||||||
|
RoomListQuickActionsBottomSheet
|
||||||
|
.newInstance(roomProfileArgs.roomId, RoomListActionsArgs.Mode.NOTIFICATIONS)
|
||||||
|
.show(childFragmentManager, "ROOM_PROFILE_NOTIFICATIONS")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onUploadsClicked() {
|
override fun onUploadsClicked() {
|
||||||
|
|
|
@ -28,4 +28,5 @@ sealed class RoomProfileSharedAction : VectorSharedAction {
|
||||||
object OpenRoomUploads : RoomProfileSharedAction()
|
object OpenRoomUploads : RoomProfileSharedAction()
|
||||||
object OpenRoomMembers : RoomProfileSharedAction()
|
object OpenRoomMembers : RoomProfileSharedAction()
|
||||||
object OpenBannedRoomMembers : RoomProfileSharedAction()
|
object OpenBannedRoomMembers : RoomProfileSharedAction()
|
||||||
|
object OpenRoomNotificationSettings : RoomProfileSharedAction()
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,24 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2021 New Vector Ltd
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package im.vector.app.features.roomprofile.notifications
|
||||||
|
|
||||||
|
import im.vector.app.core.platform.VectorViewModelAction
|
||||||
|
import org.matrix.android.sdk.api.session.room.notification.RoomNotificationState
|
||||||
|
|
||||||
|
sealed class RoomNotificationSettingsAction : VectorViewModelAction {
|
||||||
|
data class SelectNotificationState(val notificationState: RoomNotificationState): RoomNotificationSettingsAction()
|
||||||
|
}
|
|
@ -0,0 +1,76 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2021 New Vector Ltd
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package im.vector.app.features.roomprofile.notifications
|
||||||
|
|
||||||
|
import androidx.annotation.StringRes
|
||||||
|
import com.airbnb.epoxy.TypedEpoxyController
|
||||||
|
import im.vector.app.R
|
||||||
|
import im.vector.app.core.epoxy.profiles.notifications.notificationSettingsFooterItem
|
||||||
|
import im.vector.app.core.epoxy.profiles.notifications.radioButtonItem
|
||||||
|
import im.vector.app.core.epoxy.profiles.notifications.textHeaderItem
|
||||||
|
import org.matrix.android.sdk.api.session.room.notification.RoomNotificationState
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
class RoomNotificationSettingsController @Inject constructor() : TypedEpoxyController<RoomNotificationSettingsViewState>() {
|
||||||
|
|
||||||
|
interface Callback {
|
||||||
|
fun didSelectRoomNotificationState(roomNotificationState: RoomNotificationState)
|
||||||
|
fun didSelectAccountSettingsLink()
|
||||||
|
}
|
||||||
|
|
||||||
|
var callback: Callback? = null
|
||||||
|
|
||||||
|
init {
|
||||||
|
setData(null)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun buildModels(data: RoomNotificationSettingsViewState?) {
|
||||||
|
val host = this
|
||||||
|
data ?: return
|
||||||
|
|
||||||
|
textHeaderItem {
|
||||||
|
id("roomNotificationSettingsHeader")
|
||||||
|
textRes(R.string.room_settings_room_notifications_notify_me)
|
||||||
|
}
|
||||||
|
data.notificationOptions.forEach { notificationState ->
|
||||||
|
val title = titleForNotificationState(notificationState)
|
||||||
|
radioButtonItem {
|
||||||
|
id(notificationState.name)
|
||||||
|
titleRes(title)
|
||||||
|
selected(data.notificationStateMapped() == notificationState)
|
||||||
|
listener {
|
||||||
|
host.callback?.didSelectRoomNotificationState(notificationState)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
notificationSettingsFooterItem {
|
||||||
|
id("roomNotificationSettingsFooter")
|
||||||
|
encrypted(data.roomSummary()?.isEncrypted == true)
|
||||||
|
clickListener {
|
||||||
|
host.callback?.didSelectAccountSettingsLink()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@StringRes
|
||||||
|
private fun titleForNotificationState(notificationState: RoomNotificationState): Int? = when (notificationState) {
|
||||||
|
RoomNotificationState.ALL_MESSAGES_NOISY -> R.string.room_settings_all_messages
|
||||||
|
RoomNotificationState.MENTIONS_ONLY -> R.string.room_settings_mention_and_keyword_only
|
||||||
|
RoomNotificationState.MUTE -> R.string.room_settings_none
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,99 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2021 New Vector Ltd
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package im.vector.app.features.roomprofile.notifications
|
||||||
|
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import androidx.core.view.isVisible
|
||||||
|
import com.airbnb.mvrx.fragmentViewModel
|
||||||
|
import com.airbnb.mvrx.withState
|
||||||
|
import im.vector.app.R
|
||||||
|
import im.vector.app.core.extensions.cleanup
|
||||||
|
import im.vector.app.core.extensions.configureWith
|
||||||
|
import im.vector.app.core.platform.VectorBaseFragment
|
||||||
|
import im.vector.app.databinding.FragmentRoomSettingGenericBinding
|
||||||
|
import im.vector.app.features.home.AvatarRenderer
|
||||||
|
import im.vector.app.features.settings.VectorSettingsActivity
|
||||||
|
import org.matrix.android.sdk.api.session.room.notification.RoomNotificationState
|
||||||
|
import org.matrix.android.sdk.api.util.toMatrixItem
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
class RoomNotificationSettingsFragment @Inject constructor(
|
||||||
|
val viewModelFactory: RoomNotificationSettingsViewModel.Factory,
|
||||||
|
private val roomNotificationSettingsController: RoomNotificationSettingsController,
|
||||||
|
private val avatarRenderer: AvatarRenderer
|
||||||
|
) : VectorBaseFragment<FragmentRoomSettingGenericBinding>(),
|
||||||
|
RoomNotificationSettingsController.Callback {
|
||||||
|
|
||||||
|
private val viewModel: RoomNotificationSettingsViewModel by fragmentViewModel()
|
||||||
|
|
||||||
|
override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentRoomSettingGenericBinding {
|
||||||
|
return FragmentRoomSettingGenericBinding.inflate(inflater, container, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
setupToolbar(views.roomSettingsToolbar)
|
||||||
|
roomNotificationSettingsController.callback = this
|
||||||
|
views.roomSettingsRecyclerView.configureWith(roomNotificationSettingsController, hasFixedSize = true)
|
||||||
|
setupWaitingView()
|
||||||
|
observeViewEvents()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDestroyView() {
|
||||||
|
views.roomSettingsRecyclerView.cleanup()
|
||||||
|
roomNotificationSettingsController.callback = null
|
||||||
|
super.onDestroyView()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setupWaitingView() {
|
||||||
|
views.waitingView.waitingStatusText.setText(R.string.please_wait)
|
||||||
|
views.waitingView.waitingStatusText.isVisible = true
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun observeViewEvents() {
|
||||||
|
viewModel.observeViewEvents {
|
||||||
|
when (it) {
|
||||||
|
is RoomNotificationSettingsViewEvents.Failure -> displayErrorDialog(it.throwable)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun invalidate() = withState(viewModel) { viewState ->
|
||||||
|
roomNotificationSettingsController.setData(viewState)
|
||||||
|
views.waitingView.root.isVisible = viewState.isLoading
|
||||||
|
renderRoomSummary(viewState)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun didSelectRoomNotificationState(roomNotificationState: RoomNotificationState) {
|
||||||
|
viewModel.handle(RoomNotificationSettingsAction.SelectNotificationState(roomNotificationState))
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun didSelectAccountSettingsLink() {
|
||||||
|
navigator.openSettings(requireContext(), VectorSettingsActivity.EXTRA_DIRECT_ACCESS_NOTIFICATIONS)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun renderRoomSummary(state: RoomNotificationSettingsViewState) {
|
||||||
|
state.roomSummary()?.let {
|
||||||
|
views.roomSettingsToolbarTitleView.text = it.displayName
|
||||||
|
avatarRenderer.render(it.toMatrixItem(), views.roomSettingsToolbarAvatarImageView)
|
||||||
|
views.roomSettingsDecorationToolbarAvatarImageView.render(it.roomEncryptionTrustLevel)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,23 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2021 New Vector Ltd
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package im.vector.app.features.roomprofile.notifications
|
||||||
|
|
||||||
|
import im.vector.app.core.platform.VectorViewEvents
|
||||||
|
|
||||||
|
sealed class RoomNotificationSettingsViewEvents : VectorViewEvents {
|
||||||
|
data class Failure(val throwable: Throwable) : RoomNotificationSettingsViewEvents()
|
||||||
|
}
|
|
@ -0,0 +1,107 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2021 New Vector Ltd
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package im.vector.app.features.roomprofile.notifications
|
||||||
|
|
||||||
|
import androidx.lifecycle.viewModelScope
|
||||||
|
import com.airbnb.mvrx.FragmentViewModelContext
|
||||||
|
import com.airbnb.mvrx.MvRxViewModelFactory
|
||||||
|
import com.airbnb.mvrx.Success
|
||||||
|
import com.airbnb.mvrx.ViewModelContext
|
||||||
|
import dagger.assisted.Assisted
|
||||||
|
import dagger.assisted.AssistedFactory
|
||||||
|
import dagger.assisted.AssistedInject
|
||||||
|
import im.vector.app.core.platform.VectorViewModel
|
||||||
|
import im.vector.app.features.home.room.list.actions.RoomListQuickActionsBottomSheet
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import org.matrix.android.sdk.api.session.Session
|
||||||
|
import org.matrix.android.sdk.rx.rx
|
||||||
|
import org.matrix.android.sdk.rx.unwrap
|
||||||
|
|
||||||
|
class RoomNotificationSettingsViewModel @AssistedInject constructor(
|
||||||
|
@Assisted initialState: RoomNotificationSettingsViewState,
|
||||||
|
session: Session
|
||||||
|
) : VectorViewModel<RoomNotificationSettingsViewState, RoomNotificationSettingsAction, RoomNotificationSettingsViewEvents>(initialState) {
|
||||||
|
|
||||||
|
@AssistedFactory
|
||||||
|
interface Factory {
|
||||||
|
fun create(initialState: RoomNotificationSettingsViewState): RoomNotificationSettingsViewModel
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object : MvRxViewModelFactory<RoomNotificationSettingsViewModel, RoomNotificationSettingsViewState> {
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
override fun create(viewModelContext: ViewModelContext, state: RoomNotificationSettingsViewState): RoomNotificationSettingsViewModel {
|
||||||
|
val fragmentModelContext = (viewModelContext as FragmentViewModelContext)
|
||||||
|
return if (fragmentModelContext.fragment is RoomNotificationSettingsFragment) {
|
||||||
|
val fragment: RoomNotificationSettingsFragment = fragmentModelContext.fragment()
|
||||||
|
fragment.viewModelFactory.create(state)
|
||||||
|
} else {
|
||||||
|
val fragment: RoomListQuickActionsBottomSheet = fragmentModelContext.fragment()
|
||||||
|
fragment.roomNotificationSettingsViewModelFactory.create(state)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private val room = session.getRoom(initialState.roomId)!!
|
||||||
|
|
||||||
|
init {
|
||||||
|
observeSummary()
|
||||||
|
observeNotificationState()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun observeSummary() {
|
||||||
|
room.rx().liveRoomSummary()
|
||||||
|
.unwrap()
|
||||||
|
.execute { async ->
|
||||||
|
copy(roomSummary = async)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun observeNotificationState() {
|
||||||
|
room.rx()
|
||||||
|
.liveNotificationState()
|
||||||
|
.execute {
|
||||||
|
copy(notificationState = it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun handle(action: RoomNotificationSettingsAction) {
|
||||||
|
when (action) {
|
||||||
|
is RoomNotificationSettingsAction.SelectNotificationState -> handleSelectNotificationState(action)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun handleSelectNotificationState(action: RoomNotificationSettingsAction.SelectNotificationState) {
|
||||||
|
setState { copy(isLoading = true) }
|
||||||
|
viewModelScope.launch {
|
||||||
|
runCatching { room.setRoomNotificationState(action.notificationState) }
|
||||||
|
.fold(
|
||||||
|
{
|
||||||
|
setState {
|
||||||
|
copy(isLoading = false, notificationState = Success(action.notificationState))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
setState {
|
||||||
|
copy(isLoading = false)
|
||||||
|
}
|
||||||
|
_viewEvents.post(RoomNotificationSettingsViewEvents.Failure(it))
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,62 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2021 New Vector Ltd
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package im.vector.app.features.roomprofile.notifications
|
||||||
|
|
||||||
|
import com.airbnb.mvrx.Async
|
||||||
|
import com.airbnb.mvrx.MvRxState
|
||||||
|
import com.airbnb.mvrx.Success
|
||||||
|
import com.airbnb.mvrx.Uninitialized
|
||||||
|
import im.vector.app.features.home.room.list.actions.RoomListActionsArgs
|
||||||
|
import im.vector.app.features.roomprofile.RoomProfileArgs
|
||||||
|
import org.matrix.android.sdk.api.session.room.model.RoomSummary
|
||||||
|
import org.matrix.android.sdk.api.session.room.notification.RoomNotificationState
|
||||||
|
|
||||||
|
data class RoomNotificationSettingsViewState(
|
||||||
|
val roomId: String,
|
||||||
|
val roomSummary: Async<RoomSummary> = Uninitialized,
|
||||||
|
val isLoading: Boolean = false,
|
||||||
|
val notificationState: Async<RoomNotificationState> = Uninitialized
|
||||||
|
) : MvRxState {
|
||||||
|
constructor(args: RoomProfileArgs) : this(roomId = args.roomId)
|
||||||
|
constructor(args: RoomListActionsArgs) : this(roomId = args.roomId)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used to map this old room notification settings to the new options in v2.
|
||||||
|
*/
|
||||||
|
val RoomNotificationSettingsViewState.notificationStateMapped: Async<RoomNotificationState>
|
||||||
|
get() {
|
||||||
|
if ((roomSummary()?.isEncrypted == true && notificationState() == RoomNotificationState.MENTIONS_ONLY)
|
||||||
|
|| notificationState() == RoomNotificationState.ALL_MESSAGES) {
|
||||||
|
/** if in an encrypted room, mentions notifications are not supported so show "All Messages" as selected.
|
||||||
|
* Also in the new settings there is no notion of notifications without sound so it maps to noisy also
|
||||||
|
*/
|
||||||
|
return Success(RoomNotificationState.ALL_MESSAGES_NOISY)
|
||||||
|
}
|
||||||
|
return notificationState
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Used to enumerate the new settings in notification settings v2. Notifications without sound and mentions in encrypted rooms not supported.
|
||||||
|
*/
|
||||||
|
val RoomNotificationSettingsViewState.notificationOptions: List<RoomNotificationState>
|
||||||
|
get() {
|
||||||
|
return if (roomSummary()?.isEncrypted == true) {
|
||||||
|
listOf(RoomNotificationState.ALL_MESSAGES_NOISY, RoomNotificationState.MUTE)
|
||||||
|
} else {
|
||||||
|
listOf(RoomNotificationState.ALL_MESSAGES_NOISY, RoomNotificationState.MENTIONS_ONLY, RoomNotificationState.MUTE)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
style="@style/Widget.Vector.TextView.Body"
|
||||||
|
android:id="@+id/footerText"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:paddingStart="@dimen/layout_horizontal_margin"
|
||||||
|
android:paddingTop="@dimen/layout_vertical_margin"
|
||||||
|
android:paddingEnd="@dimen/layout_horizontal_margin"
|
||||||
|
android:paddingBottom="@dimen/layout_vertical_margin"
|
||||||
|
android:textColor="?vctr_content_secondary"
|
||||||
|
tools:text="@string/room_settings_room_notifications_encryption_notice" />
|
|
@ -0,0 +1,40 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:clickable="true"
|
||||||
|
android:focusable="true"
|
||||||
|
android:minHeight="64dp"
|
||||||
|
android:foreground="?attr/selectableItemBackground"
|
||||||
|
android:paddingStart="@dimen/layout_horizontal_margin"
|
||||||
|
android:paddingEnd="@dimen/layout_horizontal_margin">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/actionTitle"
|
||||||
|
style="@style/Widget.Vector.TextView.Subtitle"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginEnd="@dimen/layout_horizontal_margin"
|
||||||
|
android:ellipsize="end"
|
||||||
|
android:maxLines="2"
|
||||||
|
android:textColor="?vctr_content_primary"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toStartOf="@id/radioIcon"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
tools:text="@string/room_settings_all_messages" />
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/radioIcon"
|
||||||
|
android:layout_width="20dp"
|
||||||
|
android:layout_height="20dp"
|
||||||
|
android:contentDescription="@string/a11y_checked"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
tools:ignore="MissingPrefix"
|
||||||
|
tools:src="@drawable/ic_radio_on" />
|
||||||
|
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
@ -0,0 +1,14 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:id="@+id/headerText"
|
||||||
|
style="@style/Widget.Vector.TextView.Subtitle"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:paddingStart="@dimen/layout_horizontal_margin"
|
||||||
|
android:paddingTop="@dimen/layout_vertical_margin"
|
||||||
|
android:paddingEnd="@dimen/layout_horizontal_margin"
|
||||||
|
android:paddingBottom="@dimen/layout_vertical_margin"
|
||||||
|
android:textColor="?vctr_content_secondary"
|
||||||
|
android:textStyle="bold"
|
||||||
|
tools:text="@string/room_settings_room_notifications_notify_me" />
|
|
@ -1046,6 +1046,8 @@
|
||||||
<string name="room_settings_all_messages">All messages</string>
|
<string name="room_settings_all_messages">All messages</string>
|
||||||
<string name="room_settings_mention_only">Mentions only</string>
|
<string name="room_settings_mention_only">Mentions only</string>
|
||||||
<string name="room_settings_mute">Mute</string>
|
<string name="room_settings_mute">Mute</string>
|
||||||
|
<string name="room_settings_mention_and_keyword_only">Mentions & Keywords only</string>
|
||||||
|
<string name="room_settings_none">None</string>
|
||||||
<string name="room_settings_favourite">Favourite</string>
|
<string name="room_settings_favourite">Favourite</string>
|
||||||
<string name="room_settings_de_prioritize">De-prioritize</string>
|
<string name="room_settings_de_prioritize">De-prioritize</string>
|
||||||
<string name="room_settings_direct_chat">Direct Chat</string>
|
<string name="room_settings_direct_chat">Direct Chat</string>
|
||||||
|
@ -1436,6 +1438,10 @@
|
||||||
<string name="room_settings_category_access_visibility_title">Access and visibility</string>
|
<string name="room_settings_category_access_visibility_title">Access and visibility</string>
|
||||||
<string name="room_settings_directory_visibility">List this room in room directory</string>
|
<string name="room_settings_directory_visibility">List this room in room directory</string>
|
||||||
<string name="room_settings_room_notifications_title">Notifications</string>
|
<string name="room_settings_room_notifications_title">Notifications</string>
|
||||||
|
<string name="room_settings_room_notifications_notify_me">Notify me for</string>
|
||||||
|
<string name="room_settings_room_notifications_encryption_notice">Please note that mentions & keyword notifications are not available in encrypted rooms on mobile.</string>
|
||||||
|
<string name="room_settings_room_notifications_manage_notifications">You can manage notifications in %1$s.</string>
|
||||||
|
<string name="room_settings_room_notifications_account_settings">Account settings</string>
|
||||||
<string name="room_settings_room_access_rules_pref_title">Room Access</string>
|
<string name="room_settings_room_access_rules_pref_title">Room Access</string>
|
||||||
<string name="room_settings_room_read_history_rules_pref_title">Room History Readability</string>
|
<string name="room_settings_room_read_history_rules_pref_title">Room History Readability</string>
|
||||||
<string name="room_settings_room_read_history_rules_pref_dialog_title">Who can read history?</string>
|
<string name="room_settings_room_read_history_rules_pref_dialog_title">Who can read history?</string>
|
||||||
|
|
Loading…
Reference in New Issue