Bootstrap: Add an introduction step: BootstrapSetupRecoveryKeyFragment

This commit is contained in:
Benoit Marty 2020-06-16 18:02:55 +02:00 committed by Valere
parent 369f40c804
commit f3b464b88a
21 changed files with 226 additions and 40 deletions

View file

@ -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.BootstrapMigrateBackupFragment
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.verification.cancel.VerificationCancelFragment
import im.vector.riotx.features.crypto.verification.cancel.VerificationNotMeFragment
@ -456,6 +457,11 @@ interface FragmentModule {
@FragmentKey(BootstrapWaitingFragment::class)
fun bindBootstrapWaitingFragment(fragment: BootstrapWaitingFragment): Fragment
@Binds
@IntoMap
@FragmentKey(BootstrapSetupRecoveryKeyFragment::class)
fun bindBootstrapSetupRecoveryKeyFragment(fragment: BootstrapSetupRecoveryKeyFragment): Fragment
@Binds
@IntoMap
@FragmentKey(BootstrapSaveRecoveryKeyFragment::class)

View file

@ -27,6 +27,8 @@ sealed class BootstrapActions : VectorViewModelAction {
object GoToCompleted : BootstrapActions()
object GoToEnterAccountPassword : BootstrapActions()
object SetupRecoveryKey : BootstrapActions()
object DoInitializeGeneratedKey : BootstrapActions()
object TogglePasswordVisibility : BootstrapActions()
data class ReAuth(val pass: String) : BootstrapActions()

View file

@ -121,8 +121,12 @@ class BootstrapBottomSheet : VectorBaseBottomSheetDialogFragment() {
}
override fun invalidate() = withState(viewModel) { state ->
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 -> {
bootstrapIcon.setImageDrawable(ContextCompat.getDrawable(requireContext(), R.drawable.ic_message_password))
bootstrapTitleText.text = getString(R.string.upgrade_security)
@ -134,17 +138,17 @@ class BootstrapBottomSheet : VectorBaseBottomSheetDialogFragment() {
showFragment(BootstrapAccountPasswordFragment::class, Bundle())
}
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)
showFragment(BootstrapWaitingFragment::class, Bundle())
}
is BootstrapStep.SaveRecoveryKey -> {
bootstrapIcon.setImageDrawable(ContextCompat.getDrawable(requireContext(), R.drawable.ic_message_key))
bootstrapTitleText.text = getString(R.string.keys_backup_setup_step3_please_make_copy)
bootstrapIcon.setImageDrawable(ContextCompat.getDrawable(requireContext(), R.drawable.ic_secure_backup_24dp))
bootstrapTitleText.text = getString(R.string.bottom_sheet_save_your_recovery_key_title)
showFragment(BootstrapSaveRecoveryKeyFragment::class, Bundle())
}
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)
showFragment(BootstrapConclusionFragment::class, Bundle())
}
@ -153,7 +157,7 @@ class BootstrapBottomSheet : VectorBaseBottomSheetDialogFragment() {
is BootstrapStep.GetBackupSecretPassForMigration -> state.step.useKey
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(
requireContext(),
drawableRes)

View file

@ -16,10 +16,8 @@
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.MatrixError
import im.vector.matrix.android.api.failure.toRegistrationFlowResponse
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.MASTER_KEY_SSSS_NAME
@ -71,19 +69,23 @@ data class Params(
val keySpec: SsssKeySpec? = null
)
// TODO Rename to CreateServerRecovery
class BootstrapCrossSigningTask @Inject constructor(
private val session: Session,
private val stringProvider: StringProvider
) : ViewModelTask<Params, BootstrapResult> {
override suspend fun execute(params: Params): BootstrapResult {
val crossSigningService = session.cryptoService().crossSigningService()
// TODO Remove
/*
params.progressListener?.onProgress(
WaitingViewData(
stringProvider.getString(R.string.bootstrap_crosssigning_progress_initializing),
isIndeterminate = true
)
)
val crossSigningService = session.cryptoService().crossSigningService()
try {
awaitCallback<Unit> {
@ -92,6 +94,7 @@ class BootstrapCrossSigningTask @Inject constructor(
} catch (failure: Throwable) {
return handleInitializeXSigningError(failure)
}
*/
val keyInfo: SsssKeyCreationInfo
@ -214,6 +217,8 @@ class BootstrapCrossSigningTask @Inject constructor(
return BootstrapResult.Success(keyInfo)
}
/*
TODO Remove
private fun handleInitializeXSigningError(failure: Throwable): BootstrapResult {
if (failure is Failure.ServerError && failure.error.code == MatrixError.M_FORBIDDEN) {
return BootstrapResult.InvalidPasswordError(failure.error)
@ -230,4 +235,5 @@ class BootstrapCrossSigningTask @Inject constructor(
}
return BootstrapResult.GenericError(failure)
}
*/
}

View file

@ -21,14 +21,12 @@ import android.content.ActivityNotFoundException
import android.content.Intent
import android.os.Bundle
import android.view.View
import androidx.core.text.toSpannable
import androidx.core.view.isVisible
import com.airbnb.mvrx.parentFragmentViewModel
import com.airbnb.mvrx.withState
import im.vector.riotx.R
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.core.utils.startSharePlainTextIntent
import im.vector.riotx.core.utils.toast
import kotlinx.android.synthetic.main.fragment_bootstrap_save_key.*
@ -48,14 +46,6 @@ class BootstrapSaveRecoveryKeyFragment @Inject constructor(
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
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() }
recoveryCopy.clickableView.debouncedClicks { shareRecoveryKey() }
recoveryContinue.clickableView.debouncedClicks { sharedViewModel.handle(BootstrapActions.GoToCompleted) }

View file

@ -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)
}
}

