Create a ViewState for HomeActivity

And disable the popup - WIP
This commit is contained in:
Benoit Marty 2020-06-09 12:21:02 +02:00 committed by Valere
parent 1365240f69
commit 48a30a7b82
6 changed files with 164 additions and 54 deletions

View file

@ -20,10 +20,13 @@ import androidx.lifecycle.LiveData
interface InitialSyncProgressService { interface InitialSyncProgressService {
fun getInitialSyncProgressStatus() : LiveData<Status?> fun getInitialSyncProgressStatus(): LiveData<Status>
data class Status( sealed class Status {
object Idle : Status()
data class Progressing(
@StringRes val statusText: Int, @StringRes val statusText: Int,
val percentProgress: Int = 0 val percentProgress: Int = 0
) ) : Status()
}
} }

View file

@ -25,11 +25,11 @@ import javax.inject.Inject
@SessionScope @SessionScope
class DefaultInitialSyncProgressService @Inject constructor() : InitialSyncProgressService { class DefaultInitialSyncProgressService @Inject constructor() : InitialSyncProgressService {
private var status = MutableLiveData<InitialSyncProgressService.Status>() private val status = MutableLiveData<InitialSyncProgressService.Status>()
private var rootTask: TaskInfo? = null private var rootTask: TaskInfo? = null
override fun getInitialSyncProgressStatus(): LiveData<InitialSyncProgressService.Status?> { override fun getInitialSyncProgressStatus(): LiveData<InitialSyncProgressService.Status> {
return status return status
} }
@ -63,13 +63,13 @@ class DefaultInitialSyncProgressService @Inject constructor() : InitialSyncProgr
parent?.setProgress(endedTask.offset + (endedTask.totalProgress * endedTask.parentWeight).toInt()) parent?.setProgress(endedTask.offset + (endedTask.totalProgress * endedTask.parentWeight).toInt())
} }
if (endedTask?.parent == null) { if (endedTask?.parent == null) {
status.postValue(null) status.postValue(InitialSyncProgressService.Status.Idle)
} }
} }
fun endAll() { fun endAll() {
rootTask = null rootTask = null
status.postValue(null) status.postValue(InitialSyncProgressService.Status.Idle)
} }
private inner class TaskInfo(@StringRes var nameRes: Int, private inner class TaskInfo(@StringRes var nameRes: Int,
@ -102,9 +102,7 @@ class DefaultInitialSyncProgressService @Inject constructor() : InitialSyncProgr
it.setProgress(offset + parentProgress) it.setProgress(offset + parentProgress)
} ?: run { } ?: run {
Timber.v("--- ${leaf().nameRes}: $currentProgress") Timber.v("--- ${leaf().nameRes}: $currentProgress")
status.postValue( status.postValue(InitialSyncProgressService.Status.Progressing(leaf().nameRes, currentProgress))
InitialSyncProgressService.Status(leaf().nameRes, currentProgress)
)
} }
} }
} }

View file

