Change the entry id to key + subkey.

Test: deployed locally
Bug: 247855226
Bug: 253156958
Change-Id: I514c26a7db6c1ca77cadf5f214f165d26ec2fc24
This commit is contained in:
Helen Qin
2022-10-24 18:50:10 +00:00
parent e363a3fea1
commit 1bf852b1b9
10 changed files with 121 additions and 103 deletions

View File

@@ -82,32 +82,46 @@ public class Entry implements Parcelable {
public static final String EXTRA_ENTRY_AUTHENTICATION_ACTION =
"android.credentials.ui.extra.ENTRY_AUTHENTICATION_ACTION";
// TODO: change to string key + string subkey.
private final int mId;
@NonNull private final String mKey;
@NonNull private final String mSubkey;
@NonNull
private final Slice mSlice;
protected Entry(@NonNull Parcel in) {
int entryId = in.readInt();
String key = in.readString8();
String subkey = in.readString8();
Slice slice = Slice.CREATOR.createFromParcel(in);
mId = entryId;
mKey = key;
AnnotationValidations.validate(NonNull.class, null, mKey);
mSubkey = subkey;
AnnotationValidations.validate(NonNull.class, null, mSubkey);
mSlice = slice;
AnnotationValidations.validate(NonNull.class, null, mSlice);
}
public Entry(int id, @NonNull Slice slice) {
mId = id;
public Entry(@NonNull String key, @NonNull String subkey, @NonNull Slice slice) {
mKey = key;
mSubkey = subkey;
mSlice = slice;
}
/**
* Returns the id of this entry that's unique within the context of the CredentialManager
* Returns the identifier of this entry that's unique within the context of the CredentialManager
* request.
*/
public int getEntryId() {
return mId;
@NonNull
public String getKey() {
return mKey;
}
/**
* Returns the sub-identifier of this entry that's unique within the context of the {@code key}.
*/
@NonNull
public String getSubkey() {
return mSubkey;
}
/**
@@ -120,7 +134,8 @@ public class Entry implements Parcelable {
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeInt(mId);
dest.writeString8(mKey);
dest.writeString8(mSubkey);
mSlice.writeToParcel(dest, flags);
}

View File

@@ -54,18 +54,17 @@ public class UserSelectionDialogResult extends BaseDialogResult implements Parce
private static final String EXTRA_USER_SELECTION_RESULT =
"android.credentials.ui.extra.USER_SELECTION_RESULT";
@NonNull
private final String mProviderId;
// TODO: consider switching to string or other types, depending on the service implementation.
private final int mEntryId;
@NonNull private final String mProviderId;
@NonNull private final String mEntryKey;
@NonNull private final String mEntrySubkey;
public UserSelectionDialogResult(
@NonNull IBinder requestToken, @NonNull String providerId,
int entryId) {
@NonNull String entryKey, @NonNull String entrySubkey) {
super(requestToken);
mProviderId = providerId;
mEntryId = entryId;
mEntryKey = entryKey;
mEntrySubkey = entrySubkey;
}
/** Returns provider package name whose entry was selected by the user. */
@@ -74,26 +73,38 @@ public class UserSelectionDialogResult extends BaseDialogResult implements Parce
return mProviderId;
}
/** Returns the id of the visual entry that the user selected. */
public int getEntryId() {
return mEntryId;
/** Returns the key of the visual entry that the user selected. */
@NonNull
public String getEntryKey() {
return mEntryKey;
}
/** Returns the subkey of the visual entry that the user selected. */
@NonNull
public String getEntrySubkey() {
return mEntrySubkey;
}
protected UserSelectionDialogResult(@NonNull Parcel in) {
super(in);
String providerId = in.readString8();
int entryId = in.readInt();
String entryKey = in.readString8();
String entrySubkey = in.readString8();
mProviderId = providerId;
AnnotationValidations.validate(NonNull.class, null, mProviderId);
mEntryId = entryId;
mEntryKey = entryKey;
AnnotationValidations.validate(NonNull.class, null, mEntryKey);
mEntrySubkey = entrySubkey;
AnnotationValidations.validate(NonNull.class, null, mEntrySubkey);
}
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
super.writeToParcel(dest, flags);
dest.writeString8(mProviderId);
dest.writeInt(mEntryId);
dest.writeString8(mEntryKey);
dest.writeString8(mEntrySubkey);
}
@Override

View File

@@ -71,11 +71,12 @@ class CredentialManagerRepo(
resultReceiver?.send(BaseDialogResult.RESULT_CODE_DIALOG_CANCELED, resultData)
}
fun onOptionSelected(providerPackageName: String, entryId: Int) {
fun onOptionSelected(providerPackageName: String, entryKey: String, entrySubkey: String) {
val userSelectionDialogResult = UserSelectionDialogResult(
requestInfo.token,
providerPackageName,
entryId
entryKey,
entrySubkey
)
val resultData = Bundle()
UserSelectionDialogResult.addToBundle(userSelectionDialogResult, resultData)
@@ -137,16 +138,16 @@ class CredentialManagerRepo(
Icon.createWithResource(context, R.drawable.ic_launcher_foreground))
.setCredentialEntries(
listOf<Entry>(
newEntry(1, "elisa.beckett@gmail.com", "Elisa Backett",
"20 passwords and 7 passkeys saved"),
newEntry(2, "elisa.work@google.com", "Elisa Backett Work",
"20 passwords and 7 passkeys saved"),
newEntry("key1", "subkey-1", "elisa.beckett@gmail.com",
"Elisa Backett", "20 passwords and 7 passkeys saved"),
newEntry("key1", "subkey-2", "elisa.work@google.com",
"Elisa Backett Work", "20 passwords and 7 passkeys saved"),
)
).setActionChips(
listOf<Entry>(
newEntry(3, "Go to Settings", "",
newEntry("key2", "subkey-1", "Go to Settings", "",
"20 passwords and 7 passkeys saved"),
newEntry(4, "Switch Account", "",
newEntry("key2", "subkey-2", "Switch Account", "",
"20 passwords and 7 passkeys saved"),
),
).build(),
@@ -156,21 +157,28 @@ class CredentialManagerRepo(
Icon.createWithResource(context, R.drawable.ic_launcher_foreground))
.setCredentialEntries(
listOf<Entry>(
newEntry(1, "elisa.beckett@dashlane.com", "Elisa Backett",
"20 passwords and 7 passkeys saved"),
newEntry(2, "elisa.work@dashlane.com", "Elisa Backett Work",
"20 passwords and 7 passkeys saved"),
newEntry("key1", "subkey-3", "elisa.beckett@dashlane.com",
"Elisa Backett", "20 passwords and 7 passkeys saved"),
newEntry("key1", "subkey-4", "elisa.work@dashlane.com",
"Elisa Backett Work", "20 passwords and 7 passkeys saved"),
)
).setActionChips(
listOf<Entry>(
newEntry(3, "Manage Accounts", "Manage your accounts in the dashlane app",
newEntry("key2", "subkey-3", "Manage Accounts",
"Manage your accounts in the dashlane app",
"20 passwords and 7 passkeys saved"),
),
).build(),
)
}
private fun newEntry(id: Int, title: String, subtitle: String, usageData: String): Entry {
private fun newEntry(
key: String,
subkey: String,
title: String,
subtitle: String,
usageData: String
): Entry {
val slice = Slice.Builder(
Entry.CREDENTIAL_MANAGER_ENTRY_URI, SliceSpec(Entry.VERSION, 1)
)
@@ -183,7 +191,8 @@ class CredentialManagerRepo(
.addText(usageData, Slice.SUBTYPE_MESSAGE, listOf(Entry.HINT_SUBTITLE))
.build()
return Entry(
id,
key,
subkey,
slice
)
}

View File

@@ -59,7 +59,8 @@ class GetFlowUtils {
?: context.getDrawable(R.drawable.ic_passkey)!!,
title = credentialEntryUi.userName.toString(),
subtitle = credentialEntryUi.displayName?.toString() ?: "Unknown display name",
id = it.entryId,
entryKey = it.key,
entrySubkey = it.subkey,
usageData = credentialEntryUi.usageData?.toString() ?: "Unknown usageData",
)
}
@@ -99,7 +100,8 @@ class CreateFlowUtils {
?: context.getDrawable(R.drawable.ic_passkey)!!,
title = saveEntryUi.title.toString(),
subtitle = saveEntryUi.subTitle?.toString() ?: "Unknown subtitle",
id = it.entryId,
entryKey = it.key,
entrySubkey = it.subkey,
usageData = saveEntryUi.usageData?.toString() ?: "Unknown usageData",
)
}

View File

@@ -30,7 +30,8 @@ data class CreateOptionInfo(
val icon: Drawable,
val title: String,
val subtitle: String,
val id: Int,
val entryKey: String,
val entrySubkey: String,
val usageData: String
)

View File

@@ -63,32 +63,32 @@ fun CreatePasskeyScreen(
val uiState = viewModel.uiState
when (uiState.currentScreenState) {
CreateScreenState.PASSKEY_INTRO -> ConfirmationCard(
onConfirm = {viewModel.onConfirmIntro()},
onCancel = {viewModel.onCancel()},
onConfirm = viewModel::onConfirmIntro,
onCancel = viewModel::onCancel,
)
CreateScreenState.PROVIDER_SELECTION -> ProviderSelectionCard(
providerList = uiState.providers,
onCancel = {viewModel.onCancel()},
onProviderSelected = {viewModel.onProviderSelected(it)}
onCancel = viewModel::onCancel,
onProviderSelected = viewModel::onProviderSelected
)
CreateScreenState.CREATION_OPTION_SELECTION -> CreationSelectionCard(
requestDisplayInfo = uiState.requestDisplayInfo,
providerInfo = uiState.activeEntry?.activeProvider!!,
createOptionInfo = uiState.activeEntry.activeCreateOptionInfo,
onOptionSelected = {viewModel.onPrimaryCreateOptionInfoSelected()},
onConfirm = {viewModel.onPrimaryCreateOptionInfoSelected()},
onCancel = {viewModel.onCancel()},
onOptionSelected = viewModel::onPrimaryCreateOptionInfoSelected,
onConfirm = viewModel::onPrimaryCreateOptionInfoSelected,
onCancel = viewModel::onCancel,
multiProvider = uiState.providers.size > 1,
onMoreOptionsSelected = {viewModel.onMoreOptionsSelected()}
onMoreOptionsSelected = viewModel::onMoreOptionsSelected
)
CreateScreenState.MORE_OPTIONS_SELECTION -> MoreOptionsSelectionCard(
providerList = uiState.providers,
onBackButtonSelected = {viewModel.onBackButtonSelected()},
onOptionSelected = {viewModel.onMoreOptionsRowSelected(it)}
onBackButtonSelected = viewModel::onBackButtonSelected,
onOptionSelected = viewModel::onMoreOptionsRowSelected
)
CreateScreenState.MORE_OPTIONS_ROW_INTRO -> MoreOptionsRowIntroCard(
providerInfo = uiState.activeEntry?.activeProvider!!,
onDefaultOrNotSelected = {viewModel.onDefaultOrNotSelected()}
onDefaultOrNotSelected = viewModel::onDefaultOrNotSelected
)
}
},
@@ -491,40 +491,6 @@ fun CreationSelectionCard(
}
}
@ExperimentalMaterialApi
@Composable
fun CreateOptionRow(createOptionInfo: CreateOptionInfo, onOptionSelected: (Int) -> Unit) {
Chip(
modifier = Modifier.fillMaxWidth(),
onClick = {onOptionSelected(createOptionInfo.id)},
leadingIcon = {
Image(modifier = Modifier.size(24.dp, 24.dp).padding(start = 10.dp),
bitmap = createOptionInfo.icon.toBitmap().asImageBitmap(),
// painter = painterResource(R.drawable.ic_passkey),
// TODO: add description.
contentDescription = "")
},
colors = ChipDefaults.chipColors(
backgroundColor = Grey100,
leadingIconContentColor = Grey100
),
shape = Shapes.large
) {
Column() {
Text(
text = createOptionInfo.title,
style = Typography.h6,
modifier = Modifier.padding(top = 16.dp)
)
Text(
text = createOptionInfo.subtitle,
style = Typography.body2,
modifier = Modifier.padding(bottom = 16.dp)
)
}
}
}
@ExperimentalMaterialApi
@Composable
fun PrimaryCreateOptionRow(

View File

@@ -73,11 +73,15 @@ class CreatePasskeyViewModel(
)
}
fun onCreateOptionSelected(createOptionId: Int) {
Log.d("Account Selector", "Option selected for creation: $createOptionId")
fun onCreateOptionSelected(entryKey: String, entrySubkey: String) {
Log.d(
"Account Selector",
"Option selected for creation: {key = $entryKey, subkey = $entrySubkey}"
)
CredentialManagerRepo.getInstance().onOptionSelected(
uiState.activeEntry?.activeProvider!!.name,
createOptionId
entryKey,
entrySubkey
)
dialogResult.value = DialogResult(
ResultState.COMPLETE,
@@ -122,13 +126,21 @@ class CreatePasskeyViewModel(
}
fun onPrimaryCreateOptionInfoSelected() {
var createOptionId = uiState.activeEntry?.activeCreateOptionInfo?.id
Log.d("Account Selector", "Option selected for creation: $createOptionId")
if (createOptionId != null) {
var createOptionEntryKey = uiState.activeEntry?.activeCreateOptionInfo?.entryKey
var createOptionEntrySubkey = uiState.activeEntry?.activeCreateOptionInfo?.entrySubkey
Log.d(
"Account Selector",
"Option selected for creation: " +
"{key = $createOptionEntryKey, subkey = $createOptionEntrySubkey}"
)
if (createOptionEntryKey != null && createOptionEntrySubkey != null) {
CredentialManagerRepo.getInstance().onOptionSelected(
uiState.activeEntry?.activeProvider!!.name,
createOptionId
createOptionEntryKey,
createOptionEntrySubkey
)
} else {
TODO("Gracefully handle illegal state.")
}
dialogResult.value = DialogResult(
ResultState.COMPLETE,

View File

@@ -68,10 +68,10 @@ fun GetCredentialScreen(
GetScreenState.CREDENTIAL_SELECTION -> CredentialSelectionCard(
requestDisplayInfo = uiState.requestDisplayInfo,
providerInfo = uiState.selectedProvider!!,
onCancel = {viewModel.onCancel()},
onOptionSelected = {viewModel.onCredentailSelected(it)},
onCancel = viewModel::onCancel,
onOptionSelected = viewModel::onCredentailSelected,
multiProvider = uiState.providers.size > 1,
onMoreOptionSelected = {viewModel.onMoreOptionSelected()},
onMoreOptionSelected = viewModel::onMoreOptionSelected,
)
}
},
@@ -90,7 +90,7 @@ fun GetCredentialScreen(
fun CredentialSelectionCard(
requestDisplayInfo: RequestDisplayInfo,
providerInfo: ProviderInfo,
onOptionSelected: (Int) -> Unit,
onOptionSelected: (String, String) -> Unit,
onCancel: () -> Unit,
multiProvider: Boolean,
onMoreOptionSelected: () -> Unit,
@@ -165,11 +165,11 @@ fun CredentialSelectionCard(
@Composable
fun CredentialOptionRow(
credentialOptionInfo: CredentialOptionInfo,
onOptionSelected: (Int) -> Unit
onOptionSelected: (String, String) -> Unit,
) {
Chip(
modifier = Modifier.fillMaxWidth(),
onClick = {onOptionSelected(credentialOptionInfo.id)},
onClick = {onOptionSelected(credentialOptionInfo.entryKey, credentialOptionInfo.entrySubkey)},
leadingIcon = {
Image(modifier = Modifier.size(24.dp, 24.dp).padding(start = 10.dp),
bitmap = credentialOptionInfo.icon.toBitmap().asImageBitmap(),

View File

@@ -49,11 +49,12 @@ class GetCredentialViewModel(
return dialogResult
}
fun onCredentailSelected(credentialId: Int) {
Log.d("Account Selector", "credential selected: $credentialId")
fun onCredentailSelected(entryKey: String, entrySubkey: String) {
Log.d("Account Selector", "credential selected: {key=$entryKey,subkey=$entrySubkey}")
CredentialManagerRepo.getInstance().onOptionSelected(
uiState.selectedProvider!!.name,
credentialId
entryKey,
entrySubkey
)
dialogResult.value = DialogResult(
ResultState.COMPLETE,

View File

@@ -30,7 +30,8 @@ data class CredentialOptionInfo(
val icon: Drawable,
val title: String,
val subtitle: String,
val id: Int,
val entryKey: String,
val entrySubkey: String,
val usageData: String
)