Merge pull request #2429 from element-hq/feature/bma/testRecoveryKey
Test recovery key
This commit is contained in:
13
.github/workflows/maestro.yml
vendored
13
.github/workflows/maestro.yml
vendored
@@ -55,9 +55,10 @@ jobs:
|
||||
# app-file should point to an x86 compatible APK file, so upload the x86_64 one (much smaller than the universal APK).
|
||||
app-file: app/build/outputs/apk/gplay/debug/app-gplay-x86_64-debug.apk
|
||||
env: |
|
||||
USERNAME=maestroelement
|
||||
PASSWORD=${{ secrets.MATRIX_MAESTRO_ACCOUNT_PASSWORD }}
|
||||
ROOM_NAME=MyRoom
|
||||
INVITEE1_MXID=@maestroelement2:matrix.org
|
||||
INVITEE2_MXID=@maestroelement3:matrix.org
|
||||
APP_ID=io.element.android.x.debug
|
||||
MAESTRO_USERNAME=maestroelement
|
||||
MAESTRO_PASSWORD=${{ secrets.MATRIX_MAESTRO_ACCOUNT_PASSWORD }}
|
||||
MAESTRO_RECOVERY_KEY=${{ secrets.MATRIX_MAESTRO_ACCOUNT_RECOVERY_KEY }}
|
||||
MAESTRO_ROOM_NAME=MyRoom
|
||||
MAESTRO_INVITEE1_MXID=@maestroelement2:matrix.org
|
||||
MAESTRO_INVITEE2_MXID=@maestroelement3:matrix.org
|
||||
MAESTRO_APP_ID=io.element.android.x.debug
|
||||
|
||||
@@ -22,12 +22,13 @@ From root dir of the project
|
||||
|
||||
```shell
|
||||
maestro test \
|
||||
-e APP_ID=io.element.android.x.debug \
|
||||
-e USERNAME=user1 \
|
||||
-e PASSWORD=123 \
|
||||
-e ROOM_NAME="MyRoom" \
|
||||
-e INVITEE1_MXID=user2 \
|
||||
-e INVITEE2_MXID=user3 \
|
||||
-e MAESTRO_APP_ID=io.element.android.x.debug \
|
||||
-e MAESTRO_USERNAME=user1 \
|
||||
-e MAESTRO_PASSWORD=123 \
|
||||
-e MAESTRO_RECOVERY_KEY=ABC \
|
||||
-e MAESTRO_ROOM_NAME="MyRoom" \
|
||||
-e MAESTRO_INVITEE1_MXID=user2 \
|
||||
-e MAESTRO_INVITEE2_MXID=user3 \
|
||||
.maestro/allTests.yaml
|
||||
```
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
appId: ${APP_ID}
|
||||
appId: ${MAESTRO_APP_ID}
|
||||
---
|
||||
## Check that all env variables required in the whole test suite are declared (to fail faster)
|
||||
- runScript: ./scripts/checkEnv.js
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
// This array contains all the required environment variable. When adding a variable, add it here also.
|
||||
// If a variable is missing, an error will occur.
|
||||
|
||||
if (APP_ID == null) throw "Fatal: missing env variable APP_ID"
|
||||
if (USERNAME == null) throw "Fatal: missing env variable USERNAME"
|
||||
if (PASSWORD == null) throw "Fatal: missing env variable PASSWORD"
|
||||
if (ROOM_NAME == null) throw "Fatal: missing env variable ROOM_NAME"
|
||||
if (INVITEE1_MXID == null) throw "Fatal: missing env variable INVITEE1_MXID"
|
||||
if (INVITEE2_MXID == null) throw "Fatal: missing env variable INVITEE2_MXID"
|
||||
if (MAESTRO_APP_ID == null) throw "Fatal: missing env variable MAESTRO_APP_ID"
|
||||
if (MAESTRO_USERNAME == null) throw "Fatal: missing env variable MAESTRO_USERNAME"
|
||||
if (MAESTRO_PASSWORD == null) throw "Fatal: missing env variable MAESTRO_PASSWORD"
|
||||
if (MAESTRO_RECOVERY_KEY == null) throw "Fatal: missing env variable MAESTRO_RECOVERY_KEY"
|
||||
if (MAESTRO_ROOM_NAME == null) throw "Fatal: missing env variable MAESTRO_ROOM_NAME"
|
||||
if (MAESTRO_INVITEE1_MXID == null) throw "Fatal: missing env variable MAESTRO_INVITEE1_MXID"
|
||||
if (MAESTRO_INVITEE2_MXID == null) throw "Fatal: missing env variable MAESTRO_INVITEE2_MXID"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
appId: ${APP_ID}
|
||||
appId: ${MAESTRO_APP_ID}
|
||||
---
|
||||
- tapOn:
|
||||
id: "login-change_server"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
appId: ${APP_ID}
|
||||
appId: ${MAESTRO_APP_ID}
|
||||
---
|
||||
- tapOn: "Continue"
|
||||
- runFlow: ../assertions/assertLoginDisplayed.yaml
|
||||
@@ -9,7 +9,7 @@ appId: ${APP_ID}
|
||||
id: "login-continue"
|
||||
- tapOn:
|
||||
id: "login-email_username"
|
||||
- inputText: ${USERNAME}
|
||||
- inputText: ${MAESTRO_USERNAME}
|
||||
- pressKey: Enter
|
||||
- tapOn:
|
||||
id: "login-password"
|
||||
@@ -20,7 +20,7 @@ appId: ${APP_ID}
|
||||
- tapOn:
|
||||
id: "login-password"
|
||||
- eraseText: 20
|
||||
- inputText: ${PASSWORD}
|
||||
- inputText: ${MAESTRO_PASSWORD}
|
||||
- pressKey: Enter
|
||||
- tapOn: "Continue"
|
||||
- runFlow: ../assertions/assertWelcomeScreenDisplayed.yaml
|
||||
@@ -28,3 +28,4 @@ appId: ${APP_ID}
|
||||
- runFlow: ../assertions/assertAnalyticsDisplayed.yaml
|
||||
- tapOn: "Not now"
|
||||
- runFlow: ../assertions/assertHomeDisplayed.yaml
|
||||
- runFlow: ./verifySession.yaml
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
appId: ${APP_ID}
|
||||
appId: ${MAESTRO_APP_ID}
|
||||
---
|
||||
- tapOn:
|
||||
id: "home_screen-settings"
|
||||
|
||||
11
.maestro/tests/account/verifySession.yaml
Normal file
11
.maestro/tests/account/verifySession.yaml
Normal file
@@ -0,0 +1,11 @@
|
||||
appId: ${MAESTRO_APP_ID}
|
||||
---
|
||||
- tapOn: "Continue"
|
||||
- takeScreenshot: build/maestro/150-Verify
|
||||
- tapOn: "Enter recovery key"
|
||||
- tapOn:
|
||||
id: "verification-recovery_key"
|
||||
- inputText: ${MAESTRO_RECOVERY_KEY}
|
||||
- hideKeyboard
|
||||
- tapOn: "Confirm"
|
||||
- runFlow: ../assertions/assertHomeDisplayed.yaml
|
||||
@@ -1,4 +1,4 @@
|
||||
appId: ${APP_ID}
|
||||
appId: ${MAESTRO_APP_ID}
|
||||
---
|
||||
- extendedWaitUntil:
|
||||
visible: "Help improve Element X dbg"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
appId: ${APP_ID}
|
||||
appId: ${MAESTRO_APP_ID}
|
||||
---
|
||||
- extendedWaitUntil:
|
||||
visible: "All Chats"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
appId: ${APP_ID}
|
||||
appId: ${MAESTRO_APP_ID}
|
||||
---
|
||||
- extendedWaitUntil:
|
||||
visible: "Be in your element"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
appId: ${APP_ID}
|
||||
appId: ${MAESTRO_APP_ID}
|
||||
---
|
||||
- extendedWaitUntil:
|
||||
visible: "Change account provider"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
appId: ${APP_ID}
|
||||
appId: ${MAESTRO_APP_ID}
|
||||
---
|
||||
- extendedWaitUntil:
|
||||
visible: ${ROOM_NAME}
|
||||
visible: ${MAESTRO_ROOM_NAME}
|
||||
timeout: 10000
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
appId: ${APP_ID}
|
||||
appId: ${MAESTRO_APP_ID}
|
||||
---
|
||||
- extendedWaitUntil:
|
||||
visible:
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
appId: ${APP_ID}
|
||||
appId: ${MAESTRO_APP_ID}
|
||||
---
|
||||
- clearState
|
||||
- launchApp:
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
appId: ${APP_ID}
|
||||
appId: ${MAESTRO_APP_ID}
|
||||
---
|
||||
# Purpose: Test the creation and deletion of a DM room.
|
||||
- tapOn: "Create a new conversation or room"
|
||||
- tapOn: "Search for someone"
|
||||
- inputText: ${INVITEE1_MXID}
|
||||
- inputText: ${MAESTRO_INVITEE1_MXID}
|
||||
- tapOn:
|
||||
text: ${INVITEE1_MXID}
|
||||
text: ${MAESTRO_INVITEE1_MXID}
|
||||
index: 1
|
||||
- takeScreenshot: build/maestro/330-createAndDeleteDM
|
||||
- tapOn: "maestroelement2"
|
||||
- scroll
|
||||
- tapOn: "Leave conversation"
|
||||
- tapOn: "Leave"
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
appId: ${APP_ID}
|
||||
appId: ${MAESTRO_APP_ID}
|
||||
---
|
||||
# Purpose: Test the creation and deletion of a room
|
||||
- tapOn: "Create a new conversation or room"
|
||||
- tapOn: "New room"
|
||||
- tapOn: "Search for someone"
|
||||
- inputText: ${INVITEE1_MXID}
|
||||
- inputText: ${MAESTRO_INVITEE1_MXID}
|
||||
- tapOn:
|
||||
text: ${INVITEE1_MXID}
|
||||
text: ${MAESTRO_INVITEE1_MXID}
|
||||
index: 1
|
||||
- tapOn: "Next"
|
||||
- tapOn: "e.g. your project name"
|
||||
@@ -19,9 +19,9 @@ appId: ${APP_ID}
|
||||
- tapOn: "Invite people"
|
||||
# assert there's 1 member and 1 invitee
|
||||
- tapOn: "Search for someone"
|
||||
- inputText: ${INVITEE2_MXID}
|
||||
- inputText: ${MAESTRO_INVITEE2_MXID}
|
||||
- tapOn:
|
||||
text: ${INVITEE2_MXID}
|
||||
text: ${MAESTRO_INVITEE2_MXID}
|
||||
index: 1
|
||||
- tapOn: "Invite"
|
||||
- tapOn: "Back"
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
appId: ${APP_ID}
|
||||
appId: ${MAESTRO_APP_ID}
|
||||
---
|
||||
# Purpose: Test the context menu of a room in the room list
|
||||
- longPressOn: ${ROOM_NAME}
|
||||
- longPressOn: ${MAESTRO_ROOM_NAME}
|
||||
- takeScreenshot: build/maestro/310-RoomList-ContextMenu
|
||||
- tapOn:
|
||||
text: "Settings"
|
||||
index: 0
|
||||
- tapOn: "Back"
|
||||
- longPressOn: ${ROOM_NAME}
|
||||
- longPressOn: ${MAESTRO_ROOM_NAME}
|
||||
- tapOn:
|
||||
text: "Leave room"
|
||||
index: 0
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
appId: ${APP_ID}
|
||||
appId: ${MAESTRO_APP_ID}
|
||||
---
|
||||
- runFlow: searchRoomList.yaml
|
||||
- takeScreenshot: build/maestro/300-RoomList
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
appId: ${APP_ID}
|
||||
appId: ${MAESTRO_APP_ID}
|
||||
---
|
||||
- runFlow: ../assertions/assertRoomListSynced.yaml
|
||||
- tapOn: "search"
|
||||
- inputText: ${ROOM_NAME.substring(0, 3)}
|
||||
- inputText: ${MAESTRO_ROOM_NAME.substring(0, 3)}
|
||||
- takeScreenshot: build/maestro/400-SearchRoom
|
||||
- tapOn: ${ROOM_NAME}
|
||||
- tapOn: ${MAESTRO_ROOM_NAME}
|
||||
# Back from timeline
|
||||
- back
|
||||
- assertVisible: "MyR"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
appId: ${APP_ID}
|
||||
appId: ${MAESTRO_APP_ID}
|
||||
---
|
||||
- takeScreenshot: build/maestro/520-Timeline
|
||||
- tapOn: "Add attachment"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
appId: ${APP_ID}
|
||||
appId: ${MAESTRO_APP_ID}
|
||||
---
|
||||
- takeScreenshot: build/maestro/530-Timeline
|
||||
- tapOn: "Add attachment"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
appId: ${APP_ID}
|
||||
appId: ${MAESTRO_APP_ID}
|
||||
---
|
||||
- takeScreenshot: build/maestro/510-Timeline
|
||||
- tapOn:
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
appId: ${APP_ID}
|
||||
appId: ${MAESTRO_APP_ID}
|
||||
---
|
||||
# This is the name of one room
|
||||
- tapOn: ${ROOM_NAME}
|
||||
- tapOn: ${MAESTRO_ROOM_NAME}
|
||||
- takeScreenshot: build/maestro/500-Timeline
|
||||
- runFlow: messages/text.yaml
|
||||
- runFlow: messages/location.yaml
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
appId: ${APP_ID}
|
||||
appId: ${MAESTRO_APP_ID}
|
||||
---
|
||||
- tapOn:
|
||||
id: "home_screen-settings"
|
||||
|
||||
@@ -338,6 +338,10 @@ class LoggedInFlowNode @AssistedInject constructor(
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
override fun onDone() {
|
||||
backstack.pop()
|
||||
}
|
||||
}
|
||||
verifySessionEntryPoint
|
||||
.nodeBuilder(this, buildContext)
|
||||
|
||||
@@ -42,6 +42,7 @@ dependencies {
|
||||
implementation(projects.libraries.matrixui)
|
||||
implementation(projects.libraries.designsystem)
|
||||
implementation(projects.libraries.uiStrings)
|
||||
implementation(projects.libraries.testtags)
|
||||
api(libs.statemachine)
|
||||
api(projects.features.securebackup.api)
|
||||
|
||||
|
||||
@@ -53,6 +53,8 @@ import io.element.android.libraries.designsystem.theme.components.Icon
|
||||
import io.element.android.libraries.designsystem.theme.components.OutlinedTextField
|
||||
import io.element.android.libraries.designsystem.theme.components.Text
|
||||
import io.element.android.libraries.designsystem.theme.components.autofill
|
||||
import io.element.android.libraries.testtags.TestTags
|
||||
import io.element.android.libraries.testtags.testTag
|
||||
import io.element.android.libraries.ui.strings.CommonStrings
|
||||
|
||||
@Composable
|
||||
@@ -169,6 +171,7 @@ private fun RecoveryKeyFormContent(
|
||||
OutlinedTextField(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.testTag(TestTags.recoveryKey)
|
||||
.autofill(
|
||||
autofillTypes = listOf(AutofillType.Password),
|
||||
onFill = { onChange(it) },
|
||||
|
||||
@@ -31,5 +31,6 @@ interface VerifySessionEntryPoint : FeatureEntryPoint {
|
||||
|
||||
interface Callback : Plugin {
|
||||
fun onEnterRecoveryKey()
|
||||
fun onDone()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,14 +40,20 @@ class VerifySelfSessionNode @AssistedInject constructor(
|
||||
}
|
||||
}
|
||||
|
||||
private fun onDone() {
|
||||
plugins<VerifySessionEntryPoint.Callback>().forEach {
|
||||
it.onDone()
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
override fun View(modifier: Modifier) {
|
||||
val state = presenter.present()
|
||||
VerifySelfSessionView(
|
||||
state = state,
|
||||
modifier = modifier,
|
||||
onEnterRecoveryKey = { onEnterRecoveryKey() },
|
||||
goBack = { navigateUp() }
|
||||
onEnterRecoveryKey = ::onEnterRecoveryKey,
|
||||
goBack = ::onDone,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -138,7 +138,7 @@ class RustMatrixClient(
|
||||
syncService = rustSyncService,
|
||||
sessionCoroutineScope = sessionCoroutineScope,
|
||||
dispatchers = dispatchers,
|
||||
).apply { start() }
|
||||
)
|
||||
private val sessionDirectoryNameProvider = SessionDirectoryNameProvider()
|
||||
|
||||
private val isLoggingOut = AtomicBoolean(false)
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright (c) 2024 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 io.element.android.libraries.matrix.impl.encryption
|
||||
|
||||
import io.element.android.libraries.matrix.api.encryption.BackupState
|
||||
import io.element.android.libraries.matrix.api.encryption.RecoveryState
|
||||
import io.element.android.libraries.matrix.impl.util.mxCallbackFlow
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import org.matrix.rustcomponents.sdk.BackupStateListener
|
||||
import org.matrix.rustcomponents.sdk.EncryptionInterface
|
||||
import org.matrix.rustcomponents.sdk.RecoveryStateListener
|
||||
import org.matrix.rustcomponents.sdk.BackupState as RustBackupState
|
||||
import org.matrix.rustcomponents.sdk.RecoveryState as RustRecoveryState
|
||||
|
||||
internal fun EncryptionInterface.backupStateFlow(): Flow<BackupState> = mxCallbackFlow {
|
||||
val backupStateMapper = BackupStateMapper()
|
||||
trySend(backupStateMapper.map(backupState()))
|
||||
val listener = object : BackupStateListener {
|
||||
override fun onUpdate(status: RustBackupState) {
|
||||
trySend(backupStateMapper.map(status))
|
||||
}
|
||||
}
|
||||
backupStateListener(listener)
|
||||
}
|
||||
|
||||
internal fun EncryptionInterface.recoveryStateFlow(): Flow<RecoveryState> = mxCallbackFlow {
|
||||
val recoveryStateMapper = RecoveryStateMapper()
|
||||
trySend(recoveryStateMapper.map(recoveryState()))
|
||||
val listener = object : RecoveryStateListener {
|
||||
override fun onUpdate(status: RustRecoveryState) {
|
||||
trySend(recoveryStateMapper.map(status))
|
||||
}
|
||||
}
|
||||
recoveryStateListener(listener)
|
||||
}
|
||||
@@ -25,7 +25,6 @@ import io.element.android.libraries.matrix.api.encryption.EncryptionService
|
||||
import io.element.android.libraries.matrix.api.encryption.RecoveryState
|
||||
import io.element.android.libraries.matrix.api.sync.SyncState
|
||||
import io.element.android.libraries.matrix.impl.sync.RustSyncService
|
||||
import io.element.android.libraries.matrix.impl.util.cancelAndDestroy
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.channels.awaitClose
|
||||
import kotlinx.coroutines.currentCoroutineContext
|
||||
@@ -40,17 +39,12 @@ import kotlinx.coroutines.flow.flow
|
||||
import kotlinx.coroutines.flow.stateIn
|
||||
import kotlinx.coroutines.isActive
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.matrix.rustcomponents.sdk.BackupStateListener
|
||||
import org.matrix.rustcomponents.sdk.BackupSteadyStateListener
|
||||
import org.matrix.rustcomponents.sdk.Client
|
||||
import org.matrix.rustcomponents.sdk.EnableRecoveryProgressListener
|
||||
import org.matrix.rustcomponents.sdk.Encryption
|
||||
import org.matrix.rustcomponents.sdk.RecoveryStateListener
|
||||
import org.matrix.rustcomponents.sdk.TaskHandle
|
||||
import org.matrix.rustcomponents.sdk.BackupState as RustBackupState
|
||||
import org.matrix.rustcomponents.sdk.BackupUploadState as RustBackupUploadState
|
||||
import org.matrix.rustcomponents.sdk.EnableRecoveryProgress as RustEnableRecoveryProgress
|
||||
import org.matrix.rustcomponents.sdk.RecoveryState as RustRecoveryState
|
||||
import org.matrix.rustcomponents.sdk.SteadyStateException as RustSteadyStateException
|
||||
|
||||
internal class RustEncryptionService(
|
||||
@@ -61,18 +55,12 @@ internal class RustEncryptionService(
|
||||
) : EncryptionService {
|
||||
private val service: Encryption = client.encryption()
|
||||
|
||||
private val backupStateMapper = BackupStateMapper()
|
||||
private val recoveryStateMapper = RecoveryStateMapper()
|
||||
private val enableRecoveryProgressMapper = EnableRecoveryProgressMapper()
|
||||
private val backupUploadStateMapper = BackupUploadStateMapper()
|
||||
private val steadyStateExceptionMapper = SteadyStateExceptionMapper()
|
||||
private var backupStateListenerTaskHandle: TaskHandle? = null
|
||||
private var recoveryStateListenerTaskHandle: TaskHandle? = null
|
||||
|
||||
private val backupStateFlow = MutableStateFlow(service.backupState().let(backupStateMapper::map))
|
||||
|
||||
override val backupStateStateFlow = combine(
|
||||
backupStateFlow,
|
||||
service.backupStateFlow(),
|
||||
syncService.syncState,
|
||||
) { backupState, syncState ->
|
||||
if (syncState == SyncState.Running) {
|
||||
@@ -82,10 +70,8 @@ internal class RustEncryptionService(
|
||||
}
|
||||
}.stateIn(sessionCoroutineScope, SharingStarted.Eagerly, BackupState.WAITING_FOR_SYNC)
|
||||
|
||||
private val recoveryStateFlow: MutableStateFlow<RecoveryState> = MutableStateFlow(service.recoveryState().let(recoveryStateMapper::map))
|
||||
|
||||
override val recoveryStateStateFlow = combine(
|
||||
recoveryStateFlow,
|
||||
service.recoveryStateFlow(),
|
||||
syncService.syncState,
|
||||
) { recoveryState, syncState ->
|
||||
if (syncState == SyncState.Running) {
|
||||
@@ -111,23 +97,7 @@ internal class RustEncryptionService(
|
||||
}
|
||||
.stateIn(sessionCoroutineScope, SharingStarted.Eagerly, false)
|
||||
|
||||
fun start() {
|
||||
backupStateListenerTaskHandle = service.backupStateListener(object : BackupStateListener {
|
||||
override fun onUpdate(status: RustBackupState) {
|
||||
backupStateFlow.value = backupStateMapper.map(status)
|
||||
}
|
||||
})
|
||||
|
||||
recoveryStateListenerTaskHandle = service.recoveryStateListener(object : RecoveryStateListener {
|
||||
override fun onUpdate(status: RustRecoveryState) {
|
||||
recoveryStateFlow.value = recoveryStateMapper.map(status)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fun destroy() {
|
||||
backupStateListenerTaskHandle?.cancelAndDestroy()
|
||||
recoveryStateListenerTaskHandle?.cancelAndDestroy()
|
||||
service.destroy()
|
||||
}
|
||||
|
||||
|
||||
@@ -33,6 +33,11 @@ object TestTags {
|
||||
val loginPassword = TestTag("login-password")
|
||||
val loginContinue = TestTag("login-continue")
|
||||
|
||||
/**
|
||||
* Verification screen.
|
||||
*/
|
||||
val recoveryKey = TestTag("verification-recovery_key")
|
||||
|
||||
/**
|
||||
* Sign out screen.
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user