From 9811d6fefc0a038ab7073929db14f513d2fb0b2a Mon Sep 17 00:00:00 2001 From: David Langley Date: Tue, 20 Jul 2021 01:31:13 +0100 Subject: [PATCH 01/21] add viewmodel, business logic, events, state, fragment and layout --- .../RoomNotificationSettingsAction.kt | 25 +++++ .../RoomNotificationSettingsFragment.kt | 59 ++++++++++++ .../RoomNotificationSettingsViewEvents.kt | 24 +++++ .../RoomNotificationSettingsViewModel.kt | 91 +++++++++++++++++++ .../RoomNotificationSettingsViewState.kt | 40 ++++++++ .../view_edit_room_notification_settings.xml | 14 +++ 6 files changed, 253 insertions(+) create mode 100644 vector/src/main/java/im/vector/app/features/roomprofile/notifications/RoomNotificationSettingsAction.kt create mode 100644 vector/src/main/java/im/vector/app/features/roomprofile/notifications/RoomNotificationSettingsFragment.kt create mode 100644 vector/src/main/java/im/vector/app/features/roomprofile/notifications/RoomNotificationSettingsViewEvents.kt create mode 100644 vector/src/main/java/im/vector/app/features/roomprofile/notifications/RoomNotificationSettingsViewModel.kt create mode 100644 vector/src/main/java/im/vector/app/features/roomprofile/notifications/RoomNotificationSettingsViewState.kt create mode 100644 vector/src/main/res/layout/view_edit_room_notification_settings.xml diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/notifications/RoomNotificationSettingsAction.kt b/vector/src/main/java/im/vector/app/features/roomprofile/notifications/RoomNotificationSettingsAction.kt new file mode 100644 index 0000000000..9eca3ac5ea --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/roomprofile/notifications/RoomNotificationSettingsAction.kt @@ -0,0 +1,25 @@ +/* + * 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() + object Save: RoomNotificationSettingsAction() +} 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 new file mode 100644 index 0000000000..4ee9b60dc2 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/roomprofile/notifications/RoomNotificationSettingsFragment.kt @@ -0,0 +1,59 @@ +/* + * 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 com.airbnb.mvrx.fragmentViewModel +import im.vector.app.core.platform.VectorBaseFragment +import im.vector.app.databinding.ViewEditRoomNotificationSettingsBinding +import im.vector.app.features.home.AvatarRenderer +import javax.inject.Inject + +/** + * In this screen: + * - the account has been created and we propose the user to set an avatar and a display name + */ +class RoomNotificationSettingsFragment @Inject constructor( + val roomNotificationSettingsViewModel: RoomNotificationSettingsViewModel.Factory, + private val avatarRenderer: AvatarRenderer, +) : VectorBaseFragment() { + + private val viewModel: RoomNotificationSettingsViewModel by fragmentViewModel() + + override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): ViewEditRoomNotificationSettingsBinding { + return ViewEditRoomNotificationSettingsBinding.inflate(inflater, container, false) + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + observeViewEvents() + } + + private fun observeViewEvents() { + viewModel.observeViewEvents { + when (it) { + is RoomNotificationSettingsViewEvents.Failure -> displayErrorDialog(it.throwable) + RoomNotificationSettingsViewEvents.SaveComplete -> TODO() + } + } + } + +} diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/notifications/RoomNotificationSettingsViewEvents.kt b/vector/src/main/java/im/vector/app/features/roomprofile/notifications/RoomNotificationSettingsViewEvents.kt new file mode 100644 index 0000000000..1fe1a98340 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/roomprofile/notifications/RoomNotificationSettingsViewEvents.kt @@ -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.VectorViewEvents + +sealed class RoomNotificationSettingsViewEvents : VectorViewEvents { + data class Failure(val throwable: Throwable) : RoomNotificationSettingsViewEvents() + object SaveComplete : RoomNotificationSettingsViewEvents() +} 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 new file mode 100644 index 0000000000..d8fcc102d3 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/roomprofile/notifications/RoomNotificationSettingsViewModel.kt @@ -0,0 +1,91 @@ +/* + * 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.ViewModelContext +import dagger.assisted.Assisted +import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject +import im.vector.app.core.platform.VectorViewModel +import kotlinx.coroutines.launch +import org.matrix.android.sdk.api.session.room.Room +import org.matrix.android.sdk.rx.rx + +class RoomNotificationSettingsViewModel @AssistedInject constructor( + @Assisted initialState: RoomNotificationSettingsViewState, + private val room: Room +) : VectorViewModel(initialState) { + + @AssistedFactory + interface Factory { + fun create(initialState: RoomNotificationSettingsViewState): RoomNotificationSettingsViewModel + } + + companion object : MvRxViewModelFactory { + + @JvmStatic + override fun create(viewModelContext: ViewModelContext, state: RoomNotificationSettingsViewState): RoomNotificationSettingsViewModel? { + val fragment: RoomNotificationSettingsFragment = (viewModelContext as FragmentViewModelContext).fragment() + return fragment.roomNotificationSettingsViewModel.create(state) + } + } + + init { + observeNotificationState() + } + + private fun observeNotificationState() { + room.rx() + .liveNotificationState() + .subscribe{ + setState { + copy(notificationState = it ) + } + } + .disposeOnClear() + } + + override fun handle(action: RoomNotificationSettingsAction) { + when (action) { + is RoomNotificationSettingsAction.SelectNotificationState -> handleSelectNotificationState(action) + is RoomNotificationSettingsAction.Save -> handleSaveNotificationSelection(action) + } + } + + private fun handleSelectNotificationState(action: RoomNotificationSettingsAction.SelectNotificationState) { + setState { + copy(notificationState = action.notificationState) + } + } + + private fun handleSaveNotificationSelection(action: RoomNotificationSettingsAction.Save) { + setState { copy(isLoading = true) } + withState { state -> + viewModelScope.launch { + runCatching { room.setRoomNotificationState(state.notificationState) } + .onFailure { _viewEvents.post(RoomNotificationSettingsViewEvents.Failure(it)) } + setState { + copy(isLoading = false) + } + _viewEvents.post(RoomNotificationSettingsViewEvents.SaveComplete) + } + } + } +} 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 new file mode 100644 index 0000000000..4844521ca3 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/roomprofile/notifications/RoomNotificationSettingsViewState.kt @@ -0,0 +1,40 @@ +/* + * 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.MvRxState +import org.matrix.android.sdk.api.session.room.notification.RoomNotificationState + +data class RoomNotificationSettingsViewState( + val isLoading: Boolean, + val roomEncrypted: Boolean, + val notificationState: RoomNotificationState, + val avatarData: AvatarData? +) : MvRxState + +data class AvatarData ( + val displayName: String, + val avatarUrl: String +) + +val RoomNotificationSettingsViewState.notificationOptions: List + get() { + return if (roomEncrypted) { + listOf(RoomNotificationState.ALL_MESSAGES, RoomNotificationState.MUTE) + } else + RoomNotificationState.values().asList() + } diff --git a/vector/src/main/res/layout/view_edit_room_notification_settings.xml b/vector/src/main/res/layout/view_edit_room_notification_settings.xml new file mode 100644 index 0000000000..ecb5c742f7 --- /dev/null +++ b/vector/src/main/res/layout/view_edit_room_notification_settings.xml @@ -0,0 +1,14 @@ + + + + + + + From df7e6bd00dc8acb9290743fabbe643ca753b761b Mon Sep 17 00:00:00 2001 From: David Langley Date: Fri, 23 Jul 2021 14:16:32 +0100 Subject: [PATCH 02/21] add radioItem, RoomNotificationSettingsController and render it in fragment --- .../im/vector/app/core/di/FragmentModule.kt | 7 ++ .../vector/app/core/epoxy/RadioButtonItem.kt | 68 +++++++++++++++++++ .../roomprofile/RoomProfileActivity.kt | 18 +++-- .../roomprofile/RoomProfileFragment.kt | 12 +++- .../roomprofile/RoomProfileSharedAction.kt | 1 + .../RoomNotificationSettingsAction.kt | 1 - .../RoomNotificationSettingsController.kt | 62 +++++++++++++++++ .../RoomNotificationSettingsFragment.kt | 40 +++++++---- .../RoomNotificationSettingsViewEvents.kt | 1 - .../RoomNotificationSettingsViewModel.kt | 43 ++++++------ .../RoomNotificationSettingsViewState.kt | 19 ++++-- vector/src/main/res/layout/item_radio.xml | 40 +++++++++++ .../view_edit_room_notification_settings.xml | 21 ++++++ 13 files changed, 281 insertions(+), 52 deletions(-) create mode 100644 vector/src/main/java/im/vector/app/core/epoxy/RadioButtonItem.kt create mode 100644 vector/src/main/java/im/vector/app/features/roomprofile/notifications/RoomNotificationSettingsController.kt create mode 100644 vector/src/main/res/layout/item_radio.xml 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 8580543022..8020abd43b 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 @@ -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.banned.RoomBannedMemberListFragment 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.settings.RoomSettingsFragment import im.vector.app.features.roomprofile.uploads.RoomUploadsFragment @@ -715,6 +716,12 @@ interface FragmentModule { @FragmentKey(RoomBannedMemberListFragment::class) fun bindRoomBannedMemberListFragment(fragment: RoomBannedMemberListFragment): Fragment + @Binds + @IntoMap + @FragmentKey(RoomNotificationSettingsFragment::class) + fun bindRoomNotificationSettingsFragment(fragment: RoomNotificationSettingsFragment): Fragment + + @Binds @IntoMap @FragmentKey(SearchFragment::class) diff --git a/vector/src/main/java/im/vector/app/core/epoxy/RadioButtonItem.kt b/vector/src/main/java/im/vector/app/core/epoxy/RadioButtonItem.kt new file mode 100644 index 0000000000..e0c7028711 --- /dev/null +++ b/vector/src/main/java/im/vector/app/core/epoxy/RadioButtonItem.kt @@ -0,0 +1,68 @@ +/* + * 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 + +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 + +/** + * A action for bottom sheet. + */ +@EpoxyModelClass(layout = R.layout.item_radio) +abstract class RadioButtonItem : VectorEpoxyModel() { + + @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(R.id.actionTitle) + val radioImage by bind(R.id.radioIcon) + } +} diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileActivity.kt b/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileActivity.kt index 07ba442621..d8e080f6a4 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileActivity.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileActivity.kt @@ -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.settings.RoomSettingsFragment 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.uploads.RoomUploadsFragment import javax.inject.Inject @@ -107,12 +108,13 @@ class RoomProfileActivity : .observe() .subscribe { sharedAction -> when (sharedAction) { - RoomProfileSharedAction.OpenRoomMembers -> openRoomMembers() - RoomProfileSharedAction.OpenRoomSettings -> openRoomSettings() - RoomProfileSharedAction.OpenRoomAliasesSettings -> openRoomAlias() - RoomProfileSharedAction.OpenRoomPermissionsSettings -> openRoomPermissions() - RoomProfileSharedAction.OpenRoomUploads -> openRoomUploads() - RoomProfileSharedAction.OpenBannedRoomMembers -> openBannedRoomMembers() + RoomProfileSharedAction.OpenRoomMembers -> openRoomMembers() + RoomProfileSharedAction.OpenRoomSettings -> openRoomSettings() + RoomProfileSharedAction.OpenRoomAliasesSettings -> openRoomAlias() + RoomProfileSharedAction.OpenRoomPermissionsSettings -> openRoomPermissions() + RoomProfileSharedAction.OpenRoomUploads -> openRoomUploads() + RoomProfileSharedAction.OpenBannedRoomMembers -> openBannedRoomMembers() + RoomProfileSharedAction.OpenRoomNotificaitonSettings -> openRoomNotificationSettings() }.exhaustive } .disposeOnDestroy() @@ -162,6 +164,10 @@ class RoomProfileActivity : addFragmentToBackstack(R.id.simpleFragmentContainer, RoomBannedMemberListFragment::class.java, roomProfileArgs) } + private fun openRoomNotificationSettings() { + addFragmentToBackstack(R.id.simpleFragmentContainer, RoomNotificationSettingsFragment::class.java, roomProfileArgs) + } + override fun configure(toolbar: MaterialToolbar) { configureToolbar(toolbar) } diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileFragment.kt b/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileFragment.kt index 14ddf896ca..69e05f372d 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileFragment.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileFragment.kt @@ -30,6 +30,7 @@ import com.airbnb.mvrx.args import com.airbnb.mvrx.fragmentViewModel import com.airbnb.mvrx.withState import com.google.android.material.dialog.MaterialAlertDialogBuilder +import im.vector.app.BuildConfig import im.vector.app.R import im.vector.app.core.animations.AppBarStateChangeListener import im.vector.app.core.animations.MatrixItemAppBarStateChangeListener @@ -253,9 +254,14 @@ class RoomProfileFragment @Inject constructor( } override fun onNotificationsClicked() { - RoomListQuickActionsBottomSheet - .newInstance(roomProfileArgs.roomId, RoomListActionsArgs.Mode.NOTIFICATIONS) - .show(childFragmentManager, "ROOM_PROFILE_NOTIFICATIONS") + // TODO: Use BuildConfig here when merged in + if (false) { + RoomListQuickActionsBottomSheet + .newInstance(roomProfileArgs.roomId, RoomListActionsArgs.Mode.NOTIFICATIONS) + .show(childFragmentManager, "ROOM_PROFILE_NOTIFICATIONS") + } else { + roomProfileSharedActionViewModel.post(RoomProfileSharedAction.OpenRoomNotificaitonSettings) + } } override fun onUploadsClicked() { diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileSharedAction.kt b/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileSharedAction.kt index 2a5775d1af..6a9533a2eb 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileSharedAction.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileSharedAction.kt @@ -28,4 +28,5 @@ sealed class RoomProfileSharedAction : VectorSharedAction { object OpenRoomUploads : RoomProfileSharedAction() object OpenRoomMembers : RoomProfileSharedAction() object OpenBannedRoomMembers : RoomProfileSharedAction() + object OpenRoomNotificaitonSettings : RoomProfileSharedAction() } diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/notifications/RoomNotificationSettingsAction.kt b/vector/src/main/java/im/vector/app/features/roomprofile/notifications/RoomNotificationSettingsAction.kt index 9eca3ac5ea..10c8861183 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/notifications/RoomNotificationSettingsAction.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/notifications/RoomNotificationSettingsAction.kt @@ -21,5 +21,4 @@ import org.matrix.android.sdk.api.session.room.notification.RoomNotificationStat sealed class RoomNotificationSettingsAction : VectorViewModelAction { data class SelectNotificationState(val notificationState: RoomNotificationState): RoomNotificationSettingsAction() - object Save: RoomNotificationSettingsAction() } 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 new file mode 100644 index 0000000000..0e8d7cb2c1 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/roomprofile/notifications/RoomNotificationSettingsController.kt @@ -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 androidx.annotation.StringRes +import com.airbnb.epoxy.TypedEpoxyController +import im.vector.app.R +import im.vector.app.core.epoxy.radioButtonItem +import org.matrix.android.sdk.api.session.room.notification.RoomNotificationState +import javax.inject.Inject + +class RoomNotificationSettingsController @Inject constructor() : TypedEpoxyController() { + + interface Callback { + fun didSelectRoomNotificationState(roomNotificationState: RoomNotificationState) + } + + var callback: Callback? = null + + init { + setData(null) + } + + @StringRes + private fun titleForNotificationState(notificationState: RoomNotificationState) = when(notificationState) { + RoomNotificationState.ALL_MESSAGES_NOISY -> R.string.room_settings_all_messages_noisy + RoomNotificationState.ALL_MESSAGES -> R.string.room_settings_all_messages + RoomNotificationState.MENTIONS_ONLY -> R.string.room_settings_mention_only + RoomNotificationState.MUTE -> R.string.room_settings_mute + } + + override fun buildModels(data: RoomNotificationSettingsViewState?) { + val host = this + data ?: return + + data.notificationOptions.forEach { notificationState -> + val title = titleForNotificationState(notificationState) + radioButtonItem { + id(notificationState.name) + titleRes(title) + selected(data.notificationState() == notificationState) + listener { + host.callback?.didSelectRoomNotificationState(notificationState) + } + } + } + } +} 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 4ee9b60dc2..44ae9a3395 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 @@ -20,40 +20,56 @@ 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.configureWith import im.vector.app.core.platform.VectorBaseFragment -import im.vector.app.databinding.ViewEditRoomNotificationSettingsBinding -import im.vector.app.features.home.AvatarRenderer +import im.vector.app.databinding.FragmentRoomSettingGenericBinding +import org.matrix.android.sdk.api.session.room.notification.RoomNotificationState import javax.inject.Inject -/** - * In this screen: - * - the account has been created and we propose the user to set an avatar and a display name - */ class RoomNotificationSettingsFragment @Inject constructor( val roomNotificationSettingsViewModel: RoomNotificationSettingsViewModel.Factory, - private val avatarRenderer: AvatarRenderer, -) : VectorBaseFragment() { + val roomNotificationSettingsController: RoomNotificationSettingsController +) : VectorBaseFragment(), + RoomNotificationSettingsController.Callback { private val viewModel: RoomNotificationSettingsViewModel by fragmentViewModel() - override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): ViewEditRoomNotificationSettingsBinding { - return ViewEditRoomNotificationSettingsBinding.inflate(inflater, container, false) + 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) + views.roomSettingsToolbarTitleView.setText(R.string.settings_notifications) + roomNotificationSettingsController.callback = this + views.roomSettingsRecyclerView.configureWith(roomNotificationSettingsController, hasFixedSize = true) + setupWaitingView() observeViewEvents() } + 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) - RoomNotificationSettingsViewEvents.SaveComplete -> TODO() } } } + override fun invalidate() = withState(viewModel) { viewState -> + roomNotificationSettingsController.setData(viewState) + views.waitingView.root.isVisible = viewState.isLoading + } + + override fun didSelectRoomNotificationState(roomNotificationState: RoomNotificationState) { + viewModel.handle(RoomNotificationSettingsAction.SelectNotificationState(roomNotificationState)) + } } diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/notifications/RoomNotificationSettingsViewEvents.kt b/vector/src/main/java/im/vector/app/features/roomprofile/notifications/RoomNotificationSettingsViewEvents.kt index 1fe1a98340..dda858283b 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/notifications/RoomNotificationSettingsViewEvents.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/notifications/RoomNotificationSettingsViewEvents.kt @@ -20,5 +20,4 @@ import im.vector.app.core.platform.VectorViewEvents sealed class RoomNotificationSettingsViewEvents : VectorViewEvents { data class Failure(val throwable: Throwable) : RoomNotificationSettingsViewEvents() - object SaveComplete : RoomNotificationSettingsViewEvents() } 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 d8fcc102d3..c92d4dd422 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 @@ -19,18 +19,19 @@ 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 kotlinx.coroutines.launch -import org.matrix.android.sdk.api.session.room.Room +import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.rx.rx class RoomNotificationSettingsViewModel @AssistedInject constructor( @Assisted initialState: RoomNotificationSettingsViewState, - private val room: Room + session: Session ) : VectorViewModel(initialState) { @AssistedFactory @@ -41,50 +42,46 @@ class RoomNotificationSettingsViewModel @AssistedInject constructor( companion object : MvRxViewModelFactory { @JvmStatic - override fun create(viewModelContext: ViewModelContext, state: RoomNotificationSettingsViewState): RoomNotificationSettingsViewModel? { + override fun create(viewModelContext: ViewModelContext, state: RoomNotificationSettingsViewState): RoomNotificationSettingsViewModel { val fragment: RoomNotificationSettingsFragment = (viewModelContext as FragmentViewModelContext).fragment() return fragment.roomNotificationSettingsViewModel.create(state) } } + private val room = session.getRoom(initialState.roomId)!! + init { + initEncrypted() observeNotificationState() } + private fun initEncrypted() { + setState { + copy(roomEncrypted = room.isEncrypted()) + } + } + private fun observeNotificationState() { room.rx() .liveNotificationState() - .subscribe{ - setState { - copy(notificationState = it ) - } + .execute { + copy(notificationState = it) } - .disposeOnClear() } override fun handle(action: RoomNotificationSettingsAction) { when (action) { is RoomNotificationSettingsAction.SelectNotificationState -> handleSelectNotificationState(action) - is RoomNotificationSettingsAction.Save -> handleSaveNotificationSelection(action) } } private fun handleSelectNotificationState(action: RoomNotificationSettingsAction.SelectNotificationState) { - setState { - copy(notificationState = action.notificationState) - } - } - - private fun handleSaveNotificationSelection(action: RoomNotificationSettingsAction.Save) { setState { copy(isLoading = true) } - withState { state -> - viewModelScope.launch { - runCatching { room.setRoomNotificationState(state.notificationState) } - .onFailure { _viewEvents.post(RoomNotificationSettingsViewEvents.Failure(it)) } - setState { - copy(isLoading = false) - } - _viewEvents.post(RoomNotificationSettingsViewEvents.SaveComplete) + viewModelScope.launch { + runCatching { room.setRoomNotificationState(action.notificationState) } + .onFailure { _viewEvents.post(RoomNotificationSettingsViewEvents.Failure(it)) } + setState { + copy(isLoading = false, notificationState = Success(action.notificationState)) } } } 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 4844521ca3..f67947ca9f 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 @@ -16,15 +16,22 @@ package im.vector.app.features.roomprofile.notifications +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.notification.RoomNotificationState data class RoomNotificationSettingsViewState( - val isLoading: Boolean, - val roomEncrypted: Boolean, - val notificationState: RoomNotificationState, - val avatarData: AvatarData? -) : MvRxState + val roomId: String, + val isLoading: Boolean = false, + val roomEncrypted: Boolean = false, + val notificationState: Async = Uninitialized, + val avatarData: AvatarData? = null +) : MvRxState { + + constructor(args: RoomProfileArgs) : this(roomId = args.roomId) +} data class AvatarData ( val displayName: String, @@ -34,7 +41,7 @@ data class AvatarData ( val RoomNotificationSettingsViewState.notificationOptions: List get() { return if (roomEncrypted) { - listOf(RoomNotificationState.ALL_MESSAGES, RoomNotificationState.MUTE) + listOf(RoomNotificationState.ALL_MESSAGES_NOISY, RoomNotificationState.ALL_MESSAGES, RoomNotificationState.MUTE) } else RoomNotificationState.values().asList() } diff --git a/vector/src/main/res/layout/item_radio.xml b/vector/src/main/res/layout/item_radio.xml new file mode 100644 index 0000000000..4cd5312dc0 --- /dev/null +++ b/vector/src/main/res/layout/item_radio.xml @@ -0,0 +1,40 @@ + + + + + + + + \ No newline at end of file diff --git a/vector/src/main/res/layout/view_edit_room_notification_settings.xml b/vector/src/main/res/layout/view_edit_room_notification_settings.xml index ecb5c742f7..3d4cafc40f 100644 --- a/vector/src/main/res/layout/view_edit_room_notification_settings.xml +++ b/vector/src/main/res/layout/view_edit_room_notification_settings.xml @@ -10,5 +10,26 @@ android:layout_height="wrap_content" android:orientation="vertical"> + + + + + + From 90e04d4358e733eab1530c0ec6f2fbf2552980cf Mon Sep 17 00:00:00 2001 From: David Langley Date: Fri, 23 Jul 2021 22:37:36 +0100 Subject: [PATCH 03/21] 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? From 57c32502e55c8463dad7a73f226257e24a757114 Mon Sep 17 00:00:00 2001 From: David Langley Date: Fri, 23 Jul 2021 23:18:39 +0100 Subject: [PATCH 04/21] change notification state options as outlined in v2 --- .../RoomNotificationSettingsController.kt | 12 +++++----- .../RoomNotificationSettingsViewState.kt | 22 ++++++++++++++++--- vector/src/main/res/values/strings.xml | 2 ++ 3 files changed, 27 insertions(+), 9 deletions(-) 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 6de4b586c0..cad8ef8287 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 @@ -41,11 +41,11 @@ class RoomNotificationSettingsController @Inject constructor() : TypedEpoxyContr } @StringRes - private fun titleForNotificationState(notificationState: RoomNotificationState) = when(notificationState) { - RoomNotificationState.ALL_MESSAGES_NOISY -> R.string.room_settings_all_messages_noisy - RoomNotificationState.ALL_MESSAGES -> R.string.room_settings_all_messages - RoomNotificationState.MENTIONS_ONLY -> R.string.room_settings_mention_only - RoomNotificationState.MUTE -> R.string.room_settings_mute + 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 } override fun buildModels(data: RoomNotificationSettingsViewState?) { @@ -61,7 +61,7 @@ class RoomNotificationSettingsController @Inject constructor() : TypedEpoxyContr radioButtonItem { id(notificationState.name) titleRes(title) - selected(data.notificationState() == notificationState) + selected(data.notificationStateMapped() == notificationState) listener { host.callback?.didSelectRoomNotificationState(notificationState) } 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 2f1916751f..bff1a26595 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 @@ -18,6 +18,7 @@ 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.roomprofile.RoomProfileArgs import org.matrix.android.sdk.api.session.room.model.RoomSummary @@ -30,7 +31,6 @@ data class RoomNotificationSettingsViewState( val roomEncrypted: Boolean = false, val notificationState: Async = Uninitialized, ) : MvRxState { - constructor(args: RoomProfileArgs) : this(roomId = args.roomId) } @@ -39,10 +39,26 @@ data class AvatarData ( val avatarUrl: String ) +/** + * Used to map this old room notification settings to the new options in v2. + */ +val RoomNotificationSettingsViewState.notificationStateMapped: Async + get() { + if ((roomEncrypted && 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 get() { return if (roomEncrypted) { - listOf(RoomNotificationState.ALL_MESSAGES_NOISY, RoomNotificationState.ALL_MESSAGES, RoomNotificationState.MUTE) + listOf(RoomNotificationState.ALL_MESSAGES_NOISY, RoomNotificationState.MUTE) } else - RoomNotificationState.values().asList() + listOf(RoomNotificationState.ALL_MESSAGES_NOISY, RoomNotificationState.MENTIONS_ONLY, RoomNotificationState.MUTE) } diff --git a/vector/src/main/res/values/strings.xml b/vector/src/main/res/values/strings.xml index 5cf121b388..05980c8914 100644 --- a/vector/src/main/res/values/strings.xml +++ b/vector/src/main/res/values/strings.xml @@ -1037,6 +1037,8 @@ All messages Mentions only Mute + Mentions & Keywords only + None Favourite De-prioritize Direct Chat From 42a1ed2abe05b04dd7730126a8eef7ff2b213e99 Mon Sep 17 00:00:00 2001 From: David Langley Date: Fri, 23 Jul 2021 23:22:06 +0100 Subject: [PATCH 05/21] remove unneeded layout file --- .../RoomNotificationSettingsController.kt | 2 -- .../view_edit_room_notification_settings.xml | 35 ------------------- 2 files changed, 37 deletions(-) delete mode 100644 vector/src/main/res/layout/view_edit_room_notification_settings.xml 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 cad8ef8287..e6c57523e5 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 @@ -22,8 +22,6 @@ 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 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 diff --git a/vector/src/main/res/layout/view_edit_room_notification_settings.xml b/vector/src/main/res/layout/view_edit_room_notification_settings.xml deleted file mode 100644 index 3d4cafc40f..0000000000 --- a/vector/src/main/res/layout/view_edit_room_notification_settings.xml +++ /dev/null @@ -1,35 +0,0 @@ - - - - - - - - - - - - - From a76a936e21db572737101d9fb5edd927bdc70476 Mon Sep 17 00:00:00 2001 From: David Langley Date: Mon, 26 Jul 2021 22:54:32 +0100 Subject: [PATCH 06/21] implement bottom sheet and error handling --- .../home/room/list/RoomListFragment.kt | 12 ++- .../RoomListQuickActionsBottomSheet.kt | 36 ++++++++- .../RoomListQuickActionsEpoxyController.kt | 65 ++++++++++++---- .../RoomListQuickActionsSharedAction.kt | 7 +- .../list/actions/RoomListQuickActionsState.kt | 33 -------- .../actions/RoomListQuickActionsViewModel.kt | 78 ------------------- .../roomprofile/RoomProfileFragment.kt | 1 - .../RoomNotificationSettingsViewModel.kt | 11 ++- .../RoomNotificationSettingsViewState.kt | 2 + 9 files changed, 108 insertions(+), 137 deletions(-) delete mode 100644 vector/src/main/java/im/vector/app/features/home/room/list/actions/RoomListQuickActionsState.kt delete mode 100644 vector/src/main/java/im/vector/app/features/home/room/list/actions/RoomListQuickActionsViewModel.kt diff --git a/vector/src/main/java/im/vector/app/features/home/room/list/RoomListFragment.kt b/vector/src/main/java/im/vector/app/features/home/room/list/RoomListFragment.kt index fdfe171439..b053b5b825 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/list/RoomListFragment.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/list/RoomListFragment.kt @@ -473,9 +473,17 @@ class RoomListFragment @Inject constructor( // refresh footer footerController.setData(it) } - RoomListQuickActionsBottomSheet + val bottomSheet = RoomListQuickActionsBottomSheet .newInstance(room.roomId, RoomListActionsArgs.Mode.FULL) - .show(childFragmentManager, "ROOM_LIST_QUICK_ACTIONS") + bottomSheet.listener = object : RoomListQuickActionsBottomSheet.Listener { + override fun handleFailure(throwable: Throwable) { + showFailure(throwable) + } + } + + bottomSheet.show(childFragmentManager, "ROOM_LIST_QUICK_ACTIONS") + + return true } diff --git a/vector/src/main/java/im/vector/app/features/home/room/list/actions/RoomListQuickActionsBottomSheet.kt b/vector/src/main/java/im/vector/app/features/home/room/list/actions/RoomListQuickActionsBottomSheet.kt index 94f9aaf496..463e5ad94a 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/list/actions/RoomListQuickActionsBottomSheet.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/list/actions/RoomListQuickActionsBottomSheet.kt @@ -22,6 +22,7 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import androidx.recyclerview.widget.RecyclerView +import com.airbnb.mvrx.args import com.airbnb.mvrx.fragmentViewModel import com.airbnb.mvrx.withState import im.vector.app.core.di.ScreenComponent @@ -30,7 +31,12 @@ import im.vector.app.core.extensions.configureWith import im.vector.app.core.platform.VectorBaseBottomSheetDialogFragment import im.vector.app.databinding.BottomSheetGenericListBinding 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 im.vector.app.features.roomprofile.notifications.RoomNotificationSettingsViewState import kotlinx.parcelize.Parcelize +import org.matrix.android.sdk.api.session.room.notification.RoomNotificationState import javax.inject.Inject @Parcelize @@ -45,6 +51,11 @@ data class RoomListActionsArgs( } } +data class RoomListQuickActionViewState( + val roomListActionsArgs: RoomListActionsArgs, + val notificationSettingsViewState: RoomNotificationSettingsViewState +) + /** * Bottom sheet fragment that shows room information with list of contextual actions */ @@ -52,15 +63,20 @@ class RoomListQuickActionsBottomSheet : VectorBaseBottomSheetDialogFragment(), RoomListQuickActionsEpoxyController.Listener { + interface Listener { + fun handleFailure(throwable: Throwable) + } private lateinit var sharedActionViewModel: RoomListQuickActionsSharedActionViewModel @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 navigator: Navigator - 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 + var listener: Listener? = null override fun injectWith(injector: ScreenComponent) { injector.inject(this) @@ -80,6 +96,12 @@ class RoomListQuickActionsBottomSheet : disableItemAnimation = true ) roomListActionsEpoxyController.listener = this + + viewModel.observeViewEvents { + when(it){ + is RoomNotificationSettingsViewEvents.Failure -> listener?.handleFailure(it.throwable) + } + } } override fun onDestroyView() { @@ -89,7 +111,11 @@ class RoomListQuickActionsBottomSheet : } override fun invalidate() = withState(viewModel) { - roomListActionsEpoxyController.setData(it) + val roomListViewState = RoomListQuickActionViewState( + roomListActionsArgs, + it + ) + roomListActionsEpoxyController.setData(roomListViewState) super.invalidate() } @@ -103,6 +129,10 @@ class RoomListQuickActionsBottomSheet : } } + override fun didSelectRoomNotificationState(roomNotificationState: RoomNotificationState) { + viewModel.handle(RoomNotificationSettingsAction.SelectNotificationState(roomNotificationState)) + } + companion object { fun newInstance(roomId: String, mode: RoomListActionsArgs.Mode): RoomListQuickActionsBottomSheet { return RoomListQuickActionsBottomSheet().apply { diff --git a/vector/src/main/java/im/vector/app/features/home/room/list/actions/RoomListQuickActionsEpoxyController.kt b/vector/src/main/java/im/vector/app/features/home/room/list/actions/RoomListQuickActionsEpoxyController.kt index 4604159338..0bca6639a0 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/list/actions/RoomListQuickActionsEpoxyController.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/list/actions/RoomListQuickActionsEpoxyController.kt @@ -15,13 +15,18 @@ */ package im.vector.app.features.home.room.list.actions +import androidx.annotation.StringRes import com.airbnb.epoxy.TypedEpoxyController +import im.vector.app.R import im.vector.app.core.epoxy.bottomSheetDividerItem import im.vector.app.core.epoxy.bottomsheet.bottomSheetActionItem 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.StringProvider 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.util.toMatrixItem import javax.inject.Inject @@ -33,16 +38,26 @@ class RoomListQuickActionsEpoxyController @Inject constructor( private val avatarRenderer: AvatarRenderer, private val colorProvider: ColorProvider, private val stringProvider: StringProvider -) : TypedEpoxyController() { +) : TypedEpoxyController() { var listener: Listener? = null - override fun buildModels(state: RoomListQuickActionsState) { - val roomSummary = state.roomSummary() ?: return - val host = this - val showAll = state.mode == RoomListActionsArgs.Mode.FULL + @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 + } - if (showAll) { + override fun buildModels(state: RoomListQuickActionViewState) { + val notificationViewState = state.notificationSettingsViewState + val roomSummary = notificationViewState.roomSummary() ?: return + val host = this + val showFull = state.roomListActionsArgs.mode == RoomListActionsArgs.Mode.FULL + var isV2 = true + + if (showFull || isV2) { // Preview, favorite, settings bottomSheetRoomPreviewItem { id("room_preview") @@ -63,15 +78,30 @@ class RoomListQuickActionsEpoxyController @Inject constructor( } } - val selectedRoomState = state.roomNotificationState() - 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) { - RoomListQuickActionsSharedAction.Leave(roomSummary.roomId).toBottomSheetItem(5) + if (isV2) { + notificationViewState.notificationOptions.forEach { notificationState -> + val title = titleForNotificationState(notificationState) + radioButtonItem { + 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 (showFull || isV2) { + RoomListQuickActionsSharedAction.Leave(roomSummary.roomId, showIcon = !isV2).toBottomSheetItem(5) + } + } private fun RoomListQuickActionsSharedAction.toBottomSheetItem(index: Int, roomNotificationState: RoomNotificationState? = null) { @@ -86,7 +116,11 @@ class RoomListQuickActionsEpoxyController @Inject constructor( return bottomSheetActionItem { id("action_$index") selected(selected) - iconRes(iconResId) + if(iconResId != null){ + iconRes(iconResId) + } else{ + showIcon(false) + } textRes(titleRes) destructive(this@toBottomSheetItem.destructive) listener { host.listener?.didSelectMenuAction(this@toBottomSheetItem) } @@ -95,5 +129,6 @@ class RoomListQuickActionsEpoxyController @Inject constructor( interface Listener { fun didSelectMenuAction(quickAction: RoomListQuickActionsSharedAction) + fun didSelectRoomNotificationState(roomNotificationState: RoomNotificationState) } } diff --git a/vector/src/main/java/im/vector/app/features/home/room/list/actions/RoomListQuickActionsSharedAction.kt b/vector/src/main/java/im/vector/app/features/home/room/list/actions/RoomListQuickActionsSharedAction.kt index 075dca0c52..20c3087f54 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/list/actions/RoomListQuickActionsSharedAction.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/list/actions/RoomListQuickActionsSharedAction.kt @@ -21,9 +21,10 @@ import androidx.annotation.StringRes import im.vector.app.R import im.vector.app.core.platform.VectorSharedAction + sealed class RoomListQuickActionsSharedAction( @StringRes val titleRes: Int, - @DrawableRes val iconResId: Int, + @DrawableRes val iconResId: Int?, val destructive: Boolean = false) : VectorSharedAction { @@ -60,9 +61,9 @@ sealed class RoomListQuickActionsSharedAction( R.string.room_list_quick_actions_favorite_add, 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.drawable.ic_room_actions_leave, + if (showIcon) R.drawable.ic_room_actions_leave else null, true ) } diff --git a/vector/src/main/java/im/vector/app/features/home/room/list/actions/RoomListQuickActionsState.kt b/vector/src/main/java/im/vector/app/features/home/room/list/actions/RoomListQuickActionsState.kt deleted file mode 100644 index 2731620cec..0000000000 --- a/vector/src/main/java/im/vector/app/features/home/room/list/actions/RoomListQuickActionsState.kt +++ /dev/null @@ -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 = Uninitialized, - val roomNotificationState: Async = Uninitialized -) : MvRxState { - - constructor(args: RoomListActionsArgs) : this(roomId = args.roomId, mode = args.mode) -} diff --git a/vector/src/main/java/im/vector/app/features/home/room/list/actions/RoomListQuickActionsViewModel.kt b/vector/src/main/java/im/vector/app/features/home/room/list/actions/RoomListQuickActionsViewModel.kt deleted file mode 100644 index 75e9459d2c..0000000000 --- a/vector/src/main/java/im/vector/app/features/home/room/list/actions/RoomListQuickActionsViewModel.kt +++ /dev/null @@ -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(initialState) { - - @AssistedFactory - interface Factory { - fun create(initialState: RoomListQuickActionsState): RoomListQuickActionsViewModel - } - - companion object : MvRxViewModelFactory { - - @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 - } -} diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileFragment.kt b/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileFragment.kt index 69e05f372d..7030e96c6f 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileFragment.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileFragment.kt @@ -30,7 +30,6 @@ import com.airbnb.mvrx.args import com.airbnb.mvrx.fragmentViewModel import com.airbnb.mvrx.withState import com.google.android.material.dialog.MaterialAlertDialogBuilder -import im.vector.app.BuildConfig import im.vector.app.R import im.vector.app.core.animations.AppBarStateChangeListener import im.vector.app.core.animations.MatrixItemAppBarStateChangeListener 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 ad71343c1f..5d23931d88 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 @@ -25,6 +25,7 @@ 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 @@ -44,8 +45,14 @@ class RoomNotificationSettingsViewModel @AssistedInject constructor( @JvmStatic override fun create(viewModelContext: ViewModelContext, state: RoomNotificationSettingsViewState): RoomNotificationSettingsViewModel { - val fragment: RoomNotificationSettingsFragment = (viewModelContext as FragmentViewModelContext).fragment() - return fragment.viewModelFactory.create(state) + 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) + } } } 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 bff1a26595..a88dc84078 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,6 +20,7 @@ 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 @@ -32,6 +33,7 @@ data class RoomNotificationSettingsViewState( val notificationState: Async = Uninitialized, ) : MvRxState { constructor(args: RoomProfileArgs) : this(roomId = args.roomId) + constructor(args: RoomListActionsArgs) : this(roomId = args.roomId) } data class AvatarData ( From 92a44cd9d61c369ce79d34091b816ca543fbc566 Mon Sep 17 00:00:00 2001 From: David Langley Date: Wed, 11 Aug 2021 13:13:38 +0100 Subject: [PATCH 07/21] add build config, use single variable in grandle file and update source to use feature flag --- vector/build.gradle | 9 +++++++-- .../list/actions/RoomListQuickActionsEpoxyController.kt | 3 ++- .../app/features/roomprofile/RoomProfileFragment.kt | 8 ++++---- 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/vector/build.gradle b/vector/build.gradle index 5b5f65e9dd..311f7ed172 100644 --- a/vector/build.gradle +++ b/vector/build.gradle @@ -142,9 +142,14 @@ android { resValue "bool", "useLoginV1", "true" resValue "bool", "useLoginV2", "false" + + // NotificationSettingsV2 is disabled. To be released in conjunction with iOS/Web - resValue "bool", "useNotificationSettingsV1", "true" - resValue "bool", "useNotificationSettingsV2", "false" + def 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" diff --git a/vector/src/main/java/im/vector/app/features/home/room/list/actions/RoomListQuickActionsEpoxyController.kt b/vector/src/main/java/im/vector/app/features/home/room/list/actions/RoomListQuickActionsEpoxyController.kt index 0bca6639a0..ccfe0fdf71 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/list/actions/RoomListQuickActionsEpoxyController.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/list/actions/RoomListQuickActionsEpoxyController.kt @@ -17,6 +17,7 @@ package im.vector.app.features.home.room.list.actions import androidx.annotation.StringRes 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.bottomsheet.bottomSheetActionItem @@ -55,7 +56,7 @@ class RoomListQuickActionsEpoxyController @Inject constructor( val roomSummary = notificationViewState.roomSummary() ?: return val host = this val showFull = state.roomListActionsArgs.mode == RoomListActionsArgs.Mode.FULL - var isV2 = true + var isV2 = BuildConfig.USE_NOTIFICATION_SETTINGS_V2 if (showFull || isV2) { // Preview, favorite, settings diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileFragment.kt b/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileFragment.kt index 7030e96c6f..ea5bbd2e84 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileFragment.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileFragment.kt @@ -30,6 +30,7 @@ import com.airbnb.mvrx.args import com.airbnb.mvrx.fragmentViewModel import com.airbnb.mvrx.withState import com.google.android.material.dialog.MaterialAlertDialogBuilder +import im.vector.app.BuildConfig import im.vector.app.R import im.vector.app.core.animations.AppBarStateChangeListener import im.vector.app.core.animations.MatrixItemAppBarStateChangeListener @@ -253,13 +254,12 @@ class RoomProfileFragment @Inject constructor( } override fun onNotificationsClicked() { - // TODO: Use BuildConfig here when merged in - if (false) { + if (BuildConfig.USE_NOTIFICATION_SETTINGS_V2) { + roomProfileSharedActionViewModel.post(RoomProfileSharedAction.OpenRoomNotificaitonSettings) + } else { RoomListQuickActionsBottomSheet .newInstance(roomProfileArgs.roomId, RoomListActionsArgs.Mode.NOTIFICATIONS) .show(childFragmentManager, "ROOM_PROFILE_NOTIFICATIONS") - } else { - roomProfileSharedActionViewModel.post(RoomProfileSharedAction.OpenRoomNotificaitonSettings) } } From 00275d936763b6d09ab78e0fd29278af6b301195 Mon Sep 17 00:00:00 2001 From: David Langley Date: Wed, 11 Aug 2021 14:31:00 +0100 Subject: [PATCH 08/21] Cleanup and link --- .../im/vector/app/core/di/FragmentModule.kt | 1 - .../NotificationSettingsFooterItem.kt | 2 -- .../profiles/notifications/RadioButtonItem.kt | 1 - .../profiles/notifications/TextHeaderItem.kt | 5 ++-- .../home/room/list/RoomListFragment.kt | 1 - .../actions/RoomListQuickActionViewState.kt | 24 +++++++++++++++++++ .../RoomListQuickActionsBottomSheet.kt | 8 +------ .../RoomListQuickActionsEpoxyController.kt | 22 ++++++++--------- .../RoomListQuickActionsSharedAction.kt | 3 +-- .../RoomNotificationSettingsController.kt | 16 ++++++------- .../RoomNotificationSettingsViewState.kt | 7 +++--- 11 files changed, 51 insertions(+), 39 deletions(-) create mode 100644 vector/src/main/java/im/vector/app/features/home/room/list/actions/RoomListQuickActionViewState.kt 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 991807bbef..6b42f1e428 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 @@ -723,7 +723,6 @@ interface FragmentModule { @FragmentKey(RoomNotificationSettingsFragment::class) fun bindRoomNotificationSettingsFragment(fragment: RoomNotificationSettingsFragment): Fragment - @Binds @IntoMap @FragmentKey(SearchFragment::class) 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 index 540d1ff5bd..c882cf8615 100644 --- 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 @@ -36,8 +36,6 @@ abstract class NotificationSettingsFooterItem : 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 index 1f6cea0fd5..2dfe7be2e6 100644 --- 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 @@ -36,8 +36,9 @@ abstract class TextHeaderItem : VectorEpoxyModel() { override fun bind(holder: Holder) { super.bind(holder) - if (textRes != null) { - holder.textView.setText(textRes!!) + val textResource = textRes + if (textResource != null) { + holder.textView.setText(textResource) } else { holder.textView.text = text } diff --git a/vector/src/main/java/im/vector/app/features/home/room/list/RoomListFragment.kt b/vector/src/main/java/im/vector/app/features/home/room/list/RoomListFragment.kt index b053b5b825..344a4abbac 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/list/RoomListFragment.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/list/RoomListFragment.kt @@ -483,7 +483,6 @@ class RoomListFragment @Inject constructor( bottomSheet.show(childFragmentManager, "ROOM_LIST_QUICK_ACTIONS") - return true } diff --git a/vector/src/main/java/im/vector/app/features/home/room/list/actions/RoomListQuickActionViewState.kt b/vector/src/main/java/im/vector/app/features/home/room/list/actions/RoomListQuickActionViewState.kt new file mode 100644 index 0000000000..d063e07ed7 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/home/room/list/actions/RoomListQuickActionViewState.kt @@ -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 +) diff --git a/vector/src/main/java/im/vector/app/features/home/room/list/actions/RoomListQuickActionsBottomSheet.kt b/vector/src/main/java/im/vector/app/features/home/room/list/actions/RoomListQuickActionsBottomSheet.kt index 463e5ad94a..ccbed264e3 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/list/actions/RoomListQuickActionsBottomSheet.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/list/actions/RoomListQuickActionsBottomSheet.kt @@ -34,7 +34,6 @@ 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 im.vector.app.features.roomprofile.notifications.RoomNotificationSettingsViewState import kotlinx.parcelize.Parcelize import org.matrix.android.sdk.api.session.room.notification.RoomNotificationState import javax.inject.Inject @@ -51,11 +50,6 @@ data class RoomListActionsArgs( } } -data class RoomListQuickActionViewState( - val roomListActionsArgs: RoomListActionsArgs, - val notificationSettingsViewState: RoomNotificationSettingsViewState -) - /** * Bottom sheet fragment that shows room information with list of contextual actions */ @@ -98,7 +92,7 @@ class RoomListQuickActionsBottomSheet : roomListActionsEpoxyController.listener = this viewModel.observeViewEvents { - when(it){ + when (it) { is RoomNotificationSettingsViewEvents.Failure -> listener?.handleFailure(it.throwable) } } diff --git a/vector/src/main/java/im/vector/app/features/home/room/list/actions/RoomListQuickActionsEpoxyController.kt b/vector/src/main/java/im/vector/app/features/home/room/list/actions/RoomListQuickActionsEpoxyController.kt index ccfe0fdf71..b109f0466d 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/list/actions/RoomListQuickActionsEpoxyController.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/list/actions/RoomListQuickActionsEpoxyController.kt @@ -43,20 +43,12 @@ class RoomListQuickActionsEpoxyController @Inject constructor( var listener: Listener? = null - @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 - } - override fun buildModels(state: RoomListQuickActionViewState) { val notificationViewState = state.notificationSettingsViewState val roomSummary = notificationViewState.roomSummary() ?: return val host = this val showFull = state.roomListActionsArgs.mode == RoomListActionsArgs.Mode.FULL - var isV2 = BuildConfig.USE_NOTIFICATION_SETTINGS_V2 + val isV2 = BuildConfig.USE_NOTIFICATION_SETTINGS_V2 if (showFull || isV2) { // Preview, favorite, settings @@ -102,9 +94,15 @@ class RoomListQuickActionsEpoxyController @Inject constructor( if (showFull || isV2) { 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) { val host = this@RoomListQuickActionsEpoxyController val selected = when (this) { @@ -117,9 +115,9 @@ class RoomListQuickActionsEpoxyController @Inject constructor( return bottomSheetActionItem { id("action_$index") selected(selected) - if(iconResId != null){ + if (iconResId != null) { iconRes(iconResId) - } else{ + } else { showIcon(false) } textRes(titleRes) diff --git a/vector/src/main/java/im/vector/app/features/home/room/list/actions/RoomListQuickActionsSharedAction.kt b/vector/src/main/java/im/vector/app/features/home/room/list/actions/RoomListQuickActionsSharedAction.kt index 20c3087f54..6f93599d02 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/list/actions/RoomListQuickActionsSharedAction.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/list/actions/RoomListQuickActionsSharedAction.kt @@ -21,7 +21,6 @@ import androidx.annotation.StringRes import im.vector.app.R import im.vector.app.core.platform.VectorSharedAction - sealed class RoomListQuickActionsSharedAction( @StringRes val titleRes: Int, @DrawableRes val iconResId: Int?, @@ -61,7 +60,7 @@ sealed class RoomListQuickActionsSharedAction( R.string.room_list_quick_actions_favorite_add, R.drawable.ic_star_24dp) - data class Leave(val roomId: String, val showIcon: Boolean=true) : RoomListQuickActionsSharedAction( + data class Leave(val roomId: String, val showIcon: Boolean = true) : RoomListQuickActionsSharedAction( R.string.room_list_quick_actions_leave, if (showIcon) R.drawable.ic_room_actions_leave else null, true 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 e6c57523e5..654400e790 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 @@ -38,14 +38,6 @@ class RoomNotificationSettingsController @Inject constructor() : TypedEpoxyContr setData(null) } - @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 - } - override fun buildModels(data: RoomNotificationSettingsViewState?) { val host = this data ?: return @@ -73,4 +65,12 @@ class RoomNotificationSettingsController @Inject constructor() : TypedEpoxyContr } } } + + @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 + } } 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 a88dc84078..30fde5ddea 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 @@ -30,13 +30,13 @@ data class RoomNotificationSettingsViewState( val roomSummary: Async = Uninitialized, val isLoading: Boolean = false, val roomEncrypted: Boolean = false, - val notificationState: Async = Uninitialized, + val notificationState: Async = Uninitialized ) : MvRxState { constructor(args: RoomProfileArgs) : this(roomId = args.roomId) constructor(args: RoomListActionsArgs) : this(roomId = args.roomId) } -data class AvatarData ( +data class AvatarData( val displayName: String, val avatarUrl: String ) @@ -61,6 +61,7 @@ val RoomNotificationSettingsViewState.notificationOptions: List Date: Wed, 11 Aug 2021 14:36:32 +0100 Subject: [PATCH 09/21] fix line length --- .../notifications/NotificationSettingsFooterItem.kt | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) 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 index c882cf8615..4608f2b1ce 100644 --- 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 @@ -37,14 +37,18 @@ abstract class NotificationSettingsFooterItem : VectorEpoxyModel Date: Wed, 11 Aug 2021 15:00:27 +0100 Subject: [PATCH 10/21] simplify logic in bottom sheet and add comment --- vector/build.gradle | 3 --- .../list/actions/RoomListQuickActionsEpoxyController.kt | 7 ++++--- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/vector/build.gradle b/vector/build.gradle index 311f7ed172..7c1119f9c1 100644 --- a/vector/build.gradle +++ b/vector/build.gradle @@ -142,15 +142,12 @@ android { resValue "bool", "useLoginV1", "true" resValue "bool", "useLoginV2", "false" - - // NotificationSettingsV2 is disabled. To be released in conjunction with iOS/Web def 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 "Long", "VOICE_MESSAGE_DURATION_LIMIT_MS", "120_000L" diff --git a/vector/src/main/java/im/vector/app/features/home/room/list/actions/RoomListQuickActionsEpoxyController.kt b/vector/src/main/java/im/vector/app/features/home/room/list/actions/RoomListQuickActionsEpoxyController.kt index b109f0466d..7e39156b18 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/list/actions/RoomListQuickActionsEpoxyController.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/list/actions/RoomListQuickActionsEpoxyController.kt @@ -47,10 +47,11 @@ class RoomListQuickActionsEpoxyController @Inject constructor( val notificationViewState = state.notificationSettingsViewState val roomSummary = notificationViewState.roomSummary() ?: return val host = this - val showFull = state.roomListActionsArgs.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 (showFull || isV2) { + if (showFull) { // Preview, favorite, settings bottomSheetRoomPreviewItem { id("room_preview") @@ -91,7 +92,7 @@ class RoomListQuickActionsEpoxyController @Inject constructor( RoomListQuickActionsSharedAction.NotificationsMute(roomSummary.roomId).toBottomSheetItem(3, selectedRoomState) } - if (showFull || isV2) { + if (showFull) { RoomListQuickActionsSharedAction.Leave(roomSummary.roomId, showIcon = !isV2).toBottomSheetItem(5) } } From 99a0d17dfc1e661713df236c81e18a7d808b4864 Mon Sep 17 00:00:00 2001 From: ganfra Date: Tue, 17 Aug 2021 10:46:04 +0200 Subject: [PATCH 11/21] Call ended: handle busy reason and invite timeout. --- changelog.d/3853.feature | 1 + .../app/features/call/VectorCallActivity.kt | 51 ++++++++++++++++--- .../app/features/call/VectorCallViewEvents.kt | 1 - .../app/features/call/VectorCallViewModel.kt | 4 +- .../app/features/call/webrtc/WebRtcCall.kt | 15 +++++- vector/src/main/res/values/strings.xml | 3 ++ 6 files changed, 62 insertions(+), 13 deletions(-) create mode 100644 changelog.d/3853.feature diff --git a/changelog.d/3853.feature b/changelog.d/3853.feature new file mode 100644 index 0000000000..86c19d2c22 --- /dev/null +++ b/changelog.d/3853.feature @@ -0,0 +1 @@ +Call: show dialog for some ended reasons. \ No newline at end of file diff --git a/vector/src/main/java/im/vector/app/features/call/VectorCallActivity.kt b/vector/src/main/java/im/vector/app/features/call/VectorCallActivity.kt index a1e3717329..ab407f1598 100644 --- a/vector/src/main/java/im/vector/app/features/call/VectorCallActivity.kt +++ b/vector/src/main/java/im/vector/app/features/call/VectorCallActivity.kt @@ -26,6 +26,7 @@ import android.os.Bundle import android.os.Parcelable import android.view.View import android.view.WindowManager +import androidx.annotation.StringRes import androidx.core.content.ContextCompat import androidx.core.content.getSystemService import androidx.core.view.isInvisible @@ -58,6 +59,7 @@ import org.matrix.android.sdk.api.logger.LoggerTag import org.matrix.android.sdk.api.session.call.CallState import org.matrix.android.sdk.api.session.call.MxPeerConnectionState import org.matrix.android.sdk.api.session.call.TurnServerResponse +import org.matrix.android.sdk.api.session.room.model.call.EndCallReason import org.webrtc.EglBase import org.webrtc.RendererCommon import timber.log.Timber @@ -133,6 +135,12 @@ class VectorCallActivity : VectorBaseActivity(), CallContro renderState(it) } + callViewModel.asyncSubscribe(this, VectorCallViewState::callState) { + if (it is CallState.Ended) { + handleCallEnded(it) + } + } + callViewModel.viewEvents .observe() .observeOn(AndroidSchedulers.mainThread()) @@ -199,7 +207,7 @@ class VectorCallActivity : VectorBaseActivity(), CallContro views.callConnectingProgress.isVisible = true configureCallInfo(state) } - is CallState.Connected -> { + is CallState.Connected -> { if (callState.iceConnectionState == MxPeerConnectionState.CONNECTED) { if (state.isLocalOnHold || state.isRemoteOnHold) { views.smallIsHeldIcon.isVisible = true @@ -249,14 +257,44 @@ class VectorCallActivity : VectorBaseActivity(), CallContro views.callConnectingProgress.isVisible = true } } - is CallState.Ended -> { - finish() + is CallState.Ended -> { + views.callVideoGroup.isInvisible = true + views.callInfoGroup.isVisible = true + views.callStatusText.setText(R.string.call_ended) + configureCallInfo(state) } - null -> { + else -> { + views.callVideoGroup.isInvisible = true + views.callInfoGroup.isInvisible = true } } } + private fun handleCallEnded(callState: CallState.Ended) { + when (callState.reason) { + EndCallReason.USER_BUSY -> { + showEndCallDialog(R.string.call_ended_user_busy_title, R.string.call_ended_user_busy_description) + } + EndCallReason.INVITE_TIMEOUT -> { + showEndCallDialog(R.string.call_ended_invite_timeout_title, R.string.call_error_user_not_responding) + } + else -> { + finish() + } + } + } + + private fun showEndCallDialog(@StringRes title: Int, @StringRes description: Int) { + MaterialAlertDialogBuilder(this) + .setTitle(title) + .setMessage(description) + .setNegativeButton(R.string.ok) { _, _ -> } + .setOnDismissListener { + finish() + } + .show() + } + private fun configureCallInfo(state: VectorCallViewState, blurAvatar: Boolean = false) { state.callInfo?.opponentUserItem?.let { val colorFilter = ContextCompat.getColor(this, R.color.bg_call_screen_blur) @@ -340,9 +378,6 @@ class VectorCallActivity : VectorBaseActivity(), CallContro private fun handleViewEvents(event: VectorCallViewEvents?) { Timber.tag(loggerTag.value).v("handleViewEvents $event") when (event) { - VectorCallViewEvents.DismissNoCall -> { - finish() - } is VectorCallViewEvents.ConnectionTimeout -> { onErrorTimoutConnect(event.turn) } @@ -364,7 +399,7 @@ class VectorCallActivity : VectorBaseActivity(), CallContro // TODO ask to use default stun, etc... MaterialAlertDialogBuilder(this) .setTitle(R.string.call_failed_no_connection) - .setMessage(getString(R.string.call_failed_no_connection_description)) + .setMessage(R.string.call_failed_no_connection_description) .setNegativeButton(R.string.ok) { _, _ -> callViewModel.handle(VectorCallViewActions.EndCall) } diff --git a/vector/src/main/java/im/vector/app/features/call/VectorCallViewEvents.kt b/vector/src/main/java/im/vector/app/features/call/VectorCallViewEvents.kt index 91c3154d0a..9f19429c00 100644 --- a/vector/src/main/java/im/vector/app/features/call/VectorCallViewEvents.kt +++ b/vector/src/main/java/im/vector/app/features/call/VectorCallViewEvents.kt @@ -22,7 +22,6 @@ import org.matrix.android.sdk.api.session.call.TurnServerResponse sealed class VectorCallViewEvents : VectorViewEvents { - object DismissNoCall : VectorCallViewEvents() data class ConnectionTimeout(val turn: TurnServerResponse?) : VectorCallViewEvents() data class ShowSoundDeviceChooser( val available: Set, diff --git a/vector/src/main/java/im/vector/app/features/call/VectorCallViewModel.kt b/vector/src/main/java/im/vector/app/features/call/VectorCallViewModel.kt index e7b2b629e1..4b3a8e84bc 100644 --- a/vector/src/main/java/im/vector/app/features/call/VectorCallViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/call/VectorCallViewModel.kt @@ -137,9 +137,7 @@ class VectorCallViewModel @AssistedInject constructor( private val currentCallListener = object : WebRtcCallManager.CurrentCallListener { override fun onCurrentCallChange(call: WebRtcCall?) { - if (call == null) { - _viewEvents.post(VectorCallViewEvents.DismissNoCall) - } else { + if (call != null) { updateOtherKnownCall(call) } } diff --git a/vector/src/main/java/im/vector/app/features/call/webrtc/WebRtcCall.kt b/vector/src/main/java/im/vector/app/features/call/webrtc/WebRtcCall.kt index 91d3ab7ddf..04a979fb94 100644 --- a/vector/src/main/java/im/vector/app/features/call/webrtc/WebRtcCall.kt +++ b/vector/src/main/java/im/vector/app/features/call/webrtc/WebRtcCall.kt @@ -39,7 +39,9 @@ import io.reactivex.disposables.Disposable import io.reactivex.subjects.PublishSubject import io.reactivex.subjects.ReplaySubject import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Deferred import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.async import kotlinx.coroutines.delay import kotlinx.coroutines.launch import kotlinx.coroutines.withContext @@ -91,6 +93,7 @@ private const val STREAM_ID = "userMedia" private const val AUDIO_TRACK_ID = "${STREAM_ID}a0" private const val VIDEO_TRACK_ID = "${STREAM_ID}v0" private val DEFAULT_AUDIO_CONSTRAINTS = MediaConstraints() +private const val INVITE_TIMEOUT_IN_MS = 60_000L private val loggerTag = LoggerTag("WebRtcCall", LoggerTag.VOIP) @@ -165,6 +168,8 @@ class WebRtcCall( } } + private var inviteTimeout: Deferred? = null + // Mute status var micMuted = false private set @@ -239,6 +244,10 @@ class WebRtcCall( if (mxCall.state == CallState.CreateOffer) { // send offer to peer mxCall.offerSdp(sessionDescription.description) + inviteTimeout = async { + delay(INVITE_TIMEOUT_IN_MS) + endCall(EndCallReason.INVITE_TIMEOUT) + } } else { mxCall.negotiate(sessionDescription.description, SdpType.OFFER) } @@ -807,7 +816,7 @@ class WebRtcCall( return@launch } val reject = mxCall.state is CallState.LocalRinging - terminate(EndCallReason.USER_HANGUP, reject) + terminate(reason, reject) if (reject) { mxCall.reject() } else { @@ -824,6 +833,8 @@ class WebRtcCall( val cameraManager = context.getSystemService()!! cameraManager.unregisterAvailabilityCallback(cameraAvailabilityCallback) } + inviteTimeout?.cancel() + inviteTimeout = null mxCall.state = CallState.Ended(reason ?: EndCallReason.USER_HANGUP) release() onCallEnded(callId, reason ?: EndCallReason.USER_HANGUP, rejected) @@ -845,6 +856,8 @@ class WebRtcCall( } fun onCallAnswerReceived(callAnswerContent: CallAnswerContent) { + inviteTimeout?.cancel() + inviteTimeout = null sessionScope?.launch(dispatcher) { Timber.tag(loggerTag.value).v("onCallAnswerReceived ${callAnswerContent.callId}") val sdp = SessionDescription(SessionDescription.Type.ANSWER, callAnswerContent.answer.sdp) diff --git a/vector/src/main/res/values/strings.xml b/vector/src/main/res/values/strings.xml index 1956a0cc01..225b6a1a59 100644 --- a/vector/src/main/res/values/strings.xml +++ b/vector/src/main/res/values/strings.xml @@ -749,6 +749,9 @@ %s held the call You held the call + User busy + The user you called is busy." + No answer The remote side failed to pick up. Media Connection Failed Cannot initialize the camera From 4d413e0afd27a8ece3d2bbfab7bea97ff592617a Mon Sep 17 00:00:00 2001 From: Ekaterina Gerasimova Date: Mon, 23 Aug 2021 22:45:41 +0100 Subject: [PATCH 12/21] Issue triage: remove old templates, add new ones Remove the old style Markdown templates and replace with new style yaml templates. New templates match those used in element-web. Note that issue labels will been to be renamed to match element-web before this PR can be merged. Signed-off-by: Ekaterina Gerasimova --- .github/ISSUE_TEMPLATE/bug.yml | 69 +++++++++++++++++++++++ .github/ISSUE_TEMPLATE/bug_report.md | 34 ----------- .github/ISSUE_TEMPLATE/enhancement.yml | 36 ++++++++++++ .github/ISSUE_TEMPLATE/feature_request.md | 20 ------- changelog.d/pr-3883.misc | 1 + 5 files changed, 106 insertions(+), 54 deletions(-) create mode 100644 .github/ISSUE_TEMPLATE/bug.yml delete mode 100644 .github/ISSUE_TEMPLATE/bug_report.md create mode 100644 .github/ISSUE_TEMPLATE/enhancement.yml delete mode 100644 .github/ISSUE_TEMPLATE/feature_request.md create mode 100644 changelog.d/pr-3883.misc diff --git a/.github/ISSUE_TEMPLATE/bug.yml b/.github/ISSUE_TEMPLATE/bug.yml new file mode 100644 index 0000000000..e95387dd6c --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug.yml @@ -0,0 +1,69 @@ +name: Bug report for the Element Android app +description: Report any issues that you have found with the Element app. Please [check open issues](https://github.com/vector-im/element-android/issues) first, in case it has already been reported. +labels: [T-Defect] +body: + - type: markdown + attributes: + value: | + Thanks for taking the time to fill out this bug report! + + Please report security issues by email to security@matrix.org + - type: textarea + id: reproduction-steps + attributes: + label: Steps to reproduce + description: Please attach screenshots, videos or logs if you can. + placeholder: Tell us what you see! + value: | + 1. Where are you starting? What can you see? + 2. What do you click? + 3. More steps… + validations: + required: true + - type: textarea + id: expected-result + attributes: + label: What did you expect? + placeholder: Tell us what you expected to happen in as much detail as you can. + validations: + required: true + - type: textarea + id: actual-result + attributes: + label: What happened? + placeholder: Tell us what went wrong + validations: + required: true + - type: input + id: device + attributes: + label: Your phone model + placeholder: e.g. Samsung S6 + validations: + required: false + - type: input + id: os + attributes: + label: Operating system version + placeholder: e.g. Android 10.0 + validations: + required: false + - type: input + id: version + attributes: + label: Application version and app store + description: You can find the version information in Settings -> Help & About. + placeholder: e.g. Element version 1.7.34, olm version 3.2.3 from F-Droid + validations: + required: false + - type: dropdown + id: rageshake + attributes: + label: Have you submitted a rageshake? + description: | + Did you know that you can shake your phone to submit logs for this issue? Trigger the defect, then shake your phone and you will see a popup asking if you would like to open the bug report screen. Click YES, and describe the issue, mentioning that you have also filed a bug. Submit the report to send anonymous logs to the developers. + options: + - 'Yes' + - 'No' + validations: + required: true diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md deleted file mode 100644 index d7c3506fa0..0000000000 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ /dev/null @@ -1,34 +0,0 @@ ---- -name: Bug report -about: Create a report to help us improve Element -title: '' -labels: '' -assignees: '' - ---- - -#### Describe the bug -A clear and concise description of what the bug is. - -#### To Reproduce -Steps to reproduce the behavior: -1. Go to '...' -2. Click on '....' -3. Scroll down to '....' -4. See error - -#### Expected behavior -A clear and concise description of what you expected to happen. - -#### Screenshots -If applicable, add screenshots to help explain your problem. - -#### Smartphone (please complete the following information): - - Device: [e.g. Samsung S6] - - OS: [e.g. Android 6.0] - -#### Additional context - - App version and store [e.g. 1.0.0 - F-Droid] - - Homeserver: [e.g. matrix.org] - -Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/enhancement.yml b/.github/ISSUE_TEMPLATE/enhancement.yml new file mode 100644 index 0000000000..5d9cfb3c88 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/enhancement.yml @@ -0,0 +1,36 @@ +name: Enhancement request +description: Do you have a suggestion or feature request? +labels: [T-Enhancement] +body: + - type: markdown + attributes: + value: | + Thank you for taking the time to propose a new feature or make a suggestion. + - type: textarea + id: usecase + attributes: + label: Your use case + description: What would you like to be able to do? Please feel welcome to include screenshots or mock ups. + placeholder: Tell us what you would like to do! + value: | + #### What would you like to do? + + #### Why would you like to do it? + + #### How would you like to achieve it? + validations: + required: true + - type: textarea + id: alternative + attributes: + label: Have you considered any alternatives? + placeholder: A clear and concise description of any alternative solutions or features you've considered. + validations: + required: false + - type: textarea + id: additional-context + attributes: + label: Additional context + placeholder: Is there anything else you'd like to add? + validations: + required: false diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md deleted file mode 100644 index da96d461c5..0000000000 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ /dev/null @@ -1,20 +0,0 @@ ---- -name: Feature request -about: Suggest an idea for this project -title: '' -labels: type:suggestion -assignees: '' - ---- - -#### Is your feature request related to a problem? Please describe. -A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] - -#### Describe the solution you'd like. -A clear and concise description of what you want to happen. - -#### Describe alternatives you've considered. -A clear and concise description of any alternative solutions or features you've considered. - -#### Additional context -Add any other context or screenshots about the feature request here. diff --git a/changelog.d/pr-3883.misc b/changelog.d/pr-3883.misc new file mode 100644 index 0000000000..468e5e4927 --- /dev/null +++ b/changelog.d/pr-3883.misc @@ -0,0 +1 @@ +Issue templates: modernise and sync with element-web From fe800a56e21ba7330af86ada7669188fae239957 Mon Sep 17 00:00:00 2001 From: David Langley Date: Tue, 24 Aug 2021 12:12:39 +0100 Subject: [PATCH 13/21] Address comments. --- .../roomprofile/RoomProfileActivity.kt | 4 +-- .../roomprofile/RoomProfileFragment.kt | 2 +- .../roomprofile/RoomProfileSharedAction.kt | 2 +- .../RoomNotificationSettingsController.kt | 2 +- .../RoomNotificationSettingsFragment.kt | 11 ++++++- .../RoomNotificationSettingsViewModel.kt | 24 +++++++------- .../RoomNotificationSettingsViewState.kt | 10 ++---- .../res/layout/item_notifications_footer.xml | 30 ++++++------------ .../src/main/res/layout/item_text_header.xml | 31 ++++++------------- 9 files changed, 49 insertions(+), 67 deletions(-) diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileActivity.kt b/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileActivity.kt index d8e080f6a4..d28878283f 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileActivity.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileActivity.kt @@ -113,8 +113,8 @@ class RoomProfileActivity : RoomProfileSharedAction.OpenRoomAliasesSettings -> openRoomAlias() RoomProfileSharedAction.OpenRoomPermissionsSettings -> openRoomPermissions() RoomProfileSharedAction.OpenRoomUploads -> openRoomUploads() - RoomProfileSharedAction.OpenBannedRoomMembers -> openBannedRoomMembers() - RoomProfileSharedAction.OpenRoomNotificaitonSettings -> openRoomNotificationSettings() + RoomProfileSharedAction.OpenBannedRoomMembers -> openBannedRoomMembers() + RoomProfileSharedAction.OpenRoomNotificationSettings -> openRoomNotificationSettings() }.exhaustive } .disposeOnDestroy() diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileFragment.kt b/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileFragment.kt index ea5bbd2e84..4b37d038b5 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileFragment.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileFragment.kt @@ -255,7 +255,7 @@ class RoomProfileFragment @Inject constructor( override fun onNotificationsClicked() { if (BuildConfig.USE_NOTIFICATION_SETTINGS_V2) { - roomProfileSharedActionViewModel.post(RoomProfileSharedAction.OpenRoomNotificaitonSettings) + roomProfileSharedActionViewModel.post(RoomProfileSharedAction.OpenRoomNotificationSettings) } else { RoomListQuickActionsBottomSheet .newInstance(roomProfileArgs.roomId, RoomListActionsArgs.Mode.NOTIFICATIONS) diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileSharedAction.kt b/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileSharedAction.kt index 6a9533a2eb..eb4ab56634 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileSharedAction.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileSharedAction.kt @@ -28,5 +28,5 @@ sealed class RoomProfileSharedAction : VectorSharedAction { object OpenRoomUploads : RoomProfileSharedAction() object OpenRoomMembers : RoomProfileSharedAction() object OpenBannedRoomMembers : RoomProfileSharedAction() - object OpenRoomNotificaitonSettings : RoomProfileSharedAction() + object OpenRoomNotificationSettings : RoomProfileSharedAction() } 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 654400e790..9a2085a7e8 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 @@ -59,7 +59,7 @@ class RoomNotificationSettingsController @Inject constructor() : TypedEpoxyContr } notificationSettingsFooterItem { id("roomNotificationSettingsFooter") - encrypted(data.roomEncrypted) + encrypted(data.roomSummary()?.isEncrypted == true) 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 8baef5d10c..ce0fde32c6 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 @@ -24,10 +24,12 @@ 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 @@ -53,6 +55,13 @@ class RoomNotificationSettingsFragment @Inject constructor( 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 @@ -77,7 +86,7 @@ class RoomNotificationSettingsFragment @Inject constructor( } override fun didSelectAccountSettingsLink() { - navigator.openSettings(requireContext()) + navigator.openSettings(requireContext(), VectorSettingsActivity.EXTRA_DIRECT_ACCESS_NOTIFICATIONS) } private fun renderRoomSummary(state: RoomNotificationSettingsViewState) { 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 5d23931d88..dd0535f51b 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 @@ -59,17 +59,10 @@ class RoomNotificationSettingsViewModel @AssistedInject constructor( private val room = session.getRoom(initialState.roomId)!! init { - initEncrypted() observeSummary() observeNotificationState() } - private fun initEncrypted() { - setState { - copy(roomEncrypted = room.isEncrypted()) - } - } - private fun observeSummary() { room.rx().liveRoomSummary() .unwrap() @@ -96,10 +89,19 @@ class RoomNotificationSettingsViewModel @AssistedInject constructor( setState { copy(isLoading = true) } viewModelScope.launch { runCatching { room.setRoomNotificationState(action.notificationState) } - .onFailure { _viewEvents.post(RoomNotificationSettingsViewEvents.Failure(it)) } - setState { - copy(isLoading = false, notificationState = Success(action.notificationState)) - } + .fold( + { + setState { + copy(isLoading = false, notificationState = Success(action.notificationState)) + } + }, + { + setState { + copy(isLoading = false) + } + _viewEvents.post(RoomNotificationSettingsViewEvents.Failure(it)) + } + ) } } } 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 30fde5ddea..54e2ef9550 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 @@ -29,24 +29,18 @@ data class RoomNotificationSettingsViewState( val roomId: String, val roomSummary: Async = Uninitialized, val isLoading: Boolean = false, - val roomEncrypted: Boolean = false, val notificationState: Async = Uninitialized ) : MvRxState { constructor(args: RoomProfileArgs) : this(roomId = args.roomId) constructor(args: RoomListActionsArgs) : this(roomId = args.roomId) } -data class AvatarData( - val displayName: String, - val avatarUrl: String -) - /** * Used to map this old room notification settings to the new options in v2. */ val RoomNotificationSettingsViewState.notificationStateMapped: Async get() { - if ((roomEncrypted && notificationState() == RoomNotificationState.MENTIONS_ONLY) || notificationState() == RoomNotificationState.ALL_MESSAGES) { + 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 */ @@ -59,7 +53,7 @@ val RoomNotificationSettingsViewState.notificationStateMapped: Async get() { - return if (roomEncrypted) { + return if (roomSummary()?.isEncrypted == true) { listOf(RoomNotificationState.ALL_MESSAGES_NOISY, RoomNotificationState.MUTE) } else { listOf(RoomNotificationState.ALL_MESSAGES_NOISY, RoomNotificationState.MENTIONS_ONLY, RoomNotificationState.MUTE) diff --git a/vector/src/main/res/layout/item_notifications_footer.xml b/vector/src/main/res/layout/item_notifications_footer.xml index 8338358048..36db166e8f 100644 --- a/vector/src/main/res/layout/item_notifications_footer.xml +++ b/vector/src/main/res/layout/item_notifications_footer.xml @@ -1,25 +1,13 @@ - - - - - \ No newline at end of file + 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" /> diff --git a/vector/src/main/res/layout/item_text_header.xml b/vector/src/main/res/layout/item_text_header.xml index 15848f585e..3f876e5ece 100644 --- a/vector/src/main/res/layout/item_text_header.xml +++ b/vector/src/main/res/layout/item_text_header.xml @@ -1,25 +1,14 @@ - - - - - \ No newline at end of file + 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" /> \ No newline at end of file From cd424b0766c52eeb4b88ef64dbc61004b3f3b1a2 Mon Sep 17 00:00:00 2001 From: David Langley Date: Tue, 24 Aug 2021 12:17:39 +0100 Subject: [PATCH 14/21] fix checks --- .../notifications/RoomNotificationSettingsViewState.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) 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 54e2ef9550..72e61fba70 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 @@ -40,7 +40,8 @@ data class RoomNotificationSettingsViewState( */ val RoomNotificationSettingsViewState.notificationStateMapped: Async get() { - if ((roomSummary()?.isEncrypted == true && notificationState() == RoomNotificationState.MENTIONS_ONLY) || notificationState() == RoomNotificationState.ALL_MESSAGES) { + 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 */ From 07adc4348130f0d968f0bfd5acac054d1e83ca9c Mon Sep 17 00:00:00 2001 From: Florian Renaud Date: Tue, 24 Aug 2021 15:36:53 +0200 Subject: [PATCH 15/21] Use `when` statement instead of `if`/ `else` --- .../sdk/internal/network/RetrofitExtensions.kt | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/network/RetrofitExtensions.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/network/RetrofitExtensions.kt index 2116063626..71ba71b915 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/network/RetrofitExtensions.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/network/RetrofitExtensions.kt @@ -86,13 +86,16 @@ private fun toFailure(errorBody: ResponseBody?, httpCode: Int, globalErrorReceiv val matrixError = matrixErrorAdapter.fromJson(errorBodyStr) if (matrixError != null) { - if (matrixError.code == MatrixError.M_CONSENT_NOT_GIVEN && !matrixError.consentUri.isNullOrBlank()) { - // Also send this error to the globalErrorReceiver, for a global management - globalErrorReceiver?.handleGlobalError(GlobalError.ConsentNotGivenError(matrixError.consentUri)) - } else if (httpCode == HttpURLConnection.HTTP_UNAUTHORIZED /* 401 */ - && matrixError.code == MatrixError.M_UNKNOWN_TOKEN) { - // Also send this error to the globalErrorReceiver, for a global management - globalErrorReceiver?.handleGlobalError(GlobalError.InvalidToken(matrixError.isSoftLogout.orFalse())) + when { + matrixError.code == MatrixError.M_CONSENT_NOT_GIVEN && !matrixError.consentUri.isNullOrBlank() -> { + // Also send this error to the globalErrorReceiver, for a global management + globalErrorReceiver?.handleGlobalError(GlobalError.ConsentNotGivenError(matrixError.consentUri)) + } + httpCode == HttpURLConnection.HTTP_UNAUTHORIZED /* 401 */ + && matrixError.code == MatrixError.M_UNKNOWN_TOKEN -> { + // Also send this error to the globalErrorReceiver, for a global management + globalErrorReceiver?.handleGlobalError(GlobalError.InvalidToken(matrixError.isSoftLogout.orFalse())) + } } return Failure.ServerError(matrixError, httpCode) From 2e2deba3ac7f3f7fefce4d039f875b789b46af1f Mon Sep 17 00:00:00 2001 From: ganfra Date: Tue, 24 Aug 2021 19:58:54 +0200 Subject: [PATCH 16/21] Fix message edition is not rendered in e2e rooms after pagination (#3887) --- changelog.d/3887.bugfix | 1 + .../database/EventInsertLiveObserver.kt | 31 ++++--------------- .../database/RealmSessionStoreMigration.kt | 10 +++++- .../internal/database/model/EventEntity.kt | 10 +++++- .../database/model/EventInsertEntity.kt | 7 ++++- .../database/query/EventEntityQueries.kt | 4 ++- .../session/room/send/LocalEchoRepository.kt | 2 +- .../room/timeline/TimelineEventDecryptor.kt | 3 +- 8 files changed, 37 insertions(+), 31 deletions(-) create mode 100644 changelog.d/3887.bugfix diff --git a/changelog.d/3887.bugfix b/changelog.d/3887.bugfix new file mode 100644 index 0000000000..eecd2cea19 --- /dev/null +++ b/changelog.d/3887.bugfix @@ -0,0 +1 @@ +Message edition is not rendered in e2e rooms after pagination \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/EventInsertLiveObserver.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/EventInsertLiveObserver.kt index 88aa432fb3..758c7aa5b9 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/EventInsertLiveObserver.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/EventInsertLiveObserver.kt @@ -17,6 +17,9 @@ package org.matrix.android.sdk.internal.database import com.zhuinden.monarchy.Monarchy +import io.realm.RealmConfiguration +import io.realm.RealmResults +import kotlinx.coroutines.launch import org.matrix.android.sdk.internal.database.mapper.asDomain import org.matrix.android.sdk.internal.database.model.EventEntity import org.matrix.android.sdk.internal.database.model.EventInsertEntity @@ -24,20 +27,15 @@ import org.matrix.android.sdk.internal.database.model.EventInsertEntityFields import org.matrix.android.sdk.internal.database.query.where import org.matrix.android.sdk.internal.di.SessionDatabase import org.matrix.android.sdk.internal.session.EventInsertLiveProcessor -import io.realm.RealmConfiguration -import io.realm.RealmResults -import kotlinx.coroutines.launch -import org.matrix.android.sdk.internal.crypto.EventDecryptor import timber.log.Timber import javax.inject.Inject internal class EventInsertLiveObserver @Inject constructor(@SessionDatabase realmConfiguration: RealmConfiguration, - private val processors: Set<@JvmSuppressWildcards EventInsertLiveProcessor>, - private val eventDecryptor: EventDecryptor) + private val processors: Set<@JvmSuppressWildcards EventInsertLiveProcessor>) : RealmLiveEntityObserver(realmConfiguration) { - override val query = Monarchy.Query { - it.where(EventInsertEntity::class.java) + override val query = Monarchy.Query { + it.where(EventInsertEntity::class.java).equalTo(EventInsertEntityFields.CAN_BE_PROCESSED, true) } override fun onChange(results: RealmResults) { @@ -86,23 +84,6 @@ internal class EventInsertLiveObserver @Inject constructor(@SessionDatabase real } } -// private fun decryptIfNeeded(event: Event) { -// if (event.isEncrypted() && event.mxDecryptionResult == null) { -// try { -// val result = eventDecryptor.decryptEvent(event, event.roomId ?: "") -// event.mxDecryptionResult = OlmDecryptionResult( -// payload = result.clearEvent, -// senderKey = result.senderCurve25519Key, -// keysClaimed = result.claimedEd25519Key?.let { k -> mapOf("ed25519" to k) }, -// forwardingCurve25519KeyChain = result.forwardingCurve25519KeyChain -// ) -// } catch (e: MXCryptoError) { -// Timber.v("Failed to decrypt event") -// // TODO -> we should keep track of this and retry, or some processing will never be handled -// } -// } -// } - private fun shouldProcess(eventInsertEntity: EventInsertEntity): Boolean { return processors.any { it.shouldProcess(eventInsertEntity.eventId, eventInsertEntity.eventType, eventInsertEntity.insertType) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/RealmSessionStoreMigration.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/RealmSessionStoreMigration.kt index 28ae4d8bfd..aa96ca5e1a 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/RealmSessionStoreMigration.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/RealmSessionStoreMigration.kt @@ -29,6 +29,7 @@ import org.matrix.android.sdk.internal.database.model.CurrentStateEventEntityFie import org.matrix.android.sdk.internal.database.model.EditAggregatedSummaryEntityFields import org.matrix.android.sdk.internal.database.model.EditionOfEventFields import org.matrix.android.sdk.internal.database.model.EventEntityFields +import org.matrix.android.sdk.internal.database.model.EventInsertEntityFields import org.matrix.android.sdk.internal.database.model.HomeServerCapabilitiesEntityFields import org.matrix.android.sdk.internal.database.model.PendingThreePidEntityFields import org.matrix.android.sdk.internal.database.model.PreviewUrlCacheEntityFields @@ -46,7 +47,7 @@ import timber.log.Timber internal object RealmSessionStoreMigration : RealmMigration { - const val SESSION_STORE_SCHEMA_VERSION = 16L + const val SESSION_STORE_SCHEMA_VERSION = 17L override fun migrate(realm: DynamicRealm, oldVersion: Long, newVersion: Long) { Timber.v("Migrating Realm Session from $oldVersion to $newVersion") @@ -67,6 +68,7 @@ internal object RealmSessionStoreMigration : RealmMigration { if (oldVersion <= 13) migrateTo14(realm) if (oldVersion <= 14) migrateTo15(realm) if (oldVersion <= 15) migrateTo16(realm) + if (oldVersion <= 16) migrateTo17(realm) } private fun migrateTo1(realm: DynamicRealm) { @@ -330,4 +332,10 @@ internal object RealmSessionStoreMigration : RealmMigration { obj.setLong(HomeServerCapabilitiesEntityFields.LAST_UPDATED_TIMESTAMP, 0) } } + + private fun migrateTo17(realm: DynamicRealm) { + Timber.d("Step 16 -> 17") + realm.schema.get("EventInsertEntity") + ?.addField(EventInsertEntityFields.CAN_BE_PROCESSED, Boolean::class.java) + } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/EventEntity.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/EventEntity.kt index c9edbcd889..4dc8712afb 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/EventEntity.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/EventEntity.kt @@ -22,6 +22,7 @@ import org.matrix.android.sdk.internal.crypto.algorithms.olm.OlmDecryptionResult import org.matrix.android.sdk.internal.di.MoshiProvider import io.realm.RealmObject import io.realm.annotations.Index +import org.matrix.android.sdk.internal.extensions.assertIsManaged internal open class EventEntity(@Index var eventId: String = "", @Index var roomId: String = "", @@ -56,15 +57,22 @@ internal open class EventEntity(@Index var eventId: String = "", companion object fun setDecryptionResult(result: MXEventDecryptionResult) { + assertIsManaged() val decryptionResult = OlmDecryptionResult( payload = result.clearEvent, senderKey = result.senderCurve25519Key, keysClaimed = result.claimedEd25519Key?.let { mapOf("ed25519" to it) }, forwardingCurve25519KeyChain = result.forwardingCurve25519KeyChain ) - val adapter = MoshiProvider.providesMoshi().adapter(OlmDecryptionResult::class.java) + val adapter = MoshiProvider.providesMoshi().adapter(OlmDecryptionResult::class.java) decryptionResultJson = adapter.toJson(decryptionResult) decryptionErrorCode = null decryptionErrorReason = null + + // If we have an EventInsertEntity for the eventId we make sures it can be processed now. + realm.where(EventInsertEntity::class.java) + .equalTo(EventInsertEntityFields.EVENT_ID, eventId) + .findFirst() + ?.canBeProcessed = true } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/EventInsertEntity.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/EventInsertEntity.kt index f4426207be..5cfd306d2f 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/EventInsertEntity.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/EventInsertEntity.kt @@ -23,7 +23,12 @@ import io.realm.RealmObject * in EventEntity table. */ internal open class EventInsertEntity(var eventId: String = "", - var eventType: String = "" + var eventType: String = "", + /** + * This flag will be used to filter EventInsertEntity in EventInsertLiveObserver. + * Currently it's set to false when the event content is encrypted. + */ + var canBeProcessed: Boolean = true ) : RealmObject() { private var insertTypeStr: String = EventInsertType.INCREMENTAL_SYNC.name diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/query/EventEntityQueries.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/query/EventEntityQueries.kt index 0bf62a19fe..57e24cf88f 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/query/EventEntityQueries.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/query/EventEntityQueries.kt @@ -24,6 +24,7 @@ import io.realm.Realm import io.realm.RealmList import io.realm.RealmQuery import io.realm.kotlin.where +import org.matrix.android.sdk.api.session.events.model.EventType internal fun EventEntity.copyToRealmOrIgnore(realm: Realm, insertType: EventInsertType): EventEntity { val eventEntity = realm.where() @@ -31,7 +32,8 @@ internal fun EventEntity.copyToRealmOrIgnore(realm: Realm, insertType: EventInse .equalTo(EventEntityFields.ROOM_ID, roomId) .findFirst() return if (eventEntity == null) { - val insertEntity = EventInsertEntity(eventId = eventId, eventType = type).apply { + val canBeProcessed = type != EventType.ENCRYPTED || decryptionResultJson != null + val insertEntity = EventInsertEntity(eventId = eventId, eventType = type, canBeProcessed = canBeProcessed).apply { this.insertType = insertType } realm.insert(insertEntity) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/LocalEchoRepository.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/LocalEchoRepository.kt index e98e5646af..13095fbd58 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/LocalEchoRepository.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/LocalEchoRepository.kt @@ -77,7 +77,7 @@ internal class LocalEchoRepository @Inject constructor(@SessionDatabase private val timelineEvent = timelineEventMapper.map(timelineEventEntity) timelineInput.onLocalEchoCreated(roomId = roomId, timelineEvent = timelineEvent) taskExecutor.executorScope.asyncTransaction(monarchy) { realm -> - val eventInsertEntity = EventInsertEntity(event.eventId, event.type).apply { + val eventInsertEntity = EventInsertEntity(event.eventId, event.type, canBeProcessed = true).apply { this.insertType = EventInsertType.LOCAL_ECHO } realm.insert(eventInsertEntity) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/TimelineEventDecryptor.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/TimelineEventDecryptor.kt index 3517f26c5d..721dae0b1b 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/TimelineEventDecryptor.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/TimelineEventDecryptor.kt @@ -106,7 +106,8 @@ internal class TimelineEventDecryptor @Inject constructor( val result = cryptoService.decryptEvent(request.event, timelineId) Timber.v("Successfully decrypted event ${event.eventId}") realm.executeTransaction { - EventEntity.where(it, eventId = event.eventId ?: "") + val eventId = event.eventId ?: "" + EventEntity.where(it, eventId = eventId) .findFirst() ?.setDecryptionResult(result) } From f8114dd7a1cf281f323d7c00812c6a9c680fbe80 Mon Sep 17 00:00:00 2001 From: David Langley Date: Tue, 24 Aug 2021 22:28:31 +0100 Subject: [PATCH 17/21] Fix HomeList error snackbar and add dialog for bottom sheet errors --- vector/build.gradle | 2 +- .../vector/app/features/home/HomeActivity.kt | 2 ++ .../home/room/list/RoomListFragment.kt | 11 ++--------- .../RoomListQuickActionsBottomSheet.kt | 19 ++++++++++++++----- 4 files changed, 19 insertions(+), 15 deletions(-) diff --git a/vector/build.gradle b/vector/build.gradle index 7c1119f9c1..f5731c819f 100644 --- a/vector/build.gradle +++ b/vector/build.gradle @@ -143,7 +143,7 @@ android { resValue "bool", "useLoginV2", "false" // NotificationSettingsV2 is disabled. To be released in conjunction with iOS/Web - def useNotificationSettingsV2 = false + def useNotificationSettingsV2 = true buildConfigField "Boolean", "USE_NOTIFICATION_SETTINGS_V2", "${useNotificationSettingsV2}" resValue "bool", "useNotificationSettingsV1", "${!useNotificationSettingsV2}" resValue "bool", "useNotificationSettingsV2", "${useNotificationSettingsV2}" diff --git a/vector/src/main/java/im/vector/app/features/home/HomeActivity.kt b/vector/src/main/java/im/vector/app/features/home/HomeActivity.kt index 4a563b563a..9ae6d4c39f 100644 --- a/vector/src/main/java/im/vector/app/features/home/HomeActivity.kt +++ b/vector/src/main/java/im/vector/app/features/home/HomeActivity.kt @@ -143,6 +143,8 @@ class HomeActivity : } } + override fun getCoordinatorLayout() = views.coordinatorLayout + override fun getBinding() = ActivityHomeBinding.inflate(layoutInflater) override fun injectWith(injector: ScreenComponent) { diff --git a/vector/src/main/java/im/vector/app/features/home/room/list/RoomListFragment.kt b/vector/src/main/java/im/vector/app/features/home/room/list/RoomListFragment.kt index 344a4abbac..fdfe171439 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/list/RoomListFragment.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/list/RoomListFragment.kt @@ -473,16 +473,9 @@ class RoomListFragment @Inject constructor( // refresh footer footerController.setData(it) } - val bottomSheet = RoomListQuickActionsBottomSheet + RoomListQuickActionsBottomSheet .newInstance(room.roomId, RoomListActionsArgs.Mode.FULL) - bottomSheet.listener = object : RoomListQuickActionsBottomSheet.Listener { - override fun handleFailure(throwable: Throwable) { - showFailure(throwable) - } - } - - bottomSheet.show(childFragmentManager, "ROOM_LIST_QUICK_ACTIONS") - + .show(childFragmentManager, "ROOM_LIST_QUICK_ACTIONS") return true } diff --git a/vector/src/main/java/im/vector/app/features/home/room/list/actions/RoomListQuickActionsBottomSheet.kt b/vector/src/main/java/im/vector/app/features/home/room/list/actions/RoomListQuickActionsBottomSheet.kt index ccbed264e3..fcc9960414 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/list/actions/RoomListQuickActionsBottomSheet.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/list/actions/RoomListQuickActionsBottomSheet.kt @@ -25,10 +25,14 @@ import androidx.recyclerview.widget.RecyclerView import com.airbnb.mvrx.args import com.airbnb.mvrx.fragmentViewModel 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.error.ErrorFormatter import im.vector.app.core.extensions.cleanup import im.vector.app.core.extensions.configureWith import im.vector.app.core.platform.VectorBaseBottomSheetDialogFragment +import im.vector.app.core.platform.showOptimizedSnackbar import im.vector.app.databinding.BottomSheetGenericListBinding import im.vector.app.features.navigation.Navigator import im.vector.app.features.roomprofile.notifications.RoomNotificationSettingsAction @@ -57,20 +61,17 @@ class RoomListQuickActionsBottomSheet : VectorBaseBottomSheetDialogFragment(), RoomListQuickActionsEpoxyController.Listener { - interface Listener { - fun handleFailure(throwable: Throwable) - } private lateinit var sharedActionViewModel: RoomListQuickActionsSharedActionViewModel @Inject lateinit var sharedViewPool: RecyclerView.RecycledViewPool @Inject lateinit var roomNotificationSettingsViewModelFactory: RoomNotificationSettingsViewModel.Factory @Inject lateinit var roomListActionsEpoxyController: RoomListQuickActionsEpoxyController @Inject lateinit var navigator: Navigator + @Inject lateinit var errorFormatter: ErrorFormatter private val roomListActionsArgs: RoomListActionsArgs by args() private val viewModel: RoomNotificationSettingsViewModel by fragmentViewModel(RoomNotificationSettingsViewModel::class) override val showExpanded = true - var listener: Listener? = null override fun injectWith(injector: ScreenComponent) { injector.inject(this) @@ -93,7 +94,7 @@ class RoomListQuickActionsBottomSheet : viewModel.observeViewEvents { when (it) { - is RoomNotificationSettingsViewEvents.Failure -> listener?.handleFailure(it.throwable) + is RoomNotificationSettingsViewEvents.Failure -> displayErrorDialog(it.throwable) } } } @@ -134,4 +135,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() + } } From 4d4d370b8fca5333ef945cb0e15632a29e8d5528 Mon Sep 17 00:00:00 2001 From: David Langley Date: Tue, 24 Aug 2021 22:33:32 +0100 Subject: [PATCH 18/21] checks and remove build settings --- vector/build.gradle | 2 +- .../home/room/list/actions/RoomListQuickActionsBottomSheet.kt | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/vector/build.gradle b/vector/build.gradle index f5731c819f..7c1119f9c1 100644 --- a/vector/build.gradle +++ b/vector/build.gradle @@ -143,7 +143,7 @@ android { resValue "bool", "useLoginV2", "false" // NotificationSettingsV2 is disabled. To be released in conjunction with iOS/Web - def useNotificationSettingsV2 = true + def useNotificationSettingsV2 = false buildConfigField "Boolean", "USE_NOTIFICATION_SETTINGS_V2", "${useNotificationSettingsV2}" resValue "bool", "useNotificationSettingsV1", "${!useNotificationSettingsV2}" resValue "bool", "useNotificationSettingsV2", "${useNotificationSettingsV2}" diff --git a/vector/src/main/java/im/vector/app/features/home/room/list/actions/RoomListQuickActionsBottomSheet.kt b/vector/src/main/java/im/vector/app/features/home/room/list/actions/RoomListQuickActionsBottomSheet.kt index fcc9960414..8c1bdc086f 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/list/actions/RoomListQuickActionsBottomSheet.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/list/actions/RoomListQuickActionsBottomSheet.kt @@ -32,7 +32,6 @@ import im.vector.app.core.error.ErrorFormatter import im.vector.app.core.extensions.cleanup import im.vector.app.core.extensions.configureWith import im.vector.app.core.platform.VectorBaseBottomSheetDialogFragment -import im.vector.app.core.platform.showOptimizedSnackbar import im.vector.app.databinding.BottomSheetGenericListBinding import im.vector.app.features.navigation.Navigator import im.vector.app.features.roomprofile.notifications.RoomNotificationSettingsAction From 8988ea899789c00b11c1edea3b644b79bcdb471d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 24 Aug 2021 23:09:52 +0000 Subject: [PATCH 19/21] Bump libphonenumber from 8.12.30 to 8.12.31 Bumps [libphonenumber](https://github.com/google/libphonenumber) from 8.12.30 to 8.12.31. - [Release notes](https://github.com/google/libphonenumber/releases) - [Changelog](https://github.com/google/libphonenumber/blob/master/making-metadata-changes.md) - [Commits](https://github.com/google/libphonenumber/compare/v8.12.30...v8.12.31) --- updated-dependencies: - dependency-name: com.googlecode.libphonenumber:libphonenumber dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- matrix-sdk-android/build.gradle | 2 +- vector/build.gradle | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/matrix-sdk-android/build.gradle b/matrix-sdk-android/build.gradle index e30b63e751..90be941b6f 100644 --- a/matrix-sdk-android/build.gradle +++ b/matrix-sdk-android/build.gradle @@ -169,7 +169,7 @@ dependencies { implementation 'com.otaliastudios:transcoder:0.10.3' // Phone number https://github.com/google/libphonenumber - implementation 'com.googlecode.libphonenumber:libphonenumber:8.12.30' + implementation 'com.googlecode.libphonenumber:libphonenumber:8.12.31' testImplementation 'junit:junit:4.13.2' testImplementation 'org.robolectric:robolectric:4.5.1' diff --git a/vector/build.gradle b/vector/build.gradle index 6494436c13..a9a3217042 100644 --- a/vector/build.gradle +++ b/vector/build.gradle @@ -366,7 +366,7 @@ dependencies { implementation 'com.facebook.stetho:stetho:1.6.0' // Phone number https://github.com/google/libphonenumber - implementation 'com.googlecode.libphonenumber:libphonenumber:8.12.30' + implementation 'com.googlecode.libphonenumber:libphonenumber:8.12.31' // rx implementation 'io.reactivex.rxjava2:rxkotlin:2.4.0' From 37738489ba177a45f7099675db578edcbe7cb97f Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 25 Aug 2021 12:05:51 +0200 Subject: [PATCH 20/21] Update vector/src/main/java/im/vector/app/features/call/VectorCallActivity.kt --- .../main/java/im/vector/app/features/call/VectorCallActivity.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vector/src/main/java/im/vector/app/features/call/VectorCallActivity.kt b/vector/src/main/java/im/vector/app/features/call/VectorCallActivity.kt index ab407f1598..6f6010be15 100644 --- a/vector/src/main/java/im/vector/app/features/call/VectorCallActivity.kt +++ b/vector/src/main/java/im/vector/app/features/call/VectorCallActivity.kt @@ -288,7 +288,7 @@ class VectorCallActivity : VectorBaseActivity(), CallContro MaterialAlertDialogBuilder(this) .setTitle(title) .setMessage(description) - .setNegativeButton(R.string.ok) { _, _ -> } + .setNegativeButton(R.string.ok, null) .setOnDismissListener { finish() } From 65076a4aec2cec11c7bdfdb44028052b30300cf0 Mon Sep 17 00:00:00 2001 From: Tigermouthbear Date: Fri, 13 Aug 2021 23:23:19 -0400 Subject: [PATCH 21/21] Add mxid to autocomplete suggestion if more than one user in a room has the same displayname, closes #1823 improve performance of duplicate displayname finding fix code formatting move member autocomplete disambiguation to AutocompleteMemberPresenter and preserve original order Add mxid to autocomplete suggestion if more than one user in a room has the same displayname, closes #1823 --- changelog.d/1823.bugfix | 1 + .../member/AutocompleteMemberPresenter.kt | 17 +++++++++++++++++ 2 files changed, 18 insertions(+) create mode 100644 changelog.d/1823.bugfix diff --git a/changelog.d/1823.bugfix b/changelog.d/1823.bugfix new file mode 100644 index 0000000000..8252e1826f --- /dev/null +++ b/changelog.d/1823.bugfix @@ -0,0 +1 @@ +- Add mxid to autocomplete suggestion if more than one user in a room has the same displayname diff --git a/vector/src/main/java/im/vector/app/features/autocomplete/member/AutocompleteMemberPresenter.kt b/vector/src/main/java/im/vector/app/features/autocomplete/member/AutocompleteMemberPresenter.kt index ecc607f08d..aa0c10e0a2 100644 --- a/vector/src/main/java/im/vector/app/features/autocomplete/member/AutocompleteMemberPresenter.kt +++ b/vector/src/main/java/im/vector/app/features/autocomplete/member/AutocompleteMemberPresenter.kt @@ -71,6 +71,23 @@ class AutocompleteMemberPresenter @AssistedInject constructor(context: Context, val members = room.getRoomMembers(queryParams) .asSequence() .sortedBy { it.displayName } + .disambiguate() controller.setData(members.toList()) } } + +private fun Sequence.disambiguate(): Sequence { + val displayNames = hashMapOf().also { map -> + for (item in this) { + item.displayName?.lowercase()?.also { displayName -> + map[displayName] = map.getOrPut(displayName, { 0 }) + 1 + } + } + } + + return map { roomMemberSummary -> + if (displayNames[roomMemberSummary.displayName?.lowercase()] ?: 0 > 1) { + roomMemberSummary.copy(displayName = roomMemberSummary.displayName + " " + roomMemberSummary.userId) + } else roomMemberSummary + } +}