mirror of
https://github.com/oxen-io/session-android.git
synced 2025-02-22 03:48:26 +00:00
Fix QR code scan & display
This commit is contained in:
parent
ff6c0fb6f5
commit
39c7f27c7d
@ -24,11 +24,11 @@ import org.thoughtcrime.securesms.crypto.KeyPairUtilities
|
|||||||
import org.thoughtcrime.securesms.dependencies.ConfigFactory
|
import org.thoughtcrime.securesms.dependencies.ConfigFactory
|
||||||
|
|
||||||
internal class PickDisplayNameViewModel(
|
internal class PickDisplayNameViewModel(
|
||||||
pickNewName: Boolean,
|
private val loadFailed: Boolean,
|
||||||
private val prefs: TextSecurePreferences,
|
private val prefs: TextSecurePreferences,
|
||||||
private val configFactory: ConfigFactory
|
private val configFactory: ConfigFactory
|
||||||
): ViewModel() {
|
): ViewModel() {
|
||||||
private val _states = MutableStateFlow(if (pickNewName) pickNewNameState() else State())
|
private val _states = MutableStateFlow(if (loadFailed) pickNewNameState() else State())
|
||||||
val states = _states.asStateFlow()
|
val states = _states.asStateFlow()
|
||||||
|
|
||||||
private val _events = MutableSharedFlow<Event>()
|
private val _events = MutableSharedFlow<Event>()
|
||||||
@ -48,23 +48,25 @@ internal class PickDisplayNameViewModel(
|
|||||||
else -> {
|
else -> {
|
||||||
prefs.setProfileName(displayName)
|
prefs.setProfileName(displayName)
|
||||||
|
|
||||||
// This is here to resolve a case where the app restarts before a user completes onboarding
|
if (!loadFailed) {
|
||||||
// which can result in an invalid database state
|
// This is here to resolve a case where the app restarts before a user completes onboarding
|
||||||
database.clearAllLastMessageHashes()
|
// which can result in an invalid database state
|
||||||
database.clearReceivedMessageHashValues()
|
database.clearAllLastMessageHashes()
|
||||||
|
database.clearReceivedMessageHashValues()
|
||||||
|
|
||||||
val keyPairGenerationResult = KeyPairUtilities.generate()
|
val keyPairGenerationResult = KeyPairUtilities.generate()
|
||||||
val seed = keyPairGenerationResult.seed
|
val seed = keyPairGenerationResult.seed
|
||||||
val ed25519KeyPair = keyPairGenerationResult.ed25519KeyPair
|
val ed25519KeyPair = keyPairGenerationResult.ed25519KeyPair
|
||||||
val x25519KeyPair = keyPairGenerationResult.x25519KeyPair
|
val x25519KeyPair = keyPairGenerationResult.x25519KeyPair
|
||||||
|
|
||||||
KeyPairUtilities.store(context, seed, ed25519KeyPair, x25519KeyPair)
|
KeyPairUtilities.store(context, seed, ed25519KeyPair, x25519KeyPair)
|
||||||
configFactory.keyPairChanged()
|
configFactory.keyPairChanged()
|
||||||
val userHexEncodedPublicKey = x25519KeyPair.hexEncodedPublicKey
|
val userHexEncodedPublicKey = x25519KeyPair.hexEncodedPublicKey
|
||||||
val registrationID = KeyHelper.generateRegistrationId(false)
|
val registrationID = KeyHelper.generateRegistrationId(false)
|
||||||
prefs.setLocalRegistrationId(registrationID)
|
prefs.setLocalRegistrationId(registrationID)
|
||||||
prefs.setLocalNumber(userHexEncodedPublicKey)
|
prefs.setLocalNumber(userHexEncodedPublicKey)
|
||||||
prefs.setRestorationTime(0)
|
prefs.setRestorationTime(0)
|
||||||
|
}
|
||||||
|
|
||||||
viewModelScope.launch { _events.emit(Event.DONE) }
|
viewModelScope.launch { _events.emit(Event.DONE) }
|
||||||
}
|
}
|
||||||
@ -82,18 +84,18 @@ internal class PickDisplayNameViewModel(
|
|||||||
|
|
||||||
@dagger.assisted.AssistedFactory
|
@dagger.assisted.AssistedFactory
|
||||||
interface AssistedFactory {
|
interface AssistedFactory {
|
||||||
fun create(pickNewName: Boolean): Factory
|
fun create(loadFailed: Boolean): Factory
|
||||||
}
|
}
|
||||||
|
|
||||||
@Suppress("UNCHECKED_CAST")
|
@Suppress("UNCHECKED_CAST")
|
||||||
class Factory @AssistedInject constructor(
|
class Factory @AssistedInject constructor(
|
||||||
@Assisted private val pickNewName: Boolean,
|
@Assisted private val loadFailed: Boolean,
|
||||||
private val prefs: TextSecurePreferences,
|
private val prefs: TextSecurePreferences,
|
||||||
private val configFactory: ConfigFactory
|
private val configFactory: ConfigFactory
|
||||||
) : ViewModelProvider.Factory {
|
) : ViewModelProvider.Factory {
|
||||||
|
|
||||||
override fun <T : ViewModel> create(modelClass: Class<T>): T {
|
override fun <T : ViewModel> create(modelClass: Class<T>): T {
|
||||||
return PickDisplayNameViewModel(pickNewName, prefs, configFactory) as T
|
return PickDisplayNameViewModel(loadFailed, prefs, configFactory) as T
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -32,8 +32,6 @@ import org.thoughtcrime.securesms.ui.SessionShieldIcon
|
|||||||
import org.thoughtcrime.securesms.ui.base
|
import org.thoughtcrime.securesms.ui.base
|
||||||
import org.thoughtcrime.securesms.ui.color.Colors
|
import org.thoughtcrime.securesms.ui.color.Colors
|
||||||
import org.thoughtcrime.securesms.ui.color.LocalColors
|
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.QrImage
|
||||||
import org.thoughtcrime.securesms.ui.components.SlimOutlineButton
|
import org.thoughtcrime.securesms.ui.components.SlimOutlineButton
|
||||||
import org.thoughtcrime.securesms.ui.components.SlimOutlineCopyButton
|
import org.thoughtcrime.securesms.ui.components.SlimOutlineCopyButton
|
||||||
@ -44,7 +42,8 @@ import org.thoughtcrime.securesms.ui.h8
|
|||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
internal fun RecoveryPasswordScreen(
|
internal fun RecoveryPasswordScreen(
|
||||||
seed: String = "",
|
mnemonic: String,
|
||||||
|
seed: String? = null,
|
||||||
copySeed:() -> Unit = {},
|
copySeed:() -> Unit = {},
|
||||||
onHide:() -> Unit = {}
|
onHide:() -> Unit = {}
|
||||||
) {
|
) {
|
||||||
@ -55,13 +54,17 @@ internal fun RecoveryPasswordScreen(
|
|||||||
.verticalScroll(rememberScrollState())
|
.verticalScroll(rememberScrollState())
|
||||||
.padding(bottom = LocalDimensions.current.xsMargin)
|
.padding(bottom = LocalDimensions.current.xsMargin)
|
||||||
) {
|
) {
|
||||||
RecoveryPasswordCell(seed, copySeed)
|
RecoveryPasswordCell(mnemonic, seed, copySeed)
|
||||||
HideRecoveryPasswordCell(onHide)
|
HideRecoveryPasswordCell(onHide)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun RecoveryPasswordCell(seed: String, copySeed:() -> Unit = {}) {
|
private fun RecoveryPasswordCell(
|
||||||
|
mnemonic: String,
|
||||||
|
seed: String?,
|
||||||
|
copySeed:() -> Unit = {}
|
||||||
|
) {
|
||||||
var showQr by remember {
|
var showQr by remember {
|
||||||
mutableStateOf(false)
|
mutableStateOf(false)
|
||||||
}
|
}
|
||||||
@ -85,7 +88,7 @@ private fun RecoveryPasswordCell(seed: String, copySeed:() -> Unit = {}) {
|
|||||||
)
|
)
|
||||||
|
|
||||||
AnimatedVisibility(!showQr) {
|
AnimatedVisibility(!showQr) {
|
||||||
RecoveryPassword(seed)
|
RecoveryPassword(mnemonic)
|
||||||
}
|
}
|
||||||
|
|
||||||
AnimatedVisibility(
|
AnimatedVisibility(
|
||||||
@ -128,9 +131,9 @@ private fun RecoveryPasswordCell(seed: String, copySeed:() -> Unit = {}) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun RecoveryPassword(seed: String) {
|
private fun RecoveryPassword(mnemonic: String) {
|
||||||
Text(
|
Text(
|
||||||
seed,
|
mnemonic,
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.contentDescription(R.string.AccessibilityId_recovery_password_container)
|
.contentDescription(R.string.AccessibilityId_recovery_password_container)
|
||||||
.padding(vertical = LocalDimensions.current.smallMargin)
|
.padding(vertical = LocalDimensions.current.smallMargin)
|
||||||
@ -178,6 +181,6 @@ private fun PreviewRecoveryPasswordScreen(
|
|||||||
@PreviewParameter(SessionColorsParameterProvider::class) colors: Colors
|
@PreviewParameter(SessionColorsParameterProvider::class) colors: Colors
|
||||||
) {
|
) {
|
||||||
PreviewTheme(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")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,8 @@ package org.thoughtcrime.securesms.recoverypassword
|
|||||||
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import androidx.activity.viewModels
|
import androidx.activity.viewModels
|
||||||
|
import androidx.compose.runtime.collectAsState
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
import network.loki.messenger.R
|
import network.loki.messenger.R
|
||||||
import org.thoughtcrime.securesms.BaseActionBarActivity
|
import org.thoughtcrime.securesms.BaseActionBarActivity
|
||||||
import org.thoughtcrime.securesms.showSessionDialog
|
import org.thoughtcrime.securesms.showSessionDialog
|
||||||
@ -16,10 +18,15 @@ class RecoveryPasswordActivity : BaseActionBarActivity() {
|
|||||||
supportActionBar!!.title = resources.getString(R.string.sessionRecoveryPassword)
|
supportActionBar!!.title = resources.getString(R.string.sessionRecoveryPassword)
|
||||||
|
|
||||||
setComposeContent {
|
setComposeContent {
|
||||||
|
val mnemonic by viewModel.mnemonic.collectAsState("")
|
||||||
|
val seed by viewModel.seed.collectAsState(null)
|
||||||
|
|
||||||
RecoveryPasswordScreen(
|
RecoveryPasswordScreen(
|
||||||
viewModel.seed,
|
mnemonic = mnemonic,
|
||||||
{ viewModel.copySeed(this) }
|
seed = seed,
|
||||||
) { onHide() }
|
copySeed = { viewModel.copySeed(this) },
|
||||||
|
onHide = ::onHide
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,7 +5,15 @@ import android.content.ClipData
|
|||||||
import android.content.ClipboardManager
|
import android.content.ClipboardManager
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import androidx.lifecycle.AndroidViewModel
|
import androidx.lifecycle.AndroidViewModel
|
||||||
|
import androidx.lifecycle.viewModelScope
|
||||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
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.AppTextSecurePreferences
|
||||||
import org.session.libsession.utilities.TextSecurePreferences
|
import org.session.libsession.utilities.TextSecurePreferences
|
||||||
import org.session.libsignal.crypto.MnemonicCodec
|
import org.session.libsignal.crypto.MnemonicCodec
|
||||||
@ -20,21 +28,26 @@ class RecoveryPasswordViewModel @Inject constructor(
|
|||||||
): AndroidViewModel(application) {
|
): AndroidViewModel(application) {
|
||||||
val prefs = AppTextSecurePreferences(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() {
|
fun permanentlyHidePassword() {
|
||||||
prefs.setHidePassword(true)
|
prefs.setHidePassword(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun copySeed(context: Context) {
|
fun copySeed(context: Context) {
|
||||||
|
val seed = seed.value ?: return
|
||||||
TextSecurePreferences.setHasViewedSeed(context, true)
|
TextSecurePreferences.setHasViewedSeed(context, true)
|
||||||
val clipboard = context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
|
val clipboard = context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
|
||||||
val clip = ClipData.newPlainText("Seed", seed)
|
val clip = ClipData.newPlainText("Seed", seed)
|
||||||
clipboard.setPrimaryClip(clip)
|
clipboard.setPrimaryClip(clip)
|
||||||
}
|
}
|
||||||
|
|
||||||
val seed by lazy {
|
init {
|
||||||
val hexEncodedSeed = IdentityKeyUtil.retrieve(application, IdentityKeyUtil.LOKI_SEED)
|
viewModelScope.launch(Dispatchers.IO) {
|
||||||
?: IdentityKeyUtil.getIdentityKeyPair(application).hexEncodedPrivateKey // Legacy account
|
seed.emit(IdentityKeyUtil.retrieve(application, IdentityKeyUtil.LOKI_SEED)
|
||||||
MnemonicCodec { MnemonicUtilities.loadFileContents(application, it) }
|
?: IdentityKeyUtil.getIdentityKeyPair(application).hexEncodedPrivateKey) // Legacy account
|
||||||
.encode(hexEncodedSeed, MnemonicCodec.Language.Configuration.english)
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -37,7 +37,7 @@ import org.thoughtcrime.securesms.util.QRCodeUtilities
|
|||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun QrImage(
|
fun QrImage(
|
||||||
string: String,
|
string: String?,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
icon: Int = R.drawable.session_shield
|
icon: Int = R.drawable.session_shield
|
||||||
) {
|
) {
|
||||||
@ -47,7 +47,7 @@ fun QrImage(
|
|||||||
|
|
||||||
val scope = rememberCoroutineScope()
|
val scope = rememberCoroutineScope()
|
||||||
LaunchedEffect(string) {
|
LaunchedEffect(string) {
|
||||||
scope.launch(Dispatchers.IO) {
|
if (string != null) scope.launch(Dispatchers.IO) {
|
||||||
bitmap = (300..500 step 100).firstNotNullOf {
|
bitmap = (300..500 step 100).firstNotNullOf {
|
||||||
runCatching { QRCodeUtilities.encode(string, it) }.getOrNull()
|
runCatching { QRCodeUtilities.encode(string, it) }.getOrNull()
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user