Launch provider pending intent from the CredentialManager UI.

Test: locally
Bug: 246564035
Bug: 253156924
Bug: 253156958

Change-Id: Id56070db3fbbbef642fa126e23fdbfe619ef69d7
This commit is contained in:
Helen Qin
2022-11-17 19:50:01 +00:00
parent 42051221a0
commit 3e94495777
10 changed files with 247 additions and 60 deletions

View File

@@ -17,6 +17,7 @@
package com.android.credentialmanager
import android.credentials.Credential.TYPE_PASSWORD_CREDENTIAL
import android.app.PendingIntent
import android.app.slice.Slice
import android.app.slice.SliceSpec
import android.content.Context
@@ -32,6 +33,7 @@ import android.credentials.ui.DisabledProviderData
import android.credentials.ui.ProviderData
import android.credentials.ui.RequestInfo
import android.credentials.ui.BaseDialogResult
import android.credentials.ui.ProviderPendingIntentResponse
import android.credentials.ui.UserSelectionDialogResult
import android.graphics.drawable.Icon
import android.os.Binder
@@ -54,7 +56,7 @@ class CredentialManagerRepo(
private val context: Context,
intent: Intent,
) {
private val requestInfo: RequestInfo
val requestInfo: RequestInfo
private val providerEnabledList: List<ProviderData>
private val providerDisabledList: List<DisabledProviderData>
// TODO: require non-null.
@@ -75,7 +77,7 @@ class CredentialManagerRepo(
RequestInfo.TYPE_GET ->
intent.extras?.getParcelableArrayList(
ProviderData.EXTRA_ENABLED_PROVIDER_DATA_LIST,
DisabledProviderData::class.java
GetCredentialProviderData::class.java
) ?: testGetCredentialProviderList()
else -> {
// TODO: fail gracefully
@@ -101,12 +103,19 @@ class CredentialManagerRepo(
resultReceiver?.send(BaseDialogResult.RESULT_CODE_DIALOG_CANCELED, resultData)
}
fun onOptionSelected(providerPackageName: String, entryKey: String, entrySubkey: String) {
fun onOptionSelected(
providerPackageName: String,
entryKey: String,
entrySubkey: String,
resultCode: Int? = null,
resultData: Intent? = null,
) {
val userSelectionDialogResult = UserSelectionDialogResult(
requestInfo.token,
providerPackageName,
entryKey,
entrySubkey
entrySubkey,
if (resultCode != null) ProviderPendingIntentResponse(resultCode, resultData) else null
)
val resultData = Bundle()
UserSelectionDialogResult.addToBundle(userSelectionDialogResult, resultData)
@@ -328,6 +337,14 @@ class CredentialManagerRepo(
userDisplayName: String?,
lastUsedTimeMillis: Long?,
): 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 slice = Slice.Builder(
Entry.CREDENTIAL_MANAGER_ENTRY_URI, SliceSpec(credentialType, 1)
).addText(
@@ -347,7 +364,9 @@ class CredentialManagerRepo(
return Entry(
key,
subkey,
slice.build()
slice.build(),
pendingIntent,
null
)
}
@@ -360,10 +379,22 @@ class CredentialManagerRepo(
totalCredentialCount: Int,
lastUsedTimeMillis: Long,
): 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 createPasswordRequest = android.service.credentials.CreateCredentialRequest(
context.applicationInfo.packageName,
"PASSWORD",
toBundle("beckett-bakert@gmail.com", "password123")
)
val fillInIntent = Intent().putExtra("create_request_params", createPasswordRequest)
val slice = Slice.Builder(
Entry.CREDENTIAL_MANAGER_ENTRY_URI, SliceSpec(Entry.VERSION, 1)
)
.addText(
).addText(
providerDisplayName, null, listOf(Entry.HINT_USER_PROVIDER_ACCOUNT_NAME))
.addIcon(
Icon.createWithResource(context, R.drawable.ic_passkey),
@@ -384,7 +415,9 @@ class CredentialManagerRepo(
return Entry(
key,
subkey,
slice
slice,
pendingIntent,
fillInIntent,
)
}
@@ -415,7 +448,6 @@ class CredentialManagerRepo(
}
private fun testGetRequestInfo(): RequestInfo {
val data = Bundle()
return RequestInfo.newGetRequestInfo(
Binder(),
GetCredentialRequest.Builder()

View File

@@ -16,17 +16,21 @@
package com.android.credentialmanager
import android.credentials.ui.RequestInfo
import android.os.Bundle
import android.util.Log
import androidx.activity.ComponentActivity
import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.compose.setContent
import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.material.ExperimentalMaterialApi
import androidx.compose.runtime.Composable
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.lifecycle.Observer
import androidx.lifecycle.viewmodel.compose.viewModel
import com.android.credentialmanager.common.DialogType
import com.android.credentialmanager.common.DialogResult
import com.android.credentialmanager.common.ProviderActivityResult
import com.android.credentialmanager.common.ResultState
import com.android.credentialmanager.createflow.CreateCredentialScreen
import com.android.credentialmanager.createflow.CreateCredentialViewModel
@@ -39,28 +43,23 @@ class CredentialSelectorActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
CredentialManagerRepo.setup(this, intent)
val requestInfo = intent.extras?.getParcelable<RequestInfo>(RequestInfo.EXTRA_REQUEST_INFO)
if (requestInfo != null) {
val requestType = requestInfo.type
setContent {
CredentialSelectorTheme {
CredentialManagerBottomSheet(requestType)
}
}
} else {
// TODO: prototype only code to be removed. In production should exit.
setContent {
CredentialSelectorTheme {
CredentialManagerBottomSheet(RequestInfo.TYPE_CREATE)
}
val requestInfo = CredentialManagerRepo.getInstance().requestInfo
setContent {
CredentialSelectorTheme {
CredentialManagerBottomSheet(DialogType.toDialogType(requestInfo.type))
}
}
}
@ExperimentalMaterialApi
@Composable
fun CredentialManagerBottomSheet(operationType: String) {
val dialogType = DialogType.toDialogType(operationType)
fun CredentialManagerBottomSheet(dialogType: DialogType) {
val providerActivityResult = remember { mutableStateOf<ProviderActivityResult?>(null) }
val launcher = rememberLauncherForActivityResult(
ActivityResultContracts.StartIntentSenderForResult()
) {
providerActivityResult.value = ProviderActivityResult(it.resultCode, it.data)
}
when (dialogType) {
DialogType.CREATE_PASSKEY -> {
val viewModel: CreateCredentialViewModel = viewModel()
@@ -68,7 +67,10 @@ class CredentialSelectorActivity : ComponentActivity() {
this@CredentialSelectorActivity,
onCancel
)
CreateCredentialScreen(viewModel = viewModel)
providerActivityResult.value?.let {
viewModel.onProviderActivityResult(it)
}
CreateCredentialScreen(viewModel = viewModel, providerActivityLauncher = launcher)
}
DialogType.GET_CREDENTIALS -> {
val viewModel: GetCredentialViewModel = viewModel()
@@ -76,7 +78,10 @@ class CredentialSelectorActivity : ComponentActivity() {
this@CredentialSelectorActivity,
onCancel
)
GetCredentialScreen(viewModel = viewModel)
providerActivityResult.value?.let {
viewModel.onProviderActivityResult(it)
}
GetCredentialScreen(viewModel = viewModel, providerActivityLauncher = launcher)
}
else -> {
Log.w("AccountSelector", "Unknown type, not rendering any UI")

View File

@@ -46,8 +46,15 @@ class GetFlowUtils {
val packageManager = context.packageManager
return providerDataList.map {
// TODO: get from the actual service info
val componentName = ComponentName.unflattenFromString(it.providerFlattenedComponentName)
var packageName = componentName?.packageName
if (componentName == null) {
// TODO: Remove once test data is fixed
packageName = it.providerFlattenedComponentName
}
val pkgInfo = packageManager
.getPackageInfo(it.providerFlattenedComponentName,
.getPackageInfo(packageName!!,
PackageManager.PackageInfoFlags.of(0))
val providerDisplayName = pkgInfo.applicationInfo.loadLabel(packageManager).toString()
// TODO: decide what to do when failed to load a provider icon
@@ -86,6 +93,8 @@ class GetFlowUtils {
providerId = providerId,
entryKey = it.key,
entrySubkey = it.subkey,
pendingIntent = it.pendingIntent,
fillInIntent = it.frameworkExtrasIntent,
credentialType = credentialEntryUi.credentialType.toString(),
credentialTypeDisplayName = credentialEntryUi.credentialTypeDisplayName.toString(),
userName = credentialEntryUi.userName.toString(),
@@ -113,6 +122,8 @@ class GetFlowUtils {
providerId = providerId,
entryKey = authEntry.key,
entrySubkey = authEntry.subkey,
pendingIntent = authEntry.pendingIntent,
fillInIntent = authEntry.frameworkExtrasIntent,
title = providerDisplayName,
icon = providerIcon,
)
@@ -127,6 +138,8 @@ class GetFlowUtils {
providerId = providerId,
entryKey = remoteEntry.key,
entrySubkey = remoteEntry.subkey,
pendingIntent = remoteEntry.pendingIntent,
fillInIntent = remoteEntry.frameworkExtrasIntent,
)
}
@@ -142,6 +155,8 @@ class GetFlowUtils {
providerId = providerId,
entryKey = it.key,
entrySubkey = it.subkey,
pendingIntent = it.pendingIntent,
fillInIntent = it.frameworkExtrasIntent,
title = actionEntryUi.text.toString(),
// TODO: gracefully fail
icon = actionEntryUi.icon.loadDrawable(context)!!,
@@ -214,6 +229,8 @@ class CreateFlowUtils {
// TODO: remove fallbacks
entryKey = it.key,
entrySubkey = it.subkey,
pendingIntent = it.pendingIntent,
fillInIntent = it.frameworkExtrasIntent,
userProviderDisplayName = saveEntryUi.userProviderAccountName as String,
credentialTypeIcon = saveEntryUi.credentialTypeIcon?.loadDrawable(context)
?: context.getDrawable(R.drawable.ic_passkey)!!,
@@ -235,6 +252,8 @@ class CreateFlowUtils {
RemoteInfo(
entryKey = remoteEntry.key,
entrySubkey = remoteEntry.subkey,
pendingIntent = remoteEntry.pendingIntent,
fillInIntent = remoteEntry.frameworkExtrasIntent,
)
} else null
}

View File

@@ -0,0 +1,24 @@
/*
* 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.common
import android.content.Intent
data class ProviderActivityResult(
val resultCode: Int,
val data: Intent?,
)

View File

@@ -3,6 +3,9 @@
package com.android.credentialmanager.createflow
import android.credentials.Credential.TYPE_PASSWORD_CREDENTIAL
import androidx.activity.compose.ManagedActivityResultLauncher
import androidx.activity.result.ActivityResult
import androidx.activity.result.IntentSenderRequest
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
@@ -50,7 +53,11 @@ import com.android.credentialmanager.jetpack.developer.PublicKeyCredential.Compa
@Composable
fun CreateCredentialScreen(
viewModel: CreateCredentialViewModel,
providerActivityLauncher: ManagedActivityResultLauncher<IntentSenderRequest, ActivityResult>
) {
val primaryEntryCallback: () -> Unit = {
viewModel.onPrimaryCreateOptionInfoSelected(providerActivityLauncher)
}
val state = rememberModalBottomSheetState(
initialValue = ModalBottomSheetValue.Expanded,
skipHalfExpanded = true
@@ -73,8 +80,8 @@ fun CreateCredentialScreen(
requestDisplayInfo = uiState.requestDisplayInfo,
providerInfo = uiState.activeEntry?.activeProvider!!,
createOptionInfo = uiState.activeEntry.activeEntryInfo as CreateOptionInfo,
onOptionSelected = viewModel::onPrimaryCreateOptionInfoSelected,
onConfirm = viewModel::onPrimaryCreateOptionInfoSelected,
onOptionSelected = primaryEntryCallback,
onConfirm = primaryEntryCallback,
onCancel = viewModel::onCancel,
multiProvider = uiState.enabledProviders.size > 1,
onMoreOptionsSelected = viewModel::onMoreOptionsSelected

View File

@@ -17,6 +17,9 @@
package com.android.credentialmanager.createflow
import android.util.Log
import androidx.activity.compose.ManagedActivityResultLauncher
import androidx.activity.result.ActivityResult
import androidx.activity.result.IntentSenderRequest
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
@@ -25,6 +28,7 @@ import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import com.android.credentialmanager.CredentialManagerRepo
import com.android.credentialmanager.common.DialogResult
import com.android.credentialmanager.common.ProviderActivityResult
import com.android.credentialmanager.common.ResultState
data class CreateCredentialUiState(
@@ -59,7 +63,8 @@ class CreateCredentialViewModel(
uiState = uiState.copy(
currentScreenState = CreateScreenState.CREATION_OPTION_SELECTION,
activeEntry = ActiveEntry(uiState.enabledProviders.first(),
uiState.enabledProviders.first().createOptions.first())
uiState.enabledProviders.first().createOptions.first()
)
)
} else {
throw java.lang.IllegalStateException("Empty provider list.")
@@ -70,7 +75,8 @@ class CreateCredentialViewModel(
uiState = uiState.copy(
currentScreenState = CreateScreenState.CREATION_OPTION_SELECTION,
activeEntry = ActiveEntry(getProviderInfoByName(providerName),
getProviderInfoByName(providerName).createOptions.first())
getProviderInfoByName(providerName).createOptions.first()
)
)
}
@@ -119,22 +125,56 @@ class CreateCredentialViewModel(
// TODO: implement the if choose as default or not logic later
}
fun onPrimaryCreateOptionInfoSelected() {
val entryKey = uiState.activeEntry?.activeEntryInfo?.entryKey
val entrySubkey = uiState.activeEntry?.activeEntryInfo?.entrySubkey
Log.d(
"Account Selector",
"Option selected for creation: " +
"{key = $entryKey, subkey = $entrySubkey}"
)
if (entryKey != null && entrySubkey != null) {
fun onPrimaryCreateOptionInfoSelected(
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,
)
}
} else {
dialogResult.value = DialogResult(
ResultState.COMPLETE,
)
TODO("Gracefully handle illegal state.")
}
}
fun onProviderActivityResult(providerActivityResult: ProviderActivityResult) {
val entry = uiState.activeEntry?.activeEntryInfo
val resultCode = providerActivityResult.resultCode
val resultData = providerActivityResult.data
val providerId = uiState.activeEntry?.activeProvider!!.name
if (entry != null) {
Log.d("Account Selector", "Got provider activity result: {provider=" +
"$providerId, key=${entry.entryKey}, subkey=${entry.entrySubkey}, " +
"resultCode=$resultCode, resultData=$resultData}"
)
CredentialManagerRepo.getInstance().onOptionSelected(
uiState.activeEntry?.activeProvider!!.name,
entryKey,
entrySubkey
providerId, entry.entryKey, entry.entrySubkey, resultCode, resultData,
)
} else {
TODO("Gracefully handle illegal state.")
Log.w("Account Selector",
"Illegal state: received a provider result but found no matching entry.")
}
dialogResult.value = DialogResult(
ResultState.COMPLETE,

View File

@@ -16,6 +16,8 @@
package com.android.credentialmanager.createflow
import android.app.PendingIntent
import android.content.Intent
import android.graphics.drawable.Drawable
open class ProviderInfo(
@@ -42,11 +44,15 @@ class DisabledProviderInfo(
open class EntryInfo (
val entryKey: String,
val entrySubkey: String,
val pendingIntent: PendingIntent?,
val fillInIntent: Intent?,
)
class CreateOptionInfo(
entryKey: String,
entrySubkey: String,
pendingIntent: PendingIntent?,
fillInIntent: Intent?,
val userProviderDisplayName: String?,
val credentialTypeIcon: Drawable,
val profileIcon: Drawable,
@@ -54,12 +60,14 @@ class CreateOptionInfo(
val passkeyCount: Int?,
val totalCredentialCount: Int?,
val lastUsedTimeMillis: Long?,
) : EntryInfo(entryKey, entrySubkey)
) : EntryInfo(entryKey, entrySubkey, pendingIntent, fillInIntent)
class RemoteInfo(
entryKey: String,
entrySubkey: String,
) : EntryInfo(entryKey, entrySubkey)
pendingIntent: PendingIntent?,
fillInIntent: Intent?,
) : EntryInfo(entryKey, entrySubkey, pendingIntent, fillInIntent)
data class RequestDisplayInfo(
val title: String,

View File

@@ -17,6 +17,9 @@
package com.android.credentialmanager.getflow
import android.text.TextUtils
import androidx.activity.compose.ManagedActivityResultLauncher
import androidx.activity.result.ActivityResult
import androidx.activity.result.IntentSenderRequest
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.Arrangement
@@ -62,7 +65,11 @@ import com.android.credentialmanager.jetpack.developer.PublicKeyCredential
@Composable
fun GetCredentialScreen(
viewModel: GetCredentialViewModel,
providerActivityLauncher: ManagedActivityResultLauncher<IntentSenderRequest, ActivityResult>
) {
val entrySelectionCallback: (EntryInfo) -> Unit = {
viewModel.onEntrySelected(it, providerActivityLauncher)
}
val state = rememberModalBottomSheetState(
initialValue = ModalBottomSheetValue.Expanded,
skipHalfExpanded = true
@@ -75,14 +82,14 @@ fun GetCredentialScreen(
GetScreenState.PRIMARY_SELECTION -> PrimarySelectionCard(
requestDisplayInfo = uiState.requestDisplayInfo,
providerDisplayInfo = uiState.providerDisplayInfo,
onEntrySelected = viewModel::onEntrySelected,
onEntrySelected = entrySelectionCallback,
onCancel = viewModel::onCancel,
onMoreOptionSelected = viewModel::onMoreOptionSelected,
)
GetScreenState.ALL_SIGN_IN_OPTIONS -> AllSignInOptionCard(
providerInfoList = uiState.providerInfoList,
providerDisplayInfo = uiState.providerDisplayInfo,
onEntrySelected = viewModel::onEntrySelected,
onEntrySelected = entrySelectionCallback,
onBackButtonClicked = viewModel::onBackToPrimarySelectionScreen,
)
}

View File

@@ -17,6 +17,9 @@
package com.android.credentialmanager.getflow
import android.util.Log
import androidx.activity.compose.ManagedActivityResultLauncher
import androidx.activity.result.ActivityResult
import androidx.activity.result.IntentSenderRequest
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
@@ -25,6 +28,7 @@ import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import com.android.credentialmanager.CredentialManagerRepo
import com.android.credentialmanager.common.DialogResult
import com.android.credentialmanager.common.ProviderActivityResult
import com.android.credentialmanager.common.ResultState
import com.android.credentialmanager.jetpack.developer.PublicKeyCredential
import com.android.internal.util.Preconditions
@@ -34,6 +38,7 @@ data class GetCredentialUiState(
val currentScreenState: GetScreenState,
val requestDisplayInfo: RequestDisplayInfo,
val providerDisplayInfo: ProviderDisplayInfo = toProviderDisplayInfo(providerInfoList),
val selectedEntry: EntryInfo? = null,
)
class GetCredentialViewModel(
@@ -51,14 +56,42 @@ class GetCredentialViewModel(
return dialogResult
}
fun onEntrySelected(entry: EntryInfo) {
fun onEntrySelected(
entry: EntryInfo,
launcher: ManagedActivityResultLauncher<IntentSenderRequest, ActivityResult>
) {
Log.d("Account Selector", "credential selected:" +
" {provider=${entry.providerId}, key=${entry.entryKey}, subkey=${entry.entrySubkey}}")
CredentialManagerRepo.getInstance().onOptionSelected(
entry.providerId,
entry.entryKey,
entry.entrySubkey
)
if (entry.pendingIntent != null) {
uiState = uiState.copy(selectedEntry = entry)
val intentSenderRequest = IntentSenderRequest.Builder(entry.pendingIntent)
.setFillInIntent(entry.fillInIntent).build()
launcher.launch(intentSenderRequest)
} else {
CredentialManagerRepo.getInstance().onOptionSelected(
entry.providerId, entry.entryKey, entry.entrySubkey,
)
dialogResult.value = DialogResult(ResultState.COMPLETE)
}
}
fun onProviderActivityResult(providerActivityResult: ProviderActivityResult) {
val entry = uiState.selectedEntry
val resultCode = providerActivityResult.resultCode
val resultData = providerActivityResult.data
if (entry != null) {
Log.d("Account Selector", "Got provider activity result: {provider=" +
"${entry.providerId}, key=${entry.entryKey}, subkey=${entry.entrySubkey}, " +
"resultCode=$resultCode, resultData=$resultData}"
)
CredentialManagerRepo.getInstance().onOptionSelected(
entry.providerId, entry.entryKey, entry.entrySubkey,
resultCode, resultData,
)
} else {
Log.w("Account Selector",
"Illegal state: received a provider result but found no matching entry.")
}
dialogResult.value = DialogResult(ResultState.COMPLETE)
}

View File

@@ -16,6 +16,8 @@
package com.android.credentialmanager.getflow
import android.app.PendingIntent
import android.content.Intent
import android.graphics.drawable.Drawable
data class ProviderInfo(
@@ -49,12 +51,16 @@ abstract class EntryInfo (
val providerId: String,
val entryKey: String,
val entrySubkey: String,
val pendingIntent: PendingIntent?,
val fillInIntent: Intent?,
)
class CredentialEntryInfo(
providerId: String,
entryKey: String,
entrySubkey: String,
pendingIntent: PendingIntent?,
fillInIntent: Intent?,
/** Type of this credential used for sorting. Not localized so must not be directly displayed. */
val credentialType: String,
/** Localized type value of this credential used for display purpose. */
@@ -63,30 +69,36 @@ class CredentialEntryInfo(
val displayName: String?,
val icon: Drawable,
val lastUsedTimeMillis: Long?,
) : EntryInfo(providerId, entryKey, entrySubkey)
) : EntryInfo(providerId, entryKey, entrySubkey, pendingIntent, fillInIntent)
class AuthenticationEntryInfo(
providerId: String,
entryKey: String,
entrySubkey: String,
pendingIntent: PendingIntent?,
fillInIntent: Intent?,
val title: String,
val icon: Drawable,
) : EntryInfo(providerId, entryKey, entrySubkey)
) : EntryInfo(providerId, entryKey, entrySubkey, pendingIntent, fillInIntent)
class RemoteEntryInfo(
providerId: String,
entryKey: String,
entrySubkey: String,
) : EntryInfo(providerId, entryKey, entrySubkey)
pendingIntent: PendingIntent?,
fillInIntent: Intent?,
) : EntryInfo(providerId, entryKey, entrySubkey, pendingIntent, fillInIntent)
class ActionEntryInfo(
providerId: String,
entryKey: String,
entrySubkey: String,
pendingIntent: PendingIntent?,
fillInIntent: Intent?,
val title: String,
val icon: Drawable,
val subTitle: String?,
) : EntryInfo(providerId, entryKey, entrySubkey)
) : EntryInfo(providerId, entryKey, entrySubkey, pendingIntent, fillInIntent)
data class RequestDisplayInfo(
val appDomainName: String,