- 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(horizontal = verticalPadding)
.padding(top = verticalPadding * 1.5f) .padding(top = verticalPadding * 1.5f)
.imePadding(), .imePadding(),
account,
navController, navController,
) )
}, },
@@ -25,6 +25,7 @@ import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.ContentCopy 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.Person
import androidx.compose.material.icons.outlined.QrCode import androidx.compose.material.icons.outlined.QrCode
import androidx.compose.material.icons.outlined.Visibility 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.CenterCircularProgressIndicator
import com.greenart7c3.nostrsigner.ui.InnerQrCodeDrawer import com.greenart7c3.nostrsigner.ui.InnerQrCodeDrawer
import com.greenart7c3.nostrsigner.ui.QrCodeDrawer 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.CloseButton
import com.greenart7c3.nostrsigner.ui.components.SeedWordsPage import com.greenart7c3.nostrsigner.ui.components.SeedWordsPage
import com.greenart7c3.nostrsigner.ui.navigation.Route import com.greenart7c3.nostrsigner.ui.navigation.Route
@@ -107,38 +107,9 @@ import kotlinx.coroutines.launch
@Composable @Composable
fun AccountBackupScreen( fun AccountBackupScreen(
modifier: Modifier, modifier: Modifier,
account: Account,
navController: NavHostController, navController: NavHostController,
) { ) {
var isLoading by remember { mutableStateOf(false) } 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( Surface(
modifier = modifier =
@@ -173,41 +144,6 @@ fun AccountBackupScreen(
Spacer(modifier = Modifier.height(30.dp)) 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 content1 = stringResource(R.string.account_backup_tips3_md)
val astNode1 = val astNode1 =
@@ -344,13 +280,20 @@ fun AccountBackupScreen(
} }
Text( Text(
modifier = Modifier modifier = Modifier
.padding(start = 4.dp) .padding(start = 4.dp),
.fillMaxWidth(0.8f),
text = localAccount.name.value.ifBlank { it.npub.toShortenHex() }, text = localAccount.name.value.ifBlank { it.npub.toShortenHex() },
) )
NSecCopyButton(localAccount, password.value.text, onLoading = { value -> isLoading = value }) Row(
NSecQrButton(localAccount, password.value.text, onLoading = { value -> isLoading = value }, navController = navController) 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 @Composable
private fun NSecCopyButton( private fun NSecCopyButton(
account: Account, account: Account,
@@ -1,6 +1,7 @@
package com.greenart7c3.nostrsigner.ui.components package com.greenart7c3.nostrsigner.ui.components
import android.content.ClipData import android.content.ClipData
import android.view.WindowManager
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
@@ -15,6 +16,7 @@ import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Scaffold import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier 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.text.style.TextAlign
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp 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.R
import com.greenart7c3.nostrsigner.ui.verticalScrollbar import com.greenart7c3.nostrsigner.ui.verticalScrollbar
import kotlinx.coroutines.launch 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)
}
}
} }