From 09b72fb11a66ec678a3a5a1da292f146ee71f664 Mon Sep 17 00:00:00 2001 From: greenart7c3 Date: Mon, 15 Sep 2025 10:13:58 -0300 Subject: [PATCH] - Fix seed words in the account backup screen - Make use of secure flag when showing the seed words --- .../greenart7c3/nostrsigner/ui/MainScreen.kt | 1 - .../ui/actions/AccountBackupDialog.kt | 158 ++++++++++-------- .../ui/components/SeedWordsPage.kt | 31 ++++ 3 files changed, 120 insertions(+), 70 deletions(-) diff --git a/app/src/main/java/com/greenart7c3/nostrsigner/ui/MainScreen.kt b/app/src/main/java/com/greenart7c3/nostrsigner/ui/MainScreen.kt index e218280f..9233a490 100644 --- a/app/src/main/java/com/greenart7c3/nostrsigner/ui/MainScreen.kt +++ b/app/src/main/java/com/greenart7c3/nostrsigner/ui/MainScreen.kt @@ -427,7 +427,6 @@ fun MainScreen( .padding(horizontal = verticalPadding) .padding(top = verticalPadding * 1.5f) .imePadding(), - account, navController, ) }, diff --git a/app/src/main/java/com/greenart7c3/nostrsigner/ui/actions/AccountBackupDialog.kt b/app/src/main/java/com/greenart7c3/nostrsigner/ui/actions/AccountBackupDialog.kt index 6e5ce42e..95827f5f 100644 --- a/app/src/main/java/com/greenart7c3/nostrsigner/ui/actions/AccountBackupDialog.kt +++ b/app/src/main/java/com/greenart7c3/nostrsigner/ui/actions/AccountBackupDialog.kt @@ -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, diff --git a/app/src/main/java/com/greenart7c3/nostrsigner/ui/components/SeedWordsPage.kt b/app/src/main/java/com/greenart7c3/nostrsigner/ui/components/SeedWordsPage.kt index 28d1e902..7e643e94 100644 --- a/app/src/main/java/com/greenart7c3/nostrsigner/ui/components/SeedWordsPage.kt +++ b/app/src/main/java/com/greenart7c3/nostrsigner/ui/components/SeedWordsPage.kt @@ -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) + } + } }