Merge "Update the logic and storage about isFirstTimeUse and defaultProvider"
This commit is contained in:
@@ -41,8 +41,6 @@ import android.os.Bundle
|
||||
import android.os.ResultReceiver
|
||||
import android.service.credentials.CredentialProviderService
|
||||
import com.android.credentialmanager.createflow.CreateCredentialUiState
|
||||
import com.android.credentialmanager.createflow.EnabledProviderInfo
|
||||
import com.android.credentialmanager.createflow.RemoteInfo
|
||||
import com.android.credentialmanager.getflow.GetCredentialUiState
|
||||
import com.android.credentialmanager.jetpack.developer.CreatePasswordRequest.Companion.toBundle
|
||||
import com.android.credentialmanager.jetpack.developer.CreatePublicKeyCredentialRequest
|
||||
@@ -63,7 +61,7 @@ class CredentialManagerRepo(
|
||||
requestInfo = intent.extras?.getParcelable(
|
||||
RequestInfo.EXTRA_REQUEST_INFO,
|
||||
RequestInfo::class.java
|
||||
) ?: testGetRequestInfo()
|
||||
) ?: testCreatePasskeyRequestInfo()
|
||||
|
||||
providerEnabledList = when (requestInfo.type) {
|
||||
RequestInfo.TYPE_CREATE ->
|
||||
@@ -101,7 +99,7 @@ class CredentialManagerRepo(
|
||||
}
|
||||
|
||||
fun onOptionSelected(
|
||||
providerPackageName: String,
|
||||
providerId: String,
|
||||
entryKey: String,
|
||||
entrySubkey: String,
|
||||
resultCode: Int? = null,
|
||||
@@ -109,7 +107,7 @@ class CredentialManagerRepo(
|
||||
) {
|
||||
val userSelectionDialogResult = UserSelectionDialogResult(
|
||||
requestInfo.token,
|
||||
providerPackageName,
|
||||
providerId,
|
||||
entryKey,
|
||||
entrySubkey,
|
||||
if (resultCode != null) ProviderPendingIntentResponse(resultCode, resultData) else null
|
||||
@@ -138,36 +136,15 @@ class CredentialManagerRepo(
|
||||
val providerDisabledList = CreateFlowUtils.toDisabledProviderList(
|
||||
// Handle runtime cast error
|
||||
providerDisabledList, context)
|
||||
var defaultProvider: EnabledProviderInfo? = null
|
||||
var remoteEntry: RemoteInfo? = null
|
||||
var createOptionSize = 0
|
||||
var lastSeenProviderWithNonEmptyCreateOptions: EnabledProviderInfo? = null
|
||||
providerEnabledList.forEach{providerInfo -> providerInfo.createOptions =
|
||||
providerInfo.createOptions.sortedWith(compareBy { it.lastUsedTimeMillis }).reversed()
|
||||
if (providerInfo.isDefault) {defaultProvider = providerInfo}
|
||||
if (providerInfo.remoteEntry != null) {
|
||||
remoteEntry = providerInfo.remoteEntry!!
|
||||
}
|
||||
if (providerInfo.createOptions.isNotEmpty()) {
|
||||
createOptionSize += providerInfo.createOptions.size
|
||||
lastSeenProviderWithNonEmptyCreateOptions = providerInfo
|
||||
}
|
||||
}
|
||||
return CreateCredentialUiState(
|
||||
enabledProviders = providerEnabledList,
|
||||
disabledProviders = providerDisabledList,
|
||||
CreateFlowUtils.toCreateScreenState(
|
||||
createOptionSize, false,
|
||||
requestDisplayInfo, defaultProvider, remoteEntry),
|
||||
requestDisplayInfo,
|
||||
false,
|
||||
CreateFlowUtils.toActiveEntry(
|
||||
/*defaultProvider=*/defaultProvider, createOptionSize,
|
||||
lastSeenProviderWithNonEmptyCreateOptions, remoteEntry),
|
||||
)
|
||||
return CreateFlowUtils.toCreateCredentialUiState(
|
||||
providerEnabledList, providerDisabledList, requestDisplayInfo, false)
|
||||
}
|
||||
|
||||
companion object {
|
||||
// TODO: find a way to resolve this static field leak problem
|
||||
lateinit var repo: CredentialManagerRepo
|
||||
|
||||
fun setup(
|
||||
@@ -198,7 +175,6 @@ class CredentialManagerRepo(
|
||||
.setRemoteEntry(
|
||||
newRemoteEntry("key2", "subkey-1")
|
||||
)
|
||||
.setIsDefaultProvider(true)
|
||||
.build(),
|
||||
CreateCredentialProviderData
|
||||
.Builder("com.dashlane")
|
||||
|
||||
@@ -43,6 +43,7 @@ class CredentialSelectorActivity : ComponentActivity() {
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
CredentialManagerRepo.setup(this, intent)
|
||||
UserConfigRepo.setup(this)
|
||||
val requestInfo = CredentialManagerRepo.getInstance().requestInfo
|
||||
setContent {
|
||||
CredentialSelectorTheme {
|
||||
|
||||
@@ -31,6 +31,8 @@ import com.android.credentialmanager.createflow.RequestDisplayInfo
|
||||
import com.android.credentialmanager.createflow.EnabledProviderInfo
|
||||
import com.android.credentialmanager.createflow.CreateScreenState
|
||||
import com.android.credentialmanager.createflow.ActiveEntry
|
||||
import com.android.credentialmanager.createflow.DisabledProviderInfo
|
||||
import com.android.credentialmanager.createflow.CreateCredentialUiState
|
||||
import com.android.credentialmanager.getflow.ActionEntryInfo
|
||||
import com.android.credentialmanager.getflow.AuthenticationEntryInfo
|
||||
import com.android.credentialmanager.getflow.CredentialEntryInfo
|
||||
@@ -208,14 +210,13 @@ class CreateFlowUtils {
|
||||
val pkgInfo = packageManager
|
||||
.getPackageInfo(packageName!!,
|
||||
PackageManager.PackageInfoFlags.of(0))
|
||||
com.android.credentialmanager.createflow.EnabledProviderInfo(
|
||||
EnabledProviderInfo(
|
||||
// TODO: decide what to do when failed to load a provider icon
|
||||
icon = pkgInfo.applicationInfo.loadIcon(packageManager)!!,
|
||||
name = it.providerFlattenedComponentName,
|
||||
displayName = pkgInfo.applicationInfo.loadLabel(packageManager).toString(),
|
||||
createOptions = toCreationOptionInfoList(
|
||||
it.providerFlattenedComponentName, it.saveEntries, requestDisplayInfo, context),
|
||||
isDefault = it.isDefaultProvider,
|
||||
remoteEntry = toRemoteInfo(it.providerFlattenedComponentName, it.remoteEntry),
|
||||
)
|
||||
}
|
||||
@@ -256,8 +257,7 @@ class CreateFlowUtils {
|
||||
createCredentialRequestJetpack.password,
|
||||
createCredentialRequestJetpack.type,
|
||||
requestInfo.appPackageName,
|
||||
context.getDrawable(R.drawable.ic_password)!!,
|
||||
requestInfo.isFirstUsage
|
||||
context.getDrawable(R.drawable.ic_password)!!
|
||||
)
|
||||
}
|
||||
is CreatePublicKeyCredentialRequest -> {
|
||||
@@ -275,8 +275,7 @@ class CreateFlowUtils {
|
||||
displayName,
|
||||
createCredentialRequestJetpack.type,
|
||||
requestInfo.appPackageName,
|
||||
context.getDrawable(R.drawable.ic_passkey)!!,
|
||||
requestInfo.isFirstUsage)
|
||||
context.getDrawable(R.drawable.ic_passkey)!!)
|
||||
}
|
||||
// TODO: correctly parsing for other sign-ins
|
||||
else -> {
|
||||
@@ -285,20 +284,60 @@ class CreateFlowUtils {
|
||||
"Elisa Beckett",
|
||||
"other-sign-ins",
|
||||
requestInfo.appPackageName,
|
||||
context.getDrawable(R.drawable.ic_other_sign_in)!!,
|
||||
requestInfo.isFirstUsage)
|
||||
context.getDrawable(R.drawable.ic_other_sign_in)!!)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun toCreateScreenState(
|
||||
fun toCreateCredentialUiState(
|
||||
enabledProviders: List<EnabledProviderInfo>,
|
||||
disabledProviders: List<DisabledProviderInfo>?,
|
||||
requestDisplayInfo: RequestDisplayInfo,
|
||||
isOnPasskeyIntroStateAlready: Boolean,
|
||||
): CreateCredentialUiState {
|
||||
var createOptionSize = 0
|
||||
var lastSeenProviderWithNonEmptyCreateOptions: EnabledProviderInfo? = null
|
||||
var remoteEntry: RemoteInfo? = null
|
||||
var defaultProvider: EnabledProviderInfo? = null
|
||||
val defaultProviderId = UserConfigRepo.getInstance().getDefaultProviderId()
|
||||
enabledProviders.forEach {
|
||||
enabledProvider ->
|
||||
if (defaultProviderId != null) {
|
||||
if (enabledProvider.name == defaultProviderId) {
|
||||
defaultProvider = enabledProvider
|
||||
}
|
||||
}
|
||||
if (enabledProvider.createOptions.isNotEmpty()) {
|
||||
createOptionSize += enabledProvider.createOptions.size
|
||||
lastSeenProviderWithNonEmptyCreateOptions = enabledProvider
|
||||
}
|
||||
if (enabledProvider.remoteEntry != null) {
|
||||
remoteEntry = enabledProvider.remoteEntry!!
|
||||
}
|
||||
}
|
||||
return CreateCredentialUiState(
|
||||
enabledProviders = enabledProviders,
|
||||
disabledProviders = disabledProviders,
|
||||
toCreateScreenState(
|
||||
createOptionSize, isOnPasskeyIntroStateAlready,
|
||||
requestDisplayInfo, defaultProvider, remoteEntry),
|
||||
requestDisplayInfo,
|
||||
isOnPasskeyIntroStateAlready,
|
||||
toActiveEntry(
|
||||
/*defaultProvider=*/defaultProvider, createOptionSize,
|
||||
lastSeenProviderWithNonEmptyCreateOptions, remoteEntry),
|
||||
)
|
||||
}
|
||||
|
||||
private fun toCreateScreenState(
|
||||
createOptionSize: Int,
|
||||
isOnPasskeyIntroStateAlready: Boolean,
|
||||
requestDisplayInfo: RequestDisplayInfo,
|
||||
defaultProvider: EnabledProviderInfo?,
|
||||
remoteEntry: RemoteInfo?,
|
||||
): CreateScreenState {
|
||||
return if (requestDisplayInfo.isFirstUsage && requestDisplayInfo
|
||||
return if (
|
||||
UserConfigRepo.getInstance().getIsFirstUse() && requestDisplayInfo
|
||||
.type == TYPE_PUBLIC_KEY_CREDENTIAL && !isOnPasskeyIntroStateAlready) {
|
||||
CreateScreenState.PASSKEY_INTRO
|
||||
} else if (
|
||||
@@ -313,12 +352,12 @@ class CreateFlowUtils {
|
||||
} else if (createOptionSize == 0 && remoteEntry != null) {
|
||||
CreateScreenState.EXTERNAL_ONLY_SELECTION
|
||||
} else {
|
||||
// TODO: properly handle error and gracefully finish itself
|
||||
throw java.lang.IllegalStateException("Empty provider list.")
|
||||
// TODO: properly handle error and gracefully finish itself
|
||||
throw java.lang.IllegalStateException("Empty provider list.")
|
||||
}
|
||||
}
|
||||
|
||||
fun toActiveEntry(
|
||||
private fun toActiveEntry(
|
||||
defaultProvider: EnabledProviderInfo?,
|
||||
createOptionSize: Int,
|
||||
lastSeenProviderWithNonEmptyCreateOptions: EnabledProviderInfo?,
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* 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
|
||||
|
||||
import android.content.Context
|
||||
import android.content.SharedPreferences
|
||||
|
||||
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 setIsFirstUse(
|
||||
isFirstUse: Boolean
|
||||
) {
|
||||
sharedPreferences.edit().apply {
|
||||
putBoolean(IS_PASSKEY_FIRST_USE, isFirstUse)
|
||||
apply()
|
||||
}
|
||||
}
|
||||
|
||||
fun getDefaultProviderId(): String? {
|
||||
return sharedPreferences.getString(DEFAULT_PROVIDER, null)
|
||||
}
|
||||
|
||||
fun getIsFirstUse(): Boolean {
|
||||
return sharedPreferences.getBoolean(IS_PASSKEY_FIRST_USE, true)
|
||||
}
|
||||
|
||||
companion object {
|
||||
lateinit var repo: UserConfigRepo
|
||||
|
||||
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"
|
||||
|
||||
fun setup(
|
||||
context: Context,
|
||||
) {
|
||||
repo = UserConfigRepo(context)
|
||||
}
|
||||
|
||||
fun getInstance(): UserConfigRepo {
|
||||
return repo
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -108,7 +108,8 @@ fun CreateCredentialScreen(
|
||||
)
|
||||
CreateScreenState.MORE_OPTIONS_ROW_INTRO -> MoreOptionsRowIntroCard(
|
||||
providerInfo = uiState.activeEntry?.activeProvider!!,
|
||||
onDefaultOrNotSelected = viewModel::onDefaultOrNotSelected
|
||||
onChangeDefaultSelected = viewModel::onChangeDefaultSelected,
|
||||
onUseOnceSelected = viewModel::onUseOnceSelected,
|
||||
)
|
||||
CreateScreenState.EXTERNAL_ONLY_SELECTION -> ExternalOnlySelectionCard(
|
||||
requestDisplayInfo = uiState.requestDisplayInfo,
|
||||
@@ -464,7 +465,8 @@ fun MoreOptionsSelectionCard(
|
||||
@Composable
|
||||
fun MoreOptionsRowIntroCard(
|
||||
providerInfo: EnabledProviderInfo,
|
||||
onDefaultOrNotSelected: () -> Unit,
|
||||
onChangeDefaultSelected: () -> Unit,
|
||||
onUseOnceSelected: () -> Unit,
|
||||
) {
|
||||
ContainerCard() {
|
||||
Column() {
|
||||
@@ -496,11 +498,11 @@ fun MoreOptionsRowIntroCard(
|
||||
) {
|
||||
CancelButton(
|
||||
stringResource(R.string.use_once),
|
||||
onClick = onDefaultOrNotSelected
|
||||
onClick = onUseOnceSelected
|
||||
)
|
||||
ConfirmButton(
|
||||
stringResource(R.string.set_as_default),
|
||||
onClick = onDefaultOrNotSelected
|
||||
onClick = onChangeDefaultSelected
|
||||
)
|
||||
}
|
||||
Divider(
|
||||
|
||||
@@ -29,6 +29,7 @@ import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
import com.android.credentialmanager.CreateFlowUtils
|
||||
import com.android.credentialmanager.CredentialManagerRepo
|
||||
import com.android.credentialmanager.UserConfigRepo
|
||||
import com.android.credentialmanager.common.DialogResult
|
||||
import com.android.credentialmanager.common.ProviderActivityResult
|
||||
import com.android.credentialmanager.common.ResultState
|
||||
@@ -61,32 +62,15 @@ class CreateCredentialViewModel(
|
||||
}
|
||||
|
||||
fun onConfirmIntro() {
|
||||
var createOptionSize = 0
|
||||
var lastSeenProviderWithNonEmptyCreateOptions: EnabledProviderInfo? = null
|
||||
var remoteEntry: RemoteInfo? = null
|
||||
uiState.enabledProviders.forEach {
|
||||
enabledProvider ->
|
||||
if (enabledProvider.createOptions.isNotEmpty()) {
|
||||
createOptionSize += enabledProvider.createOptions.size
|
||||
lastSeenProviderWithNonEmptyCreateOptions = enabledProvider
|
||||
}
|
||||
if (enabledProvider.remoteEntry != null) {
|
||||
remoteEntry = enabledProvider.remoteEntry!!
|
||||
}
|
||||
}
|
||||
uiState = uiState.copy(
|
||||
currentScreenState = CreateFlowUtils.toCreateScreenState(
|
||||
createOptionSize, true,
|
||||
uiState.requestDisplayInfo, null, remoteEntry),
|
||||
showActiveEntryOnly = createOptionSize > 1,
|
||||
activeEntry = CreateFlowUtils.toActiveEntry(
|
||||
null, createOptionSize, lastSeenProviderWithNonEmptyCreateOptions, remoteEntry),
|
||||
)
|
||||
uiState = CreateFlowUtils.toCreateCredentialUiState(
|
||||
uiState.enabledProviders, uiState.disabledProviders,
|
||||
uiState.requestDisplayInfo, true)
|
||||
UserConfigRepo.getInstance().setIsFirstUse(false)
|
||||
}
|
||||
|
||||
fun getProviderInfoByName(providerName: String): EnabledProviderInfo {
|
||||
return uiState.enabledProviders.single {
|
||||
it.name.equals(providerName)
|
||||
it.name == providerName
|
||||
}
|
||||
}
|
||||
|
||||
@@ -116,6 +100,8 @@ class CreateCredentialViewModel(
|
||||
showActiveEntryOnly = true,
|
||||
activeEntry = activeEntry
|
||||
)
|
||||
val providerId = uiState.activeEntry?.activeProvider?.name
|
||||
onDefaultChanged(providerId)
|
||||
}
|
||||
|
||||
fun onDisabledPasswordManagerSelected() {
|
||||
@@ -127,11 +113,29 @@ class CreateCredentialViewModel(
|
||||
dialogResult.value = DialogResult(ResultState.CANCELED)
|
||||
}
|
||||
|
||||
fun onDefaultOrNotSelected() {
|
||||
fun onChangeDefaultSelected() {
|
||||
uiState = uiState.copy(
|
||||
currentScreenState = CreateScreenState.CREATION_OPTION_SELECTION,
|
||||
)
|
||||
// TODO: implement the if choose as default or not logic later
|
||||
val providerId = uiState.activeEntry?.activeProvider?.name
|
||||
onDefaultChanged(providerId)
|
||||
}
|
||||
|
||||
fun onUseOnceSelected() {
|
||||
uiState = uiState.copy(
|
||||
currentScreenState = CreateScreenState.CREATION_OPTION_SELECTION,
|
||||
)
|
||||
}
|
||||
|
||||
fun onDefaultChanged(providerId: String?) {
|
||||
if (providerId != null) {
|
||||
Log.d(
|
||||
"Account Selector", "Default provider changed to: " +
|
||||
" {provider=$providerId")
|
||||
UserConfigRepo.getInstance().setDefaultProvider(providerId)
|
||||
} else {
|
||||
Log.w("Account Selector", "Null provider is being changed")
|
||||
}
|
||||
}
|
||||
|
||||
fun onEntrySelected(selectedEntry: EntryInfo) {
|
||||
|
||||
@@ -31,7 +31,6 @@ class EnabledProviderInfo(
|
||||
name: String,
|
||||
displayName: String,
|
||||
var createOptions: List<CreateOptionInfo>,
|
||||
val isDefault: Boolean,
|
||||
var remoteEntry: RemoteInfo?,
|
||||
) : ProviderInfo(icon, name, displayName)
|
||||
|
||||
@@ -77,7 +76,6 @@ data class RequestDisplayInfo(
|
||||
val type: String,
|
||||
val appDomainName: String,
|
||||
val typeIcon: Drawable,
|
||||
val isFirstUsage: Boolean,
|
||||
)
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user