Browse Source

Merge pull request #1648 from vector-im/feature/bma/secureBackup

Secure backup
pull/1698/head
Benoit Marty 11 months ago committed by GitHub
parent
commit
5728d621bb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 3
      appconfig/src/main/kotlin/io/element/android/appconfig/SecureBackupConfig.kt
  2. 1
      features/logout/impl/src/main/kotlin/io/element/android/features/logout/impl/LogoutNode.kt
  3. 7
      features/logout/impl/src/main/kotlin/io/element/android/features/logout/impl/LogoutPresenter.kt
  4. 16
      features/logout/impl/src/main/kotlin/io/element/android/features/logout/impl/LogoutView.kt
  5. 19
      features/logout/impl/src/test/kotlin/io/element/android/features/logout/impl/LogoutPresenterTest.kt
  6. 29
      features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListView.kt
  7. 1
      features/securebackup/impl/build.gradle.kts
  8. 1
      features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/disable/SecureBackupDisableNode.kt
  9. 14
      features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/disable/SecureBackupDisableView.kt
  10. 1
      features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/enable/SecureBackupEnableNode.kt
  11. 14
      features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/enable/SecureBackupEnableView.kt
  12. 3
      features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/enter/SecureBackupEnterRecoveryKeyNode.kt
  13. 21
      features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/enter/SecureBackupEnterRecoveryKeyView.kt
  14. 2
      features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/root/SecureBackupRootNode.kt
  15. 1
      features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/setup/SecureBackupSetupNode.kt
  16. 28
      features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/setup/SecureBackupSetupView.kt
  17. 1
      features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/setup/SecureBackupSetupViewChangePreview.kt
  18. 23
      features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/setup/views/RecoveryKeyView.kt
  19. 6
      libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/encryption/EncryptionService.kt
  20. 15
      libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/encryption/RustEncryptionService.kt
  21. 12
      libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/encryption/FakeEncryptionService.kt
  22. BIN
      tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl_null_LogoutView-D-0_0_null_0,NEXUS_5,1.0,en].png
  23. BIN
      tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl_null_LogoutView-D-0_0_null_1,NEXUS_5,1.0,en].png
  24. BIN
      tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl_null_LogoutView-D-0_0_null_2,NEXUS_5,1.0,en].png
  25. BIN
      tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl_null_LogoutView-D-0_0_null_3,NEXUS_5,1.0,en].png
  26. BIN
      tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl_null_LogoutView-D-0_0_null_4,NEXUS_5,1.0,en].png
  27. BIN
      tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl_null_LogoutView-D-0_0_null_5,NEXUS_5,1.0,en].png
  28. BIN
      tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl_null_LogoutView-D-0_0_null_6,NEXUS_5,1.0,en].png
  29. BIN
      tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl_null_LogoutView-N-0_1_null_0,NEXUS_5,1.0,en].png
  30. BIN
      tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl_null_LogoutView-N-0_1_null_1,NEXUS_5,1.0,en].png
  31. BIN
      tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl_null_LogoutView-N-0_1_null_2,NEXUS_5,1.0,en].png
  32. BIN
      tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl_null_LogoutView-N-0_1_null_3,NEXUS_5,1.0,en].png
  33. BIN
      tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl_null_LogoutView-N-0_1_null_4,NEXUS_5,1.0,en].png
  34. BIN
      tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl_null_LogoutView-N-0_1_null_5,NEXUS_5,1.0,en].png
  35. BIN
      tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl_null_LogoutView-N-0_1_null_6,NEXUS_5,1.0,en].png
  36. BIN
      tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.disable_null_SecureBackupDisableView-D-0_0_null_0,NEXUS_5,1.0,en].png
  37. BIN
      tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.disable_null_SecureBackupDisableView-D-0_0_null_1,NEXUS_5,1.0,en].png
  38. BIN
      tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.disable_null_SecureBackupDisableView-D-0_0_null_2,NEXUS_5,1.0,en].png
  39. BIN
      tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.disable_null_SecureBackupDisableView-D-0_0_null_3,NEXUS_5,1.0,en].png
  40. BIN
      tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.disable_null_SecureBackupDisableView-N-0_1_null_0,NEXUS_5,1.0,en].png
  41. BIN
      tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.disable_null_SecureBackupDisableView-N-0_1_null_1,NEXUS_5,1.0,en].png
  42. BIN
      tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.disable_null_SecureBackupDisableView-N-0_1_null_2,NEXUS_5,1.0,en].png
  43. BIN
      tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.disable_null_SecureBackupDisableView-N-0_1_null_3,NEXUS_5,1.0,en].png
  44. BIN
      tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enable_null_SecureBackupEnableView-D-1_1_null_0,NEXUS_5,1.0,en].png
  45. BIN
      tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enable_null_SecureBackupEnableView-D-1_1_null_1,NEXUS_5,1.0,en].png
  46. BIN
      tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enable_null_SecureBackupEnableView-D-1_1_null_2,NEXUS_5,1.0,en].png
  47. BIN
      tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enable_null_SecureBackupEnableView-N-1_2_null_0,NEXUS_5,1.0,en].png
  48. BIN
      tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enable_null_SecureBackupEnableView-N-1_2_null_1,NEXUS_5,1.0,en].png
  49. BIN
      tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enable_null_SecureBackupEnableView-N-1_2_null_2,NEXUS_5,1.0,en].png
  50. BIN
      tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enter_null_SecureBackupEnterRecoveryKeyView-D-2_2_null_0,NEXUS_5,1.0,en].png
  51. BIN
      tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enter_null_SecureBackupEnterRecoveryKeyView-D-2_2_null_1,NEXUS_5,1.0,en].png
  52. BIN
      tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enter_null_SecureBackupEnterRecoveryKeyView-D-2_2_null_2,NEXUS_5,1.0,en].png
  53. BIN
      tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enter_null_SecureBackupEnterRecoveryKeyView-D-2_2_null_3,NEXUS_5,1.0,en].png
  54. BIN
      tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enter_null_SecureBackupEnterRecoveryKeyView-N-2_3_null_0,NEXUS_5,1.0,en].png
  55. BIN
      tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enter_null_SecureBackupEnterRecoveryKeyView-N-2_3_null_1,NEXUS_5,1.0,en].png
  56. BIN
      tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enter_null_SecureBackupEnterRecoveryKeyView-N-2_3_null_2,NEXUS_5,1.0,en].png
  57. BIN
      tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enter_null_SecureBackupEnterRecoveryKeyView-N-2_3_null_3,NEXUS_5,1.0,en].png
  58. BIN
      tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_null_SecureBackupSetupView-D-4_4_null_0,NEXUS_5,1.0,en].png
  59. BIN
      tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_null_SecureBackupSetupView-D-4_4_null_1,NEXUS_5,1.0,en].png
  60. BIN
      tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_null_SecureBackupSetupView-D-4_4_null_2,NEXUS_5,1.0,en].png
  61. BIN
      tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_null_SecureBackupSetupView-D-4_4_null_3,NEXUS_5,1.0,en].png
  62. BIN
      tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_null_SecureBackupSetupView-D-4_4_null_4,NEXUS_5,1.0,en].png
  63. BIN
      tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_null_SecureBackupSetupView-N-4_5_null_0,NEXUS_5,1.0,en].png
  64. BIN
      tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_null_SecureBackupSetupView-N-4_5_null_1,NEXUS_5,1.0,en].png
  65. BIN
      tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_null_SecureBackupSetupView-N-4_5_null_2,NEXUS_5,1.0,en].png
  66. BIN
      tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_null_SecureBackupSetupView-N-4_5_null_3,NEXUS_5,1.0,en].png
  67. BIN
      tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_null_SecureBackupSetupView-N-4_5_null_4,NEXUS_5,1.0,en].png
  68. BIN
      tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_null_SecureBackupSetupViewChange-D-5_5_null_0,NEXUS_5,1.0,en].png
  69. BIN
      tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_null_SecureBackupSetupViewChange-D-5_5_null_1,NEXUS_5,1.0,en].png
  70. BIN
      tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_null_SecureBackupSetupViewChange-D-5_5_null_2,NEXUS_5,1.0,en].png
  71. BIN
      tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_null_SecureBackupSetupViewChange-D-5_5_null_3,NEXUS_5,1.0,en].png
  72. BIN
      tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_null_SecureBackupSetupViewChange-D-5_5_null_4,NEXUS_5,1.0,en].png
  73. BIN
      tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_null_SecureBackupSetupViewChange-N-5_6_null_0,NEXUS_5,1.0,en].png
  74. BIN
      tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_null_SecureBackupSetupViewChange-N-5_6_null_1,NEXUS_5,1.0,en].png
  75. BIN
      tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_null_SecureBackupSetupViewChange-N-5_6_null_2,NEXUS_5,1.0,en].png
  76. BIN
      tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_null_SecureBackupSetupViewChange-N-5_6_null_3,NEXUS_5,1.0,en].png
  77. BIN
      tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_null_SecureBackupSetupViewChange-N-5_6_null_4,NEXUS_5,1.0,en].png

