Merge pull request #586 from vector-im/feature/persist_tab

Persist opened tab between session (i.e. after application restart)
This commit is contained in:
Benoit Marty 2019-09-24 16:12:28 +02:00 committed by GitHub
commit d1a61f29e4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 152 additions and 74 deletions

View File

@ -5,7 +5,7 @@ Features:
-
Improvements:
-
- Persist active tab between sessions (#503)
Other changes:
-

View File

@ -117,13 +117,13 @@ internal abstract class CryptoModule {
abstract fun bindGetDevicesTask(getDevicesTask: DefaultGetDevicesTask): GetDevicesTask
@Binds
abstract fun bindSetDeviceNameTask(getDevicesTask: DefaultSetDeviceNameTask): SetDeviceNameTask
abstract fun bindSetDeviceNameTask(setDeviceNameTask: DefaultSetDeviceNameTask): SetDeviceNameTask
@Binds
abstract fun bindUploadKeysTask(getDevicesTask: DefaultUploadKeysTask): UploadKeysTask
abstract fun bindUploadKeysTask(uploadKeysTask: DefaultUploadKeysTask): UploadKeysTask
@Binds
abstract fun bindDownloadKeysForUsersTask(downloadKeysForUsers: DefaultDownloadKeysForUsers): DownloadKeysForUsersTask
abstract fun bindDownloadKeysForUsersTask(downloadKeysForUsersTask: DefaultDownloadKeysForUsers): DownloadKeysForUsersTask
@Binds
abstract fun bindCreateKeysBackupVersionTask(createKeysBackupVersionTask: DefaultCreateKeysBackupVersionTask): CreateKeysBackupVersionTask
@ -135,10 +135,10 @@ internal abstract class CryptoModule {
abstract fun bindDeleteRoomSessionDataTask(deleteRoomSessionDataTask: DefaultDeleteRoomSessionDataTask): DeleteRoomSessionDataTask
@Binds
abstract fun bindDeleteRoomSessionsDataTask(deleteRoomSessionDataTask: DefaultDeleteRoomSessionsDataTask): DeleteRoomSessionsDataTask
abstract fun bindDeleteRoomSessionsDataTask(deleteRoomSessionsDataTask: DefaultDeleteRoomSessionsDataTask): DeleteRoomSessionsDataTask
@Binds
abstract fun bindDeleteSessionsDataTask(deleteRoomSessionDataTask: DefaultDeleteSessionsDataTask): DeleteSessionsDataTask
abstract fun bindDeleteSessionsDataTask(deleteSessionsDataTask: DefaultDeleteSessionsDataTask): DeleteSessionsDataTask
@Binds
abstract fun bindGetKeysBackupLastVersionTask(getKeysBackupLastVersionTask: DefaultGetKeysBackupLastVersionTask): GetKeysBackupLastVersionTask
@ -150,19 +150,19 @@ internal abstract class CryptoModule {
abstract fun bindGetRoomSessionDataTask(getRoomSessionDataTask: DefaultGetRoomSessionDataTask): GetRoomSessionDataTask
@Binds
abstract fun bindGetRoomSessionsDataTask(getRoomSessionDataTask: DefaultGetRoomSessionsDataTask): GetRoomSessionsDataTask
abstract fun bindGetRoomSessionsDataTask(getRoomSessionsDataTask: DefaultGetRoomSessionsDataTask): GetRoomSessionsDataTask
@Binds
abstract fun bindGetSessionsDataTask(getRoomSessionDataTask: DefaultGetSessionsDataTask): GetSessionsDataTask
abstract fun bindGetSessionsDataTask(getSessionsDataTask: DefaultGetSessionsDataTask): GetSessionsDataTask
@Binds
abstract fun bindStoreRoomSessionDataTask(storeRoomSessionDataTask: DefaultStoreRoomSessionDataTask): StoreRoomSessionDataTask
@Binds
abstract fun bindStoreRoomSessionsDataTask(storeRoomSessionDataTask: DefaultStoreRoomSessionsDataTask): StoreRoomSessionsDataTask
abstract fun bindStoreRoomSessionsDataTask(storeRoomSessionsDataTask: DefaultStoreRoomSessionsDataTask): StoreRoomSessionsDataTask
@Binds
abstract fun bindStoreSessionsDataTask(storeRoomSessionDataTask: DefaultStoreSessionsDataTask): StoreSessionsDataTask
abstract fun bindStoreSessionsDataTask(storeSessionsDataTask: DefaultStoreSessionsDataTask): StoreSessionsDataTask
@Binds
abstract fun bindUpdateKeysBackupVersionTask(updateKeysBackupVersionTask: DefaultUpdateKeysBackupVersionTask): UpdateKeysBackupVersionTask

View File

@ -162,7 +162,7 @@ internal abstract class SessionModule {
@Binds
@IntoSet
abstract fun bindEventRelationsAggregationUpdater(groupSummaryUpdater: EventRelationsAggregationUpdater): LiveEntityObserver
abstract fun bindEventRelationsAggregationUpdater(eventRelationsAggregationUpdater: EventRelationsAggregationUpdater): LiveEntityObserver
@Binds
@IntoSet

View File

@ -43,7 +43,7 @@ internal abstract class FilterModule {
abstract fun bindFilterService(filterService: DefaultFilterService): FilterService
@Binds
abstract fun bindSaveFilterTask(saveFilterTask_Factory: DefaultSaveFilterTask): SaveFilterTask
abstract fun bindSaveFilterTask(saveFilterTask: DefaultSaveFilterTask): SaveFilterTask
}

View File

@ -26,8 +26,8 @@ import javax.inject.Inject
internal interface GetPushersTask : Task<Unit, Unit>
internal class DefaultGetPusherTask @Inject constructor(private val pushersAPI: PushersAPI,
private val monarchy: Monarchy) : GetPushersTask {
internal class DefaultGetPushersTask @Inject constructor(private val pushersAPI: PushersAPI,
private val monarchy: Monarchy) : GetPushersTask {
override suspend fun execute(params: Unit) {
val response = executeRequest<GetPushersResponse> {

View File

@ -54,7 +54,7 @@ internal abstract class PushersModule {
abstract fun bindConditionResolver(conditionResolver: DefaultConditionResolver): ConditionResolver
@Binds
abstract fun bindGetPushersTask(getPusherTask: DefaultGetPusherTask): GetPushersTask
abstract fun bindGetPushersTask(getPushersTask: DefaultGetPushersTask): GetPushersTask
@Binds
abstract fun bindGetPushRulesTask(getPushRulesTask: DefaultGetPushRulesTask): GetPushRulesTask

View File

@ -127,5 +127,5 @@ internal abstract class RoomModule {
abstract fun bindFileService(fileService: DefaultFileService): FileService
@Binds
abstract fun bindFetchEditHistoryTask(editHistoryTask: DefaultFetchEditHistoryTask): FetchEditHistoryTask
abstract fun bindFetchEditHistoryTask(fetchEditHistoryTask: DefaultFetchEditHistoryTask): FetchEditHistoryTask
}

View File

@ -65,6 +65,7 @@ import im.vector.riotx.features.roomdirectory.picker.RoomDirectoryPickerFragment
import im.vector.riotx.features.roomdirectory.roompreview.RoomPreviewNoPreviewFragment
import im.vector.riotx.features.settings.*
import im.vector.riotx.features.settings.push.PushGatewaysFragment
import im.vector.riotx.features.ui.UiStateRepository
@Component(dependencies = [VectorComponent::class], modules = [AssistedInjectModule::class, ViewModelModule::class, HomeModule::class])
@ScreenScope
@ -80,6 +81,8 @@ interface ScreenComponent {
fun navigator(): Navigator
fun uiStateRepository(): UiStateRepository
fun inject(activity: HomeActivity)
fun inject(roomDetailFragment: RoomDetailFragment)

View File

@ -42,13 +42,14 @@ import im.vector.riotx.features.rageshake.BugReporter
import im.vector.riotx.features.rageshake.VectorFileLogger
import im.vector.riotx.features.rageshake.VectorUncaughtExceptionHandler
import im.vector.riotx.features.settings.VectorPreferences
import im.vector.riotx.features.ui.UiStateRepository
import javax.inject.Singleton
@Component(modules = [VectorModule::class])
@Singleton
interface VectorComponent {
fun inject(vectorApplication: NotificationBroadcastReceiver)
fun inject(notificationBroadcastReceiver: NotificationBroadcastReceiver)
fun inject(vectorApplication: VectorApplication)
@ -64,7 +65,7 @@ interface VectorComponent {
fun resources(): Resources
fun dimensionUtils(): DimensionConverter
fun dimensionConverter(): DimensionConverter
fun vectorConfiguration(): VectorConfiguration
@ -106,6 +107,8 @@ interface VectorComponent {
fun vectorFileLogger(): VectorFileLogger
fun uiStateRepository(): UiStateRepository
@Component.Factory
interface Factory {
fun create(@BindsInstance context: Context): VectorComponent

View File

@ -28,6 +28,8 @@ import im.vector.matrix.android.api.auth.Authenticator
import im.vector.matrix.android.api.session.Session
import im.vector.riotx.features.navigation.DefaultNavigator
import im.vector.riotx.features.navigation.Navigator
import im.vector.riotx.features.ui.SharedPreferencesUiStateRepository
import im.vector.riotx.features.ui.UiStateRepository
@Module
abstract class VectorModule {
@ -62,7 +64,7 @@ abstract class VectorModule {
@Provides
@JvmStatic
fun providesAuthenticator(matrix: Matrix): Authenticator{
fun providesAuthenticator(matrix: Matrix): Authenticator {
return matrix.authenticator()
}
}
@ -70,5 +72,7 @@ abstract class VectorModule {
@Binds
abstract fun bindNavigator(navigator: DefaultNavigator): Navigator
@Binds
abstract fun bindUiStateRepository(uiStateRepository: SharedPreferencesUiStateRepository): UiStateRepository
}

View File

@ -16,7 +16,6 @@
package im.vector.riotx.features.home
import android.app.ProgressDialog
import android.content.Context
import android.content.Intent
import android.os.Bundle
@ -66,8 +65,6 @@ class HomeActivity : VectorBaseActivity(), ToolbarConfigurable {
@Inject lateinit var pushManager: PushersManager
@Inject lateinit var notificationDrawerManager: NotificationDrawerManager
private var progress: ProgressDialog? = null
private val drawerListener = object : DrawerLayout.SimpleDrawerListener() {
override fun onDrawerStateChanged(newState: Int) {
hideKeyboard()
@ -93,18 +90,6 @@ class HomeActivity : VectorBaseActivity(), ToolbarConfigurable {
replaceFragment(homeDrawerFragment, R.id.homeDrawerFragmentContainer)
}
homeActivityViewModel.isLoading.observe(this, Observer<Boolean> {
// TODO better UI
if (it) {
progress?.dismiss()
progress = ProgressDialog(this)
progress?.setMessage(getString(R.string.room_recents_create_room))
progress?.show()
} else {
progress?.dismiss()
}
})
navigationViewModel.navigateTo.observeEvent(this) { navigation ->
when (navigation) {
is Navigation.OpenDrawer -> drawerLayout.openDrawer(GravityCompat.START)

View File

@ -16,8 +16,6 @@
package im.vector.riotx.features.home
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import arrow.core.Option
import com.airbnb.mvrx.ActivityViewModelContext
import com.airbnb.mvrx.MvRxState
@ -25,11 +23,9 @@ import com.airbnb.mvrx.MvRxViewModelFactory
import com.airbnb.mvrx.ViewModelContext
import com.squareup.inject.assisted.Assisted
import com.squareup.inject.assisted.AssistedInject
import im.vector.matrix.android.api.MatrixCallback
import im.vector.matrix.android.api.session.Session
import im.vector.matrix.android.api.session.group.model.GroupSummary
import im.vector.matrix.android.api.session.room.model.RoomSummary
import im.vector.matrix.android.api.session.room.model.create.CreateRoomParams
import im.vector.matrix.rx.rx
import im.vector.riotx.core.platform.VectorViewModel
import im.vector.riotx.features.home.group.ALL_COMMUNITIES_GROUP_ID
@ -61,10 +57,6 @@ class HomeActivityViewModel @AssistedInject constructor(@Assisted initialState:
}
private val _isLoading = MutableLiveData<Boolean>()
val isLoading: LiveData<Boolean>
get() = _isLoading
init {
session.addListener(this)
observeRoomAndGroup()
@ -93,7 +85,7 @@ class HomeActivityViewModel @AssistedInject constructor(@Assisted initialState:
.filter { !it.isDirect }
.filter {
selectedGroup?.groupId == ALL_COMMUNITIES_GROUP_ID
|| selectedGroup?.roomIds?.contains(it.roomId) ?: true
|| selectedGroup?.roomIds?.contains(it.roomId) ?: true
}
filteredDirectRooms + filteredGroupRooms
}
@ -104,21 +96,6 @@ class HomeActivityViewModel @AssistedInject constructor(@Assisted initialState:
.disposeOnClear()
}
fun createRoom(createRoomParams: CreateRoomParams = CreateRoomParams()) {
_isLoading.value = true
session.createRoom(createRoomParams, object : MatrixCallback<String> {
override fun onSuccess(data: String) {
_isLoading.value = false
}
override fun onFailure(failure: Throwable) {
_isLoading.value = false
super.onFailure(failure)
}
})
}
override fun onCleared() {
super.onCleared()

View File

@ -51,8 +51,6 @@ data class HomeDetailParams(
) : Parcelable
private const val CURRENT_DISPLAY_MODE = "CURRENT_DISPLAY_MODE"
private const val INDEX_CATCHUP = 0
private const val INDEX_PEOPLE = 1
private const val INDEX_ROOMS = 2
@ -61,7 +59,6 @@ class HomeDetailFragment : VectorBaseFragment(), KeysBackupBanner.Delegate {
private val params: HomeDetailParams by args()
private val unreadCounterBadgeViews = arrayListOf<UnreadCounterBadgeView>()
private lateinit var currentDisplayMode: RoomListFragment.DisplayMode
private val viewModel: HomeDetailViewModel by fragmentViewModel()
private lateinit var navigationViewModel: HomeNavigationViewModel
@ -80,15 +77,16 @@ class HomeDetailFragment : VectorBaseFragment(), KeysBackupBanner.Delegate {
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
currentDisplayMode = savedInstanceState?.getSerializable(CURRENT_DISPLAY_MODE) as? RoomListFragment.DisplayMode
?: RoomListFragment.DisplayMode.HOME
navigationViewModel = ViewModelProviders.of(requireActivity()).get(HomeNavigationViewModel::class.java)
switchDisplayMode(currentDisplayMode)
setupBottomNavigationView()
setupToolbar()
setupKeysBackupBanner()
viewModel.selectSubscribe(this, HomeDetailViewState::displayMode) { displayMode ->
switchDisplayMode(displayMode)
}
}
private fun setupKeysBackupBanner() {
@ -126,11 +124,6 @@ class HomeDetailFragment : VectorBaseFragment(), KeysBackupBanner.Delegate {
}
override fun onSaveInstanceState(outState: Bundle) {
outState.putSerializable(CURRENT_DISPLAY_MODE, currentDisplayMode)
super.onSaveInstanceState(outState)
}
private fun setupToolbar() {
val parentActivity = vectorBaseActivity
if (parentActivity is ToolbarConfigurable) {
@ -156,10 +149,7 @@ class HomeDetailFragment : VectorBaseFragment(), KeysBackupBanner.Delegate {
R.id.bottom_action_rooms -> RoomListFragment.DisplayMode.ROOMS
else -> RoomListFragment.DisplayMode.HOME
}
if (currentDisplayMode != displayMode) {
currentDisplayMode = displayMode
switchDisplayMode(displayMode)
}
viewModel.switchDisplayMode(displayMode)
true
}
@ -176,6 +166,12 @@ class HomeDetailFragment : VectorBaseFragment(), KeysBackupBanner.Delegate {
private fun switchDisplayMode(displayMode: RoomListFragment.DisplayMode) {
groupToolbarTitleView.setText(displayMode.titleRes)
updateSelectedFragment(displayMode)
// Update the navigation view (for when we restore the tabs)
bottomNavigationView.selectedItemId = when (displayMode) {
RoomListFragment.DisplayMode.PEOPLE -> R.id.bottom_action_people
RoomListFragment.DisplayMode.ROOMS -> R.id.bottom_action_rooms
else -> R.id.bottom_action_home
}
}
private fun updateSelectedFragment(displayMode: RoomListFragment.DisplayMode) {

View File

@ -23,14 +23,19 @@ import com.squareup.inject.assisted.Assisted
import com.squareup.inject.assisted.AssistedInject
import im.vector.matrix.android.api.session.Session
import im.vector.matrix.rx.rx
import im.vector.riotx.core.di.HasScreenInjector
import im.vector.riotx.core.platform.VectorViewModel
import im.vector.riotx.features.home.room.list.RoomListFragment
import im.vector.riotx.features.ui.UiStateRepository
import io.reactivex.schedulers.Schedulers
/**
* View model used to update the home bottom bar notification counts
* View model used to update the home bottom bar notification counts, observe the sync state and
* change the selected room list view
*/
class HomeDetailViewModel @AssistedInject constructor(@Assisted initialState: HomeDetailViewState,
private val session: Session,
private val uiStateRepository: UiStateRepository,
private val homeRoomListStore: HomeRoomListObservableStore)
: VectorViewModel<HomeDetailViewState>(initialState) {
@ -41,6 +46,13 @@ class HomeDetailViewModel @AssistedInject constructor(@Assisted initialState: Ho
companion object : MvRxViewModelFactory<HomeDetailViewModel, HomeDetailViewState> {
override fun initialState(viewModelContext: ViewModelContext): HomeDetailViewState? {
val uiStateRepository = (viewModelContext.activity as HasScreenInjector).injector().uiStateRepository()
return HomeDetailViewState(
displayMode = uiStateRepository.getDisplayMode()
)
}
@JvmStatic
override fun create(viewModelContext: ViewModelContext, state: HomeDetailViewState): HomeDetailViewModel? {
val fragment: HomeDetailFragment = (viewModelContext as FragmentViewModelContext).fragment()
@ -53,6 +65,16 @@ class HomeDetailViewModel @AssistedInject constructor(@Assisted initialState: Ho
observeRoomSummaries()
}
fun switchDisplayMode(displayMode: RoomListFragment.DisplayMode) = withState { state ->
if (state.displayMode != displayMode) {
setState {
copy(displayMode = displayMode)
}
uiStateRepository.storeDisplayMode(displayMode)
}
}
// PRIVATE METHODS *****************************************************************************
private fun observeSyncState() {

View File

@ -18,8 +18,10 @@ package im.vector.riotx.features.home
import com.airbnb.mvrx.MvRxState
import im.vector.matrix.android.api.session.sync.SyncState
import im.vector.riotx.features.home.room.list.RoomListFragment
data class HomeDetailViewState(
val displayMode: RoomListFragment.DisplayMode = RoomListFragment.DisplayMode.HOME,
val notificationCountCatchup: Int = 0,
val notificationHighlightCatchup: Boolean = false,
val notificationCountPeople: Int = 0,

View File

@ -0,0 +1,56 @@
/*
* 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.ui
import android.content.SharedPreferences
import androidx.core.content.edit
import im.vector.riotx.features.home.room.list.RoomListFragment
import javax.inject.Inject
/**
* This class is used to persist UI state across application restart
*/
class SharedPreferencesUiStateRepository @Inject constructor(private val sharedPreferences: SharedPreferences) : UiStateRepository {
override fun getDisplayMode(): RoomListFragment.DisplayMode {
return when (sharedPreferences.getInt(KEY_DISPLAY_MODE, VALUE_DISPLAY_MODE_CATCHUP)) {
VALUE_DISPLAY_MODE_PEOPLE -> RoomListFragment.DisplayMode.PEOPLE
VALUE_DISPLAY_MODE_ROOMS -> RoomListFragment.DisplayMode.ROOMS
else -> RoomListFragment.DisplayMode.HOME
}
}
override fun storeDisplayMode(displayMode: RoomListFragment.DisplayMode) {
sharedPreferences.edit {
putInt(KEY_DISPLAY_MODE,
when (displayMode) {
RoomListFragment.DisplayMode.PEOPLE -> VALUE_DISPLAY_MODE_PEOPLE
RoomListFragment.DisplayMode.ROOMS -> VALUE_DISPLAY_MODE_ROOMS
else -> VALUE_DISPLAY_MODE_CATCHUP
})
}
}
companion object {
private const val KEY_DISPLAY_MODE = "UI_STATE_DISPLAY_MODE"
private const val VALUE_DISPLAY_MODE_CATCHUP = 0
private const val VALUE_DISPLAY_MODE_PEOPLE = 1
private const val VALUE_DISPLAY_MODE_ROOMS = 2
}
}

View File

@ -0,0 +1,30 @@
/*
* 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.ui
import im.vector.riotx.features.home.room.list.RoomListFragment
/**
* This interface is used to persist UI state across application restart
*/
interface UiStateRepository {
fun getDisplayMode(): RoomListFragment.DisplayMode
fun storeDisplayMode(displayMode: RoomListFragment.DisplayMode)
}