diff --git a/packages/CredentialManager/res/drawable/ic_face.xml b/packages/CredentialManager/res/drawable/ic_face.xml deleted file mode 100644 index 16fe14495e9d1..0000000000000 --- a/packages/CredentialManager/res/drawable/ic_face.xml +++ /dev/null @@ -1,30 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/packages/CredentialManager/res/drawable/ic_manage_accounts.xml b/packages/CredentialManager/res/drawable/ic_manage_accounts.xml deleted file mode 100644 index adad2f105d55b..0000000000000 --- a/packages/CredentialManager/res/drawable/ic_manage_accounts.xml +++ /dev/null @@ -1,30 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt b/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt index 94aa8e81ff609..4faf00c43d34d 100644 --- a/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt +++ b/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt @@ -63,7 +63,7 @@ class CredentialManagerRepo( requestInfo = intent.extras?.getParcelable( RequestInfo.EXTRA_REQUEST_INFO, RequestInfo::class.java - ) ?: testCreatePasskeyRequestInfo() + ) ?: testGetRequestInfo() providerEnabledList = when (requestInfo.type) { RequestInfo.TYPE_CREATE -> @@ -245,12 +245,10 @@ class CredentialManagerRepo( listOf( newActionEntry( "key3", "subkey-1", TYPE_PASSWORD_CREDENTIAL, - Icon.createWithResource(context, R.drawable.ic_manage_accounts), "Open Google Password Manager", "elisa.beckett@gmail.com" ), newActionEntry( "key3", "subkey-2", TYPE_PASSWORD_CREDENTIAL, - Icon.createWithResource(context, R.drawable.ic_manage_accounts), "Open Google Password Manager", "beckett-family@gmail.com" ), ) @@ -275,7 +273,6 @@ class CredentialManagerRepo( listOf( newActionEntry( "key3", "subkey-1", TYPE_PASSWORD_CREDENTIAL, - Icon.createWithResource(context, R.drawable.ic_face), "Open Enpass" ), ) @@ -287,7 +284,6 @@ class CredentialManagerRepo( key: String, subkey: String, credentialType: String, - icon: Icon, text: String, subtext: String? = null, ): Entry { @@ -295,7 +291,7 @@ class CredentialManagerRepo( Entry.CREDENTIAL_MANAGER_ENTRY_URI, SliceSpec(credentialType, 1) ).addText( text, null, listOf(Entry.HINT_ACTION_TITLE) - ).addIcon(icon, null, listOf(Entry.HINT_ACTION_ICON)) + ) if (subtext != null) { slice.addText(subtext, null, listOf(Entry.HINT_ACTION_SUBTEXT)) } diff --git a/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt b/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt index f514b02f8abe2..56fbf6632dc50 100644 --- a/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt +++ b/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt @@ -67,7 +67,8 @@ class GetFlowUtils { .getPackageInfo(packageName!!, PackageManager.PackageInfoFlags.of(0)) val providerDisplayName = pkgInfo.applicationInfo.loadLabel(packageManager).toString() - // TODO: decide what to do when failed to load a provider icon + // TODO: get the provider icon from the service + // and decide what to do when failed to load a provider icon val providerIcon = pkgInfo.applicationInfo.loadIcon(packageManager)!! ProviderInfo( id = it.providerFlattenedComponentName, @@ -83,7 +84,7 @@ class GetFlowUtils { it.authenticationEntry), remoteEntry = getRemoteEntry(it.providerFlattenedComponentName, it.remoteEntry), actionEntryList = getActionEntryList( - it.providerFlattenedComponentName, it.actionChips, context), + it.providerFlattenedComponentName, it.actionChips, providerIcon), ) } } @@ -119,7 +120,7 @@ class GetFlowUtils { displayName = credentialEntryUi.userDisplayName?.toString(), // TODO: proper fallback icon = credentialEntryUi.entryIcon?.loadDrawable(context) - ?: context.getDrawable(R.drawable.ic_passkey)!!, + ?: context.getDrawable(R.drawable.ic_other_sign_in)!!, lastUsedTimeMillis = credentialEntryUi.lastUsedTimeMillis, ) } @@ -164,7 +165,7 @@ class GetFlowUtils { private fun getActionEntryList( providerId: String, actionEntries: List, - context: Context, + providerIcon: Drawable, ): List { return actionEntries.map { val actionEntryUi = ActionUi.fromSlice(it.slice) @@ -177,7 +178,7 @@ class GetFlowUtils { fillInIntent = it.frameworkExtrasIntent, title = actionEntryUi.text.toString(), // TODO: gracefully fail - icon = actionEntryUi.icon.loadDrawable(context)!!, + icon = providerIcon, subTitle = actionEntryUi.subtext?.toString(), ) } diff --git a/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialComponents.kt b/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialComponents.kt index 7a015a841fae7..619f5a37d45b8 100644 --- a/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialComponents.kt +++ b/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialComponents.kt @@ -16,6 +16,7 @@ package com.android.credentialmanager.getflow +import android.credentials.Credential import android.text.TextUtils import androidx.activity.compose.ManagedActivityResultLauncher import androidx.activity.result.ActivityResult @@ -35,6 +36,7 @@ import androidx.compose.foundation.lazy.items import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.ArrowBack import androidx.compose.material.icons.filled.Close +import androidx.compose.material.icons.outlined.Lock import androidx.compose.material3.Divider import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Icon @@ -189,7 +191,7 @@ fun PrimarySelectionCard( color = Color.Transparent ) Row( - horizontalArrangement = Arrangement.Start, + horizontalArrangement = Arrangement.SpaceBetween, modifier = Modifier.fillMaxWidth().padding(horizontal = 24.dp) ) { CancelButton(stringResource(R.string.get_dialog_button_label_no_thanks), onCancel) @@ -423,11 +425,12 @@ fun CredentialEntryRow( Entry( onClick = { onEntrySelected(credentialEntryInfo) }, icon = { - Image( + Icon( modifier = Modifier.padding(start = 10.dp).size(32.dp), bitmap = credentialEntryInfo.icon.toBitmap().asImageBitmap(), // TODO: add description. - contentDescription = "" + contentDescription = "", + tint = LocalAndroidColorScheme.current.colorAccentPrimaryVariant, ) }, label = { @@ -436,19 +439,24 @@ fun CredentialEntryRow( TextOnSurfaceVariant( text = credentialEntryInfo.userName, style = MaterialTheme.typography.titleLarge, - modifier = Modifier.padding(top = 16.dp) + modifier = Modifier.padding(top = 16.dp, start = 5.dp) ) TextSecondary( - text = - if (TextUtils.isEmpty(credentialEntryInfo.displayName)) - credentialEntryInfo.credentialTypeDisplayName - else - credentialEntryInfo.credentialTypeDisplayName + - stringResource( - R.string.get_dialog_sign_in_type_username_separator) + - credentialEntryInfo.displayName, + text = if ( + credentialEntryInfo.credentialType == Credential.TYPE_PASSWORD_CREDENTIAL) { + "••••••••••••" + } else { + if (TextUtils.isEmpty(credentialEntryInfo.displayName)) + credentialEntryInfo.credentialTypeDisplayName + else + credentialEntryInfo.credentialTypeDisplayName + + stringResource( + R.string.get_dialog_sign_in_type_username_separator + ) + + credentialEntryInfo.displayName + }, style = MaterialTheme.typography.bodyMedium, - modifier = Modifier.padding(bottom = 16.dp) + modifier = Modifier.padding(bottom = 16.dp, start = 5.dp) ) } } @@ -472,17 +480,27 @@ fun AuthenticationEntryRow( ) }, label = { - Column() { - // TODO: fix the text values. - TextOnSurfaceVariant( - text = authenticationEntryInfo.title, - style = MaterialTheme.typography.titleLarge, - modifier = Modifier.padding(top = 16.dp) - ) - TextSecondary( - text = stringResource(R.string.locked_credential_entry_label_subtext), - style = MaterialTheme.typography.bodyMedium, - modifier = Modifier.padding(bottom = 16.dp) + Row( + horizontalArrangement = Arrangement.SpaceBetween, + modifier = Modifier.fillMaxWidth().padding(horizontal = 5.dp), + ) { + Column() { + // TODO: fix the text values. + TextOnSurfaceVariant( + text = authenticationEntryInfo.title, + style = MaterialTheme.typography.titleLarge, + modifier = Modifier.padding(top = 16.dp) + ) + TextSecondary( + text = stringResource(R.string.locked_credential_entry_label_subtext), + style = MaterialTheme.typography.bodyMedium, + modifier = Modifier.padding(bottom = 16.dp) + ) + } + Icon( + Icons.Outlined.Lock, + null, + Modifier.align(alignment = Alignment.CenterVertically).padding(end = 10.dp), ) } } @@ -509,11 +527,13 @@ fun ActionEntryRow( TextOnSurfaceVariant( text = actionEntryInfo.title, style = MaterialTheme.typography.titleLarge, + modifier = Modifier.padding(start = 5.dp), ) if (actionEntryInfo.subTitle != null) { TextSecondary( text = actionEntryInfo.subTitle, style = MaterialTheme.typography.bodyMedium, + modifier = Modifier.padding(start = 5.dp), ) } } diff --git a/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialViewModel.kt b/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialViewModel.kt index fcf49db6935ad..c182397b8aeca 100644 --- a/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialViewModel.kt +++ b/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialViewModel.kt @@ -221,7 +221,16 @@ private fun toGetScreenState( internal class CredentialEntryInfoComparator : Comparator { override fun compare(p0: CredentialEntryInfo, p1: CredentialEntryInfo): Int { - // First order by last used timestamp + // First prefer passkey type for its security benefits + if (p0.credentialType != p1.credentialType) { + if (PublicKeyCredential.TYPE_PUBLIC_KEY_CREDENTIAL == p0.credentialType) { + return -1 + } else if (PublicKeyCredential.TYPE_PUBLIC_KEY_CREDENTIAL == p1.credentialType) { + return 1 + } + } + + // Then order by last used timestamp if (p0.lastUsedTimeMillis != null && p1.lastUsedTimeMillis != null) { if (p0.lastUsedTimeMillis < p1.lastUsedTimeMillis) { return 1 @@ -233,15 +242,6 @@ internal class CredentialEntryInfoComparator : Comparator { } else if (p1.lastUsedTimeMillis != null && p1.lastUsedTimeMillis > 0) { return 1 } - - // Then prefer passkey type for its security benefits - if (p0.credentialType != p1.credentialType) { - if (PublicKeyCredential.TYPE_PUBLIC_KEY_CREDENTIAL == p0.credentialType) { - return -1 - } else if (PublicKeyCredential.TYPE_PUBLIC_KEY_CREDENTIAL == p1.credentialType) { - return 1 - } - } return 0 } } \ No newline at end of file diff --git a/packages/CredentialManager/src/com/android/credentialmanager/jetpack/provider/ActionUi.kt b/packages/CredentialManager/src/com/android/credentialmanager/jetpack/provider/ActionUi.kt index 1e639fe6bd552..19c5c2dfa4fee 100644 --- a/packages/CredentialManager/src/com/android/credentialmanager/jetpack/provider/ActionUi.kt +++ b/packages/CredentialManager/src/com/android/credentialmanager/jetpack/provider/ActionUi.kt @@ -18,7 +18,6 @@ package com.android.credentialmanager.jetpack.provider import android.app.slice.Slice import android.credentials.ui.Entry -import android.graphics.drawable.Icon /** * UI representation for a credential entry used during the get credential flow. @@ -26,28 +25,24 @@ import android.graphics.drawable.Icon * TODO: move to jetpack. */ class ActionUi( - val icon: Icon, val text: CharSequence, val subtext: CharSequence?, ) { companion object { fun fromSlice(slice: Slice): ActionUi { - var icon: Icon? = null var text: CharSequence? = null var subtext: CharSequence? = null val items = slice.items items.forEach { - if (it.hasHint(Entry.HINT_ACTION_ICON)) { - icon = it.icon - } else if (it.hasHint(Entry.HINT_ACTION_TITLE)) { + if (it.hasHint(Entry.HINT_ACTION_TITLE)) { text = it.text } else if (it.hasHint(Entry.HINT_ACTION_SUBTEXT)) { subtext = it.text } } // TODO: fail NPE more elegantly. - return ActionUi(icon!!, text!!, subtext) + return ActionUi(text!!, subtext) } } }