Skip to content
This repository was archived by the owner on Oct 15, 2024. It is now read-only.

Commit c866bb9

Browse files
committed
feat(app): support toggling read-only status in DecryptScreen
1 parent 6d73b63 commit c866bb9

File tree

2 files changed

+54
-5
lines changed

2 files changed

+54
-5
lines changed

app/src/main/java/app/passwordstore/ui/crypto/DecryptScreen.kt

Lines changed: 52 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ import androidx.compose.ui.platform.LocalClipboardManager
1919
import androidx.compose.ui.res.painterResource
2020
import androidx.compose.ui.res.stringResource
2121
import androidx.compose.ui.text.AnnotatedString
22+
import androidx.compose.ui.text.capitalize
23+
import androidx.compose.ui.text.intl.Locale
2224
import androidx.compose.ui.tooling.preview.Preview
2325
import androidx.compose.ui.unit.dp
2426
import app.passwordstore.R
@@ -32,11 +34,23 @@ import kotlin.time.ExperimentalTime
3234
import kotlinx.coroutines.flow.first
3335
import kotlinx.coroutines.runBlocking
3436

37+
/**
38+
* Composable to show a [PasswordEntry]. It can be used for both read-only usage (decrypt screen) or
39+
* read-write (encrypt screen) to allow sharing UI logic for both these screens and deferring all
40+
* the cryptographic aspects to its parent.
41+
*
42+
* When [readOnly] is `true`, the Composable assumes that we're showcasing the provided [entry] to
43+
* the user and does not offer any edit capabilities.
44+
*
45+
* When [readOnly] is `false`, the [TextField]s are rendered editable but currently do not pass up
46+
* their "updated" state to anything. This will be changed in later commits.
47+
*/
3548
@OptIn(ExperimentalTime::class, ExperimentalMaterial3Api::class)
3649
@Composable
3750
fun PasswordEntryScreen(
3851
entryName: String,
3952
entry: PasswordEntry,
53+
readOnly: Boolean,
4054
modifier: Modifier = Modifier,
4155
) {
4256
Scaffold(
@@ -61,21 +75,22 @@ fun PasswordEntryScreen(
6175
value = entry.password!!,
6276
label = "Password",
6377
initialVisibility = false,
78+
readOnly = readOnly,
6479
modifier = Modifier.padding(bottom = 8.dp),
6580
)
6681
}
67-
if (entry.hasTotp()) {
82+
if (entry.hasTotp() && readOnly) {
6883
val totp by entry.totp.collectAsState(runBlocking { entry.totp.first() })
6984
TextField(
7085
value = totp.value,
7186
onValueChange = {},
72-
readOnly = true,
87+
readOnly = readOnly,
7388
label = { Text("OTP (expires in ${totp.remainingTime.inWholeSeconds}s)") },
7489
trailingIcon = { CopyButton({ totp.value }) },
7590
modifier = Modifier.padding(bottom = 8.dp),
7691
)
7792
}
78-
if (entry.username != null) {
93+
if (entry.username != null && readOnly) {
7994
TextField(
8095
value = entry.username!!,
8196
onValueChange = {},
@@ -85,11 +100,41 @@ fun PasswordEntryScreen(
85100
modifier = Modifier.padding(bottom = 8.dp),
86101
)
87102
}
103+
ExtraContent(entry = entry, readOnly = readOnly)
88104
}
89105
}
90106
}
91107
}
92108

109+
@Composable
110+
@OptIn(ExperimentalMaterial3Api::class)
111+
private fun ExtraContent(
112+
entry: PasswordEntry,
113+
readOnly: Boolean,
114+
modifier: Modifier = Modifier,
115+
) {
116+
if (readOnly) {
117+
entry.extraContent.forEach { (label, value) ->
118+
TextField(
119+
value = value,
120+
onValueChange = {},
121+
readOnly = true,
122+
label = { Text(label.capitalize(Locale.current)) },
123+
trailingIcon = { CopyButton({ value }) },
124+
modifier = modifier.padding(bottom = 8.dp),
125+
)
126+
}
127+
} else {
128+
TextField(
129+
value = entry.extraContentWithoutAuthData,
130+
onValueChange = {},
131+
readOnly = false,
132+
label = { Text("Extra content") },
133+
modifier = modifier,
134+
)
135+
}
136+
}
137+
93138
@Composable
94139
private fun CopyButton(
95140
textToCopy: () -> String,
@@ -110,7 +155,9 @@ private fun CopyButton(
110155
@Preview
111156
@Composable
112157
private fun PasswordEntryPreview() {
113-
APSThemePreview { PasswordEntryScreen("Test Entry", createTestEntry()) }
158+
APSThemePreview {
159+
PasswordEntryScreen(entryName = "Test Entry", entry = createTestEntry(), readOnly = true)
160+
}
114161
}
115162

116163
private fun createTestEntry() =
@@ -121,6 +168,7 @@ private fun createTestEntry() =
121168
|My Password
122169
|otpauth://totp/ACME%20Co:[email protected]?secret=HXDMVJECJJWSRB3HWIZR4IFUGFTMXBOZ&issuer=ACME%20Co&algorithm=SHA1&digits=6&period=30
123170
|login: msfjarvis
171+
|URL: example.com
124172
"""
125173
.trimMargin()
126174
.encodeToByteArray()

ui-compose/src/main/kotlin/app/passwordstore/ui/compose/PasswordField.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,14 @@ public fun PasswordField(
2121
value: String,
2222
label: String,
2323
initialVisibility: Boolean,
24+
readOnly: Boolean,
2425
modifier: Modifier = Modifier,
2526
) {
2627
var visible by remember { mutableStateOf(initialVisibility) }
2728
TextField(
2829
value = value,
2930
onValueChange = {},
30-
readOnly = true,
31+
readOnly = readOnly,
3132
label = { Text(label) },
3233
visualTransformation =
3334
if (visible) VisualTransformation.None else PasswordVisualTransformation(),

0 commit comments

Comments
 (0)