@ -19,20 +19,16 @@ package im.vector.riotx.features.home
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.os.Bundle import android.os.Bundle
import android.os.Parcelable
import android.view.MenuItem import android.view.MenuItem
import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AlertDialog
import androidx.appcompat.widget.Toolbar import androidx.appcompat.widget.Toolbar
import androidx.core.content.ContextCompat
import androidx.core.view.GravityCompat import androidx.core.view.GravityCompat
import androidx.core.view.isVisible import androidx.core.view.isVisible
import androidx.drawerlayout.widget.DrawerLayout import androidx.drawerlayout.widget.DrawerLayout
import androidx.lifecycle.Observer import com.airbnb.mvrx.MvRx
import com.airbnb.mvrx.viewModel import com.airbnb.mvrx.viewModel
import im.vector.matrix.android.api.MatrixCallback import im.vector.matrix.android.api.session.InitialSyncProgressService
import im.vector.matrix.android.api.session.Session
import im.vector.matrix.android.api.util.toMatrixItem
import im.vector.matrix.android.internal.crypto.model.CryptoDeviceInfo
import im.vector.matrix.android.internal.crypto.model.MXUsersDevicesMap
import im.vector.riotx.R import im.vector.riotx.R
import im.vector.riotx.core.di.ActiveSessionHolder import im.vector.riotx.core.di.ActiveSessionHolder
import im.vector.riotx.core.di.ScreenComponent import im.vector.riotx.core.di.ScreenComponent
@ -46,21 +42,28 @@ import im.vector.riotx.features.crypto.recover.BootstrapBottomSheet
import im.vector.riotx.features.disclaimer.showDisclaimerDialog import im.vector.riotx.features.disclaimer.showDisclaimerDialog
import im.vector.riotx.features.notifications.NotificationDrawerManager import im.vector.riotx.features.notifications.NotificationDrawerManager
import im.vector.riotx.features.popup.PopupAlertManager import im.vector.riotx.features.popup.PopupAlertManager
import im.vector.riotx.features.popup.VerificationVectorAlert
import im.vector.riotx.features.rageshake.VectorUncaughtExceptionHandler import im.vector.riotx.features.rageshake.VectorUncaughtExceptionHandler
import im.vector.riotx.features.settings.VectorPreferences import im.vector.riotx.features.settings.VectorPreferences
import im.vector.riotx.features.workers.signout.SignOutViewModel import im.vector.riotx.features.workers.signout.SignOutViewModel
import im.vector.riotx.push.fcm.FcmHelper import im.vector.riotx.push.fcm.FcmHelper
import kotlinx.android.parcel.Parcelize
import kotlinx.android.synthetic.main.activity_home.* import kotlinx.android.synthetic.main.activity_home.*
import kotlinx.android.synthetic.main.merge_overlay_waiting_view.* import kotlinx.android.synthetic.main.merge_overlay_waiting_view.*
import timber.log.Timber import timber.log.Timber
import javax.inject.Inject import javax.inject.Inject
@Parcelize
data class HomeActivityArgs(
val clearNotification: Boolean,
val accountCreation: Boolean
) : Parcelable
class HomeActivity : VectorBaseActivity(), ToolbarConfigurable, UnknownDeviceDetectorSharedViewModel.Factory { class HomeActivity : VectorBaseActivity(), ToolbarConfigurable, UnknownDeviceDetectorSharedViewModel.Factory {
private lateinit var sharedActionViewModel: HomeSharedActionViewModel private lateinit var sharedActionViewModel: HomeSharedActionViewModel
private val homeActivityViewModel: HomeActivityViewModel by viewModel() private val homeActivityViewModel: HomeActivityViewModel by viewModel()
@Inject lateinit var viewModelFactory: HomeActivityViewModel.Factory
@Inject lateinit var activeSessionHolder: ActiveSessionHolder @Inject lateinit var activeSessionHolder: ActiveSessionHolder
@Inject lateinit var vectorUncaughtExceptionHandler: VectorUncaughtExceptionHandler @Inject lateinit var vectorUncaughtExceptionHandler: VectorUncaughtExceptionHandler
@ -114,21 +117,33 @@ class HomeActivity : VectorBaseActivity(), ToolbarConfigurable, UnknownDeviceDet
} }
.disposeOnDestroy() .disposeOnDestroy()
if (intent.getBooleanExtra(EXTRA_CLEAR_EXISTING_NOTIFICATION, false)) { val args = intent.getParcelableExtra<HomeActivityArgs>(MvRx.KEY_ARG)
if (args?.clearNotification == true) {
notificationDrawerManager.clearAllEvents() notificationDrawerManager.clearAllEvents()
intent.removeExtra(EXTRA_CLEAR_EXISTING_NOTIFICATION)
}
if (intent.getBooleanExtra(EXTRA_ACCOUNT_CREATION, false)) {
sharedActionViewModel.post(HomeActivitySharedAction.PromptForSecurityBootstrap)
homeActivityViewModel.isAccountCreation = true
intent.removeExtra(EXTRA_ACCOUNT_CREATION)
} }
activeSessionHolder.getSafeActiveSession()?.getInitialSyncProgressStatus()?.observe(this, Observer { status -> homeActivityViewModel.subscribe(this) { renderState(it) }
if (status == null) {
waiting_view.isVisible = false /*
// TODO Remove
// Ask again if the app is relaunched
if (!homeActivityViewModel.hasDisplayedCompleteSecurityPrompt
&& activeSessionHolder.getSafeActiveSession()?.hasAlreadySynced() == true) {
promptCompleteSecurityIfNeeded() promptCompleteSecurityIfNeeded()
} else { }
*/
shortcutsHandler.observeRoomsAndBuildShortcuts()
.disposeOnDestroy()
}
private fun renderState(state: HomeActivityViewState) {
when (val status = state.initialSyncProgressServiceStatus) {
is InitialSyncProgressService.Status.Idle -> {
waiting_view.isVisible = false
}
is InitialSyncProgressService.Status.Progressing -> {
homeActivityViewModel.hasDisplayedCompleteSecurityPrompt = false homeActivityViewModel.hasDisplayedCompleteSecurityPrompt = false
Timber.v("${getString(status.statusText)} ${status.percentProgress}") Timber.v("${getString(status.statusText)} ${status.percentProgress}")
waiting_view.setOnClickListener { waiting_view.setOnClickListener {
@ -146,18 +161,11 @@ class HomeActivity : VectorBaseActivity(), ToolbarConfigurable, UnknownDeviceDet
} }
waiting_view.isVisible = true waiting_view.isVisible = true
} }
}) }.exhaustive
// Ask again if the app is relaunched
if (!homeActivityViewModel.hasDisplayedCompleteSecurityPrompt
&& activeSessionHolder.getSafeActiveSession()?.hasAlreadySynced() == true) {
promptCompleteSecurityIfNeeded()
}
shortcutsHandler.observeRoomsAndBuildShortcuts()
.disposeOnDestroy()
} }
/*
// TODO Remove
private fun promptCompleteSecurityIfNeeded() { private fun promptCompleteSecurityIfNeeded() {
val session = activeSessionHolder.getSafeActiveSession() ?: return val session = activeSessionHolder.getSafeActiveSession() ?: return
if (!session.hasAlreadySynced()) return if (!session.hasAlreadySynced()) return
@ -172,7 +180,10 @@ class HomeActivity : VectorBaseActivity(), ToolbarConfigurable, UnknownDeviceDet
} }
}) })
} }
*/
// TODO Remove
/*
private fun alertCompleteSecurity(session: Session) { private fun alertCompleteSecurity(session: Session) {
val myCrossSigningKeys = session.cryptoService().crossSigningService() val myCrossSigningKeys = session.cryptoService().crossSigningService()
.getMyCrossSigningKeys() .getMyCrossSigningKeys()
@ -204,7 +215,10 @@ class HomeActivity : VectorBaseActivity(), ToolbarConfigurable, UnknownDeviceDet
} }
} }
} }
*/
/*
// TODO Remove
private fun promptSecurityEvent(session: Session, titleRes: Int, descRes: Int, action: ((VectorBaseActivity) -> Unit)) { private fun promptSecurityEvent(session: Session, titleRes: Int, descRes: Int, action: ((VectorBaseActivity) -> Unit)) {
homeActivityViewModel.hasDisplayedCompleteSecurityPrompt = true homeActivityViewModel.hasDisplayedCompleteSecurityPrompt = true
popupAlertManager.postVectorAlert( popupAlertManager.postVectorAlert(
@ -225,12 +239,12 @@ class HomeActivity : VectorBaseActivity(), ToolbarConfigurable, UnknownDeviceDet
} }
) )
} }
*/
override fun onNewIntent(intent: Intent?) { override fun onNewIntent(intent: Intent?) {
super.onNewIntent(intent) super.onNewIntent(intent)
if (intent?.hasExtra(EXTRA_CLEAR_EXISTING_NOTIFICATION) == true) { if (intent?.getParcelableExtra<HomeActivityArgs>(MvRx.KEY_ARG)?.clearNotification == true) {
notificationDrawerManager.clearAllEvents() notificationDrawerManager.clearAllEvents()
intent.removeExtra(EXTRA_CLEAR_EXISTING_NOTIFICATION)
} }
} }
@ -293,14 +307,15 @@ class HomeActivity : VectorBaseActivity(), ToolbarConfigurable, UnknownDeviceDet
} }
companion object { companion object {
private const val EXTRA_CLEAR_EXISTING_NOTIFICATION = "EXTRA_CLEAR_EXISTING_NOTIFICATION"
private const val EXTRA_ACCOUNT_CREATION = "EXTRA_ACCOUNT_CREATION"
fun newIntent(context: Context, clearNotification: Boolean = false, accountCreation: Boolean = false): Intent { fun newIntent(context: Context, clearNotification: Boolean = false, accountCreation: Boolean = false): Intent {
val args = HomeActivityArgs(
clearNotification = clearNotification,
accountCreation = accountCreation
)
return Intent(context, HomeActivity::class.java) return Intent(context, HomeActivity::class.java)
.apply { .apply {
putExtra(EXTRA_CLEAR_EXISTING_NOTIFICATION, clearNotification) putExtra(MvRx.KEY_ARG, args)
putExtra(EXTRA_ACCOUNT_CREATION, accountCreation)
} }
} }
} }

