- Fix seed words in the account backup screen

- Make use of secure flag when showing the seed words
This commit is contained in:
greenart7c3
2025-09-15 11:51:16 -03:00
parent 3e05df8ec2
commit 09b72fb11a
3 changed files with 120 additions and 70 deletions
@@ -427,7 +427,6 @@ fun MainScreen(
.padding(horizontal = verticalPadding)
.padding(top = verticalPadding * 1.5f)
.imePadding(),
account,
navController,
)
},
@@ -25,6 +25,7 @@ import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.ContentCopy
import androidx.compose.material.icons.outlined.FormatListNumbered
import androidx.compose.material.icons.outlined.Person
import androidx.compose.material.icons.outlined.QrCode
import androidx.compose.material.icons.outlined.Visibility
@@ -83,7 +84,6 @@ import com.greenart7c3.nostrsigner.service.toShortenHex
import com.greenart7c3.nostrsigner.ui.CenterCircularProgressIndicator
import com.greenart7c3.nostrsigner.ui.InnerQrCodeDrawer
import com.greenart7c3.nostrsigner.ui.QrCodeDrawer
import com.greenart7c3.nostrsigner.ui.components.AmberButton
import com.greenart7c3.nostrsigner.ui.components.CloseButton
import com.greenart7c3.nostrsigner.ui.components.SeedWordsPage
import com.greenart7c3.nostrsigner.ui.navigation.Route
@@ -107,38 +107,9 @@ import kotlinx.coroutines.launch
@Composable
fun AccountBackupScreen(
modifier: Modifier,
account: Account,
navController: NavHostController,
) {
var isLoading by remember { mutableStateOf(false) }
var showSeedWords by remember { mutableStateOf(false) }
if (showSeedWords) {
ModalBottomSheet(
onDismissRequest = {
showSeedWords = false
},
sheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true),
) {
Column(
modifier =
Modifier
.background(MaterialTheme.colorScheme.background)
.fillMaxSize(),
) {
CompositionLocalProvider(
LocalDensity provides Density(
LocalDensity.current.density,
1f,
),
) {
SeedWordsPage(
seedWords = account.seedWords,
showNextButton = false,
)
}
}
}
}
Surface(
modifier =
@@ -173,41 +144,6 @@ fun AccountBackupScreen(
Spacer(modifier = Modifier.height(30.dp))
if (Nip06().isValidMnemonic(account.seedWords.joinToString(separator = " "))) {
val keyguardLauncher =
rememberLauncherForActivityResult(ActivityResultContracts.StartActivityForResult()) { result: ActivityResult ->
if (result.resultCode == Activity.RESULT_OK) {
showSeedWords = true
}
}
val context = LocalContext.current
AmberButton(
onClick = {
authenticate(
title = context.getString(R.string.show_seed_words),
context = context,
keyguardLauncher = keyguardLauncher,
onApproved = {
showSeedWords = true
account.didBackup = true
Amber.instance.applicationIOScope.launch {
LocalPreferences.saveToEncryptedStorage(context, account)
}
},
onError = { _, message ->
Toast.makeText(
context,
message,
Toast.LENGTH_SHORT,
).show()
},
)
},
text = stringResource(R.string.show_seed_words),
)
}
val content1 = stringResource(R.string.account_backup_tips3_md)
val astNode1 =
@@ -344,13 +280,20 @@ fun AccountBackupScreen(
}
Text(
modifier = Modifier
.padding(start = 4.dp)
.fillMaxWidth(0.8f),
.padding(start = 4.dp),
text = localAccount.name.value.ifBlank { it.npub.toShortenHex() },
)
NSecCopyButton(localAccount, password.value.text, onLoading = { value -> isLoading = value })
NSecQrButton(localAccount, password.value.text, onLoading = { value -> isLoading = value }, navController = navController)
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.End,
) {
NSecCopyButton(localAccount, password.value.text, onLoading = { value -> isLoading = value })
NSecQrButton(localAccount, password.value.text, onLoading = { value -> isLoading = value }, navController = navController)
if (Nip06().isValidMnemonic(localAccount.seedWords.joinToString(separator = " "))) {
SeedWordsButton(localAccount)
}
}
}
}
}
@@ -528,6 +471,83 @@ fun QrCodeDialog(
}
}
@OptIn(ExperimentalMaterial3Api::class)
@Composable
private fun SeedWordsButton(
account: Account,
) {
var showSeedWords by remember { mutableStateOf(false) }
val context = LocalContext.current
val keyguardLauncher =
rememberLauncherForActivityResult(ActivityResultContracts.StartActivityForResult()) { result: ActivityResult ->
if (result.resultCode == Activity.RESULT_OK) {
showSeedWords = true
}
}
if (showSeedWords) {
ModalBottomSheet(
onDismissRequest = {
showSeedWords = false
},
sheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true),
) {
Column(
modifier =
Modifier
.background(MaterialTheme.colorScheme.background)
.fillMaxSize(),
) {
CompositionLocalProvider(
LocalDensity provides Density(
LocalDensity.current.density,
1f,
),
) {
SeedWordsPage(
seedWords = account.seedWords,
showNextButton = false,
)
}
}
}
}
IconButton(
onClick = {
authenticate(
title = context.getString(R.string.show_seed_words),
context = context,
keyguardLauncher = keyguardLauncher,
onApproved = {
Amber.instance.applicationIOScope.launch {
account.didBackup = true
showSeedWords = true
Amber.instance.applicationIOScope.launch {
LocalPreferences.saveToEncryptedStorage(context, account)
}
}
},
onError = { _, message ->
Amber.instance.applicationIOScope.launch(Dispatchers.Main) {
Toast.makeText(
context,
message,
Toast.LENGTH_SHORT,
).show()
}
},
)
},
content = {
Icon(
imageVector = Icons.Outlined.FormatListNumbered,
contentDescription = context.getString(R.string.show_seed_words),
)
},
)
}
@Composable
private fun NSecCopyButton(
account: Account,
@@ -1,6 +1,7 @@
package com.greenart7c3.nostrsigner.ui.components
import android.content.ClipData
import android.view.WindowManager
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
@@ -15,6 +16,7 @@ import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
@@ -25,6 +27,10 @@ import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleEventObserver
import androidx.lifecycle.compose.LocalLifecycleOwner
import com.greenart7c3.nostrsigner.Amber
import com.greenart7c3.nostrsigner.R
import com.greenart7c3.nostrsigner.ui.verticalScrollbar
import kotlinx.coroutines.launch
@@ -144,4 +150,29 @@ fun SeedWordsPage(
}
}
}
val lifecycleOwner = LocalLifecycleOwner.current
DisposableEffect(key1 = lifecycleOwner) {
val observer = LifecycleEventObserver { _, event ->
when (event) {
Lifecycle.Event.ON_START -> {
Amber.instance.getMainActivity()?.window?.setFlags(
WindowManager.LayoutParams.FLAG_SECURE,
WindowManager.LayoutParams.FLAG_SECURE,
)
}
Lifecycle.Event.ON_PAUSE -> {
Amber.instance.getMainActivity()?.window?.clearFlags(WindowManager.LayoutParams.FLAG_SECURE)
}
else -> {}
}
}
lifecycleOwner.lifecycle.addObserver(observer)
onDispose {
Amber.instance.getMainActivity()?.window?.clearFlags(WindowManager.LayoutParams.FLAG_SECURE)
lifecycleOwner.lifecycle.removeObserver(observer)
}
}
}