Fix QR code scan & display

This commit is contained in:
Andrew 2024-06-26 19:25:44 +09:30
parent ff6c0fb6f5
commit 39c7f27c7d
5 changed files with 64 additions and 39 deletions

View File

@ -24,11 +24,11 @@ import org.thoughtcrime.securesms.crypto.KeyPairUtilities
import org.thoughtcrime.securesms.dependencies.ConfigFactory
internal class PickDisplayNameViewModel(
pickNewName: Boolean,
private val loadFailed: Boolean,
private val prefs: TextSecurePreferences,
private val configFactory: ConfigFactory
): ViewModel() {
private val _states = MutableStateFlow(if (pickNewName) pickNewNameState() else State())
private val _states = MutableStateFlow(if (loadFailed) pickNewNameState() else State())
val states = _states.asStateFlow()
private val _events = MutableSharedFlow<Event>()
@ -48,23 +48,25 @@ internal class PickDisplayNameViewModel(
else -> {
prefs.setProfileName(displayName)
// This is here to resolve a case where the app restarts before a user completes onboarding
// which can result in an invalid database state
database.clearAllLastMessageHashes()
database.clearReceivedMessageHashValues()
if (!loadFailed) {
// This is here to resolve a case where the app restarts before a user completes onboarding
// which can result in an invalid database state
database.clearAllLastMessageHashes()
database.clearReceivedMessageHashValues()
val keyPairGenerationResult = KeyPairUtilities.generate()
val seed = keyPairGenerationResult.seed
val ed25519KeyPair = keyPairGenerationResult.ed25519KeyPair
val x25519KeyPair = keyPairGenerationResult.x25519KeyPair
val keyPairGenerationResult = KeyPairUtilities.generate()
val seed = keyPairGenerationResult.seed
val ed25519KeyPair = keyPairGenerationResult.ed25519KeyPair
val x25519KeyPair = keyPairGenerationResult.x25519KeyPair
KeyPairUtilities.store(context, seed, ed25519KeyPair, x25519KeyPair)
configFactory.keyPairChanged()
val userHexEncodedPublicKey = x25519KeyPair.hexEncodedPublicKey
val registrationID = KeyHelper.generateRegistrationId(false)
prefs.setLocalRegistrationId(registrationID)
prefs.setLocalNumber(userHexEncodedPublicKey)
prefs.setRestorationTime(0)
KeyPairUtilities.store(context, seed, ed25519KeyPair, x25519KeyPair)
configFactory.keyPairChanged()
val userHexEncodedPublicKey = x25519KeyPair.hexEncodedPublicKey
val registrationID = KeyHelper.generateRegistrationId(false)
prefs.setLocalRegistrationId(registrationID)
prefs.setLocalNumber(userHexEncodedPublicKey)
prefs.setRestorationTime(0)
}
viewModelScope.launch { _events.emit(Event.DONE) }
}
@ -82,18 +84,18 @@ internal class PickDisplayNameViewModel(
@dagger.assisted.AssistedFactory
interface AssistedFactory {
fun create(pickNewName: Boolean): Factory
fun create(loadFailed: Boolean): Factory
}
@Suppress("UNCHECKED_CAST")
class Factory @AssistedInject constructor(
@Assisted private val pickNewName: Boolean,
@Assisted private val loadFailed: Boolean,
private val prefs: TextSecurePreferences,
private val configFactory: ConfigFactory
) : ViewModelProvider.Factory {
override fun <T : ViewModel> create(modelClass: Class<T>): T {
return PickDisplayNameViewModel(pickNewName, prefs, configFactory) as T
return PickDisplayNameViewModel(loadFailed, prefs, configFactory) as T
}
}
}

View File

@ -32,8 +32,6 @@ import org.thoughtcrime.securesms.ui.SessionShieldIcon
import org.thoughtcrime.securesms.ui.base
import org.thoughtcrime.securesms.ui.color.Colors
import org.thoughtcrime.securesms.ui.color.LocalColors
import org.thoughtcrime.securesms.ui.components.ButtonStyle
import org.thoughtcrime.securesms.ui.components.OutlineCopyButton
import org.thoughtcrime.securesms.ui.components.QrImage
import org.thoughtcrime.securesms.ui.components.SlimOutlineButton
import org.thoughtcrime.securesms.ui.components.SlimOutlineCopyButton
@ -44,7 +42,8 @@ import org.thoughtcrime.securesms.ui.h8
@Composable
internal fun RecoveryPasswordScreen(
seed: String = "",
mnemonic: String,
seed: String? = null,
copySeed:() -> Unit = {},
onHide:() -> Unit = {}
) {
@ -55,13 +54,17 @@ internal fun RecoveryPasswordScreen(
.verticalScroll(rememberScrollState())
.padding(bottom = LocalDimensions.current.xsMargin)
) {
RecoveryPasswordCell(seed, copySeed)
RecoveryPasswordCell(mnemonic, seed, copySeed)
HideRecoveryPasswordCell(onHide)
}
}
@Composable
private fun RecoveryPasswordCell(seed: String, copySeed:() -> Unit = {}) {
private fun RecoveryPasswordCell(
mnemonic: String,
seed: String?,
copySeed:() -> Unit = {}
) {
var showQr by remember {
mutableStateOf(false)
}
@ -85,7 +88,7 @@ private fun RecoveryPasswordCell(seed: String, copySeed:() -> Unit = {}) {
)
AnimatedVisibility(!showQr) {
RecoveryPassword(seed)
RecoveryPassword(mnemonic)
}
AnimatedVisibility(
@ -128,9 +131,9 @@ private fun RecoveryPasswordCell(seed: String, copySeed:() -> Unit = {}) {
}
@Composable
private fun RecoveryPassword(seed: String) {
private fun RecoveryPassword(mnemonic: String) {
Text(
seed,
mnemonic,
modifier = Modifier
.contentDescription(R.string.AccessibilityId_recovery_password_container)
.padding(vertical = LocalDimensions.current.smallMargin)
@ -178,6 +181,6 @@ private fun PreviewRecoveryPasswordScreen(
@PreviewParameter(SessionColorsParameterProvider::class) colors: Colors
) {
PreviewTheme(colors) {
RecoveryPasswordScreen(seed = "Voyage urban toyed maverick peculiar tuxedo penguin tree grass building listen speak withdraw terminal plane")
RecoveryPasswordScreen(mnemonic = "voyage urban toyed maverick peculiar tuxedo penguin tree grass building listen speak withdraw terminal plane")
}
}

View File

@ -2,6 +2,8 @@ package org.thoughtcrime.securesms.recoverypassword
import android.os.Bundle
import androidx.activity.viewModels
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import network.loki.messenger.R
import org.thoughtcrime.securesms.BaseActionBarActivity
import org.thoughtcrime.securesms.showSessionDialog
@ -16,10 +18,15 @@ class RecoveryPasswordActivity : BaseActionBarActivity() {
supportActionBar!!.title = resources.getString(R.string.sessionRecoveryPassword)
setComposeContent {
val mnemonic by viewModel.mnemonic.collectAsState("")
val seed by viewModel.seed.collectAsState(null)
RecoveryPasswordScreen(
viewModel.seed,
{ viewModel.copySeed(this) }
) { onHide() }
mnemonic = mnemonic,
seed = seed,
copySeed = { viewModel.copySeed(this) },
onHide = ::onHide
)
}
}

View File

@ -5,7 +5,15 @@ import android.content.ClipData
import android.content.ClipboardManager
import android.content.Context
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.viewModelScope
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
import org.session.libsession.utilities.AppTextSecurePreferences
import org.session.libsession.utilities.TextSecurePreferences
import org.session.libsignal.crypto.MnemonicCodec
@ -20,21 +28,26 @@ class RecoveryPasswordViewModel @Inject constructor(
): AndroidViewModel(application) {
val prefs = AppTextSecurePreferences(application)
val seed = MutableStateFlow<String?>(null)
val mnemonic = seed.filterNotNull()
.map { MnemonicCodec { MnemonicUtilities.loadFileContents(application, it) }.encode(it, MnemonicCodec.Language.Configuration.english) }
fun permanentlyHidePassword() {
prefs.setHidePassword(true)
}
fun copySeed(context: Context) {
val seed = seed.value ?: return
TextSecurePreferences.setHasViewedSeed(context, true)
val clipboard = context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
val clip = ClipData.newPlainText("Seed", seed)
clipboard.setPrimaryClip(clip)
}
val seed by lazy {
val hexEncodedSeed = IdentityKeyUtil.retrieve(application, IdentityKeyUtil.LOKI_SEED)
?: IdentityKeyUtil.getIdentityKeyPair(application).hexEncodedPrivateKey // Legacy account
MnemonicCodec { MnemonicUtilities.loadFileContents(application, it) }
.encode(hexEncodedSeed, MnemonicCodec.Language.Configuration.english)
init {
viewModelScope.launch(Dispatchers.IO) {
seed.emit(IdentityKeyUtil.retrieve(application, IdentityKeyUtil.LOKI_SEED)
?: IdentityKeyUtil.getIdentityKeyPair(application).hexEncodedPrivateKey) // Legacy account
}
}
}

View File

@ -37,7 +37,7 @@ import org.thoughtcrime.securesms.util.QRCodeUtilities
@Composable
fun QrImage(
string: String,
string: String?,
modifier: Modifier = Modifier,
icon: Int = R.drawable.session_shield
) {
@ -47,7 +47,7 @@ fun QrImage(
val scope = rememberCoroutineScope()
LaunchedEffect(string) {
scope.launch(Dispatchers.IO) {
if (string != null) scope.launch(Dispatchers.IO) {
bitmap = (300..500 step 100).firstNotNullOf {
runCatching { QRCodeUtilities.encode(string, it) }.getOrNull()
}