From 90e04d4358e733eab1530c0ec6f2fbf2552980cf Mon Sep 17 00:00:00 2001 From: David Langley Date: Fri, 23 Jul 2021 22:37:36 +0100 Subject: [PATCH] add header footer and fix toolbar --- .../NotificationSettingsFooterItem.kt | 60 +++++++++++++++++++ .../notifications}/RadioButtonItem.kt | 10 ++-- .../profiles/notifications/TextHeaderItem.kt | 49 +++++++++++++++ .../im/vector/app/core/extensions/TextView.kt | 17 ++++++ .../RoomNotificationSettingsController.kt | 18 +++++- .../RoomNotificationSettingsFragment.kt | 21 ++++++- .../RoomNotificationSettingsViewModel.kt | 12 +++- .../RoomNotificationSettingsViewState.kt | 3 +- .../res/layout/item_notifications_footer.xml | 25 ++++++++ .../src/main/res/layout/item_text_header.xml | 25 ++++++++ vector/src/main/res/values/strings.xml | 4 ++ 11 files changed, 234 insertions(+), 10 deletions(-) create mode 100644 vector/src/main/java/im/vector/app/core/epoxy/profiles/notifications/NotificationSettingsFooterItem.kt rename vector/src/main/java/im/vector/app/core/epoxy/{ => profiles/notifications}/RadioButtonItem.kt (89%) create mode 100644 vector/src/main/java/im/vector/app/core/epoxy/profiles/notifications/TextHeaderItem.kt create mode 100644 vector/src/main/res/layout/item_notifications_footer.xml create mode 100644 vector/src/main/res/layout/item_text_header.xml diff --git a/vector/src/main/java/im/vector/app/core/epoxy/profiles/notifications/NotificationSettingsFooterItem.kt b/vector/src/main/java/im/vector/app/core/epoxy/profiles/notifications/NotificationSettingsFooterItem.kt new file mode 100644 index 0000000000..540d1ff5bd --- /dev/null +++ b/vector/src/main/java/im/vector/app/core/epoxy/profiles/notifications/NotificationSettingsFooterItem.kt @@ -0,0 +1,60 @@ +/* + * 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() { + + @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 = StringBuilder(holder.view.context.getString(R.string.room_settings_room_notifications_manage_notifications, accountSettingsString)) + if (encrypted) { + val encryptionNotice = holder.view.context.getString(R.string.room_settings_room_notifications_encryption_notice) + manageNotificationsString.appendLine().append(encryptionNotice) + } + + holder.textView.setTextWithColoredPart( + manageNotificationsString.toString(), + accountSettingsString, + underline = true + ) { + clickListener?.invoke(holder.textView) + } + } + + class Holder : VectorEpoxyHolder() { + val textView by bind(R.id.footerText) + } +} diff --git a/vector/src/main/java/im/vector/app/core/epoxy/RadioButtonItem.kt b/vector/src/main/java/im/vector/app/core/epoxy/profiles/notifications/RadioButtonItem.kt similarity index 89% rename from vector/src/main/java/im/vector/app/core/epoxy/RadioButtonItem.kt rename to vector/src/main/java/im/vector/app/core/epoxy/profiles/notifications/RadioButtonItem.kt index e0c7028711..3afec2c5ce 100644 --- a/vector/src/main/java/im/vector/app/core/epoxy/RadioButtonItem.kt +++ b/vector/src/main/java/im/vector/app/core/epoxy/profiles/notifications/RadioButtonItem.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package im.vector.app.core.epoxy +package im.vector.app.core.epoxy.profiles.notifications import android.widget.ImageView import android.widget.TextView @@ -23,10 +23,12 @@ 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 + -/** - * A action for bottom sheet. - */ @EpoxyModelClass(layout = R.layout.item_radio) abstract class RadioButtonItem : VectorEpoxyModel() { diff --git a/vector/src/main/java/im/vector/app/core/epoxy/profiles/notifications/TextHeaderItem.kt b/vector/src/main/java/im/vector/app/core/epoxy/profiles/notifications/TextHeaderItem.kt new file mode 100644 index 0000000000..1f6cea0fd5 --- /dev/null +++ b/vector/src/main/java/im/vector/app/core/epoxy/profiles/notifications/TextHeaderItem.kt @@ -0,0 +1,49 @@ +/* + * 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() { + + @EpoxyAttribute + var text: String? = null + + @StringRes + @EpoxyAttribute + var textRes: Int? = null + + override fun bind(holder: Holder) { + super.bind(holder) + if (textRes != null) { + holder.textView.setText(textRes!!) + } else { + holder.textView.text = text + } + } + + class Holder : VectorEpoxyHolder() { + val textView by bind(R.id.headerText) + } +} diff --git a/vector/src/main/java/im/vector/app/core/extensions/TextView.kt b/vector/src/main/java/im/vector/app/core/extensions/TextView.kt index bb991ac32c..1c424f7071 100644 --- a/vector/src/main/java/im/vector/app/core/extensions/TextView.kt +++ b/vector/src/main/java/im/vector/app/core/extensions/TextView.kt @@ -65,6 +65,23 @@ fun TextView.setTextWithColoredPart(@StringRes fullTextRes: Int, val coloredPart = resources.getString(coloredTextRes) // Insert colored part into the full text 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 foregroundSpan = ForegroundColorSpan(color) diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/notifications/RoomNotificationSettingsController.kt b/vector/src/main/java/im/vector/app/features/roomprofile/notifications/RoomNotificationSettingsController.kt index 0e8d7cb2c1..6de4b586c0 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/notifications/RoomNotificationSettingsController.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/notifications/RoomNotificationSettingsController.kt @@ -19,7 +19,11 @@ 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.radioButtonItem +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 im.vector.app.core.resources.StringProvider +import im.vector.app.core.ui.list.genericHeaderItem import org.matrix.android.sdk.api.session.room.notification.RoomNotificationState import javax.inject.Inject @@ -27,6 +31,7 @@ class RoomNotificationSettingsController @Inject constructor() : TypedEpoxyContr interface Callback { fun didSelectRoomNotificationState(roomNotificationState: RoomNotificationState) + fun didSelectAccountSettingsLink() } var callback: Callback? = null @@ -47,6 +52,10 @@ class RoomNotificationSettingsController @Inject constructor() : TypedEpoxyContr 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 { @@ -58,5 +67,12 @@ class RoomNotificationSettingsController @Inject constructor() : TypedEpoxyContr } } } + notificationSettingsFooterItem { + id("roomNotificationSettingsFooter") + encrypted(data.roomEncrypted) + clickListener { + host.callback?.didSelectAccountSettingsLink() + } + } } } diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/notifications/RoomNotificationSettingsFragment.kt b/vector/src/main/java/im/vector/app/features/roomprofile/notifications/RoomNotificationSettingsFragment.kt index 44ae9a3395..8baef5d10c 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/notifications/RoomNotificationSettingsFragment.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/notifications/RoomNotificationSettingsFragment.kt @@ -27,12 +27,15 @@ import im.vector.app.R 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 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 roomNotificationSettingsViewModel: RoomNotificationSettingsViewModel.Factory, - val roomNotificationSettingsController: RoomNotificationSettingsController + val viewModelFactory: RoomNotificationSettingsViewModel.Factory, + private val roomNotificationSettingsController: RoomNotificationSettingsController, + private val avatarRenderer: AvatarRenderer ) : VectorBaseFragment(), RoomNotificationSettingsController.Callback { @@ -45,7 +48,6 @@ class RoomNotificationSettingsFragment @Inject constructor( override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) setupToolbar(views.roomSettingsToolbar) - views.roomSettingsToolbarTitleView.setText(R.string.settings_notifications) roomNotificationSettingsController.callback = this views.roomSettingsRecyclerView.configureWith(roomNotificationSettingsController, hasFixedSize = true) setupWaitingView() @@ -67,9 +69,22 @@ class RoomNotificationSettingsFragment @Inject constructor( 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()) + } + + private fun renderRoomSummary(state: RoomNotificationSettingsViewState) { + state.roomSummary()?.let { + views.roomSettingsToolbarTitleView.text = it.displayName + avatarRenderer.render(it.toMatrixItem(), views.roomSettingsToolbarAvatarImageView) + views.roomSettingsDecorationToolbarAvatarImageView.render(it.roomEncryptionTrustLevel) + } + } } diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/notifications/RoomNotificationSettingsViewModel.kt b/vector/src/main/java/im/vector/app/features/roomprofile/notifications/RoomNotificationSettingsViewModel.kt index c92d4dd422..ad71343c1f 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/notifications/RoomNotificationSettingsViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/notifications/RoomNotificationSettingsViewModel.kt @@ -28,6 +28,7 @@ import im.vector.app.core.platform.VectorViewModel 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, @@ -44,7 +45,7 @@ class RoomNotificationSettingsViewModel @AssistedInject constructor( @JvmStatic override fun create(viewModelContext: ViewModelContext, state: RoomNotificationSettingsViewState): RoomNotificationSettingsViewModel { val fragment: RoomNotificationSettingsFragment = (viewModelContext as FragmentViewModelContext).fragment() - return fragment.roomNotificationSettingsViewModel.create(state) + return fragment.viewModelFactory.create(state) } } @@ -52,6 +53,7 @@ class RoomNotificationSettingsViewModel @AssistedInject constructor( init { initEncrypted() + observeSummary() observeNotificationState() } @@ -61,6 +63,14 @@ class RoomNotificationSettingsViewModel @AssistedInject constructor( } } + private fun observeSummary() { + room.rx().liveRoomSummary() + .unwrap() + .execute { async -> + copy(roomSummary = async) + } + } + private fun observeNotificationState() { room.rx() .liveNotificationState() diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/notifications/RoomNotificationSettingsViewState.kt b/vector/src/main/java/im/vector/app/features/roomprofile/notifications/RoomNotificationSettingsViewState.kt index f67947ca9f..2f1916751f 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/notifications/RoomNotificationSettingsViewState.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/notifications/RoomNotificationSettingsViewState.kt @@ -20,14 +20,15 @@ import com.airbnb.mvrx.Async import com.airbnb.mvrx.MvRxState import com.airbnb.mvrx.Uninitialized 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 = Uninitialized, val isLoading: Boolean = false, val roomEncrypted: Boolean = false, val notificationState: Async = Uninitialized, - val avatarData: AvatarData? = null ) : MvRxState { constructor(args: RoomProfileArgs) : this(roomId = args.roomId) diff --git a/vector/src/main/res/layout/item_notifications_footer.xml b/vector/src/main/res/layout/item_notifications_footer.xml new file mode 100644 index 0000000000..8338358048 --- /dev/null +++ b/vector/src/main/res/layout/item_notifications_footer.xml @@ -0,0 +1,25 @@ + + + + + + \ No newline at end of file diff --git a/vector/src/main/res/layout/item_text_header.xml b/vector/src/main/res/layout/item_text_header.xml new file mode 100644 index 0000000000..15848f585e --- /dev/null +++ b/vector/src/main/res/layout/item_text_header.xml @@ -0,0 +1,25 @@ + + + + + + \ No newline at end of file diff --git a/vector/src/main/res/values/strings.xml b/vector/src/main/res/values/strings.xml index ef25329eed..5cf121b388 100644 --- a/vector/src/main/res/values/strings.xml +++ b/vector/src/main/res/values/strings.xml @@ -1408,6 +1408,10 @@ Access and visibility List this room in room directory Notifications + Notify me for + Please note that mentions & keyword notifications are not available in encrypted rooms on mobile. + You can manage notifications in %1$s. + Account settings Room Access Room History Readability Who can read history?