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) {