Merge "Support autoselect for create flows." into udc-dev am: fcdac40e64
Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/23089737 Change-Id: Ic5d6b155a2888ff3ab93920c3a763fc1d0da5beb Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
@@ -38,6 +38,7 @@ import com.android.credentialmanager.createflow.RequestDisplayInfo
|
|||||||
import com.android.credentialmanager.getflow.GetCredentialUiState
|
import com.android.credentialmanager.getflow.GetCredentialUiState
|
||||||
import com.android.credentialmanager.getflow.findAutoSelectEntry
|
import com.android.credentialmanager.getflow.findAutoSelectEntry
|
||||||
import com.android.credentialmanager.common.ProviderActivityState
|
import com.android.credentialmanager.common.ProviderActivityState
|
||||||
|
import com.android.credentialmanager.createflow.isFlowAutoSelectable
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Client for interacting with Credential Manager. Also holds data inputs from it.
|
* Client for interacting with Credential Manager. Also holds data inputs from it.
|
||||||
@@ -113,8 +114,7 @@ class CredentialManagerRepo(
|
|||||||
val providerDisableListUiState = getCreateProviderDisableListInitialUiState()
|
val providerDisableListUiState = getCreateProviderDisableListInitialUiState()
|
||||||
val requestDisplayInfoUiState =
|
val requestDisplayInfoUiState =
|
||||||
getCreateRequestDisplayInfoInitialUiState(originName)!!
|
getCreateRequestDisplayInfoInitialUiState(originName)!!
|
||||||
UiState(
|
val createCredentialUiState = CreateFlowUtils.toCreateCredentialUiState(
|
||||||
createCredentialUiState = CreateFlowUtils.toCreateCredentialUiState(
|
|
||||||
enabledProviders = providerEnableListUiState,
|
enabledProviders = providerEnableListUiState,
|
||||||
disabledProviders = providerDisableListUiState,
|
disabledProviders = providerDisableListUiState,
|
||||||
defaultProviderIdPreferredByApp =
|
defaultProviderIdPreferredByApp =
|
||||||
@@ -124,10 +124,20 @@ class CredentialManagerRepo(
|
|||||||
requestDisplayInfo = requestDisplayInfoUiState,
|
requestDisplayInfo = requestDisplayInfoUiState,
|
||||||
isOnPasskeyIntroStateAlready = false,
|
isOnPasskeyIntroStateAlready = false,
|
||||||
isPasskeyFirstUse = isPasskeyFirstUse,
|
isPasskeyFirstUse = isPasskeyFirstUse,
|
||||||
)!!,
|
)!!
|
||||||
|
val isFlowAutoSelectable = isFlowAutoSelectable(createCredentialUiState)
|
||||||
|
UiState(
|
||||||
|
createCredentialUiState = createCredentialUiState,
|
||||||
getCredentialUiState = null,
|
getCredentialUiState = null,
|
||||||
cancelRequestState = cancelUiRequestState,
|
cancelRequestState = cancelUiRequestState,
|
||||||
isInitialRender = isNewActivity,
|
isInitialRender = isNewActivity,
|
||||||
|
isAutoSelectFlow = isFlowAutoSelectable,
|
||||||
|
providerActivityState =
|
||||||
|
if (isFlowAutoSelectable) ProviderActivityState.READY_TO_LAUNCH
|
||||||
|
else ProviderActivityState.NOT_APPLICABLE,
|
||||||
|
selectedEntry =
|
||||||
|
if (isFlowAutoSelectable) createCredentialUiState.activeEntry?.activeEntryInfo
|
||||||
|
else null,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
RequestInfo.TYPE_GET -> {
|
RequestInfo.TYPE_GET -> {
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ import com.android.credentialmanager.common.DialogState
|
|||||||
import com.android.credentialmanager.common.ProviderActivityResult
|
import com.android.credentialmanager.common.ProviderActivityResult
|
||||||
import com.android.credentialmanager.common.ProviderActivityState
|
import com.android.credentialmanager.common.ProviderActivityState
|
||||||
import com.android.credentialmanager.createflow.ActiveEntry
|
import com.android.credentialmanager.createflow.ActiveEntry
|
||||||
|
import com.android.credentialmanager.createflow.isFlowAutoSelectable
|
||||||
import com.android.credentialmanager.createflow.CreateCredentialUiState
|
import com.android.credentialmanager.createflow.CreateCredentialUiState
|
||||||
import com.android.credentialmanager.createflow.CreateScreenState
|
import com.android.credentialmanager.createflow.CreateScreenState
|
||||||
import com.android.credentialmanager.getflow.GetCredentialUiState
|
import com.android.credentialmanager.getflow.GetCredentialUiState
|
||||||
@@ -262,30 +263,39 @@ class CredentialSelectorViewModel(
|
|||||||
/***** Create Flow Callbacks *****/
|
/***** Create Flow Callbacks *****/
|
||||||
/**************************************************************************/
|
/**************************************************************************/
|
||||||
fun createFlowOnConfirmIntro() {
|
fun createFlowOnConfirmIntro() {
|
||||||
|
userConfigRepo.setIsPasskeyFirstUse(false)
|
||||||
val prevUiState = uiState.createCredentialUiState
|
val prevUiState = uiState.createCredentialUiState
|
||||||
if (prevUiState == null) {
|
if (prevUiState == null) {
|
||||||
Log.d(Constants.LOG_TAG, "Encountered unexpected null create ui state")
|
Log.d(Constants.LOG_TAG, "Encountered unexpected null create ui state")
|
||||||
onInternalError()
|
onInternalError()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
val newUiState = CreateFlowUtils.toCreateCredentialUiState(
|
val newScreenState = CreateFlowUtils.toCreateScreenState(
|
||||||
enabledProviders = prevUiState.enabledProviders,
|
createOptionSize = prevUiState.sortedCreateOptionsPairs.size,
|
||||||
disabledProviders = prevUiState.disabledProviders,
|
|
||||||
defaultProviderIdPreferredByApp =
|
|
||||||
prevUiState.requestDisplayInfo.appPreferredDefaultProviderId,
|
|
||||||
defaultProviderIdsSetByUser =
|
|
||||||
prevUiState.requestDisplayInfo.userSetDefaultProviderIds,
|
|
||||||
requestDisplayInfo = prevUiState.requestDisplayInfo,
|
|
||||||
isOnPasskeyIntroStateAlready = true,
|
isOnPasskeyIntroStateAlready = true,
|
||||||
isPasskeyFirstUse = userConfigRepo.getIsPasskeyFirstUse()
|
requestDisplayInfo = prevUiState.requestDisplayInfo,
|
||||||
|
remoteEntry = prevUiState.remoteEntry,
|
||||||
|
isPasskeyFirstUse = true,
|
||||||
)
|
)
|
||||||
if (newUiState == null) {
|
if (newScreenState == null) {
|
||||||
Log.d(Constants.LOG_TAG, "Unable to update create ui state")
|
Log.d(Constants.LOG_TAG, "Unexpected: couldn't resolve new screen state")
|
||||||
onInternalError()
|
onInternalError()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
uiState = uiState.copy(createCredentialUiState = newUiState)
|
val newCreateCredentialUiState = prevUiState.copy(
|
||||||
userConfigRepo.setIsPasskeyFirstUse(false)
|
currentScreenState = newScreenState,
|
||||||
|
)
|
||||||
|
val isFlowAutoSelectable = isFlowAutoSelectable(newCreateCredentialUiState)
|
||||||
|
uiState = uiState.copy(
|
||||||
|
createCredentialUiState = newCreateCredentialUiState,
|
||||||
|
isAutoSelectFlow = isFlowAutoSelectable,
|
||||||
|
providerActivityState =
|
||||||
|
if (isFlowAutoSelectable) ProviderActivityState.READY_TO_LAUNCH
|
||||||
|
else ProviderActivityState.NOT_APPLICABLE,
|
||||||
|
selectedEntry =
|
||||||
|
if (isFlowAutoSelectable) newCreateCredentialUiState.activeEntry?.activeEntryInfo
|
||||||
|
else null,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun createFlowOnMoreOptionsSelectedOnCreationSelection() {
|
fun createFlowOnMoreOptionsSelectedOnCreationSelection() {
|
||||||
|
|||||||
@@ -487,6 +487,7 @@ class CreateFlowUtils {
|
|||||||
createCredentialRequestJetpack.preferImmediatelyAvailableCredentials,
|
createCredentialRequestJetpack.preferImmediatelyAvailableCredentials,
|
||||||
appPreferredDefaultProviderId = appPreferredDefaultProviderId,
|
appPreferredDefaultProviderId = appPreferredDefaultProviderId,
|
||||||
userSetDefaultProviderIds = requestInfo.defaultProviderIds.toSet(),
|
userSetDefaultProviderIds = requestInfo.defaultProviderIds.toSet(),
|
||||||
|
isAutoSelectRequest = createCredentialRequestJetpack.isAutoSelectAllowed,
|
||||||
)
|
)
|
||||||
is CreatePublicKeyCredentialRequest -> {
|
is CreatePublicKeyCredentialRequest -> {
|
||||||
newRequestDisplayInfoFromPasskeyJson(
|
newRequestDisplayInfoFromPasskeyJson(
|
||||||
@@ -497,6 +498,7 @@ class CreateFlowUtils {
|
|||||||
createCredentialRequestJetpack.preferImmediatelyAvailableCredentials,
|
createCredentialRequestJetpack.preferImmediatelyAvailableCredentials,
|
||||||
appPreferredDefaultProviderId = appPreferredDefaultProviderId,
|
appPreferredDefaultProviderId = appPreferredDefaultProviderId,
|
||||||
userSetDefaultProviderIds = requestInfo.defaultProviderIds.toSet(),
|
userSetDefaultProviderIds = requestInfo.defaultProviderIds.toSet(),
|
||||||
|
isAutoSelectRequest = createCredentialRequestJetpack.isAutoSelectAllowed,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
is CreateCustomCredentialRequest -> {
|
is CreateCustomCredentialRequest -> {
|
||||||
@@ -515,6 +517,7 @@ class CreateFlowUtils {
|
|||||||
createCredentialRequestJetpack.preferImmediatelyAvailableCredentials,
|
createCredentialRequestJetpack.preferImmediatelyAvailableCredentials,
|
||||||
appPreferredDefaultProviderId = appPreferredDefaultProviderId,
|
appPreferredDefaultProviderId = appPreferredDefaultProviderId,
|
||||||
userSetDefaultProviderIds = requestInfo.defaultProviderIds.toSet(),
|
userSetDefaultProviderIds = requestInfo.defaultProviderIds.toSet(),
|
||||||
|
isAutoSelectRequest = createCredentialRequestJetpack.isAutoSelectAllowed,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
else -> null
|
else -> null
|
||||||
@@ -602,7 +605,7 @@ class CreateFlowUtils {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun toCreateScreenState(
|
fun toCreateScreenState(
|
||||||
createOptionSize: Int,
|
createOptionSize: Int,
|
||||||
isOnPasskeyIntroStateAlready: Boolean,
|
isOnPasskeyIntroStateAlready: Boolean,
|
||||||
requestDisplayInfo: RequestDisplayInfo,
|
requestDisplayInfo: RequestDisplayInfo,
|
||||||
@@ -662,7 +665,13 @@ class CreateFlowUtils {
|
|||||||
passkeyCount = createEntry.getPublicKeyCredentialCount(),
|
passkeyCount = createEntry.getPublicKeyCredentialCount(),
|
||||||
totalCredentialCount = createEntry.getTotalCredentialCount(),
|
totalCredentialCount = createEntry.getTotalCredentialCount(),
|
||||||
lastUsedTime = createEntry.lastUsedTime ?: Instant.MIN,
|
lastUsedTime = createEntry.lastUsedTime ?: Instant.MIN,
|
||||||
footerDescription = createEntry.description?.toString()
|
footerDescription = createEntry.description?.toString(),
|
||||||
|
// TODO(b/281065680): replace with official library constant once available
|
||||||
|
allowAutoSelect =
|
||||||
|
it.slice.items.firstOrNull {
|
||||||
|
it.hasHint("androidx.credentials.provider.createEntry.SLICE_HINT_AUTO_" +
|
||||||
|
"SELECT_ALLOWED")
|
||||||
|
}?.text == "true",
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
return result.sortedWith(
|
return result.sortedWith(
|
||||||
@@ -694,6 +703,7 @@ class CreateFlowUtils {
|
|||||||
preferImmediatelyAvailableCredentials: Boolean,
|
preferImmediatelyAvailableCredentials: Boolean,
|
||||||
appPreferredDefaultProviderId: String?,
|
appPreferredDefaultProviderId: String?,
|
||||||
userSetDefaultProviderIds: Set<String>,
|
userSetDefaultProviderIds: Set<String>,
|
||||||
|
isAutoSelectRequest: Boolean
|
||||||
): RequestDisplayInfo? {
|
): RequestDisplayInfo? {
|
||||||
val json = JSONObject(requestJson)
|
val json = JSONObject(requestJson)
|
||||||
var passkeyUsername = ""
|
var passkeyUsername = ""
|
||||||
@@ -716,6 +726,7 @@ class CreateFlowUtils {
|
|||||||
preferImmediatelyAvailableCredentials,
|
preferImmediatelyAvailableCredentials,
|
||||||
appPreferredDefaultProviderId,
|
appPreferredDefaultProviderId,
|
||||||
userSetDefaultProviderIds,
|
userSetDefaultProviderIds,
|
||||||
|
isAutoSelectRequest,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,14 +39,16 @@ fun ModalBottomSheet(
|
|||||||
onDismiss: () -> Unit,
|
onDismiss: () -> Unit,
|
||||||
isInitialRender: Boolean,
|
isInitialRender: Boolean,
|
||||||
onInitialRenderComplete: () -> Unit,
|
onInitialRenderComplete: () -> Unit,
|
||||||
|
isAutoSelectFlow: Boolean,
|
||||||
) {
|
) {
|
||||||
val scope = rememberCoroutineScope()
|
val scope = rememberCoroutineScope()
|
||||||
val state = rememberModalBottomSheetState(
|
val state = rememberModalBottomSheetState(
|
||||||
initialValue = ModalBottomSheetValue.Hidden,
|
initialValue = if (isAutoSelectFlow) ModalBottomSheetValue.Expanded
|
||||||
|
else ModalBottomSheetValue.Hidden,
|
||||||
skipHalfExpanded = true
|
skipHalfExpanded = true
|
||||||
)
|
)
|
||||||
val sysUiController = rememberSystemUiController()
|
val sysUiController = rememberSystemUiController()
|
||||||
if (state.targetValue == ModalBottomSheetValue.Hidden) {
|
if (state.targetValue == ModalBottomSheetValue.Hidden || isAutoSelectFlow) {
|
||||||
setTransparentSystemBarsColor(sysUiController)
|
setTransparentSystemBarsColor(sysUiController)
|
||||||
} else {
|
} else {
|
||||||
setBottomSheetSystemBarsColor(sysUiController)
|
setBottomSheetSystemBarsColor(sysUiController)
|
||||||
|
|||||||
@@ -166,6 +166,7 @@ fun CreateCredentialScreen(
|
|||||||
},
|
},
|
||||||
onDismiss = viewModel::onUserCancel,
|
onDismiss = viewModel::onUserCancel,
|
||||||
isInitialRender = viewModel.uiState.isInitialRender,
|
isInitialRender = viewModel.uiState.isInitialRender,
|
||||||
|
isAutoSelectFlow = viewModel.uiState.isAutoSelectFlow,
|
||||||
onInitialRenderComplete = viewModel::onInitialRenderComplete,
|
onInitialRenderComplete = viewModel::onInitialRenderComplete,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,6 +34,21 @@ data class CreateCredentialUiState(
|
|||||||
val foundCandidateFromUserDefaultProvider: Boolean,
|
val foundCandidateFromUserDefaultProvider: Boolean,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
internal fun isFlowAutoSelectable(
|
||||||
|
uiState: CreateCredentialUiState
|
||||||
|
): Boolean {
|
||||||
|
return uiState.requestDisplayInfo.isAutoSelectRequest &&
|
||||||
|
// Even if the flow is auto selectable, still allow passkey intro screen to show once if
|
||||||
|
// applicable.
|
||||||
|
uiState.currentScreenState != CreateScreenState.PASSKEY_INTRO &&
|
||||||
|
uiState.currentScreenState != CreateScreenState.MORE_ABOUT_PASSKEYS_INTRO &&
|
||||||
|
uiState.remoteEntry == null &&
|
||||||
|
uiState.sortedCreateOptionsPairs.size == 1 &&
|
||||||
|
uiState.activeEntry?.activeEntryInfo?.let {
|
||||||
|
it is CreateOptionInfo && it.allowAutoSelect
|
||||||
|
} ?: false
|
||||||
|
}
|
||||||
|
|
||||||
internal fun hasContentToDisplay(state: CreateCredentialUiState): Boolean {
|
internal fun hasContentToDisplay(state: CreateCredentialUiState): Boolean {
|
||||||
return state.sortedCreateOptionsPairs.isNotEmpty() ||
|
return state.sortedCreateOptionsPairs.isNotEmpty() ||
|
||||||
(!state.requestDisplayInfo.preferImmediatelyAvailableCredentials &&
|
(!state.requestDisplayInfo.preferImmediatelyAvailableCredentials &&
|
||||||
@@ -74,6 +89,7 @@ class CreateOptionInfo(
|
|||||||
val totalCredentialCount: Int?,
|
val totalCredentialCount: Int?,
|
||||||
val lastUsedTime: Instant,
|
val lastUsedTime: Instant,
|
||||||
val footerDescription: String?,
|
val footerDescription: String?,
|
||||||
|
val allowAutoSelect: Boolean,
|
||||||
) : BaseEntry(
|
) : BaseEntry(
|
||||||
providerId,
|
providerId,
|
||||||
entryKey,
|
entryKey,
|
||||||
@@ -107,6 +123,8 @@ data class RequestDisplayInfo(
|
|||||||
val preferImmediatelyAvailableCredentials: Boolean,
|
val preferImmediatelyAvailableCredentials: Boolean,
|
||||||
val appPreferredDefaultProviderId: String?,
|
val appPreferredDefaultProviderId: String?,
|
||||||
val userSetDefaultProviderIds: Set<String>,
|
val userSetDefaultProviderIds: Set<String>,
|
||||||
|
// Whether the given CreateCredentialRequest allows auto select.
|
||||||
|
val isAutoSelectRequest: Boolean,
|
||||||
)
|
)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -149,6 +149,7 @@ fun GetCredentialScreen(
|
|||||||
},
|
},
|
||||||
onDismiss = viewModel::onUserCancel,
|
onDismiss = viewModel::onUserCancel,
|
||||||
isInitialRender = viewModel.uiState.isInitialRender,
|
isInitialRender = viewModel.uiState.isInitialRender,
|
||||||
|
isAutoSelectFlow = viewModel.uiState.isAutoSelectFlow,
|
||||||
onInitialRenderComplete = viewModel::onInitialRenderComplete,
|
onInitialRenderComplete = viewModel::onInitialRenderComplete,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user