diff options
| author | 2022-11-18 20:42:26 +0000 | |
|---|---|---|
| committer | 2022-11-22 17:27:32 +0000 | |
| commit | 20c29c93df2c08577af0c9986f4b9404e519f452 (patch) | |
| tree | 3926a02cb3fba68639de753bbbd321ecd4548ea2 | |
| parent | b5c35c6564033798741d3fa9b3b4260d3e7093cb (diff) | |
Add the only one createOption but has remoteEntry screen for create passkey
screenshot: https://screenshot.googleplex.com/TULWynJWxx4cAeP
Test: deployed locally
Bug: 253157211
Change-Id: I1865b89f71129ec94cac3ef0ea09a6b631eac65b
6 files changed, 118 insertions, 81 deletions
diff --git a/packages/CredentialManager/res/values/strings.xml b/packages/CredentialManager/res/values/strings.xml index 114de89ce82a..887a369093dd 100644 --- a/packages/CredentialManager/res/values/strings.xml +++ b/packages/CredentialManager/res/values/strings.xml @@ -6,6 +6,7 @@ <string name="string_more_options">More options</string> <string name="string_create_in_another_place">Create in another place</string> <string name="string_save_to_another_place">Save to another place</string> + <string name="string_use_another_device">Use another device</string> <string name="string_no_thanks">No thanks</string> <string name="passkey_creation_intro_title">A simple way to sign in safely</string> <string name="passkey_creation_intro_body">Use your fingerprint, face or screen lock to sign in with a unique passkey that can’t be forgotten or stolen. Learn more</string> diff --git a/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt b/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt index a6e64cefedcb..232d681fd209 100644 --- a/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt +++ b/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt @@ -149,8 +149,8 @@ class CredentialManagerRepo( if (providerInfo.isDefault) {hasDefault = true; defaultProvider = providerInfo} } // TODO: covert from real requestInfo for create passkey var requestDisplayInfo = RequestDisplayInfo( - "Elisa Beckett", "beckett-bakert@gmail.com", + "Elisa Beckett", TYPE_PUBLIC_KEY_CREDENTIAL, "tribank") val createCredentialRequest = requestInfo.createCredentialRequest @@ -165,8 +165,12 @@ class CredentialManagerRepo( return CreateCredentialUiState( enabledProviders = providerEnabledList, disabledProviders = providerDisabledList, - if (hasDefault) - {CreateScreenState.CREATION_OPTION_SELECTION} else {CreateScreenState.PASSKEY_INTRO}, + // TODO: Add the screen when defaultProvider has no createOption but + // there's remoteInfo under other providers + if (!hasDefault || defaultProvider.createOptions.isEmpty()) { + if (requestDisplayInfo.type == TYPE_PUBLIC_KEY_CREDENTIAL) + {CreateScreenState.PASSKEY_INTRO} else {CreateScreenState.PROVIDER_SELECTION} + } else {CreateScreenState.CREATION_OPTION_SELECTION}, requestDisplayInfo, if (hasDefault) { ActiveEntry(defaultProvider, defaultProvider.createOptions.first()) @@ -439,7 +443,7 @@ class CredentialManagerRepo( return RequestInfo.newCreateRequestInfo( Binder(), CreateCredentialRequest( - TYPE_PASSWORD_CREDENTIAL, + TYPE_PUBLIC_KEY_CREDENTIAL, data ), /*isFirstUsage=*/false, diff --git a/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt b/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt index 830bc7a11bda..1c74743ab481 100644 --- a/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt +++ b/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt @@ -193,9 +193,10 @@ class CreateFlowUtils { icon = pkgInfo.applicationInfo.loadIcon(packageManager)!!, name = it.providerFlattenedComponentName, displayName = pkgInfo.applicationInfo.loadLabel(packageManager).toString(), - createOptions = toCreationOptionInfoList(it.saveEntries, context), + createOptions = toCreationOptionInfoList( + it.providerFlattenedComponentName, it.saveEntries, context), isDefault = it.isDefaultProvider, - remoteEntry = toRemoteInfo(it.remoteEntry), + remoteEntry = toRemoteInfo(it.providerFlattenedComponentName, it.remoteEntry), ) } } @@ -219,6 +220,7 @@ class CreateFlowUtils { } private fun toCreationOptionInfoList( + providerId: String, creationEntries: List<Entry>, context: Context, ): List<CreateOptionInfo> { @@ -227,6 +229,7 @@ class CreateFlowUtils { return@map CreateOptionInfo( // TODO: remove fallbacks + providerId = providerId, entryKey = it.key, entrySubkey = it.subkey, pendingIntent = it.pendingIntent, @@ -245,11 +248,13 @@ class CreateFlowUtils { } private fun toRemoteInfo( + providerId: String, remoteEntry: Entry?, ): RemoteInfo? { // TODO: should also call fromSlice after getting the official jetpack code. return if (remoteEntry != null) { RemoteInfo( + providerId = providerId, entryKey = remoteEntry.key, entrySubkey = remoteEntry.subkey, pendingIntent = remoteEntry.pendingIntent, diff --git a/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt b/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt index 3a277a638e93..7574feed2132 100644 --- a/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt +++ b/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt @@ -55,8 +55,11 @@ fun CreateCredentialScreen( viewModel: CreateCredentialViewModel, providerActivityLauncher: ManagedActivityResultLauncher<IntentSenderRequest, ActivityResult> ) { - val primaryEntryCallback: () -> Unit = { - viewModel.onPrimaryCreateOptionInfoSelected(providerActivityLauncher) + val selectEntryCallback: (EntryInfo) -> Unit = { + viewModel.onEntrySelected(it, providerActivityLauncher) + } + val confirmEntryCallback: () -> Unit = { + viewModel.onConfirmCreationSelected(providerActivityLauncher) } val state = rememberModalBottomSheetState( initialValue = ModalBottomSheetValue.Expanded, @@ -78,23 +81,23 @@ fun CreateCredentialScreen( ) CreateScreenState.CREATION_OPTION_SELECTION -> CreationSelectionCard( requestDisplayInfo = uiState.requestDisplayInfo, + enabledProviderList = uiState.enabledProviders, providerInfo = uiState.activeEntry?.activeProvider!!, createOptionInfo = uiState.activeEntry.activeEntryInfo as CreateOptionInfo, - onOptionSelected = primaryEntryCallback, - onConfirm = primaryEntryCallback, + onOptionSelected = selectEntryCallback, + onConfirm = confirmEntryCallback, onCancel = viewModel::onCancel, - multiProvider = uiState.enabledProviders.size > 1, - onMoreOptionsSelected = viewModel::onMoreOptionsSelected + onMoreOptionsSelected = viewModel::onMoreOptionsSelected, ) CreateScreenState.MORE_OPTIONS_SELECTION -> MoreOptionsSelectionCard( - requestDisplayInfo = uiState.requestDisplayInfo, - enabledProviderList = uiState.enabledProviders, - disabledProviderList = uiState.disabledProviders, - onBackButtonSelected = viewModel::onBackButtonSelected, - onOptionSelected = viewModel::onMoreOptionsRowSelected, - onDisabledPasswordManagerSelected = viewModel::onDisabledPasswordManagerSelected, - onRemoteEntrySelected = viewModel::onRemoteEntrySelected - ) + requestDisplayInfo = uiState.requestDisplayInfo, + enabledProviderList = uiState.enabledProviders, + disabledProviderList = uiState.disabledProviders, + onBackButtonSelected = viewModel::onBackButtonSelected, + onOptionSelected = viewModel::onMoreOptionsRowSelected, + onDisabledPasswordManagerSelected = viewModel::onDisabledPasswordManagerSelected, + onRemoteEntrySelected = selectEntryCallback, + ) CreateScreenState.MORE_OPTIONS_ROW_INTRO -> MoreOptionsRowIntroCard( providerInfo = uiState.activeEntry?.activeProvider!!, onDefaultOrNotSelected = viewModel::onDefaultOrNotSelected @@ -234,7 +237,7 @@ fun MoreOptionsSelectionCard( onBackButtonSelected: () -> Unit, onOptionSelected: (ActiveEntry) -> Unit, onDisabledPasswordManagerSelected: () -> Unit, - onRemoteEntrySelected: () -> Unit, + onRemoteEntrySelected: (EntryInfo) -> Unit, ) { Card() { Column() { @@ -292,17 +295,15 @@ fun MoreOptionsSelectionCard( ) } } - var hasRemoteInfo = false + // TODO: handle the error situation that if multiple remoteInfos exists enabledProviderList.forEach { if (it.remoteEntry != null) { - hasRemoteInfo = true - } - } - if (hasRemoteInfo) { - item { - RemoteEntryRow( - onRemoteEntrySelected = onRemoteEntrySelected, - ) + item { + RemoteEntryRow( + remoteInfo = it.remoteEntry!!, + onRemoteEntrySelected = onRemoteEntrySelected, + ) + } } } } @@ -388,12 +389,12 @@ fun ProviderRow(providerInfo: ProviderInfo, onProviderSelected: (String) -> Unit @Composable fun CreationSelectionCard( requestDisplayInfo: RequestDisplayInfo, - providerInfo: ProviderInfo, + enabledProviderList: List<EnabledProviderInfo>, + providerInfo: EnabledProviderInfo, createOptionInfo: CreateOptionInfo, - onOptionSelected: () -> Unit, + onOptionSelected: (EntryInfo) -> Unit, onConfirm: () -> Unit, onCancel: () -> Unit, - multiProvider: Boolean, onMoreOptionsSelected: () -> Unit, ) { Card() { @@ -442,19 +443,16 @@ fun CreationSelectionCard( .padding(horizontal = 24.dp) .align(alignment = Alignment.CenterHorizontally), ) { - LazyColumn( - verticalArrangement = Arrangement.spacedBy(2.dp) - ) { - item { - PrimaryCreateOptionRow( - requestDisplayInfo = requestDisplayInfo, - createOptionInfo = createOptionInfo, - onOptionSelected = onOptionSelected - ) - } - } + PrimaryCreateOptionRow( + requestDisplayInfo = requestDisplayInfo, + createOptionInfo = createOptionInfo, + onOptionSelected = onOptionSelected + ) } - if (multiProvider) { + var createOptionsSize = 0 + enabledProviderList.forEach{ + enabledProvider -> createOptionsSize += enabledProvider.createOptions.size} + if (createOptionsSize > 1) { TextButton( onClick = onMoreOptionsSelected, modifier = Modifier @@ -469,6 +467,26 @@ fun CreationSelectionCard( textAlign = TextAlign.Center, ) } + } else if ( + requestDisplayInfo.type == TYPE_PUBLIC_KEY_CREDENTIAL + ) { + // TODO: handle the error situation that if multiple remoteInfos exists + enabledProviderList.forEach { enabledProvider -> + if (enabledProvider.remoteEntry != null) { + TextButton( + onClick = { + onOptionSelected(enabledProvider.remoteEntry!!) }, + modifier = Modifier + .padding(horizontal = 24.dp) + .align(alignment = Alignment.CenterHorizontally) + ) { + Text( + text = stringResource(R.string.string_use_another_device), + textAlign = TextAlign.Center, + ) + } + } + } } Divider( thickness = 24.dp, @@ -501,10 +519,10 @@ fun CreationSelectionCard( fun PrimaryCreateOptionRow( requestDisplayInfo: RequestDisplayInfo, createOptionInfo: CreateOptionInfo, - onOptionSelected: () -> Unit + onOptionSelected: (EntryInfo) -> Unit ) { Entry( - onClick = onOptionSelected, + onClick = {onOptionSelected(createOptionInfo)}, icon = { Image(modifier = Modifier.size(32.dp).padding(start = 10.dp), bitmap = createOptionInfo.credentialTypeIcon.toBitmap().asImageBitmap(), @@ -527,7 +545,7 @@ fun PrimaryCreateOptionRow( ) } else { Text( - text = requestDisplayInfo.subtitle, + text = requestDisplayInfo.title, style = MaterialTheme.typography.titleLarge, modifier = Modifier.padding(top = 16.dp, bottom = 16.dp) ) @@ -640,10 +658,11 @@ fun MoreOptionsDisabledProvidersRow( @OptIn(ExperimentalMaterial3Api::class) @Composable fun RemoteEntryRow( - onRemoteEntrySelected: () -> Unit, + remoteInfo: RemoteInfo, + onRemoteEntrySelected: (RemoteInfo) -> Unit, ) { Entry( - onClick = onRemoteEntrySelected, + onClick = {onRemoteEntrySelected(remoteInfo)}, icon = { Icon( painter = painterResource(R.drawable.ic_other_devices), diff --git a/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialViewModel.kt b/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialViewModel.kt index 093c88f1c020..a90f4da3cae6 100644 --- a/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialViewModel.kt +++ b/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialViewModel.kt @@ -37,6 +37,7 @@ data class CreateCredentialUiState( val currentScreenState: CreateScreenState, val requestDisplayInfo: RequestDisplayInfo, val activeEntry: ActiveEntry? = null, + val selectedEntry: EntryInfo? = null, ) class CreateCredentialViewModel( @@ -109,10 +110,6 @@ class CreateCredentialViewModel( // TODO: Complete this function } - fun onRemoteEntrySelected() { - // TODO: Complete this function - } - fun onCancel() { CredentialManagerRepo.getInstance().onCancel() dialogResult.value = DialogResult(ResultState.CANCELED) @@ -125,46 +122,54 @@ class CreateCredentialViewModel( // TODO: implement the if choose as default or not logic later } - fun onPrimaryCreateOptionInfoSelected( + fun onEntrySelected( + selectedEntry: EntryInfo, + launcher: ManagedActivityResultLauncher<IntentSenderRequest, ActivityResult> + ) { + val providerId = selectedEntry.providerId + val entryKey = selectedEntry.entryKey + val entrySubkey = selectedEntry.entrySubkey + Log.d( + "Account Selector", "Option selected for entry: " + + " {provider=$providerId, key=$entryKey, subkey=$entrySubkey") + if (selectedEntry.pendingIntent != null) { + uiState = uiState.copy(selectedEntry = selectedEntry) + val intentSenderRequest = IntentSenderRequest.Builder(selectedEntry.pendingIntent) + .setFillInIntent(selectedEntry.fillInIntent).build() + launcher.launch(intentSenderRequest) + } else { + CredentialManagerRepo.getInstance().onOptionSelected( + providerId, + entryKey, + entrySubkey + ) + dialogResult.value = DialogResult( + ResultState.COMPLETE, + ) + } + } + + fun onConfirmCreationSelected( launcher: ManagedActivityResultLauncher<IntentSenderRequest, ActivityResult> ) { val selectedEntry = uiState.activeEntry?.activeEntryInfo if (selectedEntry != null) { - val entryKey = selectedEntry.entryKey - val entrySubkey = selectedEntry.entrySubkey - Log.d( - "Account Selector", - "Option selected for creation: " + - "{key = $entryKey, subkey = $entrySubkey}" - ) - if (selectedEntry.pendingIntent != null) { - val intentSenderRequest = IntentSenderRequest.Builder(selectedEntry.pendingIntent) - .setFillInIntent(selectedEntry.fillInIntent).build() - launcher.launch(intentSenderRequest) - } else { - CredentialManagerRepo.getInstance().onOptionSelected( - uiState.activeEntry?.activeProvider!!.name, - entryKey, - entrySubkey - ) - dialogResult.value = DialogResult( - ResultState.COMPLETE, - ) - } + onEntrySelected(selectedEntry, launcher) } else { + Log.w("Account Selector", + "Illegal state: confirm is pressed but activeEntry isn't set.") dialogResult.value = DialogResult( ResultState.COMPLETE, ) - TODO("Gracefully handle illegal state.") } } fun onProviderActivityResult(providerActivityResult: ProviderActivityResult) { - val entry = uiState.activeEntry?.activeEntryInfo + val entry = uiState.selectedEntry val resultCode = providerActivityResult.resultCode val resultData = providerActivityResult.data - val providerId = uiState.activeEntry?.activeProvider!!.name if (entry != null) { + val providerId = entry.providerId Log.d("Account Selector", "Got provider activity result: {provider=" + "$providerId, key=${entry.entryKey}, subkey=${entry.entrySubkey}, " + "resultCode=$resultCode, resultData=$resultData}" diff --git a/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateModel.kt b/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateModel.kt index 753dc3c63bc7..e27db43768a0 100644 --- a/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateModel.kt +++ b/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateModel.kt @@ -42,6 +42,7 @@ class DisabledProviderInfo( ) : ProviderInfo(icon, name, displayName) open class EntryInfo ( + val providerId: String, val entryKey: String, val entrySubkey: String, val pendingIntent: PendingIntent?, @@ -49,6 +50,7 @@ open class EntryInfo ( ) class CreateOptionInfo( + providerId: String, entryKey: String, entrySubkey: String, pendingIntent: PendingIntent?, @@ -60,14 +62,15 @@ class CreateOptionInfo( val passkeyCount: Int?, val totalCredentialCount: Int?, val lastUsedTimeMillis: Long?, -) : EntryInfo(entryKey, entrySubkey, pendingIntent, fillInIntent) +) : EntryInfo(providerId, entryKey, entrySubkey, pendingIntent, fillInIntent) class RemoteInfo( + providerId: String, entryKey: String, entrySubkey: String, pendingIntent: PendingIntent?, fillInIntent: Intent?, -) : EntryInfo(entryKey, entrySubkey, pendingIntent, fillInIntent) +) : EntryInfo(providerId, entryKey, entrySubkey, pendingIntent, fillInIntent) data class RequestDisplayInfo( val title: String, |