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 c66ea5e1af9e0..6ea1d8d522f93 100644 --- a/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt +++ b/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt @@ -64,7 +64,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 f8d008e3e4afc..7648ea432b323 100644 --- a/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt +++ b/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt @@ -10,9 +10,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 @@ -20,18 +26,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,11 +50,13 @@ 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 @@ -851,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 + } + }) + } } CredentialType.UNKNOWN -> { if (requestDisplayInfo.subtitle != null) {