diff options
Diffstat (limited to 'app/src/main/java')
-rw-r--r-- | app/src/main/java/app/passwordstore/ui/crypto/DecryptScreen.kt | 56 |
1 files changed, 52 insertions, 4 deletions
diff --git a/app/src/main/java/app/passwordstore/ui/crypto/DecryptScreen.kt b/app/src/main/java/app/passwordstore/ui/crypto/DecryptScreen.kt index d48e70a7..64257709 100644 --- a/app/src/main/java/app/passwordstore/ui/crypto/DecryptScreen.kt +++ b/app/src/main/java/app/passwordstore/ui/crypto/DecryptScreen.kt @@ -19,6 +19,8 @@ import androidx.compose.ui.platform.LocalClipboardManager import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.AnnotatedString +import androidx.compose.ui.text.capitalize +import androidx.compose.ui.text.intl.Locale import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import app.passwordstore.R @@ -32,11 +34,23 @@ import kotlin.time.ExperimentalTime import kotlinx.coroutines.flow.first import kotlinx.coroutines.runBlocking +/** + * Composable to show a [PasswordEntry]. It can be used for both read-only usage (decrypt screen) or + * read-write (encrypt screen) to allow sharing UI logic for both these screens and deferring all + * the cryptographic aspects to its parent. + * + * When [readOnly] is `true`, the Composable assumes that we're showcasing the provided [entry] to + * the user and does not offer any edit capabilities. + * + * When [readOnly] is `false`, the [TextField]s are rendered editable but currently do not pass up + * their "updated" state to anything. This will be changed in later commits. + */ @OptIn(ExperimentalTime::class, ExperimentalMaterial3Api::class) @Composable fun PasswordEntryScreen( entryName: String, entry: PasswordEntry, + readOnly: Boolean, modifier: Modifier = Modifier, ) { Scaffold( @@ -61,21 +75,22 @@ fun PasswordEntryScreen( value = entry.password!!, label = "Password", initialVisibility = false, + readOnly = readOnly, modifier = Modifier.padding(bottom = 8.dp), ) } - if (entry.hasTotp()) { + if (entry.hasTotp() && readOnly) { val totp by entry.totp.collectAsState(runBlocking { entry.totp.first() }) TextField( value = totp.value, onValueChange = {}, - readOnly = true, + readOnly = readOnly, label = { Text("OTP (expires in ${totp.remainingTime.inWholeSeconds}s)") }, trailingIcon = { CopyButton({ totp.value }) }, modifier = Modifier.padding(bottom = 8.dp), ) } - if (entry.username != null) { + if (entry.username != null && readOnly) { TextField( value = entry.username!!, onValueChange = {}, @@ -85,12 +100,42 @@ fun PasswordEntryScreen( modifier = Modifier.padding(bottom = 8.dp), ) } + ExtraContent(entry = entry, readOnly = readOnly) } } } } @Composable +@OptIn(ExperimentalMaterial3Api::class) +private fun ExtraContent( + entry: PasswordEntry, + readOnly: Boolean, + modifier: Modifier = Modifier, +) { + if (readOnly) { + entry.extraContent.forEach { (label, value) -> + TextField( + value = value, + onValueChange = {}, + readOnly = true, + label = { Text(label.capitalize(Locale.current)) }, + trailingIcon = { CopyButton({ value }) }, + modifier = modifier.padding(bottom = 8.dp), + ) + } + } else { + TextField( + value = entry.extraContentWithoutAuthData, + onValueChange = {}, + readOnly = false, + label = { Text("Extra content") }, + modifier = modifier, + ) + } +} + +@Composable private fun CopyButton( textToCopy: () -> String, modifier: Modifier = Modifier, @@ -110,7 +155,9 @@ private fun CopyButton( @Preview @Composable private fun PasswordEntryPreview() { - APSThemePreview { PasswordEntryScreen("Test Entry", createTestEntry()) } + APSThemePreview { + PasswordEntryScreen(entryName = "Test Entry", entry = createTestEntry(), readOnly = true) + } } private fun createTestEntry() = @@ -121,6 +168,7 @@ private fun createTestEntry() = |My Password |otpauth://totp/ACME%20Co:john@example.com?secret=HXDMVJECJJWSRB3HWIZR4IFUGFTMXBOZ&issuer=ACME%20Co&algorithm=SHA1&digits=6&period=30 |login: msfjarvis + |URL: example.com """ .trimMargin() .encodeToByteArray() |