diff options
8 files changed, 100 insertions, 323 deletions
diff --git a/packages/CredentialManager/AndroidManifest.xml b/packages/CredentialManager/AndroidManifest.xml index 8724d69258ed..4161601e2317 100644 --- a/packages/CredentialManager/AndroidManifest.xml +++ b/packages/CredentialManager/AndroidManifest.xml @@ -42,15 +42,6 @@ android:excludeFromRecents="true" android:theme="@style/Theme.CredentialSelector"> </activity> - - <receiver - android:name=".CredentialProviderReceiver" - android:exported="true" - android:permission="android.permission.LAUNCH_CREDENTIAL_SELECTOR"> - <intent-filter> - <action android:name="android.credentials.ui.action.CREDMAN_ENABLED_PROVIDERS_UPDATED"/> - </intent-filter> - </receiver> </application> </manifest> diff --git a/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt b/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt index a9bee039264e..693e76731834 100644 --- a/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt +++ b/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt @@ -107,7 +107,6 @@ class CredentialManagerRepo( initialUiState = when (requestInfo?.type) { RequestInfo.TYPE_CREATE -> { - val defaultProviderIdSetByUser = userConfigRepo.getDefaultProviderId() val isPasskeyFirstUse = userConfigRepo.getIsPasskeyFirstUse() val providerEnableListUiState = getCreateProviderEnableListInitialUiState() val providerDisableListUiState = getCreateProviderDisableListInitialUiState() @@ -119,7 +118,8 @@ class CredentialManagerRepo( disabledProviders = providerDisableListUiState, defaultProviderIdPreferredByApp = requestDisplayInfoUiState.appPreferredDefaultProviderId, - defaultProviderIdSetByUser = defaultProviderIdSetByUser, + defaultProviderIdsSetByUser = + requestDisplayInfoUiState.userSetDefaultProviderIds, requestDisplayInfo = requestDisplayInfoUiState, isOnPasskeyIntroStateAlready = false, isPasskeyFirstUse = isPasskeyFirstUse, diff --git a/packages/CredentialManager/src/com/android/credentialmanager/CredentialProviderReceiver.kt b/packages/CredentialManager/src/com/android/credentialmanager/CredentialProviderReceiver.kt deleted file mode 100644 index ee8cffed7f7d..000000000000 --- a/packages/CredentialManager/src/com/android/credentialmanager/CredentialProviderReceiver.kt +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (C) 2023 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.credentialmanager - -import android.content.BroadcastReceiver -import android.content.Context -import android.content.Intent -import android.util.Log -import com.android.credentialmanager.common.Constants - - -class CredentialProviderReceiver : BroadcastReceiver() { - - override fun onReceive(context: Context?, intent: Intent?) { - Log.d(Constants.LOG_TAG, "Received intent in CredentialProviderReceiver") - - val sharedPreferences = context?.getSharedPreferences(context?.packageName, - Context.MODE_PRIVATE) - sharedPreferences?.edit()?.remove(UserConfigRepo.DEFAULT_PROVIDER)?.commit() - } -}
\ No newline at end of file diff --git a/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorViewModel.kt b/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorViewModel.kt index 8b74d766a152..de679895eedb 100644 --- a/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorViewModel.kt +++ b/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorViewModel.kt @@ -255,7 +255,8 @@ class CredentialSelectorViewModel( disabledProviders = prevUiState.disabledProviders, defaultProviderIdPreferredByApp = prevUiState.requestDisplayInfo.appPreferredDefaultProviderId, - defaultProviderIdSetByUser = userConfigRepo.getDefaultProviderId(), + defaultProviderIdsSetByUser = + prevUiState.requestDisplayInfo.userSetDefaultProviderIds, requestDisplayInfo = prevUiState.requestDisplayInfo, isOnPasskeyIntroStateAlready = true, isPasskeyFirstUse = userConfigRepo.getIsPasskeyFirstUse() @@ -269,28 +270,10 @@ class CredentialSelectorViewModel( userConfigRepo.setIsPasskeyFirstUse(false) } - fun createFlowOnMoreOptionsSelectedOnProviderSelection() { - uiState = uiState.copy( - createCredentialUiState = uiState.createCredentialUiState?.copy( - currentScreenState = CreateScreenState.MORE_OPTIONS_SELECTION, - isFromProviderSelection = true - ) - ) - } - fun createFlowOnMoreOptionsSelectedOnCreationSelection() { uiState = uiState.copy( createCredentialUiState = uiState.createCredentialUiState?.copy( currentScreenState = CreateScreenState.MORE_OPTIONS_SELECTION, - isFromProviderSelection = false - ) - ) - } - - fun createFlowOnBackProviderSelectionButtonSelected() { - uiState = uiState.copy( - createCredentialUiState = uiState.createCredentialUiState?.copy( - currentScreenState = CreateScreenState.PROVIDER_SELECTION, ) ) } @@ -315,7 +298,10 @@ class CredentialSelectorViewModel( uiState = uiState.copy( createCredentialUiState = uiState.createCredentialUiState?.copy( currentScreenState = - if (activeEntry.activeProvider.id == userConfigRepo.getDefaultProviderId() || + if (uiState.createCredentialUiState?.requestDisplayInfo?.userSetDefaultProviderIds + ?.contains(activeEntry.activeProvider.id) ?: true || + !(uiState.createCredentialUiState?.foundCandidateFromUserDefaultProvider + ?: false) || !TextUtils.isEmpty(uiState.createCredentialUiState?.requestDisplayInfo ?.appPreferredDefaultProviderId)) CreateScreenState.CREATION_OPTION_SELECTION @@ -325,18 +311,7 @@ class CredentialSelectorViewModel( ) } - fun createFlowOnEntrySelectedFromFirstUseScreen(activeEntry: ActiveEntry) { - val providerId = activeEntry.activeProvider.id - createFlowOnDefaultChanged(providerId) - uiState = uiState.copy( - createCredentialUiState = uiState.createCredentialUiState?.copy( - currentScreenState = CreateScreenState.CREATION_OPTION_SELECTION, - activeEntry = activeEntry - ) - ) - } - - fun createFlowOnDisabledProvidersSelected() { + fun createFlowOnLaunchSettings() { credManRepo.onSettingLaunchCancel() uiState = uiState.copy(dialogState = DialogState.CANCELED_FOR_SETTINGS) } @@ -349,16 +324,6 @@ class CredentialSelectorViewModel( ) } - fun createFlowOnChangeDefaultSelected() { - uiState = uiState.copy( - createCredentialUiState = uiState.createCredentialUiState?.copy( - currentScreenState = CreateScreenState.CREATION_OPTION_SELECTION, - ) - ) - val providerId = uiState.createCredentialUiState?.activeEntry?.activeProvider?.id - createFlowOnDefaultChanged(providerId) - } - fun createFlowOnUseOnceSelected() { uiState = uiState.copy( createCredentialUiState = uiState.createCredentialUiState?.copy( @@ -367,17 +332,6 @@ class CredentialSelectorViewModel( ) } - fun createFlowOnDefaultChanged(providerId: String?) { - if (providerId != null) { - Log.d( - Constants.LOG_TAG, "Default provider changed to: " + - " {provider=$providerId") - userConfigRepo.setDefaultProvider(providerId) - } else { - Log.w(Constants.LOG_TAG, "Null provider is being changed") - } - } - fun createFlowOnEntrySelected(selectedEntry: BaseEntry) { val providerId = selectedEntry.providerId val entryKey = selectedEntry.entryKey diff --git a/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt b/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt index 57035d426654..00c2f1a63ed4 100644 --- a/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt +++ b/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt @@ -61,6 +61,7 @@ import androidx.credentials.provider.PasswordCredentialEntry import androidx.credentials.provider.PublicKeyCredentialEntry import androidx.credentials.provider.RemoteEntry import org.json.JSONObject +import java.time.Instant // TODO: remove all !! checks fun getAppLabel( @@ -420,7 +421,7 @@ class CreateFlowUtils { id = it.providerFlattenedComponentName, displayName = providerLabel, icon = providerIcon, - createOptions = toCreationOptionInfoList( + sortedCreateOptions = toSortedCreationOptionInfoList( it.providerFlattenedComponentName, it.saveEntries, context ), remoteEntry = toRemoteInfo(it.providerFlattenedComponentName, it.remoteEntry), @@ -483,6 +484,7 @@ class CreateFlowUtils { context.getDrawable(R.drawable.ic_password_24) ?: return null, preferImmediatelyAvailableCredentials = false, appPreferredDefaultProviderId = appPreferredDefaultProviderId, + userSetDefaultProviderIds = requestInfo.defaultProviderIds.toSet(), ) is CreatePublicKeyCredentialRequest -> { newRequestDisplayInfoFromPasskeyJson( @@ -492,6 +494,7 @@ class CreateFlowUtils { preferImmediatelyAvailableCredentials = createCredentialRequestJetpack.preferImmediatelyAvailableCredentials, appPreferredDefaultProviderId = appPreferredDefaultProviderId, + userSetDefaultProviderIds = requestInfo.defaultProviderIds.toSet(), ) } is CreateCustomCredentialRequest -> { @@ -508,6 +511,7 @@ class CreateFlowUtils { ?: context.getDrawable(R.drawable.ic_other_sign_in_24) ?: return null, preferImmediatelyAvailableCredentials = false, appPreferredDefaultProviderId = appPreferredDefaultProviderId, + userSetDefaultProviderIds = requestInfo.defaultProviderIds.toSet(), ) } else -> null @@ -518,13 +522,13 @@ class CreateFlowUtils { enabledProviders: List<EnabledProviderInfo>, disabledProviders: List<DisabledProviderInfo>?, defaultProviderIdPreferredByApp: String?, - defaultProviderIdSetByUser: String?, + defaultProviderIdsSetByUser: Set<String>, requestDisplayInfo: RequestDisplayInfo, isOnPasskeyIntroStateAlready: Boolean, isPasskeyFirstUse: Boolean, ): CreateCredentialUiState? { - var lastSeenProviderWithNonEmptyCreateOptions: EnabledProviderInfo? = null var remoteEntry: RemoteInfo? = null + var remoteEntryProvider: EnabledProviderInfo? = null var defaultProviderPreferredByApp: EnabledProviderInfo? = null var defaultProviderSetByUser: EnabledProviderInfo? = null var createOptionsPairs: @@ -535,14 +539,24 @@ class CreateFlowUtils { defaultProviderPreferredByApp = enabledProvider } } - if (defaultProviderIdSetByUser != null) { - if (enabledProvider.id == defaultProviderIdSetByUser) { + if (enabledProvider.sortedCreateOptions.isNotEmpty() && + defaultProviderIdsSetByUser.contains(enabledProvider.id)) { + if (defaultProviderSetByUser == null) { defaultProviderSetByUser = enabledProvider + } else { + val newLastUsedTime = enabledProvider.sortedCreateOptions.firstOrNull() + ?.lastUsedTime + val curLastUsedTime = defaultProviderSetByUser?.sortedCreateOptions + ?.firstOrNull()?.lastUsedTime ?: Instant.MIN + if (newLastUsedTime != null) { + if (curLastUsedTime == null || newLastUsedTime > curLastUsedTime) { + defaultProviderSetByUser = enabledProvider + } + } } } - if (enabledProvider.createOptions.isNotEmpty()) { - lastSeenProviderWithNonEmptyCreateOptions = enabledProvider - enabledProvider.createOptions.forEach { + if (enabledProvider.sortedCreateOptions.isNotEmpty()) { + enabledProvider.sortedCreateOptions.forEach { createOptionsPairs.add(Pair(it, enabledProvider)) } } @@ -554,6 +568,7 @@ class CreateFlowUtils { return null } remoteEntry = currRemoteEntry + remoteEntryProvider = enabledProvider } } val defaultProvider = defaultProviderPreferredByApp ?: defaultProviderSetByUser @@ -561,27 +576,26 @@ class CreateFlowUtils { createOptionSize = createOptionsPairs.size, isOnPasskeyIntroStateAlready = isOnPasskeyIntroStateAlready, requestDisplayInfo = requestDisplayInfo, - defaultProvider = defaultProvider, remoteEntry = remoteEntry, isPasskeyFirstUse = isPasskeyFirstUse ) ?: return null + val sortedCreateOptionsPairs = createOptionsPairs.sortedWith( + compareByDescending { it.first.lastUsedTime } + ) return CreateCredentialUiState( enabledProviders = enabledProviders, disabledProviders = disabledProviders, currentScreenState = initialScreenState, requestDisplayInfo = requestDisplayInfo, - sortedCreateOptionsPairs = createOptionsPairs.sortedWith( - compareByDescending { it.first.lastUsedTime } - ), - hasDefaultProvider = defaultProvider != null, + sortedCreateOptionsPairs = sortedCreateOptionsPairs, activeEntry = toActiveEntry( - /*defaultProvider=*/defaultProvider, - /*createOptionSize=*/createOptionsPairs.size, - /*lastSeenProviderWithNonEmptyCreateOptions=*/ - lastSeenProviderWithNonEmptyCreateOptions, - /*remoteEntry=*/remoteEntry + defaultProvider = defaultProvider, + sortedCreateOptionsPairs = sortedCreateOptionsPairs, + remoteEntry = remoteEntry, + remoteEntryProvider = remoteEntryProvider, ), remoteEntry = remoteEntry, + foundCandidateFromUserDefaultProvider = defaultProviderSetByUser != null, ) } @@ -589,59 +603,43 @@ class CreateFlowUtils { createOptionSize: Int, isOnPasskeyIntroStateAlready: Boolean, requestDisplayInfo: RequestDisplayInfo, - defaultProvider: EnabledProviderInfo?, remoteEntry: RemoteInfo?, isPasskeyFirstUse: Boolean, ): CreateScreenState? { - return if (isPasskeyFirstUse && requestDisplayInfo.type == - CredentialType.PASSKEY && !isOnPasskeyIntroStateAlready) { + return if (isPasskeyFirstUse && requestDisplayInfo.type == CredentialType.PASSKEY && + !isOnPasskeyIntroStateAlready) { CreateScreenState.PASSKEY_INTRO - } else if ((defaultProvider == null || defaultProvider.createOptions.isEmpty()) && - createOptionSize > 1) { - CreateScreenState.PROVIDER_SELECTION - } else if (((defaultProvider == null || defaultProvider.createOptions.isEmpty()) && - createOptionSize == 1) || (defaultProvider != null && - defaultProvider.createOptions.isNotEmpty())) { - CreateScreenState.CREATION_OPTION_SELECTION } else if (createOptionSize == 0 && remoteEntry != null) { CreateScreenState.EXTERNAL_ONLY_SELECTION } else { - Log.d( - Constants.LOG_TAG, - "Unexpected failure: the screen state failed to instantiate" + - " because the provider list is empty." - ) - null + CreateScreenState.CREATION_OPTION_SELECTION } } private fun toActiveEntry( defaultProvider: EnabledProviderInfo?, - createOptionSize: Int, - lastSeenProviderWithNonEmptyCreateOptions: EnabledProviderInfo?, + sortedCreateOptionsPairs: List<Pair<CreateOptionInfo, EnabledProviderInfo>>, remoteEntry: RemoteInfo?, + remoteEntryProvider: EnabledProviderInfo?, ): ActiveEntry? { return if ( - defaultProvider != null && defaultProvider.createOptions.isEmpty() && - remoteEntry != null - ) { - ActiveEntry(defaultProvider, remoteEntry) - } else if ( - defaultProvider != null && defaultProvider.createOptions.isNotEmpty() + sortedCreateOptionsPairs.isEmpty() && remoteEntry != null && + remoteEntryProvider != null ) { - ActiveEntry(defaultProvider, defaultProvider.createOptions.first()) - } else if (createOptionSize == 1) { - ActiveEntry( - lastSeenProviderWithNonEmptyCreateOptions!!, - lastSeenProviderWithNonEmptyCreateOptions.createOptions.first() - ) + ActiveEntry(remoteEntryProvider, remoteEntry) + } else if (defaultProvider != null && + defaultProvider.sortedCreateOptions.isNotEmpty()) { + ActiveEntry(defaultProvider, defaultProvider.sortedCreateOptions.first()) + } else if (sortedCreateOptionsPairs.isNotEmpty()) { + val (topEntry, topEntryProvider) = sortedCreateOptionsPairs.first() + ActiveEntry(topEntryProvider, topEntry) } else null } /** * Note: caller required handle empty list due to parsing error. */ - private fun toCreationOptionInfoList( + private fun toSortedCreationOptionInfoList( providerId: String, creationEntries: List<Entry>, context: Context, @@ -664,7 +662,9 @@ class CreateFlowUtils { footerDescription = createEntry.description?.toString() )) } - return result + return result.sortedWith( + compareByDescending { it.lastUsedTime } + ) } private fun toRemoteInfo( @@ -690,6 +690,7 @@ class CreateFlowUtils { context: Context, preferImmediatelyAvailableCredentials: Boolean, appPreferredDefaultProviderId: String?, + userSetDefaultProviderIds: Set<String>, ): RequestDisplayInfo? { val json = JSONObject(requestJson) var passkeyUsername = "" @@ -711,6 +712,7 @@ class CreateFlowUtils { context.getDrawable(R.drawable.ic_passkey_24) ?: return null, preferImmediatelyAvailableCredentials, appPreferredDefaultProviderId, + userSetDefaultProviderIds, ) } } diff --git a/packages/CredentialManager/src/com/android/credentialmanager/UserConfigRepo.kt b/packages/CredentialManager/src/com/android/credentialmanager/UserConfigRepo.kt index a17f2c88abcd..bfcca4970a71 100644 --- a/packages/CredentialManager/src/com/android/credentialmanager/UserConfigRepo.kt +++ b/packages/CredentialManager/src/com/android/credentialmanager/UserConfigRepo.kt @@ -23,15 +23,6 @@ class UserConfigRepo(context: Context) { val sharedPreferences: SharedPreferences = context.getSharedPreferences( context.packageName, Context.MODE_PRIVATE) - fun setDefaultProvider( - providerId: String - ) { - sharedPreferences.edit().apply { - putString(DEFAULT_PROVIDER, providerId) - apply() - } - } - fun setIsPasskeyFirstUse( isFirstUse: Boolean ) { @@ -41,16 +32,11 @@ class UserConfigRepo(context: Context) { } } - fun getDefaultProviderId(): String? { - return sharedPreferences.getString(DEFAULT_PROVIDER, null) - } - fun getIsPasskeyFirstUse(): Boolean { return sharedPreferences.getBoolean(IS_PASSKEY_FIRST_USE, true) } companion object { - const val DEFAULT_PROVIDER = "default_provider" // This first use value only applies to passkeys, not related with if generally // credential manager is first use or not const val IS_PASSKEY_FIRST_USE = "is_passkey_first_use" diff --git a/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt b/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt index 9d871ed75494..dfa517b118b3 100644 --- a/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt +++ b/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt @@ -88,27 +88,11 @@ fun CreateCredentialScreen( onLearnMore = viewModel::createFlowOnLearnMore, onLog = { viewModel.logUiEvent(it) }, ) - CreateScreenState.PROVIDER_SELECTION -> ProviderSelectionCard( - requestDisplayInfo = createCredentialUiState.requestDisplayInfo, - disabledProviderList = createCredentialUiState - .disabledProviders, - sortedCreateOptionsPairs = - createCredentialUiState.sortedCreateOptionsPairs, - hasRemoteEntry = createCredentialUiState.remoteEntry != null, - onOptionSelected = - viewModel::createFlowOnEntrySelectedFromFirstUseScreen, - onDisabledProvidersSelected = - viewModel::createFlowOnDisabledProvidersSelected, - onMoreOptionsSelected = - viewModel::createFlowOnMoreOptionsSelectedOnProviderSelection, - onLog = { viewModel.logUiEvent(it) }, - ) CreateScreenState.CREATION_OPTION_SELECTION -> CreationSelectionCard( requestDisplayInfo = createCredentialUiState.requestDisplayInfo, enabledProviderList = createCredentialUiState.enabledProviders, providerInfo = createCredentialUiState .activeEntry?.activeProvider!!, - hasDefaultProvider = createCredentialUiState.hasDefaultProvider, createOptionInfo = createCredentialUiState.activeEntry.activeEntryInfo as CreateOptionInfo, @@ -121,21 +105,15 @@ fun CreateCredentialScreen( CreateScreenState.MORE_OPTIONS_SELECTION -> MoreOptionsSelectionCard( requestDisplayInfo = createCredentialUiState.requestDisplayInfo, enabledProviderList = createCredentialUiState.enabledProviders, - disabledProviderList = createCredentialUiState - .disabledProviders, + disabledProviderList = createCredentialUiState.disabledProviders, sortedCreateOptionsPairs = createCredentialUiState.sortedCreateOptionsPairs, - hasDefaultProvider = createCredentialUiState.hasDefaultProvider, - isFromProviderSelection = - createCredentialUiState.isFromProviderSelection!!, - onBackProviderSelectionButtonSelected = - viewModel::createFlowOnBackProviderSelectionButtonSelected, onBackCreationSelectionButtonSelected = viewModel::createFlowOnBackCreationSelectionButtonSelected, onOptionSelected = viewModel::createFlowOnEntrySelectedFromMoreOptionScreen, onDisabledProvidersSelected = - viewModel::createFlowOnDisabledProvidersSelected, + viewModel::createFlowOnLaunchSettings, onRemoteEntrySelected = viewModel::createFlowOnEntrySelected, onLog = { viewModel.logUiEvent(it) }, ) @@ -144,11 +122,11 @@ fun CreateCredentialScreen( viewModel.onIllegalUiState("Expect active entry to be non-null" + " upon default provider dialog.") } else { - DefaultProviderConfirmationCard( + NonDefaultUsageConfirmationCard( selectedEntry = createCredentialUiState.activeEntry, onIllegalScreenState = viewModel::onIllegalUiState, - onChangeDefaultSelected = - viewModel::createFlowOnChangeDefaultSelected, + onLaunchSettings = + viewModel::createFlowOnLaunchSettings, onUseOnceSelected = viewModel::createFlowOnUseOnceSelected, onLog = { viewModel.logUiEvent(it) }, ) @@ -263,94 +241,11 @@ fun PasskeyIntroCard( } @Composable -fun ProviderSelectionCard( - requestDisplayInfo: RequestDisplayInfo, - disabledProviderList: List<DisabledProviderInfo>?, - sortedCreateOptionsPairs: List<Pair<CreateOptionInfo, EnabledProviderInfo>>, - hasRemoteEntry: Boolean, - onOptionSelected: (ActiveEntry) -> Unit, - onDisabledProvidersSelected: () -> Unit, - onMoreOptionsSelected: () -> Unit, - onLog: @Composable (UiEventEnum) -> Unit, -) { - SheetContainerCard { - item { HeadlineIcon(bitmap = requestDisplayInfo.typeIcon.toBitmap().asImageBitmap()) } - item { Divider(thickness = 16.dp, color = Color.Transparent) } - item { - HeadlineText( - text = stringResource( - R.string.choose_provider_title, - when (requestDisplayInfo.type) { - CredentialType.PASSKEY -> - stringResource(R.string.passkeys) - CredentialType.PASSWORD -> - stringResource(R.string.passwords) - CredentialType.UNKNOWN -> stringResource(R.string.sign_in_info) - } - ) - ) - } - item { Divider(thickness = 24.dp, color = Color.Transparent) } - - item { - Row(modifier = Modifier.fillMaxWidth().wrapContentHeight()) { - BodyMediumText(text = stringResource(R.string.choose_provider_body)) - } - } - item { Divider(thickness = 16.dp, color = Color.Transparent) } - item { - CredentialContainerCard { - Column( - verticalArrangement = Arrangement.spacedBy(2.dp) - ) { - sortedCreateOptionsPairs.forEach { entry -> - MoreOptionsInfoRow( - requestDisplayInfo = requestDisplayInfo, - providerInfo = entry.second, - createOptionInfo = entry.first, - onOptionSelected = { - onOptionSelected( - ActiveEntry( - entry.second, - entry.first - ) - ) - } - ) - } - MoreOptionsDisabledProvidersRow( - disabledProviders = disabledProviderList, - onDisabledProvidersSelected = onDisabledProvidersSelected, - ) - } - } - } - if (hasRemoteEntry) { - item { Divider(thickness = 24.dp, color = Color.Transparent) } - item { - CtaButtonRow( - leftButton = { - ActionButton( - stringResource(R.string.string_more_options), - onMoreOptionsSelected - ) - } - ) - } - } - } - onLog(CreateCredentialEvent.CREDMAN_CREATE_CRED_PROVIDER_SELECTION) -} - -@Composable fun MoreOptionsSelectionCard( requestDisplayInfo: RequestDisplayInfo, enabledProviderList: List<EnabledProviderInfo>, disabledProviderList: List<DisabledProviderInfo>?, sortedCreateOptionsPairs: List<Pair<CreateOptionInfo, EnabledProviderInfo>>, - hasDefaultProvider: Boolean, - isFromProviderSelection: Boolean, - onBackProviderSelectionButtonSelected: () -> Unit, onBackCreationSelectionButtonSelected: () -> Unit, onOptionSelected: (ActiveEntry) -> Unit, onDisabledProvidersSelected: () -> Unit, @@ -369,9 +264,7 @@ fun MoreOptionsSelectionCard( CredentialType.UNKNOWN -> stringResource(R.string.sign_in_info) } ), - onNavigationIconClicked = - if (isFromProviderSelection) onBackProviderSelectionButtonSelected - else onBackCreationSelectionButtonSelected, + onNavigationIconClicked = onBackCreationSelectionButtonSelected, bottomPadding = 16.dp, ) }) { @@ -379,30 +272,26 @@ fun MoreOptionsSelectionCard( item { CredentialContainerCard { Column(verticalArrangement = Arrangement.spacedBy(2.dp)) { - // Only in the flows with default provider(not first time use) we can show the - // createOptions here, or they will be shown on ProviderSelectionCard - if (hasDefaultProvider) { - sortedCreateOptionsPairs.forEach { entry -> - MoreOptionsInfoRow( - requestDisplayInfo = requestDisplayInfo, - providerInfo = entry.second, - createOptionInfo = entry.first, - onOptionSelected = { - onOptionSelected( - ActiveEntry( - entry.second, - entry.first - ) + sortedCreateOptionsPairs.forEach { entry -> + MoreOptionsInfoRow( + requestDisplayInfo = requestDisplayInfo, + providerInfo = entry.second, + createOptionInfo = entry.first, + onOptionSelected = { + onOptionSelected( + ActiveEntry( + entry.second, + entry.first ) - } - ) - } - MoreOptionsDisabledProvidersRow( - disabledProviders = disabledProviderList, - onDisabledProvidersSelected = - onDisabledProvidersSelected, + ) + } ) } + MoreOptionsDisabledProvidersRow( + disabledProviders = disabledProviderList, + onDisabledProvidersSelected = + onDisabledProvidersSelected, + ) enabledProviderList.forEach { if (it.remoteEntry != null) { RemoteEntryRow( @@ -420,10 +309,10 @@ fun MoreOptionsSelectionCard( } @Composable -fun DefaultProviderConfirmationCard( +fun NonDefaultUsageConfirmationCard( selectedEntry: ActiveEntry, onIllegalScreenState: (String) -> Unit, - onChangeDefaultSelected: () -> Unit, + onLaunchSettings: () -> Unit, onUseOnceSelected: () -> Unit, onLog: @Composable (UiEventEnum) -> Unit, ) { @@ -454,14 +343,14 @@ fun DefaultProviderConfirmationCard( CtaButtonRow( leftButton = { ActionButton( - stringResource(R.string.use_once), - onClick = onUseOnceSelected + stringResource(R.string.settings), + onClick = onLaunchSettings, ) }, rightButton = { ConfirmButton( - stringResource(R.string.set_as_default), - onClick = onChangeDefaultSelected + stringResource(R.string.use_once), + onClick = onUseOnceSelected, ) }, ) @@ -479,7 +368,6 @@ fun CreationSelectionCard( onOptionSelected: (BaseEntry) -> Unit, onConfirm: () -> Unit, onMoreOptionsSelected: () -> Unit, - hasDefaultProvider: Boolean, onLog: @Composable (UiEventEnum) -> Unit, ) { SheetContainerCard { @@ -527,16 +415,9 @@ fun CreationSelectionCard( if (enabledProvider.remoteEntry != null) { remoteEntry = enabledProvider.remoteEntry } - createOptionsSize += enabledProvider.createOptions.size - } - val shouldShowMoreOptionsButton = if (!hasDefaultProvider) { - // User has already been presented with all options on the default provider - // selection screen. Don't show them again. Therefore, only show the more option - // button if remote option is present. - remoteEntry != null - } else { - createOptionsSize > 1 || remoteEntry != null + createOptionsSize += enabledProvider.sortedCreateOptions.size } + val shouldShowMoreOptionsButton = createOptionsSize > 1 || remoteEntry != null item { CtaButtonRow( leftButton = if (shouldShowMoreOptionsButton) { diff --git a/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateModel.kt b/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateModel.kt index 225dbf2b744f..fe1ce1b60413 100644 --- a/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateModel.kt +++ b/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateModel.kt @@ -29,12 +29,9 @@ data class CreateCredentialUiState( val currentScreenState: CreateScreenState, val requestDisplayInfo: RequestDisplayInfo, val sortedCreateOptionsPairs: List<Pair<CreateOptionInfo, EnabledProviderInfo>>, - // Should not change with the real time update of default provider, only determine whether - // we're showing provider selection page at the beginning - val hasDefaultProvider: Boolean, val activeEntry: ActiveEntry? = null, val remoteEntry: RemoteInfo? = null, - val isFromProviderSelection: Boolean? = null, + val foundCandidateFromUserDefaultProvider: Boolean, ) internal fun hasContentToDisplay(state: CreateCredentialUiState): Boolean { @@ -50,11 +47,12 @@ open class ProviderInfo( ) class EnabledProviderInfo( - icon: Drawable, - id: String, - displayName: String, - var createOptions: List<CreateOptionInfo>, - var remoteEntry: RemoteInfo?, + icon: Drawable, + id: String, + displayName: String, + // Sorted by last used time + var sortedCreateOptions: List<CreateOptionInfo>, + var remoteEntry: RemoteInfo?, ) : ProviderInfo(icon, id, displayName) class DisabledProviderInfo( @@ -108,6 +106,7 @@ data class RequestDisplayInfo( val typeIcon: Drawable, val preferImmediatelyAvailableCredentials: Boolean, val appPreferredDefaultProviderId: String?, + val userSetDefaultProviderIds: Set<String>, ) /** @@ -123,7 +122,6 @@ data class ActiveEntry ( enum class CreateScreenState { PASSKEY_INTRO, MORE_ABOUT_PASSKEYS_INTRO, - PROVIDER_SELECTION, CREATION_OPTION_SELECTION, MORE_OPTIONS_SELECTION, DEFAULT_PROVIDER_CONFIRMATION, |