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

Commit cb373db

Browse files
committed
feat(app): start working on a Compose-backed decrypt screen
1 parent 48ae52f commit cb373db

File tree

1 file changed

+91
-0
lines changed

1 file changed

+91
-0
lines changed
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
package app.passwordstore.ui.crypto
2+
3+
import androidx.compose.foundation.layout.Box
4+
import androidx.compose.foundation.layout.Column
5+
import androidx.compose.foundation.layout.fillMaxSize
6+
import androidx.compose.material3.ExperimentalMaterial3Api
7+
import androidx.compose.material3.Icon
8+
import androidx.compose.material3.IconButton
9+
import androidx.compose.material3.Text
10+
import androidx.compose.material3.TextField
11+
import androidx.compose.runtime.Composable
12+
import androidx.compose.runtime.collectAsState
13+
import androidx.compose.runtime.getValue
14+
import androidx.compose.ui.Modifier
15+
import androidx.compose.ui.platform.LocalClipboardManager
16+
import androidx.compose.ui.res.painterResource
17+
import androidx.compose.ui.res.stringResource
18+
import androidx.compose.ui.text.AnnotatedString
19+
import androidx.compose.ui.tooling.preview.Preview
20+
import app.passwordstore.R
21+
import app.passwordstore.data.passfile.PasswordEntry
22+
import app.passwordstore.util.time.UserClock
23+
import app.passwordstore.util.totp.UriTotpFinder
24+
import kotlin.time.ExperimentalTime
25+
import kotlinx.coroutines.flow.first
26+
import kotlinx.coroutines.runBlocking
27+
28+
@OptIn(ExperimentalTime::class, ExperimentalMaterial3Api::class)
29+
@Composable
30+
fun PasswordEntryScreen(
31+
entryName: String,
32+
entry: PasswordEntry,
33+
modifier: Modifier = Modifier,
34+
) {
35+
val clipboard = LocalClipboardManager.current
36+
Box(modifier = modifier.fillMaxSize()) {
37+
Column {
38+
Text(entryName)
39+
if (entry.password != null) {
40+
TextField(
41+
value = entry.password!!,
42+
onValueChange = {},
43+
readOnly = true,
44+
label = { Text("Password") },
45+
trailingIcon = { CopyButton { clipboard.setText(AnnotatedString(entry.password!!)) } },
46+
)
47+
}
48+
if (entry.hasTotp()) {
49+
val totp by entry.totp.collectAsState(runBlocking { entry.totp.first() })
50+
TextField(
51+
value = totp.value,
52+
onValueChange = {},
53+
readOnly = true,
54+
label = { Text("OTP (expires in ${totp.remainingTime.inWholeSeconds}s)") },
55+
trailingIcon = { CopyButton { clipboard.setText(AnnotatedString(totp.value)) } }
56+
)
57+
}
58+
}
59+
}
60+
}
61+
62+
@Composable
63+
private fun CopyButton(onClick: () -> Unit) {
64+
IconButton(
65+
onClick = onClick,
66+
) {
67+
Icon(
68+
painter = painterResource(R.drawable.ic_content_copy),
69+
contentDescription = stringResource(R.string.copy_password),
70+
)
71+
}
72+
}
73+
74+
@Preview
75+
@Composable
76+
private fun PasswordEntryPreview() {
77+
PasswordEntryScreen("Test Entry", createTestEntry())
78+
}
79+
80+
fun createTestEntry() =
81+
PasswordEntry(
82+
UserClock(),
83+
UriTotpFinder(),
84+
"""
85+
|My Password
86+
|otpauth://totp/ACME%20Co:[email protected]?secret=HXDMVJECJJWSRB3HWIZR4IFUGFTMXBOZ&issuer=ACME%20Co&algorithm=SHA1&digits=6&period=30
87+
|login: msfjarvis
88+
"""
89+
.trimMargin()
90+
.encodeToByteArray()
91+
)

0 commit comments

Comments
 (0)