From c714266a8122b3f496ef21cffe947c21b5426dd2 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 6 Dec 2019 12:00:29 +0100 Subject: [PATCH 01/22] Fix crash reported by the PlayStore. NullPointerException: at im.vector.riotx.features.home.room.detail.RoomDetailFragment.updateJumpToReadMarkerViewVisibility (RoomDetailFragment.kt:524) Also properly cleanup model build listener --- .../home/room/detail/RoomDetailFragment.kt | 55 +++++++++++-------- .../home/room/list/RoomListFragment.kt | 11 ++-- 2 files changed, 39 insertions(+), 27 deletions(-) diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailFragment.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailFragment.kt index 19ba1a85ce..27edc6bc5d 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailFragment.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailFragment.kt @@ -46,6 +46,7 @@ import androidx.recyclerview.widget.RecyclerView import butterknife.BindView import com.airbnb.epoxy.EpoxyModel import com.airbnb.epoxy.EpoxyVisibilityTracker +import com.airbnb.epoxy.OnModelBuildFinishedListener import com.airbnb.mvrx.* import com.github.piasy.biv.BigImageViewer import com.github.piasy.biv.loader.ImageLoader @@ -193,6 +194,8 @@ class RoomDetailFragment @Inject constructor( private lateinit var sharedActionViewModel: MessageSharedActionViewModel private lateinit var layoutManager: LinearLayoutManager + private lateinit var modelBuildListener: OnModelBuildFinishedListener + private lateinit var attachmentsHelper: AttachmentsHelper private lateinit var keyboardStateUtils: KeyboardStateUtils @@ -286,8 +289,9 @@ class RoomDetailFragment @Inject constructor( } override fun onDestroyView() { - super.onDestroyView() + timelineEventController.removeModelBuildListener(modelBuildListener) recyclerView.adapter = null + super.onDestroyView() } override fun onDestroy() { @@ -470,13 +474,14 @@ class RoomDetailFragment @Inject constructor( recyclerView.layoutManager = layoutManager recyclerView.itemAnimator = null recyclerView.setHasFixedSize(true) - timelineEventController.addModelBuildListener { + modelBuildListener = OnModelBuildFinishedListener { it.dispatchTo(stateRestorer) it.dispatchTo(scrollOnNewMessageCallback) it.dispatchTo(scrollOnHighlightedEventCallback) updateJumpToReadMarkerViewVisibility() updateJumpToBottomViewVisibility() } + timelineEventController.addModelBuildListener { modelBuildListener } recyclerView.adapter = timelineEventController.adapter recyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener() { @@ -521,37 +526,41 @@ class RoomDetailFragment @Inject constructor( } } - private fun updateJumpToReadMarkerViewVisibility() = jumpToReadMarkerView.post { - withState(roomDetailViewModel) { - val showJumpToUnreadBanner = when (it.unreadState) { - UnreadState.Unknown, - UnreadState.HasNoUnread -> false - is UnreadState.ReadMarkerNotLoaded -> true - is UnreadState.HasUnread -> { - if (it.canShowJumpToReadMarker) { - val lastVisibleItem = layoutManager.findLastVisibleItemPosition() - val positionOfReadMarker = timelineEventController.getPositionOfReadMarker() - if (positionOfReadMarker == null) { - false + private fun updateJumpToReadMarkerViewVisibility() { + jumpToReadMarkerView?.post { + withState(roomDetailViewModel) { + val showJumpToUnreadBanner = when (it.unreadState) { + UnreadState.Unknown, + UnreadState.HasNoUnread -> false + is UnreadState.ReadMarkerNotLoaded -> true + is UnreadState.HasUnread -> { + if (it.canShowJumpToReadMarker) { + val lastVisibleItem = layoutManager.findLastVisibleItemPosition() + val positionOfReadMarker = timelineEventController.getPositionOfReadMarker() + if (positionOfReadMarker == null) { + false + } else { + positionOfReadMarker > lastVisibleItem + } } else { - positionOfReadMarker > lastVisibleItem + false } - } else { - false } } + jumpToReadMarkerView.isVisible = showJumpToUnreadBanner } - jumpToReadMarkerView.isVisible = showJumpToUnreadBanner } } private fun updateJumpToBottomViewVisibility() { debouncer.debounce("jump_to_bottom_visibility", 250, Runnable { - Timber.v("First visible: ${layoutManager.findFirstCompletelyVisibleItemPosition()}") - if (layoutManager.findFirstVisibleItemPosition() != 0) { - jumpToBottomView.show() - } else { - jumpToBottomView.hide() + if (isAdded) { + Timber.v("First visible: ${layoutManager.findFirstCompletelyVisibleItemPosition()}") + if (layoutManager.findFirstVisibleItemPosition() != 0) { + jumpToBottomView.show() + } else { + jumpToBottomView.hide() + } } }) } diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/list/RoomListFragment.kt b/vector/src/main/java/im/vector/riotx/features/home/room/list/RoomListFragment.kt index 04d1802264..0fab14867c 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/room/list/RoomListFragment.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/room/list/RoomListFragment.kt @@ -26,6 +26,7 @@ import androidx.core.content.ContextCompat import androidx.core.view.isVisible import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView +import com.airbnb.epoxy.OnModelBuildFinishedListener import com.airbnb.mvrx.* import com.google.android.material.snackbar.Snackbar import im.vector.matrix.android.api.failure.Failure @@ -38,10 +39,9 @@ import im.vector.riotx.core.error.ErrorFormatter import im.vector.riotx.core.platform.OnBackPressed import im.vector.riotx.core.platform.StateView import im.vector.riotx.core.platform.VectorBaseFragment - import im.vector.riotx.features.home.RoomListDisplayMode -import im.vector.riotx.features.home.room.list.actions.RoomListQuickActionsSharedAction import im.vector.riotx.features.home.room.list.actions.RoomListQuickActionsBottomSheet +import im.vector.riotx.features.home.room.list.actions.RoomListQuickActionsSharedAction import im.vector.riotx.features.home.room.list.actions.RoomListQuickActionsSharedActionViewModel import im.vector.riotx.features.home.room.list.widget.FabMenuView import im.vector.riotx.features.notifications.NotificationDrawerManager @@ -65,6 +65,7 @@ class RoomListFragment @Inject constructor( ) : VectorBaseFragment(), RoomSummaryController.Listener, OnBackPressed, FabMenuView.Listener { + private lateinit var modelBuildListener: OnModelBuildFinishedListener private lateinit var sharedActionViewModel: RoomListQuickActionsSharedActionViewModel private val roomListParams: RoomListParams by args() private val roomListViewModel: RoomListViewModel by fragmentViewModel() @@ -118,8 +119,9 @@ class RoomListFragment @Inject constructor( } override fun onDestroyView() { - super.onDestroyView() + roomController.removeModelBuildListener(modelBuildListener) roomListView.adapter = null + super.onDestroyView() } private fun openSelectedRoom(event: RoomListViewEvents.SelectRoom) { @@ -198,7 +200,8 @@ class RoomListFragment @Inject constructor( roomListView.layoutManager = layoutManager roomListView.itemAnimator = RoomListAnimator() roomController.listener = this - roomController.addModelBuildListener { it.dispatchTo(stateRestorer) } + modelBuildListener = OnModelBuildFinishedListener { it.dispatchTo(stateRestorer) } + roomController.addModelBuildListener(modelBuildListener) roomListView.adapter = roomController.adapter stateView.contentView = roomListView } From dbd4525404e67719449f93c90279430107cc76bf Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 6 Dec 2019 14:39:19 +0100 Subject: [PATCH 02/22] Make sure unhandled Rx error does not crash the app in production --- .../java/im/vector/riotx/VectorApplication.kt | 5 +-- .../main/java/im/vector/riotx/core/rx/Rx.kt | 35 +++++++++++++++++++ 2 files changed, 38 insertions(+), 2 deletions(-) create mode 100644 vector/src/main/java/im/vector/riotx/core/rx/Rx.kt diff --git a/vector/src/main/java/im/vector/riotx/VectorApplication.kt b/vector/src/main/java/im/vector/riotx/VectorApplication.kt index 5ca888fc2e..875e6c417a 100644 --- a/vector/src/main/java/im/vector/riotx/VectorApplication.kt +++ b/vector/src/main/java/im/vector/riotx/VectorApplication.kt @@ -42,6 +42,7 @@ import im.vector.riotx.core.di.DaggerVectorComponent import im.vector.riotx.core.di.HasVectorInjector import im.vector.riotx.core.di.VectorComponent import im.vector.riotx.core.extensions.configureAndStart +import im.vector.riotx.core.rx.setupRxPlugin import im.vector.riotx.core.utils.initKnownEmojiHashSet import im.vector.riotx.features.configuration.VectorConfiguration import im.vector.riotx.features.lifecycle.VectorActivityLifecycleCallbacks @@ -55,8 +56,7 @@ import im.vector.riotx.features.version.VersionProvider import im.vector.riotx.push.fcm.FcmHelper import timber.log.Timber import java.text.SimpleDateFormat -import java.util.Date -import java.util.Locale +import java.util.* import javax.inject.Inject class VectorApplication : Application(), HasVectorInjector, MatrixConfiguration.Provider, androidx.work.Configuration.Provider { @@ -87,6 +87,7 @@ class VectorApplication : Application(), HasVectorInjector, MatrixConfiguration. vectorComponent = DaggerVectorComponent.factory().create(this) vectorComponent.inject(this) vectorUncaughtExceptionHandler.activate(this) + setupRxPlugin() if (BuildConfig.DEBUG) { Timber.plant(Timber.DebugTree()) diff --git a/vector/src/main/java/im/vector/riotx/core/rx/Rx.kt b/vector/src/main/java/im/vector/riotx/core/rx/Rx.kt new file mode 100644 index 0000000000..89de9030dc --- /dev/null +++ b/vector/src/main/java/im/vector/riotx/core/rx/Rx.kt @@ -0,0 +1,35 @@ +/* + * 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.riotx.core.rx + +import im.vector.riotx.BuildConfig +import io.reactivex.plugins.RxJavaPlugins +import timber.log.Timber + +/** + * Make sure unhandled Rx error does not crash the app in production + */ +fun setupRxPlugin() { + RxJavaPlugins.setErrorHandler { throwable -> + Timber.e(throwable, "RxError") + + // Avoid crash in production + if (BuildConfig.DEBUG) { + throw throwable + } + } +} From 109c1fe482495f7b97bbfdaf474c91609321faf5 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 6 Dec 2019 14:42:41 +0100 Subject: [PATCH 03/22] Cleanup --- vector/src/main/java/im/vector/riotx/VectorApplication.kt | 2 -- 1 file changed, 2 deletions(-) diff --git a/vector/src/main/java/im/vector/riotx/VectorApplication.kt b/vector/src/main/java/im/vector/riotx/VectorApplication.kt index 875e6c417a..f3043fbec8 100644 --- a/vector/src/main/java/im/vector/riotx/VectorApplication.kt +++ b/vector/src/main/java/im/vector/riotx/VectorApplication.kt @@ -79,8 +79,6 @@ class VectorApplication : Application(), HasVectorInjector, MatrixConfiguration. lateinit var vectorComponent: VectorComponent private var fontThreadHandler: Handler? = null -// var slowMode = false - override fun onCreate() { super.onCreate() appContext = this From 9a01b4ace95af9e3376d9971f4baad19b4fffb9c Mon Sep 17 00:00:00 2001 From: ganfra Date: Mon, 9 Dec 2019 17:08:50 +0100 Subject: [PATCH 04/22] Make it through bunch of classes removing potential leaks --- .../content/ContentUploadStateTracker.kt | 2 ++ .../DefaultContentUploadStateTracker.kt | 4 ++++ .../session/room/timeline/DefaultTimeline.kt | 11 +++++---- .../debug/sas/DebugSasEmojiActivity.kt | 14 +++++++---- .../riotx/core/platform/VectorBaseFragment.kt | 1 + .../settings/KeysBackupSettingsFragment.kt | 12 +++++++--- .../CreateDirectRoomDirectoryUsersFragment.kt | 11 ++++++++- .../CreateDirectRoomKnownUsersFragment.kt | 10 +++++++- .../features/home/group/GroupListFragment.kt | 13 +++++++++-- .../home/room/detail/RoomDetailFragment.kt | 20 ++++++++-------- .../home/room/detail/RoomDetailViewModel.kt | 2 +- .../timeline/TimelineEventController.kt | 9 +++++++- .../helper/ContentUploadStateTrackerBinder.kt | 13 +++++++++-- .../helper/TimelineMediaSizeProvider.kt | 15 ++++++------ .../home/room/list/RoomListFragment.kt | 5 +++- .../login/terms/LoginTermsFragment.kt | 12 ++++++++-- .../reactions/EmojiSearchResultFragment.kt | 18 ++++++++++----- .../roomdirectory/PublicRoomsFragment.kt | 23 +++++++++---------- .../createroom/CreateRoomFragment.kt | 10 +++++--- .../picker/RoomDirectoryPickerFragment.kt | 10 +++++--- .../VectorSettingsIgnoredUsersFragment.kt | 14 ++++++++--- .../settings/push/PushGatewaysFragment.kt | 22 +++++++++++------- .../settings/push/PushRulesFragment.kt | 22 +++++++++++------- .../layout/fragment_create_direct_room.xml | 2 +- ...ent_create_direct_room_directory_users.xml | 2 +- .../main/res/layout/fragment_create_room.xml | 2 +- ...poxy.xml => fragment_generic_recycler.xml} | 4 ++-- .../main/res/layout/fragment_group_list.xml | 4 ++-- .../layout/fragment_keys_backup_settings.xml | 2 +- .../main/res/layout/fragment_login_terms.xml | 2 +- .../main/res/layout/fragment_public_rooms.xml | 2 +- .../layout/fragment_room_directory_picker.xml | 2 +- 32 files changed, 202 insertions(+), 93 deletions(-) rename vector/src/main/res/layout/{fragment_generic_recycler_epoxy.xml => fragment_generic_recycler.xml} (86%) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/content/ContentUploadStateTracker.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/content/ContentUploadStateTracker.kt index 540b164aa5..dedf9a83cf 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/content/ContentUploadStateTracker.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/content/ContentUploadStateTracker.kt @@ -22,6 +22,8 @@ interface ContentUploadStateTracker { fun untrack(key: String, updateListener: UpdateListener) + fun clear() + interface UpdateListener { fun onUpdate(state: State) } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/content/DefaultContentUploadStateTracker.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/content/DefaultContentUploadStateTracker.kt index 68f48d20db..66a8341801 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/content/DefaultContentUploadStateTracker.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/content/DefaultContentUploadStateTracker.kt @@ -42,6 +42,10 @@ internal class DefaultContentUploadStateTracker @Inject constructor() : ContentU } } + override fun clear() { + listeners.clear() + } + internal fun setFailure(key: String, throwable: Throwable) { val failure = ContentUploadStateTracker.State.Failure(throwable) updateState(key, failure) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/DefaultTimeline.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/DefaultTimeline.kt index b83240a681..693855edbc 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/DefaultTimeline.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/DefaultTimeline.kt @@ -289,6 +289,9 @@ internal class DefaultTimeline( } override fun addListener(listener: Timeline.Listener) = synchronized(listeners) { + if (listeners.contains(listener)) { + return false + } listeners.add(listener).also { postSnapshot() } @@ -494,9 +497,9 @@ internal class DefaultTimeline( return } val params = PaginationTask.Params(roomId = roomId, - from = token, - direction = direction.toPaginationDirection(), - limit = limit) + from = token, + direction = direction.toPaginationDirection(), + limit = limit) Timber.v("Should fetch $limit items $direction") cancelableBag += paginationTask @@ -571,7 +574,7 @@ internal class DefaultTimeline( val timelineEvent = buildTimelineEvent(eventEntity) if (timelineEvent.isEncrypted() - && timelineEvent.root.mxDecryptionResult == null) { + && timelineEvent.root.mxDecryptionResult == null) { timelineEvent.root.eventId?.let { eventDecryptor.requestDecryption(it) } } diff --git a/vector/src/debug/java/im/vector/riotx/features/debug/sas/DebugSasEmojiActivity.kt b/vector/src/debug/java/im/vector/riotx/features/debug/sas/DebugSasEmojiActivity.kt index ba1c47c08c..17a78f1c6a 100644 --- a/vector/src/debug/java/im/vector/riotx/features/debug/sas/DebugSasEmojiActivity.kt +++ b/vector/src/debug/java/im/vector/riotx/features/debug/sas/DebugSasEmojiActivity.kt @@ -18,18 +18,24 @@ package im.vector.riotx.features.debug.sas import android.os.Bundle import androidx.appcompat.app.AppCompatActivity +import androidx.recyclerview.widget.LinearLayoutManager import im.vector.matrix.android.api.crypto.getAllVerificationEmojis import im.vector.riotx.R -import kotlinx.android.synthetic.main.fragment_generic_recycler_epoxy.* +import kotlinx.android.synthetic.main.fragment_generic_recycler.* class DebugSasEmojiActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - setContentView(R.layout.fragment_generic_recycler_epoxy) - + setContentView(R.layout.fragment_generic_recycler) val controller = SasEmojiController() - epoxyRecyclerView.setController(controller) + recyclerView.adapter = controller.adapter + recyclerView.layoutManager = LinearLayoutManager(this) controller.setData(SasState(getAllVerificationEmojis())) } + + override fun onDestroy() { + recyclerView.adapter = null + super.onDestroy() + } } diff --git a/vector/src/main/java/im/vector/riotx/core/platform/VectorBaseFragment.kt b/vector/src/main/java/im/vector/riotx/core/platform/VectorBaseFragment.kt index 9f94c15edd..924cb6c7bc 100644 --- a/vector/src/main/java/im/vector/riotx/core/platform/VectorBaseFragment.kt +++ b/vector/src/main/java/im/vector/riotx/core/platform/VectorBaseFragment.kt @@ -135,6 +135,7 @@ abstract class VectorBaseFragment : BaseMvRxFragment(), HasScreenInjector { override fun onSaveInstanceState(outState: Bundle) { super.onSaveInstanceState(outState) restorables.forEach { it.onSaveInstanceState(outState) } + restorables.clear() } override fun onViewStateRestored(savedInstanceState: Bundle?) { diff --git a/vector/src/main/java/im/vector/riotx/features/crypto/keysbackup/settings/KeysBackupSettingsFragment.kt b/vector/src/main/java/im/vector/riotx/features/crypto/keysbackup/settings/KeysBackupSettingsFragment.kt index 9994ee5002..a00f71e5f8 100644 --- a/vector/src/main/java/im/vector/riotx/features/crypto/keysbackup/settings/KeysBackupSettingsFragment.kt +++ b/vector/src/main/java/im/vector/riotx/features/crypto/keysbackup/settings/KeysBackupSettingsFragment.kt @@ -18,6 +18,7 @@ package im.vector.riotx.features.crypto.keysbackup.settings import android.os.Bundle import android.view.View import androidx.appcompat.app.AlertDialog +import androidx.recyclerview.widget.LinearLayoutManager import com.airbnb.mvrx.activityViewModel import com.airbnb.mvrx.withState import im.vector.riotx.R @@ -37,12 +38,17 @@ class KeysBackupSettingsFragment @Inject constructor(private val keysBackupSetti override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - - keysBackupSettingsRecyclerView.setController(keysBackupSettingsRecyclerViewController) - + keysBackupSettingsRecyclerView.layoutManager = LinearLayoutManager(requireContext()) + keysBackupSettingsRecyclerView.adapter = keysBackupSettingsRecyclerViewController.adapter keysBackupSettingsRecyclerViewController.listener = this } + override fun onDestroyView() { + keysBackupSettingsRecyclerViewController.listener = null + keysBackupSettingsRecyclerView.adapter = null + super.onDestroyView() + } + override fun invalidate() = withState(viewModel) { state -> keysBackupSettingsRecyclerViewController.setData(state) } diff --git a/vector/src/main/java/im/vector/riotx/features/home/createdirect/CreateDirectRoomDirectoryUsersFragment.kt b/vector/src/main/java/im/vector/riotx/features/home/createdirect/CreateDirectRoomDirectoryUsersFragment.kt index 59f31ec2ee..575940a376 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/createdirect/CreateDirectRoomDirectoryUsersFragment.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/createdirect/CreateDirectRoomDirectoryUsersFragment.kt @@ -18,6 +18,8 @@ package im.vector.riotx.features.home.createdirect import android.os.Bundle import android.view.View +import androidx.recyclerview.widget.LinearLayoutManager +import androidx.recyclerview.widget.RecyclerView import com.airbnb.mvrx.activityViewModel import com.airbnb.mvrx.withState import com.jakewharton.rxbinding3.widget.textChanges @@ -48,10 +50,17 @@ class CreateDirectRoomDirectoryUsersFragment @Inject constructor( setupCloseView() } + override fun onDestroyView() { + recyclerView.adapter = null + directRoomController.callback = null + super.onDestroyView() + } + private fun setupRecyclerView() { recyclerView.setHasFixedSize(true) directRoomController.callback = this - recyclerView.setController(directRoomController) + recyclerView.layoutManager = LinearLayoutManager(context, RecyclerView.VERTICAL, false) + recyclerView.adapter = directRoomController.adapter } private fun setupSearchByMatrixIdView() { diff --git a/vector/src/main/java/im/vector/riotx/features/home/createdirect/CreateDirectRoomKnownUsersFragment.kt b/vector/src/main/java/im/vector/riotx/features/home/createdirect/CreateDirectRoomKnownUsersFragment.kt index 12019fa39e..bad322292b 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/createdirect/CreateDirectRoomKnownUsersFragment.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/createdirect/CreateDirectRoomKnownUsersFragment.kt @@ -24,6 +24,7 @@ import android.view.MenuItem import android.view.View import android.widget.ScrollView import androidx.core.view.size +import androidx.recyclerview.widget.LinearLayoutManager import com.airbnb.mvrx.activityViewModel import com.airbnb.mvrx.withState import com.google.android.material.chip.Chip @@ -67,6 +68,12 @@ class CreateDirectRoomKnownUsersFragment @Inject constructor( } } + override fun onDestroyView() { + knownUsersController.callback = null + recyclerView.adapter = null + super.onDestroyView() + } + override fun onPrepareOptionsMenu(menu: Menu) { withState(viewModel) { val createMenuItem = menu.findItem(R.id.action_create_direct_room) @@ -98,7 +105,8 @@ class CreateDirectRoomKnownUsersFragment @Inject constructor( // Don't activate animation as we might have way to much item animation when filtering recyclerView.itemAnimator = null knownUsersController.callback = this - recyclerView.setController(knownUsersController) + recyclerView.layoutManager = LinearLayoutManager(requireContext()) + recyclerView.adapter = knownUsersController.adapter } private fun setupFilterView() { diff --git a/vector/src/main/java/im/vector/riotx/features/home/group/GroupListFragment.kt b/vector/src/main/java/im/vector/riotx/features/home/group/GroupListFragment.kt index 39f8c17f05..5bc0c0f61d 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/group/GroupListFragment.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/group/GroupListFragment.kt @@ -18,6 +18,8 @@ package im.vector.riotx.features.home.group import android.os.Bundle import android.view.View +import androidx.recyclerview.widget.LinearLayoutManager +import androidx.recyclerview.widget.RecyclerView import com.airbnb.mvrx.Incomplete import com.airbnb.mvrx.Success import com.airbnb.mvrx.fragmentViewModel @@ -45,14 +47,21 @@ class GroupListFragment @Inject constructor( super.onViewCreated(view, savedInstanceState) sharedActionViewModel = activityViewModelProvider.get(HomeSharedActionViewModel::class.java) groupController.callback = this - stateView.contentView = groupListEpoxyRecyclerView - groupListEpoxyRecyclerView.setController(groupController) + stateView.contentView = groupListView + groupListView.layoutManager = LinearLayoutManager(requireContext(), RecyclerView.VERTICAL, false) + groupListView.adapter = groupController.adapter viewModel.subscribe { renderState(it) } viewModel.openGroupLiveData.observeEvent(this) { sharedActionViewModel.post(HomeActivitySharedAction.OpenGroup) } } + override fun onDestroyView() { + groupController.callback = null + groupListView.adapter = null + super.onDestroyView() + } + private fun renderState(state: GroupListViewState) { when (state.asyncGroups) { is Incomplete -> stateView.state = StateView.State.Loading diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailFragment.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailFragment.kt index 27edc6bc5d..0d869048fb 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailFragment.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailFragment.kt @@ -194,7 +194,7 @@ class RoomDetailFragment @Inject constructor( private lateinit var sharedActionViewModel: MessageSharedActionViewModel private lateinit var layoutManager: LinearLayoutManager - private lateinit var modelBuildListener: OnModelBuildFinishedListener + private var modelBuildListener: OnModelBuildFinishedListener? = null private lateinit var attachmentsHelper: AttachmentsHelper private lateinit var keyboardStateUtils: KeyboardStateUtils @@ -289,14 +289,16 @@ class RoomDetailFragment @Inject constructor( } override fun onDestroyView() { + timelineEventController.callback = null timelineEventController.removeModelBuildListener(modelBuildListener) + modelBuildListener = null + debouncer.cancelAll() recyclerView.adapter = null super.onDestroyView() } override fun onDestroy() { roomDetailViewModel.handle(RoomDetailAction.ExitTrackingUnreadMessagesState) - debouncer.cancelAll() super.onDestroy() } @@ -481,7 +483,7 @@ class RoomDetailFragment @Inject constructor( updateJumpToReadMarkerViewVisibility() updateJumpToBottomViewVisibility() } - timelineEventController.addModelBuildListener { modelBuildListener } + timelineEventController.addModelBuildListener(modelBuildListener) recyclerView.adapter = timelineEventController.adapter recyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener() { @@ -554,13 +556,11 @@ class RoomDetailFragment @Inject constructor( private fun updateJumpToBottomViewVisibility() { debouncer.debounce("jump_to_bottom_visibility", 250, Runnable { - if (isAdded) { - Timber.v("First visible: ${layoutManager.findFirstCompletelyVisibleItemPosition()}") - if (layoutManager.findFirstVisibleItemPosition() != 0) { - jumpToBottomView.show() - } else { - jumpToBottomView.hide() - } + Timber.v("First visible: ${layoutManager.findFirstCompletelyVisibleItemPosition()}") + if (layoutManager.findFirstVisibleItemPosition() != 0) { + jumpToBottomView.show() + } else { + jumpToBottomView.hide() } }) } diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailViewModel.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailViewModel.kt index 00a31db455..e7a18753cd 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailViewModel.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailViewModel.kt @@ -863,7 +863,7 @@ class RoomDetailViewModel @AssistedInject constructor(@Assisted initialState: Ro override fun onCleared() { timeline.dispose() - timeline.removeAllListeners() + timeline.removeListener(this) super.onCleared() } } diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/TimelineEventController.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/TimelineEventController.kt index 326e19c431..83f9163496 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/TimelineEventController.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/TimelineEventController.kt @@ -44,7 +44,7 @@ import org.threeten.bp.LocalDateTime import javax.inject.Inject class TimelineEventController @Inject constructor(private val dateFormatter: VectorDateFormatter, - private val session: Session, + private val contentUploadStateTrackerBinder: ContentUploadStateTrackerBinder, private val timelineItemFactory: TimelineItemFactory, private val timelineMediaSizeProvider: TimelineMediaSizeProvider, private val mergedHeaderItemFactory: MergedHeaderItemFactory, @@ -209,6 +209,13 @@ class TimelineEventController @Inject constructor(private val dateFormatter: Vec timelineMediaSizeProvider.recyclerView = recyclerView } + override fun onDetachedFromRecyclerView(recyclerView: RecyclerView) { + timelineMediaSizeProvider.recyclerView = null + contentUploadStateTrackerBinder.clear() + timeline?.removeListener(this) + super.onDetachedFromRecyclerView(recyclerView) + } + override fun buildModels() { val timestamp = System.currentTimeMillis() showingForwardLoader = LoadingItem_() diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/helper/ContentUploadStateTrackerBinder.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/helper/ContentUploadStateTrackerBinder.kt index 3fdce63668..bbc3474e1e 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/helper/ContentUploadStateTrackerBinder.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/helper/ContentUploadStateTrackerBinder.kt @@ -25,12 +25,14 @@ import im.vector.matrix.android.api.session.content.ContentUploadStateTracker import im.vector.matrix.android.api.session.room.send.SendState import im.vector.riotx.R import im.vector.riotx.core.di.ActiveSessionHolder +import im.vector.riotx.core.di.ScreenScope import im.vector.riotx.core.error.ErrorFormatter import im.vector.riotx.core.resources.ColorProvider import im.vector.riotx.core.utils.TextUtils import im.vector.riotx.features.ui.getMessageTextColor import javax.inject.Inject +@ScreenScope class ContentUploadStateTrackerBinder @Inject constructor(private val activeSessionHolder: ActiveSessionHolder, private val colorProvider: ColorProvider, private val errorFormatter: ErrorFormatter) { @@ -40,7 +42,7 @@ class ContentUploadStateTrackerBinder @Inject constructor(private val activeSess fun bind(eventId: String, isLocalFile: Boolean, progressLayout: ViewGroup) { - activeSessionHolder.getActiveSession().also { session -> + activeSessionHolder.getSafeActiveSession()?.also { session -> val uploadStateTracker = session.contentUploadProgressTracker() val updateListener = ContentMediaProgressUpdater(progressLayout, isLocalFile, colorProvider, errorFormatter) updateListeners[eventId] = updateListener @@ -49,13 +51,20 @@ class ContentUploadStateTrackerBinder @Inject constructor(private val activeSess } fun unbind(eventId: String) { - activeSessionHolder.getActiveSession().also { session -> + activeSessionHolder.getSafeActiveSession()?.also { session -> val uploadStateTracker = session.contentUploadProgressTracker() updateListeners[eventId]?.also { uploadStateTracker.untrack(eventId, it) } } } + + fun clear() { + activeSessionHolder.getSafeActiveSession()?.also { + it.contentUploadProgressTracker().clear() + } + } + } private class ContentMediaProgressUpdater(private val progressLayout: ViewGroup, diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/helper/TimelineMediaSizeProvider.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/helper/TimelineMediaSizeProvider.kt index 0d11cf8a31..fc3ae98f5c 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/helper/TimelineMediaSizeProvider.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/helper/TimelineMediaSizeProvider.kt @@ -19,11 +19,12 @@ package im.vector.riotx.features.home.room.detail.timeline.helper import androidx.recyclerview.widget.RecyclerView import im.vector.riotx.core.di.ScreenScope import javax.inject.Inject +import kotlin.math.roundToInt @ScreenScope class TimelineMediaSizeProvider @Inject constructor() { - lateinit var recyclerView: RecyclerView + var recyclerView: RecyclerView? = null private var cachedSize: Pair? = null fun getMaxSize(): Pair { @@ -31,17 +32,17 @@ class TimelineMediaSizeProvider @Inject constructor() { } private fun computeMaxSize(): Pair { - val width = recyclerView.width - val height = recyclerView.height + val width = recyclerView?.width ?: 0 + val height = recyclerView?.height ?: 0 val maxImageWidth: Int val maxImageHeight: Int // landscape / portrait if (width < height) { - maxImageWidth = Math.round(width * 0.7f) - maxImageHeight = Math.round(height * 0.5f) + maxImageWidth = (width * 0.7f).roundToInt() + maxImageHeight = (height * 0.5f).roundToInt() } else { - maxImageWidth = Math.round(width * 0.5f) - maxImageHeight = Math.round(height * 0.7f) + maxImageWidth = (width * 0.5f).roundToInt() + maxImageHeight = (height * 0.7f).roundToInt() } return Pair(maxImageWidth, maxImageHeight) } diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/list/RoomListFragment.kt b/vector/src/main/java/im/vector/riotx/features/home/room/list/RoomListFragment.kt index 0fab14867c..334f8b3417 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/room/list/RoomListFragment.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/room/list/RoomListFragment.kt @@ -65,7 +65,7 @@ class RoomListFragment @Inject constructor( ) : VectorBaseFragment(), RoomSummaryController.Listener, OnBackPressed, FabMenuView.Listener { - private lateinit var modelBuildListener: OnModelBuildFinishedListener + private var modelBuildListener: OnModelBuildFinishedListener? = null private lateinit var sharedActionViewModel: RoomListQuickActionsSharedActionViewModel private val roomListParams: RoomListParams by args() private val roomListViewModel: RoomListViewModel by fragmentViewModel() @@ -120,7 +120,10 @@ class RoomListFragment @Inject constructor( override fun onDestroyView() { roomController.removeModelBuildListener(modelBuildListener) + modelBuildListener = null roomListView.adapter = null + roomController.listener = null + createChatFabMenu.listener = null super.onDestroyView() } diff --git a/vector/src/main/java/im/vector/riotx/features/login/terms/LoginTermsFragment.kt b/vector/src/main/java/im/vector/riotx/features/login/terms/LoginTermsFragment.kt index 08110f3b33..b50e7cfdcd 100755 --- a/vector/src/main/java/im/vector/riotx/features/login/terms/LoginTermsFragment.kt +++ b/vector/src/main/java/im/vector/riotx/features/login/terms/LoginTermsFragment.kt @@ -20,6 +20,7 @@ import android.os.Bundle import android.os.Parcelable import android.view.View import androidx.appcompat.app.AlertDialog +import androidx.recyclerview.widget.LinearLayoutManager import butterknife.OnClick import com.airbnb.mvrx.args import im.vector.riotx.R @@ -55,8 +56,9 @@ class LoginTermsFragment @Inject constructor( override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - - loginTermsPolicyList.setController(policyController) + loginTermsPolicyList.setHasFixedSize(true) + loginTermsPolicyList.layoutManager = LinearLayoutManager(requireContext()) + loginTermsPolicyList.adapter = policyController.adapter policyController.listener = this val list = ArrayList() @@ -69,6 +71,12 @@ class LoginTermsFragment @Inject constructor( loginTermsViewState = LoginTermsViewState(list) } + override fun onDestroyView() { + loginTermsPolicyList.adapter = null + policyController.listener = null + super.onDestroyView() + } + private fun renderState() { policyController.setData(loginTermsViewState.localizedFlowDataLoginTermsChecked) diff --git a/vector/src/main/java/im/vector/riotx/features/reactions/EmojiSearchResultFragment.kt b/vector/src/main/java/im/vector/riotx/features/reactions/EmojiSearchResultFragment.kt index e5b46c2176..bef8a65542 100644 --- a/vector/src/main/java/im/vector/riotx/features/reactions/EmojiSearchResultFragment.kt +++ b/vector/src/main/java/im/vector/riotx/features/reactions/EmojiSearchResultFragment.kt @@ -25,14 +25,14 @@ import com.airbnb.mvrx.withState import im.vector.riotx.R import im.vector.riotx.core.platform.VectorBaseFragment import im.vector.riotx.core.utils.LiveEvent -import kotlinx.android.synthetic.main.fragment_generic_recycler_epoxy.* +import kotlinx.android.synthetic.main.fragment_generic_recycler.* import javax.inject.Inject class EmojiSearchResultFragment @Inject constructor( private val epoxyController: EmojiSearchResultController ) : VectorBaseFragment() { - override fun getLayoutResId(): Int = R.layout.fragment_generic_recycler_epoxy + override fun getLayoutResId(): Int = R.layout.fragment_generic_recycler val viewModel: EmojiSearchResultViewModel by activityViewModel() @@ -50,10 +50,16 @@ class EmojiSearchResultFragment @Inject constructor( } val lmgr = LinearLayoutManager(context, RecyclerView.VERTICAL, false) - epoxyRecyclerView.layoutManager = lmgr - val dividerItemDecoration = DividerItemDecoration(epoxyRecyclerView.context, lmgr.orientation) - epoxyRecyclerView.addItemDecoration(dividerItemDecoration) - epoxyRecyclerView.setController(epoxyController) + recyclerView.layoutManager = lmgr + val dividerItemDecoration = DividerItemDecoration(recyclerView.context, lmgr.orientation) + recyclerView.addItemDecoration(dividerItemDecoration) + recyclerView.adapter = epoxyController.adapter + } + + override fun onDestroyView() { + epoxyController.listener = null + recyclerView.adapter = null + super.onDestroyView() } override fun invalidate() = withState(viewModel) { state -> diff --git a/vector/src/main/java/im/vector/riotx/features/roomdirectory/PublicRoomsFragment.kt b/vector/src/main/java/im/vector/riotx/features/roomdirectory/PublicRoomsFragment.kt index b41c563256..109364194d 100644 --- a/vector/src/main/java/im/vector/riotx/features/roomdirectory/PublicRoomsFragment.kt +++ b/vector/src/main/java/im/vector/riotx/features/roomdirectory/PublicRoomsFragment.kt @@ -62,6 +62,9 @@ class PublicRoomsFragment @Inject constructor( it.setDisplayHomeAsUpEnabled(true) } + sharedActionViewModel = activityViewModelProvider.get(RoomDirectorySharedActionViewModel::class.java) + setupRecyclerView() + publicRoomsFilter.queryTextChanges() .debounce(500, TimeUnit.MILLISECONDS) .subscribeBy { @@ -79,6 +82,12 @@ class PublicRoomsFragment @Inject constructor( } } + override fun onDestroyView() { + publicRoomsController.callback = null + publicRoomsList.adapter = null + super.onDestroyView() + } + override fun onOptionsItemSelected(item: MenuItem): Boolean { return when (item.itemId) { R.id.menu_room_directory_change_protocol -> { @@ -90,22 +99,12 @@ class PublicRoomsFragment @Inject constructor( } } - override fun onActivityCreated(savedInstanceState: Bundle?) { - super.onActivityCreated(savedInstanceState) - sharedActionViewModel = activityViewModelProvider.get(RoomDirectorySharedActionViewModel::class.java) - setupRecyclerView() - } - private fun setupRecyclerView() { val epoxyVisibilityTracker = EpoxyVisibilityTracker() epoxyVisibilityTracker.attach(publicRoomsList) - - val layoutManager = LinearLayoutManager(context) - - publicRoomsList.layoutManager = layoutManager + publicRoomsList.layoutManager = LinearLayoutManager(context) publicRoomsController.callback = this - - publicRoomsList.setController(publicRoomsController) + publicRoomsList.adapter = publicRoomsController.adapter } override fun onPublicRoomClicked(publicRoom: PublicRoom, joinState: JoinState) { diff --git a/vector/src/main/java/im/vector/riotx/features/roomdirectory/createroom/CreateRoomFragment.kt b/vector/src/main/java/im/vector/riotx/features/roomdirectory/createroom/CreateRoomFragment.kt index 0dec14f50e..95d9c198ee 100644 --- a/vector/src/main/java/im/vector/riotx/features/roomdirectory/createroom/CreateRoomFragment.kt +++ b/vector/src/main/java/im/vector/riotx/features/roomdirectory/createroom/CreateRoomFragment.kt @@ -50,6 +50,12 @@ class CreateRoomFragment @Inject constructor(private val createRoomController: C } } + override fun onDestroyView() { + createRoomForm.adapter = null + createRoomController.listener = null + super.onDestroyView() + } + override fun onOptionsItemSelected(item: MenuItem): Boolean { return when (item.itemId) { R.id.action_create_room -> { @@ -63,11 +69,9 @@ class CreateRoomFragment @Inject constructor(private val createRoomController: C private fun setupRecyclerView() { val layoutManager = LinearLayoutManager(context) - createRoomForm.layoutManager = layoutManager createRoomController.listener = this - - createRoomForm.setController(createRoomController) + createRoomForm.adapter = createRoomController.adapter } override fun onNameChange(newName: String) { diff --git a/vector/src/main/java/im/vector/riotx/features/roomdirectory/picker/RoomDirectoryPickerFragment.kt b/vector/src/main/java/im/vector/riotx/features/roomdirectory/picker/RoomDirectoryPickerFragment.kt index ce7a57deba..cc56de7cd7 100644 --- a/vector/src/main/java/im/vector/riotx/features/roomdirectory/picker/RoomDirectoryPickerFragment.kt +++ b/vector/src/main/java/im/vector/riotx/features/roomdirectory/picker/RoomDirectoryPickerFragment.kt @@ -60,6 +60,12 @@ class RoomDirectoryPickerFragment @Inject constructor(val roomDirectoryPickerVie setupRecyclerView() } + override fun onDestroyView() { + roomDirectoryPickerList.adapter = null + roomDirectoryPickerController.callback = null + super.onDestroyView() + } + override fun getMenuRes() = R.menu.menu_directory_server_picker override fun onOptionsItemSelected(item: MenuItem): Boolean { @@ -74,11 +80,9 @@ class RoomDirectoryPickerFragment @Inject constructor(val roomDirectoryPickerVie private fun setupRecyclerView() { val layoutManager = LinearLayoutManager(context) - roomDirectoryPickerList.layoutManager = layoutManager roomDirectoryPickerController.callback = this - - roomDirectoryPickerList.setController(roomDirectoryPickerController) + roomDirectoryPickerList.adapter = roomDirectoryPickerController.adapter } override fun onRoomDirectoryClicked(roomDirectoryData: RoomDirectoryData) { diff --git a/vector/src/main/java/im/vector/riotx/features/settings/ignored/VectorSettingsIgnoredUsersFragment.kt b/vector/src/main/java/im/vector/riotx/features/settings/ignored/VectorSettingsIgnoredUsersFragment.kt index 11e473ae24..de267047b9 100644 --- a/vector/src/main/java/im/vector/riotx/features/settings/ignored/VectorSettingsIgnoredUsersFragment.kt +++ b/vector/src/main/java/im/vector/riotx/features/settings/ignored/VectorSettingsIgnoredUsersFragment.kt @@ -20,6 +20,7 @@ import android.os.Bundle import android.view.View import androidx.appcompat.app.AlertDialog import androidx.core.view.isVisible +import androidx.recyclerview.widget.LinearLayoutManager import com.airbnb.mvrx.Async import com.airbnb.mvrx.Loading import com.airbnb.mvrx.fragmentViewModel @@ -29,7 +30,7 @@ import im.vector.riotx.core.error.ErrorFormatter import im.vector.riotx.core.extensions.observeEvent import im.vector.riotx.core.platform.VectorBaseActivity import im.vector.riotx.core.platform.VectorBaseFragment -import kotlinx.android.synthetic.main.fragment_generic_recycler_epoxy.* +import kotlinx.android.synthetic.main.fragment_generic_recycler.* import kotlinx.android.synthetic.main.merge_overlay_waiting_view.* import javax.inject.Inject @@ -39,7 +40,7 @@ class VectorSettingsIgnoredUsersFragment @Inject constructor( private val errorFormatter: ErrorFormatter ) : VectorBaseFragment(), IgnoredUsersController.Callback { - override fun getLayoutResId() = R.layout.fragment_generic_recycler_epoxy + override fun getLayoutResId() = R.layout.fragment_generic_recycler private val ignoredUsersViewModel: IgnoredUsersViewModel by fragmentViewModel() @@ -49,12 +50,19 @@ class VectorSettingsIgnoredUsersFragment @Inject constructor( waiting_view_status_text.setText(R.string.please_wait) waiting_view_status_text.isVisible = true ignoredUsersController.callback = this - epoxyRecyclerView.setController(ignoredUsersController) + recyclerView.layoutManager = LinearLayoutManager(requireContext()) + recyclerView.adapter = ignoredUsersController.adapter ignoredUsersViewModel.requestErrorLiveData.observeEvent(this) { displayErrorDialog(it) } } + override fun onDestroyView() { + ignoredUsersController.callback = null + recyclerView.adapter = null + super.onDestroyView() + } + override fun onResume() { super.onResume() diff --git a/vector/src/main/java/im/vector/riotx/features/settings/push/PushGatewaysFragment.kt b/vector/src/main/java/im/vector/riotx/features/settings/push/PushGatewaysFragment.kt index ea23ba2583..e35ec6b5ff 100644 --- a/vector/src/main/java/im/vector/riotx/features/settings/push/PushGatewaysFragment.kt +++ b/vector/src/main/java/im/vector/riotx/features/settings/push/PushGatewaysFragment.kt @@ -17,6 +17,7 @@ package im.vector.riotx.features.settings.push import android.os.Bundle +import android.view.View import androidx.recyclerview.widget.DividerItemDecoration import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView @@ -28,7 +29,7 @@ import im.vector.riotx.core.platform.VectorBaseActivity import im.vector.riotx.core.platform.VectorBaseFragment import im.vector.riotx.core.resources.StringProvider import im.vector.riotx.core.ui.list.genericFooterItem -import kotlinx.android.synthetic.main.fragment_generic_recycler_epoxy.* +import kotlinx.android.synthetic.main.fragment_generic_recycler.* import javax.inject.Inject // Referenced in vector_settings_notifications.xml @@ -36,7 +37,7 @@ class PushGatewaysFragment @Inject constructor( val pushGatewaysViewModelFactory: PushGatewaysViewModel.Factory ) : VectorBaseFragment() { - override fun getLayoutResId(): Int = R.layout.fragment_generic_recycler_epoxy + override fun getLayoutResId(): Int = R.layout.fragment_generic_recycler private val viewModel: PushGatewaysViewModel by fragmentViewModel(PushGatewaysViewModel::class) private val epoxyController by lazy { PushGateWayController(StringProvider(requireContext().resources)) } @@ -46,14 +47,19 @@ class PushGatewaysFragment @Inject constructor( (activity as? VectorBaseActivity)?.supportActionBar?.setTitle(R.string.settings_notifications_targets) } - override fun onActivityCreated(savedInstanceState: Bundle?) { - super.onActivityCreated(savedInstanceState) + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) val lmgr = LinearLayoutManager(context, RecyclerView.VERTICAL, false) - epoxyRecyclerView.layoutManager = lmgr - val dividerItemDecoration = DividerItemDecoration(epoxyRecyclerView.context, + recyclerView.layoutManager = lmgr + val dividerItemDecoration = DividerItemDecoration(recyclerView.context, lmgr.orientation) - epoxyRecyclerView.addItemDecoration(dividerItemDecoration) - epoxyRecyclerView.setController(epoxyController) + recyclerView.addItemDecoration(dividerItemDecoration) + recyclerView.adapter = epoxyController.adapter + } + + override fun onDestroyView() { + recyclerView.adapter = null + super.onDestroyView() } override fun invalidate() = withState(viewModel) { state -> diff --git a/vector/src/main/java/im/vector/riotx/features/settings/push/PushRulesFragment.kt b/vector/src/main/java/im/vector/riotx/features/settings/push/PushRulesFragment.kt index 5e14053d0b..52a5928e5d 100644 --- a/vector/src/main/java/im/vector/riotx/features/settings/push/PushRulesFragment.kt +++ b/vector/src/main/java/im/vector/riotx/features/settings/push/PushRulesFragment.kt @@ -16,6 +16,7 @@ package im.vector.riotx.features.settings.push import android.os.Bundle +import android.view.View import androidx.recyclerview.widget.DividerItemDecoration import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView @@ -27,12 +28,12 @@ import im.vector.riotx.core.platform.VectorBaseActivity import im.vector.riotx.core.platform.VectorBaseFragment import im.vector.riotx.core.resources.StringProvider import im.vector.riotx.core.ui.list.genericFooterItem -import kotlinx.android.synthetic.main.fragment_generic_recycler_epoxy.* +import kotlinx.android.synthetic.main.fragment_generic_recycler.* // Referenced in vector_settings_notifications.xml class PushRulesFragment : VectorBaseFragment() { - override fun getLayoutResId(): Int = R.layout.fragment_generic_recycler_epoxy + override fun getLayoutResId(): Int = R.layout.fragment_generic_recycler private val viewModel: PushRulesViewModel by fragmentViewModel(PushRulesViewModel::class) @@ -43,14 +44,19 @@ class PushRulesFragment : VectorBaseFragment() { (activity as? VectorBaseActivity)?.supportActionBar?.setTitle(R.string.settings_push_rules) } - override fun onActivityCreated(savedInstanceState: Bundle?) { - super.onActivityCreated(savedInstanceState) + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) val lmgr = LinearLayoutManager(context, RecyclerView.VERTICAL, false) - epoxyRecyclerView.layoutManager = lmgr - val dividerItemDecoration = DividerItemDecoration(epoxyRecyclerView.context, + recyclerView.layoutManager = lmgr + val dividerItemDecoration = DividerItemDecoration(recyclerView.context, lmgr.orientation) - epoxyRecyclerView.addItemDecoration(dividerItemDecoration) - epoxyRecyclerView.setController(epoxyController) + recyclerView.addItemDecoration(dividerItemDecoration) + recyclerView.adapter = epoxyController.adapter + } + + override fun onDestroyView() { + recyclerView.adapter = null + super.onDestroyView() } override fun invalidate() = withState(viewModel) { state -> diff --git a/vector/src/main/res/layout/fragment_create_direct_room.xml b/vector/src/main/res/layout/fragment_create_direct_room.xml index f8450d1e6e..5ad2da1032 100644 --- a/vector/src/main/res/layout/fragment_create_direct_room.xml +++ b/vector/src/main/res/layout/fragment_create_direct_room.xml @@ -122,7 +122,7 @@ app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@id/createDirectRoomFilterDivider" /> - - - - - - - - - Date: Mon, 9 Dec 2019 17:40:52 +0100 Subject: [PATCH 05/22] Ensure we will not use EpoxyRecyclerView as a View anymore --- tools/check/forbidden_strings_in_resources.txt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tools/check/forbidden_strings_in_resources.txt b/tools/check/forbidden_strings_in_resources.txt index 435a81e3ba..17ae015265 100644 --- a/tools/check/forbidden_strings_in_resources.txt +++ b/tools/check/forbidden_strings_in_resources.txt @@ -81,4 +81,7 @@ layout_constraintLeft_ ### Will crash on API < 21. Use ?colorAccent instead \?android:colorAccent -\?android:attr/colorAccent \ No newline at end of file +\?android:attr/colorAccent + +### Use androidx.recyclerview.widget.RecyclerView because EpoxyRecyclerViews add behavior we do not want to + Date: Mon, 9 Dec 2019 17:43:14 +0100 Subject: [PATCH 06/22] Update CHANGES.md --- CHANGES.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGES.md b/CHANGES.md index 677bc23965..21cf052d45 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -12,12 +12,14 @@ Other changes: Bugfix 🐛: - When automardown is ON, pills are sent as MD in body (#739) + - "ban" event are not rendered correctly (#716) + - Fix crash when rotating screen in Room timeline Translations 🗣: - Build 🧱: - - "ban" event are not rendered correctly (#716) + - Changes in RiotX 0.9.1 (2019-12-05) =================================================== From 742136abe82ea7beb1ff6829633a6e494698aa1b Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 9 Dec 2019 18:01:58 +0100 Subject: [PATCH 07/22] Create RecyclerView extensions and cleanup all the recycler views --- .../debug/sas/DebugSasEmojiActivity.kt | 3 +- .../riotx/core/extensions/RecyclerView.kt | 32 +++++++++++++++++++ .../settings/KeysBackupSettingsFragment.kt | 3 +- .../CreateDirectRoomDirectoryUsersFragment.kt | 3 +- .../CreateDirectRoomKnownUsersFragment.kt | 3 +- .../features/home/group/GroupListFragment.kt | 3 +- .../room/breadcrumbs/BreadcrumbsFragment.kt | 3 +- .../home/room/detail/RoomDetailFragment.kt | 7 ++-- .../DisplayReadReceiptsBottomSheet.kt | 6 ++++ .../action/MessageActionsBottomSheet.kt | 6 ++++ .../edithistory/ViewEditHistoryBottomSheet.kt | 7 ++++ .../reactions/ViewReactionsBottomSheet.kt | 6 ++++ .../home/room/list/RoomListFragment.kt | 3 +- .../RoomListQuickActionsBottomSheet.kt | 6 ++++ .../login/terms/LoginTermsFragment.kt | 3 +- .../reactions/EmojiChooserFragment.kt | 6 ++++ .../reactions/EmojiChooserViewModel.kt | 1 + .../reactions/EmojiSearchResultFragment.kt | 3 +- .../roomdirectory/PublicRoomsFragment.kt | 3 +- .../createroom/CreateRoomFragment.kt | 3 +- .../picker/RoomDirectoryPickerFragment.kt | 3 +- ...ttingsNotificationsTroubleshootFragment.kt | 6 ++++ .../VectorSettingsIgnoredUsersFragment.kt | 3 +- .../settings/push/PushGatewaysFragment.kt | 3 +- .../settings/push/PushRulesFragment.kt | 3 +- 25 files changed, 108 insertions(+), 20 deletions(-) create mode 100644 vector/src/main/java/im/vector/riotx/core/extensions/RecyclerView.kt diff --git a/vector/src/debug/java/im/vector/riotx/features/debug/sas/DebugSasEmojiActivity.kt b/vector/src/debug/java/im/vector/riotx/features/debug/sas/DebugSasEmojiActivity.kt index 17a78f1c6a..89c31474b3 100644 --- a/vector/src/debug/java/im/vector/riotx/features/debug/sas/DebugSasEmojiActivity.kt +++ b/vector/src/debug/java/im/vector/riotx/features/debug/sas/DebugSasEmojiActivity.kt @@ -21,6 +21,7 @@ import androidx.appcompat.app.AppCompatActivity import androidx.recyclerview.widget.LinearLayoutManager import im.vector.matrix.android.api.crypto.getAllVerificationEmojis import im.vector.riotx.R +import im.vector.riotx.core.extensions.cleanup import kotlinx.android.synthetic.main.fragment_generic_recycler.* class DebugSasEmojiActivity : AppCompatActivity() { @@ -35,7 +36,7 @@ class DebugSasEmojiActivity : AppCompatActivity() { } override fun onDestroy() { - recyclerView.adapter = null + recyclerView.cleanup() super.onDestroy() } } diff --git a/vector/src/main/java/im/vector/riotx/core/extensions/RecyclerView.kt b/vector/src/main/java/im/vector/riotx/core/extensions/RecyclerView.kt new file mode 100644 index 0000000000..f844bf6c9a --- /dev/null +++ b/vector/src/main/java/im/vector/riotx/core/extensions/RecyclerView.kt @@ -0,0 +1,32 @@ +/* + * 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.riotx.core.extensions + +import android.content.Context +import androidx.recyclerview.widget.RecyclerView +import com.airbnb.epoxy.EpoxyController + +fun RecyclerView.configureWith(context: Context, controller: EpoxyController) { + +} + +/** + * To call from Fragment.onDestroyView() + */ +fun RecyclerView.cleanup() { + adapter = null +} diff --git a/vector/src/main/java/im/vector/riotx/features/crypto/keysbackup/settings/KeysBackupSettingsFragment.kt b/vector/src/main/java/im/vector/riotx/features/crypto/keysbackup/settings/KeysBackupSettingsFragment.kt index a00f71e5f8..7091bead4e 100644 --- a/vector/src/main/java/im/vector/riotx/features/crypto/keysbackup/settings/KeysBackupSettingsFragment.kt +++ b/vector/src/main/java/im/vector/riotx/features/crypto/keysbackup/settings/KeysBackupSettingsFragment.kt @@ -22,6 +22,7 @@ import androidx.recyclerview.widget.LinearLayoutManager import com.airbnb.mvrx.activityViewModel import com.airbnb.mvrx.withState import im.vector.riotx.R +import im.vector.riotx.core.extensions.cleanup import im.vector.riotx.core.platform.VectorBaseFragment import im.vector.riotx.features.crypto.keysbackup.restore.KeysBackupRestoreActivity import im.vector.riotx.features.crypto.keysbackup.setup.KeysBackupSetupActivity @@ -45,7 +46,7 @@ class KeysBackupSettingsFragment @Inject constructor(private val keysBackupSetti override fun onDestroyView() { keysBackupSettingsRecyclerViewController.listener = null - keysBackupSettingsRecyclerView.adapter = null + keysBackupSettingsRecyclerView.cleanup() super.onDestroyView() } diff --git a/vector/src/main/java/im/vector/riotx/features/home/createdirect/CreateDirectRoomDirectoryUsersFragment.kt b/vector/src/main/java/im/vector/riotx/features/home/createdirect/CreateDirectRoomDirectoryUsersFragment.kt index 575940a376..ac2230eae3 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/createdirect/CreateDirectRoomDirectoryUsersFragment.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/createdirect/CreateDirectRoomDirectoryUsersFragment.kt @@ -25,6 +25,7 @@ import com.airbnb.mvrx.withState import com.jakewharton.rxbinding3.widget.textChanges import im.vector.matrix.android.api.session.user.model.User import im.vector.riotx.R +import im.vector.riotx.core.extensions.cleanup import im.vector.riotx.core.extensions.hideKeyboard import im.vector.riotx.core.extensions.setupAsSearch import im.vector.riotx.core.extensions.showKeyboard @@ -51,7 +52,7 @@ class CreateDirectRoomDirectoryUsersFragment @Inject constructor( } override fun onDestroyView() { - recyclerView.adapter = null + recyclerView.cleanup() directRoomController.callback = null super.onDestroyView() } diff --git a/vector/src/main/java/im/vector/riotx/features/home/createdirect/CreateDirectRoomKnownUsersFragment.kt b/vector/src/main/java/im/vector/riotx/features/home/createdirect/CreateDirectRoomKnownUsersFragment.kt index bad322292b..c333b6e09e 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/createdirect/CreateDirectRoomKnownUsersFragment.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/createdirect/CreateDirectRoomKnownUsersFragment.kt @@ -32,6 +32,7 @@ import com.google.android.material.chip.ChipGroup import com.jakewharton.rxbinding3.widget.textChanges import im.vector.matrix.android.api.session.user.model.User import im.vector.riotx.R +import im.vector.riotx.core.extensions.cleanup import im.vector.riotx.core.extensions.hideKeyboard import im.vector.riotx.core.extensions.observeEvent import im.vector.riotx.core.extensions.setupAsSearch @@ -70,7 +71,7 @@ class CreateDirectRoomKnownUsersFragment @Inject constructor( override fun onDestroyView() { knownUsersController.callback = null - recyclerView.adapter = null + recyclerView.cleanup() super.onDestroyView() } diff --git a/vector/src/main/java/im/vector/riotx/features/home/group/GroupListFragment.kt b/vector/src/main/java/im/vector/riotx/features/home/group/GroupListFragment.kt index 5bc0c0f61d..312a66d1db 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/group/GroupListFragment.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/group/GroupListFragment.kt @@ -25,6 +25,7 @@ import com.airbnb.mvrx.Success import com.airbnb.mvrx.fragmentViewModel import im.vector.matrix.android.api.session.group.model.GroupSummary import im.vector.riotx.R +import im.vector.riotx.core.extensions.cleanup import im.vector.riotx.core.extensions.observeEvent import im.vector.riotx.core.platform.StateView import im.vector.riotx.core.platform.VectorBaseFragment @@ -58,7 +59,7 @@ class GroupListFragment @Inject constructor( override fun onDestroyView() { groupController.callback = null - groupListView.adapter = null + groupListView.cleanup() super.onDestroyView() } diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/breadcrumbs/BreadcrumbsFragment.kt b/vector/src/main/java/im/vector/riotx/features/home/room/breadcrumbs/BreadcrumbsFragment.kt index e57a2ddac4..713b69d454 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/room/breadcrumbs/BreadcrumbsFragment.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/room/breadcrumbs/BreadcrumbsFragment.kt @@ -21,6 +21,7 @@ import android.view.View import androidx.recyclerview.widget.LinearLayoutManager import com.airbnb.mvrx.fragmentViewModel import im.vector.riotx.R +import im.vector.riotx.core.extensions.cleanup import im.vector.riotx.core.platform.VectorBaseFragment import im.vector.riotx.features.home.room.detail.RoomDetailSharedAction import im.vector.riotx.features.home.room.detail.RoomDetailSharedActionViewModel @@ -46,8 +47,8 @@ class BreadcrumbsFragment @Inject constructor( } override fun onDestroyView() { + breadcrumbsRecyclerView.cleanup() super.onDestroyView() - breadcrumbsRecyclerView.adapter = null } private fun setupRecyclerView() { diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailFragment.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailFragment.kt index 0d869048fb..e1bb24eb8f 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailFragment.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailFragment.kt @@ -70,10 +70,7 @@ import im.vector.riotx.R import im.vector.riotx.core.dialogs.withColoredButton import im.vector.riotx.core.epoxy.LayoutManagerStateRestorer import im.vector.riotx.core.error.ErrorFormatter -import im.vector.riotx.core.extensions.hideKeyboard -import im.vector.riotx.core.extensions.observeEvent -import im.vector.riotx.core.extensions.setTextOrHide -import im.vector.riotx.core.extensions.showKeyboard +import im.vector.riotx.core.extensions.* import im.vector.riotx.core.files.addEntryToDownloadManager import im.vector.riotx.core.glide.GlideApp import im.vector.riotx.core.platform.VectorBaseFragment @@ -293,7 +290,7 @@ class RoomDetailFragment @Inject constructor( timelineEventController.removeModelBuildListener(modelBuildListener) modelBuildListener = null debouncer.cancelAll() - recyclerView.adapter = null + recyclerView.cleanup() super.onDestroyView() } diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/readreceipts/DisplayReadReceiptsBottomSheet.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/readreceipts/DisplayReadReceiptsBottomSheet.kt index f220570e69..b098ce2638 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/room/detail/readreceipts/DisplayReadReceiptsBottomSheet.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/readreceipts/DisplayReadReceiptsBottomSheet.kt @@ -29,6 +29,7 @@ import com.airbnb.mvrx.MvRx import com.airbnb.mvrx.args import im.vector.riotx.R import im.vector.riotx.core.di.ScreenComponent +import im.vector.riotx.core.extensions.cleanup import im.vector.riotx.core.platform.VectorBaseBottomSheetDialogFragment import im.vector.riotx.features.home.room.detail.timeline.item.ReadReceiptData import kotlinx.android.parcel.Parcelize @@ -70,6 +71,11 @@ class DisplayReadReceiptsBottomSheet : VectorBaseBottomSheetDialogFragment() { epoxyController.setData(displayReadReceiptArgs.readReceipts) } + override fun onDestroyView() { + recyclerView.cleanup() + super.onDestroyView() + } + // we are not using state for this one as it's static, so no need to override invalidate() companion object { diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/action/MessageActionsBottomSheet.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/action/MessageActionsBottomSheet.kt index a5bf6f8558..8046b996fc 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/action/MessageActionsBottomSheet.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/action/MessageActionsBottomSheet.kt @@ -27,6 +27,7 @@ import com.airbnb.mvrx.fragmentViewModel import com.airbnb.mvrx.withState import im.vector.riotx.R import im.vector.riotx.core.di.ScreenComponent +import im.vector.riotx.core.extensions.cleanup import im.vector.riotx.core.platform.VectorBaseBottomSheetDialogFragment import im.vector.riotx.features.home.room.detail.timeline.item.MessageInformationData import javax.inject.Inject @@ -68,6 +69,11 @@ class MessageActionsBottomSheet : VectorBaseBottomSheetDialogFragment(), Message messageActionsEpoxyController.listener = this } + override fun onDestroyView() { + recyclerView.cleanup() + super.onDestroyView() + } + override fun onUrlClicked(url: String): Boolean { sharedActionViewModel.post(EventSharedAction.OnUrlClicked(url)) // Always consume diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/edithistory/ViewEditHistoryBottomSheet.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/edithistory/ViewEditHistoryBottomSheet.kt index 709bcb53c7..e28f42c078 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/edithistory/ViewEditHistoryBottomSheet.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/edithistory/ViewEditHistoryBottomSheet.kt @@ -30,6 +30,7 @@ import com.airbnb.mvrx.fragmentViewModel import com.airbnb.mvrx.withState import im.vector.riotx.R import im.vector.riotx.core.di.ScreenComponent +import im.vector.riotx.core.extensions.cleanup import im.vector.riotx.core.platform.VectorBaseBottomSheetDialogFragment import im.vector.riotx.features.home.room.detail.timeline.action.TimelineEventFragmentArgs import im.vector.riotx.features.home.room.detail.timeline.item.MessageInformationData @@ -73,6 +74,12 @@ class ViewEditHistoryBottomSheet : VectorBaseBottomSheetDialogFragment() { bottomSheetTitle.text = context?.getString(R.string.message_edits) } + override fun onDestroyView() { + recyclerView.cleanup() + super.onDestroyView() + } + + override fun invalidate() = withState(viewModel) { epoxyController.setData(it) super.invalidate() diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/reactions/ViewReactionsBottomSheet.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/reactions/ViewReactionsBottomSheet.kt index d5df8f7b40..d570a56242 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/reactions/ViewReactionsBottomSheet.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/reactions/ViewReactionsBottomSheet.kt @@ -29,6 +29,7 @@ import com.airbnb.mvrx.fragmentViewModel import com.airbnb.mvrx.withState import im.vector.riotx.R import im.vector.riotx.core.di.ScreenComponent +import im.vector.riotx.core.extensions.cleanup import im.vector.riotx.core.platform.VectorBaseBottomSheetDialogFragment import im.vector.riotx.features.home.room.detail.timeline.action.TimelineEventFragmentArgs import im.vector.riotx.features.home.room.detail.timeline.item.MessageInformationData @@ -66,6 +67,11 @@ class ViewReactionsBottomSheet : VectorBaseBottomSheetDialogFragment() { bottomSheetTitle.text = context?.getString(R.string.reactions) } + override fun onDestroyView() { + recyclerView.cleanup() + super.onDestroyView() + } + override fun invalidate() = withState(viewModel) { epoxyController.setData(it) super.invalidate() diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/list/RoomListFragment.kt b/vector/src/main/java/im/vector/riotx/features/home/room/list/RoomListFragment.kt index 334f8b3417..00d964b28c 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/room/list/RoomListFragment.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/room/list/RoomListFragment.kt @@ -36,6 +36,7 @@ import im.vector.matrix.android.api.session.room.notification.RoomNotificationSt import im.vector.riotx.R import im.vector.riotx.core.epoxy.LayoutManagerStateRestorer import im.vector.riotx.core.error.ErrorFormatter +import im.vector.riotx.core.extensions.cleanup import im.vector.riotx.core.platform.OnBackPressed import im.vector.riotx.core.platform.StateView import im.vector.riotx.core.platform.VectorBaseFragment @@ -121,7 +122,7 @@ class RoomListFragment @Inject constructor( override fun onDestroyView() { roomController.removeModelBuildListener(modelBuildListener) modelBuildListener = null - roomListView.adapter = null + roomListView.cleanup() roomController.listener = null createChatFabMenu.listener = null super.onDestroyView() diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/list/actions/RoomListQuickActionsBottomSheet.kt b/vector/src/main/java/im/vector/riotx/features/home/room/list/actions/RoomListQuickActionsBottomSheet.kt index 3a85cf26fa..8dab44b7a8 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/room/list/actions/RoomListQuickActionsBottomSheet.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/room/list/actions/RoomListQuickActionsBottomSheet.kt @@ -29,6 +29,7 @@ import com.airbnb.mvrx.fragmentViewModel import com.airbnb.mvrx.withState import im.vector.riotx.R import im.vector.riotx.core.di.ScreenComponent +import im.vector.riotx.core.extensions.cleanup import im.vector.riotx.core.platform.VectorBaseBottomSheetDialogFragment import im.vector.riotx.features.navigation.Navigator import kotlinx.android.parcel.Parcelize @@ -76,6 +77,11 @@ class RoomListQuickActionsBottomSheet : VectorBaseBottomSheetDialogFragment(), R roomListActionsEpoxyController.listener = this } + override fun onDestroyView() { + recyclerView.cleanup() + super.onDestroyView() + } + override fun invalidate() = withState(viewModel) { roomListActionsEpoxyController.setData(it) super.invalidate() diff --git a/vector/src/main/java/im/vector/riotx/features/login/terms/LoginTermsFragment.kt b/vector/src/main/java/im/vector/riotx/features/login/terms/LoginTermsFragment.kt index b50e7cfdcd..1e0d80de24 100755 --- a/vector/src/main/java/im/vector/riotx/features/login/terms/LoginTermsFragment.kt +++ b/vector/src/main/java/im/vector/riotx/features/login/terms/LoginTermsFragment.kt @@ -25,6 +25,7 @@ import butterknife.OnClick import com.airbnb.mvrx.args import im.vector.riotx.R import im.vector.riotx.core.error.ErrorFormatter +import im.vector.riotx.core.extensions.cleanup import im.vector.riotx.core.utils.openUrlInExternalBrowser import im.vector.riotx.features.login.AbstractLoginFragment import im.vector.riotx.features.login.LoginAction @@ -72,7 +73,7 @@ class LoginTermsFragment @Inject constructor( } override fun onDestroyView() { - loginTermsPolicyList.adapter = null + loginTermsPolicyList.cleanup() policyController.listener = null super.onDestroyView() } diff --git a/vector/src/main/java/im/vector/riotx/features/reactions/EmojiChooserFragment.kt b/vector/src/main/java/im/vector/riotx/features/reactions/EmojiChooserFragment.kt index 5e705a70c2..c49bdcbf92 100644 --- a/vector/src/main/java/im/vector/riotx/features/reactions/EmojiChooserFragment.kt +++ b/vector/src/main/java/im/vector/riotx/features/reactions/EmojiChooserFragment.kt @@ -19,6 +19,7 @@ import android.os.Bundle import android.view.View import androidx.recyclerview.widget.RecyclerView import im.vector.riotx.R +import im.vector.riotx.core.extensions.cleanup import im.vector.riotx.core.platform.VectorBaseFragment import javax.inject.Inject @@ -37,4 +38,9 @@ class EmojiChooserFragment @Inject constructor() : VectorBaseFragment() { it.adapter?.notifyDataSetChanged() } } + + override fun onDestroyView() { + (view as? RecyclerView)?.cleanup() + super.onDestroyView() + } } diff --git a/vector/src/main/java/im/vector/riotx/features/reactions/EmojiChooserViewModel.kt b/vector/src/main/java/im/vector/riotx/features/reactions/EmojiChooserViewModel.kt index bbde2ac54c..1979b02bfd 100644 --- a/vector/src/main/java/im/vector/riotx/features/reactions/EmojiChooserViewModel.kt +++ b/vector/src/main/java/im/vector/riotx/features/reactions/EmojiChooserViewModel.kt @@ -23,6 +23,7 @@ import javax.inject.Inject class EmojiChooserViewModel @Inject constructor() : ViewModel() { + // TODO Move the adapter out of the ViewModel var adapter: EmojiRecyclerAdapter? = null val emojiSourceLiveData: MutableLiveData = MutableLiveData() diff --git a/vector/src/main/java/im/vector/riotx/features/reactions/EmojiSearchResultFragment.kt b/vector/src/main/java/im/vector/riotx/features/reactions/EmojiSearchResultFragment.kt index bef8a65542..cf5b6aa211 100644 --- a/vector/src/main/java/im/vector/riotx/features/reactions/EmojiSearchResultFragment.kt +++ b/vector/src/main/java/im/vector/riotx/features/reactions/EmojiSearchResultFragment.kt @@ -23,6 +23,7 @@ import androidx.recyclerview.widget.RecyclerView import com.airbnb.mvrx.activityViewModel import com.airbnb.mvrx.withState import im.vector.riotx.R +import im.vector.riotx.core.extensions.cleanup import im.vector.riotx.core.platform.VectorBaseFragment import im.vector.riotx.core.utils.LiveEvent import kotlinx.android.synthetic.main.fragment_generic_recycler.* @@ -58,7 +59,7 @@ class EmojiSearchResultFragment @Inject constructor( override fun onDestroyView() { epoxyController.listener = null - recyclerView.adapter = null + recyclerView.cleanup() super.onDestroyView() } diff --git a/vector/src/main/java/im/vector/riotx/features/roomdirectory/PublicRoomsFragment.kt b/vector/src/main/java/im/vector/riotx/features/roomdirectory/PublicRoomsFragment.kt index 109364194d..f8d99d4b1c 100644 --- a/vector/src/main/java/im/vector/riotx/features/roomdirectory/PublicRoomsFragment.kt +++ b/vector/src/main/java/im/vector/riotx/features/roomdirectory/PublicRoomsFragment.kt @@ -28,6 +28,7 @@ import com.jakewharton.rxbinding3.appcompat.queryTextChanges import im.vector.matrix.android.api.session.room.model.roomdirectory.PublicRoom import im.vector.riotx.R import im.vector.riotx.core.error.ErrorFormatter +import im.vector.riotx.core.extensions.cleanup import im.vector.riotx.core.extensions.observeEvent import im.vector.riotx.core.platform.VectorBaseFragment import io.reactivex.rxkotlin.subscribeBy @@ -84,7 +85,7 @@ class PublicRoomsFragment @Inject constructor( override fun onDestroyView() { publicRoomsController.callback = null - publicRoomsList.adapter = null + publicRoomsList.cleanup() super.onDestroyView() } diff --git a/vector/src/main/java/im/vector/riotx/features/roomdirectory/createroom/CreateRoomFragment.kt b/vector/src/main/java/im/vector/riotx/features/roomdirectory/createroom/CreateRoomFragment.kt index 95d9c198ee..507dd7c71d 100644 --- a/vector/src/main/java/im/vector/riotx/features/roomdirectory/createroom/CreateRoomFragment.kt +++ b/vector/src/main/java/im/vector/riotx/features/roomdirectory/createroom/CreateRoomFragment.kt @@ -24,6 +24,7 @@ import com.airbnb.mvrx.Success import com.airbnb.mvrx.activityViewModel import com.airbnb.mvrx.withState import im.vector.riotx.R +import im.vector.riotx.core.extensions.cleanup import im.vector.riotx.core.platform.VectorBaseFragment import im.vector.riotx.features.roomdirectory.RoomDirectorySharedAction import im.vector.riotx.features.roomdirectory.RoomDirectorySharedActionViewModel @@ -51,7 +52,7 @@ class CreateRoomFragment @Inject constructor(private val createRoomController: C } override fun onDestroyView() { - createRoomForm.adapter = null + createRoomForm.cleanup() createRoomController.listener = null super.onDestroyView() } diff --git a/vector/src/main/java/im/vector/riotx/features/roomdirectory/picker/RoomDirectoryPickerFragment.kt b/vector/src/main/java/im/vector/riotx/features/roomdirectory/picker/RoomDirectoryPickerFragment.kt index cc56de7cd7..f1ca8a25cd 100644 --- a/vector/src/main/java/im/vector/riotx/features/roomdirectory/picker/RoomDirectoryPickerFragment.kt +++ b/vector/src/main/java/im/vector/riotx/features/roomdirectory/picker/RoomDirectoryPickerFragment.kt @@ -25,6 +25,7 @@ import com.airbnb.mvrx.fragmentViewModel import com.airbnb.mvrx.withState import im.vector.matrix.android.api.session.room.model.thirdparty.RoomDirectoryData import im.vector.riotx.R +import im.vector.riotx.core.extensions.cleanup import im.vector.riotx.core.platform.VectorBaseFragment import im.vector.riotx.features.roomdirectory.RoomDirectoryAction import im.vector.riotx.features.roomdirectory.RoomDirectorySharedAction @@ -61,7 +62,7 @@ class RoomDirectoryPickerFragment @Inject constructor(val roomDirectoryPickerVie } override fun onDestroyView() { - roomDirectoryPickerList.adapter = null + roomDirectoryPickerList.cleanup() roomDirectoryPickerController.callback = null super.onDestroyView() } diff --git a/vector/src/main/java/im/vector/riotx/features/settings/VectorSettingsNotificationsTroubleshootFragment.kt b/vector/src/main/java/im/vector/riotx/features/settings/VectorSettingsNotificationsTroubleshootFragment.kt index 6f43114eb4..3f69b5880e 100644 --- a/vector/src/main/java/im/vector/riotx/features/settings/VectorSettingsNotificationsTroubleshootFragment.kt +++ b/vector/src/main/java/im/vector/riotx/features/settings/VectorSettingsNotificationsTroubleshootFragment.kt @@ -29,6 +29,7 @@ import androidx.recyclerview.widget.RecyclerView import androidx.transition.TransitionManager import butterknife.BindView import im.vector.riotx.R +import im.vector.riotx.core.extensions.cleanup import im.vector.riotx.core.platform.VectorBaseActivity import im.vector.riotx.core.platform.VectorBaseFragment import im.vector.riotx.features.rageshake.BugReporter @@ -136,6 +137,11 @@ class VectorSettingsNotificationsTroubleshootFragment @Inject constructor( testManager?.runDiagnostic() } + override fun onDestroyView() { + mRecyclerView.cleanup() + super.onDestroyView() + } + override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { if (resultCode == Activity.RESULT_OK && requestCode == NotificationTroubleshootTestManager.REQ_CODE_FIX) { testManager?.retry() diff --git a/vector/src/main/java/im/vector/riotx/features/settings/ignored/VectorSettingsIgnoredUsersFragment.kt b/vector/src/main/java/im/vector/riotx/features/settings/ignored/VectorSettingsIgnoredUsersFragment.kt index de267047b9..3c48336620 100644 --- a/vector/src/main/java/im/vector/riotx/features/settings/ignored/VectorSettingsIgnoredUsersFragment.kt +++ b/vector/src/main/java/im/vector/riotx/features/settings/ignored/VectorSettingsIgnoredUsersFragment.kt @@ -27,6 +27,7 @@ import com.airbnb.mvrx.fragmentViewModel import com.airbnb.mvrx.withState import im.vector.riotx.R import im.vector.riotx.core.error.ErrorFormatter +import im.vector.riotx.core.extensions.cleanup import im.vector.riotx.core.extensions.observeEvent import im.vector.riotx.core.platform.VectorBaseActivity import im.vector.riotx.core.platform.VectorBaseFragment @@ -59,7 +60,7 @@ class VectorSettingsIgnoredUsersFragment @Inject constructor( override fun onDestroyView() { ignoredUsersController.callback = null - recyclerView.adapter = null + recyclerView.cleanup() super.onDestroyView() } diff --git a/vector/src/main/java/im/vector/riotx/features/settings/push/PushGatewaysFragment.kt b/vector/src/main/java/im/vector/riotx/features/settings/push/PushGatewaysFragment.kt index e35ec6b5ff..e76b7cbc85 100644 --- a/vector/src/main/java/im/vector/riotx/features/settings/push/PushGatewaysFragment.kt +++ b/vector/src/main/java/im/vector/riotx/features/settings/push/PushGatewaysFragment.kt @@ -25,6 +25,7 @@ import com.airbnb.epoxy.TypedEpoxyController import com.airbnb.mvrx.fragmentViewModel import com.airbnb.mvrx.withState import im.vector.riotx.R +import im.vector.riotx.core.extensions.cleanup import im.vector.riotx.core.platform.VectorBaseActivity import im.vector.riotx.core.platform.VectorBaseFragment import im.vector.riotx.core.resources.StringProvider @@ -58,7 +59,7 @@ class PushGatewaysFragment @Inject constructor( } override fun onDestroyView() { - recyclerView.adapter = null + recyclerView.cleanup() super.onDestroyView() } diff --git a/vector/src/main/java/im/vector/riotx/features/settings/push/PushRulesFragment.kt b/vector/src/main/java/im/vector/riotx/features/settings/push/PushRulesFragment.kt index 52a5928e5d..7d5dd74a80 100644 --- a/vector/src/main/java/im/vector/riotx/features/settings/push/PushRulesFragment.kt +++ b/vector/src/main/java/im/vector/riotx/features/settings/push/PushRulesFragment.kt @@ -24,6 +24,7 @@ import com.airbnb.epoxy.TypedEpoxyController import com.airbnb.mvrx.fragmentViewModel import com.airbnb.mvrx.withState import im.vector.riotx.R +import im.vector.riotx.core.extensions.cleanup import im.vector.riotx.core.platform.VectorBaseActivity import im.vector.riotx.core.platform.VectorBaseFragment import im.vector.riotx.core.resources.StringProvider @@ -55,7 +56,7 @@ class PushRulesFragment : VectorBaseFragment() { } override fun onDestroyView() { - recyclerView.adapter = null + recyclerView.cleanup() super.onDestroyView() } From f0aa34774e0b4fa35e0949472bfd97edd0926ab2 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 9 Dec 2019 21:31:04 +0100 Subject: [PATCH 08/22] Create RecyclerView extensions and cleanup all the recycler views --- .../features/debug/sas/DebugSasEmojiActivity.kt | 6 +++--- .../vector/riotx/core/extensions/RecyclerView.kt | 16 +++++++++++++--- .../settings/KeysBackupSettingsFragment.kt | 5 ++--- .../CreateDirectRoomDirectoryUsersFragment.kt | 11 ++--------- .../CreateDirectRoomKnownUsersFragment.kt | 10 ++-------- .../features/home/group/GroupListFragment.kt | 8 +++----- .../home/room/breadcrumbs/BreadcrumbsFragment.kt | 7 ++----- .../DisplayReadReceiptsBottomSheet.kt | 5 ++--- .../detail/timeline/TimelineEventController.kt | 1 - .../timeline/action/MessageActionsBottomSheet.kt | 8 ++++---- .../edithistory/ViewEditHistoryBottomSheet.kt | 12 +++++------- .../helper/ContentUploadStateTrackerBinder.kt | 1 - .../reactions/ViewReactionsBottomSheet.kt | 5 ++--- .../actions/RoomListQuickActionsBottomSheet.kt | 5 ++--- .../features/login/terms/LoginTermsFragment.kt | 6 ++---- .../reactions/EmojiSearchResultFragment.kt | 9 ++------- .../roomdirectory/PublicRoomsFragment.kt | 5 ++--- .../createroom/CreateRoomFragment.kt | 6 ++---- .../picker/RoomDirectoryPickerFragment.kt | 6 ++---- .../VectorSettingsIgnoredUsersFragment.kt | 5 ++--- .../settings/push/PushGatewaysFragment.kt | 11 +++-------- .../features/settings/push/PushRulesFragment.kt | 10 ++-------- .../src/main/res/layout/fragment_breadcrumbs.xml | 2 +- 23 files changed, 60 insertions(+), 100 deletions(-) diff --git a/vector/src/debug/java/im/vector/riotx/features/debug/sas/DebugSasEmojiActivity.kt b/vector/src/debug/java/im/vector/riotx/features/debug/sas/DebugSasEmojiActivity.kt index 89c31474b3..acca04d09f 100644 --- a/vector/src/debug/java/im/vector/riotx/features/debug/sas/DebugSasEmojiActivity.kt +++ b/vector/src/debug/java/im/vector/riotx/features/debug/sas/DebugSasEmojiActivity.kt @@ -18,10 +18,10 @@ package im.vector.riotx.features.debug.sas import android.os.Bundle import androidx.appcompat.app.AppCompatActivity -import androidx.recyclerview.widget.LinearLayoutManager import im.vector.matrix.android.api.crypto.getAllVerificationEmojis import im.vector.riotx.R import im.vector.riotx.core.extensions.cleanup +import im.vector.riotx.core.extensions.configureWith import kotlinx.android.synthetic.main.fragment_generic_recycler.* class DebugSasEmojiActivity : AppCompatActivity() { @@ -29,9 +29,9 @@ class DebugSasEmojiActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.fragment_generic_recycler) + // TODO Inject val controller = SasEmojiController() - recyclerView.adapter = controller.adapter - recyclerView.layoutManager = LinearLayoutManager(this) + recyclerView.configureWith(controller) controller.setData(SasState(getAllVerificationEmojis())) } diff --git a/vector/src/main/java/im/vector/riotx/core/extensions/RecyclerView.kt b/vector/src/main/java/im/vector/riotx/core/extensions/RecyclerView.kt index f844bf6c9a..b34edc0e89 100644 --- a/vector/src/main/java/im/vector/riotx/core/extensions/RecyclerView.kt +++ b/vector/src/main/java/im/vector/riotx/core/extensions/RecyclerView.kt @@ -16,12 +16,22 @@ package im.vector.riotx.core.extensions -import android.content.Context +import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView import com.airbnb.epoxy.EpoxyController -fun RecyclerView.configureWith(context: Context, controller: EpoxyController) { - +/** + * Apply a Vertical LinearLayout Manager to the recyclerView and set the adapter from the epoxy controller + */ +fun RecyclerView.configureWith(epoxyController: EpoxyController, + itemAnimator: RecyclerView.ItemAnimator? = null, + itemDecoration: RecyclerView.ItemDecoration? = null, + hasFixedSize: Boolean = true) { + layoutManager = LinearLayoutManager(context, LinearLayoutManager.VERTICAL, false) + itemAnimator?.let { this.itemAnimator = it } + itemDecoration?.let { addItemDecoration(it) } + setHasFixedSize(hasFixedSize) + adapter = epoxyController.adapter } /** diff --git a/vector/src/main/java/im/vector/riotx/features/crypto/keysbackup/settings/KeysBackupSettingsFragment.kt b/vector/src/main/java/im/vector/riotx/features/crypto/keysbackup/settings/KeysBackupSettingsFragment.kt index 7091bead4e..f0997de372 100644 --- a/vector/src/main/java/im/vector/riotx/features/crypto/keysbackup/settings/KeysBackupSettingsFragment.kt +++ b/vector/src/main/java/im/vector/riotx/features/crypto/keysbackup/settings/KeysBackupSettingsFragment.kt @@ -18,11 +18,11 @@ package im.vector.riotx.features.crypto.keysbackup.settings import android.os.Bundle import android.view.View import androidx.appcompat.app.AlertDialog -import androidx.recyclerview.widget.LinearLayoutManager import com.airbnb.mvrx.activityViewModel import com.airbnb.mvrx.withState import im.vector.riotx.R import im.vector.riotx.core.extensions.cleanup +import im.vector.riotx.core.extensions.configureWith import im.vector.riotx.core.platform.VectorBaseFragment import im.vector.riotx.features.crypto.keysbackup.restore.KeysBackupRestoreActivity import im.vector.riotx.features.crypto.keysbackup.setup.KeysBackupSetupActivity @@ -39,8 +39,7 @@ class KeysBackupSettingsFragment @Inject constructor(private val keysBackupSetti override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - keysBackupSettingsRecyclerView.layoutManager = LinearLayoutManager(requireContext()) - keysBackupSettingsRecyclerView.adapter = keysBackupSettingsRecyclerViewController.adapter + keysBackupSettingsRecyclerView.configureWith(keysBackupSettingsRecyclerViewController) keysBackupSettingsRecyclerViewController.listener = this } diff --git a/vector/src/main/java/im/vector/riotx/features/home/createdirect/CreateDirectRoomDirectoryUsersFragment.kt b/vector/src/main/java/im/vector/riotx/features/home/createdirect/CreateDirectRoomDirectoryUsersFragment.kt index ac2230eae3..4230ea030d 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/createdirect/CreateDirectRoomDirectoryUsersFragment.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/createdirect/CreateDirectRoomDirectoryUsersFragment.kt @@ -18,17 +18,12 @@ package im.vector.riotx.features.home.createdirect import android.os.Bundle import android.view.View -import androidx.recyclerview.widget.LinearLayoutManager -import androidx.recyclerview.widget.RecyclerView import com.airbnb.mvrx.activityViewModel import com.airbnb.mvrx.withState import com.jakewharton.rxbinding3.widget.textChanges import im.vector.matrix.android.api.session.user.model.User import im.vector.riotx.R -import im.vector.riotx.core.extensions.cleanup -import im.vector.riotx.core.extensions.hideKeyboard -import im.vector.riotx.core.extensions.setupAsSearch -import im.vector.riotx.core.extensions.showKeyboard +import im.vector.riotx.core.extensions.* import im.vector.riotx.core.platform.VectorBaseFragment import kotlinx.android.synthetic.main.fragment_create_direct_room_directory_users.* import javax.inject.Inject @@ -58,10 +53,8 @@ class CreateDirectRoomDirectoryUsersFragment @Inject constructor( } private fun setupRecyclerView() { - recyclerView.setHasFixedSize(true) directRoomController.callback = this - recyclerView.layoutManager = LinearLayoutManager(context, RecyclerView.VERTICAL, false) - recyclerView.adapter = directRoomController.adapter + recyclerView.configureWith(directRoomController) } private fun setupSearchByMatrixIdView() { diff --git a/vector/src/main/java/im/vector/riotx/features/home/createdirect/CreateDirectRoomKnownUsersFragment.kt b/vector/src/main/java/im/vector/riotx/features/home/createdirect/CreateDirectRoomKnownUsersFragment.kt index c333b6e09e..8108e9705c 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/createdirect/CreateDirectRoomKnownUsersFragment.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/createdirect/CreateDirectRoomKnownUsersFragment.kt @@ -24,7 +24,6 @@ import android.view.MenuItem import android.view.View import android.widget.ScrollView import androidx.core.view.size -import androidx.recyclerview.widget.LinearLayoutManager import com.airbnb.mvrx.activityViewModel import com.airbnb.mvrx.withState import com.google.android.material.chip.Chip @@ -32,10 +31,7 @@ import com.google.android.material.chip.ChipGroup import com.jakewharton.rxbinding3.widget.textChanges import im.vector.matrix.android.api.session.user.model.User import im.vector.riotx.R -import im.vector.riotx.core.extensions.cleanup -import im.vector.riotx.core.extensions.hideKeyboard -import im.vector.riotx.core.extensions.observeEvent -import im.vector.riotx.core.extensions.setupAsSearch +import im.vector.riotx.core.extensions.* import im.vector.riotx.core.platform.VectorBaseFragment import im.vector.riotx.core.utils.DimensionConverter import kotlinx.android.synthetic.main.fragment_create_direct_room.* @@ -102,12 +98,10 @@ class CreateDirectRoomKnownUsersFragment @Inject constructor( } private fun setupRecyclerView() { - recyclerView.setHasFixedSize(true) // Don't activate animation as we might have way to much item animation when filtering recyclerView.itemAnimator = null knownUsersController.callback = this - recyclerView.layoutManager = LinearLayoutManager(requireContext()) - recyclerView.adapter = knownUsersController.adapter + recyclerView.configureWith(knownUsersController) } private fun setupFilterView() { diff --git a/vector/src/main/java/im/vector/riotx/features/home/group/GroupListFragment.kt b/vector/src/main/java/im/vector/riotx/features/home/group/GroupListFragment.kt index 312a66d1db..254571f8cf 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/group/GroupListFragment.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/group/GroupListFragment.kt @@ -18,19 +18,18 @@ package im.vector.riotx.features.home.group import android.os.Bundle import android.view.View -import androidx.recyclerview.widget.LinearLayoutManager -import androidx.recyclerview.widget.RecyclerView import com.airbnb.mvrx.Incomplete import com.airbnb.mvrx.Success import com.airbnb.mvrx.fragmentViewModel import im.vector.matrix.android.api.session.group.model.GroupSummary import im.vector.riotx.R import im.vector.riotx.core.extensions.cleanup +import im.vector.riotx.core.extensions.configureWith import im.vector.riotx.core.extensions.observeEvent import im.vector.riotx.core.platform.StateView import im.vector.riotx.core.platform.VectorBaseFragment -import im.vector.riotx.features.home.HomeSharedActionViewModel import im.vector.riotx.features.home.HomeActivitySharedAction +import im.vector.riotx.features.home.HomeSharedActionViewModel import kotlinx.android.synthetic.main.fragment_group_list.* import javax.inject.Inject @@ -49,8 +48,7 @@ class GroupListFragment @Inject constructor( sharedActionViewModel = activityViewModelProvider.get(HomeSharedActionViewModel::class.java) groupController.callback = this stateView.contentView = groupListView - groupListView.layoutManager = LinearLayoutManager(requireContext(), RecyclerView.VERTICAL, false) - groupListView.adapter = groupController.adapter + groupListView.configureWith(groupController) viewModel.subscribe { renderState(it) } viewModel.openGroupLiveData.observeEvent(this) { sharedActionViewModel.post(HomeActivitySharedAction.OpenGroup) diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/breadcrumbs/BreadcrumbsFragment.kt b/vector/src/main/java/im/vector/riotx/features/home/room/breadcrumbs/BreadcrumbsFragment.kt index 713b69d454..b8e2cf7987 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/room/breadcrumbs/BreadcrumbsFragment.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/room/breadcrumbs/BreadcrumbsFragment.kt @@ -18,10 +18,10 @@ package im.vector.riotx.features.home.room.breadcrumbs import android.os.Bundle import android.view.View -import androidx.recyclerview.widget.LinearLayoutManager import com.airbnb.mvrx.fragmentViewModel import im.vector.riotx.R import im.vector.riotx.core.extensions.cleanup +import im.vector.riotx.core.extensions.configureWith import im.vector.riotx.core.platform.VectorBaseFragment import im.vector.riotx.features.home.room.detail.RoomDetailSharedAction import im.vector.riotx.features.home.room.detail.RoomDetailSharedActionViewModel @@ -52,11 +52,8 @@ class BreadcrumbsFragment @Inject constructor( } private fun setupRecyclerView() { - val layoutManager = LinearLayoutManager(context) - breadcrumbsRecyclerView.layoutManager = layoutManager - breadcrumbsRecyclerView.itemAnimator = BreadcrumbsAnimator() + breadcrumbsRecyclerView.configureWith(breadcrumbsController, BreadcrumbsAnimator(), hasFixedSize = false) breadcrumbsController.listener = this - breadcrumbsRecyclerView.setController(breadcrumbsController) } private fun renderState(state: BreadcrumbsViewState) { diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/readreceipts/DisplayReadReceiptsBottomSheet.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/readreceipts/DisplayReadReceiptsBottomSheet.kt index b098ce2638..f943556bcd 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/room/detail/readreceipts/DisplayReadReceiptsBottomSheet.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/readreceipts/DisplayReadReceiptsBottomSheet.kt @@ -21,7 +21,6 @@ import android.os.Parcelable import android.view.LayoutInflater import android.view.View import android.view.ViewGroup -import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView import butterknife.BindView import butterknife.ButterKnife @@ -30,6 +29,7 @@ import com.airbnb.mvrx.args import im.vector.riotx.R import im.vector.riotx.core.di.ScreenComponent import im.vector.riotx.core.extensions.cleanup +import im.vector.riotx.core.extensions.configureWith import im.vector.riotx.core.platform.VectorBaseBottomSheetDialogFragment import im.vector.riotx.features.home.room.detail.timeline.item.ReadReceiptData import kotlinx.android.parcel.Parcelize @@ -65,8 +65,7 @@ class DisplayReadReceiptsBottomSheet : VectorBaseBottomSheetDialogFragment() { override fun onActivityCreated(savedInstanceState: Bundle?) { super.onActivityCreated(savedInstanceState) - recyclerView.layoutManager = LinearLayoutManager(context, RecyclerView.VERTICAL, false) - recyclerView.adapter = epoxyController.adapter + recyclerView.configureWith(epoxyController, hasFixedSize = false) bottomSheetTitle.text = getString(R.string.seen_by) epoxyController.setData(displayReadReceiptArgs.readReceipts) } diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/TimelineEventController.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/TimelineEventController.kt index 83f9163496..576b9fa0ba 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/TimelineEventController.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/TimelineEventController.kt @@ -25,7 +25,6 @@ import androidx.recyclerview.widget.RecyclerView import com.airbnb.epoxy.EpoxyController import com.airbnb.epoxy.EpoxyModel import com.airbnb.epoxy.VisibilityState -import im.vector.matrix.android.api.session.Session import im.vector.matrix.android.api.session.room.model.message.* import im.vector.matrix.android.api.session.room.timeline.Timeline import im.vector.matrix.android.api.session.room.timeline.TimelineEvent diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/action/MessageActionsBottomSheet.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/action/MessageActionsBottomSheet.kt index 8046b996fc..23729734cb 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/action/MessageActionsBottomSheet.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/action/MessageActionsBottomSheet.kt @@ -19,7 +19,6 @@ import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup -import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView import butterknife.BindView import butterknife.ButterKnife @@ -28,8 +27,10 @@ import com.airbnb.mvrx.withState import im.vector.riotx.R import im.vector.riotx.core.di.ScreenComponent import im.vector.riotx.core.extensions.cleanup +import im.vector.riotx.core.extensions.configureWith import im.vector.riotx.core.platform.VectorBaseBottomSheetDialogFragment import im.vector.riotx.features.home.room.detail.timeline.item.MessageInformationData +import kotlinx.android.synthetic.main.activity_image_media_viewer.* import javax.inject.Inject /** @@ -62,8 +63,7 @@ class MessageActionsBottomSheet : VectorBaseBottomSheetDialogFragment(), Message override fun onActivityCreated(savedInstanceState: Bundle?) { super.onActivityCreated(savedInstanceState) sharedActionViewModel = activityViewModelProvider.get(MessageSharedActionViewModel::class.java) - recyclerView.layoutManager = LinearLayoutManager(requireContext(), RecyclerView.VERTICAL, false) - recyclerView.adapter = messageActionsEpoxyController.adapter + recyclerView.configureWith(messageActionsEpoxyController, hasFixedSize = false) // Disable item animation recyclerView.itemAnimator = null messageActionsEpoxyController.listener = this @@ -88,7 +88,7 @@ class MessageActionsBottomSheet : VectorBaseBottomSheetDialogFragment(), Message override fun didSelectMenuAction(eventAction: EventSharedAction) { if (eventAction is EventSharedAction.ReportContent) { - // Toggle report menu + // Toggle report menu // TODO Reanable item animation? viewModel.handle(MessageActionsAction.ToggleReportMenu) } else { sharedActionViewModel.post(eventAction) diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/edithistory/ViewEditHistoryBottomSheet.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/edithistory/ViewEditHistoryBottomSheet.kt index e28f42c078..e8ee6ce10e 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/edithistory/ViewEditHistoryBottomSheet.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/edithistory/ViewEditHistoryBottomSheet.kt @@ -19,9 +19,7 @@ import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup -import android.widget.LinearLayout import androidx.recyclerview.widget.DividerItemDecoration -import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView import butterknife.BindView import butterknife.ButterKnife @@ -31,6 +29,7 @@ import com.airbnb.mvrx.withState import im.vector.riotx.R import im.vector.riotx.core.di.ScreenComponent import im.vector.riotx.core.extensions.cleanup +import im.vector.riotx.core.extensions.configureWith import im.vector.riotx.core.platform.VectorBaseBottomSheetDialogFragment import im.vector.riotx.features.home.room.detail.timeline.action.TimelineEventFragmentArgs import im.vector.riotx.features.home.room.detail.timeline.item.MessageInformationData @@ -67,10 +66,10 @@ class ViewEditHistoryBottomSheet : VectorBaseBottomSheetDialogFragment() { override fun onActivityCreated(savedInstanceState: Bundle?) { super.onActivityCreated(savedInstanceState) - recyclerView.adapter = epoxyController.adapter - recyclerView.layoutManager = LinearLayoutManager(context, RecyclerView.VERTICAL, false) - val dividerItemDecoration = DividerItemDecoration(requireContext(), LinearLayout.VERTICAL) - recyclerView.addItemDecoration(dividerItemDecoration) + recyclerView.configureWith( + epoxyController, + itemDecoration = DividerItemDecoration(requireContext(), DividerItemDecoration.VERTICAL), + hasFixedSize = false) bottomSheetTitle.text = context?.getString(R.string.message_edits) } @@ -79,7 +78,6 @@ class ViewEditHistoryBottomSheet : VectorBaseBottomSheetDialogFragment() { super.onDestroyView() } - override fun invalidate() = withState(viewModel) { epoxyController.setData(it) super.invalidate() diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/helper/ContentUploadStateTrackerBinder.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/helper/ContentUploadStateTrackerBinder.kt index bbc3474e1e..d80c625e8f 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/helper/ContentUploadStateTrackerBinder.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/helper/ContentUploadStateTrackerBinder.kt @@ -64,7 +64,6 @@ class ContentUploadStateTrackerBinder @Inject constructor(private val activeSess it.contentUploadProgressTracker().clear() } } - } private class ContentMediaProgressUpdater(private val progressLayout: ViewGroup, diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/reactions/ViewReactionsBottomSheet.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/reactions/ViewReactionsBottomSheet.kt index d570a56242..9ece3e2bbd 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/reactions/ViewReactionsBottomSheet.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/reactions/ViewReactionsBottomSheet.kt @@ -20,7 +20,6 @@ import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup -import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView import butterknife.BindView import butterknife.ButterKnife @@ -30,6 +29,7 @@ import com.airbnb.mvrx.withState import im.vector.riotx.R import im.vector.riotx.core.di.ScreenComponent import im.vector.riotx.core.extensions.cleanup +import im.vector.riotx.core.extensions.configureWith import im.vector.riotx.core.platform.VectorBaseBottomSheetDialogFragment import im.vector.riotx.features.home.room.detail.timeline.action.TimelineEventFragmentArgs import im.vector.riotx.features.home.room.detail.timeline.item.MessageInformationData @@ -62,8 +62,7 @@ class ViewReactionsBottomSheet : VectorBaseBottomSheetDialogFragment() { override fun onActivityCreated(savedInstanceState: Bundle?) { super.onActivityCreated(savedInstanceState) - recyclerView.layoutManager = LinearLayoutManager(context, RecyclerView.VERTICAL, false) - recyclerView.adapter = epoxyController.adapter + recyclerView.configureWith(epoxyController, hasFixedSize = false) bottomSheetTitle.text = context?.getString(R.string.reactions) } diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/list/actions/RoomListQuickActionsBottomSheet.kt b/vector/src/main/java/im/vector/riotx/features/home/room/list/actions/RoomListQuickActionsBottomSheet.kt index 8dab44b7a8..28656e1d6e 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/room/list/actions/RoomListQuickActionsBottomSheet.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/room/list/actions/RoomListQuickActionsBottomSheet.kt @@ -21,7 +21,6 @@ import android.os.Parcelable import android.view.LayoutInflater import android.view.View import android.view.ViewGroup -import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView import butterknife.BindView import butterknife.ButterKnife @@ -30,6 +29,7 @@ import com.airbnb.mvrx.withState import im.vector.riotx.R import im.vector.riotx.core.di.ScreenComponent import im.vector.riotx.core.extensions.cleanup +import im.vector.riotx.core.extensions.configureWith import im.vector.riotx.core.platform.VectorBaseBottomSheetDialogFragment import im.vector.riotx.features.navigation.Navigator import kotlinx.android.parcel.Parcelize @@ -70,8 +70,7 @@ class RoomListQuickActionsBottomSheet : VectorBaseBottomSheetDialogFragment(), R override fun onActivityCreated(savedInstanceState: Bundle?) { super.onActivityCreated(savedInstanceState) sharedActionViewModel = activityViewModelProvider.get(RoomListQuickActionsSharedActionViewModel::class.java) - recyclerView.layoutManager = LinearLayoutManager(requireContext(), RecyclerView.VERTICAL, false) - recyclerView.adapter = roomListActionsEpoxyController.adapter + recyclerView.configureWith(roomListActionsEpoxyController, hasFixedSize = false) // Disable item animation recyclerView.itemAnimator = null roomListActionsEpoxyController.listener = this diff --git a/vector/src/main/java/im/vector/riotx/features/login/terms/LoginTermsFragment.kt b/vector/src/main/java/im/vector/riotx/features/login/terms/LoginTermsFragment.kt index 1e0d80de24..83d68f5f31 100755 --- a/vector/src/main/java/im/vector/riotx/features/login/terms/LoginTermsFragment.kt +++ b/vector/src/main/java/im/vector/riotx/features/login/terms/LoginTermsFragment.kt @@ -20,12 +20,12 @@ import android.os.Bundle import android.os.Parcelable import android.view.View import androidx.appcompat.app.AlertDialog -import androidx.recyclerview.widget.LinearLayoutManager import butterknife.OnClick import com.airbnb.mvrx.args import im.vector.riotx.R import im.vector.riotx.core.error.ErrorFormatter import im.vector.riotx.core.extensions.cleanup +import im.vector.riotx.core.extensions.configureWith import im.vector.riotx.core.utils.openUrlInExternalBrowser import im.vector.riotx.features.login.AbstractLoginFragment import im.vector.riotx.features.login.LoginAction @@ -57,9 +57,7 @@ class LoginTermsFragment @Inject constructor( override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - loginTermsPolicyList.setHasFixedSize(true) - loginTermsPolicyList.layoutManager = LinearLayoutManager(requireContext()) - loginTermsPolicyList.adapter = policyController.adapter + loginTermsPolicyList.configureWith(policyController) policyController.listener = this val list = ArrayList() diff --git a/vector/src/main/java/im/vector/riotx/features/reactions/EmojiSearchResultFragment.kt b/vector/src/main/java/im/vector/riotx/features/reactions/EmojiSearchResultFragment.kt index cf5b6aa211..976d5e984e 100644 --- a/vector/src/main/java/im/vector/riotx/features/reactions/EmojiSearchResultFragment.kt +++ b/vector/src/main/java/im/vector/riotx/features/reactions/EmojiSearchResultFragment.kt @@ -18,12 +18,11 @@ package im.vector.riotx.features.reactions import android.os.Bundle import android.view.View import androidx.recyclerview.widget.DividerItemDecoration -import androidx.recyclerview.widget.LinearLayoutManager -import androidx.recyclerview.widget.RecyclerView import com.airbnb.mvrx.activityViewModel import com.airbnb.mvrx.withState import im.vector.riotx.R import im.vector.riotx.core.extensions.cleanup +import im.vector.riotx.core.extensions.configureWith import im.vector.riotx.core.platform.VectorBaseFragment import im.vector.riotx.core.utils.LiveEvent import kotlinx.android.synthetic.main.fragment_generic_recycler.* @@ -50,11 +49,7 @@ class EmojiSearchResultFragment @Inject constructor( } } - val lmgr = LinearLayoutManager(context, RecyclerView.VERTICAL, false) - recyclerView.layoutManager = lmgr - val dividerItemDecoration = DividerItemDecoration(recyclerView.context, lmgr.orientation) - recyclerView.addItemDecoration(dividerItemDecoration) - recyclerView.adapter = epoxyController.adapter + recyclerView.configureWith(epoxyController, itemDecoration = DividerItemDecoration(requireContext(), DividerItemDecoration.VERTICAL)) } override fun onDestroyView() { diff --git a/vector/src/main/java/im/vector/riotx/features/roomdirectory/PublicRoomsFragment.kt b/vector/src/main/java/im/vector/riotx/features/roomdirectory/PublicRoomsFragment.kt index f8d99d4b1c..1d8ed48b08 100644 --- a/vector/src/main/java/im/vector/riotx/features/roomdirectory/PublicRoomsFragment.kt +++ b/vector/src/main/java/im/vector/riotx/features/roomdirectory/PublicRoomsFragment.kt @@ -19,7 +19,6 @@ package im.vector.riotx.features.roomdirectory import android.os.Bundle import android.view.MenuItem import android.view.View -import androidx.recyclerview.widget.LinearLayoutManager import com.airbnb.epoxy.EpoxyVisibilityTracker import com.airbnb.mvrx.activityViewModel import com.airbnb.mvrx.withState @@ -29,6 +28,7 @@ import im.vector.matrix.android.api.session.room.model.roomdirectory.PublicRoom import im.vector.riotx.R import im.vector.riotx.core.error.ErrorFormatter import im.vector.riotx.core.extensions.cleanup +import im.vector.riotx.core.extensions.configureWith import im.vector.riotx.core.extensions.observeEvent import im.vector.riotx.core.platform.VectorBaseFragment import io.reactivex.rxkotlin.subscribeBy @@ -103,9 +103,8 @@ class PublicRoomsFragment @Inject constructor( private fun setupRecyclerView() { val epoxyVisibilityTracker = EpoxyVisibilityTracker() epoxyVisibilityTracker.attach(publicRoomsList) - publicRoomsList.layoutManager = LinearLayoutManager(context) + publicRoomsList.configureWith(publicRoomsController) publicRoomsController.callback = this - publicRoomsList.adapter = publicRoomsController.adapter } override fun onPublicRoomClicked(publicRoom: PublicRoom, joinState: JoinState) { diff --git a/vector/src/main/java/im/vector/riotx/features/roomdirectory/createroom/CreateRoomFragment.kt b/vector/src/main/java/im/vector/riotx/features/roomdirectory/createroom/CreateRoomFragment.kt index 507dd7c71d..aacc21916a 100644 --- a/vector/src/main/java/im/vector/riotx/features/roomdirectory/createroom/CreateRoomFragment.kt +++ b/vector/src/main/java/im/vector/riotx/features/roomdirectory/createroom/CreateRoomFragment.kt @@ -19,12 +19,12 @@ package im.vector.riotx.features.roomdirectory.createroom import android.os.Bundle import android.view.MenuItem import android.view.View -import androidx.recyclerview.widget.LinearLayoutManager import com.airbnb.mvrx.Success import com.airbnb.mvrx.activityViewModel import com.airbnb.mvrx.withState import im.vector.riotx.R import im.vector.riotx.core.extensions.cleanup +import im.vector.riotx.core.extensions.configureWith import im.vector.riotx.core.platform.VectorBaseFragment import im.vector.riotx.features.roomdirectory.RoomDirectorySharedAction import im.vector.riotx.features.roomdirectory.RoomDirectorySharedActionViewModel @@ -69,10 +69,8 @@ class CreateRoomFragment @Inject constructor(private val createRoomController: C } private fun setupRecyclerView() { - val layoutManager = LinearLayoutManager(context) - createRoomForm.layoutManager = layoutManager + createRoomForm.configureWith(createRoomController) createRoomController.listener = this - createRoomForm.adapter = createRoomController.adapter } override fun onNameChange(newName: String) { diff --git a/vector/src/main/java/im/vector/riotx/features/roomdirectory/picker/RoomDirectoryPickerFragment.kt b/vector/src/main/java/im/vector/riotx/features/roomdirectory/picker/RoomDirectoryPickerFragment.kt index f1ca8a25cd..1299919d2b 100644 --- a/vector/src/main/java/im/vector/riotx/features/roomdirectory/picker/RoomDirectoryPickerFragment.kt +++ b/vector/src/main/java/im/vector/riotx/features/roomdirectory/picker/RoomDirectoryPickerFragment.kt @@ -19,13 +19,13 @@ package im.vector.riotx.features.roomdirectory.picker import android.os.Bundle import android.view.MenuItem import android.view.View -import androidx.recyclerview.widget.LinearLayoutManager import com.airbnb.mvrx.activityViewModel import com.airbnb.mvrx.fragmentViewModel import com.airbnb.mvrx.withState import im.vector.matrix.android.api.session.room.model.thirdparty.RoomDirectoryData import im.vector.riotx.R import im.vector.riotx.core.extensions.cleanup +import im.vector.riotx.core.extensions.configureWith import im.vector.riotx.core.platform.VectorBaseFragment import im.vector.riotx.features.roomdirectory.RoomDirectoryAction import im.vector.riotx.features.roomdirectory.RoomDirectorySharedAction @@ -80,10 +80,8 @@ class RoomDirectoryPickerFragment @Inject constructor(val roomDirectoryPickerVie } private fun setupRecyclerView() { - val layoutManager = LinearLayoutManager(context) - roomDirectoryPickerList.layoutManager = layoutManager + roomDirectoryPickerList.configureWith(roomDirectoryPickerController) roomDirectoryPickerController.callback = this - roomDirectoryPickerList.adapter = roomDirectoryPickerController.adapter } override fun onRoomDirectoryClicked(roomDirectoryData: RoomDirectoryData) { diff --git a/vector/src/main/java/im/vector/riotx/features/settings/ignored/VectorSettingsIgnoredUsersFragment.kt b/vector/src/main/java/im/vector/riotx/features/settings/ignored/VectorSettingsIgnoredUsersFragment.kt index 3c48336620..a6b8a5414f 100644 --- a/vector/src/main/java/im/vector/riotx/features/settings/ignored/VectorSettingsIgnoredUsersFragment.kt +++ b/vector/src/main/java/im/vector/riotx/features/settings/ignored/VectorSettingsIgnoredUsersFragment.kt @@ -20,7 +20,6 @@ import android.os.Bundle import android.view.View import androidx.appcompat.app.AlertDialog import androidx.core.view.isVisible -import androidx.recyclerview.widget.LinearLayoutManager import com.airbnb.mvrx.Async import com.airbnb.mvrx.Loading import com.airbnb.mvrx.fragmentViewModel @@ -28,6 +27,7 @@ import com.airbnb.mvrx.withState import im.vector.riotx.R import im.vector.riotx.core.error.ErrorFormatter import im.vector.riotx.core.extensions.cleanup +import im.vector.riotx.core.extensions.configureWith import im.vector.riotx.core.extensions.observeEvent import im.vector.riotx.core.platform.VectorBaseActivity import im.vector.riotx.core.platform.VectorBaseFragment @@ -51,8 +51,7 @@ class VectorSettingsIgnoredUsersFragment @Inject constructor( waiting_view_status_text.setText(R.string.please_wait) waiting_view_status_text.isVisible = true ignoredUsersController.callback = this - recyclerView.layoutManager = LinearLayoutManager(requireContext()) - recyclerView.adapter = ignoredUsersController.adapter + recyclerView.configureWith(ignoredUsersController) ignoredUsersViewModel.requestErrorLiveData.observeEvent(this) { displayErrorDialog(it) } diff --git a/vector/src/main/java/im/vector/riotx/features/settings/push/PushGatewaysFragment.kt b/vector/src/main/java/im/vector/riotx/features/settings/push/PushGatewaysFragment.kt index e76b7cbc85..816e76c4c4 100644 --- a/vector/src/main/java/im/vector/riotx/features/settings/push/PushGatewaysFragment.kt +++ b/vector/src/main/java/im/vector/riotx/features/settings/push/PushGatewaysFragment.kt @@ -19,13 +19,12 @@ package im.vector.riotx.features.settings.push import android.os.Bundle import android.view.View import androidx.recyclerview.widget.DividerItemDecoration -import androidx.recyclerview.widget.LinearLayoutManager -import androidx.recyclerview.widget.RecyclerView import com.airbnb.epoxy.TypedEpoxyController import com.airbnb.mvrx.fragmentViewModel import com.airbnb.mvrx.withState import im.vector.riotx.R import im.vector.riotx.core.extensions.cleanup +import im.vector.riotx.core.extensions.configureWith import im.vector.riotx.core.platform.VectorBaseActivity import im.vector.riotx.core.platform.VectorBaseFragment import im.vector.riotx.core.resources.StringProvider @@ -50,12 +49,7 @@ class PushGatewaysFragment @Inject constructor( override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - val lmgr = LinearLayoutManager(context, RecyclerView.VERTICAL, false) - recyclerView.layoutManager = lmgr - val dividerItemDecoration = DividerItemDecoration(recyclerView.context, - lmgr.orientation) - recyclerView.addItemDecoration(dividerItemDecoration) - recyclerView.adapter = epoxyController.adapter + recyclerView.configureWith(epoxyController, itemDecoration = DividerItemDecoration(requireContext(), DividerItemDecoration.VERTICAL)) } override fun onDestroyView() { @@ -67,6 +61,7 @@ class PushGatewaysFragment @Inject constructor( epoxyController.setData(state) } + // TODO Move to a proper file class PushGateWayController(private val stringProvider: StringProvider) : TypedEpoxyController() { override fun buildModels(data: PushGatewayViewState?) { data?.pushGateways?.invoke()?.let { pushers -> diff --git a/vector/src/main/java/im/vector/riotx/features/settings/push/PushRulesFragment.kt b/vector/src/main/java/im/vector/riotx/features/settings/push/PushRulesFragment.kt index 7d5dd74a80..62ac49e738 100644 --- a/vector/src/main/java/im/vector/riotx/features/settings/push/PushRulesFragment.kt +++ b/vector/src/main/java/im/vector/riotx/features/settings/push/PushRulesFragment.kt @@ -18,13 +18,12 @@ package im.vector.riotx.features.settings.push import android.os.Bundle import android.view.View import androidx.recyclerview.widget.DividerItemDecoration -import androidx.recyclerview.widget.LinearLayoutManager -import androidx.recyclerview.widget.RecyclerView import com.airbnb.epoxy.TypedEpoxyController import com.airbnb.mvrx.fragmentViewModel import com.airbnb.mvrx.withState import im.vector.riotx.R import im.vector.riotx.core.extensions.cleanup +import im.vector.riotx.core.extensions.configureWith import im.vector.riotx.core.platform.VectorBaseActivity import im.vector.riotx.core.platform.VectorBaseFragment import im.vector.riotx.core.resources.StringProvider @@ -47,12 +46,7 @@ class PushRulesFragment : VectorBaseFragment() { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - val lmgr = LinearLayoutManager(context, RecyclerView.VERTICAL, false) - recyclerView.layoutManager = lmgr - val dividerItemDecoration = DividerItemDecoration(recyclerView.context, - lmgr.orientation) - recyclerView.addItemDecoration(dividerItemDecoration) - recyclerView.adapter = epoxyController.adapter + recyclerView.configureWith(epoxyController, itemDecoration = DividerItemDecoration(requireContext(), DividerItemDecoration.VERTICAL)) } override fun onDestroyView() { diff --git a/vector/src/main/res/layout/fragment_breadcrumbs.xml b/vector/src/main/res/layout/fragment_breadcrumbs.xml index 22cceadc03..5cdd2e964a 100644 --- a/vector/src/main/res/layout/fragment_breadcrumbs.xml +++ b/vector/src/main/res/layout/fragment_breadcrumbs.xml @@ -1,5 +1,5 @@ - Date: Mon, 9 Dec 2019 21:33:10 +0100 Subject: [PATCH 09/22] Cleanup --- vector/src/main/java/im/vector/riotx/core/platform/StateView.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vector/src/main/java/im/vector/riotx/core/platform/StateView.kt b/vector/src/main/java/im/vector/riotx/core/platform/StateView.kt index 247d6f54ee..f674478724 100755 --- a/vector/src/main/java/im/vector/riotx/core/platform/StateView.kt +++ b/vector/src/main/java/im/vector/riotx/core/platform/StateView.kt @@ -51,7 +51,7 @@ class StateView @JvmOverloads constructor(context: Context, attrs: AttributeSet? init { View.inflate(context, R.layout.view_state, this) - layoutParams = LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT) + layoutParams = LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT) errorRetryView.setOnClickListener { eventCallback?.onRetryClicked() } From a761a0dbd2edb94e4198a70ead84d8d5fa46b7de Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 9 Dec 2019 21:56:03 +0100 Subject: [PATCH 10/22] Cleanup --- .../debug/sas/DebugSasEmojiActivity.kt | 1 - .../riotx/features/home/HomeDetailFragment.kt | 5 +- .../timeline/action/MessageActionsAnimator.kt | 34 +++++++++++++ .../action/MessageActionsBottomSheet.kt | 7 ++- .../room/filtered/FilteredRoomsActivity.kt | 4 +- .../reactions/EmojiReactionPickerActivity.kt | 6 +-- .../reactions/EmojiSearchResultFragment.kt | 2 +- .../settings/push/PushGateWayController.kt | 51 +++++++++++++++++++ .../settings/push/PushGatewaysFragment.kt | 35 ++----------- .../settings/push/PushRulesFragment.kt | 2 +- .../features/share/IncomingShareActivity.kt | 4 +- 11 files changed, 101 insertions(+), 50 deletions(-) create mode 100644 vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/action/MessageActionsAnimator.kt create mode 100644 vector/src/main/java/im/vector/riotx/features/settings/push/PushGateWayController.kt diff --git a/vector/src/debug/java/im/vector/riotx/features/debug/sas/DebugSasEmojiActivity.kt b/vector/src/debug/java/im/vector/riotx/features/debug/sas/DebugSasEmojiActivity.kt index acca04d09f..170c156858 100644 --- a/vector/src/debug/java/im/vector/riotx/features/debug/sas/DebugSasEmojiActivity.kt +++ b/vector/src/debug/java/im/vector/riotx/features/debug/sas/DebugSasEmojiActivity.kt @@ -29,7 +29,6 @@ class DebugSasEmojiActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.fragment_generic_recycler) - // TODO Inject val controller = SasEmojiController() recyclerView.configureWith(controller) controller.setData(SasState(getAllVerificationEmojis())) diff --git a/vector/src/main/java/im/vector/riotx/features/home/HomeDetailFragment.kt b/vector/src/main/java/im/vector/riotx/features/home/HomeDetailFragment.kt index fe98501e73..f60db12459 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/HomeDetailFragment.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/HomeDetailFragment.kt @@ -46,7 +46,6 @@ private const val INDEX_PEOPLE = 1 private const val INDEX_ROOMS = 2 class HomeDetailFragment @Inject constructor( - private val session: Session, val homeDetailViewModelFactory: HomeDetailViewModel.Factory, private val avatarRenderer: AvatarRenderer ) : VectorBaseFragment(), KeysBackupBanner.Delegate { @@ -56,9 +55,7 @@ class HomeDetailFragment @Inject constructor( private val viewModel: HomeDetailViewModel by fragmentViewModel() private lateinit var sharedActionViewModel: HomeSharedActionViewModel - override fun getLayoutResId(): Int { - return R.layout.fragment_home_detail - } + override fun getLayoutResId() = R.layout.fragment_home_detail override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/action/MessageActionsAnimator.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/action/MessageActionsAnimator.kt new file mode 100644 index 0000000000..fbe32e0e2b --- /dev/null +++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/action/MessageActionsAnimator.kt @@ -0,0 +1,34 @@ +/* + * 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.riotx.features.home.room.detail.timeline.action + +import androidx.recyclerview.widget.DefaultItemAnimator + +private const val ANIM_DURATION_IN_MILLIS = 300L + +/** + * We only want to animate the expand of the "Report content" submenu + */ +class MessageActionsAnimator : DefaultItemAnimator() { + + init { + addDuration = ANIM_DURATION_IN_MILLIS + removeDuration = 0 + moveDuration = 0 + changeDuration = 0 + } +} diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/action/MessageActionsBottomSheet.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/action/MessageActionsBottomSheet.kt index 23729734cb..63e3be908d 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/action/MessageActionsBottomSheet.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/action/MessageActionsBottomSheet.kt @@ -30,7 +30,6 @@ import im.vector.riotx.core.extensions.cleanup import im.vector.riotx.core.extensions.configureWith import im.vector.riotx.core.platform.VectorBaseBottomSheetDialogFragment import im.vector.riotx.features.home.room.detail.timeline.item.MessageInformationData -import kotlinx.android.synthetic.main.activity_image_media_viewer.* import javax.inject.Inject /** @@ -88,7 +87,11 @@ class MessageActionsBottomSheet : VectorBaseBottomSheetDialogFragment(), Message override fun didSelectMenuAction(eventAction: EventSharedAction) { if (eventAction is EventSharedAction.ReportContent) { - // Toggle report menu // TODO Reanable item animation? + // Toggle report menu + // Enable item animation + if (recyclerView.itemAnimator == null) { + recyclerView.itemAnimator = MessageActionsAnimator() + } viewModel.handle(MessageActionsAction.ToggleReportMenu) } else { sharedActionViewModel.post(eventAction) diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/filtered/FilteredRoomsActivity.kt b/vector/src/main/java/im/vector/riotx/features/home/room/filtered/FilteredRoomsActivity.kt index c4bb0d9b15..04e738cec8 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/room/filtered/FilteredRoomsActivity.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/room/filtered/FilteredRoomsActivity.kt @@ -36,9 +36,7 @@ class FilteredRoomsActivity : VectorBaseActivity() { return supportFragmentManager.findFragmentByTag(FRAGMENT_TAG) as? RoomListFragment } - override fun getLayoutRes(): Int { - return R.layout.activity_filtered_rooms - } + override fun getLayoutRes() = R.layout.activity_filtered_rooms override fun injectWith(injector: ScreenComponent) { injector.inject(this) diff --git a/vector/src/main/java/im/vector/riotx/features/reactions/EmojiReactionPickerActivity.kt b/vector/src/main/java/im/vector/riotx/features/reactions/EmojiReactionPickerActivity.kt index 85e4eecf21..02f564ba72 100644 --- a/vector/src/main/java/im/vector/riotx/features/reactions/EmojiReactionPickerActivity.kt +++ b/vector/src/main/java/im/vector/riotx/features/reactions/EmojiReactionPickerActivity.kt @@ -54,11 +54,11 @@ class EmojiReactionPickerActivity : VectorBaseActivity(), lateinit var viewModel: EmojiChooserViewModel - override fun getMenuRes(): Int = R.menu.menu_emoji_reaction_picker + override fun getMenuRes() = R.menu.menu_emoji_reaction_picker - override fun getLayoutRes(): Int = R.layout.activity_emoji_reaction_picker + override fun getLayoutRes() = R.layout.activity_emoji_reaction_picker - override fun getTitleRes(): Int = R.string.title_activity_emoji_reaction_picker + override fun getTitleRes() = R.string.title_activity_emoji_reaction_picker @Inject lateinit var emojiCompatFontProvider: EmojiCompatFontProvider diff --git a/vector/src/main/java/im/vector/riotx/features/reactions/EmojiSearchResultFragment.kt b/vector/src/main/java/im/vector/riotx/features/reactions/EmojiSearchResultFragment.kt index 976d5e984e..d87b7d7946 100644 --- a/vector/src/main/java/im/vector/riotx/features/reactions/EmojiSearchResultFragment.kt +++ b/vector/src/main/java/im/vector/riotx/features/reactions/EmojiSearchResultFragment.kt @@ -32,7 +32,7 @@ class EmojiSearchResultFragment @Inject constructor( private val epoxyController: EmojiSearchResultController ) : VectorBaseFragment() { - override fun getLayoutResId(): Int = R.layout.fragment_generic_recycler + override fun getLayoutResId() = R.layout.fragment_generic_recycler val viewModel: EmojiSearchResultViewModel by activityViewModel() diff --git a/vector/src/main/java/im/vector/riotx/features/settings/push/PushGateWayController.kt b/vector/src/main/java/im/vector/riotx/features/settings/push/PushGateWayController.kt new file mode 100644 index 0000000000..d8d23fbaf4 --- /dev/null +++ b/vector/src/main/java/im/vector/riotx/features/settings/push/PushGateWayController.kt @@ -0,0 +1,51 @@ +/* + * 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.riotx.features.settings.push + +import com.airbnb.epoxy.TypedEpoxyController +import im.vector.riotx.R +import im.vector.riotx.core.resources.StringProvider +import im.vector.riotx.core.ui.list.genericFooterItem +import javax.inject.Inject + +class PushGateWayController @Inject constructor( + private val stringProvider: StringProvider +) : TypedEpoxyController() { + + override fun buildModels(data: PushGatewayViewState?) { + data?.pushGateways?.invoke()?.let { pushers -> + if (pushers.isEmpty()) { + genericFooterItem { + id("footer") + text(stringProvider.getString(R.string.settings_push_gateway_no_pushers)) + } + } else { + pushers.forEach { + pushGatewayItem { + id("${it.pushKey}_${it.appId}") + pusher(it) + } + } + } + } ?: run { + genericFooterItem { + id("loading") + text(stringProvider.getString(R.string.loading)) + } + } + } +} diff --git a/vector/src/main/java/im/vector/riotx/features/settings/push/PushGatewaysFragment.kt b/vector/src/main/java/im/vector/riotx/features/settings/push/PushGatewaysFragment.kt index 816e76c4c4..c4d2118a77 100644 --- a/vector/src/main/java/im/vector/riotx/features/settings/push/PushGatewaysFragment.kt +++ b/vector/src/main/java/im/vector/riotx/features/settings/push/PushGatewaysFragment.kt @@ -19,7 +19,6 @@ package im.vector.riotx.features.settings.push import android.os.Bundle import android.view.View import androidx.recyclerview.widget.DividerItemDecoration -import com.airbnb.epoxy.TypedEpoxyController import com.airbnb.mvrx.fragmentViewModel import com.airbnb.mvrx.withState import im.vector.riotx.R @@ -27,20 +26,18 @@ import im.vector.riotx.core.extensions.cleanup import im.vector.riotx.core.extensions.configureWith import im.vector.riotx.core.platform.VectorBaseActivity import im.vector.riotx.core.platform.VectorBaseFragment -import im.vector.riotx.core.resources.StringProvider -import im.vector.riotx.core.ui.list.genericFooterItem import kotlinx.android.synthetic.main.fragment_generic_recycler.* import javax.inject.Inject // Referenced in vector_settings_notifications.xml class PushGatewaysFragment @Inject constructor( - val pushGatewaysViewModelFactory: PushGatewaysViewModel.Factory + val pushGatewaysViewModelFactory: PushGatewaysViewModel.Factory, + private val epoxyController: PushGateWayController ) : VectorBaseFragment() { - override fun getLayoutResId(): Int = R.layout.fragment_generic_recycler + override fun getLayoutResId() = R.layout.fragment_generic_recycler private val viewModel: PushGatewaysViewModel by fragmentViewModel(PushGatewaysViewModel::class) - private val epoxyController by lazy { PushGateWayController(StringProvider(requireContext().resources)) } override fun onResume() { super.onResume() @@ -60,30 +57,4 @@ class PushGatewaysFragment @Inject constructor( override fun invalidate() = withState(viewModel) { state -> epoxyController.setData(state) } - - // TODO Move to a proper file - class PushGateWayController(private val stringProvider: StringProvider) : TypedEpoxyController() { - override fun buildModels(data: PushGatewayViewState?) { - data?.pushGateways?.invoke()?.let { pushers -> - if (pushers.isEmpty()) { - genericFooterItem { - id("footer") - text(stringProvider.getString(R.string.settings_push_gateway_no_pushers)) - } - } else { - pushers.forEach { - pushGatewayItem { - id("${it.pushKey}_${it.appId}") - pusher(it) - } - } - } - } ?: run { - genericFooterItem { - id("footer") - text(stringProvider.getString(R.string.loading)) - } - } - } - } } diff --git a/vector/src/main/java/im/vector/riotx/features/settings/push/PushRulesFragment.kt b/vector/src/main/java/im/vector/riotx/features/settings/push/PushRulesFragment.kt index 62ac49e738..4deb410d03 100644 --- a/vector/src/main/java/im/vector/riotx/features/settings/push/PushRulesFragment.kt +++ b/vector/src/main/java/im/vector/riotx/features/settings/push/PushRulesFragment.kt @@ -33,7 +33,7 @@ import kotlinx.android.synthetic.main.fragment_generic_recycler.* // Referenced in vector_settings_notifications.xml class PushRulesFragment : VectorBaseFragment() { - override fun getLayoutResId(): Int = R.layout.fragment_generic_recycler + override fun getLayoutResId() = R.layout.fragment_generic_recycler private val viewModel: PushRulesViewModel by fragmentViewModel(PushRulesViewModel::class) diff --git a/vector/src/main/java/im/vector/riotx/features/share/IncomingShareActivity.kt b/vector/src/main/java/im/vector/riotx/features/share/IncomingShareActivity.kt index 33b0744d4f..e48c8246d2 100644 --- a/vector/src/main/java/im/vector/riotx/features/share/IncomingShareActivity.kt +++ b/vector/src/main/java/im/vector/riotx/features/share/IncomingShareActivity.kt @@ -50,9 +50,7 @@ class IncomingShareActivity : return supportFragmentManager.findFragmentById(R.id.shareRoomListFragmentContainer) as? RoomListFragment } - override fun getLayoutRes(): Int { - return R.layout.activity_incoming_share - } + override fun getLayoutRes() = R.layout.activity_incoming_share override fun injectWith(injector: ScreenComponent) { injector.inject(this) From edc6c3dd4fa42e1e78186af04faf7912637a1b05 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 9 Dec 2019 22:00:41 +0100 Subject: [PATCH 11/22] Cleanup --- .../core/platform/VectorBaseBottomSheetDialogFragment.kt | 2 +- .../detail/readreceipts/DisplayReadReceiptsBottomSheet.kt | 4 ++-- .../room/detail/timeline/action/MessageActionsBottomSheet.kt | 4 ++-- .../detail/timeline/edithistory/ViewEditHistoryBottomSheet.kt | 4 ++-- .../detail/timeline/reactions/ViewReactionsBottomSheet.kt | 4 ++-- .../home/room/list/actions/RoomListQuickActionsBottomSheet.kt | 4 ++-- 6 files changed, 11 insertions(+), 11 deletions(-) diff --git a/vector/src/main/java/im/vector/riotx/core/platform/VectorBaseBottomSheetDialogFragment.kt b/vector/src/main/java/im/vector/riotx/core/platform/VectorBaseBottomSheetDialogFragment.kt index 5727580653..70311e2f57 100644 --- a/vector/src/main/java/im/vector/riotx/core/platform/VectorBaseBottomSheetDialogFragment.kt +++ b/vector/src/main/java/im/vector/riotx/core/platform/VectorBaseBottomSheetDialogFragment.kt @@ -73,7 +73,7 @@ abstract class VectorBaseBottomSheetDialogFragment : BottomSheetDialogFragment() injectWith(screenComponent) } - protected open fun injectWith(screenComponent: ScreenComponent) = Unit + protected open fun injectWith(injector: ScreenComponent) = Unit override fun onCreate(savedInstanceState: Bundle?) { mvrxViewIdProperty.restoreFrom(savedInstanceState) diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/readreceipts/DisplayReadReceiptsBottomSheet.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/readreceipts/DisplayReadReceiptsBottomSheet.kt index f943556bcd..4827c825cb 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/room/detail/readreceipts/DisplayReadReceiptsBottomSheet.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/readreceipts/DisplayReadReceiptsBottomSheet.kt @@ -53,8 +53,8 @@ class DisplayReadReceiptsBottomSheet : VectorBaseBottomSheetDialogFragment() { private val displayReadReceiptArgs: DisplayReadReceiptArgs by args() - override fun injectWith(screenComponent: ScreenComponent) { - screenComponent.inject(this) + override fun injectWith(injector: ScreenComponent) { + injector.inject(this) } override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/action/MessageActionsBottomSheet.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/action/MessageActionsBottomSheet.kt index 63e3be908d..ba772344e0 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/action/MessageActionsBottomSheet.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/action/MessageActionsBottomSheet.kt @@ -49,8 +49,8 @@ class MessageActionsBottomSheet : VectorBaseBottomSheetDialogFragment(), Message private lateinit var sharedActionViewModel: MessageSharedActionViewModel - override fun injectWith(screenComponent: ScreenComponent) { - screenComponent.inject(this) + override fun injectWith(injector: ScreenComponent) { + injector.inject(this) } override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/edithistory/ViewEditHistoryBottomSheet.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/edithistory/ViewEditHistoryBottomSheet.kt index e8ee6ce10e..5a5fc5de95 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/edithistory/ViewEditHistoryBottomSheet.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/edithistory/ViewEditHistoryBottomSheet.kt @@ -54,8 +54,8 @@ class ViewEditHistoryBottomSheet : VectorBaseBottomSheetDialogFragment() { ViewEditHistoryEpoxyController(requireContext(), viewModel.dateFormatter, eventHtmlRenderer) } - override fun injectWith(screenComponent: ScreenComponent) { - screenComponent.inject(this) + override fun injectWith(injector: ScreenComponent) { + injector.inject(this) } override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/reactions/ViewReactionsBottomSheet.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/reactions/ViewReactionsBottomSheet.kt index 9ece3e2bbd..8fddc4c06a 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/reactions/ViewReactionsBottomSheet.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/reactions/ViewReactionsBottomSheet.kt @@ -50,8 +50,8 @@ class ViewReactionsBottomSheet : VectorBaseBottomSheetDialogFragment() { @Inject lateinit var epoxyController: ViewReactionsEpoxyController - override fun injectWith(screenComponent: ScreenComponent) { - screenComponent.inject(this) + override fun injectWith(injector: ScreenComponent) { + injector.inject(this) } override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/list/actions/RoomListQuickActionsBottomSheet.kt b/vector/src/main/java/im/vector/riotx/features/home/room/list/actions/RoomListQuickActionsBottomSheet.kt index 28656e1d6e..60a26c8151 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/room/list/actions/RoomListQuickActionsBottomSheet.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/room/list/actions/RoomListQuickActionsBottomSheet.kt @@ -57,8 +57,8 @@ class RoomListQuickActionsBottomSheet : VectorBaseBottomSheetDialogFragment(), R override val showExpanded = true - override fun injectWith(screenComponent: ScreenComponent) { - screenComponent.inject(this) + override fun injectWith(injector: ScreenComponent) { + injector.inject(this) } override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { From 99423bacb234706cc42bf6803857f19266a8fc45 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 9 Dec 2019 22:09:17 +0100 Subject: [PATCH 12/22] Cleanup --- .../java/im/vector/riotx/core/extensions/RecyclerView.kt | 7 +++++-- .../timeline/edithistory/ViewEditHistoryBottomSheet.kt | 2 +- .../riotx/features/reactions/EmojiSearchResultFragment.kt | 3 +-- .../riotx/features/settings/push/PushGatewaysFragment.kt | 3 +-- .../riotx/features/settings/push/PushRulesFragment.kt | 3 +-- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/vector/src/main/java/im/vector/riotx/core/extensions/RecyclerView.kt b/vector/src/main/java/im/vector/riotx/core/extensions/RecyclerView.kt index b34edc0e89..003045af51 100644 --- a/vector/src/main/java/im/vector/riotx/core/extensions/RecyclerView.kt +++ b/vector/src/main/java/im/vector/riotx/core/extensions/RecyclerView.kt @@ -16,6 +16,7 @@ package im.vector.riotx.core.extensions +import androidx.recyclerview.widget.DividerItemDecoration import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView import com.airbnb.epoxy.EpoxyController @@ -25,11 +26,13 @@ import com.airbnb.epoxy.EpoxyController */ fun RecyclerView.configureWith(epoxyController: EpoxyController, itemAnimator: RecyclerView.ItemAnimator? = null, - itemDecoration: RecyclerView.ItemDecoration? = null, + showDivider: Boolean = false, hasFixedSize: Boolean = true) { layoutManager = LinearLayoutManager(context, LinearLayoutManager.VERTICAL, false) itemAnimator?.let { this.itemAnimator = it } - itemDecoration?.let { addItemDecoration(it) } + if (showDivider) { + addItemDecoration(DividerItemDecoration(context, DividerItemDecoration.VERTICAL)) + } setHasFixedSize(hasFixedSize) adapter = epoxyController.adapter } diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/edithistory/ViewEditHistoryBottomSheet.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/edithistory/ViewEditHistoryBottomSheet.kt index 5a5fc5de95..845d2a2b7a 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/edithistory/ViewEditHistoryBottomSheet.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/edithistory/ViewEditHistoryBottomSheet.kt @@ -68,7 +68,7 @@ class ViewEditHistoryBottomSheet : VectorBaseBottomSheetDialogFragment() { super.onActivityCreated(savedInstanceState) recyclerView.configureWith( epoxyController, - itemDecoration = DividerItemDecoration(requireContext(), DividerItemDecoration.VERTICAL), + showDivider = true, hasFixedSize = false) bottomSheetTitle.text = context?.getString(R.string.message_edits) } diff --git a/vector/src/main/java/im/vector/riotx/features/reactions/EmojiSearchResultFragment.kt b/vector/src/main/java/im/vector/riotx/features/reactions/EmojiSearchResultFragment.kt index d87b7d7946..b34a5f2357 100644 --- a/vector/src/main/java/im/vector/riotx/features/reactions/EmojiSearchResultFragment.kt +++ b/vector/src/main/java/im/vector/riotx/features/reactions/EmojiSearchResultFragment.kt @@ -17,7 +17,6 @@ package im.vector.riotx.features.reactions import android.os.Bundle import android.view.View -import androidx.recyclerview.widget.DividerItemDecoration import com.airbnb.mvrx.activityViewModel import com.airbnb.mvrx.withState import im.vector.riotx.R @@ -49,7 +48,7 @@ class EmojiSearchResultFragment @Inject constructor( } } - recyclerView.configureWith(epoxyController, itemDecoration = DividerItemDecoration(requireContext(), DividerItemDecoration.VERTICAL)) + recyclerView.configureWith(epoxyController, showDivider = true) } override fun onDestroyView() { diff --git a/vector/src/main/java/im/vector/riotx/features/settings/push/PushGatewaysFragment.kt b/vector/src/main/java/im/vector/riotx/features/settings/push/PushGatewaysFragment.kt index c4d2118a77..d5c19e8781 100644 --- a/vector/src/main/java/im/vector/riotx/features/settings/push/PushGatewaysFragment.kt +++ b/vector/src/main/java/im/vector/riotx/features/settings/push/PushGatewaysFragment.kt @@ -18,7 +18,6 @@ package im.vector.riotx.features.settings.push import android.os.Bundle import android.view.View -import androidx.recyclerview.widget.DividerItemDecoration import com.airbnb.mvrx.fragmentViewModel import com.airbnb.mvrx.withState import im.vector.riotx.R @@ -46,7 +45,7 @@ class PushGatewaysFragment @Inject constructor( override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - recyclerView.configureWith(epoxyController, itemDecoration = DividerItemDecoration(requireContext(), DividerItemDecoration.VERTICAL)) + recyclerView.configureWith(epoxyController, showDivider = true) } override fun onDestroyView() { diff --git a/vector/src/main/java/im/vector/riotx/features/settings/push/PushRulesFragment.kt b/vector/src/main/java/im/vector/riotx/features/settings/push/PushRulesFragment.kt index 4deb410d03..bee9cf54d9 100644 --- a/vector/src/main/java/im/vector/riotx/features/settings/push/PushRulesFragment.kt +++ b/vector/src/main/java/im/vector/riotx/features/settings/push/PushRulesFragment.kt @@ -17,7 +17,6 @@ package im.vector.riotx.features.settings.push import android.os.Bundle import android.view.View -import androidx.recyclerview.widget.DividerItemDecoration import com.airbnb.epoxy.TypedEpoxyController import com.airbnb.mvrx.fragmentViewModel import com.airbnb.mvrx.withState @@ -46,7 +45,7 @@ class PushRulesFragment : VectorBaseFragment() { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - recyclerView.configureWith(epoxyController, itemDecoration = DividerItemDecoration(requireContext(), DividerItemDecoration.VERTICAL)) + recyclerView.configureWith(epoxyController, showDivider = true) } override fun onDestroyView() { From 8527d3f162205b70f0b6d19f6cb283e8de9354c4 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 9 Dec 2019 22:30:29 +0100 Subject: [PATCH 13/22] Improve emoji picker search result --- .../reactions/EmojiSearchResultController.kt | 7 ++-- .../reactions/EmojiSearchResultFragment.kt | 20 +++++------ .../reactions/EmojiSearchResultItem.kt | 3 +- .../src/main/res/layout/item_emoji_result.xml | 33 ++++++++++--------- 4 files changed, 32 insertions(+), 31 deletions(-) diff --git a/vector/src/main/java/im/vector/riotx/features/reactions/EmojiSearchResultController.kt b/vector/src/main/java/im/vector/riotx/features/reactions/EmojiSearchResultController.kt index 3e8f1c9769..208d9d7a56 100644 --- a/vector/src/main/java/im/vector/riotx/features/reactions/EmojiSearchResultController.kt +++ b/vector/src/main/java/im/vector/riotx/features/reactions/EmojiSearchResultController.kt @@ -24,9 +24,10 @@ import im.vector.riotx.core.resources.StringProvider import im.vector.riotx.core.ui.list.genericFooterItem import javax.inject.Inject -class EmojiSearchResultController @Inject constructor(val stringProvider: StringProvider, - private val fontProvider: EmojiCompatFontProvider) - : TypedEpoxyController() { +class EmojiSearchResultController @Inject constructor( + private val stringProvider: StringProvider, + private val fontProvider: EmojiCompatFontProvider +) : TypedEpoxyController() { var emojiTypeface: Typeface? = fontProvider.typeface diff --git a/vector/src/main/java/im/vector/riotx/features/reactions/EmojiSearchResultFragment.kt b/vector/src/main/java/im/vector/riotx/features/reactions/EmojiSearchResultFragment.kt index b34a5f2357..a62b2d39cb 100644 --- a/vector/src/main/java/im/vector/riotx/features/reactions/EmojiSearchResultFragment.kt +++ b/vector/src/main/java/im/vector/riotx/features/reactions/EmojiSearchResultFragment.kt @@ -29,25 +29,18 @@ import javax.inject.Inject class EmojiSearchResultFragment @Inject constructor( private val epoxyController: EmojiSearchResultController -) : VectorBaseFragment() { +) : VectorBaseFragment(), ReactionClickListener { override fun getLayoutResId() = R.layout.fragment_generic_recycler - val viewModel: EmojiSearchResultViewModel by activityViewModel() + private val viewModel: EmojiSearchResultViewModel by activityViewModel() - var sharedViewModel: EmojiChooserViewModel? = null + private lateinit var sharedViewModel: EmojiChooserViewModel override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) sharedViewModel = activityViewModelProvider.get(EmojiChooserViewModel::class.java) - - epoxyController.listener = object : ReactionClickListener { - override fun onReactionSelected(reaction: String) { - sharedViewModel?.selectedReaction = reaction - sharedViewModel?.navigateEvent?.value = LiveEvent(EmojiChooserViewModel.NAVIGATE_FINISH) - } - } - + epoxyController.listener = this recyclerView.configureWith(epoxyController, showDivider = true) } @@ -57,6 +50,11 @@ class EmojiSearchResultFragment @Inject constructor( super.onDestroyView() } + override fun onReactionSelected(reaction: String) { + sharedViewModel.selectedReaction = reaction + sharedViewModel.navigateEvent.value = LiveEvent(EmojiChooserViewModel.NAVIGATE_FINISH) + } + override fun invalidate() = withState(viewModel) { state -> epoxyController.setData(state) } diff --git a/vector/src/main/java/im/vector/riotx/features/reactions/EmojiSearchResultItem.kt b/vector/src/main/java/im/vector/riotx/features/reactions/EmojiSearchResultItem.kt index 1b117035d9..746fdddbf3 100644 --- a/vector/src/main/java/im/vector/riotx/features/reactions/EmojiSearchResultItem.kt +++ b/vector/src/main/java/im/vector/riotx/features/reactions/EmojiSearchResultItem.kt @@ -22,6 +22,7 @@ import com.airbnb.epoxy.EpoxyModelClass import com.airbnb.epoxy.EpoxyModelWithHolder import im.vector.riotx.R import im.vector.riotx.core.epoxy.VectorEpoxyHolder +import im.vector.riotx.core.extensions.setTextOrHide @EpoxyModelClass(layout = R.layout.item_emoji_result) abstract class EmojiSearchResultItem : EpoxyModelWithHolder() { @@ -44,7 +45,7 @@ abstract class EmojiSearchResultItem : EpoxyModelWithHolder + android:paddingStart="@dimen/layout_horizontal_margin" + android:paddingEnd="@dimen/layout_horizontal_margin"> + android:layout_weight="1" + android:orientation="vertical"> - + android:textColor="?riotx_text_secondary" + android:textSize="14sp" + android:visibility="gone" + tools:text="Smile, foo, bar" + tools:visibility="visible" /> From 1ad8f47dc1bc5e5804271596bd545d65b5a344b0 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 9 Dec 2019 22:36:38 +0100 Subject: [PATCH 14/22] Split EmojiDataSource --- .../java/im/vector/riotx/core/utils/Emoji.kt | 4 +- .../reactions/EmojiChooserViewModel.kt | 1 + .../reactions/EmojiRecyclerAdapter.kt | 1 + .../reactions/EmojiSearchResultItem.kt | 3 +- .../reactions/EmojiSearchResultViewModel.kt | 4 +- .../features/reactions/data/EmojiCategory.kt | 26 +++++++++ .../features/reactions/data/EmojiData.kt | 26 +++++++++ .../reactions/{ => data}/EmojiDataSource.kt | 46 +--------------- .../features/reactions/data/EmojiItem.kt | 54 +++++++++++++++++++ 9 files changed, 116 insertions(+), 49 deletions(-) create mode 100644 vector/src/main/java/im/vector/riotx/features/reactions/data/EmojiCategory.kt create mode 100644 vector/src/main/java/im/vector/riotx/features/reactions/data/EmojiData.kt rename vector/src/main/java/im/vector/riotx/features/reactions/{ => data}/EmojiDataSource.kt (51%) create mode 100644 vector/src/main/java/im/vector/riotx/features/reactions/data/EmojiItem.kt diff --git a/vector/src/main/java/im/vector/riotx/core/utils/Emoji.kt b/vector/src/main/java/im/vector/riotx/core/utils/Emoji.kt index a5babcc885..9b5552a73b 100644 --- a/vector/src/main/java/im/vector/riotx/core/utils/Emoji.kt +++ b/vector/src/main/java/im/vector/riotx/core/utils/Emoji.kt @@ -19,7 +19,7 @@ package im.vector.riotx.core.utils import android.content.Context import com.squareup.moshi.Moshi import im.vector.riotx.R -import im.vector.riotx.features.reactions.EmojiDataSource +import im.vector.riotx.features.reactions.data.EmojiData import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.launch import timber.log.Timber @@ -56,7 +56,7 @@ fun initKnownEmojiHashSet(context: Context, done: (() -> Unit)? = null) { GlobalScope.launch { context.resources.openRawResource(R.raw.emoji_picker_datasource).use { input -> val moshi = Moshi.Builder().build() - val jsonAdapter = moshi.adapter(EmojiDataSource.EmojiData::class.java) + val jsonAdapter = moshi.adapter(EmojiData::class.java) val inputAsString = input.bufferedReader().use { it.readText() } val source = jsonAdapter.fromJson(inputAsString) knownEmojiSet = HashSet().also { diff --git a/vector/src/main/java/im/vector/riotx/features/reactions/EmojiChooserViewModel.kt b/vector/src/main/java/im/vector/riotx/features/reactions/EmojiChooserViewModel.kt index 1979b02bfd..014250d860 100644 --- a/vector/src/main/java/im/vector/riotx/features/reactions/EmojiChooserViewModel.kt +++ b/vector/src/main/java/im/vector/riotx/features/reactions/EmojiChooserViewModel.kt @@ -19,6 +19,7 @@ import android.content.Context import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel import im.vector.riotx.core.utils.LiveEvent +import im.vector.riotx.features.reactions.data.EmojiDataSource import javax.inject.Inject class EmojiChooserViewModel @Inject constructor() : ViewModel() { diff --git a/vector/src/main/java/im/vector/riotx/features/reactions/EmojiRecyclerAdapter.kt b/vector/src/main/java/im/vector/riotx/features/reactions/EmojiRecyclerAdapter.kt index b5270bad29..c7df7fbe7b 100644 --- a/vector/src/main/java/im/vector/riotx/features/reactions/EmojiRecyclerAdapter.kt +++ b/vector/src/main/java/im/vector/riotx/features/reactions/EmojiRecyclerAdapter.kt @@ -30,6 +30,7 @@ import androidx.recyclerview.widget.RecyclerView import androidx.transition.AutoTransition import androidx.transition.TransitionManager import im.vector.riotx.R +import im.vector.riotx.features.reactions.data.EmojiDataSource import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.launch diff --git a/vector/src/main/java/im/vector/riotx/features/reactions/EmojiSearchResultItem.kt b/vector/src/main/java/im/vector/riotx/features/reactions/EmojiSearchResultItem.kt index 746fdddbf3..b3e70bcdf6 100644 --- a/vector/src/main/java/im/vector/riotx/features/reactions/EmojiSearchResultItem.kt +++ b/vector/src/main/java/im/vector/riotx/features/reactions/EmojiSearchResultItem.kt @@ -23,12 +23,13 @@ import com.airbnb.epoxy.EpoxyModelWithHolder import im.vector.riotx.R import im.vector.riotx.core.epoxy.VectorEpoxyHolder import im.vector.riotx.core.extensions.setTextOrHide +import im.vector.riotx.features.reactions.data.EmojiItem @EpoxyModelClass(layout = R.layout.item_emoji_result) abstract class EmojiSearchResultItem : EpoxyModelWithHolder() { @EpoxyAttribute - lateinit var emojiItem: EmojiDataSource.EmojiItem + lateinit var emojiItem: EmojiItem @EpoxyAttribute var currentQuery: String? = null diff --git a/vector/src/main/java/im/vector/riotx/features/reactions/EmojiSearchResultViewModel.kt b/vector/src/main/java/im/vector/riotx/features/reactions/EmojiSearchResultViewModel.kt index 8225fa7bd6..96746e583e 100644 --- a/vector/src/main/java/im/vector/riotx/features/reactions/EmojiSearchResultViewModel.kt +++ b/vector/src/main/java/im/vector/riotx/features/reactions/EmojiSearchResultViewModel.kt @@ -19,10 +19,12 @@ import com.airbnb.mvrx.MvRxState import com.airbnb.mvrx.MvRxViewModelFactory import com.airbnb.mvrx.ViewModelContext import im.vector.riotx.core.platform.VectorViewModel +import im.vector.riotx.features.reactions.data.EmojiDataSource +import im.vector.riotx.features.reactions.data.EmojiItem data class EmojiSearchResultViewState( val query: String = "", - val results: List = emptyList() + val results: List = emptyList() ) : MvRxState class EmojiSearchResultViewModel(val dataSource: EmojiDataSource, initialState: EmojiSearchResultViewState) diff --git a/vector/src/main/java/im/vector/riotx/features/reactions/data/EmojiCategory.kt b/vector/src/main/java/im/vector/riotx/features/reactions/data/EmojiCategory.kt new file mode 100644 index 0000000000..9aa7428047 --- /dev/null +++ b/vector/src/main/java/im/vector/riotx/features/reactions/data/EmojiCategory.kt @@ -0,0 +1,26 @@ +/* + * 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.riotx.features.reactions.data + +import com.squareup.moshi.JsonClass + +@JsonClass(generateAdapter = true) +data class EmojiCategory( + val id: String, + val name: String, + val emojis: List +) diff --git a/vector/src/main/java/im/vector/riotx/features/reactions/data/EmojiData.kt b/vector/src/main/java/im/vector/riotx/features/reactions/data/EmojiData.kt new file mode 100644 index 0000000000..9db9a01ce8 --- /dev/null +++ b/vector/src/main/java/im/vector/riotx/features/reactions/data/EmojiData.kt @@ -0,0 +1,26 @@ +/* + * 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.riotx.features.reactions.data + +import com.squareup.moshi.JsonClass + +@JsonClass(generateAdapter = true) +data class EmojiData( + val categories: List, + val emojis: Map, + val aliases: Map +) diff --git a/vector/src/main/java/im/vector/riotx/features/reactions/EmojiDataSource.kt b/vector/src/main/java/im/vector/riotx/features/reactions/data/EmojiDataSource.kt similarity index 51% rename from vector/src/main/java/im/vector/riotx/features/reactions/EmojiDataSource.kt rename to vector/src/main/java/im/vector/riotx/features/reactions/data/EmojiDataSource.kt index 2853975938..794fdbfe12 100644 --- a/vector/src/main/java/im/vector/riotx/features/reactions/EmojiDataSource.kt +++ b/vector/src/main/java/im/vector/riotx/features/reactions/data/EmojiDataSource.kt @@ -13,11 +13,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package im.vector.riotx.features.reactions +package im.vector.riotx.features.reactions.data import android.content.Context -import com.squareup.moshi.Json -import com.squareup.moshi.JsonClass import com.squareup.moshi.Moshi import im.vector.riotx.R @@ -31,32 +29,6 @@ class EmojiDataSource(val context: Context) { val jsonAdapter = moshi.adapter(EmojiData::class.java) val inputAsString = input.bufferedReader().use { it.readText() } this.rawData = jsonAdapter.fromJson(inputAsString) - // this.rawData = mb.fr(InputStreamReader(it), EmojiData::class.java) - } - } - @JsonClass(generateAdapter = true) - data class EmojiData(val categories: List, - val emojis: Map, - val aliases: Map) - - @JsonClass(generateAdapter = true) - data class EmojiCategory(val id: String, val name: String, val emojis: List) - - @JsonClass(generateAdapter = true) - data class EmojiItem( - @Json(name = "a") val name: String, - @Json(name = "b") val unicode: String, - @Json(name = "j") val keywords: List?, - val k: List?) { - - var _emojiText: String? = null - - fun emojiString() : String { - if (_emojiText == null) { - val utf8Text = unicode.split("-").joinToString("") { "\\u$it" } // "\u0048\u0065\u006C\u006C\u006F World" - _emojiText = fromUnicode(utf8Text) - } - return _emojiText!! } } @@ -72,20 +44,4 @@ class EmojiDataSource(val context: Context) { return text.toString() } } - -// name: 'a', -// unified: 'b', -// non_qualified: 'c', -// has_img_apple: 'd', -// has_img_google: 'e', -// has_img_twitter: 'f', -// has_img_emojione: 'g', -// has_img_facebook: 'h', -// has_img_messenger: 'i', -// keywords: 'j', -// sheet: 'k', -// emoticons: 'l', -// text: 'm', -// short_names: 'n', -// added_in: 'o', } diff --git a/vector/src/main/java/im/vector/riotx/features/reactions/data/EmojiItem.kt b/vector/src/main/java/im/vector/riotx/features/reactions/data/EmojiItem.kt new file mode 100644 index 0000000000..95deb32279 --- /dev/null +++ b/vector/src/main/java/im/vector/riotx/features/reactions/data/EmojiItem.kt @@ -0,0 +1,54 @@ +/* + * 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.riotx.features.reactions.data + +import com.squareup.moshi.Json +import com.squareup.moshi.JsonClass + +@JsonClass(generateAdapter = true) +data class EmojiItem( + @Json(name = "a") val name: String, + @Json(name = "b") val unicode: String, + @Json(name = "j") val keywords: List?, + val k: List?) { + + var _emojiText: String? = null + + fun emojiString(): String { + if (_emojiText == null) { + val utf8Text = unicode.split("-").joinToString("") { "\\u$it" } // "\u0048\u0065\u006C\u006C\u006F World" + _emojiText = EmojiDataSource.fromUnicode(utf8Text) + } + return _emojiText!! + } +} + +// name: 'a', +// unified: 'b', +// non_qualified: 'c', +// has_img_apple: 'd', +// has_img_google: 'e', +// has_img_twitter: 'f', +// has_img_emojione: 'g', +// has_img_facebook: 'h', +// has_img_messenger: 'i', +// keywords: 'j', +// sheet: 'k', +// emoticons: 'l', +// text: 'm', +// short_names: 'n', +// added_in: 'o', From 29721775419ba914ed506c1462a4c7cfb0d4dabd Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 9 Dec 2019 22:46:39 +0100 Subject: [PATCH 15/22] Split EmojiDataSource - cleanup --- .../reactions/EmojiSearchResultViewModel.kt | 10 +++++++--- .../features/reactions/data/EmojiCategory.kt | 7 ++++--- .../features/reactions/data/EmojiData.kt | 7 ++++--- .../reactions/data/EmojiDataSource.kt | 13 ------------- .../features/reactions/data/EmojiItem.kt | 19 ++++++++++++++++--- 5 files changed, 31 insertions(+), 25 deletions(-) diff --git a/vector/src/main/java/im/vector/riotx/features/reactions/EmojiSearchResultViewModel.kt b/vector/src/main/java/im/vector/riotx/features/reactions/EmojiSearchResultViewModel.kt index 96746e583e..a194e4ad4d 100644 --- a/vector/src/main/java/im/vector/riotx/features/reactions/EmojiSearchResultViewModel.kt +++ b/vector/src/main/java/im/vector/riotx/features/reactions/EmojiSearchResultViewModel.kt @@ -44,9 +44,13 @@ class EmojiSearchResultViewModel(val dataSource: EmojiDataSource, initialState: ?.map { it.second } ?.filter { it.name.contains(action.queryString, true) - || action.queryString.split("\\s".toRegex()).fold(true, { prev, q -> - prev && (it.keywords?.any { it.contains(q, true) } ?: false) - }) + || action.queryString + .split("\\s".toRegex()) + .fold(true, { prev, q -> + prev + && (it.keywords?.any { it.contains(q, true) } + ?: false) + }) } ?: emptyList() ) } diff --git a/vector/src/main/java/im/vector/riotx/features/reactions/data/EmojiCategory.kt b/vector/src/main/java/im/vector/riotx/features/reactions/data/EmojiCategory.kt index 9aa7428047..a6c238a4c4 100644 --- a/vector/src/main/java/im/vector/riotx/features/reactions/data/EmojiCategory.kt +++ b/vector/src/main/java/im/vector/riotx/features/reactions/data/EmojiCategory.kt @@ -16,11 +16,12 @@ package im.vector.riotx.features.reactions.data +import com.squareup.moshi.Json import com.squareup.moshi.JsonClass @JsonClass(generateAdapter = true) data class EmojiCategory( - val id: String, - val name: String, - val emojis: List + @Json(name = "id") val id: String, + @Json(name = "name") val name: String, + @Json(name = "emojis") val emojis: List ) diff --git a/vector/src/main/java/im/vector/riotx/features/reactions/data/EmojiData.kt b/vector/src/main/java/im/vector/riotx/features/reactions/data/EmojiData.kt index 9db9a01ce8..f2094bfc7b 100644 --- a/vector/src/main/java/im/vector/riotx/features/reactions/data/EmojiData.kt +++ b/vector/src/main/java/im/vector/riotx/features/reactions/data/EmojiData.kt @@ -16,11 +16,12 @@ package im.vector.riotx.features.reactions.data +import com.squareup.moshi.Json import com.squareup.moshi.JsonClass @JsonClass(generateAdapter = true) data class EmojiData( - val categories: List, - val emojis: Map, - val aliases: Map + @Json(name = "categories") val categories: List, + @Json(name = "emojis") val emojis: Map, + @Json(name = "aliases") val aliases: Map ) diff --git a/vector/src/main/java/im/vector/riotx/features/reactions/data/EmojiDataSource.kt b/vector/src/main/java/im/vector/riotx/features/reactions/data/EmojiDataSource.kt index 794fdbfe12..5564182f4e 100644 --- a/vector/src/main/java/im/vector/riotx/features/reactions/data/EmojiDataSource.kt +++ b/vector/src/main/java/im/vector/riotx/features/reactions/data/EmojiDataSource.kt @@ -31,17 +31,4 @@ class EmojiDataSource(val context: Context) { this.rawData = jsonAdapter.fromJson(inputAsString) } } - - companion object { - fun fromUnicode(unicode: String): String { - val str = unicode.replace("\\", "") - val arr = str.split("u".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray() - val text = StringBuffer() - for (i in 1 until arr.size) { - val hexVal = Integer.parseInt(arr[i], 16) - text.append(Character.toChars(hexVal)) - } - return text.toString() - } - } } diff --git a/vector/src/main/java/im/vector/riotx/features/reactions/data/EmojiItem.kt b/vector/src/main/java/im/vector/riotx/features/reactions/data/EmojiItem.kt index 95deb32279..fc0959dfbb 100644 --- a/vector/src/main/java/im/vector/riotx/features/reactions/data/EmojiItem.kt +++ b/vector/src/main/java/im/vector/riotx/features/reactions/data/EmojiItem.kt @@ -23,18 +23,31 @@ import com.squareup.moshi.JsonClass data class EmojiItem( @Json(name = "a") val name: String, @Json(name = "b") val unicode: String, - @Json(name = "j") val keywords: List?, - val k: List?) { + @Json(name = "j") val keywords: List? +) { var _emojiText: String? = null fun emojiString(): String { if (_emojiText == null) { val utf8Text = unicode.split("-").joinToString("") { "\\u$it" } // "\u0048\u0065\u006C\u006C\u006F World" - _emojiText = EmojiDataSource.fromUnicode(utf8Text) + _emojiText = fromUnicode(utf8Text) } return _emojiText!! } + + companion object { + private fun fromUnicode(unicode: String): String { + val str = unicode.replace("\\", "") + val arr = str.split("u".toRegex()).dropLastWhile { it.isEmpty() } + return buildString { + for (i in 1 until arr.size) { + val hexVal = Integer.parseInt(arr[i], 16) + append(Character.toChars(hexVal)) + } + } + } + } } // name: 'a', From 80306f20dfcd2d135d796751a8e6026aa31b0a43 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 9 Dec 2019 22:57:23 +0100 Subject: [PATCH 16/22] Split EmojiDataSource - avoid !! --- .../features/reactions/data/EmojiItem.kt | 44 ++++++++++--------- 1 file changed, 23 insertions(+), 21 deletions(-) diff --git a/vector/src/main/java/im/vector/riotx/features/reactions/data/EmojiItem.kt b/vector/src/main/java/im/vector/riotx/features/reactions/data/EmojiItem.kt index fc0959dfbb..9b4600b5c1 100644 --- a/vector/src/main/java/im/vector/riotx/features/reactions/data/EmojiItem.kt +++ b/vector/src/main/java/im/vector/riotx/features/reactions/data/EmojiItem.kt @@ -19,6 +19,23 @@ package im.vector.riotx.features.reactions.data import com.squareup.moshi.Json import com.squareup.moshi.JsonClass +/** + * name: 'a', + * unified: 'b', + * non_qualified: 'c', + * has_img_apple: 'd', + * has_img_google: 'e', + * has_img_twitter: 'f', + * has_img_emojione: 'g', + * has_img_facebook: 'h', + * has_img_messenger: 'i', + * keywords: 'j', + * sheet: 'k', + * emoticons: 'l', + * text: 'm', + * short_names: 'n', + * added_in: 'o' + */ @JsonClass(generateAdapter = true) data class EmojiItem( @Json(name = "a") val name: String, @@ -26,14 +43,14 @@ data class EmojiItem( @Json(name = "j") val keywords: List? ) { - var _emojiText: String? = null + private var emojiText: String? = null fun emojiString(): String { - if (_emojiText == null) { - val utf8Text = unicode.split("-").joinToString("") { "\\u$it" } // "\u0048\u0065\u006C\u006C\u006F World" - _emojiText = fromUnicode(utf8Text) - } - return _emojiText!! + emojiText?.let { return it } + + val utf8Text = unicode.split("-").joinToString("") { "\\u$it" } // "\u0048\u0065\u006C\u006C\u006F World" + return fromUnicode(utf8Text) + .also { emojiText = it } } companion object { @@ -50,18 +67,3 @@ data class EmojiItem( } } -// name: 'a', -// unified: 'b', -// non_qualified: 'c', -// has_img_apple: 'd', -// has_img_google: 'e', -// has_img_twitter: 'f', -// has_img_emojione: 'g', -// has_img_facebook: 'h', -// has_img_messenger: 'i', -// keywords: 'j', -// sheet: 'k', -// emoticons: 'l', -// text: 'm', -// short_names: 'n', -// added_in: 'o', From 63e0b15f3d80262efd86ba6c7464db59112847ad Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 9 Dec 2019 23:08:50 +0100 Subject: [PATCH 17/22] Split EmojiDataSource - cleanup --- .../features/reactions/data/EmojiItem.kt | 26 ++++++++++++------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/vector/src/main/java/im/vector/riotx/features/reactions/data/EmojiItem.kt b/vector/src/main/java/im/vector/riotx/features/reactions/data/EmojiItem.kt index 9b4600b5c1..fdd9a80911 100644 --- a/vector/src/main/java/im/vector/riotx/features/reactions/data/EmojiItem.kt +++ b/vector/src/main/java/im/vector/riotx/features/reactions/data/EmojiItem.kt @@ -42,21 +42,27 @@ data class EmojiItem( @Json(name = "b") val unicode: String, @Json(name = "j") val keywords: List? ) { + // Cannot be private... + var cache: String? = null - private var emojiText: String? = null + val emoji: String + get() { + cache?.let { return it } - fun emojiString(): String { - emojiText?.let { return it } - - val utf8Text = unicode.split("-").joinToString("") { "\\u$it" } // "\u0048\u0065\u006C\u006C\u006F World" - return fromUnicode(utf8Text) - .also { emojiText = it } - } + // "\u0048\u0065\u006C\u006C\u006F World" + val utf8Text = unicode + .split("-") + .joinToString("") { "\\u$it" } + return fromUnicode(utf8Text) + .also { cache = it } + } companion object { private fun fromUnicode(unicode: String): String { - val str = unicode.replace("\\", "") - val arr = str.split("u".toRegex()).dropLastWhile { it.isEmpty() } + val arr = unicode + .replace("\\", "") + .split("u".toRegex()) + .dropLastWhile { it.isEmpty() } return buildString { for (i in 1 until arr.size) { val hexVal = Integer.parseInt(arr[i], 16) From f00f34b244eed97debd1b8cf49c0a67c199562aa Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 9 Dec 2019 23:41:35 +0100 Subject: [PATCH 18/22] Improve EmojiChooserFragment: DI --- .../java/im/vector/riotx/VectorApplication.kt | 3 +- .../im/vector/riotx/core/di/FragmentModule.kt | 6 ++++ .../java/im/vector/riotx/core/utils/Emoji.kt | 2 ++ .../reactions/EmojiChooserFragment.kt | 28 ++++++++++++++-- .../reactions/EmojiChooserViewModel.kt | 33 ++++++------------- .../reactions/EmojiReactionPickerActivity.kt | 33 +++++++++---------- .../reactions/EmojiRecyclerAdapter.kt | 11 ++++--- .../reactions/EmojiSearchResultItem.kt | 4 +-- .../reactions/EmojiSearchResultViewModel.kt | 31 ++++++++++------- .../reactions/data/EmojiDataSource.kt | 23 +++++++++---- 10 files changed, 105 insertions(+), 69 deletions(-) diff --git a/vector/src/main/java/im/vector/riotx/VectorApplication.kt b/vector/src/main/java/im/vector/riotx/VectorApplication.kt index f3043fbec8..b6b0d16360 100644 --- a/vector/src/main/java/im/vector/riotx/VectorApplication.kt +++ b/vector/src/main/java/im/vector/riotx/VectorApplication.kt @@ -43,7 +43,6 @@ import im.vector.riotx.core.di.HasVectorInjector import im.vector.riotx.core.di.VectorComponent import im.vector.riotx.core.extensions.configureAndStart import im.vector.riotx.core.rx.setupRxPlugin -import im.vector.riotx.core.utils.initKnownEmojiHashSet import im.vector.riotx.features.configuration.VectorConfiguration import im.vector.riotx.features.lifecycle.VectorActivityLifecycleCallbacks import im.vector.riotx.features.notifications.NotificationDrawerManager @@ -137,7 +136,7 @@ class VectorApplication : Application(), HasVectorInjector, MatrixConfiguration. }) ProcessLifecycleOwner.get().lifecycle.addObserver(appStateHandler) // This should be done as early as possible - initKnownEmojiHashSet(appContext) + // initKnownEmojiHashSet(appContext) } override fun providesMatrixConfiguration() = MatrixConfiguration(BuildConfig.FLAVOR_DESCRIPTION) diff --git a/vector/src/main/java/im/vector/riotx/core/di/FragmentModule.kt b/vector/src/main/java/im/vector/riotx/core/di/FragmentModule.kt index 1a7a07bee2..442c5f6f96 100644 --- a/vector/src/main/java/im/vector/riotx/core/di/FragmentModule.kt +++ b/vector/src/main/java/im/vector/riotx/core/di/FragmentModule.kt @@ -38,6 +38,7 @@ import im.vector.riotx.features.home.room.detail.RoomDetailFragment import im.vector.riotx.features.home.room.list.RoomListFragment import im.vector.riotx.features.login.* import im.vector.riotx.features.login.terms.LoginTermsFragment +import im.vector.riotx.features.reactions.EmojiChooserFragment import im.vector.riotx.features.reactions.EmojiSearchResultFragment import im.vector.riotx.features.roomdirectory.PublicRoomsFragment import im.vector.riotx.features.roomdirectory.createroom.CreateRoomFragment @@ -255,4 +256,9 @@ interface FragmentModule { @IntoMap @FragmentKey(BreadcrumbsFragment::class) fun bindBreadcrumbsFragment(fragment: BreadcrumbsFragment): Fragment + + @Binds + @IntoMap + @FragmentKey(EmojiChooserFragment::class) + fun bindEmojiChooserFragment(fragment: EmojiChooserFragment): Fragment } diff --git a/vector/src/main/java/im/vector/riotx/core/utils/Emoji.kt b/vector/src/main/java/im/vector/riotx/core/utils/Emoji.kt index 9b5552a73b..3b16c3ea22 100644 --- a/vector/src/main/java/im/vector/riotx/core/utils/Emoji.kt +++ b/vector/src/main/java/im/vector/riotx/core/utils/Emoji.kt @@ -49,6 +49,7 @@ private val emojisPattern = Pattern.compile("((?:[\uD83C\uDF00-\uD83D\uDDFF]" + "|\uD83C\uDCCF\uFE0F?" + "|[\u231A\u231B\u2328\u23CF\u23E9-\u23F3\u23F8-\u23FA]\uFE0F?))") +/* // A hashset from all supported emoji private var knownEmojiSet: HashSet? = null @@ -77,6 +78,7 @@ fun isSingleEmoji(string: String): Boolean { } return knownEmojiSet?.contains(string) ?: false } + */ /** * Test if a string contains emojis. diff --git a/vector/src/main/java/im/vector/riotx/features/reactions/EmojiChooserFragment.kt b/vector/src/main/java/im/vector/riotx/features/reactions/EmojiChooserFragment.kt index c49bdcbf92..bbedeff552 100644 --- a/vector/src/main/java/im/vector/riotx/features/reactions/EmojiChooserFragment.kt +++ b/vector/src/main/java/im/vector/riotx/features/reactions/EmojiChooserFragment.kt @@ -17,13 +17,18 @@ package im.vector.riotx.features.reactions import android.os.Bundle import android.view.View +import androidx.lifecycle.observe import androidx.recyclerview.widget.RecyclerView import im.vector.riotx.R import im.vector.riotx.core.extensions.cleanup import im.vector.riotx.core.platform.VectorBaseFragment import javax.inject.Inject -class EmojiChooserFragment @Inject constructor() : VectorBaseFragment() { +class EmojiChooserFragment @Inject constructor( + private val emojiRecyclerAdapter: EmojiRecyclerAdapter +) : VectorBaseFragment(), + EmojiRecyclerAdapter.InteractionListener, + ReactionClickListener { override fun getLayoutResId() = R.layout.emoji_chooser_fragment @@ -32,15 +37,32 @@ class EmojiChooserFragment @Inject constructor() : VectorBaseFragment() { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) viewModel = activityViewModelProvider.get(EmojiChooserViewModel::class.java) - viewModel.initWithContext(context!!) + + emojiRecyclerAdapter.reactionClickListener = this + emojiRecyclerAdapter.interactionListener = this + (view as? RecyclerView)?.let { - it.adapter = viewModel.adapter + it.adapter = emojiRecyclerAdapter it.adapter?.notifyDataSetChanged() } + + viewModel.moveToSection.observe(viewLifecycleOwner) { section -> + emojiRecyclerAdapter.scrollToSection(section) + } + } + + override fun firstVisibleSectionChange(section: Int) { + viewModel.setCurrentSection(section) + } + + override fun onReactionSelected(reaction: String) { + viewModel.onReactionSelected(reaction) } override fun onDestroyView() { (view as? RecyclerView)?.cleanup() + emojiRecyclerAdapter.reactionClickListener = null + emojiRecyclerAdapter.interactionListener = null super.onDestroyView() } } diff --git a/vector/src/main/java/im/vector/riotx/features/reactions/EmojiChooserViewModel.kt b/vector/src/main/java/im/vector/riotx/features/reactions/EmojiChooserViewModel.kt index 014250d860..9a0317f454 100644 --- a/vector/src/main/java/im/vector/riotx/features/reactions/EmojiChooserViewModel.kt +++ b/vector/src/main/java/im/vector/riotx/features/reactions/EmojiChooserViewModel.kt @@ -15,46 +15,33 @@ */ package im.vector.riotx.features.reactions -import android.content.Context import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel import im.vector.riotx.core.utils.LiveEvent -import im.vector.riotx.features.reactions.data.EmojiDataSource import javax.inject.Inject class EmojiChooserViewModel @Inject constructor() : ViewModel() { - // TODO Move the adapter out of the ViewModel - var adapter: EmojiRecyclerAdapter? = null - val emojiSourceLiveData: MutableLiveData = MutableLiveData() - val navigateEvent: MutableLiveData> = MutableLiveData() var selectedReaction: String? = null var eventId: String? = null val currentSection: MutableLiveData = MutableLiveData() + val moveToSection: MutableLiveData = MutableLiveData() - var reactionClickListener = object : ReactionClickListener { - override fun onReactionSelected(reaction: String) { - selectedReaction = reaction - navigateEvent.value = LiveEvent(NAVIGATE_FINISH) - } + fun onReactionSelected(reaction: String) { + selectedReaction = reaction + navigateEvent.value = LiveEvent(NAVIGATE_FINISH) } - fun initWithContext(context: Context) { - // TODO load async - val emojiDataSource = EmojiDataSource(context) - emojiSourceLiveData.value = emojiDataSource - adapter = EmojiRecyclerAdapter(emojiDataSource, reactionClickListener) - adapter?.interactionListener = object : EmojiRecyclerAdapter.InteractionListener { - override fun firstVisibleSectionChange(section: Int) { - currentSection.value = section - } - } + // Called by the Fragment, when the List is scrolled + fun setCurrentSection(section: Int) { + currentSection.value = section } - fun scrollToSection(sectionIndex: Int) { - adapter?.scrollToSection(sectionIndex) + // Called by the Activity, when a tab item is clicked + fun scrollToSection(section: Int) { + moveToSection.value = section } companion object { diff --git a/vector/src/main/java/im/vector/riotx/features/reactions/EmojiReactionPickerActivity.kt b/vector/src/main/java/im/vector/riotx/features/reactions/EmojiReactionPickerActivity.kt index 02f564ba72..7c2a9349e0 100644 --- a/vector/src/main/java/im/vector/riotx/features/reactions/EmojiReactionPickerActivity.kt +++ b/vector/src/main/java/im/vector/riotx/features/reactions/EmojiReactionPickerActivity.kt @@ -35,6 +35,7 @@ import im.vector.riotx.R import im.vector.riotx.core.di.ScreenComponent import im.vector.riotx.core.extensions.observeEvent import im.vector.riotx.core.platform.VectorBaseActivity +import im.vector.riotx.features.reactions.data.EmojiDataSource import io.reactivex.android.schedulers.AndroidSchedulers import kotlinx.android.synthetic.main.activity_emoji_reaction_picker.* import timber.log.Timber @@ -44,7 +45,6 @@ import javax.inject.Inject /** * * TODO: Loading indicator while getting emoji data source? - * TODO: migrate to MvRx * TODO: Finish Refactor to vector base activity */ class EmojiReactionPickerActivity : VectorBaseActivity(), @@ -60,7 +60,9 @@ class EmojiReactionPickerActivity : VectorBaseActivity(), override fun getTitleRes() = R.string.title_activity_emoji_reaction_picker + @Inject lateinit var emojiSearchResultViewModelFactory: EmojiSearchResultViewModel.Factory @Inject lateinit var emojiCompatFontProvider: EmojiCompatFontProvider + @Inject lateinit var emojiDataSource: EmojiDataSource private val searchResultViewModel: EmojiSearchResultViewModel by viewModel() @@ -93,22 +95,20 @@ class EmojiReactionPickerActivity : VectorBaseActivity(), viewModel.eventId = intent.getStringExtra(EXTRA_EVENT_ID) - viewModel.emojiSourceLiveData.observe(this, Observer { - it.rawData?.categories?.let { categories -> - for (category in categories) { - val s = category.emojis[0] - tabLayout.newTab() - .also { tab -> - tab.text = it.rawData!!.emojis[s]!!.emojiString() - tab.contentDescription = category.name - } - .also { tab -> - tabLayout.addTab(tab) - } - } - tabLayout.addOnTabSelectedListener(tabLayoutSelectionListener) + emojiDataSource.rawData?.categories?.let { categories -> + for (category in categories) { + val s = category.emojis[0] + tabLayout.newTab() + .also { tab -> + tab.text = emojiDataSource.rawData!!.emojis[s]!!.emoji + tab.contentDescription = category.name + } + .also { tab -> + tabLayout.addTab(tab) + } } - }) + tabLayout.addOnTabSelectedListener(tabLayoutSelectionListener) + } viewModel.currentSection.observe(this, Observer { section -> section?.let { @@ -136,7 +136,6 @@ class EmojiReactionPickerActivity : VectorBaseActivity(), override fun compatibilityFontUpdate(typeface: Typeface?) { EmojiDrawView.configureTextPaint(this, typeface) - searchResultViewModel.dataSource } override fun onDestroy() { diff --git a/vector/src/main/java/im/vector/riotx/features/reactions/EmojiRecyclerAdapter.kt b/vector/src/main/java/im/vector/riotx/features/reactions/EmojiRecyclerAdapter.kt index c7df7fbe7b..a955189b17 100644 --- a/vector/src/main/java/im/vector/riotx/features/reactions/EmojiRecyclerAdapter.kt +++ b/vector/src/main/java/im/vector/riotx/features/reactions/EmojiRecyclerAdapter.kt @@ -34,6 +34,7 @@ import im.vector.riotx.features.reactions.data.EmojiDataSource import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.launch +import javax.inject.Inject import kotlin.math.abs /** @@ -43,10 +44,12 @@ import kotlin.math.abs * TODO: Performances * TODO: Scroll to section - Find a way to snap section to the top */ -class EmojiRecyclerAdapter(private val dataSource: EmojiDataSource? = null, - private var reactionClickListener: ReactionClickListener?) : +class EmojiRecyclerAdapter @Inject constructor( + private val dataSource: EmojiDataSource? +) : RecyclerView.Adapter() { + var reactionClickListener: ReactionClickListener? = null var interactionListener: InteractionListener? = null private var mRecyclerView: RecyclerView? = null @@ -73,7 +76,7 @@ class EmojiRecyclerAdapter(private val dataSource: EmojiDataSource? = null, val sectionMojis = categories[sectionNumber].emojis val sectionOffset = getSectionOffset(sectionNumber) val emoji = sectionMojis[itemPosition - sectionOffset] - val item = dataSource.rawData!!.emojis.getValue(emoji).emojiString() + val item = dataSource.rawData!!.emojis.getValue(emoji).emoji reactionClickListener?.onReactionSelected(item) } } @@ -197,7 +200,7 @@ class EmojiRecyclerAdapter(private val dataSource: EmojiDataSource? = null, val sectionMojis = categories[sectionNumber].emojis val sectionOffset = getSectionOffset(sectionNumber) val emoji = sectionMojis[position - sectionOffset] - val item = dataSource.rawData!!.emojis[emoji]!!.emojiString() + val item = dataSource.rawData!!.emojis[emoji]!!.emoji (holder as EmojiViewHolder).data = item if (scrollState != ScrollState.SETTLING || !isFastScroll) { // Log.i("PERF","Bind with draw at position:$position") diff --git a/vector/src/main/java/im/vector/riotx/features/reactions/EmojiSearchResultItem.kt b/vector/src/main/java/im/vector/riotx/features/reactions/EmojiSearchResultItem.kt index b3e70bcdf6..fd0a6d70ff 100644 --- a/vector/src/main/java/im/vector/riotx/features/reactions/EmojiSearchResultItem.kt +++ b/vector/src/main/java/im/vector/riotx/features/reactions/EmojiSearchResultItem.kt @@ -43,12 +43,12 @@ abstract class EmojiSearchResultItem : EpoxyModelWithHolder = emptyList() ) : MvRxState -class EmojiSearchResultViewModel(val dataSource: EmojiDataSource, initialState: EmojiSearchResultViewState) +class EmojiSearchResultViewModel @AssistedInject constructor( + @Assisted initialState: EmojiSearchResultViewState, + private val dataSource: EmojiDataSource) : VectorViewModel(initialState) { + @AssistedInject.Factory + interface Factory { + fun create(initialState: EmojiSearchResultViewState): EmojiSearchResultViewModel + } + + companion object : MvRxViewModelFactory { + + override fun create(viewModelContext: ViewModelContext, state: EmojiSearchResultViewState): EmojiSearchResultViewModel? { + val activity: EmojiReactionPickerActivity = (viewModelContext as ActivityViewModelContext).activity() + return activity.emojiSearchResultViewModelFactory.create(state) + } + } + override fun handle(action: EmojiSearchAction) { when (action) { is EmojiSearchAction.UpdateQuery -> updateQuery(action) @@ -55,12 +70,4 @@ class EmojiSearchResultViewModel(val dataSource: EmojiDataSource, initialState: ) } } - - companion object : MvRxViewModelFactory { - - override fun create(viewModelContext: ViewModelContext, state: EmojiSearchResultViewState): EmojiSearchResultViewModel? { - // TODO get the data source from activity? share it with other fragment - return EmojiSearchResultViewModel(EmojiDataSource(viewModelContext.activity), state) - } - } } diff --git a/vector/src/main/java/im/vector/riotx/features/reactions/data/EmojiDataSource.kt b/vector/src/main/java/im/vector/riotx/features/reactions/data/EmojiDataSource.kt index 5564182f4e..c9d683a2b9 100644 --- a/vector/src/main/java/im/vector/riotx/features/reactions/data/EmojiDataSource.kt +++ b/vector/src/main/java/im/vector/riotx/features/reactions/data/EmojiDataSource.kt @@ -18,17 +18,28 @@ package im.vector.riotx.features.reactions.data import android.content.Context import com.squareup.moshi.Moshi import im.vector.riotx.R +import im.vector.riotx.core.di.ScreenScope +import timber.log.Timber +import javax.inject.Inject +import kotlin.system.measureTimeMillis -class EmojiDataSource(val context: Context) { +@ScreenScope +class EmojiDataSource @Inject constructor( + context: Context +) { var rawData: EmojiData? = null init { - context.resources.openRawResource(R.raw.emoji_picker_datasource).use { input -> - val moshi = Moshi.Builder().build() - val jsonAdapter = moshi.adapter(EmojiData::class.java) - val inputAsString = input.bufferedReader().use { it.readText() } - this.rawData = jsonAdapter.fromJson(inputAsString) + measureTimeMillis { + context.resources.openRawResource(R.raw.emoji_picker_datasource).use { input -> + val moshi = Moshi.Builder().build() + val jsonAdapter = moshi.adapter(EmojiData::class.java) + val inputAsString = input.bufferedReader().use { it.readText() } + this.rawData = jsonAdapter.fromJson(inputAsString) + } + }.also { + Timber.e("Emoji: $it millis") } } } From 3c18fd5335d69154cb54e9cdb67b791093761b7c Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 10 Dec 2019 00:42:24 +0100 Subject: [PATCH 19/22] Improve EmojiChooserFragment: improve filtering result: sort --- .../reactions/EmojiReactionPickerActivity.kt | 24 +++-- .../reactions/EmojiRecyclerAdapter.kt | 87 ++++++++----------- .../reactions/EmojiSearchResultItem.kt | 2 +- .../reactions/EmojiSearchResultViewModel.kt | 34 +++++--- .../reactions/data/EmojiDataSource.kt | 27 ++---- .../features/reactions/data/EmojiItem.kt | 2 +- 6 files changed, 78 insertions(+), 98 deletions(-) diff --git a/vector/src/main/java/im/vector/riotx/features/reactions/EmojiReactionPickerActivity.kt b/vector/src/main/java/im/vector/riotx/features/reactions/EmojiReactionPickerActivity.kt index 7c2a9349e0..96536e1f16 100644 --- a/vector/src/main/java/im/vector/riotx/features/reactions/EmojiReactionPickerActivity.kt +++ b/vector/src/main/java/im/vector/riotx/features/reactions/EmojiReactionPickerActivity.kt @@ -95,20 +95,18 @@ class EmojiReactionPickerActivity : VectorBaseActivity(), viewModel.eventId = intent.getStringExtra(EXTRA_EVENT_ID) - emojiDataSource.rawData?.categories?.let { categories -> - for (category in categories) { - val s = category.emojis[0] - tabLayout.newTab() - .also { tab -> - tab.text = emojiDataSource.rawData!!.emojis[s]!!.emoji - tab.contentDescription = category.name - } - .also { tab -> - tabLayout.addTab(tab) - } - } - tabLayout.addOnTabSelectedListener(tabLayoutSelectionListener) + emojiDataSource.rawData.categories.forEach { category -> + val s = category.emojis[0] + tabLayout.newTab() + .also { tab -> + tab.text = emojiDataSource.rawData.emojis[s]!!.emoji + tab.contentDescription = category.name + } + .also { tab -> + tabLayout.addTab(tab) + } } + tabLayout.addOnTabSelectedListener(tabLayoutSelectionListener) viewModel.currentSection.observe(this, Observer { section -> section?.let { diff --git a/vector/src/main/java/im/vector/riotx/features/reactions/EmojiRecyclerAdapter.kt b/vector/src/main/java/im/vector/riotx/features/reactions/EmojiRecyclerAdapter.kt index a955189b17..efccb9c917 100644 --- a/vector/src/main/java/im/vector/riotx/features/reactions/EmojiRecyclerAdapter.kt +++ b/vector/src/main/java/im/vector/riotx/features/reactions/EmojiRecyclerAdapter.kt @@ -40,12 +40,11 @@ import kotlin.math.abs /** * * TODO: Configure Span using available width and emoji size - * TODO: Search * TODO: Performances * TODO: Scroll to section - Find a way to snap section to the top */ class EmojiRecyclerAdapter @Inject constructor( - private val dataSource: EmojiDataSource? + private val dataSource: EmojiDataSource ) : RecyclerView.Adapter() { @@ -70,13 +69,12 @@ class EmojiRecyclerAdapter @Inject constructor( private val itemClickListener = View.OnClickListener { view -> mRecyclerView?.getChildLayoutPosition(view)?.let { itemPosition -> if (itemPosition != RecyclerView.NO_POSITION) { - val categories = dataSource?.rawData?.categories ?: return@OnClickListener val sectionNumber = getSectionForAbsoluteIndex(itemPosition) if (!isSection(itemPosition)) { - val sectionMojis = categories[sectionNumber].emojis + val sectionMojis = dataSource.rawData.categories[sectionNumber].emojis val sectionOffset = getSectionOffset(sectionNumber) val emoji = sectionMojis[itemPosition - sectionOffset] - val item = dataSource.rawData!!.emojis.getValue(emoji).emoji + val item = dataSource.rawData.emojis.getValue(emoji).emoji reactionClickListener?.onReactionSelected(item) } } @@ -117,7 +115,7 @@ class EmojiRecyclerAdapter @Inject constructor( } fun scrollToSection(section: Int) { - if (section < 0 || section >= dataSource?.rawData?.categories?.size ?: 0) { + if (section < 0 || section >= dataSource.rawData.categories.size) { // ignore return } @@ -149,14 +147,12 @@ class EmojiRecyclerAdapter @Inject constructor( } private fun isSection(position: Int): Boolean { - dataSource?.rawData?.categories?.let { categories -> - var sectionOffset = 1 - var lastItemInSection: Int - for (category in categories) { - lastItemInSection = sectionOffset + category.emojis.size - 1 - if (position == sectionOffset - 1) return true - sectionOffset = lastItemInSection + 2 - } + var sectionOffset = 1 + var lastItemInSection: Int + dataSource.rawData.categories.forEach { category -> + lastItemInSection = sectionOffset + category.emojis.size - 1 + if (position == sectionOffset - 1) return true + sectionOffset = lastItemInSection + 2 } return false } @@ -165,13 +161,11 @@ class EmojiRecyclerAdapter @Inject constructor( var sectionOffset = 1 var lastItemInSection: Int var index = 0 - dataSource?.rawData?.categories?.let { - for (category in it) { - lastItemInSection = sectionOffset + category.emojis.size - 1 - if (position <= lastItemInSection) return index - sectionOffset = lastItemInSection + 2 - index++ - } + dataSource.rawData.categories.forEach { category -> + lastItemInSection = sectionOffset + category.emojis.size - 1 + if (position <= lastItemInSection) return index + sectionOffset = lastItemInSection + 2 + index++ } return index } @@ -180,36 +174,32 @@ class EmojiRecyclerAdapter @Inject constructor( // Todo cache this for fast access var sectionOffset = 1 var lastItemInSection: Int - dataSource?.rawData?.categories?.let { - for ((index, category) in it.withIndex()) { - lastItemInSection = sectionOffset + category.emojis.size - 1 - if (section == index) return sectionOffset - sectionOffset = lastItemInSection + 2 - } + dataSource.rawData.categories.forEachIndexed { index, category -> + lastItemInSection = sectionOffset + category.emojis.size - 1 + if (section == index) return sectionOffset + sectionOffset = lastItemInSection + 2 } return sectionOffset } override fun onBindViewHolder(holder: ViewHolder, position: Int) { beginTraceSession("MyAdapter.onBindViewHolder") - dataSource?.rawData?.categories?.let { categories -> - val sectionNumber = getSectionForAbsoluteIndex(position) - if (isSection(position)) { - holder.bind(categories[sectionNumber].name) - } else { - val sectionMojis = categories[sectionNumber].emojis - val sectionOffset = getSectionOffset(sectionNumber) - val emoji = sectionMojis[position - sectionOffset] - val item = dataSource.rawData!!.emojis[emoji]!!.emoji - (holder as EmojiViewHolder).data = item - if (scrollState != ScrollState.SETTLING || !isFastScroll) { + val sectionNumber = getSectionForAbsoluteIndex(position) + if (isSection(position)) { + holder.bind(dataSource.rawData.categories[sectionNumber].name) + } else { + val sectionMojis = dataSource.rawData.categories[sectionNumber].emojis + val sectionOffset = getSectionOffset(sectionNumber) + val emoji = sectionMojis[position - sectionOffset] + val item = dataSource.rawData.emojis[emoji]!!.emoji + (holder as EmojiViewHolder).data = item + if (scrollState != ScrollState.SETTLING || !isFastScroll) { // Log.i("PERF","Bind with draw at position:$position") - holder.bind(item) - } else { + holder.bind(item) + } else { // Log.i("PERF","Bind without draw at position:$position") - toUpdateWhenNotBusy.add(item to holder) - holder.bind(null) - } + toUpdateWhenNotBusy.add(item to holder) + holder.bind(null) } } endTraceSession() @@ -230,15 +220,8 @@ class EmojiRecyclerAdapter @Inject constructor( super.onViewRecycled(holder) } - override fun getItemCount(): Int { - return dataSource?.rawData?.categories?.let { - var count = /*number of sections*/ it.size - for (ad in it) { - count += ad.emojis.size - } - count - } ?: 0 - } + override fun getItemCount() = dataSource.rawData.categories + .sumBy { emojiCategory -> 1 /* Section */ + emojiCategory.emojis.size } abstract class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { abstract fun bind(s: String?) diff --git a/vector/src/main/java/im/vector/riotx/features/reactions/EmojiSearchResultItem.kt b/vector/src/main/java/im/vector/riotx/features/reactions/EmojiSearchResultItem.kt index fd0a6d70ff..55bf29e25f 100644 --- a/vector/src/main/java/im/vector/riotx/features/reactions/EmojiSearchResultItem.kt +++ b/vector/src/main/java/im/vector/riotx/features/reactions/EmojiSearchResultItem.kt @@ -46,7 +46,7 @@ abstract class EmojiSearchResultItem : EpoxyModelWithHolder - prev - && (it.keywords?.any { it.contains(q, true) } - ?: false) - }) - } ?: emptyList() + // First add emojis with name matching query, sorted by name + // Then emojis with keyword matching any of the word in the query, sorted by name + results = dataSource.rawData.emojis + .values + .filter { emojiItem -> + emojiItem.name.contains(action.queryString, true) + } + .sortedBy { it.name } + + dataSource.rawData.emojis + .values + .filter { emojiItem -> + words.fold(true, { prev, word -> + prev && emojiItem.keywords.any { keyword -> keyword.contains(word, true) } + }) + } + .sortedBy { it.name } ) } } diff --git a/vector/src/main/java/im/vector/riotx/features/reactions/data/EmojiDataSource.kt b/vector/src/main/java/im/vector/riotx/features/reactions/data/EmojiDataSource.kt index c9d683a2b9..a326828112 100644 --- a/vector/src/main/java/im/vector/riotx/features/reactions/data/EmojiDataSource.kt +++ b/vector/src/main/java/im/vector/riotx/features/reactions/data/EmojiDataSource.kt @@ -15,31 +15,22 @@ */ package im.vector.riotx.features.reactions.data -import android.content.Context +import android.content.res.Resources import com.squareup.moshi.Moshi import im.vector.riotx.R import im.vector.riotx.core.di.ScreenScope -import timber.log.Timber import javax.inject.Inject -import kotlin.system.measureTimeMillis @ScreenScope class EmojiDataSource @Inject constructor( - context: Context + resources: Resources ) { - - var rawData: EmojiData? = null - - init { - measureTimeMillis { - context.resources.openRawResource(R.raw.emoji_picker_datasource).use { input -> - val moshi = Moshi.Builder().build() - val jsonAdapter = moshi.adapter(EmojiData::class.java) - val inputAsString = input.bufferedReader().use { it.readText() } - this.rawData = jsonAdapter.fromJson(inputAsString) + val rawData = resources.openRawResource(R.raw.emoji_picker_datasource) + .use { input -> + Moshi.Builder() + .build() + .adapter(EmojiData::class.java) + .fromJson(input.bufferedReader().use { it.readText() }) } - }.also { - Timber.e("Emoji: $it millis") - } - } + ?: EmojiData(emptyList(), emptyMap(), emptyMap()) } diff --git a/vector/src/main/java/im/vector/riotx/features/reactions/data/EmojiItem.kt b/vector/src/main/java/im/vector/riotx/features/reactions/data/EmojiItem.kt index fdd9a80911..caf6672964 100644 --- a/vector/src/main/java/im/vector/riotx/features/reactions/data/EmojiItem.kt +++ b/vector/src/main/java/im/vector/riotx/features/reactions/data/EmojiItem.kt @@ -40,7 +40,7 @@ import com.squareup.moshi.JsonClass data class EmojiItem( @Json(name = "a") val name: String, @Json(name = "b") val unicode: String, - @Json(name = "j") val keywords: List? + @Json(name = "j") val keywords: List = emptyList() ) { // Cannot be private... var cache: String? = null From 3ac53d20e9d7fe01a2698cd5092f155f60e410a6 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 10 Dec 2019 01:05:20 +0100 Subject: [PATCH 20/22] Bugfix: react several times with the same reaction was possible (was a TODO). --- .../room/model/relation/RelationService.kt | 1 + .../room/relation/DefaultRelationService.kt | 31 ++++++++++++++++--- .../home/room/detail/RoomDetailFragment.kt | 6 +--- .../reactions/EmojiReactionPickerActivity.kt | 10 ++++-- 4 files changed, 36 insertions(+), 12 deletions(-) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/relation/RelationService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/relation/RelationService.kt index 385699b4db..b3dd1c6f22 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/relation/RelationService.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/relation/RelationService.kt @@ -50,6 +50,7 @@ interface RelationService { /** * Sends a reaction (emoji) to the targetedEvent. + * It has no effect if the user has already added the same reaction to the event. * @param targetEventId the id of the event being reacted * @param reaction the reaction (preferably emoji) */ diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/relation/DefaultRelationService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/relation/DefaultRelationService.kt index db3b6100a0..8731045e14 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/relation/DefaultRelationService.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/relation/DefaultRelationService.kt @@ -30,10 +30,13 @@ import im.vector.matrix.android.api.session.room.model.message.MessageType import im.vector.matrix.android.api.session.room.model.relation.RelationService import im.vector.matrix.android.api.session.room.timeline.TimelineEvent import im.vector.matrix.android.api.util.Cancelable +import im.vector.matrix.android.api.util.NoOpCancellable import im.vector.matrix.android.api.util.Optional import im.vector.matrix.android.api.util.toOptional +import im.vector.matrix.android.internal.database.mapper.TimelineEventMapper import im.vector.matrix.android.internal.database.mapper.asDomain import im.vector.matrix.android.internal.database.model.EventAnnotationsSummaryEntity +import im.vector.matrix.android.internal.database.model.TimelineEventEntity import im.vector.matrix.android.internal.database.query.where import im.vector.matrix.android.internal.di.UserId import im.vector.matrix.android.internal.session.room.send.EncryptEventWorker @@ -44,6 +47,7 @@ import im.vector.matrix.android.internal.session.room.timeline.TimelineSendEvent import im.vector.matrix.android.internal.task.TaskExecutor import im.vector.matrix.android.internal.task.configureWith import im.vector.matrix.android.internal.util.CancelableWork +import im.vector.matrix.android.internal.util.fetchCopyMap import im.vector.matrix.android.internal.worker.WorkerParamsFactory import timber.log.Timber @@ -54,6 +58,7 @@ internal class DefaultRelationService @AssistedInject constructor(@Assisted priv private val cryptoService: CryptoService, private val findReactionEventForUndoTask: FindReactionEventForUndoTask, private val fetchEditHistoryTask: FetchEditHistoryTask, + private val timelineEventMapper: TimelineEventMapper, private val monarchy: Monarchy, private val taskExecutor: TaskExecutor) : RelationService { @@ -64,11 +69,27 @@ internal class DefaultRelationService @AssistedInject constructor(@Assisted priv } override fun sendReaction(targetEventId: String, reaction: String): Cancelable { - val event = eventFactory.createReactionEvent(roomId, targetEventId, reaction) - .also { saveLocalEcho(it) } - val sendRelationWork = createSendEventWork(event, true) - TimelineSendEventWorkCommon.postWork(context, roomId, sendRelationWork) - return CancelableWork(context, sendRelationWork.id) + return if (monarchy + .fetchCopyMap( + { realm -> + TimelineEventEntity.where(realm, roomId, targetEventId).findFirst() + }, + { entity, _ -> + timelineEventMapper.map(entity) + }) + ?.annotations + ?.reactionsSummary + .orEmpty() + .none { it.addedByMe && it.key == reaction }) { + val event = eventFactory.createReactionEvent(roomId, targetEventId, reaction) + .also { saveLocalEcho(it) } + val sendRelationWork = createSendEventWork(event, true) + TimelineSendEventWorkCommon.postWork(context, roomId, sendRelationWork) + CancelableWork(context, sendRelationWork.id) + } else { + Timber.w("Reaction already added") + NoOpCancellable + } } override fun undoReaction(targetEventId: String, reaction: String): Cancelable { diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailFragment.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailFragment.kt index e1bb24eb8f..80f54a9c1f 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailFragment.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailFragment.kt @@ -450,11 +450,7 @@ class RoomDetailFragment @Inject constructor( if (!hasBeenHandled && resultCode == RESULT_OK && data != null) { when (requestCode) { REACTION_SELECT_REQUEST_CODE -> { - val eventId = data.getStringExtra(EmojiReactionPickerActivity.EXTRA_EVENT_ID) - ?: return - val reaction = data.getStringExtra(EmojiReactionPickerActivity.EXTRA_REACTION_RESULT) - ?: return - // TODO check if already reacted with that? + val (eventId, reaction) = EmojiReactionPickerActivity.getOutput(data) ?: return roomDetailViewModel.handle(RoomDetailAction.SendReaction(eventId, reaction)) } } diff --git a/vector/src/main/java/im/vector/riotx/features/reactions/EmojiReactionPickerActivity.kt b/vector/src/main/java/im/vector/riotx/features/reactions/EmojiReactionPickerActivity.kt index 96536e1f16..562ad6f5b2 100644 --- a/vector/src/main/java/im/vector/riotx/features/reactions/EmojiReactionPickerActivity.kt +++ b/vector/src/main/java/im/vector/riotx/features/reactions/EmojiReactionPickerActivity.kt @@ -203,13 +203,19 @@ class EmojiReactionPickerActivity : VectorBaseActivity(), companion object { - const val EXTRA_EVENT_ID = "EXTRA_EVENT_ID" - const val EXTRA_REACTION_RESULT = "EXTRA_REACTION_RESULT" + private const val EXTRA_EVENT_ID = "EXTRA_EVENT_ID" + private const val EXTRA_REACTION_RESULT = "EXTRA_REACTION_RESULT" fun intent(context: Context, eventId: String): Intent { val intent = Intent(context, EmojiReactionPickerActivity::class.java) intent.putExtra(EXTRA_EVENT_ID, eventId) return intent } + + fun getOutput(data: Intent): Pair? { + val eventId = data.getStringExtra(EXTRA_EVENT_ID) ?: return null + val reaction = data.getStringExtra(EXTRA_REACTION_RESULT) ?: return null + return eventId to reaction + } } } From a9e2c31c32fcd3c602a2709f5247131c29235946 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 10 Dec 2019 01:08:05 +0100 Subject: [PATCH 21/22] Remove log for privacy --- .../timeline/edithistory/ViewEditHistoryEpoxyController.kt | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/edithistory/ViewEditHistoryEpoxyController.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/edithistory/ViewEditHistoryEpoxyController.kt index 4661d8f8cd..1a5c6db270 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/edithistory/ViewEditHistoryEpoxyController.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/edithistory/ViewEditHistoryEpoxyController.kt @@ -28,17 +28,16 @@ import im.vector.matrix.android.api.session.events.model.toModel import im.vector.matrix.android.api.session.room.model.message.MessageTextContent import im.vector.matrix.android.api.util.ContentUtils.extractUsefulTextFromReply import im.vector.riotx.R +import im.vector.riotx.core.date.VectorDateFormatter import im.vector.riotx.core.extensions.localDateTime import im.vector.riotx.core.ui.list.genericFooterItem import im.vector.riotx.core.ui.list.genericItem import im.vector.riotx.core.ui.list.genericItemHeader import im.vector.riotx.core.ui.list.genericLoaderItem -import im.vector.riotx.core.date.VectorDateFormatter import im.vector.riotx.features.html.EventHtmlRenderer import me.gujun.android.span.span import name.fraser.neil.plaintext.diff_match_patch -import timber.log.Timber -import java.util.Calendar +import java.util.* /** * Epoxy controller for edit history list @@ -104,9 +103,7 @@ class ViewEditHistoryEpoxyController(private val context: Context, ?: nContent.first val dmp = diff_match_patch() val diff = dmp.diff_main(nextBody.toString(), body.toString()) - Timber.e("#### Diff: $diff") dmp.diff_cleanupSemantic(diff) - Timber.e("#### Diff: $diff") spannedDiff = span { diff.map { when (it.operation) { From 3a761be6b4c894650705ba52679694d7e495e5ea Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 10 Dec 2019 01:27:12 +0100 Subject: [PATCH 22/22] Last cleanup --- vector/src/main/java/im/vector/riotx/core/utils/Emoji.kt | 7 ------- .../im/vector/riotx/features/home/HomeDetailFragment.kt | 1 - .../timeline/edithistory/ViewEditHistoryBottomSheet.kt | 1 - .../riotx/features/reactions/EmojiChooserFragment.kt | 9 +++------ .../im/vector/riotx/features/reactions/data/EmojiItem.kt | 1 - vector/src/main/res/layout/emoji_chooser_fragment.xml | 6 ++---- 6 files changed, 5 insertions(+), 20 deletions(-) diff --git a/vector/src/main/java/im/vector/riotx/core/utils/Emoji.kt b/vector/src/main/java/im/vector/riotx/core/utils/Emoji.kt index 3b16c3ea22..f9e5654726 100644 --- a/vector/src/main/java/im/vector/riotx/core/utils/Emoji.kt +++ b/vector/src/main/java/im/vector/riotx/core/utils/Emoji.kt @@ -16,13 +16,6 @@ package im.vector.riotx.core.utils -import android.content.Context -import com.squareup.moshi.Moshi -import im.vector.riotx.R -import im.vector.riotx.features.reactions.data.EmojiData -import kotlinx.coroutines.GlobalScope -import kotlinx.coroutines.launch -import timber.log.Timber import java.util.regex.Pattern private val emojisPattern = Pattern.compile("((?:[\uD83C\uDF00-\uD83D\uDDFF]" + diff --git a/vector/src/main/java/im/vector/riotx/features/home/HomeDetailFragment.kt b/vector/src/main/java/im/vector/riotx/features/home/HomeDetailFragment.kt index f60db12459..ac8d429cb1 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/HomeDetailFragment.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/HomeDetailFragment.kt @@ -25,7 +25,6 @@ import com.airbnb.mvrx.fragmentViewModel import com.airbnb.mvrx.withState import com.google.android.material.bottomnavigation.BottomNavigationItemView import com.google.android.material.bottomnavigation.BottomNavigationMenuView -import im.vector.matrix.android.api.session.Session import im.vector.matrix.android.api.session.crypto.keysbackup.KeysBackupState import im.vector.matrix.android.api.session.group.model.GroupSummary import im.vector.riotx.R diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/edithistory/ViewEditHistoryBottomSheet.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/edithistory/ViewEditHistoryBottomSheet.kt index 845d2a2b7a..8aa7c8561c 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/edithistory/ViewEditHistoryBottomSheet.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/edithistory/ViewEditHistoryBottomSheet.kt @@ -19,7 +19,6 @@ import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup -import androidx.recyclerview.widget.DividerItemDecoration import androidx.recyclerview.widget.RecyclerView import butterknife.BindView import butterknife.ButterKnife diff --git a/vector/src/main/java/im/vector/riotx/features/reactions/EmojiChooserFragment.kt b/vector/src/main/java/im/vector/riotx/features/reactions/EmojiChooserFragment.kt index bbedeff552..1dc9f34924 100644 --- a/vector/src/main/java/im/vector/riotx/features/reactions/EmojiChooserFragment.kt +++ b/vector/src/main/java/im/vector/riotx/features/reactions/EmojiChooserFragment.kt @@ -18,10 +18,10 @@ package im.vector.riotx.features.reactions import android.os.Bundle import android.view.View import androidx.lifecycle.observe -import androidx.recyclerview.widget.RecyclerView import im.vector.riotx.R import im.vector.riotx.core.extensions.cleanup import im.vector.riotx.core.platform.VectorBaseFragment +import kotlinx.android.synthetic.main.emoji_chooser_fragment.* import javax.inject.Inject class EmojiChooserFragment @Inject constructor( @@ -41,10 +41,7 @@ class EmojiChooserFragment @Inject constructor( emojiRecyclerAdapter.reactionClickListener = this emojiRecyclerAdapter.interactionListener = this - (view as? RecyclerView)?.let { - it.adapter = emojiRecyclerAdapter - it.adapter?.notifyDataSetChanged() - } + emojiRecyclerView.adapter = emojiRecyclerAdapter viewModel.moveToSection.observe(viewLifecycleOwner) { section -> emojiRecyclerAdapter.scrollToSection(section) @@ -60,7 +57,7 @@ class EmojiChooserFragment @Inject constructor( } override fun onDestroyView() { - (view as? RecyclerView)?.cleanup() + emojiRecyclerView.cleanup() emojiRecyclerAdapter.reactionClickListener = null emojiRecyclerAdapter.interactionListener = null super.onDestroyView() diff --git a/vector/src/main/java/im/vector/riotx/features/reactions/data/EmojiItem.kt b/vector/src/main/java/im/vector/riotx/features/reactions/data/EmojiItem.kt index caf6672964..57083e8467 100644 --- a/vector/src/main/java/im/vector/riotx/features/reactions/data/EmojiItem.kt +++ b/vector/src/main/java/im/vector/riotx/features/reactions/data/EmojiItem.kt @@ -72,4 +72,3 @@ data class EmojiItem( } } } - diff --git a/vector/src/main/res/layout/emoji_chooser_fragment.xml b/vector/src/main/res/layout/emoji_chooser_fragment.xml index 197e7ce6ef..586411f3d5 100644 --- a/vector/src/main/res/layout/emoji_chooser_fragment.xml +++ b/vector/src/main/res/layout/emoji_chooser_fragment.xml @@ -1,7 +1,7 @@ - - \ No newline at end of file + tools:spanCount="10" />