Revert "Remove BootstrapStep.SetupPassphrase and BootstrapStep.ConfirmPassphrase"

This reverts commit 23fa44b6a6a6d34b425e2c1adef4fd2beb9800a7.
This commit is contained in:
Benoit Marty 2020-06-25 14:38:22 +02:00 committed by Valere
parent 957fe189dc
commit e758ede706
10 changed files with 427 additions and 47 deletions

View File

@ -28,6 +28,8 @@ import im.vector.riotx.features.crypto.quads.SharedSecuredStorageKeyFragment
import im.vector.riotx.features.crypto.quads.SharedSecuredStoragePassphraseFragment
import im.vector.riotx.features.crypto.recover.BootstrapAccountPasswordFragment
import im.vector.riotx.features.crypto.recover.BootstrapConclusionFragment
import im.vector.riotx.features.crypto.recover.BootstrapConfirmPassphraseFragment
import im.vector.riotx.features.crypto.recover.BootstrapEnterPassphraseFragment
import im.vector.riotx.features.crypto.recover.BootstrapMigrateBackupFragment
import im.vector.riotx.features.crypto.recover.BootstrapSaveRecoveryKeyFragment
import im.vector.riotx.features.crypto.recover.BootstrapSetupRecoveryKeyFragment
@ -452,6 +454,16 @@ interface FragmentModule {
@FragmentKey(GossipingEventsPaperTrailFragment::class)
fun bindGossipingEventsPaperTrailFragment(fragment: GossipingEventsPaperTrailFragment): Fragment
@Binds
@IntoMap
@FragmentKey(BootstrapEnterPassphraseFragment::class)
fun bindBootstrapEnterPassphraseFragment(fragment: BootstrapEnterPassphraseFragment): Fragment
@Binds
@IntoMap
@FragmentKey(BootstrapConfirmPassphraseFragment::class)
fun bindBootstrapConfirmPassphraseFragment(fragment: BootstrapConfirmPassphraseFragment): Fragment
@Binds
@IntoMap
@FragmentKey(BootstrapWaitingFragment::class)

View File

@ -24,13 +24,17 @@ sealed class BootstrapActions : VectorViewModelAction {
// Navigation
object GoBack : BootstrapActions()
data class GoToConfirmPassphrase(val passphrase: String) : BootstrapActions()
object GoToCompleted : BootstrapActions()
object GoToEnterAccountPassword : BootstrapActions()
object SetupRecoveryKey : BootstrapActions()
data class DoInitialize(val passphrase: String) : BootstrapActions()
object DoInitializeGeneratedKey : BootstrapActions()
object TogglePasswordVisibility : BootstrapActions()
data class UpdateCandidatePassphrase(val pass: String) : BootstrapActions()
data class UpdateConfirmCandidatePassphrase(val pass: String) : BootstrapActions()
data class ReAuth(val pass: String) : BootstrapActions()
object RecoveryKeySaved : BootstrapActions()
object Completed : BootstrapActions()

View File

@ -132,6 +132,16 @@ class BootstrapBottomSheet : VectorBaseBottomSheetDialogFragment() {
bootstrapTitleText.text = getString(R.string.upgrade_security)
showFragment(BootstrapWaitingFragment::class, Bundle())
}
is BootstrapStep.SetupPassphrase -> {
bootstrapIcon.setImageDrawable(ContextCompat.getDrawable(requireContext(), R.drawable.ic_message_password))
bootstrapTitleText.text = getString(R.string.set_recovery_passphrase, getString(R.string.recovery_passphrase))
showFragment(BootstrapEnterPassphraseFragment::class, Bundle())
}
is BootstrapStep.ConfirmPassphrase -> {
bootstrapIcon.setImageDrawable(ContextCompat.getDrawable(requireContext(), R.drawable.ic_message_password))
bootstrapTitleText.text = getString(R.string.confirm_recovery_passphrase, getString(R.string.recovery_passphrase))
showFragment(BootstrapConfirmPassphraseFragment::class, Bundle())
}
is BootstrapStep.AccountPassword -> {
bootstrapIcon.setImageDrawable(ContextCompat.getDrawable(requireContext(), R.drawable.ic_user))
bootstrapTitleText.text = getString(R.string.account_password)

View File

@ -0,0 +1,118 @@
/*
* 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.crypto.recover
import android.os.Bundle
import android.view.View
import android.view.inputmethod.EditorInfo
import androidx.core.text.toSpannable
import androidx.core.view.isGone
import com.airbnb.mvrx.parentFragmentViewModel
import com.airbnb.mvrx.withState
import com.jakewharton.rxbinding3.widget.editorActionEvents
import com.jakewharton.rxbinding3.widget.textChanges
import im.vector.riotx.R
import im.vector.riotx.core.extensions.hideKeyboard
import im.vector.riotx.core.extensions.showPassword
import im.vector.riotx.core.platform.VectorBaseFragment
import im.vector.riotx.core.resources.ColorProvider
import im.vector.riotx.core.utils.colorizeMatchingText
import io.reactivex.android.schedulers.AndroidSchedulers
import kotlinx.android.synthetic.main.fragment_bootstrap_enter_passphrase.*
import java.util.concurrent.TimeUnit
import javax.inject.Inject
class BootstrapConfirmPassphraseFragment @Inject constructor(
private val colorProvider: ColorProvider
) : VectorBaseFragment() {
override fun getLayoutResId() = R.layout.fragment_bootstrap_enter_passphrase
val sharedViewModel: BootstrapSharedViewModel by parentFragmentViewModel()
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
ssss_passphrase_security_progress.isGone = true
val recPassPhrase = getString(R.string.recovery_passphrase)
bootstrapDescriptionText.text = getString(R.string.bootstrap_info_confirm_text, recPassPhrase)
.toSpannable()
.colorizeMatchingText(recPassPhrase, colorProvider.getColorFromAttribute(android.R.attr.textColorLink))
ssss_passphrase_enter_edittext.hint = getString(R.string.passphrase_confirm_passphrase)
withState(sharedViewModel) {
// set initial value (useful when coming back)
ssss_passphrase_enter_edittext.setText(it.passphraseRepeat ?: "")
ssss_passphrase_enter_edittext.requestFocus()
}
ssss_passphrase_enter_edittext.editorActionEvents()
.throttleFirst(300, TimeUnit.MILLISECONDS)
.observeOn(AndroidSchedulers.mainThread())
.subscribe {
if (it.actionId == EditorInfo.IME_ACTION_DONE) {
submit()
}
}
.disposeOnDestroyView()
ssss_passphrase_enter_edittext.textChanges()
.subscribe {
ssss_passphrase_enter_til.error = null
sharedViewModel.handle(BootstrapActions.UpdateConfirmCandidatePassphrase(it?.toString() ?: ""))
}
.disposeOnDestroyView()
sharedViewModel.observeViewEvents {
// when (it) {
// is SharedSecureStorageViewEvent.InlineError -> {
// ssss_passphrase_enter_til.error = it.message
// }
// }
}
ssss_view_show_password.debouncedClicks { sharedViewModel.handle(BootstrapActions.TogglePasswordVisibility) }
bootstrapSubmit.debouncedClicks { submit() }
}
private fun submit() = withState(sharedViewModel) { state ->
if (state.step !is BootstrapStep.ConfirmPassphrase) {
return@withState
}
val passphrase = ssss_passphrase_enter_edittext.text?.toString()
when {
passphrase.isNullOrBlank() ->
ssss_passphrase_enter_til.error = getString(R.string.passphrase_empty_error_message)
passphrase != state.passphrase ->
ssss_passphrase_enter_til.error = getString(R.string.passphrase_passphrase_does_not_match)
else -> {
view?.hideKeyboard()
sharedViewModel.handle(BootstrapActions.DoInitialize(passphrase))
}
}
}
override fun invalidate() = withState(sharedViewModel) { state ->
if (state.step is BootstrapStep.ConfirmPassphrase) {
val isPasswordVisible = state.step.isPasswordVisible
ssss_passphrase_enter_edittext.showPassword(isPasswordVisible, updateCursor = false)
ssss_view_show_password.setImageResource(if (isPasswordVisible) R.drawable.ic_eye_closed_black else R.drawable.ic_eye_black)
}
}
}

View File

@ -66,6 +66,7 @@ interface BootstrapProgressListener {
data class Params(
val userPasswordAuth: UserPasswordAuth? = null,
val progressListener: BootstrapProgressListener? = null,
val passphrase: String?,
val keySpec: SsssKeySpec? = null
)
@ -107,13 +108,24 @@ class BootstrapCrossSigningTask @Inject constructor(
)
try {
keyInfo = awaitCallback {
ssssService.generateKey(
UUID.randomUUID().toString(),
params.keySpec,
"ssss_key",
EmptyKeySigner(),
it
)
params.passphrase?.let { passphrase ->
ssssService.generateKeyWithPassphrase(
UUID.randomUUID().toString(),
"ssss_key",
passphrase,
EmptyKeySigner(),
null,
it
)
} ?: kotlin.run {
ssssService.generateKey(
UUID.randomUUID().toString(),
params.keySpec,
"ssss_key",
EmptyKeySigner(),
it
)
}
}
} catch (failure: Failure) {
return BootstrapResult.FailedToCreateSSSSKey(failure)

View File

@ -0,0 +1,126 @@
/*
* 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.crypto.recover
import android.os.Bundle
import android.view.View
import android.view.inputmethod.EditorInfo
import androidx.core.text.toSpannable
import com.airbnb.mvrx.parentFragmentViewModel
import com.airbnb.mvrx.withState
import com.jakewharton.rxbinding3.widget.editorActionEvents
import com.jakewharton.rxbinding3.widget.textChanges
import im.vector.riotx.R
import im.vector.riotx.core.extensions.showPassword
import im.vector.riotx.core.platform.VectorBaseFragment
import im.vector.riotx.core.resources.ColorProvider
import im.vector.riotx.core.utils.colorizeMatchingText
import im.vector.riotx.features.settings.VectorLocale
import io.reactivex.android.schedulers.AndroidSchedulers
import kotlinx.android.synthetic.main.fragment_bootstrap_enter_passphrase.*
import java.util.concurrent.TimeUnit
import javax.inject.Inject
class BootstrapEnterPassphraseFragment @Inject constructor(
private val colorProvider: ColorProvider
) : VectorBaseFragment() {
override fun getLayoutResId() = R.layout.fragment_bootstrap_enter_passphrase
val sharedViewModel: BootstrapSharedViewModel by parentFragmentViewModel()
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val recPassPhrase = getString(R.string.recovery_passphrase)
bootstrapDescriptionText.text = getString(R.string.bootstrap_info_text, recPassPhrase)
.toSpannable()
.colorizeMatchingText(recPassPhrase, colorProvider.getColorFromAttribute(android.R.attr.textColorLink))
ssss_passphrase_enter_edittext.hint = getString(R.string.passphrase_enter_passphrase)
withState(sharedViewModel) {
// set initial value (useful when coming back)
ssss_passphrase_enter_edittext.setText(it.passphrase ?: "")
}
ssss_passphrase_enter_edittext.editorActionEvents()
.throttleFirst(300, TimeUnit.MILLISECONDS)
.observeOn(AndroidSchedulers.mainThread())
.subscribe {
if (it.actionId == EditorInfo.IME_ACTION_DONE) {
submit()
}
}
.disposeOnDestroyView()
ssss_passphrase_enter_edittext.textChanges()
.subscribe {
// ssss_passphrase_enter_til.error = null
sharedViewModel.handle(BootstrapActions.UpdateCandidatePassphrase(it?.toString() ?: ""))
// ssss_passphrase_submit.isEnabled = it.isNotBlank()
}
.disposeOnDestroyView()
sharedViewModel.observeViewEvents {
// when (it) {
// is SharedSecureStorageViewEvent.InlineError -> {
// ssss_passphrase_enter_til.error = it.message
// }
// }
}
ssss_view_show_password.debouncedClicks { sharedViewModel.handle(BootstrapActions.TogglePasswordVisibility) }
bootstrapSubmit.debouncedClicks { submit() }
}
private fun submit() = withState(sharedViewModel) { state ->
if (state.step !is BootstrapStep.SetupPassphrase) {
return@withState
}
val score = state.passphraseStrength.invoke()?.score
val passphrase = ssss_passphrase_enter_edittext.text?.toString()
if (passphrase.isNullOrBlank()) {
ssss_passphrase_enter_til.error = getString(R.string.passphrase_empty_error_message)
} else if (score != 4) {
ssss_passphrase_enter_til.error = getString(R.string.passphrase_passphrase_too_weak)
} else {
sharedViewModel.handle(BootstrapActions.GoToConfirmPassphrase(passphrase))
}
}
override fun invalidate() = withState(sharedViewModel) { state ->
if (state.step is BootstrapStep.SetupPassphrase) {
val isPasswordVisible = state.step.isPasswordVisible
ssss_passphrase_enter_edittext.showPassword(isPasswordVisible, updateCursor = false)
ssss_view_show_password.setImageResource(if (isPasswordVisible) R.drawable.ic_eye_closed_black else R.drawable.ic_eye_black)
state.passphraseStrength.invoke()?.let { strength ->
val score = strength.score
ssss_passphrase_security_progress.strength = score
if (score in 1..3) {
val hint =
strength.feedback?.getWarning(VectorLocale.applicationLocale)?.takeIf { it.isNotBlank() }
?: strength.feedback?.getSuggestions(VectorLocale.applicationLocale)?.firstOrNull()
if (hint != null && hint != ssss_passphrase_enter_til.error.toString()) {
ssss_passphrase_enter_til.error = hint
}
} else {
ssss_passphrase_enter_til.error = null
}
}
}
}
}

View File

@ -51,11 +51,15 @@ class BootstrapMigrateBackupFragment @Inject constructor(
override fun getLayoutResId() = R.layout.fragment_bootstrap_migrate_backup
private val sharedViewModel: BootstrapSharedViewModel by parentFragmentViewModel()
val sharedViewModel: BootstrapSharedViewModel by parentFragmentViewModel()
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
withState(sharedViewModel) {
// set initial value (useful when coming back)
bootstrapMigrateEditText.setText(it.passphrase ?: "")
}
bootstrapMigrateEditText.editorActionEvents()
.throttleFirst(300, TimeUnit.MILLISECONDS)
.observeOn(AndroidSchedulers.mainThread())

View File

@ -24,6 +24,7 @@ import com.airbnb.mvrx.MvRxViewModelFactory
import com.airbnb.mvrx.Success
import com.airbnb.mvrx.Uninitialized
import com.airbnb.mvrx.ViewModelContext
import com.nulabinc.zxcvbn.Zxcvbn
import com.squareup.inject.assisted.Assisted
import com.squareup.inject.assisted.AssistedInject
import im.vector.matrix.android.api.failure.Failure
@ -43,6 +44,7 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import java.io.OutputStream
class BootstrapSharedViewModel @AssistedInject constructor(
@Assisted initialState: BootstrapViewState,
@Assisted val args: BootstrapBottomSheet.Args,
@ -53,6 +55,8 @@ class BootstrapSharedViewModel @AssistedInject constructor(
private val reAuthHelper: ReAuthHelper
) : VectorViewModel<BootstrapViewState, BootstrapActions, BootstrapViewEvents>(initialState) {
private val zxcvbn = Zxcvbn()
@AssistedInject.Factory
interface Factory {
fun create(initialState: BootstrapViewState, args: BootstrapBottomSheet.Args): BootstrapSharedViewModel
@ -64,7 +68,7 @@ class BootstrapSharedViewModel @AssistedInject constructor(
// need to check if user have an existing keybackup
if (args.isNewAccount) {
setState {
copy(step = BootstrapStep.AccountPassword(false))
copy(step = BootstrapStep.SetupPassphrase(false))
}
} else {
setState {
@ -79,7 +83,7 @@ class BootstrapSharedViewModel @AssistedInject constructor(
if (version == null) {
// we just resume plain bootstrap
setState {
copy(step = BootstrapStep.AccountPassword(false))
copy(step = BootstrapStep.SetupPassphrase(false))
}
} else {
// we need to get existing backup passphrase/key and convert to SSSS
@ -108,9 +112,19 @@ class BootstrapSharedViewModel @AssistedInject constructor(
override fun handle(action: BootstrapActions) = withState { state ->
when (action) {
is BootstrapActions.GoBack -> queryBack()
BootstrapActions.TogglePasswordVisibility -> {
is BootstrapActions.GoBack -> queryBack()
BootstrapActions.TogglePasswordVisibility -> {
when (state.step) {
is BootstrapStep.SetupPassphrase -> {
setState {
copy(step = state.step.copy(isPasswordVisible = !state.step.isPasswordVisible))
}
}
is BootstrapStep.ConfirmPassphrase -> {
setState {
copy(step = state.step.copy(isPasswordVisible = !state.step.isPasswordVisible))
}
}
is BootstrapStep.AccountPassword -> {
setState {
copy(step = state.step.copy(isPasswordVisible = !state.step.isPasswordVisible))
@ -128,64 +142,118 @@ class BootstrapSharedViewModel @AssistedInject constructor(
BootstrapActions.SetupRecoveryKey -> {
startProcess()
}
is BootstrapActions.DoInitializeGeneratedKey -> {
is BootstrapActions.UpdateCandidatePassphrase -> {
val strength = zxcvbn.measure(action.pass)
setState {
copy(
passphrase = action.pass,
passphraseStrength = Success(strength)
)
}
}
is BootstrapActions.GoToConfirmPassphrase -> {
setState {
copy(
passphrase = action.passphrase,
step = BootstrapStep.ConfirmPassphrase(
isPasswordVisible = (state.step as? BootstrapStep.SetupPassphrase)?.isPasswordVisible ?: false
)
)
}
}
is BootstrapActions.UpdateConfirmCandidatePassphrase -> {
setState {
copy(
passphraseRepeat = action.pass
)
}
}
is BootstrapActions.DoInitialize -> {
if (state.passphrase == state.passphraseRepeat) {
val userPassword = reAuthHelper.data
if (userPassword == null) {
setState {
copy(
step = BootstrapStep.AccountPassword(false)
)
}
} else {
startInitializeFlow(userPassword)
}
} else {
setState {
copy(
passphraseConfirmMatch = Fail(Throwable(stringProvider.getString(R.string.passphrase_passphrase_does_not_match)))
)
}
}
}
is BootstrapActions.DoInitializeGeneratedKey -> {
val userPassword = reAuthHelper.data
if (userPassword == null) {
setState {
copy(
passphrase = null,
passphraseRepeat = null,
step = BootstrapStep.AccountPassword(false)
)
}
} else {
setState {
copy(
passphrase = null,
passphraseRepeat = null
)
}
startInitializeFlow(userPassword)
}
}
BootstrapActions.RecoveryKeySaved -> {
BootstrapActions.RecoveryKeySaved -> {
_viewEvents.post(BootstrapViewEvents.RecoveryKeySaved)
setState {
copy(step = BootstrapStep.SaveRecoveryKey(true))
}
}
BootstrapActions.Completed -> {
BootstrapActions.Completed -> {
_viewEvents.post(BootstrapViewEvents.Dismiss)
}
BootstrapActions.GoToCompleted -> {
BootstrapActions.GoToCompleted -> {
setState {
copy(step = BootstrapStep.DoneSuccess)
}
}
BootstrapActions.SaveReqQueryStarted -> {
BootstrapActions.SaveReqQueryStarted -> {
setState {
copy(recoverySaveFileProcess = Loading())
}
}
is BootstrapActions.SaveKeyToUri -> {
is BootstrapActions.SaveKeyToUri -> {
saveRecoveryKeyToUri(action.os)
}
BootstrapActions.SaveReqFailed -> {
BootstrapActions.SaveReqFailed -> {
setState {
copy(recoverySaveFileProcess = Uninitialized)
}
}
BootstrapActions.GoToEnterAccountPassword -> {
BootstrapActions.GoToEnterAccountPassword -> {
setState {
copy(step = BootstrapStep.AccountPassword(false))
}
}
BootstrapActions.HandleForgotBackupPassphrase -> {
BootstrapActions.HandleForgotBackupPassphrase -> {
if (state.step is BootstrapStep.GetBackupSecretPassForMigration) {
setState {
copy(step = BootstrapStep.GetBackupSecretPassForMigration(state.step.isPasswordVisible, true))
}
} else return@withState
}
is BootstrapActions.ReAuth -> {
is BootstrapActions.ReAuth -> {
startInitializeFlow(action.pass)
}
is BootstrapActions.DoMigrateWithPassphrase -> {
is BootstrapActions.DoMigrateWithPassphrase -> {
startMigrationFlow(state.step, action.passphrase, null)
}
is BootstrapActions.DoMigrateWithRecoveryKey -> {
is BootstrapActions.DoMigrateWithRecoveryKey -> {
startMigrationFlow(state.step, null, action.recoveryKey)
}
}.exhaustive
@ -234,6 +302,8 @@ class BootstrapSharedViewModel @AssistedInject constructor(
if (it is BackupToQuadSMigrationTask.Result.Success) {
setState {
copy(
passphrase = passphrase,
passphraseRepeat = passphrase,
migrationRecoveryKey = recoveryKey
)
}
@ -281,7 +351,6 @@ class BootstrapSharedViewModel @AssistedInject constructor(
}
withState { state ->
val previousStep = state.step
viewModelScope.launch(Dispatchers.IO) {
val userPasswordAuth = userPassword?.let {
UserPasswordAuth(
@ -296,6 +365,7 @@ class BootstrapSharedViewModel @AssistedInject constructor(
Params(
userPasswordAuth = userPasswordAuth,
progressListener = progressListener,
passphrase = state.passphrase,
keySpec = state.migrationRecoveryKey?.let { extractCurveKeyFromRecoveryKey(it)?.let { RawBytesKeySpec(it) } }
)
) { bootstrapResult ->
@ -339,7 +409,7 @@ class BootstrapSharedViewModel @AssistedInject constructor(
_viewEvents.post(BootstrapViewEvents.ModalError(bootstrapResult.error ?: stringProvider.getString(R.string.matrix_error)))
setState {
copy(
step = previousStep
step = BootstrapStep.ConfirmPassphrase(false)
)
}
}
@ -375,12 +445,25 @@ class BootstrapSharedViewModel @AssistedInject constructor(
// do we let you cancel from here?
_viewEvents.post(BootstrapViewEvents.SkipBootstrap())
}
is BootstrapStep.SetupPassphrase -> {
// do we let you cancel from here?
_viewEvents.post(BootstrapViewEvents.SkipBootstrap())
}
is BootstrapStep.ConfirmPassphrase -> {
setState {
copy(
step = BootstrapStep.SetupPassphrase(
isPasswordVisible = (state.step as? BootstrapStep.ConfirmPassphrase)?.isPasswordVisible ?: false
)
)
}
}
is BootstrapStep.AccountPassword -> {
_viewEvents.post(BootstrapViewEvents.SkipBootstrap(false))
_viewEvents.post(BootstrapViewEvents.SkipBootstrap(state.passphrase != null))
}
BootstrapStep.Initializing -> {
// do we let you cancel from here?
_viewEvents.post(BootstrapViewEvents.SkipBootstrap(false))
_viewEvents.post(BootstrapViewEvents.SkipBootstrap(state.passphrase != null))
}
is BootstrapStep.SaveRecoveryKey,
BootstrapStep.DoneSuccess -> {
@ -393,8 +476,7 @@ class BootstrapSharedViewModel @AssistedInject constructor(
// Companion, view model assisted creation
// ======================================
companion object
: MvRxViewModelFactory<BootstrapSharedViewModel, BootstrapViewState> {
companion object : MvRxViewModelFactory<BootstrapSharedViewModel, BootstrapViewState> {
override fun create(viewModelContext: ViewModelContext, state: BootstrapViewState): BootstrapSharedViewModel? {
val fragment: BootstrapBottomSheet = (viewModelContext as FragmentViewModelContext).fragment()

View File

@ -36,25 +36,29 @@ package im.vector.riotx.features.crypto.recover
*
*
*
* Existing No
* Keybackup KeyBackup
*
*
*
*
* BootstrapStep.GetBackupSecretForMigration
*
*
*
* is password needed?
*
*
* Existing No
* Keybackup KeyBackup
*
*
*
* BootstrapStep.SetupPassphrase
* BootstrapStep.GetBackupSecretForMigration
*
* Back
*
*
* BootstrapStep.ConfirmPassphrase
*
*
* is password needed?
*
*
*
* BootstrapStep.AccountPassword
*
*
*
* password not needed (in
*
*
* password not needed (in
* memory)
*
*
@ -81,6 +85,9 @@ package im.vector.riotx.features.crypto.recover
sealed class BootstrapStep {
object SetupSecureBackup : BootstrapStep()
data class SetupPassphrase(val isPasswordVisible: Boolean) : BootstrapStep()
data class ConfirmPassphrase(val isPasswordVisible: Boolean) : BootstrapStep()
data class AccountPassword(val isPasswordVisible: Boolean, val failure: String? = null) : BootstrapStep()
object CheckingMigration : BootstrapStep()

View File

@ -19,13 +19,18 @@ package im.vector.riotx.features.crypto.recover
import com.airbnb.mvrx.Async
import com.airbnb.mvrx.MvRxState
import com.airbnb.mvrx.Uninitialized
import com.nulabinc.zxcvbn.Strength
import im.vector.matrix.android.api.session.securestorage.SsssKeyCreationInfo
import im.vector.riotx.core.platform.WaitingViewData
data class BootstrapViewState(
val step: BootstrapStep = BootstrapStep.SetupSecureBackup,
val step: BootstrapStep = BootstrapStep.SetupPassphrase(false),
val passphrase: String? = null,
val migrationRecoveryKey: String? = null,
val passphraseRepeat: String? = null,
val crossSigningInitialization: Async<Unit> = Uninitialized,
val passphraseStrength: Async<Strength> = Uninitialized,
val passphraseConfirmMatch: Async<Unit> = Uninitialized,
val recoveryKeyCreationInfo: SsssKeyCreationInfo? = null,
val initializationWaitingViewData: WaitingViewData? = null,
val recoverySaveFileProcess: Async<Unit> = Uninitialized