From 89fdaa3b3e313fb8ce3890ae034c58c9b22dafdc Mon Sep 17 00:00:00 2001 From: Harsh Lal Date: Fri, 27 Jan 2023 00:08:01 +0000 Subject: [PATCH] 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 --- .../CredentialManager/res/values/strings.xml | 4 ++ .../CredentialManagerRepo.kt | 2 +- .../common/ui/ActionButton.kt | 35 +++++++++++ .../createflow/CreateCredentialComponents.kt | 58 +++++++++++++++---- 4 files changed, 86 insertions(+), 13 deletions(-) diff --git a/packages/CredentialManager/res/values/strings.xml b/packages/CredentialManager/res/values/strings.xml index d690971997128..49ac482699cfb 100644 --- a/packages/CredentialManager/res/values/strings.xml +++ b/packages/CredentialManager/res/values/strings.xml @@ -13,6 +13,10 @@ More options Learn more + + Show password + + Hide password Safer with passkeys diff --git a/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt b/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt index a48cd2b164516..45dcf3ce2ed56 100644 --- a/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt +++ b/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt @@ -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 -> diff --git a/packages/CredentialManager/src/com/android/credentialmanager/common/ui/ActionButton.kt b/packages/CredentialManager/src/com/android/credentialmanager/common/ui/ActionButton.kt index d0271abccabda..04483b19c574f 100644 --- a/packages/CredentialManager/src/com/android/credentialmanager/common/ui/ActionButton.kt +++ b/packages/CredentialManager/src/com/android/credentialmanager/common/ui/ActionButton.kt @@ -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 = 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 + ) + } } \ No newline at end of file diff --git a/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt b/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt index 5e432b9a5edfe..6e2d05d40b8f3 100644 --- a/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt +++ b/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt @@ -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) {