From 0dde7297b86bb64cf348334ac7e48efe815fafcf Mon Sep 17 00:00:00 2001 From: Andrew Date: Tue, 12 Mar 2024 20:12:18 +1030 Subject: [PATCH] Fix multiple loading activities launched --- .../onboarding/LinkDeviceActivity.kt | 5 +- .../onboarding/LinkDeviceViewModel.kt | 55 +++++++++++-------- 2 files changed, 37 insertions(+), 23 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/onboarding/LinkDeviceActivity.kt b/app/src/main/java/org/thoughtcrime/securesms/onboarding/LinkDeviceActivity.kt index 0677f2bcb2..3cff0a26be 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/onboarding/LinkDeviceActivity.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/onboarding/LinkDeviceActivity.kt @@ -66,6 +66,7 @@ import com.google.mlkit.vision.barcode.BarcodeScanning import com.google.mlkit.vision.barcode.common.Barcode import com.google.mlkit.vision.common.InputImage import dagger.hilt.android.AndroidEntryPoint +import kotlinx.coroutines.flow.first import kotlinx.coroutines.launch import network.loki.messenger.R import org.session.libsession.utilities.TextSecurePreferences @@ -78,6 +79,7 @@ import org.thoughtcrime.securesms.ui.colorDestructive import org.thoughtcrime.securesms.ui.components.SessionTabRow import java.util.concurrent.Executors import javax.inject.Inject +import kotlin.coroutines.CoroutineContext private const val TAG = "LinkDeviceActivity" @@ -108,6 +110,7 @@ class LinkDeviceActivity : BaseActionBarActivity() { lifecycleScope.launch { viewModel.eventFlow.collect { startLoadingActivity(it.mnemonic) + finish() } } @@ -115,7 +118,7 @@ class LinkDeviceActivity : BaseActionBarActivity() { setContent { val state by viewModel.stateFlow.collectAsState() AppTheme { - LoadAccountScreen(state, viewModel::onChange, viewModel::tryPhrase) + LoadAccountScreen(state, viewModel::onChange, viewModel::onContinue) } } }.let(::setContentView) diff --git a/app/src/main/java/org/thoughtcrime/securesms/onboarding/LinkDeviceViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/onboarding/LinkDeviceViewModel.kt index 5cd22637c7..c35111b230 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/onboarding/LinkDeviceViewModel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/onboarding/LinkDeviceViewModel.kt @@ -1,28 +1,26 @@ package org.thoughtcrime.securesms.onboarding import android.app.Application -import androidx.compose.runtime.snapshots.SnapshotApplyResult import androidx.lifecycle.AndroidViewModel import androidx.lifecycle.viewModelScope import dagger.hilt.android.lifecycle.HiltViewModel -import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.collect import kotlinx.coroutines.flow.consumeAsFlow -import kotlinx.coroutines.flow.distinctUntilChanged -import kotlinx.coroutines.flow.first +import kotlinx.coroutines.flow.debounce import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.receiveAsFlow +import kotlinx.coroutines.flow.take import kotlinx.coroutines.flow.takeWhile -import kotlinx.coroutines.flow.transformWhile import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch import org.session.libsignal.crypto.MnemonicCodec import org.session.libsignal.utilities.Hex import org.thoughtcrime.securesms.crypto.MnemonicUtilities import javax.inject.Inject +import kotlin.time.Duration.Companion.seconds class LinkDeviceEvent(val mnemonic: ByteArray) @@ -30,38 +28,51 @@ class LinkDeviceEvent(val mnemonic: ByteArray) class LinkDeviceViewModel @Inject constructor( application: Application ): AndroidViewModel(application) { + private val QR_ERROR_TIME = 3.seconds private val state = MutableStateFlow(LinkDeviceState()) val stateFlow = state.asStateFlow() - private val qrErrors = Channel() private val event = Channel() - val eventFlow = event.receiveAsFlow() + val eventFlow = event.receiveAsFlow().take(1) + private val qrErrors = Channel() + private val qrErrorsFlow = qrErrors.receiveAsFlow().debounce(QR_ERROR_TIME).takeWhile { event.isEmpty } - private val phrases = Channel() + private val codec by lazy { MnemonicCodec { MnemonicUtilities.loadFileContents(getApplication(), it) } } - init { + fun onContinue() { viewModelScope.launch { - phrases.receiveAsFlow().map { - runCatching { - MnemonicCodec { MnemonicUtilities.loadFileContents(getApplication(), it) } - .decode(it) - .let(Hex::fromStringCondensed) - } - }.takeWhile { - it.getOrNull()?.let(::LinkDeviceEvent)?.let { event.send(it) } - it.exceptionOrNull()?.let { qrErrors.send(it) } - it.isFailure - }.collect() + runDecodeCatching(state.value.recoveryPhrase) + .onSuccess(::onSuccess) + .onFailure(::onFailure) } } - fun tryPhrase(string: String = state.value.recoveryPhrase) { + fun scan(string: String) { viewModelScope.launch { - phrases.send(string) + runDecodeCatching(string) + .onSuccess(::onSuccess) + .onFailure(::onScanFailure) } } + fun onChange(recoveryPhrase: String) { state.value = LinkDeviceState(recoveryPhrase) } + private fun onSuccess(seed: ByteArray) { + viewModelScope.launch { event.send(LinkDeviceEvent(seed)) } + } + + private fun onFailure(error: Throwable) { + state.update { it.copy(error = error.message) } + } + + private fun onScanFailure(error: Throwable) { + state.update { it.copy(error = error.message) } + } + + private fun runDecodeCatching(mnemonic: String) = runCatching { + decode(mnemonic) + } + private fun decode(mnemonic: String) = codec.decode(mnemonic).let(Hex::fromStringCondensed)!! }