3
features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/SecureBackupConfig.kt → appconfig/src/main/kotlin/io/element/android/appconfig/SecureBackupConfig.kt

@ -14,9 +14,8 @@ @@ -14,9 +14,8 @@
* limitations under the License.
*/
package io.element.android.features.securebackup.impl
package io.element.android.appconfig
// TODO Move to appconfig module when it will be available
object SecureBackupConfig {
const val LearnMoreUrl: String = "https://element.io/help#encryption5"
}

1
features/logout/impl/src/main/kotlin/io/element/android/features/logout/impl/LogoutNode.kt

@ -58,6 +58,7 @@ class LogoutNode @AssistedInject constructor( @@ -58,6 +58,7 @@ class LogoutNode @AssistedInject constructor(
state = state,
onChangeRecoveryKeyClicked = ::onChangeRecoveryKeyClicked,
onSuccessLogout = { onSuccessLogout(activity, it) },
onBackClicked = ::navigateUp,
modifier = modifier,
)
}

7
features/logout/impl/src/main/kotlin/io/element/android/features/logout/impl/LogoutPresenter.kt

@ -29,6 +29,7 @@ import io.element.android.libraries.architecture.Async @@ -29,6 +29,7 @@ import io.element.android.libraries.architecture.Async
import io.element.android.libraries.architecture.Presenter
import io.element.android.libraries.architecture.runCatchingUpdatingState
import io.element.android.libraries.matrix.api.MatrixClient
import io.element.android.libraries.matrix.api.encryption.BackupUploadState
import io.element.android.libraries.matrix.api.encryption.EncryptionService
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
@ -46,13 +47,15 @@ class LogoutPresenter @Inject constructor( @@ -46,13 +47,15 @@ class LogoutPresenter @Inject constructor(
mutableStateOf(Async.Uninitialized)
}
val backupUploadState by encryptionService.backupUploadStateStateFlow.collectAsState()
val backupUploadState: BackupUploadState by remember {
encryptionService.waitForBackupUploadSteadyState()
}
.collectAsState(initial = BackupUploadState.Unknown)
var showLogoutDialog by remember { mutableStateOf(false) }
var isLastSession by remember { mutableStateOf(false) }
LaunchedEffect(Unit) {
isLastSession = encryptionService.isLastDevice().getOrNull() ?: false
encryptionService.waitForBackupUploadSteadyState()
}
fun handleEvents(event: LogoutEvents) {

16
features/logout/impl/src/main/kotlin/io/element/android/features/logout/impl/LogoutView.kt

@ -20,6 +20,7 @@ import androidx.compose.foundation.layout.Arrangement @@ -20,6 +20,7 @@ import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.ui.Alignment
@ -32,6 +33,7 @@ import io.element.android.libraries.designsystem.atomic.molecules.ButtonColumnMo @@ -32,6 +33,7 @@ import io.element.android.libraries.designsystem.atomic.molecules.ButtonColumnMo
import io.element.android.libraries.designsystem.atomic.molecules.IconTitleSubtitleMolecule
import io.element.android.libraries.designsystem.atomic.pages.HeaderFooterPage
import io.element.android.libraries.designsystem.components.ProgressDialog
import io.element.android.libraries.designsystem.components.button.BackButton
import io.element.android.libraries.designsystem.components.dialogs.ConfirmationDialog
import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
@ -39,16 +41,19 @@ import io.element.android.libraries.designsystem.theme.components.Button @@ -39,16 +41,19 @@ import io.element.android.libraries.designsystem.theme.components.Button
import io.element.android.libraries.designsystem.theme.components.LinearProgressIndicator
import io.element.android.libraries.designsystem.theme.components.OutlinedButton
import io.element.android.libraries.designsystem.theme.components.Text
import io.element.android.libraries.designsystem.theme.components.TopAppBar
import io.element.android.libraries.designsystem.theme.progressIndicatorTrackColor
import io.element.android.libraries.designsystem.utils.CommonDrawables
import io.element.android.libraries.matrix.api.encryption.BackupUploadState
import io.element.android.libraries.theme.ElementTheme
import io.element.android.libraries.ui.strings.CommonStrings
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun LogoutView(
state: LogoutState,
onChangeRecoveryKeyClicked: () -> Unit,
onBackClicked: () -> Unit,
onSuccessLogout: (logoutUrlResult: String?) -> Unit,
modifier: Modifier = Modifier,
) {
@ -56,6 +61,12 @@ fun LogoutView( @@ -56,6 +61,12 @@ fun LogoutView(
HeaderFooterPage(
modifier = modifier,
topBar = {
TopAppBar(
navigationIcon = { BackButton(onClick = onBackClicked) },
title = {},
)
},
header = {
HeaderContent(state = state)
},
@ -134,7 +145,7 @@ private fun HeaderContent( @@ -134,7 +145,7 @@ private fun HeaderContent(
else -> null
}
val paddingTop = 60.dp
val paddingTop = 0.dp
IconTitleSubtitleMolecule(
modifier = modifier.padding(top = paddingTop),
iconResourceId = CommonDrawables.ic_key,
@ -219,6 +230,7 @@ internal fun LogoutViewPreview( @@ -219,6 +230,7 @@ internal fun LogoutViewPreview(
LogoutView(
state,
onChangeRecoveryKeyClicked = {},
onSuccessLogout = {}
onSuccessLogout = {},
onBackClicked = {},
)
}

19
features/logout/impl/src/test/kotlin/io/element/android/features/logout/impl/LogoutPresenterTest.kt

@ -28,6 +28,8 @@ import io.element.android.libraries.matrix.test.A_THROWABLE @@ -28,6 +28,8 @@ import io.element.android.libraries.matrix.test.A_THROWABLE
import io.element.android.libraries.matrix.test.FakeMatrixClient
import io.element.android.libraries.matrix.test.encryption.FakeEncryptionService
import io.element.android.tests.testutils.WarmUpRule
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.test.runTest
import org.junit.Rule
import org.junit.Test
@ -73,6 +75,15 @@ class LogoutPresenterTest { @@ -73,6 +75,15 @@ class LogoutPresenterTest {
@Test
fun `present - initial state - backing up`() = runTest {
val encryptionService = FakeEncryptionService()
encryptionService.givenWaitForBackupUploadSteadyStateFlow(
flow {
emit(BackupUploadState.Waiting)
delay(1)
emit(BackupUploadState.Uploading(backedUpCount = 1, totalCount = 2))
delay(1)
emit(BackupUploadState.Done)
}
)
val presenter = createLogoutPresenter(
encryptionService = encryptionService
)
@ -84,10 +95,10 @@ class LogoutPresenterTest { @@ -84,10 +95,10 @@ class LogoutPresenterTest {
assertThat(initialState.backupUploadState).isEqualTo(BackupUploadState.Unknown)
assertThat(initialState.showConfirmationDialog).isFalse()
assertThat(initialState.logoutAction).isEqualTo(Async.Uninitialized)
encryptionService.emitBackupUploadState(BackupUploadState.Uploading(backedUpCount = 1, totalCount = 2))
val state = awaitItem()
assertThat(state.backupUploadState).isEqualTo(BackupUploadState.Uploading(backedUpCount = 1, totalCount = 2))
encryptionService.emitBackupUploadState(BackupUploadState.Done)
val waitingState = awaitItem()
assertThat(waitingState.backupUploadState).isEqualTo(BackupUploadState.Waiting)
val uploadingState = awaitItem()
assertThat(uploadingState.backupUploadState).isEqualTo(BackupUploadState.Uploading(backedUpCount = 1, totalCount = 2))
val doneState = awaitItem()
assertThat(doneState.backupUploadState).isEqualTo(BackupUploadState.Done)
}

29
features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListView.kt

@ -52,8 +52,8 @@ import io.element.android.features.roomlist.impl.components.RoomListTopBar @@ -52,8 +52,8 @@ import io.element.android.features.roomlist.impl.components.RoomListTopBar
import io.element.android.features.roomlist.impl.components.RoomSummaryRow
import io.element.android.features.roomlist.impl.model.RoomListRoomSummary
import io.element.android.features.roomlist.impl.search.RoomListSearchResultView
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.theme.components.FloatingActionButton
import io.element.android.libraries.designsystem.theme.components.HorizontalDivider
import io.element.android.libraries.designsystem.theme.components.Icon
@ -190,19 +190,22 @@ private fun RoomListContent( @@ -190,19 +190,22 @@ private fun RoomListContent(
.nestedScroll(nestedScrollConnection),
state = lazyListState,
) {
if (state.displayVerificationPrompt) {
item {
RequestVerificationHeader(
onVerifyClicked = onVerifyClicked,
onDismissClicked = { state.eventSink(RoomListEvents.DismissRequestVerificationPrompt) }
)
when {
state.displayVerificationPrompt -> {
item {
RequestVerificationHeader(
onVerifyClicked = onVerifyClicked,
onDismissClicked = { state.eventSink(RoomListEvents.DismissRequestVerificationPrompt) }
)
}
}
} else if (state.displayRecoveryKeyPrompt) {
item {
ConfirmRecoveryKeyBanner(
onContinueClicked = onOpenSettings,
onDismissClicked = { state.eventSink(RoomListEvents.DismissRecoveryKeyPrompt) }
)
state.displayRecoveryKeyPrompt -> {
item {
ConfirmRecoveryKeyBanner(
onContinueClicked = onOpenSettings,
onDismissClicked = { state.eventSink(RoomListEvents.DismissRecoveryKeyPrompt) }
)
}
}
}

1
features/securebackup/impl/build.gradle.kts

@ -33,6 +33,7 @@ dependencies { @@ -33,6 +33,7 @@ dependencies {
anvil(projects.anvilcodegen)
implementation(projects.anvilannotations)
implementation(projects.appconfig)
implementation(projects.libraries.androidutils)
implementation(projects.libraries.core)
implementation(projects.libraries.androidutils)

1
features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/disable/SecureBackupDisableNode.kt

@ -40,6 +40,7 @@ class SecureBackupDisableNode @AssistedInject constructor( @@ -40,6 +40,7 @@ class SecureBackupDisableNode @AssistedInject constructor(
state = state,
modifier = modifier,
onDone = ::navigateUp,
onBackClicked = ::navigateUp,
)
}
}

14
features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/disable/SecureBackupDisableView.kt

@ -22,6 +22,7 @@ import androidx.compose.foundation.layout.Row @@ -22,6 +22,7 @@ import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.ui.Modifier
@ -33,6 +34,7 @@ import io.element.android.libraries.architecture.Async @@ -33,6 +34,7 @@ import io.element.android.libraries.architecture.Async
import io.element.android.libraries.designsystem.atomic.molecules.ButtonColumnMolecule
import io.element.android.libraries.designsystem.atomic.molecules.IconTitleSubtitleMolecule
import io.element.android.libraries.designsystem.atomic.pages.HeaderFooterPage
import io.element.android.libraries.designsystem.components.button.BackButton
import io.element.android.libraries.designsystem.components.dialogs.ConfirmationDialog
import io.element.android.libraries.designsystem.components.dialogs.ErrorDialog
import io.element.android.libraries.designsystem.preview.ElementPreview
@ -40,13 +42,16 @@ import io.element.android.libraries.designsystem.preview.PreviewsDayNight @@ -40,13 +42,16 @@ import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.theme.components.Button
import io.element.android.libraries.designsystem.theme.components.Icon
import io.element.android.libraries.designsystem.theme.components.Text
import io.element.android.libraries.designsystem.theme.components.TopAppBar
import io.element.android.libraries.designsystem.utils.CommonDrawables
import io.element.android.libraries.theme.ElementTheme
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun SecureBackupDisableView(
state: SecureBackupDisableState,
onDone: () -> Unit,
onBackClicked: () -> Unit,
modifier: Modifier = Modifier,
) {
LaunchedEffect(state.disableAction) {
@ -56,6 +61,12 @@ fun SecureBackupDisableView( @@ -56,6 +61,12 @@ fun SecureBackupDisableView(
}
HeaderFooterPage(
modifier = modifier,
topBar = {
TopAppBar(
navigationIcon = { BackButton(onClick = onBackClicked) },
title = {},
)
},
header = {
HeaderContent()
},
@ -95,7 +106,7 @@ private fun HeaderContent( @@ -95,7 +106,7 @@ private fun HeaderContent(
modifier: Modifier = Modifier,
) {
IconTitleSubtitleMolecule(
modifier = modifier.padding(top = 60.dp),
modifier = modifier.padding(top = 0.dp),
iconResourceId = CommonDrawables.ic_key_off,
title = stringResource(id = R.string.screen_key_backup_disable_title),
subTitle = stringResource(id = R.string.screen_key_backup_disable_description),
@ -158,5 +169,6 @@ internal fun SecureBackupDisableViewPreview( @@ -158,5 +169,6 @@ internal fun SecureBackupDisableViewPreview(
SecureBackupDisableView(
state = state,
onDone = {},
onBackClicked = {},
)
}

1
features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/enable/SecureBackupEnableNode.kt

@ -40,6 +40,7 @@ class SecureBackupEnableNode @AssistedInject constructor( @@ -40,6 +40,7 @@ class SecureBackupEnableNode @AssistedInject constructor(
state = state,
modifier = modifier,
onDone = ::navigateUp,
onBackClicked = ::navigateUp,
)
}
}

14
features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/enable/SecureBackupEnableView.kt

@ -18,6 +18,7 @@ package io.element.android.features.securebackup.impl.enable @@ -18,6 +18,7 @@ package io.element.android.features.securebackup.impl.enable
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.ui.Modifier
@ -29,16 +30,20 @@ import io.element.android.libraries.architecture.Async @@ -29,16 +30,20 @@ import io.element.android.libraries.architecture.Async
import io.element.android.libraries.designsystem.atomic.molecules.ButtonColumnMolecule
import io.element.android.libraries.designsystem.atomic.molecules.IconTitleSubtitleMolecule
import io.element.android.libraries.designsystem.atomic.pages.HeaderFooterPage
import io.element.android.libraries.designsystem.components.button.BackButton
import io.element.android.libraries.designsystem.components.dialogs.ErrorDialog
import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.theme.components.Button
import io.element.android.libraries.designsystem.theme.components.TopAppBar
import io.element.android.libraries.designsystem.utils.CommonDrawables
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun SecureBackupEnableView(
state: SecureBackupEnableState,
onDone: () -> Unit,
onBackClicked: () -> Unit,
modifier: Modifier = Modifier,
) {
LaunchedEffect(state.enableAction) {
@ -48,6 +53,12 @@ fun SecureBackupEnableView( @@ -48,6 +53,12 @@ fun SecureBackupEnableView(
}
HeaderFooterPage(
modifier = modifier,
topBar = {
TopAppBar(
navigationIcon = { BackButton(onClick = onBackClicked) },
title = {},
)
},
header = {
HeaderContent()
},
@ -68,7 +79,7 @@ private fun HeaderContent( @@ -68,7 +79,7 @@ private fun HeaderContent(
modifier: Modifier = Modifier,
) {
IconTitleSubtitleMolecule(
modifier = modifier.padding(top = 60.dp),
modifier = modifier.padding(top = 0.dp),
iconResourceId = CommonDrawables.ic_key,
title = stringResource(id = R.string.screen_chat_backup_key_backup_action_enable),
subTitle = null,
@ -99,5 +110,6 @@ internal fun SecureBackupEnableViewPreview( @@ -99,5 +110,6 @@ internal fun SecureBackupEnableViewPreview(
SecureBackupEnableView(
state = state,
onDone = {},
onBackClicked = {},
)
}

3
features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/enter/SecureBackupEnterRecoveryKeyNode.kt

@ -50,7 +50,8 @@ class SecureBackupEnterRecoveryKeyNode @AssistedInject constructor( @@ -50,7 +50,8 @@ class SecureBackupEnterRecoveryKeyNode @AssistedInject constructor(
onDone = {
coroutineScope.postSuccessSnackbar()
navigateUp()
}
},
onBackClicked = ::navigateUp,
)
}

21
features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/enter/SecureBackupEnterRecoveryKeyView.kt

@ -18,6 +18,7 @@ package io.element.android.features.securebackup.impl.enter @@ -18,6 +18,7 @@ package io.element.android.features.securebackup.impl.enter
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.ui.Modifier
@ -30,17 +31,21 @@ import io.element.android.libraries.architecture.Async @@ -30,17 +31,21 @@ import io.element.android.libraries.architecture.Async
import io.element.android.libraries.designsystem.atomic.molecules.ButtonColumnMolecule
import io.element.android.libraries.designsystem.atomic.molecules.IconTitleSubtitleMolecule
import io.element.android.libraries.designsystem.atomic.pages.HeaderFooterPage
import io.element.android.libraries.designsystem.components.button.BackButton
import io.element.android.libraries.designsystem.components.dialogs.ErrorDialog
import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.theme.components.Button
import io.element.android.libraries.designsystem.theme.components.TopAppBar
import io.element.android.libraries.designsystem.utils.CommonDrawables
import io.element.android.libraries.ui.strings.CommonStrings
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun SecureBackupEnterRecoveryKeyView(
state: SecureBackupEnterRecoveryKeyState,
onDone: () -> Unit,
onBackClicked: () -> Unit,
modifier: Modifier = Modifier,
) {
when (state.submitAction) {
@ -59,6 +64,12 @@ fun SecureBackupEnterRecoveryKeyView( @@ -59,6 +64,12 @@ fun SecureBackupEnterRecoveryKeyView(
HeaderFooterPage(
modifier = modifier,
topBar = {
TopAppBar(
navigationIcon = { BackButton(onClick = onBackClicked) },
title = {},
)
},
header = {
HeaderContent()
},
@ -75,6 +86,9 @@ fun SecureBackupEnterRecoveryKeyView( @@ -75,6 +86,9 @@ fun SecureBackupEnterRecoveryKeyView(
state = state,
onChange = {
state.eventSink.invoke(SecureBackupEnterRecoveryKeyEvents.OnRecoveryKeyChange(it))
},
onSubmit = {
state.eventSink.invoke(SecureBackupEnterRecoveryKeyEvents.Submit)
})
}
}
@ -84,7 +98,7 @@ private fun HeaderContent( @@ -84,7 +98,7 @@ private fun HeaderContent(
modifier: Modifier = Modifier,
) {
IconTitleSubtitleMolecule(
modifier = modifier.padding(top = 60.dp),
modifier = modifier.padding(top = 0.dp),
iconResourceId = CommonDrawables.ic_key,
title = stringResource(id = R.string.screen_recovery_key_confirm_title),
subTitle = stringResource(id = R.string.screen_recovery_key_confirm_description),
@ -112,13 +126,15 @@ private fun BottomMenu( @@ -112,13 +126,15 @@ private fun BottomMenu(
@Composable
private fun Content(
state: SecureBackupEnterRecoveryKeyState,
onChange: ((String) -> Unit)?,
onChange: (String) -> Unit,
onSubmit: () -> Unit,
) {
RecoveryKeyView(
modifier = Modifier.padding(top = 52.dp),
state = state.recoveryKeyViewState,
onClick = null,
onChange = onChange,
onSubmit = onSubmit,
)
}
@ -130,5 +146,6 @@ internal fun SecureBackupEnterRecoveryKeyViewPreview( @@ -130,5 +146,6 @@ internal fun SecureBackupEnterRecoveryKeyViewPreview(
SecureBackupEnterRecoveryKeyView(
state = state,
onDone = {},
onBackClicked = {},
)
}

2
features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/root/SecureBackupRootNode.kt

@ -27,7 +27,7 @@ import com.bumble.appyx.core.plugin.plugins @@ -27,7 +27,7 @@ import com.bumble.appyx.core.plugin.plugins
import dagger.assisted.Assisted
import dagger.assisted.AssistedInject
import io.element.android.anvilannotations.ContributesNode
import io.element.android.features.securebackup.impl.SecureBackupConfig
import io.element.android.appconfig.SecureBackupConfig
import io.element.android.libraries.di.SessionScope
@ContributesNode(SessionScope::class)

1
features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/setup/SecureBackupSetupNode.kt

@ -60,6 +60,7 @@ class SecureBackupSetupNode @AssistedInject constructor( @@ -60,6 +60,7 @@ class SecureBackupSetupNode @AssistedInject constructor(
coroutineScope.postSuccessSnackbar()
navigateUp()
},
onBackClicked = ::navigateUp,
modifier = modifier,
)
}

28
features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/setup/SecureBackupSetupView.kt

@ -16,8 +16,10 @@ @@ -16,8 +16,10 @@
package io.element.android.features.securebackup.impl.setup
import androidx.activity.compose.BackHandler
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
@ -32,24 +34,42 @@ import io.element.android.libraries.androidutils.system.startSharePlainTextInten @@ -32,24 +34,42 @@ import io.element.android.libraries.androidutils.system.startSharePlainTextInten
import io.element.android.libraries.designsystem.atomic.molecules.ButtonColumnMolecule
import io.element.android.libraries.designsystem.atomic.molecules.IconTitleSubtitleMolecule
import io.element.android.libraries.designsystem.atomic.pages.HeaderFooterPage
import io.element.android.libraries.designsystem.components.button.BackButton
import io.element.android.libraries.designsystem.components.dialogs.ConfirmationDialog
import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.theme.components.Button
import io.element.android.libraries.designsystem.theme.components.IconSource
import io.element.android.libraries.designsystem.theme.components.OutlinedButton
import io.element.android.libraries.designsystem.theme.components.TopAppBar
import io.element.android.libraries.designsystem.utils.CommonDrawables
import io.element.android.libraries.ui.strings.CommonStrings
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun SecureBackupSetupView(
state: SecureBackupSetupState,
onDone: () -> Unit,
onBackClicked: () -> Unit,
modifier: Modifier = Modifier,
) {
val context = LocalContext.current
val canGoBack = state.canGoBack()
BackHandler(enabled = canGoBack) {
onBackClicked()
}
HeaderFooterPage(
modifier = modifier,
topBar = {
TopAppBar(
navigationIcon = {
if (canGoBack) {
BackButton(onClick = onBackClicked)
}
},
title = {},
)
},
header = {
HeaderContent(state = state)
},
@ -109,6 +129,10 @@ fun SecureBackupSetupView( @@ -109,6 +129,10 @@ fun SecureBackupSetupView(
}
}
private fun SecureBackupSetupState.canGoBack(): Boolean {
return recoveryKeyViewState.formattedRecoveryKey == null
}
@Composable
private fun HeaderContent(
state: SecureBackupSetupState,
@ -136,7 +160,7 @@ private fun HeaderContent( @@ -136,7 +160,7 @@ private fun HeaderContent(
stringResource(id = R.string.screen_recovery_key_save_description)
}
IconTitleSubtitleMolecule(
modifier = modifier.padding(top = 60.dp),
modifier = modifier.padding(top = 0.dp),
iconResourceId = CommonDrawables.ic_key,
title = title,
subTitle = subTitle,
@ -192,6 +216,7 @@ private fun Content( @@ -192,6 +216,7 @@ private fun Content(
state = state,
onClick = onClick,
onChange = null,
onSubmit = null,
)
}
@ -203,5 +228,6 @@ internal fun SecureBackupSetupViewPreview( @@ -203,5 +228,6 @@ internal fun SecureBackupSetupViewPreview(
SecureBackupSetupView(
state = state,
onDone = {},
onBackClicked = {},
)
}

1
features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/setup/SecureBackupSetupViewChangePreview.kt

@ -33,5 +33,6 @@ internal fun SecureBackupSetupViewChangePreview( @@ -33,5 +33,6 @@ internal fun SecureBackupSetupViewChangePreview(
recoveryKeyViewState = state.recoveryKeyViewState.copy(recoveryKeyUserStory = RecoveryKeyUserStory.Change),
),
onDone = {},
onBackClicked = {},
)
}

23
features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/setup/views/RecoveryKeyView.kt

@ -25,12 +25,15 @@ import androidx.compose.foundation.layout.padding @@ -25,12 +25,15 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.progressSemantics
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.unit.dp
@ -52,6 +55,7 @@ internal fun RecoveryKeyView( @@ -52,6 +55,7 @@ internal fun RecoveryKeyView(
state: RecoveryKeyViewState,
onClick: (() -> Unit)?,
onChange: ((String) -> Unit)?,
onSubmit: (() -> Unit)?,
modifier: Modifier = Modifier,
) {
Column(
@ -63,7 +67,7 @@ internal fun RecoveryKeyView( @@ -63,7 +67,7 @@ internal fun RecoveryKeyView(
modifier = Modifier.padding(start = 16.dp),
style = ElementTheme.typography.fontBodyMdRegular,
)
RecoveryKeyContent(state, onClick, onChange)
RecoveryKeyContent(state, onClick, onChange, onSubmit)
RecoveryKeyFooter(state)
}
}
@ -73,11 +77,12 @@ private fun RecoveryKeyContent( @@ -73,11 +77,12 @@ private fun RecoveryKeyContent(
state: RecoveryKeyViewState,
onClick: (() -> Unit)?,
onChange: ((String) -> Unit)?,
onSubmit: (() -> Unit)?,
) {
when (state.recoveryKeyUserStory) {
RecoveryKeyUserStory.Setup,
RecoveryKeyUserStory.Change -> RecoveryKeyStaticContent(state, onClick)
RecoveryKeyUserStory.Enter -> RecoveryKeyFormContent(state, onChange)
RecoveryKeyUserStory.Enter -> RecoveryKeyFormContent(state, onChange, onSubmit)
}
}
@ -143,8 +148,13 @@ private fun RecoveryKeyStaticContent( @@ -143,8 +148,13 @@ private fun RecoveryKeyStaticContent(
}
@Composable
private fun RecoveryKeyFormContent(state: RecoveryKeyViewState, onChange: ((String) -> Unit)?) {
private fun RecoveryKeyFormContent(
state: RecoveryKeyViewState,
onChange: ((String) -> Unit)?,
onSubmit: (() -> Unit)?,
) {
onChange ?: error("onChange should not be null")
onSubmit ?: error("onSubmit should not be null")
val recoveryKeyVisualTransformation = remember {
RecoveryKeyVisualTransformation()
}
@ -155,6 +165,12 @@ private fun RecoveryKeyFormContent(state: RecoveryKeyViewState, onChange: ((Stri @@ -155,6 +165,12 @@ private fun RecoveryKeyFormContent(state: RecoveryKeyViewState, onChange: ((Stri
onValueChange = onChange,
enabled = state.inProgress.not(),
visualTransformation = recoveryKeyVisualTransformation,
keyboardOptions = KeyboardOptions(
imeAction = ImeAction.Done,
),
keyboardActions = KeyboardActions(
onDone = { onSubmit() }
),
label = { Text(text = stringResource(id = R.string.screen_recovery_key_confirm_key_placeholder)) }
)
}
@ -217,5 +233,6 @@ internal fun RecoveryKeyViewPreview( @@ -217,5 +233,6 @@ internal fun RecoveryKeyViewPreview(
state = state,
onClick = {},
onChange = {},
onSubmit = {},
)
}

6
libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/encryption/EncryptionService.kt

@ -16,12 +16,12 @@ @@ -16,12 +16,12 @@
package io.element.android.libraries.matrix.api.encryption
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.StateFlow
interface EncryptionService {
val backupStateStateFlow: StateFlow<BackupState>
val recoveryStateStateFlow: StateFlow<RecoveryState>
val backupUploadStateStateFlow: StateFlow<BackupUploadState>
val enableRecoveryProgressStateFlow: StateFlow<EnableRecoveryProgress>
suspend fun enableBackups(): Result<Unit>
@ -46,7 +46,7 @@ interface EncryptionService { @@ -46,7 +46,7 @@ interface EncryptionService {
suspend fun fixRecoveryIssues(recoveryKey: String): Result<Unit>
/**
* Observe [backupUploadStateStateFlow] to get progress.
* Wait for backup upload steady state.
*/
suspend fun waitForBackupUploadSteadyState(): Result<Unit>
fun waitForBackupUploadSteadyState(): Flow<BackupUploadState>
}

15
libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/encryption/RustEncryptionService.kt

@ -22,7 +22,10 @@ import io.element.android.libraries.matrix.api.encryption.BackupUploadState @@ -22,7 +22,10 @@ import io.element.android.libraries.matrix.api.encryption.BackupUploadState
import io.element.android.libraries.matrix.api.encryption.EnableRecoveryProgress
import io.element.android.libraries.matrix.api.encryption.EncryptionService
import io.element.android.libraries.matrix.api.encryption.RecoveryState
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.callbackFlow
import kotlinx.coroutines.withContext
import org.matrix.rustcomponents.sdk.BackupStateListener
import org.matrix.rustcomponents.sdk.BackupSteadyStateListener
@ -50,7 +53,6 @@ internal class RustEncryptionService( @@ -50,7 +53,6 @@ internal class RustEncryptionService(
override val backupStateStateFlow: MutableStateFlow<BackupState> = MutableStateFlow(service.backupState().let(backupStateMapper::map))
override val recoveryStateStateFlow: MutableStateFlow<RecoveryState> = MutableStateFlow(service.recoveryState().let(recoveryStateMapper::map))
override val enableRecoveryProgressStateFlow: MutableStateFlow<EnableRecoveryProgress> = MutableStateFlow(EnableRecoveryProgress.Unknown)
override val backupUploadStateStateFlow: MutableStateFlow<BackupUploadState> = MutableStateFlow(BackupUploadState.Unknown)
fun start() {
service.backupStateListener(object : BackupStateListener {
@ -94,16 +96,19 @@ internal class RustEncryptionService( @@ -94,16 +96,19 @@ internal class RustEncryptionService(
}
}
override suspend fun waitForBackupUploadSteadyState(
): Result<Unit> = withContext(dispatchers.io) {
runCatching {
override fun waitForBackupUploadSteadyState(): Flow<BackupUploadState> {
return callbackFlow {
service.waitForBackupUploadSteadyState(
progressListener = object : BackupSteadyStateListener {
override fun onUpdate(status: RustBackupUploadState) {
backupUploadStateStateFlow.value = backupUploadStateMapper.map(status)
trySend(backupUploadStateMapper.map(status))
if (status == RustBackupUploadState.Done) {
close()
}
}
}
)
awaitClose {}
}
}

12
libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/encryption/FakeEncryptionService.kt

@ -22,14 +22,16 @@ import io.element.android.libraries.matrix.api.encryption.EnableRecoveryProgress @@ -22,14 +22,16 @@ import io.element.android.libraries.matrix.api.encryption.EnableRecoveryProgress
import io.element.android.libraries.matrix.api.encryption.EncryptionService
import io.element.android.libraries.matrix.api.encryption.RecoveryState
import io.element.android.tests.testutils.simulateLongTask
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.flowOf
class FakeEncryptionService : EncryptionService {
private var disableRecoveryFailure: Exception? = null
override val backupStateStateFlow: MutableStateFlow<BackupState> = MutableStateFlow(BackupState.UNKNOWN)
override val recoveryStateStateFlow: MutableStateFlow<RecoveryState> = MutableStateFlow(RecoveryState.UNKNOWN)
override val enableRecoveryProgressStateFlow: MutableStateFlow<EnableRecoveryProgress> = MutableStateFlow(EnableRecoveryProgress.Unknown)
override val backupUploadStateStateFlow: MutableStateFlow<BackupUploadState> = MutableStateFlow(BackupUploadState.Unknown)
private var waitForBackupUploadSteadyStateFlow: Flow<BackupUploadState> = flowOf()
private var fixRecoveryIssuesFailure: Exception? = null
@ -73,12 +75,12 @@ class FakeEncryptionService : EncryptionService { @@ -73,12 +75,12 @@ class FakeEncryptionService : EncryptionService {
return Result.success(Unit)
}
override suspend fun waitForBackupUploadSteadyState(): Result<Unit> {
return Result.success(Unit)
fun givenWaitForBackupUploadSteadyStateFlow(flow: Flow<BackupUploadState>) {
waitForBackupUploadSteadyStateFlow = flow
}
suspend fun emitBackupUploadState(state: BackupUploadState) {
backupUploadStateStateFlow.emit(state)
override fun waitForBackupUploadSteadyState(): Flow<BackupUploadState> {
return waitForBackupUploadSteadyStateFlow
}
suspend fun emitBackupState(state: BackupState) {

BIN
tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl_null_LogoutView-D-0_0_null_0,NEXUS_5,1.0,en].png (Stored with Git LFS)

Binary file not shown.

BIN
tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl_null_LogoutView-D-0_0_null_1,NEXUS_5,1.0,en].png (Stored with Git LFS)

Binary file not shown.

BIN
tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl_null_LogoutView-D-0_0_null_2,NEXUS_5,1.0,en].png (Stored with Git LFS)

Binary file not shown.

BIN
tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl_null_LogoutView-D-0_0_null_3,NEXUS_5,1.0,en].png (Stored with Git LFS)

Binary file not shown.

BIN
tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl_null_LogoutView-D-0_0_null_4,NEXUS_5,1.0,en].png (Stored with Git LFS)

Binary file not shown.

BIN
tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl_null_LogoutView-D-0_0_null_5,NEXUS_5,1.0,en].png (Stored with Git LFS)

Binary file not shown.

BIN
tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl_null_LogoutView-D-0_0_null_6,NEXUS_5,1.0,en].png (Stored with Git LFS)

Binary file not shown.

BIN
tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl_null_LogoutView-N-0_1_null_0,NEXUS_5,1.0,en].png (Stored with Git LFS)

Binary file not shown.

BIN
tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl_null_LogoutView-N-0_1_null_1,NEXUS_5,1.0,en].png (Stored with Git LFS)

Binary file not shown.

BIN
tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl_null_LogoutView-N-0_1_null_2,NEXUS_5,1.0,en].png (Stored with Git LFS)

Binary file not shown.

BIN
tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl_null_LogoutView-N-0_1_null_3,NEXUS_5,1.0,en].png (Stored with Git LFS)

Binary file not shown.

BIN
tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl_null_LogoutView-N-0_1_null_4,NEXUS_5,1.0,en].png (Stored with Git LFS)

Binary file not shown.

BIN
tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl_null_LogoutView-N-0_1_null_5,NEXUS_5,1.0,en].png (Stored with Git LFS)

Binary file not shown.

BIN
tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl_null_LogoutView-N-0_1_null_6,NEXUS_5,1.0,en].png (Stored with Git LFS)

Binary file not shown.

BIN
tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.disable_null_SecureBackupDisableView-D-0_0_null_0,NEXUS_5,1.0,en].png (Stored with Git LFS)

Binary file not shown.

BIN
tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.disable_null_SecureBackupDisableView-D-0_0_null_1,NEXUS_5,1.0,en].png (Stored with Git LFS)

Binary file not shown.

BIN
tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.disable_null_SecureBackupDisableView-D-0_0_null_2,NEXUS_5,1.0,en].png (Stored with Git LFS)

Binary file not shown.

BIN
tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.disable_null_SecureBackupDisableView-D-0_0_null_3,NEXUS_5,1.0,en].png (Stored with Git LFS)

Binary file not shown.

BIN
tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.disable_null_SecureBackupDisableView-N-0_1_null_0,NEXUS_5,1.0,en].png (Stored with Git LFS)

Binary file not shown.

BIN
tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.disable_null_SecureBackupDisableView-N-0_1_null_1,NEXUS_5,1.0,en].png (Stored with Git LFS)

Binary file not shown.

BIN
tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.disable_null_SecureBackupDisableView-N-0_1_null_2,NEXUS_5,1.0,en].png (Stored with Git LFS)

Binary file not shown.

BIN
tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.disable_null_SecureBackupDisableView-N-0_1_null_3,NEXUS_5,1.0,en].png (Stored with Git LFS)

Binary file not shown.

BIN
tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enable_null_SecureBackupEnableView-D-1_1_null_0,NEXUS_5,1.0,en].png (Stored with Git LFS)

Binary file not shown.

BIN
tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enable_null_SecureBackupEnableView-D-1_1_null_1,NEXUS_5,1.0,en].png (Stored with Git LFS)

Binary file not shown.

BIN
tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enable_null_SecureBackupEnableView-D-1_1_null_2,NEXUS_5,1.0,en].png (Stored with Git LFS)

Binary file not shown.

BIN
tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enable_null_SecureBackupEnableView-N-1_2_null_0,NEXUS_5,1.0,en].png (Stored with Git LFS)

Binary file not shown.

BIN
tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enable_null_SecureBackupEnableView-N-1_2_null_1,NEXUS_5,1.0,en].png (Stored with Git LFS)

Binary file not shown.

BIN
tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enable_null_SecureBackupEnableView-N-1_2_null_2,NEXUS_5,1.0,en].png (Stored with Git LFS)

Binary file not shown.

BIN
tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enter_null_SecureBackupEnterRecoveryKeyView-D-2_2_null_0,NEXUS_5,1.0,en].png (Stored with Git LFS)

Binary file not shown.

BIN
tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enter_null_SecureBackupEnterRecoveryKeyView-D-2_2_null_1,NEXUS_5,1.0,en].png (Stored with Git LFS)

Binary file not shown.

BIN
tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enter_null_SecureBackupEnterRecoveryKeyView-D-2_2_null_2,NEXUS_5,1.0,en].png (Stored with Git LFS)

Binary file not shown.

BIN
tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enter_null_SecureBackupEnterRecoveryKeyView-D-2_2_null_3,NEXUS_5,1.0,en].png (Stored with Git LFS)

Binary file not shown.

BIN
tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enter_null_SecureBackupEnterRecoveryKeyView-N-2_3_null_0,NEXUS_5,1.0,en].png (Stored with Git LFS)

Binary file not shown.

BIN
tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enter_null_SecureBackupEnterRecoveryKeyView-N-2_3_null_1,NEXUS_5,1.0,en].png (Stored with Git LFS)

Binary file not shown.

BIN
tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enter_null_SecureBackupEnterRecoveryKeyView-N-2_3_null_2,NEXUS_5,1.0,en].png (Stored with Git LFS)

Binary file not shown.

BIN
tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.enter_null_SecureBackupEnterRecoveryKeyView-N-2_3_null_3,NEXUS_5,1.0,en].png (Stored with Git LFS)

Binary file not shown.

BIN
tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_null_SecureBackupSetupView-D-4_4_null_0,NEXUS_5,1.0,en].png (Stored with Git LFS)

Binary file not shown.

BIN
tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_null_SecureBackupSetupView-D-4_4_null_1,NEXUS_5,1.0,en].png (Stored with Git LFS)

Binary file not shown.

BIN
tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_null_SecureBackupSetupView-D-4_4_null_2,NEXUS_5,1.0,en].png (Stored with Git LFS)

Binary file not shown.

BIN
tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_null_SecureBackupSetupView-D-4_4_null_3,NEXUS_5,1.0,en].png (Stored with Git LFS)

Binary file not shown.

BIN
tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_null_SecureBackupSetupView-D-4_4_null_4,NEXUS_5,1.0,en].png (Stored with Git LFS)

Binary file not shown.

BIN
tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_null_SecureBackupSetupView-N-4_5_null_0,NEXUS_5,1.0,en].png (Stored with Git LFS)

Binary file not shown.

BIN
tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_null_SecureBackupSetupView-N-4_5_null_1,NEXUS_5,1.0,en].png (Stored with Git LFS)

Binary file not shown.

BIN
tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_null_SecureBackupSetupView-N-4_5_null_2,NEXUS_5,1.0,en].png (Stored with Git LFS)

Binary file not shown.

BIN
tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_null_SecureBackupSetupView-N-4_5_null_3,NEXUS_5,1.0,en].png (Stored with Git LFS)

Binary file not shown.

BIN
tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_null_SecureBackupSetupView-N-4_5_null_4,NEXUS_5,1.0,en].png (Stored with Git LFS)

Binary file not shown.

BIN
tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_null_SecureBackupSetupViewChange-D-5_5_null_0,NEXUS_5,1.0,en].png (Stored with Git LFS)

Binary file not shown.

BIN
tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_null_SecureBackupSetupViewChange-D-5_5_null_1,NEXUS_5,1.0,en].png (Stored with Git LFS)

Binary file not shown.

BIN
tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_null_SecureBackupSetupViewChange-D-5_5_null_2,NEXUS_5,1.0,en].png (Stored with Git LFS)

Binary file not shown.

BIN
tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_null_SecureBackupSetupViewChange-D-5_5_null_3,NEXUS_5,1.0,en].png (Stored with Git LFS)

Binary file not shown.

BIN
tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_null_SecureBackupSetupViewChange-D-5_5_null_4,NEXUS_5,1.0,en].png (Stored with Git LFS)

Binary file not shown.

BIN
tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_null_SecureBackupSetupViewChange-N-5_6_null_0,NEXUS_5,1.0,en].png (Stored with Git LFS)

Binary file not shown.

BIN
tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_null_SecureBackupSetupViewChange-N-5_6_null_1,NEXUS_5,1.0,en].png (Stored with Git LFS)

Binary file not shown.

BIN
tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_null_SecureBackupSetupViewChange-N-5_6_null_2,NEXUS_5,1.0,en].png (Stored with Git LFS)

Binary file not shown.

BIN
tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_null_SecureBackupSetupViewChange-N-5_6_null_3,NEXUS_5,1.0,en].png (Stored with Git LFS)

Binary file not shown.

BIN
tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.setup_null_SecureBackupSetupViewChange-N-5_6_null_4,NEXUS_5,1.0,en].png (Stored with Git LFS)

Binary file not shown.
Loading…
Cancel
Save