View file

@ -25,5 +25,6 @@ sealed class HomeActivitySharedAction : VectorSharedAction {
object OpenDrawer : HomeActivitySharedAction() object OpenDrawer : HomeActivitySharedAction()
object CloseDrawer : HomeActivitySharedAction() object CloseDrawer : HomeActivitySharedAction()
object OpenGroup : HomeActivitySharedAction() object OpenGroup : HomeActivitySharedAction()
// TODO Remove?
object PromptForSecurityBootstrap : HomeActivitySharedAction() object PromptForSecurityBootstrap : HomeActivitySharedAction()
} }

View file

@ -16,18 +16,87 @@
package im.vector.riotx.features.home package im.vector.riotx.features.home
import com.airbnb.mvrx.MvRxState import com.airbnb.mvrx.MvRx
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.internal.crypto.model.rest.UserPasswordAuth
import im.vector.matrix.rx.asObservable
import im.vector.riotx.core.di.ActiveSessionHolder
import im.vector.riotx.core.platform.EmptyAction import im.vector.riotx.core.platform.EmptyAction
import im.vector.riotx.core.platform.EmptyViewEvents import im.vector.riotx.core.platform.EmptyViewEvents
import im.vector.riotx.core.platform.VectorViewModel import im.vector.riotx.core.platform.VectorViewModel
import im.vector.riotx.features.login.ReAuthHelper
import timber.log.Timber
data class EmptyState( class HomeActivityViewModel @AssistedInject constructor(
val dummy: Boolean = false @Assisted initialState: HomeActivityViewState,
) : MvRxState @Assisted private val args: HomeActivityArgs,
private val activeSessionHolder: ActiveSessionHolder,
private val reAuthHelper: ReAuthHelper
) : VectorViewModel<HomeActivityViewState, EmptyAction, EmptyViewEvents>(initialState) {
class HomeActivityViewModel : VectorViewModel<EmptyState, EmptyAction, EmptyViewEvents>(EmptyState()) { @AssistedInject.Factory
interface Factory {
fun create(initialState: HomeActivityViewState, args: HomeActivityArgs): HomeActivityViewModel
}
companion object : MvRxViewModelFactory<HomeActivityViewModel, HomeActivityViewState> {
@JvmStatic
override fun create(viewModelContext: ViewModelContext, state: HomeActivityViewState): HomeActivityViewModel? {
val activity: HomeActivity = viewModelContext.activity()
val args: HomeActivityArgs? = activity.intent.getParcelableExtra(MvRx.KEY_ARG)
return args?.let { activity.viewModelFactory.create(state, it) }
}
}
// TODO Remove?
var hasDisplayedCompleteSecurityPrompt: Boolean = false var hasDisplayedCompleteSecurityPrompt: Boolean = false
var isAccountCreation: Boolean = false
init {
observeInitialSync()
mayBeInitializeCrossSigning()
}
private fun observeInitialSync() {
val session = activeSessionHolder.getSafeActiveSession() ?: return
session.getInitialSyncProgressStatus()
.asObservable()
.subscribe { status ->
setState {
copy(
initialSyncProgressServiceStatus = status
)
}
}
.disposeOnClear()
}
private fun mayBeInitializeCrossSigning() {
if (args.accountCreation) {
val password = reAuthHelper.data ?: return Unit.also {
Timber.w("No password to init cross signing")
}
val session = activeSessionHolder.getSafeActiveSession() ?: return Unit.also {
Timber.w("No session to init cross signing")
}
// We do not use the viewModel context because we do not want to cancel this action
Timber.d("Initialize cross signing")
session.cryptoService().crossSigningService().initializeCrossSigning(
authParams = UserPasswordAuth(
session = null,
user = session.myUserId,
password = password
)
)
// TODO Download keys?
}
}
override fun handle(action: EmptyAction) { override fun handle(action: EmptyAction) {
// NA // NA

View file

@ -0,0 +1,24 @@
/*
* Copyright (c) 2020 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
import com.airbnb.mvrx.MvRxState
import im.vector.matrix.android.api.session.InitialSyncProgressService
data class HomeActivityViewState(
val initialSyncProgressServiceStatus: InitialSyncProgressService.Status = InitialSyncProgressService.Status.Idle
) : MvRxState