diff options
4 files changed, 84 insertions, 8 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 41687985..3dc2a401 100644 --- a/app/src/main/java/app/passwordstore/ui/crypto/DecryptScreen.kt +++ b/app/src/main/java/app/passwordstore/ui/crypto/DecryptScreen.kt @@ -24,6 +24,7 @@ import androidx.compose.ui.unit.dp import app.passwordstore.R import app.passwordstore.data.passfile.PasswordEntry import app.passwordstore.ui.APSAppBar +import app.passwordstore.ui.compose.PasswordField import app.passwordstore.ui.compose.theme.APSThemePreview import app.passwordstore.util.time.UserClock import app.passwordstore.util.totp.UriTotpFinder @@ -56,13 +57,7 @@ fun PasswordEntryScreen( style = MaterialTheme.typography.headlineSmall, ) if (entry.password != null) { - TextField( - value = entry.password!!, - onValueChange = {}, - readOnly = true, - label = { Text("Password") }, - trailingIcon = { CopyButton { clipboard.setText(AnnotatedString(entry.password!!)) } }, - ) + PasswordField(value = entry.password!!, label = "Password", initialVisibility = false) } if (entry.hasTotp()) { val totp by entry.totp.collectAsState(runBlocking { entry.totp.first() }) @@ -97,7 +92,7 @@ private fun PasswordEntryPreview() { APSThemePreview { PasswordEntryScreen("Test Entry", createTestEntry()) } } -fun createTestEntry() = +private fun createTestEntry() = PasswordEntry( UserClock(), UriTotpFinder(), diff --git a/ui-compose/src/main/kotlin/app/passwordstore/ui/compose/PasswordField.kt b/ui-compose/src/main/kotlin/app/passwordstore/ui/compose/PasswordField.kt new file mode 100644 index 00000000..81255979 --- /dev/null +++ b/ui-compose/src/main/kotlin/app/passwordstore/ui/compose/PasswordField.kt @@ -0,0 +1,61 @@ +package app.passwordstore.ui.compose + +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.material3.Text +import androidx.compose.material3.TextField +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.text.input.PasswordVisualTransformation +import androidx.compose.ui.text.input.VisualTransformation + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +public fun PasswordField( + value: String, + label: String, + initialVisibility: Boolean, + modifier: Modifier = Modifier, +) { + var visible by remember { mutableStateOf(initialVisibility) } + TextField( + value = value, + onValueChange = {}, + readOnly = true, + label = { Text(label) }, + visualTransformation = + if (visible) VisualTransformation.None else PasswordVisualTransformation(), + trailingIcon = { + ToggleButton( + visible = visible, + contentDescription = "Toggle password visibility", + onButtonClick = { visible = !visible }, + ) + }, + modifier = modifier, + ) +} + +@Composable +private fun ToggleButton( + visible: Boolean, + contentDescription: String, + onButtonClick: () -> Unit, + modifier: Modifier = Modifier, +) { + IconButton(onClick = onButtonClick, modifier = modifier) { + val icon = + if (visible) painterResource(id = R.drawable.baseline_visibility_off_24) + else painterResource(id = R.drawable.baseline_visibility_24) + Icon( + painter = icon, + contentDescription = contentDescription, + ) + } +} diff --git a/ui-compose/src/main/res/drawable/baseline_visibility_24.xml b/ui-compose/src/main/res/drawable/baseline_visibility_24.xml new file mode 100644 index 00000000..e732f005 --- /dev/null +++ b/ui-compose/src/main/res/drawable/baseline_visibility_24.xml @@ -0,0 +1,10 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:tint="#000000" + android:viewportWidth="24" + android:viewportHeight="24"> + <path + android:fillColor="@android:color/white" + android:pathData="M12,4.5C7,4.5 2.73,7.61 1,12c1.73,4.39 6,7.5 11,7.5s9.27,-3.11 11,-7.5c-1.73,-4.39 -6,-7.5 -11,-7.5zM12,17c-2.76,0 -5,-2.24 -5,-5s2.24,-5 5,-5 5,2.24 5,5 -2.24,5 -5,5zM12,9c-1.66,0 -3,1.34 -3,3s1.34,3 3,3 3,-1.34 3,-3 -1.34,-3 -3,-3z" /> +</vector> diff --git a/ui-compose/src/main/res/drawable/baseline_visibility_off_24.xml b/ui-compose/src/main/res/drawable/baseline_visibility_off_24.xml new file mode 100644 index 00000000..a5cad715 --- /dev/null +++ b/ui-compose/src/main/res/drawable/baseline_visibility_off_24.xml @@ -0,0 +1,10 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:tint="#000000" + android:viewportWidth="24" + android:viewportHeight="24"> + <path + android:fillColor="@android:color/white" + android:pathData="M12,7c2.76,0 5,2.24 5,5 0,0.65 -0.13,1.26 -0.36,1.83l2.92,2.92c1.51,-1.26 2.7,-2.89 3.43,-4.75 -1.73,-4.39 -6,-7.5 -11,-7.5 -1.4,0 -2.74,0.25 -3.98,0.7l2.16,2.16C10.74,7.13 11.35,7 12,7zM2,4.27l2.28,2.28 0.46,0.46C3.08,8.3 1.78,10.02 1,12c1.73,4.39 6,7.5 11,7.5 1.55,0 3.03,-0.3 4.38,-0.84l0.42,0.42L19.73,22 21,20.73 3.27,3 2,4.27zM7.53,9.8l1.55,1.55c-0.05,0.21 -0.08,0.43 -0.08,0.65 0,1.66 1.34,3 3,3 0.22,0 0.44,-0.03 0.65,-0.08l1.55,1.55c-0.67,0.33 -1.41,0.53 -2.2,0.53 -2.76,0 -5,-2.24 -5,-5 0,-0.79 0.2,-1.53 0.53,-2.2zM11.84,9.02l3.15,3.15 0.02,-0.16c0,-1.66 -1.34,-3 -3,-3l-0.17,0.01z" /> +</vector> |