From 59634753b38792cd9b2fae2ca35ea2a56b6479f7 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 15 Feb 2021 14:50:26 +0100 Subject: [PATCH] Code review --- .../api/session/room/state/StateService.kt | 17 ++ .../devtools/DevToolsInteractionListener.kt | 21 ++ .../features/devtools/DevToolsViewEvents.kt | 4 +- .../features/devtools/RoomDevToolActivity.kt | 55 +++--- .../devtools/RoomDevToolEditFragment.kt | 16 +- .../features/devtools/RoomDevToolFragment.kt | 14 +- .../devtools/RoomDevToolRootController.kt | 55 +++--- .../devtools/RoomDevToolSendFormController.kt | 23 ++- .../devtools/RoomDevToolSendFormFragment.kt | 6 +- .../RoomDevToolStateEventListFragment.kt | 6 +- .../features/devtools/RoomDevToolViewModel.kt | 185 ++++++++++-------- .../devtools/RoomStateListController.kt | 22 +-- .../form/FormMultiLineEditTextItem.kt | 2 +- .../home/room/detail/RoomDetailFragment.kt | 3 +- .../features/navigation/DefaultNavigator.kt | 5 + .../app/features/navigation/Navigator.kt | 2 + .../members/RoomMemberListController.kt | 3 +- .../res/layout/fragment_devtools_editor.xml | 14 +- .../layout/item_form_multiline_text_input.xml | 4 +- vector/src/main/res/values/strings.xml | 19 ++ 20 files changed, 268 insertions(+), 208 deletions(-) create mode 100644 vector/src/main/java/im/vector/app/features/devtools/DevToolsInteractionListener.kt diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/state/StateService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/state/StateService.kt index 444366e912..e614ea91d6 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/state/StateService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/state/StateService.kt @@ -65,13 +65,30 @@ interface StateService { */ suspend fun deleteAvatar() + /** + * Send a state event to the room + */ suspend fun sendStateEvent(eventType: String, stateKey: String?, body: JsonDict) + /** + * Get a state event of the room + */ fun getStateEvent(eventType: String, stateKey: QueryStringValue = QueryStringValue.NoCondition): Event? + /** + * Get a live state event of the room + */ fun getStateEventLive(eventType: String, stateKey: QueryStringValue = QueryStringValue.NoCondition): LiveData> + /** + * Get state events of the room + * @param eventTypes Set of eventType. If empty, all state events will be returned + */ fun getStateEvents(eventTypes: Set, stateKey: QueryStringValue = QueryStringValue.NoCondition): List + /** + * Get live state events of the room + * @param eventTypes Set of eventType to observe. If empty, all state events will be observed + */ fun getStateEventsLive(eventTypes: Set, stateKey: QueryStringValue = QueryStringValue.NoCondition): LiveData> } diff --git a/vector/src/main/java/im/vector/app/features/devtools/DevToolsInteractionListener.kt b/vector/src/main/java/im/vector/app/features/devtools/DevToolsInteractionListener.kt new file mode 100644 index 0000000000..e1e6f6b7cb --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/devtools/DevToolsInteractionListener.kt @@ -0,0 +1,21 @@ +/* + * 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.devtools + +interface DevToolsInteractionListener { + fun processAction(action: RoomDevToolAction) +} diff --git a/vector/src/main/java/im/vector/app/features/devtools/DevToolsViewEvents.kt b/vector/src/main/java/im/vector/app/features/devtools/DevToolsViewEvents.kt index 615144aaf6..96aed20dc4 100644 --- a/vector/src/main/java/im/vector/app/features/devtools/DevToolsViewEvents.kt +++ b/vector/src/main/java/im/vector/app/features/devtools/DevToolsViewEvents.kt @@ -22,6 +22,6 @@ sealed class DevToolsViewEvents : VectorViewEvents { object Dismiss : DevToolsViewEvents() // object ShowStateList : DevToolsViewEvents() - data class showAlertMessage(val message: String) : DevToolsViewEvents() - data class showSnackMessage(val message: String) : DevToolsViewEvents() + data class ShowAlertMessage(val message: String) : DevToolsViewEvents() + data class ShowSnackMessage(val message: String) : DevToolsViewEvents() } diff --git a/vector/src/main/java/im/vector/app/features/devtools/RoomDevToolActivity.kt b/vector/src/main/java/im/vector/app/features/devtools/RoomDevToolActivity.kt index fe6d684474..31b495a4d4 100644 --- a/vector/src/main/java/im/vector/app/features/devtools/RoomDevToolActivity.kt +++ b/vector/src/main/java/im/vector/app/features/devtools/RoomDevToolActivity.kt @@ -35,6 +35,7 @@ import com.airbnb.mvrx.viewModel import com.airbnb.mvrx.withState import im.vector.app.R import im.vector.app.core.di.ScreenComponent +import im.vector.app.core.extensions.exhaustive import im.vector.app.core.extensions.replaceFragment import im.vector.app.core.extensions.toMvRxBundle import im.vector.app.core.platform.SimpleFragmentActivity @@ -57,7 +58,7 @@ class RoomDevToolActivity : SimpleFragmentActivity(), RoomDevToolViewModel.Facto override fun getMenuRes() = R.menu.menu_devtools - var currentDisplayMode: RoomDevToolViewState.Mode? = null + private var currentDisplayMode: RoomDevToolViewState.Mode? = null @Parcelize data class Args( @@ -81,15 +82,16 @@ class RoomDevToolActivity : SimpleFragmentActivity(), RoomDevToolViewModel.Facto viewModel.observeViewEvents { when (it) { - DevToolsViewEvents.Dismiss -> finish() - is DevToolsViewEvents.showAlertMessage -> { + DevToolsViewEvents.Dismiss -> finish() + is DevToolsViewEvents.ShowAlertMessage -> { AlertDialog.Builder(this) .setMessage(it.message) .setPositiveButton(R.string.ok, null) .show() + Unit } - is DevToolsViewEvents.showSnackMessage -> showSnackbar(it.message) - } + is DevToolsViewEvents.ShowSnackMessage -> showSnackbar(it.message) + }.exhaustive } supportFragmentManager.addOnBackStackChangedListener(this) } @@ -97,7 +99,7 @@ class RoomDevToolActivity : SimpleFragmentActivity(), RoomDevToolViewModel.Facto private fun renderState(it: RoomDevToolViewState) { if (it.displayMode != currentDisplayMode) { when (it.displayMode) { - RoomDevToolViewState.Mode.Root -> { + RoomDevToolViewState.Mode.Root -> { val classJava = RoomDevToolFragment::class.java val tag = classJava.name if (supportFragmentManager.findFragmentByTag(tag) == null) { @@ -106,8 +108,13 @@ class RoomDevToolActivity : SimpleFragmentActivity(), RoomDevToolViewModel.Facto supportFragmentManager.popBackStack() } } - RoomDevToolViewState.Mode.StateEventDetail -> { - val frag = JSonViewerFragment.newInstance(it.selectedEventJson ?: "", -1, true, createJSonViewerStyleProvider(colorProvider)) + RoomDevToolViewState.Mode.StateEventDetail -> { + val frag = JSonViewerFragment.newInstance( + jsonString = it.selectedEventJson ?: "", + initialOpenDepth = -1, + wrap = true, + styleProvider = createJSonViewerStyleProvider(colorProvider) + ) navigateTo(frag) } RoomDevToolViewState.Mode.StateEventList, @@ -115,11 +122,11 @@ class RoomDevToolActivity : SimpleFragmentActivity(), RoomDevToolViewModel.Facto val frag = createFragment(RoomDevToolStateEventListFragment::class.java, Bundle().toMvRxBundle()) navigateTo(frag) } - RoomDevToolViewState.Mode.EditEventContent -> { + RoomDevToolViewState.Mode.EditEventContent -> { val frag = createFragment(RoomDevToolEditFragment::class.java, Bundle().toMvRxBundle()) navigateTo(frag) } - is RoomDevToolViewState.Mode.SendEventForm -> { + is RoomDevToolViewState.Mode.SendEventForm -> { val frag = createFragment(RoomDevToolSendFormFragment::class.java, Bundle().toMvRxBundle()) navigateTo(frag) } @@ -129,9 +136,9 @@ class RoomDevToolActivity : SimpleFragmentActivity(), RoomDevToolViewModel.Facto } when (it.modalLoading) { - is Loading -> showWaitingView() - is Success -> hideWaitingView() - is Fail -> { + is Loading -> showWaitingView() + is Success -> hideWaitingView() + is Fail -> { hideWaitingView() } Uninitialized -> { @@ -203,7 +210,7 @@ class RoomDevToolActivity : SimpleFragmentActivity(), RoomDevToolViewModel.Facto companion object { - fun intent(roomId: String, context: Context): Intent { + fun intent(context: Context, roomId: String): Intent { return Intent(context, RoomDevToolActivity::class.java).apply { putExtra(MvRx.KEY_ARG, Args(roomId)) } @@ -216,24 +223,26 @@ class RoomDevToolActivity : SimpleFragmentActivity(), RoomDevToolViewModel.Facto private fun updateToolBar(state: RoomDevToolViewState) { val title = when (state.displayMode) { - RoomDevToolViewState.Mode.Root -> { + RoomDevToolViewState.Mode.Root -> { getString(getTitleRes()) } - RoomDevToolViewState.Mode.StateEventList -> { - "State Events" + RoomDevToolViewState.Mode.StateEventList -> { + getString(R.string.dev_tools_state_event) } - RoomDevToolViewState.Mode.StateEventDetail -> { + RoomDevToolViewState.Mode.StateEventDetail -> { state.selectedEvent?.type } - RoomDevToolViewState.Mode.EditEventContent -> { - "Edit Content" + RoomDevToolViewState.Mode.EditEventContent -> { + getString(R.string.dev_tools_edit_content) } RoomDevToolViewState.Mode.StateEventListByType -> { state.currentStateType ?: "" } - is RoomDevToolViewState.Mode.SendEventForm -> { - if (state.displayMode.isState) "Send Custom State Event" - else "Send Custom Event" + is RoomDevToolViewState.Mode.SendEventForm -> { + getString( + if (state.displayMode.isState) R.string.dev_tools_send_custom_state_event + else R.string.dev_tools_send_custom_event + ) } } diff --git a/vector/src/main/java/im/vector/app/features/devtools/RoomDevToolEditFragment.kt b/vector/src/main/java/im/vector/app/features/devtools/RoomDevToolEditFragment.kt index cbb85f5ee6..98fe19a765 100644 --- a/vector/src/main/java/im/vector/app/features/devtools/RoomDevToolEditFragment.kt +++ b/vector/src/main/java/im/vector/app/features/devtools/RoomDevToolEditFragment.kt @@ -25,16 +25,13 @@ import com.airbnb.mvrx.withState import com.jakewharton.rxbinding3.widget.textChanges import im.vector.app.core.extensions.hideKeyboard import im.vector.app.core.platform.VectorBaseFragment -import im.vector.app.core.resources.ColorProvider import im.vector.app.databinding.FragmentDevtoolsEditorBinding import javax.inject.Inject -class RoomDevToolEditFragment @Inject constructor( - val epoxyController: RoomDevToolRootController, - private val colorProvider: ColorProvider -) : VectorBaseFragment(), RoomDevToolRootController.InteractionListener { +class RoomDevToolEditFragment @Inject constructor() + : VectorBaseFragment() { - val sharedViewModel: RoomDevToolViewModel by activityViewModel() + private val sharedViewModel: RoomDevToolViewModel by activityViewModel() override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentDevtoolsEditorBinding { return FragmentDevtoolsEditorBinding.inflate(inflater, container, false) @@ -53,13 +50,6 @@ class RoomDevToolEditFragment @Inject constructor( .disposeOnDestroyView() } - override fun invalidate() = withState(sharedViewModel) { _ -> - } - - override fun processAction(action: RoomDevToolAction) { - sharedViewModel.handle(action) - } - override fun onResume() { super.onResume() views.editText.requestFocus() diff --git a/vector/src/main/java/im/vector/app/features/devtools/RoomDevToolFragment.kt b/vector/src/main/java/im/vector/app/features/devtools/RoomDevToolFragment.kt index 36c1142bc2..0cc2a69bcf 100644 --- a/vector/src/main/java/im/vector/app/features/devtools/RoomDevToolFragment.kt +++ b/vector/src/main/java/im/vector/app/features/devtools/RoomDevToolFragment.kt @@ -21,20 +21,18 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import com.airbnb.mvrx.activityViewModel -import com.airbnb.mvrx.withState 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.core.resources.ColorProvider import im.vector.app.databinding.FragmentGenericRecyclerBinding import javax.inject.Inject class RoomDevToolFragment @Inject constructor( - val epoxyController: RoomDevToolRootController, - private val colorProvider: ColorProvider -) : VectorBaseFragment(), RoomDevToolRootController.InteractionListener { + private val epoxyController: RoomDevToolRootController +) : VectorBaseFragment(), + DevToolsInteractionListener { - val sharedViewModel: RoomDevToolViewModel by activityViewModel() + private val sharedViewModel: RoomDevToolViewModel by activityViewModel() override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentGenericRecyclerBinding { return FragmentGenericRecyclerBinding.inflate(inflater, container, false) @@ -62,10 +60,6 @@ class RoomDevToolFragment @Inject constructor( super.onDestroyView() } - override fun invalidate() = withState(sharedViewModel) { state -> - epoxyController.setData(state) - } - override fun processAction(action: RoomDevToolAction) { sharedViewModel.handle(action) } diff --git a/vector/src/main/java/im/vector/app/features/devtools/RoomDevToolRootController.kt b/vector/src/main/java/im/vector/app/features/devtools/RoomDevToolRootController.kt index 48f74eaaa5..785e0140ac 100644 --- a/vector/src/main/java/im/vector/app/features/devtools/RoomDevToolRootController.kt +++ b/vector/src/main/java/im/vector/app/features/devtools/RoomDevToolRootController.kt @@ -17,44 +17,43 @@ package im.vector.app.features.devtools import android.view.View -import com.airbnb.epoxy.TypedEpoxyController +import com.airbnb.epoxy.EpoxyController +import im.vector.app.R import im.vector.app.core.resources.StringProvider import im.vector.app.core.ui.list.genericButtonItem import javax.inject.Inject class RoomDevToolRootController @Inject constructor( private val stringProvider: StringProvider -) : TypedEpoxyController() { +) : EpoxyController() { - interface InteractionListener { - fun processAction(action: RoomDevToolAction) + init { + requestModelBuild() } - var interactionListener: InteractionListener? = null + var interactionListener: DevToolsInteractionListener? = null - override fun buildModels(data: RoomDevToolViewState?) { - if (data?.displayMode == RoomDevToolViewState.Mode.Root) { - genericButtonItem { - id("explore") - text("Explore Room State") - buttonClickAction(View.OnClickListener { - interactionListener?.processAction(RoomDevToolAction.ExploreRoomState) - }) - } - genericButtonItem { - id("send") - text("Send Custom Event") - buttonClickAction(View.OnClickListener { - interactionListener?.processAction(RoomDevToolAction.SendCustomEvent(false)) - }) - } - genericButtonItem { - id("send_state") - text("Send State Event") - buttonClickAction(View.OnClickListener { - interactionListener?.processAction(RoomDevToolAction.SendCustomEvent(true)) - }) - } + override fun buildModels() { + genericButtonItem { + id("explore") + text(stringProvider.getString(R.string.dev_tools_explore_room_state)) + buttonClickAction(View.OnClickListener { + interactionListener?.processAction(RoomDevToolAction.ExploreRoomState) + }) + } + genericButtonItem { + id("send") + text(stringProvider.getString(R.string.dev_tools_send_custom_event)) + buttonClickAction(View.OnClickListener { + interactionListener?.processAction(RoomDevToolAction.SendCustomEvent(false)) + }) + } + genericButtonItem { + id("send_state") + text(stringProvider.getString(R.string.dev_tools_send_state_event)) + buttonClickAction(View.OnClickListener { + interactionListener?.processAction(RoomDevToolAction.SendCustomEvent(true)) + }) } } } diff --git a/vector/src/main/java/im/vector/app/features/devtools/RoomDevToolSendFormController.kt b/vector/src/main/java/im/vector/app/features/devtools/RoomDevToolSendFormController.kt index c6e9e37e9f..e5b3fb737e 100644 --- a/vector/src/main/java/im/vector/app/features/devtools/RoomDevToolSendFormController.kt +++ b/vector/src/main/java/im/vector/app/features/devtools/RoomDevToolSendFormController.kt @@ -17,22 +17,21 @@ package im.vector.app.features.devtools import com.airbnb.epoxy.TypedEpoxyController +import im.vector.app.R +import im.vector.app.core.resources.StringProvider import im.vector.app.core.ui.list.genericFooterItem import im.vector.app.features.form.formEditTextItem import im.vector.app.features.form.formMultiLineEditTextItem import javax.inject.Inject -class RoomDevToolSendFormController @Inject constructor() : TypedEpoxyController() { +class RoomDevToolSendFormController @Inject constructor( + private val stringProvider: StringProvider +) : TypedEpoxyController() { - interface InteractionListener { - fun processAction(action: RoomDevToolAction) - } - - var interactionListener: InteractionListener? = null + var interactionListener: DevToolsInteractionListener? = null override fun buildModels(data: RoomDevToolViewState?) { - val sendMode = (data?.displayMode as? RoomDevToolViewState.Mode.SendEventForm) - ?: return + val sendEventForm = (data?.displayMode as? RoomDevToolViewState.Mode.SendEventForm) ?: return genericFooterItem { id("topSpace") @@ -42,19 +41,19 @@ class RoomDevToolSendFormController @Inject constructor() : TypedEpoxyController id("event_type") enabled(true) value(data.sendEventDraft?.type) - hint("Type") + hint(stringProvider.getString(R.string.dev_tools_form_hint_type)) showBottomSeparator(false) onTextChange { text -> interactionListener?.processAction(RoomDevToolAction.CustomEventTypeChange(text)) } } - if (sendMode.isState) { + if (sendEventForm.isState) { formEditTextItem { id("state_key") enabled(true) value(data.sendEventDraft?.stateKey) - hint("State Key") + hint(stringProvider.getString(R.string.dev_tools_form_hint_state_key)) showBottomSeparator(false) onTextChange { text -> interactionListener?.processAction(RoomDevToolAction.CustomEventStateKeyChange(text)) @@ -66,7 +65,7 @@ class RoomDevToolSendFormController @Inject constructor() : TypedEpoxyController id("event_content") enabled(true) value(data.sendEventDraft?.content) - hint("Event Content") + hint(stringProvider.getString(R.string.dev_tools_form_hint_event_content)) showBottomSeparator(false) onTextChange { text -> interactionListener?.processAction(RoomDevToolAction.CustomEventContentChange(text)) diff --git a/vector/src/main/java/im/vector/app/features/devtools/RoomDevToolSendFormFragment.kt b/vector/src/main/java/im/vector/app/features/devtools/RoomDevToolSendFormFragment.kt index d9e6f911c9..abda6104cd 100644 --- a/vector/src/main/java/im/vector/app/features/devtools/RoomDevToolSendFormFragment.kt +++ b/vector/src/main/java/im/vector/app/features/devtools/RoomDevToolSendFormFragment.kt @@ -25,14 +25,12 @@ import com.airbnb.mvrx.withState 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.core.resources.ColorProvider import im.vector.app.databinding.FragmentGenericRecyclerBinding import javax.inject.Inject class RoomDevToolSendFormFragment @Inject constructor( - val epoxyController: RoomDevToolSendFormController, - private val colorProvider: ColorProvider -) : VectorBaseFragment(), RoomDevToolSendFormController.InteractionListener { + private val epoxyController: RoomDevToolSendFormController +) : VectorBaseFragment(), DevToolsInteractionListener { val sharedViewModel: RoomDevToolViewModel by activityViewModel() diff --git a/vector/src/main/java/im/vector/app/features/devtools/RoomDevToolStateEventListFragment.kt b/vector/src/main/java/im/vector/app/features/devtools/RoomDevToolStateEventListFragment.kt index f2425b9713..600464bb6d 100644 --- a/vector/src/main/java/im/vector/app/features/devtools/RoomDevToolStateEventListFragment.kt +++ b/vector/src/main/java/im/vector/app/features/devtools/RoomDevToolStateEventListFragment.kt @@ -25,14 +25,12 @@ import com.airbnb.mvrx.withState 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.core.resources.ColorProvider import im.vector.app.databinding.FragmentGenericRecyclerBinding import javax.inject.Inject class RoomDevToolStateEventListFragment @Inject constructor( - val epoxyController: RoomStateListController, - private val colorProvider: ColorProvider -) : VectorBaseFragment(), RoomStateListController.InteractionListener { + private val epoxyController: RoomStateListController +) : VectorBaseFragment(), DevToolsInteractionListener { val sharedViewModel: RoomDevToolViewModel by activityViewModel() diff --git a/vector/src/main/java/im/vector/app/features/devtools/RoomDevToolViewModel.kt b/vector/src/main/java/im/vector/app/features/devtools/RoomDevToolViewModel.kt index 7c847be3b1..9fffe70872 100644 --- a/vector/src/main/java/im/vector/app/features/devtools/RoomDevToolViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/devtools/RoomDevToolViewModel.kt @@ -28,12 +28,15 @@ import com.squareup.moshi.Types import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject +import im.vector.app.R import im.vector.app.core.error.ErrorFormatter import im.vector.app.core.platform.VectorViewModel +import im.vector.app.core.resources.StringProvider import kotlinx.coroutines.launch import org.json.JSONObject import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.events.model.Event +import org.matrix.android.sdk.api.session.events.model.EventType import org.matrix.android.sdk.api.session.events.model.toModel import org.matrix.android.sdk.api.session.room.model.message.MessageContent import org.matrix.android.sdk.api.util.JsonDict @@ -43,6 +46,7 @@ import org.matrix.android.sdk.rx.rx class RoomDevToolViewModel @AssistedInject constructor( @Assisted val initialState: RoomDevToolViewState, private val errorFormatter: ErrorFormatter, + private val stringProvider: StringProvider, private val session: Session ) : VectorViewModel(initialState) { @@ -64,8 +68,8 @@ class RoomDevToolViewModel @AssistedInject constructor( } init { - - session.getRoom(initialState.roomId)?.rx() + session.getRoom(initialState.roomId) + ?.rx() ?.liveStateEvents(emptySet()) ?.execute { async -> copy(stateEvents = async) @@ -74,7 +78,7 @@ class RoomDevToolViewModel @AssistedInject constructor( override fun handle(action: RoomDevToolAction) { when (action) { - RoomDevToolAction.ExploreRoomState -> { + RoomDevToolAction.ExploreRoomState -> { setState { copy( displayMode = RoomDevToolViewState.Mode.StateEventList, @@ -82,7 +86,7 @@ class RoomDevToolViewModel @AssistedInject constructor( ) } } - is RoomDevToolAction.ShowStateEvent -> { + is RoomDevToolAction.ShowStateEvent -> { val jsonString = MoshiProvider.providesMoshi() .adapter(Event::class.java) .toJson(action.event) @@ -95,10 +99,10 @@ class RoomDevToolViewModel @AssistedInject constructor( ) } } - RoomDevToolAction.OnBackPressed -> { + RoomDevToolAction.OnBackPressed -> { handleBack() } - RoomDevToolAction.MenuEdit -> { + RoomDevToolAction.MenuEdit -> { withState { if (it.displayMode == RoomDevToolViewState.Mode.StateEventDetail) { // we want to edit it @@ -112,7 +116,7 @@ class RoomDevToolViewModel @AssistedInject constructor( } } } - is RoomDevToolAction.ShowStateEventType -> { + is RoomDevToolAction.ShowStateEventType -> { setState { copy( displayMode = RoomDevToolViewState.Mode.StateEventListByType, @@ -120,23 +124,23 @@ class RoomDevToolViewModel @AssistedInject constructor( ) } } - RoomDevToolAction.MenuItemSend -> { + RoomDevToolAction.MenuItemSend -> { handleMenuItemSend() } - is RoomDevToolAction.UpdateContentText -> { + is RoomDevToolAction.UpdateContentText -> { setState { copy(editedContent = action.contentJson) } } - is RoomDevToolAction.SendCustomEvent -> { + is RoomDevToolAction.SendCustomEvent -> { setState { copy( displayMode = RoomDevToolViewState.Mode.SendEventForm(action.isStateEvent), - sendEventDraft = RoomDevToolViewState.SendEventDraft("m.room.message", null, "{\n}") + sendEventDraft = RoomDevToolViewState.SendEventDraft(EventType.MESSAGE, null, "{\n}") ) } } - is RoomDevToolAction.CustomEventTypeChange -> { + is RoomDevToolAction.CustomEventTypeChange -> { setState { copy( sendEventDraft = sendEventDraft?.copy(type = action.type) @@ -150,7 +154,7 @@ class RoomDevToolViewModel @AssistedInject constructor( ) } } - is RoomDevToolAction.CustomEventContentChange -> { + is RoomDevToolAction.CustomEventContentChange -> { setState { copy( sendEventDraft = sendEventDraft?.copy(content = action.content) @@ -160,95 +164,102 @@ class RoomDevToolViewModel @AssistedInject constructor( } } - private fun handleMenuItemSend() = withState { - when (it.displayMode) { - RoomDevToolViewState.Mode.EditEventContent -> { - setState { copy(modalLoading = Loading()) } - viewModelScope.launch { - try { - val room = session.getRoom(initialState.roomId) - ?: throw IllegalArgumentException("Room not found") + private fun handleMenuItemSend() = withState { state -> + when (state.displayMode) { + RoomDevToolViewState.Mode.EditEventContent -> editEventContent(state) + is RoomDevToolViewState.Mode.SendEventForm -> sendEventContent(state, state.displayMode.isState) + else -> Unit + } + } - val adapter = MoshiProvider.providesMoshi() - .adapter(Types.newParameterizedType(Map::class.java, String::class.java, Any::class.java)) - val json = adapter.fromJson(it.editedContent ?: "") - ?: throw IllegalArgumentException("No content") + private fun editEventContent(state: RoomDevToolViewState) { + setState { copy(modalLoading = Loading()) } - room.sendStateEvent( - it.selectedEvent?.type ?: "", - it.selectedEvent?.stateKey, - json + viewModelScope.launch { + try { + val room = session.getRoom(initialState.roomId) + ?: throw IllegalArgumentException(stringProvider.getString(R.string.room_error_not_found)) - ) - _viewEvents.post(DevToolsViewEvents.showSnackMessage("State event sent!")) - setState { - copy( - modalLoading = Success(Unit), - selectedEventJson = null, - editedContent = null, - displayMode = RoomDevToolViewState.Mode.StateEventListByType - ) - } - } catch (failure: Throwable) { - _viewEvents.post(DevToolsViewEvents.showAlertMessage(errorFormatter.toHumanReadable(failure))) - setState { copy(modalLoading = Fail(failure)) } - } + val adapter = MoshiProvider.providesMoshi() + .adapter(Types.newParameterizedType(Map::class.java, String::class.java, Any::class.java)) + val json = adapter.fromJson(state.editedContent ?: "") + ?: throw IllegalArgumentException(stringProvider.getString(R.string.dev_tools_error_no_content)) + + room.sendStateEvent( + state.selectedEvent?.type ?: "", + state.selectedEvent?.stateKey, + json + + ) + _viewEvents.post(DevToolsViewEvents.ShowSnackMessage(stringProvider.getString(R.string.dev_tools_success_state_event))) + setState { + copy( + modalLoading = Success(Unit), + selectedEventJson = null, + editedContent = null, + displayMode = RoomDevToolViewState.Mode.StateEventListByType + ) } + } catch (failure: Throwable) { + _viewEvents.post(DevToolsViewEvents.ShowAlertMessage(errorFormatter.toHumanReadable(failure))) + setState { copy(modalLoading = Fail(failure)) } } - is RoomDevToolViewState.Mode.SendEventForm -> { - setState { copy(modalLoading = Loading()) } - viewModelScope.launch { - try { - val room = session.getRoom(initialState.roomId) - ?: throw IllegalArgumentException("Room not found") + } + } - val adapter = MoshiProvider.providesMoshi() - .adapter(Types.newParameterizedType(Map::class.java, String::class.java, Any::class.java)) - val json = adapter.fromJson(it.sendEventDraft?.content ?: "") - ?: throw IllegalArgumentException("No content") + private fun sendEventContent(state: RoomDevToolViewState, isState: Boolean) { + setState { copy(modalLoading = Loading()) } + viewModelScope.launch { + try { + val room = session.getRoom(initialState.roomId) + ?: throw IllegalArgumentException(stringProvider.getString(R.string.room_error_not_found)) - val eventType = it.sendEventDraft?.type ?: throw IllegalArgumentException("Missing message type") - if (it.displayMode.isState) { - room.sendStateEvent( - eventType, - it.sendEventDraft.stateKey, - json + val adapter = MoshiProvider.providesMoshi() + .adapter(Types.newParameterizedType(Map::class.java, String::class.java, Any::class.java)) + val json = adapter.fromJson(state.sendEventDraft?.content ?: "") + ?: throw IllegalArgumentException(stringProvider.getString(R.string.dev_tools_error_no_content)) - ) - } else { - // can we try to do some validation?? - // val validParse = MoshiProvider.providesMoshi().adapter(MessageContent::class.java).fromJson(it.sendEventDraft.content ?: "") - json.toModel(catchError = false) - ?: throw IllegalArgumentException("Malformed event") - room.sendEvent( - eventType, - json - ) - } + val eventType = state.sendEventDraft?.type + ?: throw IllegalArgumentException(stringProvider.getString(R.string.dev_tools_error_no_message_type)) - _viewEvents.post(DevToolsViewEvents.showSnackMessage("Event sent!")) - setState { - copy( - modalLoading = Success(Unit), - sendEventDraft = null, - displayMode = RoomDevToolViewState.Mode.Root - ) - } - } catch (failure: Throwable) { - _viewEvents.post(DevToolsViewEvents.showAlertMessage(errorFormatter.toHumanReadable(failure))) - setState { copy(modalLoading = Fail(failure)) } - } + if (isState) { + room.sendStateEvent( + eventType, + state.sendEventDraft.stateKey, + json + ) + } else { + // can we try to do some validation?? + // val validParse = MoshiProvider.providesMoshi().adapter(MessageContent::class.java).fromJson(it.sendEventDraft.content ?: "") + json.toModel(catchError = false) + ?: throw IllegalArgumentException(stringProvider.getString(R.string.dev_tools_error_malformed_event)) + room.sendEvent( + eventType, + json + ) } + + _viewEvents.post(DevToolsViewEvents.ShowSnackMessage(stringProvider.getString(R.string.dev_tools_success_event))) + setState { + copy( + modalLoading = Success(Unit), + sendEventDraft = null, + displayMode = RoomDevToolViewState.Mode.Root + ) + } + } catch (failure: Throwable) { + _viewEvents.post(DevToolsViewEvents.ShowAlertMessage(errorFormatter.toHumanReadable(failure))) + setState { copy(modalLoading = Fail(failure)) } } } } private fun handleBack() = withState { when (it.displayMode) { - RoomDevToolViewState.Mode.Root -> { + RoomDevToolViewState.Mode.Root -> { _viewEvents.post(DevToolsViewEvents.Dismiss) } - RoomDevToolViewState.Mode.StateEventList -> { + RoomDevToolViewState.Mode.StateEventList -> { setState { copy( selectedEvent = null, @@ -257,7 +268,7 @@ class RoomDevToolViewModel @AssistedInject constructor( ) } } - RoomDevToolViewState.Mode.StateEventDetail -> { + RoomDevToolViewState.Mode.StateEventDetail -> { setState { copy( selectedEvent = null, @@ -266,7 +277,7 @@ class RoomDevToolViewModel @AssistedInject constructor( ) } } - RoomDevToolViewState.Mode.EditEventContent -> { + RoomDevToolViewState.Mode.EditEventContent -> { setState { copy( displayMode = RoomDevToolViewState.Mode.StateEventDetail @@ -281,7 +292,7 @@ class RoomDevToolViewModel @AssistedInject constructor( ) } } - is RoomDevToolViewState.Mode.SendEventForm -> { + is RoomDevToolViewState.Mode.SendEventForm -> { setState { copy( displayMode = RoomDevToolViewState.Mode.Root diff --git a/vector/src/main/java/im/vector/app/features/devtools/RoomStateListController.kt b/vector/src/main/java/im/vector/app/features/devtools/RoomStateListController.kt index 844ed9be20..69070c509b 100644 --- a/vector/src/main/java/im/vector/app/features/devtools/RoomStateListController.kt +++ b/vector/src/main/java/im/vector/app/features/devtools/RoomStateListController.kt @@ -32,18 +32,12 @@ class RoomStateListController @Inject constructor( private val colorProvider: ColorProvider ) : TypedEpoxyController() { - interface InteractionListener { - fun processAction(action: RoomDevToolAction) - } - - var interactionListener: InteractionListener? = null + var interactionListener: DevToolsInteractionListener? = null override fun buildModels(data: RoomDevToolViewState?) { when (data?.displayMode) { RoomDevToolViewState.Mode.StateEventList -> { - val stateEventsGroups = (data.stateEvents.invoke() ?: emptyList()).groupBy { - it.type - } + val stateEventsGroups = data.stateEvents.invoke().orEmpty().groupBy { it.type } if (stateEventsGroups.isEmpty()) { noResultItem { @@ -55,7 +49,7 @@ class RoomStateListController @Inject constructor( genericItem { id(entry.key) title(entry.key) - description("${entry.value.size} entries") + description(stringProvider.getQuantityString(R.plurals.entries, entry.value.size, entry.value.size)) itemClickAction(GenericItem.Action("view").apply { perform = Runnable { interactionListener?.processAction(RoomDevToolAction.ShowStateEventType(entry.key)) @@ -66,7 +60,7 @@ class RoomStateListController @Inject constructor( } } RoomDevToolViewState.Mode.StateEventListByType -> { - val stateEvents = (data.stateEvents.invoke() ?: emptyList()).filter { it.type == data.currentStateType } + val stateEvents = data.stateEvents.invoke().orEmpty().filter { it.type == data.currentStateType } if (stateEvents.isEmpty()) { noResultItem { id("no state events") @@ -74,10 +68,12 @@ class RoomStateListController @Inject constructor( } } else { stateEvents.forEach { stateEvent -> - val contentMap = JSONObject(stateEvent.content ?: emptyMap()).toString().let { + val contentJson = JSONObject(stateEvent.content.orEmpty()).toString().let { if (it.length > 140) { it.take(140) + Typography.ellipsis - } else it.take(140) + } else { + it.take(140) + } } genericItem { id(stateEvent.eventId) @@ -95,7 +91,7 @@ class RoomStateListController @Inject constructor( textStyle = "normal" } }) - description(contentMap) + description(contentJson) itemClickAction(GenericItem.Action("view").apply { perform = Runnable { interactionListener?.processAction(RoomDevToolAction.ShowStateEvent(stateEvent)) diff --git a/vector/src/main/java/im/vector/app/features/form/FormMultiLineEditTextItem.kt b/vector/src/main/java/im/vector/app/features/form/FormMultiLineEditTextItem.kt index 974d2594ee..4ba668a051 100644 --- a/vector/src/main/java/im/vector/app/features/form/FormMultiLineEditTextItem.kt +++ b/vector/src/main/java/im/vector/app/features/form/FormMultiLineEditTextItem.kt @@ -1,5 +1,5 @@ /* - * Copyright 2019 New Vector Ltd + * 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. diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailFragment.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailFragment.kt index 8347735d0a..fc380193d1 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailFragment.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailFragment.kt @@ -128,7 +128,6 @@ import im.vector.app.features.command.Command import im.vector.app.features.crypto.keysbackup.restore.KeysBackupRestoreActivity import im.vector.app.features.crypto.util.toImageRes import im.vector.app.features.crypto.verification.VerificationBottomSheet -import im.vector.app.features.devtools.RoomDevToolActivity import im.vector.app.features.home.AvatarRenderer import im.vector.app.features.home.room.detail.composer.TextComposerView import im.vector.app.features.home.room.detail.readreceipts.DisplayReadReceiptsBottomSheet @@ -769,7 +768,7 @@ class RoomDetailFragment @Inject constructor( true } R.id.dev_tools -> { - startActivity(RoomDevToolActivity.intent(roomDetailArgs.roomId, requireContext())) + navigator.openDevTools(requireContext(), roomDetailArgs.roomId) true } else -> super.onOptionsItemSelected(item) diff --git a/vector/src/main/java/im/vector/app/features/navigation/DefaultNavigator.kt b/vector/src/main/java/im/vector/app/features/navigation/DefaultNavigator.kt index 43a3f748a5..c802a5f65c 100644 --- a/vector/src/main/java/im/vector/app/features/navigation/DefaultNavigator.kt +++ b/vector/src/main/java/im/vector/app/features/navigation/DefaultNavigator.kt @@ -42,6 +42,7 @@ import im.vector.app.features.crypto.recover.SetupMode import im.vector.app.features.crypto.verification.SupportedVerificationMethodsProvider import im.vector.app.features.crypto.verification.VerificationBottomSheet import im.vector.app.features.debug.DebugMenuActivity +import im.vector.app.features.devtools.RoomDevToolActivity import im.vector.app.features.home.room.detail.RoomDetailActivity import im.vector.app.features.home.room.detail.RoomDetailArgs import im.vector.app.features.home.room.detail.search.SearchActivity @@ -345,6 +346,10 @@ class DefaultNavigator @Inject constructor( context.startActivity(intent) } + override fun openDevTools(context: Context, roomId: String) { + context.startActivity(RoomDevToolActivity.intent(context, roomId)) + } + private fun startActivity(context: Context, intent: Intent, buildTask: Boolean) { if (buildTask) { val stackBuilder = TaskStackBuilder.create(context) diff --git a/vector/src/main/java/im/vector/app/features/navigation/Navigator.kt b/vector/src/main/java/im/vector/app/features/navigation/Navigator.kt index dda071795b..4d09bde93c 100644 --- a/vector/src/main/java/im/vector/app/features/navigation/Navigator.kt +++ b/vector/src/main/java/im/vector/app/features/navigation/Navigator.kt @@ -117,4 +117,6 @@ interface Navigator { options: ((MutableList>) -> Unit)?) fun openSearch(context: Context, roomId: String) + + fun openDevTools(context: Context, roomId: String) } diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/members/RoomMemberListController.kt b/vector/src/main/java/im/vector/app/features/roomprofile/members/RoomMemberListController.kt index 86146e7d3a..eda461de14 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/members/RoomMemberListController.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/members/RoomMemberListController.kt @@ -63,8 +63,7 @@ class RoomMemberListController @Inject constructor( ?.filter { event -> event.content.toModel() ?.takeIf { - it.displayName != null - && (data.filter.isEmpty() || it.displayName!!.contains(data.filter, ignoreCase = true)) + data.filter.isEmpty() || it.displayName?.contains(data.filter, ignoreCase = true) == true } != null } .orEmpty() diff --git a/vector/src/main/res/layout/fragment_devtools_editor.xml b/vector/src/main/res/layout/fragment_devtools_editor.xml index 98682a1b65..eaa7399279 100644 --- a/vector/src/main/res/layout/fragment_devtools_editor.xml +++ b/vector/src/main/res/layout/fragment_devtools_editor.xml @@ -1,16 +1,20 @@ + android:textSize="12sp" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent" /> \ No newline at end of file diff --git a/vector/src/main/res/layout/item_form_multiline_text_input.xml b/vector/src/main/res/layout/item_form_multiline_text_input.xml index f844f91a5a..aaad0826ef 100644 --- a/vector/src/main/res/layout/item_form_multiline_text_input.xml +++ b/vector/src/main/res/layout/item_form_multiline_text_input.xml @@ -25,9 +25,9 @@ android:id="@+id/formMultiLineEditText" android:layout_width="match_parent" android:layout_height="wrap_content" - android:inputType="textMultiLine" - android:imeOptions="actionDone" android:gravity="top|start" + android:imeOptions="actionDone" + android:inputType="textMultiLine" android:minLines="4" tools:hint="@string/create_room_name_hint" /> diff --git a/vector/src/main/res/values/strings.xml b/vector/src/main/res/values/strings.xml index c2677bf89e..2d54948116 100644 --- a/vector/src/main/res/values/strings.xml +++ b/vector/src/main/res/values/strings.xml @@ -2745,6 +2745,10 @@ Wrong code, %d remaining attempt Wrong code, %d remaining attempts + + %d entry" + %d entries" + Warning! Last remaining attempt before logout! Too many errors, you\'ve been logged out Choose a PIN for security @@ -2793,4 +2797,19 @@ Element requires you to enter your credentials to perform this action. Failed to authenticate Dev Tools + Explore Room State + Send Custom Event + Send State Event + State Events + Edit Content + Send Custom State Event + Type + State Key + Event Content + No content + Missing message type + Malformed event + Event sent! + State event sent! +