private AutofillId collectViewIds(AssistStructure.ViewNode node, Set<AutofillId> ids) { AutofillId result=null; if (node.getAutofillHints()!=null && node.getAutofillHints().length>0) { result=node.getAutofillId(); ids.add(result); } for (int i=0; i<node.getChildCount(); i++) { AutofillId temp=collectViewIds(node.getChildAt(i), ids); if (result==null) { result=temp; } } return(result); }
throws JSONException { json.put("accessibilityFocused", wrap(node.isAccessibilityFocused())); json.put("activated", wrap(node.isActivated())); json.put("alpha", wrap(node.getAlpha())); json.put("assistBlocked", wrap(node.isAssistBlocked())); json.put("autofillHints", wrap(node.getAutofillHints())); json.put("autofillId", wrap(node.getAutofillId())); json.put("autofillOptions", wrap(node.getAutofillOptions())); json.put("autofillType", wrap(node.getAutofillType())); json.put("autofillValue", wrap(node.getAutofillValue())); json.put("checkable", wrap(node.isCheckable())); json.put("checked", wrap(node.isChecked())); json.put("className", wrap(node.getClassName())); json.put("clickable", wrap(node.isClickable())); json.put("contentDescription", wrap(node.getContentDescription())); json.put("contextClickable", wrap(node.isContextClickable())); json.put("elevation", wrap(node.getElevation())); json.put("enabled", wrap(node.isEnabled())); if (node.getExtras()!=null) { json.put("extras", dumpBundle(node.getExtras(), new JSONObject())); json.put("focusable", wrap(node.isFocusable())); json.put("focused", wrap(node.isFocused())); json.put("height", wrap(node.getHeight()));
void bindValueToNode(AssistStructure.ViewNode viewNode, FilledAutofillField field, Dataset.Builder builder, MutableBoolean setValueAtLeastOnce) { AutofillId autofillId = viewNode.getAutofillId(); if (autofillId == null) { logw("Autofill ID null for %s", viewNode.toString()); return; int autofillType = viewNode.getAutofillType(); switch (autofillType) { case View.AUTOFILL_TYPE_LIST: CharSequence[] options = viewNode.getAutofillOptions(); int listValue = -1; if (options != null) { listValue = indexOf(viewNode.getAutofillOptions(), field.getTextValue());
private static void dumpNode(String prefix, ViewNode node) { StringBuilder builder = new StringBuilder(); builder.append(prefix) .append("autoFillId: ").append(node.getAutofillId()) .append("\tidEntry: ").append(node.getIdEntry()) .append("\tid: ").append(node.getId()) .append("\tclassName: ").append(node.getClassName()) .append('\n'); .append("focused: ").append(node.isFocused()) .append("\tvisibility").append(node.getVisibility()) .append("\tchecked: ").append(node.isChecked()) .append("\twebDomain: ").append(node.getWebDomain()) .append("\thint: ").append(node.getHint()) .append('\n'); HtmlInfo htmlInfo = node.getHtmlInfo(); String[] afHints = node.getAutofillHints(); CharSequence[] options = node.getAutofillOptions(); builder.append(prefix).append("afType: ").append(getAutofillTypeAsString(node.getAutofillType())) .append("\tafValue:") .append(getAutofillValueAndTypeAsString(node.getAutofillValue())) .append("\tafOptions:").append(options == null ? "N/A" : Arrays.toString(options)) .append("\tafHints: ").append(afHints == null ? "N/A" : Arrays.toString(afHints)) .append("\tinputType:").append(node.getInputType()) .append('\n'); int numberChildren = node.getChildCount(); builder.append(prefix).append("# children: ").append(numberChildren)
.append("autoFillId: ").append(node.getAutofillId()) .append("\tidEntry: ").append(node.getIdEntry()) .append("\tid: ").append(node.getId()) .append("\tclassName: ").append(node.getClassName()) .append('\n'); .append("focused: ").append(node.isFocused()) .append("\tvisibility").append(node.getVisibility()) .append("\tchecked: ").append(node.isChecked()) .append("\twebDomain: ").append(node.getWebDomain()) .append("\thint: ").append(node.getHint()) .append('\n'); HtmlInfo htmlInfo = node.getHtmlInfo(); String[] afHints = node.getAutofillHints(); CharSequence[] options = node.getAutofillOptions(); builder.append(prefix).append("afType: ").append(getTypeAsString(node.getAutofillType())) .append("\tafValue:") .append(getAutofillValueAndTypeAsString(node.getAutofillValue())) .append("\tafOptions:").append(options == null ? "N/A" : Arrays.toString(options)) .append("\tafHints: ").append(afHints == null ? "N/A" : Arrays.toString(afHints)) .append("\tinputType:").append(node.getInputType()) .append('\n'); int numberChildren = node.getChildCount(); builder.append(prefix).append("# children: ").append(numberChildren) .append("\ttext: ").append(node.getText()) .append('\n');
String[] hints = node.getAutofillHints(); if (hints != null) { String viewHint = node.getHint(); String hint = inferHint(node, viewHint); if (hint != null) { String resourceId = node.getIdEntry(); hint = inferHint(node, resourceId); if (hint != null) { CharSequence text = node.getText(); CharSequence className = node.getClassName(); if (text != null && className != null && className.toString().contains("EditText")) { hint = inferHint(node, text.toString());
/** * Uses heuristics to infer an autofill hint from a {@code string}. * * @return standard autofill hint, or {@code null} when it could not be inferred. */ @Nullable protected String inferHint(ViewNode node, @Nullable String actualHint) { if (actualHint == null) return null; String hint = actualHint.toLowerCase(); if (hint.contains("label") || hint.contains("container")) { Log.v(TAG, "Ignoring 'label/container' hint: " + hint); return null; } if (hint.contains("password")) return View.AUTOFILL_HINT_PASSWORD; if (hint.contains("username") || (hint.contains("login") && hint.contains("id"))) return View.AUTOFILL_HINT_USERNAME; if (hint.contains("email")) return View.AUTOFILL_HINT_EMAIL_ADDRESS; if (hint.contains("name")) return View.AUTOFILL_HINT_NAME; if (hint.contains("phone")) return View.AUTOFILL_HINT_PHONE; // When everything else fails, return the full string - this is helpful to help app // developers visualize when autofill is triggered when it shouldn't (for example, in a // chat conversation window), so they can mark the root view of such activities with // android:importantForAutofill=noExcludeDescendants if (node.isEnabled() && node.getAutofillType() != View.AUTOFILL_TYPE_NONE) { Log.v(TAG, "Falling back to " + actualHint); return actualHint; } return null; }
private void parseAutofillFields(AssistStructure.ViewNode viewNode, DatasetWithFilledAutofillFields datasetWithFilledAutofillFields, int partition) { String[] hints = viewNode.getAutofillHints(); if (hints == null || hints.length == 0) { return; } AutofillValue autofillValue = viewNode.getAutofillValue(); String textValue = null; Long dateValue = null; Boolean toggleValue = null; CharSequence[] autofillOptions = null; Integer listIndex = null; if (autofillValue != null) { if (autofillValue.isText()) { // Using toString of AutofillValue.getTextValue in order to save it to // SharedPreferences. textValue = autofillValue.getTextValue().toString(); } else if (autofillValue.isDate()) { dateValue = autofillValue.getDateValue(); } else if (autofillValue.isList()) { autofillOptions = viewNode.getAutofillOptions(); listIndex = autofillValue.getListValue(); } else if (autofillValue.isToggle()) { toggleValue = autofillValue.getToggleValue(); } } appendViewMetadata(datasetWithFilledAutofillFields, hints, partition, textValue, dateValue, toggleValue, autofillOptions, listIndex); }
/** * 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)); } }
private void parseViewNode(AssistStructure.ViewNode node) { String[] hints = node.getAutofillHints(); if (hints != null && hints.length > 0) { if (Arrays.stream(hints).anyMatch(View.AUTOFILL_HINT_USERNAME::equals)) result.username.add(node.getAutofillId()); else if (Arrays.stream(hints).anyMatch(View.AUTOFILL_HINT_EMAIL_ADDRESS::equals)) result.email.add(node.getAutofillId()); else if (Arrays.stream(hints).anyMatch(View.AUTOFILL_HINT_PASSWORD::equals)) result.password.add(node.getAutofillId()); else Log.d(TAG, "unsupported hints"); } else if (node.getAutofillType() == View.AUTOFILL_TYPE_TEXT) { int inputType = node.getInputType(); if ((inputType & InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS) > 0) result.email.add(node.getAutofillId()); else if ((inputType & InputType.TYPE_TEXT_VARIATION_PASSWORD) > 0) result.password.add(node.getAutofillId()); else if (result.password.isEmpty()) usernameCandidate = node.getAutofillId(); } for (int i=0; i<node.getChildCount(); ++i) parseViewNode(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)); } }
private void parseAutofillFields(AssistStructure.ViewNode viewNode, HashMap<String, FieldTypeWithHeuristics> fieldTypesByAutofillHint, Map<String, FilledAutofillField> filledAutofillFieldsByTypeName, Dataset.Builder builder, MutableBoolean setValueAtLeastOnce) { String[] rawHints = viewNode.getAutofillHints(); if (rawHints == null || rawHints.length == 0) { logv("No af hints at ViewNode - %s", viewNode.getIdEntry()); return; } String fieldTypeName = AutofillHints.getFieldTypeNameFromAutofillHints( fieldTypesByAutofillHint, Arrays.asList(rawHints)); if (fieldTypeName == null) { return; } FilledAutofillField field = filledAutofillFieldsByTypeName.get(fieldTypeName); if (field == null) { return; } bindValueToNode(viewNode, field, builder, setValueAtLeastOnce); }
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)); } }
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 a node if it matches the filter criteria for the given id. */ public static ViewNode findNodeByFilter(@NonNull ViewNode node, @NonNull Object id, @NonNull NodeFilter filter) { if (filter.matches(node, id)) { return node; } final int childrenSize = node.getChildCount(); if (childrenSize > 0) { for (int i = 0; i < childrenSize; i++) { final ViewNode found = findNodeByFilter(node.getChildAt(i), id, filter); if (found != null) { return found; } } } return null; }
private boolean bindDatasetToFocusedNode(FilledAutofillField field, FieldType fieldType, Dataset.Builder builder) { MutableBoolean setValueAtLeastOnce = new MutableBoolean(false); mClientParser.parse((node) -> { if (node.isFocused() && node.getAutofillId() != null) { bindValueToNode(node, field, builder, setValueAtLeastOnce); } }); return setValueAtLeastOnce.value; }
private void traverseRoot(AssistStructure.ViewNode viewNode, NodeProcessor processor) { processor.processNode(viewNode); int childrenSize = viewNode.getChildCount(); if (childrenSize > 0) { for (int i = 0; i < childrenSize; i++) { traverseRoot(viewNode.getChildAt(i), processor); } } }
throws JSONException { json.put("accessibilityFocused", wrap(node.isAccessibilityFocused())); json.put("activated", wrap(node.isActivated())); json.put("alpha", wrap(node.getAlpha())); json.put("assistBlocked", wrap(node.isAssistBlocked())); json.put("checkable", wrap(node.isCheckable())); json.put("checked", wrap(node.isChecked())); json.put("className", wrap(node.getClassName())); json.put("clickable", wrap(node.isClickable())); json.put("contentDescription", wrap(node.getContentDescription())); json.put("contextClickable", wrap(node.isContextClickable())); json.put("elevation", wrap(node.getElevation())); json.put("enabled", wrap(node.isEnabled())); if (node.getExtras()!=null) { json.put("extras", dumpBundle(node.getExtras(), new JSONObject())); json.put("focusable", wrap(node.isFocusable())); json.put("focused", wrap(node.isFocused())); json.put("height", wrap(node.getHeight())); json.put("hint", wrap(node.getHint())); json.put("id", wrap(node.getId())); json.put("idEntry", wrap(node.getIdEntry())); json.put("idPackage", wrap(node.getIdPackage())); json.put("idType", wrap(node.getIdType()));
private JSONArray dumpStructureNodes( AssistStructure.ViewNode node, JSONArray children) throws JSONException { for (int i=0; i<node.getChildCount(); i++) { children.put(dumpStructureNode(node.getChildAt(i), new JSONObject())); } return (children); }
private JSONArray dumpStructureNodes( AssistStructure.ViewNode node, JSONArray children) throws JSONException { for (int i=0; i<node.getChildCount(); i++) { children.put(dumpStructureNode(node.getChildAt(i), new JSONObject())); } return(children); }