mirror of
https://github.com/element-hq/element-android
synced 2024-10-26 20:57:20 +03:00
Bootstrap: Add an introduction step: BootstrapSetupRecoveryKeyFragment
This commit is contained in:
parent
369f40c804
commit
f3b464b88a
21 changed files with 226 additions and 40 deletions
|
@ -30,6 +30,7 @@ import im.vector.riotx.features.crypto.recover.BootstrapAccountPasswordFragment
|
||||||
import im.vector.riotx.features.crypto.recover.BootstrapConclusionFragment
|
import im.vector.riotx.features.crypto.recover.BootstrapConclusionFragment
|
||||||
import im.vector.riotx.features.crypto.recover.BootstrapMigrateBackupFragment
|
import im.vector.riotx.features.crypto.recover.BootstrapMigrateBackupFragment
|
||||||
import im.vector.riotx.features.crypto.recover.BootstrapSaveRecoveryKeyFragment
|
import im.vector.riotx.features.crypto.recover.BootstrapSaveRecoveryKeyFragment
|
||||||
|
import im.vector.riotx.features.crypto.recover.BootstrapSetupRecoveryKeyFragment
|
||||||
import im.vector.riotx.features.crypto.recover.BootstrapWaitingFragment
|
import im.vector.riotx.features.crypto.recover.BootstrapWaitingFragment
|
||||||
import im.vector.riotx.features.crypto.verification.cancel.VerificationCancelFragment
|
import im.vector.riotx.features.crypto.verification.cancel.VerificationCancelFragment
|
||||||
import im.vector.riotx.features.crypto.verification.cancel.VerificationNotMeFragment
|
import im.vector.riotx.features.crypto.verification.cancel.VerificationNotMeFragment
|
||||||
|
@ -456,6 +457,11 @@ interface FragmentModule {
|
||||||
@FragmentKey(BootstrapWaitingFragment::class)
|
@FragmentKey(BootstrapWaitingFragment::class)
|
||||||
fun bindBootstrapWaitingFragment(fragment: BootstrapWaitingFragment): Fragment
|
fun bindBootstrapWaitingFragment(fragment: BootstrapWaitingFragment): Fragment
|
||||||
|
|
||||||
|
@Binds
|
||||||
|
@IntoMap
|
||||||
|
@FragmentKey(BootstrapSetupRecoveryKeyFragment::class)
|
||||||
|
fun bindBootstrapSetupRecoveryKeyFragment(fragment: BootstrapSetupRecoveryKeyFragment): Fragment
|
||||||
|
|
||||||
@Binds
|
@Binds
|
||||||
@IntoMap
|
@IntoMap
|
||||||
@FragmentKey(BootstrapSaveRecoveryKeyFragment::class)
|
@FragmentKey(BootstrapSaveRecoveryKeyFragment::class)
|
||||||
|
|
|
@ -27,6 +27,8 @@ sealed class BootstrapActions : VectorViewModelAction {
|
||||||
object GoToCompleted : BootstrapActions()
|
object GoToCompleted : BootstrapActions()
|
||||||
object GoToEnterAccountPassword : BootstrapActions()
|
object GoToEnterAccountPassword : BootstrapActions()
|
||||||
|
|
||||||
|
object SetupRecoveryKey : BootstrapActions()
|
||||||
|
|
||||||
object DoInitializeGeneratedKey : BootstrapActions()
|
object DoInitializeGeneratedKey : BootstrapActions()
|
||||||
object TogglePasswordVisibility : BootstrapActions()
|
object TogglePasswordVisibility : BootstrapActions()
|
||||||
data class ReAuth(val pass: String) : BootstrapActions()
|
data class ReAuth(val pass: String) : BootstrapActions()
|
||||||
|
|
|
@ -121,8 +121,12 @@ class BootstrapBottomSheet : VectorBaseBottomSheetDialogFragment() {
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun invalidate() = withState(viewModel) { state ->
|
override fun invalidate() = withState(viewModel) { state ->
|
||||||
|
|
||||||
when (state.step) {
|
when (state.step) {
|
||||||
|
is BootstrapStep.SetupSecureBackup -> {
|
||||||
|
bootstrapIcon.setImageDrawable(ContextCompat.getDrawable(requireContext(), R.drawable.ic_secure_backup_24dp))
|
||||||
|
bootstrapTitleText.text = getString(R.string.bottom_sheet_setup_secure_backup_title)
|
||||||
|
showFragment(BootstrapSetupRecoveryKeyFragment::class, Bundle())
|
||||||
|
}
|
||||||
is BootstrapStep.CheckingMigration -> {
|
is BootstrapStep.CheckingMigration -> {
|
||||||
bootstrapIcon.setImageDrawable(ContextCompat.getDrawable(requireContext(), R.drawable.ic_message_password))
|
bootstrapIcon.setImageDrawable(ContextCompat.getDrawable(requireContext(), R.drawable.ic_message_password))
|
||||||
bootstrapTitleText.text = getString(R.string.upgrade_security)
|
bootstrapTitleText.text = getString(R.string.upgrade_security)
|
||||||
|
@ -134,17 +138,17 @@ class BootstrapBottomSheet : VectorBaseBottomSheetDialogFragment() {
|
||||||
showFragment(BootstrapAccountPasswordFragment::class, Bundle())
|
showFragment(BootstrapAccountPasswordFragment::class, Bundle())
|
||||||
}
|
}
|
||||||
is BootstrapStep.Initializing -> {
|
is BootstrapStep.Initializing -> {
|
||||||
bootstrapIcon.setImageDrawable(ContextCompat.getDrawable(requireContext(), R.drawable.ic_message_key))
|
bootstrapIcon.setImageDrawable(ContextCompat.getDrawable(requireContext(), R.drawable.ic_secure_backup_24dp))
|
||||||
bootstrapTitleText.text = getString(R.string.bootstrap_loading_title)
|
bootstrapTitleText.text = getString(R.string.bootstrap_loading_title)
|
||||||
showFragment(BootstrapWaitingFragment::class, Bundle())
|
showFragment(BootstrapWaitingFragment::class, Bundle())
|
||||||
}
|
}
|
||||||
is BootstrapStep.SaveRecoveryKey -> {
|
is BootstrapStep.SaveRecoveryKey -> {
|
||||||
bootstrapIcon.setImageDrawable(ContextCompat.getDrawable(requireContext(), R.drawable.ic_message_key))
|
bootstrapIcon.setImageDrawable(ContextCompat.getDrawable(requireContext(), R.drawable.ic_secure_backup_24dp))
|
||||||
bootstrapTitleText.text = getString(R.string.keys_backup_setup_step3_please_make_copy)
|
bootstrapTitleText.text = getString(R.string.bottom_sheet_save_your_recovery_key_title)
|
||||||
showFragment(BootstrapSaveRecoveryKeyFragment::class, Bundle())
|
showFragment(BootstrapSaveRecoveryKeyFragment::class, Bundle())
|
||||||
}
|
}
|
||||||
is BootstrapStep.DoneSuccess -> {
|
is BootstrapStep.DoneSuccess -> {
|
||||||
bootstrapIcon.setImageDrawable(ContextCompat.getDrawable(requireContext(), R.drawable.ic_message_key))
|
bootstrapIcon.setImageDrawable(ContextCompat.getDrawable(requireContext(), R.drawable.ic_secure_backup_24dp))
|
||||||
bootstrapTitleText.text = getString(R.string.bootstrap_finish_title)
|
bootstrapTitleText.text = getString(R.string.bootstrap_finish_title)
|
||||||
showFragment(BootstrapConclusionFragment::class, Bundle())
|
showFragment(BootstrapConclusionFragment::class, Bundle())
|
||||||
}
|
}
|
||||||
|
@ -153,7 +157,7 @@ class BootstrapBottomSheet : VectorBaseBottomSheetDialogFragment() {
|
||||||
is BootstrapStep.GetBackupSecretPassForMigration -> state.step.useKey
|
is BootstrapStep.GetBackupSecretPassForMigration -> state.step.useKey
|
||||||
else -> true
|
else -> true
|
||||||
}
|
}
|
||||||
val drawableRes = if (isKey) R.drawable.ic_message_key else R.drawable.ic_message_password
|
val drawableRes = if (isKey) R.drawable.ic_secure_backup_24dp else R.drawable.ic_message_password
|
||||||
bootstrapIcon.setImageDrawable(ContextCompat.getDrawable(
|
bootstrapIcon.setImageDrawable(ContextCompat.getDrawable(
|
||||||
requireContext(),
|
requireContext(),
|
||||||
drawableRes)
|
drawableRes)
|
||||||
|
|
|
@ -16,10 +16,8 @@
|
||||||
|
|
||||||
package im.vector.riotx.features.crypto.recover
|
package im.vector.riotx.features.crypto.recover
|
||||||
|
|
||||||
import im.vector.matrix.android.api.auth.data.LoginFlowTypes
|
|
||||||
import im.vector.matrix.android.api.failure.Failure
|
import im.vector.matrix.android.api.failure.Failure
|
||||||
import im.vector.matrix.android.api.failure.MatrixError
|
import im.vector.matrix.android.api.failure.MatrixError
|
||||||
import im.vector.matrix.android.api.failure.toRegistrationFlowResponse
|
|
||||||
import im.vector.matrix.android.api.session.Session
|
import im.vector.matrix.android.api.session.Session
|
||||||
import im.vector.matrix.android.api.session.crypto.crosssigning.KEYBACKUP_SECRET_SSSS_NAME
|
import im.vector.matrix.android.api.session.crypto.crosssigning.KEYBACKUP_SECRET_SSSS_NAME
|
||||||
import im.vector.matrix.android.api.session.crypto.crosssigning.MASTER_KEY_SSSS_NAME
|
import im.vector.matrix.android.api.session.crypto.crosssigning.MASTER_KEY_SSSS_NAME
|
||||||
|
@ -71,19 +69,23 @@ data class Params(
|
||||||
val keySpec: SsssKeySpec? = null
|
val keySpec: SsssKeySpec? = null
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// TODO Rename to CreateServerRecovery
|
||||||
class BootstrapCrossSigningTask @Inject constructor(
|
class BootstrapCrossSigningTask @Inject constructor(
|
||||||
private val session: Session,
|
private val session: Session,
|
||||||
private val stringProvider: StringProvider
|
private val stringProvider: StringProvider
|
||||||
) : ViewModelTask<Params, BootstrapResult> {
|
) : ViewModelTask<Params, BootstrapResult> {
|
||||||
|
|
||||||
override suspend fun execute(params: Params): BootstrapResult {
|
override suspend fun execute(params: Params): BootstrapResult {
|
||||||
|
val crossSigningService = session.cryptoService().crossSigningService()
|
||||||
|
|
||||||
|
// TODO Remove
|
||||||
|
/*
|
||||||
params.progressListener?.onProgress(
|
params.progressListener?.onProgress(
|
||||||
WaitingViewData(
|
WaitingViewData(
|
||||||
stringProvider.getString(R.string.bootstrap_crosssigning_progress_initializing),
|
stringProvider.getString(R.string.bootstrap_crosssigning_progress_initializing),
|
||||||
isIndeterminate = true
|
isIndeterminate = true
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
val crossSigningService = session.cryptoService().crossSigningService()
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
awaitCallback<Unit> {
|
awaitCallback<Unit> {
|
||||||
|
@ -92,6 +94,7 @@ class BootstrapCrossSigningTask @Inject constructor(
|
||||||
} catch (failure: Throwable) {
|
} catch (failure: Throwable) {
|
||||||
return handleInitializeXSigningError(failure)
|
return handleInitializeXSigningError(failure)
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
val keyInfo: SsssKeyCreationInfo
|
val keyInfo: SsssKeyCreationInfo
|
||||||
|
|
||||||
|
@ -214,6 +217,8 @@ class BootstrapCrossSigningTask @Inject constructor(
|
||||||
return BootstrapResult.Success(keyInfo)
|
return BootstrapResult.Success(keyInfo)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
TODO Remove
|
||||||
private fun handleInitializeXSigningError(failure: Throwable): BootstrapResult {
|
private fun handleInitializeXSigningError(failure: Throwable): BootstrapResult {
|
||||||
if (failure is Failure.ServerError && failure.error.code == MatrixError.M_FORBIDDEN) {
|
if (failure is Failure.ServerError && failure.error.code == MatrixError.M_FORBIDDEN) {
|
||||||
return BootstrapResult.InvalidPasswordError(failure.error)
|
return BootstrapResult.InvalidPasswordError(failure.error)
|
||||||
|
@ -230,4 +235,5 @@ class BootstrapCrossSigningTask @Inject constructor(
|
||||||
}
|
}
|
||||||
return BootstrapResult.GenericError(failure)
|
return BootstrapResult.GenericError(failure)
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,14 +21,12 @@ import android.content.ActivityNotFoundException
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import androidx.core.text.toSpannable
|
|
||||||
import androidx.core.view.isVisible
|
import androidx.core.view.isVisible
|
||||||
import com.airbnb.mvrx.parentFragmentViewModel
|
import com.airbnb.mvrx.parentFragmentViewModel
|
||||||
import com.airbnb.mvrx.withState
|
import com.airbnb.mvrx.withState
|
||||||
import im.vector.riotx.R
|
import im.vector.riotx.R
|
||||||
import im.vector.riotx.core.platform.VectorBaseFragment
|
import im.vector.riotx.core.platform.VectorBaseFragment
|
||||||
import im.vector.riotx.core.resources.ColorProvider
|
import im.vector.riotx.core.resources.ColorProvider
|
||||||
import im.vector.riotx.core.utils.colorizeMatchingText
|
|
||||||
import im.vector.riotx.core.utils.startSharePlainTextIntent
|
import im.vector.riotx.core.utils.startSharePlainTextIntent
|
||||||
import im.vector.riotx.core.utils.toast
|
import im.vector.riotx.core.utils.toast
|
||||||
import kotlinx.android.synthetic.main.fragment_bootstrap_save_key.*
|
import kotlinx.android.synthetic.main.fragment_bootstrap_save_key.*
|
||||||
|
@ -48,14 +46,6 @@ class BootstrapSaveRecoveryKeyFragment @Inject constructor(
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
super.onViewCreated(view, savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
|
||||||
val messageKey = getString(R.string.message_key)
|
|
||||||
val recoveryPassphrase = getString(R.string.recovery_passphrase)
|
|
||||||
val color = colorProvider.getColorFromAttribute(R.attr.vctr_toolbar_link_text_color)
|
|
||||||
bootstrapSaveText.text = getString(R.string.bootstrap_save_key_description, messageKey, recoveryPassphrase)
|
|
||||||
.toSpannable()
|
|
||||||
.colorizeMatchingText(messageKey, color)
|
|
||||||
.colorizeMatchingText(recoveryPassphrase, color)
|
|
||||||
|
|
||||||
recoverySave.clickableView.debouncedClicks { downloadRecoveryKey() }
|
recoverySave.clickableView.debouncedClicks { downloadRecoveryKey() }
|
||||||
recoveryCopy.clickableView.debouncedClicks { shareRecoveryKey() }
|
recoveryCopy.clickableView.debouncedClicks { shareRecoveryKey() }
|
||||||
recoveryContinue.clickableView.debouncedClicks { sharedViewModel.handle(BootstrapActions.GoToCompleted) }
|
recoveryContinue.clickableView.debouncedClicks { sharedViewModel.handle(BootstrapActions.GoToCompleted) }
|
||||||
|
|
|
@ -0,0 +1,42 @@
|
||||||
|
/*
|
||||||
|
* 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 com.airbnb.mvrx.parentFragmentViewModel
|
||||||
|
import im.vector.riotx.R
|
||||||
|
import im.vector.riotx.core.platform.VectorBaseFragment
|
||||||
|
import kotlinx.android.synthetic.main.fragment_bootstrap_setup_recovery.*
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
class BootstrapSetupRecoveryKeyFragment @Inject constructor() : VectorBaseFragment() {
|
||||||
|
|
||||||
|
override fun getLayoutResId() = R.layout.fragment_bootstrap_setup_recovery
|
||||||
|
|
||||||
|
val sharedViewModel: BootstrapSharedViewModel by parentFragmentViewModel()
|
||||||
|
|
||||||
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
|
||||||
|
bootstrapSetupSecureSubmit.clickableView.debouncedClicks { setupRecoveryKey() }
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setupRecoveryKey() {
|
||||||
|
sharedViewModel.handle(BootstrapActions.SetupRecoveryKey)
|
||||||
|
}
|
||||||
|
}
|
|
@ -17,11 +17,9 @@
|
||||||
package im.vector.riotx.features.crypto.recover
|
package im.vector.riotx.features.crypto.recover
|
||||||
|
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import com.airbnb.mvrx.Async
|
|
||||||
import com.airbnb.mvrx.Fail
|
import com.airbnb.mvrx.Fail
|
||||||
import com.airbnb.mvrx.FragmentViewModelContext
|
import com.airbnb.mvrx.FragmentViewModelContext
|
||||||
import com.airbnb.mvrx.Loading
|
import com.airbnb.mvrx.Loading
|
||||||
import com.airbnb.mvrx.MvRxState
|
|
||||||
import com.airbnb.mvrx.MvRxViewModelFactory
|
import com.airbnb.mvrx.MvRxViewModelFactory
|
||||||
import com.airbnb.mvrx.Success
|
import com.airbnb.mvrx.Success
|
||||||
import com.airbnb.mvrx.Uninitialized
|
import com.airbnb.mvrx.Uninitialized
|
||||||
|
@ -31,7 +29,6 @@ import com.squareup.inject.assisted.AssistedInject
|
||||||
import im.vector.matrix.android.api.failure.Failure
|
import im.vector.matrix.android.api.failure.Failure
|
||||||
import im.vector.matrix.android.api.session.Session
|
import im.vector.matrix.android.api.session.Session
|
||||||
import im.vector.matrix.android.api.session.securestorage.RawBytesKeySpec
|
import im.vector.matrix.android.api.session.securestorage.RawBytesKeySpec
|
||||||
import im.vector.matrix.android.api.session.securestorage.SsssKeyCreationInfo
|
|
||||||
import im.vector.matrix.android.internal.crypto.keysbackup.model.rest.KeysVersionResult
|
import im.vector.matrix.android.internal.crypto.keysbackup.model.rest.KeysVersionResult
|
||||||
import im.vector.matrix.android.internal.crypto.keysbackup.util.extractCurveKeyFromRecoveryKey
|
import im.vector.matrix.android.internal.crypto.keysbackup.util.extractCurveKeyFromRecoveryKey
|
||||||
import im.vector.matrix.android.internal.crypto.model.rest.UserPasswordAuth
|
import im.vector.matrix.android.internal.crypto.model.rest.UserPasswordAuth
|
||||||
|
@ -46,15 +43,6 @@ import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import java.io.OutputStream
|
import java.io.OutputStream
|
||||||
|
|
||||||
data class BootstrapViewState(
|
|
||||||
val step: BootstrapStep = BootstrapStep.AccountPassword(false),
|
|
||||||
val migrationRecoveryKey: String? = null,
|
|
||||||
val crossSigningInitialization: Async<Unit> = Uninitialized,
|
|
||||||
val recoveryKeyCreationInfo: SsssKeyCreationInfo? = null,
|
|
||||||
val initializationWaitingViewData: WaitingViewData? = null,
|
|
||||||
val recoverySaveFileProcess: Async<Unit> = Uninitialized
|
|
||||||
) : MvRxState
|
|
||||||
|
|
||||||
class BootstrapSharedViewModel @AssistedInject constructor(
|
class BootstrapSharedViewModel @AssistedInject constructor(
|
||||||
@Assisted initialState: BootstrapViewState,
|
@Assisted initialState: BootstrapViewState,
|
||||||
@Assisted val args: BootstrapBottomSheet.Args,
|
@Assisted val args: BootstrapBottomSheet.Args,
|
||||||
|
@ -72,7 +60,7 @@ class BootstrapSharedViewModel @AssistedInject constructor(
|
||||||
|
|
||||||
private var _pendingSession: String? = null
|
private var _pendingSession: String? = null
|
||||||
|
|
||||||
init {
|
private fun startProcess() {
|
||||||
// need to check if user have an existing keybackup
|
// need to check if user have an existing keybackup
|
||||||
if (args.isNewAccount) {
|
if (args.isNewAccount) {
|
||||||
setState {
|
setState {
|
||||||
|
@ -137,6 +125,9 @@ class BootstrapSharedViewModel @AssistedInject constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
BootstrapActions.SetupRecoveryKey -> {
|
||||||
|
startProcess()
|
||||||
|
}
|
||||||
is BootstrapActions.DoInitializeGeneratedKey -> {
|
is BootstrapActions.DoInitializeGeneratedKey -> {
|
||||||
val userPassword = reAuthHelper.data
|
val userPassword = reAuthHelper.data
|
||||||
if (userPassword == null) {
|
if (userPassword == null) {
|
||||||
|
|
|
@ -17,6 +17,12 @@
|
||||||
package im.vector.riotx.features.crypto.recover
|
package im.vector.riotx.features.crypto.recover
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* ┌───────────────────────────────────┐
|
||||||
|
* │ BootstrapStep.SetupSecureBackup │
|
||||||
|
* └───────────────────────────────────┘
|
||||||
|
* │
|
||||||
|
* │
|
||||||
|
* ▼
|
||||||
* ┌─────────────────────────┐
|
* ┌─────────────────────────┐
|
||||||
* │ User has signing keys? │──────────── Account
|
* │ User has signing keys? │──────────── Account
|
||||||
* └─────────────────────────┘ Creation ?
|
* └─────────────────────────┘ Creation ?
|
||||||
|
@ -73,6 +79,8 @@ package im.vector.riotx.features.crypto.recover
|
||||||
*/
|
*/
|
||||||
|
|
||||||
sealed class BootstrapStep {
|
sealed class BootstrapStep {
|
||||||
|
object SetupSecureBackup : BootstrapStep()
|
||||||
|
|
||||||
data class AccountPassword(val isPasswordVisible: Boolean, val failure: String? = null) : BootstrapStep()
|
data class AccountPassword(val isPasswordVisible: Boolean, val failure: String? = null) : BootstrapStep()
|
||||||
object CheckingMigration : BootstrapStep()
|
object CheckingMigration : BootstrapStep()
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,32 @@
|
||||||
|
/*
|
||||||
|
* 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 com.airbnb.mvrx.Async
|
||||||
|
import com.airbnb.mvrx.MvRxState
|
||||||
|
import com.airbnb.mvrx.Uninitialized
|
||||||
|
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 migrationRecoveryKey: String? = null,
|
||||||
|
val crossSigningInitialization: Async<Unit> = Uninitialized,
|
||||||
|
val recoveryKeyCreationInfo: SsssKeyCreationInfo? = null,
|
||||||
|
val initializationWaitingViewData: WaitingViewData? = null,
|
||||||
|
val recoverySaveFileProcess: Async<Unit> = Uninitialized
|
||||||
|
) : MvRxState
|
|
@ -19,5 +19,6 @@ package im.vector.riotx.features.settings.crosssigning
|
||||||
import im.vector.riotx.core.platform.VectorViewModelAction
|
import im.vector.riotx.core.platform.VectorViewModelAction
|
||||||
|
|
||||||
sealed class CrossSigningSettingsAction : VectorViewModelAction {
|
sealed class CrossSigningSettingsAction : VectorViewModelAction {
|
||||||
|
object SetUpRecovery : CrossSigningSettingsAction()
|
||||||
object VerifySession : CrossSigningSettingsAction()
|
object VerifySession : CrossSigningSettingsAction()
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,6 +33,7 @@ class CrossSigningSettingsController @Inject constructor(
|
||||||
) : TypedEpoxyController<CrossSigningSettingsViewState>() {
|
) : TypedEpoxyController<CrossSigningSettingsViewState>() {
|
||||||
|
|
||||||
interface InteractionListener {
|
interface InteractionListener {
|
||||||
|
fun setupRecovery()
|
||||||
fun verifySession()
|
fun verifySession()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -68,6 +69,15 @@ class CrossSigningSettingsController @Inject constructor(
|
||||||
titleIconResourceId(R.drawable.ic_shield_black)
|
titleIconResourceId(R.drawable.ic_shield_black)
|
||||||
title(stringProvider.getString(R.string.encryption_information_dg_xsigning_not_trusted))
|
title(stringProvider.getString(R.string.encryption_information_dg_xsigning_not_trusted))
|
||||||
}
|
}
|
||||||
|
bottomSheetVerificationActionItem {
|
||||||
|
id("setup_recovery")
|
||||||
|
title(stringProvider.getString(R.string.settings_setup_secure_backup))
|
||||||
|
titleColor(colorProvider.getColorFromAttribute(R.attr.riotx_text_primary))
|
||||||
|
iconRes(R.drawable.ic_arrow_right)
|
||||||
|
listener {
|
||||||
|
interactionListener?.setupRecovery()
|
||||||
|
}
|
||||||
|
}
|
||||||
bottomSheetVerificationActionItem {
|
bottomSheetVerificationActionItem {
|
||||||
id("verify")
|
id("verify")
|
||||||
title(stringProvider.getString(R.string.crosssigning_verify_this_session))
|
title(stringProvider.getString(R.string.crosssigning_verify_this_session))
|
||||||
|
|
|
@ -53,6 +53,9 @@ class CrossSigningSettingsFragment @Inject constructor(
|
||||||
CrossSigningSettingsViewEvents.VerifySession -> {
|
CrossSigningSettingsViewEvents.VerifySession -> {
|
||||||
navigator.waitSessionVerification(requireActivity())
|
navigator.waitSessionVerification(requireActivity())
|
||||||
}
|
}
|
||||||
|
CrossSigningSettingsViewEvents.SetUpRecovery -> {
|
||||||
|
navigator.upgradeSessionSecurity(requireActivity(), false)
|
||||||
|
}
|
||||||
}.exhaustive
|
}.exhaustive
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -82,6 +85,10 @@ class CrossSigningSettingsFragment @Inject constructor(
|
||||||
super.onDestroyView()
|
super.onDestroyView()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun setupRecovery() {
|
||||||
|
viewModel.handle(CrossSigningSettingsAction.SetUpRecovery)
|
||||||
|
}
|
||||||
|
|
||||||
override fun verifySession() {
|
override fun verifySession() {
|
||||||
viewModel.handle(CrossSigningSettingsAction.VerifySession)
|
viewModel.handle(CrossSigningSettingsAction.VerifySession)
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,5 +24,6 @@ import im.vector.riotx.core.platform.VectorViewEvents
|
||||||
sealed class CrossSigningSettingsViewEvents : VectorViewEvents {
|
sealed class CrossSigningSettingsViewEvents : VectorViewEvents {
|
||||||
data class Failure(val throwable: Throwable) : CrossSigningSettingsViewEvents()
|
data class Failure(val throwable: Throwable) : CrossSigningSettingsViewEvents()
|
||||||
|
|
||||||
|
object SetUpRecovery : CrossSigningSettingsViewEvents()
|
||||||
object VerifySession : CrossSigningSettingsViewEvents()
|
object VerifySession : CrossSigningSettingsViewEvents()
|
||||||
}
|
}
|
||||||
|
|
|
@ -53,6 +53,9 @@ class CrossSigningSettingsViewModel @AssistedInject constructor(@Assisted privat
|
||||||
|
|
||||||
override fun handle(action: CrossSigningSettingsAction) {
|
override fun handle(action: CrossSigningSettingsAction) {
|
||||||
when (action) {
|
when (action) {
|
||||||
|
CrossSigningSettingsAction.SetUpRecovery -> {
|
||||||
|
_viewEvents.post(CrossSigningSettingsViewEvents.SetUpRecovery)
|
||||||
|
}
|
||||||
CrossSigningSettingsAction.VerifySession -> {
|
CrossSigningSettingsAction.VerifySession -> {
|
||||||
_viewEvents.post(CrossSigningSettingsViewEvents.VerifySession)
|
_viewEvents.post(CrossSigningSettingsViewEvents.VerifySession)
|
||||||
}
|
}
|
||||||
|
|
20
vector/src/main/res/drawable/ic_secure_backup_24dp.xml
Normal file
20
vector/src/main/res/drawable/ic_secure_backup_24dp.xml
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<group>
|
||||||
|
<clip-path
|
||||||
|
android:pathData="M1,2h21.5v5h-21.5zM1,17.7h21.5v5h-21.5z"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M3.1663,12.4014C3.1663,7.6467 6.9953,3.8177 11.75,3.8177C13.0964,3.8177 14.4429,4.1544 15.6631,4.7435H14.9899C14.5691,4.7435 14.2325,5.0801 14.2325,5.5008C14.2325,5.9216 14.5691,6.2582 14.9899,6.2582H17.3041C17.809,6.2582 18.1877,5.8375 18.1877,5.3746V3.0604C18.1877,2.6396 17.8511,2.303 17.4303,2.303C17.0096,2.303 16.673,2.6396 16.673,3.0604V3.6074C16.6309,3.5653 16.5888,3.5653 16.5467,3.5232C15.074,2.7238 13.433,2.303 11.75,2.303C6.1958,2.303 1.6515,6.8473 1.6515,12.4014C1.6515,14.0845 2.0723,15.7676 2.8717,17.2403C2.998,17.4928 3.2504,17.619 3.545,17.619C3.6712,17.619 3.7974,17.5769 3.9236,17.5348C4.3023,17.3245 4.4286,16.8616 4.2182,16.525C3.5029,15.2627 3.1663,13.8321 3.1663,12.4014Z"
|
||||||
|
android:fillColor="#2E2F32"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M20.6281,7.5626C20.4177,7.1839 19.9548,7.0577 19.6182,7.2681C19.2395,7.4785 19.1133,7.9413 19.3237,8.2779C19.9969,9.5402 20.3756,10.9288 20.3756,12.4015C20.3756,17.1562 16.5045,20.9852 11.7919,20.9852C10.4454,20.9852 9.099,20.6486 7.8787,20.0595H8.552C8.9727,20.0595 9.3094,19.7229 9.3094,19.3021C9.3094,18.8813 8.9727,18.5447 8.552,18.5447H6.2377C5.7328,18.5447 5.3541,18.9655 5.3541,19.4283V21.7426C5.3541,22.1633 5.6908,22.4999 6.1115,22.4999C6.5323,22.4999 6.8689,22.1633 6.8689,21.7426V21.1956C6.911,21.2376 6.9531,21.2376 6.9951,21.2797C8.4257,22.0792 10.0667,22.4999 11.7498,22.4999C17.304,22.4999 21.8483,17.9556 21.8483,12.4015C21.8483,10.7184 21.4275,9.0353 20.6281,7.5626Z"
|
||||||
|
android:fillColor="#2E2F32"/>
|
||||||
|
</group>
|
||||||
|
<path
|
||||||
|
android:pathData="M3,9C1.8954,9 1,9.8954 1,11V14C1,15.1046 1.8954,16 3,16H21C22.1046,16 23,15.1046 23,14V11C23,9.8954 22.1046,9 21,9H3ZM5.25,10.5C4.8358,10.5 4.5,10.8358 4.5,11.25C4.5,11.6642 4.8358,12 5.25,12H7.75C8.1642,12 8.5,11.6642 8.5,11.25C8.5,10.8358 8.1642,10.5 7.75,10.5H5.25ZM9.5,11.25C9.5,10.8358 9.8358,10.5 10.25,10.5H10.75C11.1642,10.5 11.5,10.8358 11.5,11.25C11.5,11.6642 11.1642,12 10.75,12H10.25C9.8358,12 9.5,11.6642 9.5,11.25ZM13.25,10.5C12.8358,10.5 12.5,10.8358 12.5,11.25C12.5,11.6642 12.8358,12 13.25,12H15.75C16.1642,12 16.5,11.6642 16.5,11.25C16.5,10.8358 16.1642,10.5 15.75,10.5H13.25ZM17.5,11.25C17.5,10.8358 17.8358,10.5 18.25,10.5H18.75C19.1642,10.5 19.5,10.8358 19.5,11.25C19.5,11.6642 19.1642,12 18.75,12H18.25C17.8358,12 17.5,11.6642 17.5,11.25ZM5.25,13C4.8358,13 4.5,13.3358 4.5,13.75C4.5,14.1642 4.8358,14.5 5.25,14.5H5.75C6.1642,14.5 6.5,14.1642 6.5,13.75C6.5,13.3358 6.1642,13 5.75,13H5.25ZM7.5,13.75C7.5,13.3358 7.8358,13 8.25,13H10.75C11.1642,13 11.5,13.3358 11.5,13.75C11.5,14.1642 11.1642,14.5 10.75,14.5H8.25C7.8358,14.5 7.5,14.1642 7.5,13.75ZM13.25,13C12.8358,13 12.5,13.3358 12.5,13.75C12.5,14.1642 12.8358,14.5 13.25,14.5H13.75C14.1642,14.5 14.5,14.1642 14.5,13.75C14.5,13.3358 14.1642,13 13.75,13H13.25Z"
|
||||||
|
android:fillColor="#2E2F32"
|
||||||
|
android:fillType="evenOdd"/>
|
||||||
|
</vector>
|
|
@ -29,7 +29,7 @@
|
||||||
android:layout_height="32dp"
|
android:layout_height="32dp"
|
||||||
android:contentDescription="@string/avatar"
|
android:contentDescription="@string/avatar"
|
||||||
android:scaleType="fitCenter"
|
android:scaleType="fitCenter"
|
||||||
android:src="@drawable/ic_message_key" />
|
android:src="@drawable/ic_secure_backup_24dp" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/bootstrapTitleText"
|
android:id="@+id/bootstrapTitleText"
|
||||||
|
|
|
@ -15,10 +15,9 @@
|
||||||
android:layout_marginStart="16dp"
|
android:layout_marginStart="16dp"
|
||||||
android:layout_marginTop="8dp"
|
android:layout_marginTop="8dp"
|
||||||
android:layout_marginEnd="16dp"
|
android:layout_marginEnd="16dp"
|
||||||
android:text="@string/bootstrap_save_key_description"
|
android:text="@string/bottom_sheet_save_your_recovery_key_content"
|
||||||
android:textColor="?riotx_text_primary"
|
android:textColor="?riotx_text_primary"
|
||||||
android:textSize="14sp"
|
android:textSize="14sp" />
|
||||||
app:layout_constraintTop_toTopOf="parent" />
|
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/bootstrapRecoveryKeyText"
|
android:id="@+id/bootstrapRecoveryKeyText"
|
||||||
|
@ -44,7 +43,7 @@
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:minHeight="50dp"
|
android:minHeight="50dp"
|
||||||
app:actionTitle="@string/copy_value"
|
app:actionTitle="@string/action_copy"
|
||||||
app:leftIcon="@drawable/ic_clipboard"
|
app:leftIcon="@drawable/ic_clipboard"
|
||||||
app:rightIcon="@drawable/ic_arrow_right"
|
app:rightIcon="@drawable/ic_arrow_right"
|
||||||
app:tint="?colorAccent" />
|
app:tint="?colorAccent" />
|
||||||
|
|
|
@ -0,0 +1,53 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:minHeight="200dp"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/bootstrapSetupSecureText1"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="16dp"
|
||||||
|
android:layout_marginTop="8dp"
|
||||||
|
android:layout_marginEnd="16dp"
|
||||||
|
android:text="@string/bottom_sheet_setup_secure_backup_content_1"
|
||||||
|
android:textColor="?riotx_text_primary"
|
||||||
|
android:textSize="14sp" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/bootstrapSetupSecureText2"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="16dp"
|
||||||
|
android:layout_marginTop="8dp"
|
||||||
|
android:layout_marginEnd="16dp"
|
||||||
|
android:text="@string/bottom_sheet_setup_secure_backup_content_2"
|
||||||
|
android:textColor="?riotx_text_primary"
|
||||||
|
android:textSize="14sp"
|
||||||
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
|
<View
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="1dp"
|
||||||
|
android:layout_marginTop="@dimen/layout_vertical_margin"
|
||||||
|
android:background="?attr/vctr_list_divider_color" />
|
||||||
|
|
||||||
|
<im.vector.riotx.core.ui.views.BottomSheetActionButton
|
||||||
|
android:id="@+id/bootstrapSetupSecureSubmit"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:minHeight="50dp"
|
||||||
|
app:actionTitle="@string/bottom_sheet_setup_secure_backup_submit"
|
||||||
|
app:rightIcon="@drawable/ic_arrow_right"
|
||||||
|
app:tint="?colorAccent" />
|
||||||
|
|
||||||
|
<View
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="1dp"
|
||||||
|
android:background="?attr/vctr_list_divider_color" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
|
@ -19,7 +19,7 @@
|
||||||
app:layout_constraintBottom_toBottomOf="@+id/ssss_restore_with_key"
|
app:layout_constraintBottom_toBottomOf="@+id/ssss_restore_with_key"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toTopOf="@+id/ssss_restore_with_key"
|
app:layout_constraintTop_toTopOf="@+id/ssss_restore_with_key"
|
||||||
android:src="@drawable/ic_message_key" />
|
android:src="@drawable/ic_secure_backup_24dp" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/ssss_restore_with_key"
|
android:id="@+id/ssss_restore_with_key"
|
||||||
|
|
|
@ -19,7 +19,7 @@
|
||||||
app:layout_constraintBottom_toBottomOf="@+id/ssss_restore_with_passphrase"
|
app:layout_constraintBottom_toBottomOf="@+id/ssss_restore_with_passphrase"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toTopOf="@+id/ssss_restore_with_passphrase"
|
app:layout_constraintTop_toTopOf="@+id/ssss_restore_with_passphrase"
|
||||||
android:src="@drawable/ic_message_password" />
|
android:src="@drawable/ic_secure_backup_24dp" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/ssss_restore_with_passphrase"
|
android:id="@+id/ssss_restore_with_passphrase"
|
||||||
|
@ -98,7 +98,7 @@
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:text="@string/use_recovery_key"
|
android:text="@string/use_recovery_key"
|
||||||
app:icon="@drawable/ic_message_key"
|
app:icon="@drawable/ic_secure_backup_24dp"
|
||||||
tools:ignore="MissingConstraints" />
|
tools:ignore="MissingConstraints" />
|
||||||
|
|
||||||
<com.google.android.material.button.MaterialButton
|
<com.google.android.material.button.MaterialButton
|
||||||
|
|
|
@ -117,6 +117,7 @@
|
||||||
<string name="action_mark_room_read">Mark as read</string>
|
<string name="action_mark_room_read">Mark as read</string>
|
||||||
<string name="action_open">Open</string>
|
<string name="action_open">Open</string>
|
||||||
<string name="action_close">Close</string>
|
<string name="action_close">Close</string>
|
||||||
|
<string name="action_copy">Copy</string>
|
||||||
<string name="copied_to_clipboard">Copied to clipboard</string>
|
<string name="copied_to_clipboard">Copied to clipboard</string>
|
||||||
<string name="disable">Disable</string>
|
<string name="disable">Disable</string>
|
||||||
|
|
||||||
|
@ -2478,4 +2479,14 @@ Not all features in Riot are implemented in RiotX yet. Main missing (and coming
|
||||||
<string name="a11y_stop_camera">Stop the camera</string>
|
<string name="a11y_stop_camera">Stop the camera</string>
|
||||||
<string name="a11y_start_camera">Start the camera</string>
|
<string name="a11y_start_camera">Start the camera</string>
|
||||||
|
|
||||||
|
<string name="settings_setup_secure_backup">Set up Secure Backup</string>
|
||||||
|
|
||||||
|
<string name="bottom_sheet_setup_secure_backup_title">Set up Secure Backup</string>
|
||||||
|
<string name="bottom_sheet_setup_secure_backup_content_1">Backup your encryption keys with your account data in case you lose access to your logins.</string>
|
||||||
|
<string name="bottom_sheet_setup_secure_backup_content_2">Your keys will be secured with a unique Recovery Key.</string>
|
||||||
|
<string name="bottom_sheet_setup_secure_backup_submit">Set up</string>
|
||||||
|
|
||||||
|
<string name="bottom_sheet_save_your_recovery_key_title">Save your Recovery Key</string>
|
||||||
|
<string name="bottom_sheet_save_your_recovery_key_content">Store your Recovery Key somewhere safe. It can be used to unlock your encrypted messages and data.</string>
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
|
|
Loading…
Reference in a new issue