Merge "Launch provider pending intent from the CredentialManager UI."
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt b/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt
index 7e69987..a6e64ce 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt
@@ -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.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 @@
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 @@
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 @@
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 @@
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 @@
return Entry(
key,
subkey,
- slice.build()
+ slice.build(),
+ pendingIntent,
+ null
)
}
@@ -360,10 +379,22 @@
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("[email protected]", "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 @@
return Entry(
key,
subkey,
- slice
+ slice,
+ pendingIntent,
+ fillInIntent,
)
}
@@ -415,7 +448,6 @@
}
private fun testGetRequestInfo(): RequestInfo {
- val data = Bundle()
return RequestInfo.newGetRequestInfo(
Binder(),
GetCredentialRequest.Builder()
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorActivity.kt b/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorActivity.kt
index 1041a33..d324f87 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorActivity.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorActivity.kt
@@ -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 @@
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 @@
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 @@
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")
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt b/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt
index fad9364..bea1e59 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt
@@ -46,8 +46,15 @@
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 @@
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 @@
providerId = providerId,
entryKey = authEntry.key,
entrySubkey = authEntry.subkey,
+ pendingIntent = authEntry.pendingIntent,
+ fillInIntent = authEntry.frameworkExtrasIntent,
title = providerDisplayName,
icon = providerIcon,
)
@@ -127,6 +138,8 @@
providerId = providerId,
entryKey = remoteEntry.key,
entrySubkey = remoteEntry.subkey,
+ pendingIntent = remoteEntry.pendingIntent,
+ fillInIntent = remoteEntry.frameworkExtrasIntent,
)
}
@@ -142,6 +155,8 @@
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 @@
// 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 @@
RemoteInfo(
entryKey = remoteEntry.key,
entrySubkey = remoteEntry.subkey,
+ pendingIntent = remoteEntry.pendingIntent,
+ fillInIntent = remoteEntry.frameworkExtrasIntent,
)
} else null
}
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/common/ProviderActivityResult.kt b/packages/CredentialManager/src/com/android/credentialmanager/common/ProviderActivityResult.kt
new file mode 100644
index 0000000..9e33d51
--- /dev/null
+++ b/packages/CredentialManager/src/com/android/credentialmanager/common/ProviderActivityResult.kt
@@ -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?,
+)
\ No newline at end of file
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt b/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt
index 27d366d..3a277a6 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt
@@ -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 @@
@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 @@
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
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialViewModel.kt b/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialViewModel.kt
index 6be019f..093c88f 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialViewModel.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialViewModel.kt
@@ -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.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 @@
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 @@
uiState = uiState.copy(
currentScreenState = CreateScreenState.CREATION_OPTION_SELECTION,
activeEntry = ActiveEntry(getProviderInfoByName(providerName),
- getProviderInfoByName(providerName).createOptions.first())
+ getProviderInfoByName(providerName).createOptions.first()
+ )
)
}
@@ -119,22 +125,56 @@
// 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,
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateModel.kt b/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateModel.kt
index 1ab234a..753dc3c 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateModel.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateModel.kt
@@ -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 @@
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 @@
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,
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialComponents.kt b/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialComponents.kt
index 19a032f..db0c16c 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialComponents.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialComponents.kt
@@ -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 @@
@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 @@
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,
)
}
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialViewModel.kt b/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialViewModel.kt
index 22370a9..6dea9c2 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialViewModel.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialViewModel.kt
@@ -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.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 @@
val currentScreenState: GetScreenState,
val requestDisplayInfo: RequestDisplayInfo,
val providerDisplayInfo: ProviderDisplayInfo = toProviderDisplayInfo(providerInfoList),
+ val selectedEntry: EntryInfo? = null,
)
class GetCredentialViewModel(
@@ -51,14 +56,42 @@
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)
}
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetModel.kt b/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetModel.kt
index 76d9847..0c3baff 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetModel.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetModel.kt
@@ -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 @@
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 @@
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,