View file

@ -17,11 +17,9 @@
package im.vector.riotx.features.crypto.recover
import androidx.lifecycle.viewModelScope
import com.airbnb.mvrx.Async
import com.airbnb.mvrx.Fail
import com.airbnb.mvrx.FragmentViewModelContext
import com.airbnb.mvrx.Loading
import com.airbnb.mvrx.MvRxState
import com.airbnb.mvrx.MvRxViewModelFactory
import com.airbnb.mvrx.Success
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.session.Session
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.util.extractCurveKeyFromRecoveryKey
import im.vector.matrix.android.internal.crypto.model.rest.UserPasswordAuth
@ -46,15 +43,6 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
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(
@Assisted initialState: BootstrapViewState,
@Assisted val args: BootstrapBottomSheet.Args,
@ -72,7 +60,7 @@ class BootstrapSharedViewModel @AssistedInject constructor(
private var _pendingSession: String? = null
init {
private fun startProcess() {
// need to check if user have an existing keybackup
if (args.isNewAccount) {
setState {
@ -137,6 +125,9 @@ class BootstrapSharedViewModel @AssistedInject constructor(
}
}
}
BootstrapActions.SetupRecoveryKey -> {
startProcess()
}
is BootstrapActions.DoInitializeGeneratedKey -> {
val userPassword = reAuthHelper.data
if (userPassword == null) {

View file

@ -17,6 +17,12 @@
package im.vector.riotx.features.crypto.recover
/**
*
* BootstrapStep.SetupSecureBackup
*
*
*
*
*
* User has signing keys? Account
* Creation ?
@ -73,6 +79,8 @@ package im.vector.riotx.features.crypto.recover
*/
sealed class BootstrapStep {
object SetupSecureBackup : BootstrapStep()
data class AccountPassword(val isPasswordVisible: Boolean, val failure: String? = null) : BootstrapStep()
object CheckingMigration : BootstrapStep()

View file

@ -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

View file

@ -19,5 +19,6 @@ package im.vector.riotx.features.settings.crosssigning
import im.vector.riotx.core.platform.VectorViewModelAction
sealed class CrossSigningSettingsAction : VectorViewModelAction {
object SetUpRecovery : CrossSigningSettingsAction()
object VerifySession : CrossSigningSettingsAction()
}

View file

@ -33,6 +33,7 @@ class CrossSigningSettingsController @Inject constructor(
) : TypedEpoxyController<CrossSigningSettingsViewState>() {
interface InteractionListener {
fun setupRecovery()
fun verifySession()
}
@ -68,6 +69,15 @@ class CrossSigningSettingsController @Inject constructor(
titleIconResourceId(R.drawable.ic_shield_black)
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 {
id("verify")
title(stringProvider.getString(R.string.crosssigning_verify_this_session))

View file

@ -53,6 +53,9 @@ class CrossSigningSettingsFragment @Inject constructor(
CrossSigningSettingsViewEvents.VerifySession -> {
navigator.waitSessionVerification(requireActivity())
}
CrossSigningSettingsViewEvents.SetUpRecovery -> {
navigator.upgradeSessionSecurity(requireActivity(), false)
}
}.exhaustive
}
}
@ -82,6 +85,10 @@ class CrossSigningSettingsFragment @Inject constructor(
super.onDestroyView()
}
override fun setupRecovery() {
viewModel.handle(CrossSigningSettingsAction.SetUpRecovery)
}
override fun verifySession() {
viewModel.handle(CrossSigningSettingsAction.VerifySession)
}

View file

@ -24,5 +24,6 @@ import im.vector.riotx.core.platform.VectorViewEvents
sealed class CrossSigningSettingsViewEvents : VectorViewEvents {
data class Failure(val throwable: Throwable) : CrossSigningSettingsViewEvents()
object SetUpRecovery : CrossSigningSettingsViewEvents()
object VerifySession : CrossSigningSettingsViewEvents()
}

View file

@ -53,6 +53,9 @@ class CrossSigningSettingsViewModel @AssistedInject constructor(@Assisted privat
override fun handle(action: CrossSigningSettingsAction) {
when (action) {
CrossSigningSettingsAction.SetUpRecovery -> {
_viewEvents.post(CrossSigningSettingsViewEvents.SetUpRecovery)
}
CrossSigningSettingsAction.VerifySession -> {
_viewEvents.post(CrossSigningSettingsViewEvents.VerifySession)
}

View 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>

View file

@ -29,7 +29,7 @@
android:layout_height="32dp"
android:contentDescription="@string/avatar"
android:scaleType="fitCenter"
android:src="@drawable/ic_message_key" />
android:src="@drawable/ic_secure_backup_24dp" />
<TextView
android:id="@+id/bootstrapTitleText"

View file

@ -15,10 +15,9 @@
android:layout_marginStart="16dp"
android:layout_marginTop="8dp"
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:textSize="14sp"
app:layout_constraintTop_toTopOf="parent" />
android:textSize="14sp" />
<TextView
android:id="@+id/bootstrapRecoveryKeyText"
@ -44,7 +43,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="50dp"
app:actionTitle="@string/copy_value"
app:actionTitle="@string/action_copy"
app:leftIcon="@drawable/ic_clipboard"
app:rightIcon="@drawable/ic_arrow_right"
app:tint="?colorAccent" />

View file

@ -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>

View file

@ -19,7 +19,7 @@
app:layout_constraintBottom_toBottomOf="@+id/ssss_restore_with_key"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@+id/ssss_restore_with_key"
android:src="@drawable/ic_message_key" />
android:src="@drawable/ic_secure_backup_24dp" />
<TextView
android:id="@+id/ssss_restore_with_key"

View file

@ -19,7 +19,7 @@
app:layout_constraintBottom_toBottomOf="@+id/ssss_restore_with_passphrase"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@+id/ssss_restore_with_passphrase"
android:src="@drawable/ic_message_password" />
android:src="@drawable/ic_secure_backup_24dp" />
<TextView
android:id="@+id/ssss_restore_with_passphrase"
@ -98,7 +98,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/use_recovery_key"
app:icon="@drawable/ic_message_key"
app:icon="@drawable/ic_secure_backup_24dp"
tools:ignore="MissingConstraints" />
<com.google.android.material.button.MaterialButton

View file

@ -117,6 +117,7 @@
<string name="action_mark_room_read">Mark as read</string>
<string name="action_open">Open</string>
<string name="action_close">Close</string>
<string name="action_copy">Copy</string>
<string name="copied_to_clipboard">Copied to clipboard</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_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>