Add ability to show/hide password

Screenshots:
- https://screenshot.googleplex.com/BJpAPUcZGLex2JR
- https://screenshot.googleplex.com/9HdkdBw7cg9XYRM

Bug: 266571936
Fix: 266571936
Test: local deployment
Change-Id: I42dc1346f719351590c01e81be3a466a3c4a0d01
This commit is contained in:
Harsh Lal
2023-01-27 00:08:01 +00:00
parent 50fce41954
commit 89fdaa3b3e
4 changed files with 86 additions and 13 deletions

View File

@@ -13,6 +13,10 @@
<string name="string_more_options">More options</string>
<!-- This is a label for a button that links to additional information about passkeys. [CHAR LIMIT=20] -->
<string name="string_learn_more">Learn more</string>
<!-- This is a label for content description for show password icon button. -->
<string name="content_description_show_password">Show password</string>
<!-- This is a label for content description for hide password icon button. -->
<string name="content_description_hide_password">Hide password</string>
<!-- This string introduces passkeys to the users for the first time they use this method. Tip: to avoid gendered language patterns, this header could be translated as if the original string were "More safety with passkeys". [CHAR LIMIT=200] -->
<string name="passkey_creation_intro_title">Safer with passkeys</string>
<!-- This string highlight passkey benefits related with the password. [CHAR LIMIT=200] -->

View File

@@ -69,7 +69,7 @@ class CredentialManagerRepo(
requestInfo = intent.extras?.getParcelable(
RequestInfo.EXTRA_REQUEST_INFO,
RequestInfo::class.java
) ?: testCreatePasskeyRequestInfo()
) ?: testCreatePasswordRequestInfo()
providerEnabledList = when (requestInfo.type) {
RequestInfo.TYPE_CREATE ->

View File

@@ -16,11 +16,23 @@
package com.android.credentialmanager.common.ui
import com.android.credentialmanager.R
import androidx.compose.material.Icon
import androidx.compose.material.IconButton
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.Visibility
import androidx.compose.material.icons.outlined.VisibilityOff
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import com.android.credentialmanager.ui.theme.LocalAndroidColorScheme
@Composable
fun ActionButton(text: String, onClick: () -> Unit) {
@@ -32,4 +44,27 @@ fun ActionButton(text: String, onClick: () -> Unit) {
) {
Text(text = text)
}
}
@Composable
fun ToggleVisibilityButton(modifier: Modifier = Modifier, onToggle: (Boolean) -> Unit) {
// default state is visibility off
val toggleState: MutableState<Boolean> = remember { mutableStateOf(false) }
IconButton(
modifier = modifier,
onClick = {
toggleState.value = !toggleState.value
onToggle(toggleState.value)
}
) {
Icon(
imageVector = if (toggleState.value)
Icons.Outlined.Visibility else Icons.Outlined.VisibilityOff,
contentDescription = if (toggleState.value)
stringResource(R.string.content_description_show_password) else
stringResource(R.string.content_description_hide_password),
tint = LocalAndroidColorScheme.current.colorAccentPrimaryVariant
)
}
}

View File

@@ -11,9 +11,15 @@ import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Add
import androidx.compose.material.icons.filled.ArrowBack
import androidx.compose.material.icons.outlined.NewReleases
import androidx.compose.material3.Divider
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
@@ -21,18 +27,19 @@ import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.TopAppBar
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.ArrowBack
import androidx.compose.material.icons.outlined.NewReleases
import androidx.compose.material.icons.filled.Add
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.asImageBitmap
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.AnnotatedString
import androidx.compose.ui.text.input.PasswordVisualTransformation
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import androidx.core.graphics.drawable.toBitmap
@@ -43,14 +50,15 @@ import com.android.credentialmanager.common.material.ModalBottomSheetValue
import com.android.credentialmanager.common.material.rememberModalBottomSheetState
import com.android.credentialmanager.common.ui.ActionButton
import com.android.credentialmanager.common.ui.ConfirmButton
import com.android.credentialmanager.common.ui.ContainerCard
import com.android.credentialmanager.common.ui.Entry
import com.android.credentialmanager.common.ui.TextOnSurface
import com.android.credentialmanager.common.ui.TextSecondary
import com.android.credentialmanager.common.ui.TextOnSurfaceVariant
import com.android.credentialmanager.common.ui.ContainerCard
import com.android.credentialmanager.common.ui.TextSecondary
import com.android.credentialmanager.common.ui.ToggleVisibilityButton
import com.android.credentialmanager.jetpack.developer.PublicKeyCredential.Companion.TYPE_PUBLIC_KEY_CREDENTIAL
import com.android.credentialmanager.ui.theme.EntryShape
import com.android.credentialmanager.ui.theme.LocalAndroidColorScheme
import com.android.credentialmanager.jetpack.developer.PublicKeyCredential.Companion.TYPE_PUBLIC_KEY_CREDENTIAL
@OptIn(ExperimentalMaterial3Api::class)
@Composable
@@ -852,12 +860,38 @@ fun PrimaryCreateOptionRow(
style = MaterialTheme.typography.titleLarge,
modifier = Modifier.padding(top = 16.dp, start = 5.dp),
)
TextSecondary(
Row(modifier = Modifier.fillMaxWidth().padding(top = 4.dp, bottom = 16.dp,
start = 5.dp),
verticalAlignment = Alignment.CenterVertically) {
val visualTransformation = remember { PasswordVisualTransformation() }
// This subtitle would never be null for create password
text = requestDisplayInfo.subtitle ?: "",
style = MaterialTheme.typography.bodyMedium,
modifier = Modifier.padding(bottom = 16.dp, start = 5.dp),
)
val originalPassword by remember {
mutableStateOf(requestDisplayInfo.subtitle ?: "")
}
val displayedPassword = remember {
mutableStateOf(
visualTransformation.filter(
AnnotatedString(originalPassword)
).text.text
)
}
TextSecondary(
text = displayedPassword.value,
style = MaterialTheme.typography.bodyMedium,
modifier = Modifier.padding(top = 4.dp, bottom = 4.dp),
)
ToggleVisibilityButton(modifier = Modifier.padding(start = 4.dp)
.height(24.dp).width(24.dp), onToggle = {
if (it) {
displayedPassword.value = originalPassword
} else {
displayedPassword.value = visualTransformation.filter(
AnnotatedString(originalPassword)
).text.text
}
})
}
}
else -> {
if (requestDisplayInfo.subtitle != null) {