From 501b0735f683f58f296f6e3ad9ce5b54a2fefe31 Mon Sep 17 00:00:00 2001 From: Qinmei Du Date: Tue, 17 Jan 2023 21:30:06 +0000 Subject: [PATCH] Add the learn more screen for the passkey intro page screencast: https://screencast.googleplex.com/cast/NjY5NDQ1MjY2MDc5NzQ0MHw5OTJhNzVkYS1mNA Test: deployed locally Bug: 265038081 Fix: 265038081 Change-Id: I4adcbeae4786f8c2dcf6238c0c1ed41c3a56d773 --- .../CredentialManager/res/values/strings.xml | 25 +++- .../createflow/CreateCredentialComponents.kt | 109 +++++++++++++++--- .../createflow/CreateCredentialViewModel.kt | 12 ++ .../createflow/CreateModel.kt | 1 + 4 files changed, 132 insertions(+), 15 deletions(-) diff --git a/packages/CredentialManager/res/values/strings.xml b/packages/CredentialManager/res/values/strings.xml index 81505e1393cc2..d690971997128 100644 --- a/packages/CredentialManager/res/values/strings.xml +++ b/packages/CredentialManager/res/values/strings.xml @@ -11,13 +11,34 @@ Continue More options + + Learn more Safer with passkeys - + With passkeys, you don’t need to create or remember complex passwords + Passkeys are encrypted digital keys you create using your fingerprint, face, or screen lock + They are saved to a password manager, so you can sign in on other devices - + + More about passkeys + + Passwordless technology + + Passkeys allow you to sign in without relying on passwords. You just need to use your fingerprint, face recognition, PIN, or swipe pattern to verify your identity and create a passkey. + + Public key cryptography + + Based on FIDO Alliance (which includes Google, Apple, Microsoft, and more) and W3C standards, passkeys use cryptographic key pairs. Unlike the username and string of characters we use for passwords, a private-public key pair is created for an app or website. The private key is safely stored on your device or password manager and it confirms your identity. The public key is shared with the app or website server. With corresponding keys, you can instantly register and sign in. + + Improved account security + + Each key is exclusively linked with the app or website they were created for, so you can never sign in to a fraudulent app or website by mistake. Plus, with servers only keeping public keys, hacking is a lot harder. + + Seamless transition + + As we move towards a passwordless future, passwords will still be available alongside passkeys. Choose where to save your %1$s diff --git a/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt b/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt index 498f0a193af15..03d7de5457a79 100644 --- a/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt +++ b/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt @@ -70,7 +70,7 @@ fun CreateCredentialScreen( when (uiState.currentScreenState) { CreateScreenState.PASSKEY_INTRO -> ConfirmationCard( onConfirm = viewModel::onConfirmIntro, - onCancel = viewModel::onCancel, + onLearnMore = viewModel::onLearnMore, ) CreateScreenState.PROVIDER_SELECTION -> ProviderSelectionCard( requestDisplayInfo = uiState.requestDisplayInfo, @@ -115,7 +115,10 @@ fun CreateCredentialScreen( activeRemoteEntry = uiState.activeEntry?.activeEntryInfo!!, onOptionSelected = viewModel::onEntrySelected, onConfirm = viewModel::onConfirmEntrySelected, - onCancel = viewModel::onCancel, + ) + CreateScreenState.MORE_ABOUT_PASSKEYS_INTRO -> MoreAboutPasskeysIntroCard( + onBackPasskeyIntroButtonSelected = + viewModel::onBackPasskeyIntroButtonSelected, ) } } else if (uiState.selectedEntry != null && !uiState.providerActivityPending) { @@ -136,7 +139,7 @@ fun CreateCredentialScreen( @Composable fun ConfirmationCard( onConfirm: () -> Unit, - onCancel: () -> Unit, + onLearnMore: () -> Unit, ) { ContainerCard() { Column() { @@ -223,8 +226,8 @@ fun ConfirmationCard( modifier = Modifier.fillMaxWidth().padding(horizontal = 24.dp) ) { ActionButton( - stringResource(R.string.string_cancel), - onClick = onCancel + stringResource(R.string.string_learn_more), + onClick = onLearnMore ) ConfirmButton( stringResource(R.string.string_continue), @@ -396,8 +399,7 @@ fun MoreOptionsSelectionCard( ) } }, - colors = TopAppBarDefaults.smallTopAppBarColors - (containerColor = Color.Transparent), + colors = TopAppBarDefaults.topAppBarColors(containerColor = Color.Transparent), modifier = Modifier.padding(top = 12.dp) ) Divider( @@ -634,7 +636,6 @@ fun ExternalOnlySelectionCard( activeRemoteEntry: EntryInfo, onOptionSelected: (EntryInfo) -> Unit, onConfirm: () -> Unit, - onCancel: () -> Unit, ) { ContainerCard() { Column() { @@ -673,13 +674,9 @@ fun ExternalOnlySelectionCard( color = Color.Transparent ) Row( - horizontalArrangement = Arrangement.SpaceBetween, + horizontalArrangement = Arrangement.End, modifier = Modifier.fillMaxWidth().padding(horizontal = 24.dp) ) { - ActionButton( - stringResource(R.string.string_cancel), - onClick = onCancel - ) ConfirmButton( stringResource(R.string.string_continue), onClick = onConfirm @@ -694,6 +691,92 @@ fun ExternalOnlySelectionCard( } } +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun MoreAboutPasskeysIntroCard( + onBackPasskeyIntroButtonSelected: () -> Unit, +) { + ContainerCard() { + Column() { + TopAppBar( + title = { + TextOnSurface( + text = + stringResource( + R.string.more_about_passkeys_title), + style = MaterialTheme.typography.titleMedium, + ) + }, + navigationIcon = { + IconButton( + onClick = onBackPasskeyIntroButtonSelected + ) { + Icon( + Icons.Filled.ArrowBack, + stringResource(R.string.accessibility_back_arrow_button) + ) + } + }, + colors = TopAppBarDefaults.topAppBarColors(containerColor = Color.Transparent), + modifier = Modifier.padding(top = 12.dp) + ) + Column( + modifier = Modifier.fillMaxWidth().padding(start = 24.dp, end = 68.dp) + ) { + TextOnSurfaceVariant( + text = stringResource(R.string.passwordless_technology_title), + style = MaterialTheme.typography.titleLarge, + ) + TextSecondary( + text = stringResource(R.string.passwordless_technology_detail), + style = MaterialTheme.typography.bodyMedium, + ) + Divider( + thickness = 24.dp, + color = Color.Transparent + ) + TextOnSurfaceVariant( + text = stringResource(R.string.public_key_cryptography_title), + style = MaterialTheme.typography.titleLarge, + ) + TextSecondary( + text = stringResource(R.string.public_key_cryptography_detail), + style = MaterialTheme.typography.bodyMedium, + ) + Divider( + thickness = 24.dp, + color = Color.Transparent + ) + TextOnSurfaceVariant( + text = stringResource(R.string.improved_account_security_title), + style = MaterialTheme.typography.titleLarge, + ) + TextSecondary( + text = stringResource(R.string.improved_account_security_detail), + style = MaterialTheme.typography.bodyMedium, + ) + Divider( + thickness = 24.dp, + color = Color.Transparent + ) + TextOnSurfaceVariant( + text = stringResource(R.string.seamless_transition_title), + style = MaterialTheme.typography.titleLarge, + ) + TextSecondary( + text = stringResource(R.string.seamless_transition_detail), + style = MaterialTheme.typography.bodyMedium, + ) + } + Divider( + thickness = 18.dp, + color = Color.Transparent, + modifier = Modifier.padding(bottom = 24.dp) + ) + } + } +} + @OptIn(ExperimentalMaterial3Api::class) @Composable fun PrimaryCreateOptionRow( diff --git a/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialViewModel.kt b/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialViewModel.kt index ac84503583a8a..7a04a8e0e045a 100644 --- a/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialViewModel.kt +++ b/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialViewModel.kt @@ -122,6 +122,12 @@ class CreateCredentialViewModel( ) } + fun onBackPasskeyIntroButtonSelected() { + uiState = uiState.copy( + currentScreenState = CreateScreenState.PASSKEY_INTRO, + ) + } + fun onEntrySelectedFromMoreOptionScreen(activeEntry: ActiveEntry) { uiState = uiState.copy( currentScreenState = if ( @@ -150,6 +156,12 @@ class CreateCredentialViewModel( dialogResult.tryEmit(DialogResult(ResultState.NORMAL_CANCELED)) } + fun onLearnMore() { + uiState = uiState.copy( + currentScreenState = CreateScreenState.MORE_ABOUT_PASSKEYS_INTRO, + ) + } + fun onChangeDefaultSelected() { uiState = uiState.copy( currentScreenState = CreateScreenState.CREATION_OPTION_SELECTION, diff --git a/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateModel.kt b/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateModel.kt index 97477a75f8bfa..957488ffcbcc1 100644 --- a/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateModel.kt +++ b/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateModel.kt @@ -91,6 +91,7 @@ data class ActiveEntry ( /** The name of the current screen. */ enum class CreateScreenState { PASSKEY_INTRO, + MORE_ABOUT_PASSKEYS_INTRO, PROVIDER_SELECTION, CREATION_OPTION_SELECTION, MORE_OPTIONS_SELECTION,