@@ -19,6 +19,8 @@ import androidx.compose.ui.platform.LocalClipboardManager
19
19
import androidx.compose.ui.res.painterResource
20
20
import androidx.compose.ui.res.stringResource
21
21
import androidx.compose.ui.text.AnnotatedString
22
+ import androidx.compose.ui.text.capitalize
23
+ import androidx.compose.ui.text.intl.Locale
22
24
import androidx.compose.ui.tooling.preview.Preview
23
25
import androidx.compose.ui.unit.dp
24
26
import app.passwordstore.R
@@ -32,11 +34,23 @@ import kotlin.time.ExperimentalTime
32
34
import kotlinx.coroutines.flow.first
33
35
import kotlinx.coroutines.runBlocking
34
36
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
+ */
35
48
@OptIn(ExperimentalTime ::class , ExperimentalMaterial3Api ::class )
36
49
@Composable
37
50
fun PasswordEntryScreen (
38
51
entryName : String ,
39
52
entry : PasswordEntry ,
53
+ readOnly : Boolean ,
40
54
modifier : Modifier = Modifier ,
41
55
) {
42
56
Scaffold (
@@ -61,21 +75,22 @@ fun PasswordEntryScreen(
61
75
value = entry.password!! ,
62
76
label = " Password" ,
63
77
initialVisibility = false ,
78
+ readOnly = readOnly,
64
79
modifier = Modifier .padding(bottom = 8 .dp),
65
80
)
66
81
}
67
- if (entry.hasTotp()) {
82
+ if (entry.hasTotp() && readOnly ) {
68
83
val totp by entry.totp.collectAsState(runBlocking { entry.totp.first() })
69
84
TextField (
70
85
value = totp.value,
71
86
onValueChange = {},
72
- readOnly = true ,
87
+ readOnly = readOnly ,
73
88
label = { Text (" OTP (expires in ${totp.remainingTime.inWholeSeconds} s)" ) },
74
89
trailingIcon = { CopyButton ({ totp.value }) },
75
90
modifier = Modifier .padding(bottom = 8 .dp),
76
91
)
77
92
}
78
- if (entry.username != null ) {
93
+ if (entry.username != null && readOnly ) {
79
94
TextField (
80
95
value = entry.username!! ,
81
96
onValueChange = {},
@@ -85,11 +100,41 @@ fun PasswordEntryScreen(
85
100
modifier = Modifier .padding(bottom = 8 .dp),
86
101
)
87
102
}
103
+ ExtraContent (entry = entry, readOnly = readOnly)
88
104
}
89
105
}
90
106
}
91
107
}
92
108
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
+
93
138
@Composable
94
139
private fun CopyButton (
95
140
textToCopy : () -> String ,
@@ -110,7 +155,9 @@ private fun CopyButton(
110
155
@Preview
111
156
@Composable
112
157
private fun PasswordEntryPreview () {
113
- APSThemePreview { PasswordEntryScreen (" Test Entry" , createTestEntry()) }
158
+ APSThemePreview {
159
+ PasswordEntryScreen (entryName = " Test Entry" , entry = createTestEntry(), readOnly = true )
160
+ }
114
161
}
115
162
116
163
private fun createTestEntry () =
@@ -121,6 +168,7 @@ private fun createTestEntry() =
121
168
|My Password
122
169
|otpauth://totp/ACME%20Co:[email protected] ?secret=HXDMVJECJJWSRB3HWIZR4IFUGFTMXBOZ&issuer=ACME%20Co&algorithm=SHA1&digits=6&period=30
123
170
|login: msfjarvis
171
+ |URL: example.com
124
172
"""
125
173
.trimMargin()
126
174
.encodeToByteArray()
0 commit comments