下面列出了怎么用android.view.autofill.AutofillId的API类实例代码及写法,或者点击链接到github查看源代码。
private void parseNode(AssistStructure.ViewNode root, List<String> allHints,
MutableInt autofillSaveType, List<AutofillId> autofillIds,
List<AutofillId> focusedAutofillIds) {
String[] hints = root.getAutofillHints();
if (hints != null) {
for (String hint : hints) {
FieldTypeWithHeuristics fieldTypeWithHints = mFieldTypesByAutofillHint.get(hint);
if (fieldTypeWithHints != null && fieldTypeWithHints.fieldType != null) {
allHints.add(hint);
autofillSaveType.value |= fieldTypeWithHints.fieldType.getSaveInfo();
autofillIds.add(root.getAutofillId());
}
}
}
if (root.isFocused()) {
focusedAutofillIds.add(root.getAutofillId());
}
}
/**
* Adds any autofillable view from the {@link ViewNode} and its descendants to the map.
*/
private void addAutofillableFields(@NonNull Map<String, AutofillId> fields,
@NonNull ViewNode node) {
String[] hints = node.getAutofillHints();
if (hints != null) {
// We're simple, we only care about the first hint
String hint = hints[0].toLowerCase();
if (hint != null) {
AutofillId id = node.getAutofillId();
if (!fields.containsKey(hint)) {
Log.v(TAG, "Setting hint '" + hint + "' on " + id);
fields.put(hint, id);
} else {
Log.v(TAG, "Ignoring hint '" + hint + "' on " + id
+ " because it was already set");
}
}
}
int childrenSize = node.getChildCount();
for (int i = 0; i < childrenSize; i++) {
addAutofillableFields(fields, node.getChildAt(i));
}
}
/**
* Adds any autofillable view from the {@link ViewNode} and its descendants to the map.
*/
private void addAutofillableFields(@NonNull Map<String, AutofillId> fields,
@NonNull ViewNode node) {
String hint = getHint(node);
if (hint != null) {
AutofillId id = node.getAutofillId();
if (!fields.containsKey(hint)) {
Log.v(TAG, "Setting hint '" + hint + "' on " + id);
fields.put(hint, id);
} else {
Log.v(TAG, "Ignoring hint '" + hint + "' on " + id
+ " because it was already set");
}
}
int childrenSize = node.getChildCount();
for (int i = 0; i < childrenSize; i++) {
addAutofillableFields(fields, node.getChildAt(i));
}
}
static Dataset newUnlockedDataset(@NonNull Map<String, AutofillId> fields,
@NonNull String packageName, int i) {
Dataset.Builder dataset = new Dataset.Builder();
for (Entry<String, AutofillId> field : fields.entrySet()) {
String hint = field.getKey();
AutofillId id = field.getValue();
String value = i + "-" + hint;
// We're simple - our dataset values are hardcoded as "N-hint" (for example,
// "1-username", "2-username") and they're displayed as such, except if they're a
// password
String displayValue = hint.contains("password") ? "password for #" + i : value;
RemoteViews presentation = newDatasetPresentation(packageName, displayValue);
dataset.setValue(id, AutofillValue.forText(value), presentation);
}
return dataset.build();
}
private void onYes() {
Intent myIntent = getIntent();
Intent replyIntent = new Intent();
Dataset dataset = myIntent.getParcelableExtra(EXTRA_DATASET);
if (dataset != null) {
replyIntent.putExtra(EXTRA_AUTHENTICATION_RESULT, dataset);
} else {
String[] hints = myIntent.getStringArrayExtra(EXTRA_HINTS);
Parcelable[] ids = myIntent.getParcelableArrayExtra(EXTRA_IDS);
boolean authenticateDatasets = myIntent.getBooleanExtra(EXTRA_AUTH_DATASETS, false);
int size = hints.length;
ArrayMap<String, AutofillId> fields = new ArrayMap<>(size);
for (int i = 0; i < size; i++) {
fields.put(hints[i], (AutofillId) ids[i]);
}
FillResponse response =
DebugService.createResponse(this, fields, 1, authenticateDatasets);
replyIntent.putExtra(EXTRA_AUTHENTICATION_RESULT, response);
}
setResult(RESULT_OK, replyIntent);
finish();
}
private void parseNode(AssistStructure.ViewNode root, List<String> allHints,
MutableInt autofillSaveType, List<AutofillId> autofillIds,
List<AutofillId> focusedAutofillIds) {
String[] hints = root.getAutofillHints();
if (hints != null) {
for (String hint : hints) {
FieldTypeWithHeuristics fieldTypeWithHints = mFieldTypesByAutofillHint.get(hint);
if (fieldTypeWithHints != null && fieldTypeWithHints.fieldType != null) {
allHints.add(hint);
autofillSaveType.value |= fieldTypeWithHints.fieldType.getSaveInfo();
autofillIds.add(root.getAutofillId());
}
}
}
if (root.isFocused()) {
focusedAutofillIds.add(root.getAutofillId());
}
}
/**
* Gets the <a href="AutofillService.html#FieldClassification">field classification</a>
* results.
*
* <p><b>Note: </b>Only set on events of type {@link #TYPE_CONTEXT_COMMITTED}, when the
* service requested {@link FillResponse.Builder#setFieldClassificationIds(AutofillId...)
* field classification}.
*/
@NonNull public Map<AutofillId, FieldClassification> getFieldsClassification() {
if (mDetectedFieldIds == null) {
return Collections.emptyMap();
}
final int size = mDetectedFieldIds.length;
final ArrayMap<AutofillId, FieldClassification> map = new ArrayMap<>(size);
for (int i = 0; i < size; i++) {
final AutofillId id = mDetectedFieldIds[i];
final FieldClassification fc = mDetectedFieldClassifications[i];
if (sVerbose) {
Log.v(TAG, "getFieldsClassification[" + i + "]: id=" + id + ", fc=" + fc);
}
map.put(id, fc);
}
return map;
}
/** @hide */
@Override
@TestApi
public boolean isValid(@NonNull ValueFinder finder) {
if (mIds == null || mIds.length == 0) return false;
final StringBuilder builder = new StringBuilder();
for (AutofillId id : mIds) {
final String partialNumber = finder.findByAutofillId(id);
if (partialNumber == null) {
if (sDebug) Log.d(TAG, "No partial number for id " + id);
return false;
}
builder.append(partialNumber);
}
final String number = builder.toString();
boolean valid = isLuhnChecksumValid(number);
if (sDebug) Log.d(TAG, "isValid(" + number.length() + " chars): " + valid);
return valid;
}
/**
* Adds any autofillable view from the {@link ViewNode} and its descendants to the map.
*/
private void addAutofillableFields(@NonNull Map<String, AutofillId> fields,
@NonNull ViewNode node) {
String[] hints = node.getAutofillHints();
if (hints != null) {
// We're simple, we only care about the first hint
String hint = hints[0].toLowerCase();
if (hint != null) {
AutofillId id = node.getAutofillId();
if (!fields.containsKey(hint)) {
Log.v(TAG, "Setting hint '" + hint + "' on " + id);
fields.put(hint, id);
} else {
Log.v(TAG, "Ignoring hint '" + hint + "' on " + id
+ " because it was already set");
}
}
}
int childrenSize = node.getChildCount();
for (int i = 0; i < childrenSize; i++) {
addAutofillableFields(fields, node.getChildAt(i));
}
}
@Override
public void writeToParcel(Parcel parcel, int flags) {
final int size = mFields.size();
final AutofillId[] ids = new AutofillId[size];
final Pattern[] regexs = new Pattern[size];
final String[] substs = new String[size];
Pair<Pattern, String> pair;
int i = 0;
for (Entry<AutofillId, Pair<Pattern, String>> entry : mFields.entrySet()) {
ids[i] = entry.getKey();
pair = entry.getValue();
regexs[i] = pair.first;
substs[i] = pair.second;
i++;
}
parcel.writeParcelableArray(ids, flags);
parcel.writeSerializable(regexs);
parcel.writeStringArray(substs);
}
@Override
public CharSequenceTransformation createFromParcel(Parcel parcel) {
final AutofillId[] ids = parcel.readParcelableArray(null, AutofillId.class);
final Pattern[] regexs = (Pattern[]) parcel.readSerializable();
final String[] substs = parcel.createStringArray();
// Always go through the builder to ensure the data ingested by
// the system obeys the contract of the builder to avoid attacks
// using specially crafted parcels.
final CharSequenceTransformation.Builder builder =
new CharSequenceTransformation.Builder(ids[0], regexs[0], substs[0]);
final int size = ids.length;
for (int i = 1; i < size; i++) {
builder.addField(ids[i], regexs[i], substs[i]);
}
return builder.build();
}
private SaveInfo(Builder builder) {
mType = builder.mType;
mNegativeButtonStyle = builder.mNegativeButtonStyle;
mNegativeActionListener = builder.mNegativeActionListener;
mRequiredIds = builder.mRequiredIds;
mOptionalIds = builder.mOptionalIds;
mDescription = builder.mDescription;
mFlags = builder.mFlags;
mCustomDescription = builder.mCustomDescription;
mValidator = builder.mValidator;
if (builder.mSanitizers == null) {
mSanitizerKeys = null;
mSanitizerValues = null;
} else {
final int size = builder.mSanitizers.size();
mSanitizerKeys = new InternalSanitizer[size];
mSanitizerValues = new AutofillId[size][];
for (int i = 0; i < size; i++) {
mSanitizerKeys[i] = builder.mSanitizers.keyAt(i);
mSanitizerValues[i] = builder.mSanitizers.valueAt(i);
}
}
mTriggerId = builder.mTriggerId;
}
private void addAutofillableFields(@NonNull Map<String, AutofillId> fields,
@NonNull ViewNode node) {
String[] hints = node.getAutofillHints();
if (hints != null) {
// We're simple, we only care about the first hint
String hint = hints[0];
AutofillId id = node.getAutofillId();
if (!fields.containsKey(hint)) {
Log.v(TAG, "Setting hint '" + hint + "' on " + id);
fields.put(hint, id);
} else {
Log.v(TAG, "Ignoring hint '" + hint + "' on " + id
+ " because it was already set");
}
}
int childrenSize = node.getChildCount();
for (int i = 0; i < childrenSize; i++) {
addAutofillableFields(fields, node.getChildAt(i));
}
}
@Override
public ImageTransformation createFromParcel(Parcel parcel) {
final AutofillId id = parcel.readParcelable(null);
final Pattern[] regexs = (Pattern[]) parcel.readSerializable();
final int[] resIds = parcel.createIntArray();
final CharSequence[] contentDescriptions = parcel.readCharSequenceArray();
// Always go through the builder to ensure the data ingested by the system obeys the
// contract of the builder to avoid attacks using specially crafted parcels.
final CharSequence contentDescription = contentDescriptions[0];
final ImageTransformation.Builder builder = (contentDescription != null)
? new ImageTransformation.Builder(id, regexs[0], resIds[0], contentDescription)
: new ImageTransformation.Builder(id, regexs[0], resIds[0]);
final int size = regexs.length;
for (int i = 1; i < size; i++) {
if (contentDescriptions[i] != null) {
builder.addOption(regexs[i], resIds[i], contentDescriptions[i]);
} else {
builder.addOption(regexs[i], resIds[i]);
}
}
return builder.build();
}
/**
* Adds any autofillable view from the {@link ViewNode} and its descendants to the map.
*/
private void addAutofillableFields(@NonNull Map<String, AutofillId> fields,
@NonNull ViewNode node) {
String hint = getHint(node);
if (hint != null) {
AutofillId id = node.getAutofillId();
if (!fields.containsKey(hint)) {
Log.v(TAG, "Setting hint '" + hint + "' on " + id);
fields.put(hint, id);
} else {
Log.v(TAG, "Ignoring hint '" + hint + "' on " + id
+ " because it was already set");
}
}
int childrenSize = node.getChildCount();
for (int i = 0; i < childrenSize; i++) {
addAutofillableFields(fields, node.getChildAt(i));
}
}
private void setLifeTheUniverseAndEverything(@NonNull AutofillId id,
@Nullable AutofillValue value, @Nullable RemoteViews presentation,
@Nullable DatasetFieldFilter filter) {
Preconditions.checkNotNull(id, "id cannot be null");
if (mFieldIds != null) {
final int existingIdx = mFieldIds.indexOf(id);
if (existingIdx >= 0) {
mFieldValues.set(existingIdx, value);
mFieldPresentations.set(existingIdx, presentation);
mFieldFilters.set(existingIdx, filter);
return;
}
} else {
mFieldIds = new ArrayList<>();
mFieldValues = new ArrayList<>();
mFieldPresentations = new ArrayList<>();
mFieldFilters = new ArrayList<>();
}
mFieldIds.add(id);
mFieldValues.add(value);
mFieldPresentations.add(presentation);
mFieldFilters.add(filter);
}
static Dataset newUnlockedDataset(@NonNull Map<String, AutofillId> fields,
@NonNull String packageName, int i) {
Dataset.Builder dataset = new Dataset.Builder();
for (Entry<String, AutofillId> field : fields.entrySet()) {
String hint = field.getKey();
AutofillId id = field.getValue();
String value = i + "-" + hint;
// We're simple - our dataset values are hardcoded as "N-hint" (for example,
// "1-username", "2-username") and they're displayed as such, except if they're a
// password
String displayValue = hint.contains("password") ? "password for #" + i : value;
RemoteViews presentation = newDatasetPresentation(packageName, displayValue);
dataset.setValue(id, AutofillValue.forText(value), presentation);
}
return dataset.build();
}
private static IntentSender newIntentSender(@NonNull Context context,
@Nullable Dataset dataset, @Nullable String[] hints, @Nullable AutofillId[] ids,
boolean authenticateDatasets) {
Intent intent = new Intent(context, SimpleAuthActivity.class);
if (dataset != null) {
intent.putExtra(EXTRA_DATASET, dataset);
} else {
intent.putExtra(EXTRA_HINTS, hints);
intent.putExtra(EXTRA_IDS, ids);
intent.putExtra(EXTRA_AUTH_DATASETS, authenticateDatasets);
}
return PendingIntent.getActivity(context, ++sPendingIntentId, intent,
PendingIntent.FLAG_CANCEL_CURRENT).getIntentSender();
}
public ClientViewMetadata buildClientViewMetadata() {
List<String> allHints = new ArrayList<>();
MutableInt saveType = new MutableInt(0);
List<AutofillId> autofillIds = new ArrayList<>();
StringBuilder webDomainBuilder = new StringBuilder();
List<AutofillId> focusedAutofillIds = new ArrayList<>();
mClientParser.parse((node) -> parseNode(node, allHints, saveType, autofillIds, focusedAutofillIds));
mClientParser.parse((node) -> parseWebDomain(node, webDomainBuilder));
String webDomain = webDomainBuilder.toString();
AutofillId[] autofillIdsArray = autofillIds.toArray(new AutofillId[autofillIds.size()]);
AutofillId[] focusedIds = focusedAutofillIds.toArray(new AutofillId[focusedAutofillIds.size()]);
return new ClientViewMetadata(allHints, saveType.value, autofillIdsArray, focusedIds, webDomain);
}
public FillResponse buildManualResponse(IntentSender sender, RemoteViews remoteViews) {
FillResponse.Builder responseBuilder = new FillResponse.Builder();
int saveType = mClientViewMetadata.getSaveType();
AutofillId[] focusedIds = mClientViewMetadata.getFocusedIds();
if (focusedIds != null && focusedIds.length > 0) {
SaveInfo saveInfo = new SaveInfo.Builder(saveType, focusedIds).build();
return responseBuilder.setSaveInfo(saveInfo)
.setAuthentication(focusedIds, sender, remoteViews)
.build();
} else {
return null;
}
}
public ClientViewMetadata buildClientViewMetadata() {
List<String> allHints = new ArrayList<>();
MutableInt saveType = new MutableInt(0);
List<AutofillId> autofillIds = new ArrayList<>();
StringBuilder webDomainBuilder = new StringBuilder();
List<AutofillId> focusedAutofillIds = new ArrayList<>();
mClientParser.parse((node) -> parseNode(node, allHints, saveType, autofillIds, focusedAutofillIds));
mClientParser.parse((node) -> parseWebDomain(node, webDomainBuilder));
String webDomain = webDomainBuilder.toString();
AutofillId[] autofillIdsArray = autofillIds.toArray(new AutofillId[autofillIds.size()]);
AutofillId[] focusedIds = focusedAutofillIds.toArray(new AutofillId[focusedAutofillIds.size()]);
return new ClientViewMetadata(allHints, saveType.value, autofillIdsArray, focusedIds, webDomain);
}
@NonNull
private ArrayMap<String, AutofillId> getAutofillableFields(@NonNull AssistStructure structure) {
ArrayMap<String, AutofillId> fields = new ArrayMap<>();
int nodes = structure.getWindowNodeCount();
for (int i = 0; i < nodes; i++) {
ViewNode node = structure.getWindowNodeAt(i).getRootViewNode();
addAutofillableFields(fields, node);
}
return fields;
}
/**
* Parses the {@link AssistStructure} representing the activity being autofilled, and returns a
* map of autofillable fields (represented by their autofill ids) mapped by the hint associate
* with them.
*
* <p>An autofillable field is a {@link ViewNode} whose {@link #getHint(ViewNode)} metho
*/
@NonNull
private Map<String, AutofillId> getAutofillableFields(@NonNull AssistStructure structure) {
Map<String, AutofillId> fields = new ArrayMap<>();
int nodes = structure.getWindowNodeCount();
for (int i = 0; i < nodes; i++) {
ViewNode node = structure.getWindowNodeAt(i).getRootViewNode();
addAutofillableFields(fields, node);
}
return fields;
}
static FillResponse createResponse(@NonNull Context context,
@NonNull ArrayMap<String, AutofillId> fields, int numDatasets,
boolean authenticateDatasets) {
String packageName = context.getPackageName();
FillResponse.Builder response = new FillResponse.Builder();
// 1.Add the dynamic datasets
for (int i = 1; i <= numDatasets; i++) {
Dataset unlockedDataset = newUnlockedDataset(fields, packageName, i);
if (authenticateDatasets) {
Dataset.Builder lockedDataset = new Dataset.Builder();
for (Entry<String, AutofillId> field : fields.entrySet()) {
String hint = field.getKey();
AutofillId id = field.getValue();
String value = i + "-" + hint;
IntentSender authentication =
SimpleAuthActivity.newIntentSenderForDataset(context, unlockedDataset);
RemoteViews presentation = newDatasetPresentation(packageName,
"Tap to auth " + value);
lockedDataset.setValue(id, null, presentation)
.setAuthentication(authentication);
}
response.addDataset(lockedDataset.build());
} else {
response.addDataset(unlockedDataset);
}
}
// 2.Add save info
Collection<AutofillId> ids = fields.values();
AutofillId[] requiredIds = new AutofillId[ids.size()];
ids.toArray(requiredIds);
response.setSaveInfo(
// We're simple, so we're generic
new SaveInfo.Builder(SaveInfo.SAVE_DATA_TYPE_GENERIC, requiredIds).build());
// 3.Profit!
return response.build();
}
private static IntentSender newIntentSender(@NonNull Context context,
@Nullable Dataset dataset, @Nullable String[] hints, @Nullable AutofillId[] ids,
boolean authenticateDatasets) {
Intent intent = new Intent(context, SimpleAuthActivity.class);
if (dataset != null) {
intent.putExtra(EXTRA_DATASET, dataset);
} else {
intent.putExtra(EXTRA_HINTS, hints);
intent.putExtra(EXTRA_IDS, ids);
intent.putExtra(EXTRA_AUTH_DATASETS, authenticateDatasets);
}
return PendingIntent.getActivity(context, ++sPendingIntentId, intent,
PendingIntent.FLAG_CANCEL_CURRENT).getIntentSender();
}
FieldMetadata(AutofillId autofillId, String autofillHint, @StringRes int labelRes,
@DrawableRes int iconRes, int inputType) {
mAutofillId = autofillId;
mAutofillHint = autofillHint;
mLabelRes = labelRes;
mIconRes = iconRes;
mInputType = inputType;
}
@NonNull
private ArrayMap<String, AutofillId> getAutofillableFields(@NonNull AssistStructure structure) {
ArrayMap<String, AutofillId> fields = new ArrayMap<>();
int nodes = structure.getWindowNodeCount();
for (int i = 0; i < nodes; i++) {
ViewNode node = structure.getWindowNodeAt(i).getRootViewNode();
addAutofillableFields(fields, node);
}
return fields;
}
/**
* Creates a new event.
*
* @param eventType The type of the event
* @param datasetId The dataset the event was on, or {@code null} if the event was on the
* whole response.
* @param clientState The client state associated with the event.
* @param selectedDatasetIds The ids of datasets selected by the user.
* @param ignoredDatasetIds The ids of datasets NOT select by the user.
* @param changedFieldIds The ids of fields changed by the user.
* @param changedDatasetIds The ids of the datasets that havd values matching the
* respective entry on {@code changedFieldIds}.
* @param manuallyFilledFieldIds The ids of fields that were manually entered by the user
* and belonged to datasets.
* @param manuallyFilledDatasetIds The ids of datasets that had values matching the
* respective entry on {@code manuallyFilledFieldIds}.
* @param detectedFieldClassifications the field classification matches.
*
* @throws IllegalArgumentException If the length of {@code changedFieldIds} and
* {@code changedDatasetIds} doesn't match.
* @throws IllegalArgumentException If the length of {@code manuallyFilledFieldIds} and
* {@code manuallyFilledDatasetIds} doesn't match.
*
* @hide
*/
public Event(int eventType, @Nullable String datasetId, @Nullable Bundle clientState,
@Nullable List<String> selectedDatasetIds,
@Nullable ArraySet<String> ignoredDatasetIds,
@Nullable ArrayList<AutofillId> changedFieldIds,
@Nullable ArrayList<String> changedDatasetIds,
@Nullable ArrayList<AutofillId> manuallyFilledFieldIds,
@Nullable ArrayList<ArrayList<String>> manuallyFilledDatasetIds,
@Nullable AutofillId[] detectedFieldIds,
@Nullable FieldClassification[] detectedFieldClassifications) {
mEventType = Preconditions.checkArgumentInRange(eventType, 0, TYPE_CONTEXT_COMMITTED,
"eventType");
mDatasetId = datasetId;
mClientState = clientState;
mSelectedDatasetIds = selectedDatasetIds;
mIgnoredDatasetIds = ignoredDatasetIds;
if (changedFieldIds != null) {
Preconditions.checkArgument(!ArrayUtils.isEmpty(changedFieldIds)
&& changedDatasetIds != null
&& changedFieldIds.size() == changedDatasetIds.size(),
"changed ids must have same length and not be empty");
}
mChangedFieldIds = changedFieldIds;
mChangedDatasetIds = changedDatasetIds;
if (manuallyFilledFieldIds != null) {
Preconditions.checkArgument(!ArrayUtils.isEmpty(manuallyFilledFieldIds)
&& manuallyFilledDatasetIds != null
&& manuallyFilledFieldIds.size() == manuallyFilledDatasetIds.size(),
"manually filled ids must have same length and not be empty");
}
mManuallyFilledFieldIds = manuallyFilledFieldIds;
mManuallyFilledDatasetIds = manuallyFilledDatasetIds;
mDetectedFieldIds = detectedFieldIds;
mDetectedFieldClassifications = detectedFieldClassifications;
}
/**
* Parses the {@link AssistStructure} representing the activity being autofilled, and returns a
* map of autofillable fields (represented by their autofill ids) mapped by the hint associate
* with them.
*
* <p>An autofillable field is a {@link ViewNode} whose {@link #getHint(ViewNode)} metho
*/
@NonNull
private ArrayMap<String, AutofillId> getAutofillableFields(@NonNull AssistStructure structure) {
ArrayMap<String, AutofillId> fields = new ArrayMap<>();
int nodes = structure.getWindowNodeCount();
for (int i = 0; i < nodes; i++) {
ViewNode node = structure.getWindowNodeAt(i).getRootViewNode();
addAutofillableFields(fields, node);
}
return fields;
}
static AutofillId[] assertValid(@Nullable AutofillId[] ids) {
Preconditions.checkArgument(ids != null && ids.length > 0, "must have at least one id");
// Can't use Preconditions.checkArrayElementsNotNull() because it throws NPE instead of IAE
for (int i = 0; i < ids.length; ++i) {
if (ids[i] == null) {
throw new IllegalArgumentException("ids[" + i + "] must not be null");
}
}
return ids;
}