Merge "[CredManUi] Support auto select." into udc-dev
This commit is contained in:
@@ -42,11 +42,13 @@ import com.android.credentialmanager.createflow.DisabledProviderInfo
|
||||
import com.android.credentialmanager.createflow.EnabledProviderInfo
|
||||
import com.android.credentialmanager.createflow.RequestDisplayInfo
|
||||
import com.android.credentialmanager.getflow.GetCredentialUiState
|
||||
import com.android.credentialmanager.getflow.findAutoSelectEntry
|
||||
import androidx.credentials.CreateCredentialRequest.DisplayInfo
|
||||
import androidx.credentials.CreatePublicKeyCredentialRequest
|
||||
import androidx.credentials.CreatePasswordRequest
|
||||
import androidx.credentials.GetPasswordOption
|
||||
import androidx.credentials.GetPublicKeyCredentialOption
|
||||
import com.android.credentialmanager.common.ProviderActivityState
|
||||
|
||||
import java.time.Instant
|
||||
|
||||
@@ -128,10 +130,20 @@ class CredentialManagerRepo(
|
||||
getCredentialUiState = null,
|
||||
)
|
||||
}
|
||||
RequestInfo.TYPE_GET -> UiState(
|
||||
createCredentialUiState = null,
|
||||
getCredentialUiState = getCredentialInitialUiState(originName)!!,
|
||||
)
|
||||
RequestInfo.TYPE_GET -> {
|
||||
val getCredentialInitialUiState = getCredentialInitialUiState(originName)!!
|
||||
val autoSelectEntry =
|
||||
findAutoSelectEntry(getCredentialInitialUiState.providerDisplayInfo)
|
||||
UiState(
|
||||
createCredentialUiState = null,
|
||||
getCredentialUiState = getCredentialInitialUiState,
|
||||
selectedEntry = autoSelectEntry,
|
||||
providerActivityState =
|
||||
if (autoSelectEntry == null) ProviderActivityState.NOT_APPLICABLE
|
||||
else ProviderActivityState.READY_TO_LAUNCH,
|
||||
isAutoSelectFlow = autoSelectEntry != null,
|
||||
)
|
||||
}
|
||||
else -> throw IllegalStateException("Unrecognized request type: ${requestInfo.type}")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,6 +44,9 @@ data class UiState(
|
||||
val selectedEntry: BaseEntry? = null,
|
||||
val providerActivityState: ProviderActivityState = ProviderActivityState.NOT_APPLICABLE,
|
||||
val dialogState: DialogState = DialogState.ACTIVE,
|
||||
// True if the UI has one and onely one auto selectable entry. Its provider activiey will be
|
||||
// launched immediately, and canceling it will cancel the whole UI flow.
|
||||
val isAutoSelectFlow: Boolean = false,
|
||||
)
|
||||
|
||||
class CredentialSelectorViewModel(
|
||||
@@ -96,13 +99,20 @@ class CredentialSelectorViewModel(
|
||||
val resultCode = providerActivityResult.resultCode
|
||||
val resultData = providerActivityResult.data
|
||||
if (resultCode == Activity.RESULT_CANCELED) {
|
||||
// Re-display the CredMan UI if the user canceled from the provider UI.
|
||||
Log.d(Constants.LOG_TAG, "The provider activity was cancelled," +
|
||||
" re-displaying our UI.")
|
||||
uiState = uiState.copy(
|
||||
selectedEntry = null,
|
||||
providerActivityState = ProviderActivityState.NOT_APPLICABLE,
|
||||
)
|
||||
// Re-display the CredMan UI if the user canceled from the provider UI, or cancel
|
||||
// the UI if this is the auto select flow.
|
||||
if (uiState.isAutoSelectFlow) {
|
||||
Log.d(Constants.LOG_TAG, "The auto selected provider activity was cancelled," +
|
||||
" ending the credential manager activity.")
|
||||
onUserCancel()
|
||||
} else {
|
||||
Log.d(Constants.LOG_TAG, "The provider activity was cancelled," +
|
||||
" re-displaying our UI.")
|
||||
uiState = uiState.copy(
|
||||
selectedEntry = null,
|
||||
providerActivityState = ProviderActivityState.NOT_APPLICABLE,
|
||||
)
|
||||
}
|
||||
} else {
|
||||
if (entry != null) {
|
||||
Log.d(
|
||||
|
||||
@@ -228,6 +228,8 @@ class GetFlowUtils {
|
||||
icon = credentialEntry.icon.loadDrawable(context),
|
||||
shouldTintIcon = credentialEntry.isDefaultIcon ?: false,
|
||||
lastUsedTimeMillis = credentialEntry.lastUsedTime,
|
||||
isAutoSelectable = credentialEntry.isAutoSelectAllowed &&
|
||||
credentialEntry.autoSelectAllowedFromOption,
|
||||
))
|
||||
}
|
||||
is PublicKeyCredentialEntry -> {
|
||||
@@ -245,6 +247,8 @@ class GetFlowUtils {
|
||||
icon = credentialEntry.icon.loadDrawable(context),
|
||||
shouldTintIcon = credentialEntry.isDefaultIcon,
|
||||
lastUsedTimeMillis = credentialEntry.lastUsedTime,
|
||||
isAutoSelectable = credentialEntry.isAutoSelectAllowed &&
|
||||
credentialEntry.autoSelectAllowedFromOption,
|
||||
))
|
||||
}
|
||||
is CustomCredentialEntry -> {
|
||||
@@ -262,6 +266,8 @@ class GetFlowUtils {
|
||||
icon = credentialEntry.icon.loadDrawable(context),
|
||||
shouldTintIcon = credentialEntry.isDefaultIcon,
|
||||
lastUsedTimeMillis = credentialEntry.lastUsedTime,
|
||||
isAutoSelectable = credentialEntry.isAutoSelectAllowed &&
|
||||
credentialEntry.autoSelectAllowedFromOption,
|
||||
))
|
||||
}
|
||||
else -> Log.d(
|
||||
|
||||
@@ -155,21 +155,23 @@ class GetTestUtils {
|
||||
userName: String,
|
||||
userDisplayName: String?,
|
||||
lastUsedTime: Instant?,
|
||||
isAutoSelectAllowed: Boolean = false,
|
||||
): Entry {
|
||||
val intent = Intent("com.androidauth.androidvault.CONFIRM_PASSWORD")
|
||||
.setPackage("com.androidauth.androidvault")
|
||||
intent.putExtra("provider_extra_sample", "testprovider")
|
||||
val pendingIntent = PendingIntent.getActivity(
|
||||
context, 1,
|
||||
intent, (PendingIntent.FLAG_MUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
|
||||
or PendingIntent.FLAG_ONE_SHOT)
|
||||
val intent = Intent(Settings.ACTION_SYNC_SETTINGS)
|
||||
val pendingIntent =
|
||||
PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_IMMUTABLE)
|
||||
val candidateQueryData = Bundle()
|
||||
candidateQueryData.putBoolean(
|
||||
"androidx.credentials.BUNDLE_KEY_IS_AUTO_SELECT_ALLOWED",
|
||||
isAutoSelectAllowed
|
||||
)
|
||||
val passkeyEntry = PublicKeyCredentialEntry.Builder(
|
||||
context,
|
||||
userName,
|
||||
pendingIntent,
|
||||
BeginGetPublicKeyCredentialOption(Bundle(), "id", "requestjson")
|
||||
).setDisplayName(userDisplayName).setLastUsedTime(lastUsedTime).build()
|
||||
BeginGetPublicKeyCredentialOption(candidateQueryData, "id", "requestjson")
|
||||
).setDisplayName(userDisplayName).setLastUsedTime(lastUsedTime)
|
||||
.setAutoSelectAllowed(isAutoSelectAllowed).build()
|
||||
return Entry(key, subkey, passkeyEntry.slice, Intent())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,18 @@
|
||||
@file:OptIn(ExperimentalMaterial3Api::class)
|
||||
/*
|
||||
* Copyright (C) 2022 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.createflow
|
||||
|
||||
@@ -15,7 +29,6 @@ import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.wrapContentHeight
|
||||
import androidx.compose.material3.Divider
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.outlined.NewReleases
|
||||
|
||||
@@ -41,6 +41,23 @@ internal fun hasContentToDisplay(state: GetCredentialUiState): Boolean {
|
||||
!state.requestDisplayInfo.preferImmediatelyAvailableCredentials)
|
||||
}
|
||||
|
||||
internal fun findAutoSelectEntry(providerDisplayInfo: ProviderDisplayInfo): CredentialEntryInfo? {
|
||||
if (providerDisplayInfo.authenticationEntryList.isNotEmpty()) {
|
||||
return null
|
||||
}
|
||||
if (providerDisplayInfo.sortedUserNameToCredentialEntryList.size == 1) {
|
||||
val entryList = providerDisplayInfo.sortedUserNameToCredentialEntryList.firstOrNull()
|
||||
?: return null
|
||||
if (entryList.sortedCredentialEntryList.size == 1) {
|
||||
val entry = entryList.sortedCredentialEntryList.firstOrNull() ?: return null
|
||||
if (entry.isAutoSelectable) {
|
||||
return entry
|
||||
}
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
data class ProviderInfo(
|
||||
/**
|
||||
* Unique id (component name) of this provider.
|
||||
@@ -83,6 +100,7 @@ class CredentialEntryInfo(
|
||||
val icon: Drawable?,
|
||||
val shouldTintIcon: Boolean,
|
||||
val lastUsedTimeMillis: Instant?,
|
||||
val isAutoSelectable: Boolean,
|
||||
) : BaseEntry(
|
||||
providerId,
|
||||
entryKey,
|
||||
|
||||
Reference in New Issue
Block a user