Merge "Support authentication entry with locked & unlocked state."
This commit is contained in:
@@ -1033,6 +1033,22 @@ package android.content.rollback {
|
||||
|
||||
package android.credentials.ui {
|
||||
|
||||
public final class AuthenticationEntry implements android.os.Parcelable {
|
||||
ctor public AuthenticationEntry(@NonNull String, @NonNull String, @NonNull android.app.slice.Slice, int);
|
||||
ctor public AuthenticationEntry(@NonNull String, @NonNull String, @NonNull android.app.slice.Slice, int, @NonNull android.content.Intent);
|
||||
method public int describeContents();
|
||||
method @Nullable public android.content.Intent getFrameworkExtrasIntent();
|
||||
method @NonNull public String getKey();
|
||||
method @NonNull public android.app.slice.Slice getSlice();
|
||||
method @NonNull public int getStatus();
|
||||
method @NonNull public String getSubkey();
|
||||
method public void writeToParcel(@NonNull android.os.Parcel, int);
|
||||
field @NonNull public static final android.os.Parcelable.Creator<android.credentials.ui.AuthenticationEntry> CREATOR;
|
||||
field public static final int STATUS_LOCKED = 0; // 0x0
|
||||
field public static final int STATUS_UNLOCKED_BUT_EMPTY_LESS_RECENT = 1; // 0x1
|
||||
field public static final int STATUS_UNLOCKED_BUT_EMPTY_MOST_RECENT = 2; // 0x2
|
||||
}
|
||||
|
||||
public final class CreateCredentialProviderData extends android.credentials.ui.ProviderData implements android.os.Parcelable {
|
||||
ctor public CreateCredentialProviderData(@NonNull String, @NonNull java.util.List<android.credentials.ui.Entry>, @Nullable android.credentials.ui.Entry);
|
||||
method @Nullable public android.credentials.ui.Entry getRemoteEntry();
|
||||
@@ -1064,15 +1080,12 @@ package android.credentials.ui {
|
||||
method @NonNull public String getSubkey();
|
||||
method public void writeToParcel(@NonNull android.os.Parcel, int);
|
||||
field @NonNull public static final android.os.Parcelable.Creator<android.credentials.ui.Entry> CREATOR;
|
||||
field @NonNull public static final String EXTRA_ENTRY_AUTHENTICATION_ACTION = "android.credentials.ui.extra.ENTRY_AUTHENTICATION_ACTION";
|
||||
field @NonNull public static final String EXTRA_ENTRY_LIST_ACTION_CHIP = "android.credentials.ui.extra.ENTRY_LIST_ACTION_CHIP";
|
||||
field @NonNull public static final String EXTRA_ENTRY_LIST_CREDENTIAL = "android.credentials.ui.extra.ENTRY_LIST_CREDENTIAL";
|
||||
}
|
||||
|
||||
public final class GetCredentialProviderData extends android.credentials.ui.ProviderData implements android.os.Parcelable {
|
||||
ctor public GetCredentialProviderData(@NonNull String, @NonNull java.util.List<android.credentials.ui.Entry>, @NonNull java.util.List<android.credentials.ui.Entry>, @NonNull java.util.List<android.credentials.ui.Entry>, @Nullable android.credentials.ui.Entry);
|
||||
ctor public GetCredentialProviderData(@NonNull String, @NonNull java.util.List<android.credentials.ui.Entry>, @NonNull java.util.List<android.credentials.ui.Entry>, @NonNull java.util.List<android.credentials.ui.AuthenticationEntry>, @Nullable android.credentials.ui.Entry);
|
||||
method @NonNull public java.util.List<android.credentials.ui.Entry> getActionChips();
|
||||
method @NonNull public java.util.List<android.credentials.ui.Entry> getAuthenticationEntries();
|
||||
method @NonNull public java.util.List<android.credentials.ui.AuthenticationEntry> getAuthenticationEntries();
|
||||
method @NonNull public java.util.List<android.credentials.ui.Entry> getCredentialEntries();
|
||||
method @Nullable public android.credentials.ui.Entry getRemoteEntry();
|
||||
field @NonNull public static final android.os.Parcelable.Creator<android.credentials.ui.GetCredentialProviderData> CREATOR;
|
||||
@@ -1082,7 +1095,7 @@ package android.credentials.ui {
|
||||
ctor public GetCredentialProviderData.Builder(@NonNull String);
|
||||
method @NonNull public android.credentials.ui.GetCredentialProviderData build();
|
||||
method @NonNull public android.credentials.ui.GetCredentialProviderData.Builder setActionChips(@NonNull java.util.List<android.credentials.ui.Entry>);
|
||||
method @NonNull public android.credentials.ui.GetCredentialProviderData.Builder setAuthenticationEntries(@NonNull java.util.List<android.credentials.ui.Entry>);
|
||||
method @NonNull public android.credentials.ui.GetCredentialProviderData.Builder setAuthenticationEntries(@NonNull java.util.List<android.credentials.ui.AuthenticationEntry>);
|
||||
method @NonNull public android.credentials.ui.GetCredentialProviderData.Builder setCredentialEntries(@NonNull java.util.List<android.credentials.ui.Entry>);
|
||||
method @NonNull public android.credentials.ui.GetCredentialProviderData.Builder setRemoteEntry(@Nullable android.credentials.ui.Entry);
|
||||
}
|
||||
|
||||
162
core/java/android/credentials/ui/AuthenticationEntry.java
Normal file
162
core/java/android/credentials/ui/AuthenticationEntry.java
Normal file
@@ -0,0 +1,162 @@
|
||||
/*
|
||||
* Copyright 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 android.credentials.ui;
|
||||
|
||||
import android.annotation.IntDef;
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.annotation.SuppressLint;
|
||||
import android.annotation.TestApi;
|
||||
import android.app.slice.Slice;
|
||||
import android.content.Intent;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
|
||||
import com.android.internal.util.AnnotationValidations;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
|
||||
/**
|
||||
* An authentication entry.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@TestApi
|
||||
public final class AuthenticationEntry implements Parcelable {
|
||||
@NonNull private final String mKey;
|
||||
@NonNull private final String mSubkey;
|
||||
@NonNull private final @Status int mStatus;
|
||||
@Nullable private Intent mFrameworkExtrasIntent;
|
||||
@NonNull private final Slice mSlice;
|
||||
|
||||
/** @hide **/
|
||||
@IntDef(prefix = {"STATUS_"}, value = {
|
||||
STATUS_LOCKED,
|
||||
STATUS_UNLOCKED_BUT_EMPTY_LESS_RECENT,
|
||||
STATUS_UNLOCKED_BUT_EMPTY_MOST_RECENT,
|
||||
})
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
public @interface Status {}
|
||||
|
||||
/** This entry is still locked, as initially supplied by the provider. */
|
||||
public static final int STATUS_LOCKED = 0;
|
||||
/** This entry was unlocked but didn't contain any credential. Meanwhile, "less recent" means
|
||||
* there is another such entry that was unlocked more recently. */
|
||||
public static final int STATUS_UNLOCKED_BUT_EMPTY_LESS_RECENT = 1;
|
||||
/** This is the most recent entry that was unlocked but didn't contain any credential.
|
||||
* There should be at most one authentication entry with this status. */
|
||||
public static final int STATUS_UNLOCKED_BUT_EMPTY_MOST_RECENT = 2;
|
||||
|
||||
private AuthenticationEntry(@NonNull Parcel in) {
|
||||
mKey = in.readString8();
|
||||
mSubkey = in.readString8();
|
||||
mStatus = in.readInt();
|
||||
mSlice = in.readTypedObject(Slice.CREATOR);
|
||||
mFrameworkExtrasIntent = in.readTypedObject(Intent.CREATOR);
|
||||
|
||||
AnnotationValidations.validate(NonNull.class, null, mKey);
|
||||
AnnotationValidations.validate(NonNull.class, null, mSubkey);
|
||||
AnnotationValidations.validate(NonNull.class, null, mSlice);
|
||||
}
|
||||
|
||||
/** Constructor to be used for an entry that does not require further activities
|
||||
* to be invoked when selected.
|
||||
*/
|
||||
public AuthenticationEntry(@NonNull String key, @NonNull String subkey, @NonNull Slice slice,
|
||||
@Status int status) {
|
||||
mKey = key;
|
||||
mSubkey = subkey;
|
||||
mSlice = slice;
|
||||
mStatus = status;
|
||||
}
|
||||
|
||||
/** Constructor to be used for an entry that requires a pending intent to be invoked
|
||||
* when clicked.
|
||||
*/
|
||||
public AuthenticationEntry(@NonNull String key, @NonNull String subkey, @NonNull Slice slice,
|
||||
@Status int status, @NonNull Intent intent) {
|
||||
this(key, subkey, slice, status);
|
||||
mFrameworkExtrasIntent = intent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the identifier of this entry that's unique within the context of the CredentialManager
|
||||
* request.
|
||||
*/
|
||||
@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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Slice to be rendered.
|
||||
*/
|
||||
@NonNull
|
||||
public Slice getSlice() {
|
||||
return mSlice;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the entry status.
|
||||
*/
|
||||
@NonNull
|
||||
@Status
|
||||
public int getStatus() {
|
||||
return mStatus;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@SuppressLint("IntentBuilderName") // Not building a new intent.
|
||||
public Intent getFrameworkExtrasIntent() {
|
||||
return mFrameworkExtrasIntent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(@NonNull Parcel dest, int flags) {
|
||||
dest.writeString8(mKey);
|
||||
dest.writeString8(mSubkey);
|
||||
dest.writeInt(mStatus);
|
||||
dest.writeTypedObject(mSlice, flags);
|
||||
dest.writeTypedObject(mFrameworkExtrasIntent, flags);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static final @NonNull Creator<AuthenticationEntry> CREATOR = new Creator<>() {
|
||||
@Override
|
||||
public AuthenticationEntry createFromParcel(@NonNull Parcel in) {
|
||||
return new AuthenticationEntry(in);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AuthenticationEntry[] newArray(int size) {
|
||||
return new AuthenticationEntry[size];
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -29,30 +29,12 @@ import android.os.Parcelable;
|
||||
import com.android.internal.util.AnnotationValidations;
|
||||
|
||||
/**
|
||||
* A credential, save, or action entry to be rendered.
|
||||
* A credential, create, or action entry to be rendered.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@TestApi
|
||||
public final class Entry implements Parcelable {
|
||||
/**
|
||||
* The intent extra key for the action chip {@code Entry} list when launching the UX activities.
|
||||
*/
|
||||
@NonNull public static final String EXTRA_ENTRY_LIST_ACTION_CHIP =
|
||||
"android.credentials.ui.extra.ENTRY_LIST_ACTION_CHIP";
|
||||
/**
|
||||
* The intent extra key for the credential / save {@code Entry} list when launching the UX
|
||||
* activities.
|
||||
*/
|
||||
@NonNull public static final String EXTRA_ENTRY_LIST_CREDENTIAL =
|
||||
"android.credentials.ui.extra.ENTRY_LIST_CREDENTIAL";
|
||||
/**
|
||||
* The intent extra key for the authentication action {@code Entry} when launching the UX
|
||||
* activities.
|
||||
*/
|
||||
@NonNull public static final String EXTRA_ENTRY_AUTHENTICATION_ACTION =
|
||||
"android.credentials.ui.extra.ENTRY_AUTHENTICATION_ACTION";
|
||||
|
||||
@NonNull private final String mKey;
|
||||
@NonNull private final String mSubkey;
|
||||
@Nullable private PendingIntent mPendingIntent;
|
||||
|
||||
@@ -39,13 +39,14 @@ public final class GetCredentialProviderData extends ProviderData implements Par
|
||||
@NonNull
|
||||
private final List<Entry> mActionChips;
|
||||
@NonNull
|
||||
private final List<Entry> mAuthenticationEntries;
|
||||
private final List<AuthenticationEntry> mAuthenticationEntries;
|
||||
@Nullable
|
||||
private final Entry mRemoteEntry;
|
||||
|
||||
public GetCredentialProviderData(
|
||||
@NonNull String providerFlattenedComponentName, @NonNull List<Entry> credentialEntries,
|
||||
@NonNull List<Entry> actionChips, @NonNull List<Entry> authenticationEntries,
|
||||
@NonNull List<Entry> actionChips,
|
||||
@NonNull List<AuthenticationEntry> authenticationEntries,
|
||||
@Nullable Entry remoteEntry) {
|
||||
super(providerFlattenedComponentName);
|
||||
mCredentialEntries = credentialEntries;
|
||||
@@ -65,7 +66,7 @@ public final class GetCredentialProviderData extends ProviderData implements Par
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public List<Entry> getAuthenticationEntries() {
|
||||
public List<AuthenticationEntry> getAuthenticationEntries() {
|
||||
return mAuthenticationEntries;
|
||||
}
|
||||
|
||||
@@ -87,8 +88,8 @@ public final class GetCredentialProviderData extends ProviderData implements Par
|
||||
mActionChips = actionChips;
|
||||
AnnotationValidations.validate(NonNull.class, null, mActionChips);
|
||||
|
||||
List<Entry> authenticationEntries = new ArrayList<>();
|
||||
in.readTypedList(authenticationEntries, Entry.CREATOR);
|
||||
List<AuthenticationEntry> authenticationEntries = new ArrayList<>();
|
||||
in.readTypedList(authenticationEntries, AuthenticationEntry.CREATOR);
|
||||
mAuthenticationEntries = authenticationEntries;
|
||||
AnnotationValidations.validate(NonNull.class, null, mAuthenticationEntries);
|
||||
|
||||
@@ -133,7 +134,7 @@ public final class GetCredentialProviderData extends ProviderData implements Par
|
||||
@NonNull private String mProviderFlattenedComponentName;
|
||||
@NonNull private List<Entry> mCredentialEntries = new ArrayList<>();
|
||||
@NonNull private List<Entry> mActionChips = new ArrayList<>();
|
||||
@NonNull private List<Entry> mAuthenticationEntries = new ArrayList<>();
|
||||
@NonNull private List<AuthenticationEntry> mAuthenticationEntries = new ArrayList<>();
|
||||
@Nullable private Entry mRemoteEntry = null;
|
||||
|
||||
/** Constructor with required properties. */
|
||||
@@ -157,7 +158,8 @@ public final class GetCredentialProviderData extends ProviderData implements Par
|
||||
|
||||
/** Sets the authentication entry to be displayed to the user. */
|
||||
@NonNull
|
||||
public Builder setAuthenticationEntries(@NonNull List<Entry> authenticationEntry) {
|
||||
public Builder setAuthenticationEntries(
|
||||
@NonNull List<AuthenticationEntry> authenticationEntry) {
|
||||
mAuthenticationEntries = authenticationEntry;
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -116,8 +116,10 @@
|
||||
<string name="get_dialog_heading_for_username">For <xliff:g id="username" example="becket@gmail.com">%1$s</xliff:g></string>
|
||||
<!-- Column heading for displaying locked (that is, the user needs to first authenticate via pin, fingerprint, faceId, etc.) sign-ins. [CHAR LIMIT=80] -->
|
||||
<string name="get_dialog_heading_locked_password_managers">Locked password managers</string>
|
||||
<!-- Explanatory sub/body text for an option entry to use a locked (that is, the user needs to first authenticate via pin, fingerprint, faceId, etc.) sign-in. [CHAR LIMIT=120] -->
|
||||
<string name="locked_credential_entry_label_subtext">Tap to unlock</string>
|
||||
<!-- Explanatory label for a button that takes the user to unlock a credential provider by authenticating via pin, fingerprint, faceId, etc. [CHAR LIMIT=120] -->
|
||||
<string name="locked_credential_entry_label_subtext_tap_to_unlock">Tap to unlock</string>
|
||||
<!-- Explanatory label for a disabled button explaining that this option isn't viable because it does not contain any available credential (e.g. password, passkey, etc.) for the user. [CHAR LIMIT=120] -->
|
||||
<string name="locked_credential_entry_label_subtext_no_sign_in">No sign-in info</string>
|
||||
<!-- Column heading for displaying action chips for managing sign-ins from each credential provider. [CHAR LIMIT=80] -->
|
||||
<string name="get_dialog_heading_manage_sign_ins">Manage sign-ins</string>
|
||||
<!-- Column heading for displaying option to use sign-ins saved on a different device. [CHAR LIMIT=80] -->
|
||||
|
||||
@@ -24,6 +24,7 @@ import android.credentials.CreateCredentialRequest
|
||||
import android.credentials.Credential.TYPE_PASSWORD_CREDENTIAL
|
||||
import android.credentials.CredentialOption
|
||||
import android.credentials.GetCredentialRequest
|
||||
import android.credentials.ui.AuthenticationEntry
|
||||
import android.credentials.ui.Constants
|
||||
import android.credentials.ui.Entry
|
||||
import android.credentials.ui.CreateCredentialProviderData
|
||||
@@ -291,9 +292,13 @@ class CredentialManagerRepo(
|
||||
).setAuthenticationEntries(
|
||||
listOf(
|
||||
GetTestUtils.newAuthenticationEntry(
|
||||
context, "key2", "subkey-1", "locked-user1@gmail.com"),
|
||||
context, "key2", "subkey-1", "locked-user1@gmail.com",
|
||||
AuthenticationEntry.STATUS_LOCKED
|
||||
),
|
||||
GetTestUtils.newAuthenticationEntry(
|
||||
context, "key2", "subkey-2", "locked-user2@gmail.com"),
|
||||
context, "key2", "subkey-2", "locked-user2@gmail.com",
|
||||
AuthenticationEntry.STATUS_UNLOCKED_BUT_EMPTY_MOST_RECENT
|
||||
),
|
||||
)
|
||||
).setActionChips(
|
||||
listOf(
|
||||
@@ -323,7 +328,9 @@ class CredentialManagerRepo(
|
||||
)
|
||||
).setAuthenticationEntries(
|
||||
listOf(GetTestUtils.newAuthenticationEntry(
|
||||
context, "key2", "subkey-1", "foo@email.com"))
|
||||
context, "key2", "subkey-1", "foo@email.com",
|
||||
AuthenticationEntry.STATUS_UNLOCKED_BUT_EMPTY_LESS_RECENT
|
||||
))
|
||||
).setActionChips(
|
||||
listOf(
|
||||
GetTestUtils.newActionEntry(
|
||||
|
||||
@@ -22,6 +22,7 @@ import android.content.ComponentName
|
||||
import android.content.Context
|
||||
import android.content.pm.PackageManager
|
||||
import android.credentials.Credential.TYPE_PASSWORD_CREDENTIAL
|
||||
import android.credentials.ui.AuthenticationEntry
|
||||
import android.credentials.ui.CreateCredentialProviderData
|
||||
import android.credentials.ui.DisabledProviderData
|
||||
import android.credentials.ui.Entry
|
||||
@@ -265,7 +266,7 @@ class GetFlowUtils {
|
||||
providerId: String,
|
||||
providerDisplayName: String,
|
||||
providerIcon: Drawable,
|
||||
authEntryList: List<Entry>,
|
||||
authEntryList: List<AuthenticationEntry>,
|
||||
): List<AuthenticationEntryInfo> {
|
||||
val result: MutableList<AuthenticationEntryInfo> = mutableListOf()
|
||||
authEntryList.forEach { entry ->
|
||||
@@ -287,6 +288,9 @@ class GetFlowUtils {
|
||||
fillInIntent = entry.frameworkExtrasIntent,
|
||||
title = title,
|
||||
icon = providerIcon,
|
||||
isUnlockedAndEmpty = entry.status != AuthenticationEntry.STATUS_LOCKED,
|
||||
isLastUnlocked =
|
||||
entry.status == AuthenticationEntry.STATUS_UNLOCKED_BUT_EMPTY_MOST_RECENT
|
||||
))
|
||||
}
|
||||
return result
|
||||
|
||||
@@ -22,6 +22,7 @@ import android.app.slice.SliceSpec
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.credentials.Credential.TYPE_PASSWORD_CREDENTIAL
|
||||
import android.credentials.ui.AuthenticationEntry
|
||||
import android.credentials.ui.Entry
|
||||
import android.net.Uri
|
||||
import android.provider.Settings
|
||||
@@ -39,7 +40,8 @@ class GetTestUtils {
|
||||
key: String,
|
||||
subkey: String,
|
||||
title: String,
|
||||
): Entry {
|
||||
status: Int
|
||||
): AuthenticationEntry {
|
||||
val slice = Slice.Builder(
|
||||
Uri.EMPTY, SliceSpec("AuthenticationAction", 0)
|
||||
)
|
||||
@@ -59,10 +61,11 @@ class GetTestUtils {
|
||||
null,
|
||||
listOf("androidx.credentials.provider.authenticationAction.SLICE_HINT_TITLE")
|
||||
)
|
||||
return Entry(
|
||||
return AuthenticationEntry(
|
||||
key,
|
||||
subkey,
|
||||
slice.build()
|
||||
slice.build(),
|
||||
status
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -840,7 +840,6 @@ fun PrimaryCreateOptionRow(
|
||||
},
|
||||
label = {
|
||||
Column() {
|
||||
// TODO: Add the function to hide/view password when the type is create password
|
||||
when (requestDisplayInfo.type) {
|
||||
CredentialType.PASSKEY -> {
|
||||
TextOnSurfaceVariant(
|
||||
|
||||
@@ -485,7 +485,6 @@ fun PerUserNameCredentials(
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun CredentialEntryRow(
|
||||
credentialEntryInfo: CredentialEntryInfo,
|
||||
@@ -498,15 +497,13 @@ fun CredentialEntryRow(
|
||||
Image(
|
||||
modifier = Modifier.padding(start = 10.dp).size(32.dp),
|
||||
bitmap = credentialEntryInfo.icon.toBitmap().asImageBitmap(),
|
||||
// TODO: add description.
|
||||
contentDescription = "",
|
||||
contentDescription = null,
|
||||
)
|
||||
} else {
|
||||
Icon(
|
||||
modifier = Modifier.padding(start = 10.dp).size(32.dp),
|
||||
painter = painterResource(R.drawable.ic_other_sign_in),
|
||||
// TODO: add description.
|
||||
contentDescription = "",
|
||||
contentDescription = null,
|
||||
tint = LocalAndroidColorScheme.current.colorAccentPrimaryVariant
|
||||
)
|
||||
}
|
||||
@@ -553,8 +550,7 @@ fun AuthenticationEntryRow(
|
||||
Image(
|
||||
modifier = Modifier.padding(start = 10.dp).size(32.dp),
|
||||
bitmap = authenticationEntryInfo.icon.toBitmap().asImageBitmap(),
|
||||
// TODO: add description.
|
||||
contentDescription = ""
|
||||
contentDescription = null
|
||||
)
|
||||
},
|
||||
label = {
|
||||
@@ -563,23 +559,28 @@ fun AuthenticationEntryRow(
|
||||
modifier = Modifier.fillMaxWidth().padding(horizontal = 5.dp),
|
||||
) {
|
||||
Column() {
|
||||
// TODO: fix the text values.
|
||||
TextOnSurfaceVariant(
|
||||
text = authenticationEntryInfo.title,
|
||||
style = MaterialTheme.typography.titleLarge,
|
||||
modifier = Modifier.padding(top = 16.dp)
|
||||
)
|
||||
TextSecondary(
|
||||
text = stringResource(R.string.locked_credential_entry_label_subtext),
|
||||
text = stringResource(
|
||||
if (authenticationEntryInfo.isUnlockedAndEmpty)
|
||||
R.string.locked_credential_entry_label_subtext_no_sign_in
|
||||
else R.string.locked_credential_entry_label_subtext_tap_to_unlock
|
||||
),
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
modifier = Modifier.padding(bottom = 16.dp)
|
||||
)
|
||||
}
|
||||
Icon(
|
||||
Icons.Outlined.Lock,
|
||||
null,
|
||||
Modifier.align(alignment = Alignment.CenterVertically).padding(end = 10.dp),
|
||||
)
|
||||
if (!authenticationEntryInfo.isUnlockedAndEmpty) {
|
||||
Icon(
|
||||
Icons.Outlined.Lock,
|
||||
null,
|
||||
Modifier.align(alignment = Alignment.CenterVertically).padding(end = 10.dp),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
@@ -596,8 +597,7 @@ fun ActionEntryRow(
|
||||
Image(
|
||||
modifier = Modifier.padding(start = 10.dp).size(24.dp),
|
||||
bitmap = actionEntryInfo.icon.toBitmap().asImageBitmap(),
|
||||
// TODO: add description.
|
||||
contentDescription = ""
|
||||
contentDescription = null,
|
||||
)
|
||||
},
|
||||
label = {
|
||||
|
||||
@@ -91,6 +91,11 @@ class AuthenticationEntryInfo(
|
||||
fillInIntent: Intent?,
|
||||
val title: String,
|
||||
val icon: Drawable,
|
||||
// The entry had been unlocked and turned out to be empty. Used to determine whether to
|
||||
// show "Tap to unlock" or "No sign-in info" for this entry.
|
||||
val isUnlockedAndEmpty: Boolean,
|
||||
// True if the entry was the last one unlocked. Used to show the no sign-in info snackbar.
|
||||
val isLastUnlocked: Boolean,
|
||||
) : BaseEntry(
|
||||
providerId,
|
||||
entryKey, entrySubkey,
|
||||
|
||||
@@ -24,6 +24,7 @@ import android.content.Intent;
|
||||
import android.credentials.CredentialOption;
|
||||
import android.credentials.GetCredentialException;
|
||||
import android.credentials.GetCredentialResponse;
|
||||
import android.credentials.ui.AuthenticationEntry;
|
||||
import android.credentials.ui.Entry;
|
||||
import android.credentials.ui.GetCredentialProviderData;
|
||||
import android.credentials.ui.ProviderPendingIntentResponse;
|
||||
@@ -278,16 +279,18 @@ public final class ProviderGetSession extends ProviderSession<BeginGetCredential
|
||||
return remoteEntry;
|
||||
}
|
||||
|
||||
private List<Entry> prepareUiAuthenticationEntries(
|
||||
private List<AuthenticationEntry> prepareUiAuthenticationEntries(
|
||||
@NonNull List<Action> authenticationEntries) {
|
||||
List<Entry> authenticationUiEntries = new ArrayList<>();
|
||||
List<AuthenticationEntry> authenticationUiEntries = new ArrayList<>();
|
||||
|
||||
// TODO: properly construct entries when they should have the unlocked status.
|
||||
for (Action authenticationAction : authenticationEntries) {
|
||||
String entryId = generateUniqueId();
|
||||
mUiAuthenticationEntries.put(entryId, authenticationAction);
|
||||
authenticationUiEntries.add(new Entry(
|
||||
authenticationUiEntries.add(new AuthenticationEntry(
|
||||
AUTHENTICATION_ACTION_ENTRY_KEY, entryId,
|
||||
authenticationAction.getSlice(),
|
||||
AuthenticationEntry.STATUS_LOCKED,
|
||||
setUpFillInIntentForAuthentication()));
|
||||
}
|
||||
return authenticationUiEntries;
|
||||
@@ -346,7 +349,7 @@ public final class ProviderGetSession extends ProviderSession<BeginGetCredential
|
||||
}
|
||||
|
||||
private GetCredentialProviderData prepareUiProviderData(List<Entry> actionEntries,
|
||||
List<Entry> credentialEntries, List<Entry> authenticationActionEntries,
|
||||
List<Entry> credentialEntries, List<AuthenticationEntry> authenticationActionEntries,
|
||||
Entry remoteEntry) {
|
||||
return new GetCredentialProviderData.Builder(
|
||||
mComponentName.flattenToString()).setActionChips(actionEntries)
|
||||
|
||||
Reference in New Issue
Block a user