summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--api/current.txt7
-rw-r--r--api/system-current.txt7
-rw-r--r--api/test-current.txt7
-rw-r--r--core/java/android/view/View.java157
-rw-r--r--core/java/android/view/ViewGroup.java188
-rw-r--r--core/java/android/webkit/WebView.java6
-rw-r--r--core/java/android/widget/AbsSpinner.java47
-rw-r--r--core/java/android/widget/DatePicker.java5
-rw-r--r--core/java/android/widget/ImageView.java5
-rw-r--r--core/java/android/widget/RadioGroup.java5
-rw-r--r--core/java/android/widget/Spinner.java19
-rw-r--r--core/java/android/widget/TextView.java5
-rw-r--r--core/java/android/widget/TimePicker.java5
-rw-r--r--core/res/res/values/attrs.xml11
-rw-r--r--core/res/res/values/public.xml1
15 files changed, 389 insertions, 86 deletions
diff --git a/api/current.txt b/api/current.txt
index f8c2615c519e..69e2f8bcfc98 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -710,6 +710,7 @@ package android {
field public static final int imeSubtypeMode = 16843501; // 0x10102ed
field public static final int immersive = 16843456; // 0x10102c0
field public static final int importantForAccessibility = 16843690; // 0x10103aa
+ field public static final int importantForAutofill = 16844123; // 0x101055b
field public static final int inAnimation = 16843127; // 0x1010177
field public static final int includeFontPadding = 16843103; // 0x101015f
field public static final int includeInGlobalSearch = 16843374; // 0x101026e
@@ -45099,6 +45100,7 @@ package android.view {
method protected int getHorizontalScrollbarHeight();
method public int getId();
method public int getImportantForAccessibility();
+ method public int getImportantForAutofill();
method public boolean getKeepScreenOn();
method public android.view.KeyEvent.DispatcherState getKeyDispatcherState();
method public int getLabelFor();
@@ -45228,6 +45230,7 @@ package android.view {
method public boolean isHorizontalScrollBarEnabled();
method public boolean isHovered();
method public boolean isImportantForAccessibility();
+ method public final boolean isImportantForAutofill();
method public boolean isInEditMode();
method public boolean isInLayout();
method public boolean isInTouchMode();
@@ -45410,6 +45413,7 @@ package android.view {
method public void setHovered(boolean);
method public void setId(int);
method public void setImportantForAccessibility(int);
+ method public void setImportantForAutofill(int);
method public void setKeepScreenOn(boolean);
method public void setKeyboardNavigationCluster(boolean);
method public void setLabelFor(int);
@@ -45575,6 +45579,9 @@ package android.view {
field public static final int IMPORTANT_FOR_ACCESSIBILITY_NO = 2; // 0x2
field public static final int IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS = 4; // 0x4
field public static final int IMPORTANT_FOR_ACCESSIBILITY_YES = 1; // 0x1
+ field public static final int IMPORTANT_FOR_AUTOFILL_AUTO = 0; // 0x0
+ field public static final int IMPORTANT_FOR_AUTOFILL_NO = 2; // 0x2
+ field public static final int IMPORTANT_FOR_AUTOFILL_YES = 1; // 0x1
field public static final int INVISIBLE = 4; // 0x4
field public static final int KEEP_SCREEN_ON = 67108864; // 0x4000000
field public static final int LAYER_TYPE_HARDWARE = 2; // 0x2
diff --git a/api/system-current.txt b/api/system-current.txt
index b3d1a894c4e8..dac9ec8d0b4b 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -822,6 +822,7 @@ package android {
field public static final int imeSubtypeMode = 16843501; // 0x10102ed
field public static final int immersive = 16843456; // 0x10102c0
field public static final int importantForAccessibility = 16843690; // 0x10103aa
+ field public static final int importantForAutofill = 16844123; // 0x101055b
field public static final int inAnimation = 16843127; // 0x1010177
field public static final int includeFontPadding = 16843103; // 0x101015f
field public static final int includeInGlobalSearch = 16843374; // 0x101026e
@@ -48559,6 +48560,7 @@ package android.view {
method protected int getHorizontalScrollbarHeight();
method public int getId();
method public int getImportantForAccessibility();
+ method public int getImportantForAutofill();
method public boolean getKeepScreenOn();
method public android.view.KeyEvent.DispatcherState getKeyDispatcherState();
method public int getLabelFor();
@@ -48688,6 +48690,7 @@ package android.view {
method public boolean isHorizontalScrollBarEnabled();
method public boolean isHovered();
method public boolean isImportantForAccessibility();
+ method public final boolean isImportantForAutofill();
method public boolean isInEditMode();
method public boolean isInLayout();
method public boolean isInTouchMode();
@@ -48870,6 +48873,7 @@ package android.view {
method public void setHovered(boolean);
method public void setId(int);
method public void setImportantForAccessibility(int);
+ method public void setImportantForAutofill(int);
method public void setKeepScreenOn(boolean);
method public void setKeyboardNavigationCluster(boolean);
method public void setLabelFor(int);
@@ -49035,6 +49039,9 @@ package android.view {
field public static final int IMPORTANT_FOR_ACCESSIBILITY_NO = 2; // 0x2
field public static final int IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS = 4; // 0x4
field public static final int IMPORTANT_FOR_ACCESSIBILITY_YES = 1; // 0x1
+ field public static final int IMPORTANT_FOR_AUTOFILL_AUTO = 0; // 0x0
+ field public static final int IMPORTANT_FOR_AUTOFILL_NO = 2; // 0x2
+ field public static final int IMPORTANT_FOR_AUTOFILL_YES = 1; // 0x1
field public static final int INVISIBLE = 4; // 0x4
field public static final int KEEP_SCREEN_ON = 67108864; // 0x4000000
field public static final int LAYER_TYPE_HARDWARE = 2; // 0x2
diff --git a/api/test-current.txt b/api/test-current.txt
index f581171eb994..dc381fb3995b 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -710,6 +710,7 @@ package android {
field public static final int imeSubtypeMode = 16843501; // 0x10102ed
field public static final int immersive = 16843456; // 0x10102c0
field public static final int importantForAccessibility = 16843690; // 0x10103aa
+ field public static final int importantForAutofill = 16844123; // 0x101055b
field public static final int inAnimation = 16843127; // 0x1010177
field public static final int includeFontPadding = 16843103; // 0x101015f
field public static final int includeInGlobalSearch = 16843374; // 0x101026e
@@ -45459,6 +45460,7 @@ package android.view {
method protected int getHorizontalScrollbarHeight();
method public int getId();
method public int getImportantForAccessibility();
+ method public int getImportantForAutofill();
method public boolean getKeepScreenOn();
method public android.view.KeyEvent.DispatcherState getKeyDispatcherState();
method public int getLabelFor();
@@ -45589,6 +45591,7 @@ package android.view {
method public boolean isHorizontalScrollBarEnabled();
method public boolean isHovered();
method public boolean isImportantForAccessibility();
+ method public final boolean isImportantForAutofill();
method public boolean isInEditMode();
method public boolean isInLayout();
method public boolean isInTouchMode();
@@ -45773,6 +45776,7 @@ package android.view {
method public void setHovered(boolean);
method public void setId(int);
method public void setImportantForAccessibility(int);
+ method public void setImportantForAutofill(int);
method public void setKeepScreenOn(boolean);
method public void setKeyboardNavigationCluster(boolean);
method public void setLabelFor(int);
@@ -45938,6 +45942,9 @@ package android.view {
field public static final int IMPORTANT_FOR_ACCESSIBILITY_NO = 2; // 0x2
field public static final int IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS = 4; // 0x4
field public static final int IMPORTANT_FOR_ACCESSIBILITY_YES = 1; // 0x1
+ field public static final int IMPORTANT_FOR_AUTOFILL_AUTO = 0; // 0x0
+ field public static final int IMPORTANT_FOR_AUTOFILL_NO = 2; // 0x2
+ field public static final int IMPORTANT_FOR_AUTOFILL_YES = 1; // 0x1
field public static final int INVISIBLE = 4; // 0x4
field public static final int KEEP_SCREEN_ON = 67108864; // 0x4000000
field public static final int LAYER_TYPE_HARDWARE = 2; // 0x2
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index a9ee52dbf0a4..8cfd6a7b885c 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -1174,6 +1174,30 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
*/
public static final int AUTOFILL_TYPE_DATE = 4;
+ /** @hide */
+ @IntDef({
+ IMPORTANT_FOR_AUTOFILL_AUTO,
+ IMPORTANT_FOR_AUTOFILL_YES,
+ IMPORTANT_FOR_AUTOFILL_NO
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface AutofillImportance {}
+
+ /**
+ * Automatically determine whether a view is important for auto-fill.
+ */
+ public static final int IMPORTANT_FOR_AUTOFILL_AUTO = 0x0;
+
+ /**
+ * The view is important for important for auto-fill.
+ */
+ public static final int IMPORTANT_FOR_AUTOFILL_YES = 0x1;
+
+ /**
+ * The view is not important for auto-fill.
+ */
+ public static final int IMPORTANT_FOR_AUTOFILL_NO = 0x2;
+
/**
* This view is enabled. Interpretation varies by subclass.
* Use with ENABLED_MASK when calling setFlags.
@@ -2745,7 +2769,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
* 1 PFLAG3_FINGER_DOWN
* 1 PFLAG3_FOCUSED_BY_DEFAULT
* 11 PFLAG3_AUTO_FILL_MODE_MASK
- * xx * NO LONGER NEEDED, SHOULD BE REUSED *
+ * 11 PFLAG3_IMPORTANT_FOR_AUTOFILL
* 1 PFLAG3_OVERLAPPING_RENDERING_FORCED_VALUE
* 1 PFLAG3_HAS_OVERLAPPING_RENDERING_FORCED
* 1 PFLAG3_TEMPORARY_DETACH
@@ -2983,6 +3007,20 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
| AUTO_FILL_MODE_AUTO | AUTO_FILL_MODE_MANUAL) << PFLAG3_AUTO_FILL_MODE_SHIFT;
/**
+ * Shift for the bits in {@link #mPrivateFlags3} related to the
+ * "importantForAutofill" attribute.
+ */
+ static final int PFLAG3_IMPORTANT_FOR_AUTOFILL_SHIFT = 21;
+
+ /**
+ * Mask for obtaining the bits which specify how to determine
+ * whether a view is important for autofill.
+ */
+ static final int PFLAG3_IMPORTANT_FOR_AUTOFILL_MASK = (IMPORTANT_FOR_AUTOFILL_AUTO
+ | IMPORTANT_FOR_AUTOFILL_YES | IMPORTANT_FOR_AUTOFILL_NO)
+ << PFLAG3_IMPORTANT_FOR_AUTOFILL_SHIFT;
+
+ /**
* Whether this view has rendered elements that overlap (see {@link
* #hasOverlappingRendering()}, {@link #forceHasOverlappingRendering(boolean)}, and
* {@link #getHasOverlappingRendering()} ). The value in this bit is only valid when
@@ -5009,6 +5047,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
setAutoFillHint(a.getInt(attr, AUTO_FILL_HINT_NONE));
}
break;
+ case R.styleable.View_importantForAutofill:
+ if (a.peekValue(attr) != null) {
+ setImportantForAutofill(a.getInt(attr, IMPORTANT_FOR_AUTOFILL_AUTO));
+ }
+ break;
}
}
@@ -7443,6 +7486,118 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
return null;
}
+ /**
+ * Gets the mode for determining whether this View is important for autofill.
+ *
+ * <p>See {@link #setImportantForAutofill(int)} for more info about this mode.
+ *
+ * @return {@link #IMPORTANT_FOR_AUTOFILL_AUTO} by default, or value passed to
+ * {@link #setImportantForAutofill(int)}.
+ *
+ * @attr ref android.R.styleable#View_importantForAutofill
+ */
+ @ViewDebug.ExportedProperty(mapping = {
+ @ViewDebug.IntToString(from = IMPORTANT_FOR_AUTOFILL_AUTO, to = "auto"),
+ @ViewDebug.IntToString(from = IMPORTANT_FOR_AUTOFILL_YES, to = "yes"),
+ @ViewDebug.IntToString(from = IMPORTANT_FOR_AUTOFILL_NO, to = "no")})
+ public @AutofillImportance int getImportantForAutofill() {
+ return (mPrivateFlags3
+ & PFLAG3_IMPORTANT_FOR_AUTOFILL_MASK) >> PFLAG3_IMPORTANT_FOR_AUTOFILL_SHIFT;
+ }
+
+ /**
+ * Sets the mode for determining whether this View is important for autofill.
+ *
+ * <p>See {@link #setImportantForAutofill(int)} for more info about this mode.
+ *
+ * @param mode {@link #IMPORTANT_FOR_AUTOFILL_AUTO}, {@link #IMPORTANT_FOR_AUTOFILL_YES},
+ * or {@link #IMPORTANT_FOR_AUTOFILL_NO}.
+ *
+ * @attr ref android.R.styleable#View_importantForAutofill
+ */
+ public void setImportantForAutofill(@AutofillImportance int mode) {
+ mPrivateFlags3 &= ~PFLAG3_IMPORTANT_FOR_AUTOFILL_MASK;
+ mPrivateFlags3 |= (mode << PFLAG3_IMPORTANT_FOR_AUTOFILL_SHIFT)
+ & PFLAG3_IMPORTANT_FOR_AUTOFILL_MASK;
+ }
+
+ /**
+ * Hints the Android System whether the {@link android.app.assist.AssistStructure.ViewNode}
+ * associated with this View should be included in a {@link ViewStructure} used for
+ * autofill purposes.
+ *
+ * <p>Generally speaking, a view is important for autofill if:
+ * <ol>
+ * <li>The view can-be autofilled by an {@link android.service.autofill.AutoFillService}.
+ * <li>The view contents can help an {@link android.service.autofill.AutoFillService} to
+ * autofill other views.
+ * <ol>
+ *
+ * <p>For example, view containers should typically return {@code false} for performance reasons
+ * (since the important info is provided by their children), but if the container is actually
+ * whose children are part of a compound view, it should return {@code true} (and then override
+ * {@link #dispatchProvideAutoFillStructure(ViewStructure, int)} to simply call
+ * {@link #onProvideAutoFillStructure(ViewStructure, int)} so its children are not included in
+ * the structure). On the other hand, views representing labels or editable fields should
+ * typically return {@code true}, but in some cases they could return {@code false} (for
+ * example, if they're part of a "Captcha" mechanism).
+ *
+ * <p>By default, this method returns {@code true} if {@link #getImportantForAutofill()} returns
+ * {@link #IMPORTANT_FOR_AUTOFILL_YES}, {@code false } if it returns
+ * {@link #IMPORTANT_FOR_AUTOFILL_NO}, and use some heuristics to define the importance when it
+ * returns {@link #IMPORTANT_FOR_AUTOFILL_AUTO}. Hence, it should rarely be overridden - Views
+ * should use {@link #setImportantForAutofill(int)} instead.
+ *
+ * <p><strong>Note:</strong> returning {@code false} does not guarantee the view will be
+ * excluded from the structure; for example, if the user explicitly requested auto-fill, the
+ * View might be always included.
+ *
+ * <p>This decision applies just for the view, not its children - if the view children are not
+ * important for autofill, the view should override
+ * {@link #dispatchProvideAutoFillStructure(ViewStructure, int)} to simply call
+ * {@link #onProvideAutoFillStructure(ViewStructure, int)} (instead of calling
+ * {@link #dispatchProvideAutoFillStructure(ViewStructure, int)} for each child).
+ *
+ * @return whether the view is considered important for autofill.
+ *
+ * @see #IMPORTANT_FOR_AUTOFILL_AUTO
+ * @see #IMPORTANT_FOR_AUTOFILL_YES
+ * @see #IMPORTANT_FOR_AUTOFILL_NO
+ */
+ public final boolean isImportantForAutofill() {
+ final int flag = getImportantForAutofill();
+
+ // First, check if view explicity set it to YES or NO
+ if ((flag & IMPORTANT_FOR_AUTOFILL_YES) != 0) {
+ return true;
+ }
+ if ((flag & IMPORTANT_FOR_AUTOFILL_NO) != 0) {
+ return false;
+ }
+
+ // Then use some heuristics to handle AUTO.
+
+ // Always include views that have a explicity resource id.
+ final int id = mID;
+ if (id != NO_ID && !isViewIdGenerated(id)) {
+ final Resources res = getResources();
+ String entry = null;
+ String pkg = null;
+ try {
+ entry = res.getResourceEntryName(id);
+ pkg = res.getResourcePackageName(id);
+ } catch (Resources.NotFoundException e) {
+ // ignore
+ }
+ if (entry != null && pkg != null && pkg.equals(mContext.getPackageName())) {
+ return true;
+ }
+ }
+
+ // Otherwise, assume it's not important...
+ return false;
+ }
+
@Nullable
private AutoFillManager getAutoFillManager() {
return mContext.getSystemService(AutoFillManager.class);
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 3dd3ba8d3294..214249fc9a6a 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -595,6 +595,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
public ViewGroup(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
+
initViewGroup();
initFromAttributes(context, attrs, defStyleAttr, defStyleRes);
}
@@ -3341,83 +3342,123 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
dispatchProvideStructureForAssistOrAutoFill(structure, true);
}
+ /** @hide */
+ private ArrayList<View> getChildrenForAutofill() {
+ final ArrayList<View> list = new ArrayList<>();
+ populateChildrenForAutofill(list);
+ return list;
+ }
+
+ /** @hide */
+ private void populateChildrenForAutofill(ArrayList<View> list) {
+ final int count = mChildrenCount;
+ for (int i = 0; i < count; i++) {
+ final View child = mChildren[i];
+ if (child.isImportantForAutofill()) {
+ list.add(child);
+ } else if (child instanceof ViewGroup) {
+ ((ViewGroup) child).populateChildrenForAutofill(list);
+ }
+ }
+ }
+
private void dispatchProvideStructureForAssistOrAutoFill(ViewStructure structure,
boolean forAutoFill) {
boolean blocked = forAutoFill ? isAutoFillBlocked() : isAssistBlocked();
+ if (blocked || structure.getChildCount() != 0) {
+ return;
+ }
+ final View[] childrenArray;
+ final ArrayList<View> childrenList;
+ final int childrenCount;
- if (!blocked) {
- if (structure.getChildCount() == 0) {
- final int childrenCount = getChildCount();
- if (childrenCount > 0) {
- structure.setChildCount(childrenCount);
- ArrayList<View> preorderedList = buildOrderedChildList();
- boolean customOrder = preorderedList == null
- && isChildrenDrawingOrderEnabled();
- final View[] children = mChildren;
- for (int i=0; i<childrenCount; i++) {
- int childIndex;
- try {
- childIndex = getAndVerifyPreorderedIndex(childrenCount, i, customOrder);
- } catch (IndexOutOfBoundsException e) {
- childIndex = i;
- if (mContext.getApplicationInfo().targetSdkVersion
- < Build.VERSION_CODES.M) {
- Log.w(TAG, "Bad getChildDrawingOrder while collecting assist @ "
- + i + " of " + childrenCount, e);
- // At least one app is failing when we call getChildDrawingOrder
- // at this point, so deal semi-gracefully with it by falling back
- // on the basic order.
- customOrder = false;
- if (i > 0) {
- // If we failed at the first index, there really isn't
- // anything to do -- we will just proceed with the simple
- // sequence order.
- // Otherwise, we failed in the middle, so need to come up
- // with an order for the remaining indices and use that.
- // Failed at the first one, easy peasy.
- int[] permutation = new int[childrenCount];
- SparseBooleanArray usedIndices = new SparseBooleanArray();
- // Go back and collected the indices we have done so far.
- for (int j=0; j<i; j++) {
- permutation[j] = getChildDrawingOrder(childrenCount, j);
- usedIndices.put(permutation[j], true);
- }
- // Fill in the remaining indices with indices that have not
- // yet been used.
- int nextIndex = 0;
- for (int j=i; j<childrenCount; j++) {
- while (usedIndices.get(nextIndex, false)) {
- nextIndex++;
- }
- permutation[j] = nextIndex;
- nextIndex++;
- }
- // Build the final view list.
- preorderedList = new ArrayList<>(childrenCount);
- for (int j=0; j<childrenCount; j++) {
- preorderedList.add(children[permutation[j]]);
- }
+ if (forAutoFill) {
+ childrenArray = null;
+ // TODO(b/33197203): the current algorithm allocates a new list for each children that
+ // is a view group; ideally, we should use mAttachInfo.mTempArrayList instead, but that
+ // would complicated the algorithm a lot...
+ childrenList = getChildrenForAutofill();
+
+ childrenCount = childrenList.size();
+ } else {
+ childrenArray = mChildren;
+ childrenList = null;
+ childrenCount = getChildCount();
+ }
+
+ if (childrenCount > 0) {
+ structure.setChildCount(childrenCount);
+ ArrayList<View> preorderedList = buildOrderedChildList();
+ boolean customOrder = preorderedList == null
+ && isChildrenDrawingOrderEnabled();
+ for (int i = 0; i < childrenCount; i++) {
+ int childIndex;
+ try {
+ childIndex = getAndVerifyPreorderedIndex(childrenCount, i, customOrder);
+ } catch (IndexOutOfBoundsException e) {
+ childIndex = i;
+ if (mContext.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.M) {
+ Log.w(TAG, "Bad getChildDrawingOrder while collecting assist @ "
+ + i + " of " + childrenCount, e);
+ // At least one app is failing when we call getChildDrawingOrder
+ // at this point, so deal semi-gracefully with it by falling back
+ // on the basic order.
+ customOrder = false;
+ if (i > 0) {
+ // If we failed at the first index, there really isn't
+ // anything to do -- we will just proceed with the simple
+ // sequence order.
+ // Otherwise, we failed in the middle, so need to come up
+ // with an order for the remaining indices and use that.
+ // Failed at the first one, easy peasy.
+ int[] permutation = new int[childrenCount];
+ SparseBooleanArray usedIndices = new SparseBooleanArray();
+ // Go back and collected the indices we have done so far.
+ for (int j = 0; j < i; j++) {
+ permutation[j] = getChildDrawingOrder(childrenCount, j);
+ usedIndices.put(permutation[j], true);
+ }
+ // Fill in the remaining indices with indices that have not
+ // yet been used.
+ int nextIndex = 0;
+ for (int j = i; j < childrenCount; j++) {
+ while (usedIndices.get(nextIndex, false)) {
+ nextIndex++;
}
- } else {
- throw e;
+ permutation[j] = nextIndex;
+ nextIndex++;
+ }
+ // Build the final view list.
+ preorderedList = new ArrayList<>(childrenCount);
+ for (int j = 0; j < childrenCount; j++) {
+ final int index = permutation[j];
+ final View child = forAutoFill
+ ? childrenList.get(index)
+ : childrenArray[index];
+ preorderedList.add(child);
}
}
+ } else {
+ throw e;
+ }
+ }
- final View child = getAndVerifyPreorderedView(
- preorderedList, children, childIndex);
- final ViewStructure cstructure = structure.newChild(i);
+ final View child = forAutoFill
+ ? getAndVerifyPreorderedView(preorderedList, childrenList, childIndex)
+ : getAndVerifyPreorderedView(preorderedList, childrenArray, childIndex);
+ final ViewStructure cstructure = structure.newChild(i);
- // Must explicitly check which recursive method to call.
- if (forAutoFill) {
- // NOTE: flags are not currently supported, hence 0
- child.dispatchProvideAutoFillStructure(cstructure, 0);
- } else {
- child.dispatchProvideStructure(cstructure);
- }
- }
- if (preorderedList != null) preorderedList.clear();
+ // Must explicitly check which recursive method to call.
+ if (forAutoFill) {
+ // NOTE: flags are not currently supported, hence 0
+ child.dispatchProvideAutoFillStructure(cstructure, 0);
+ } else {
+ child.dispatchProvideStructure(cstructure);
}
}
+ if (preorderedList != null) {
+ preorderedList.clear();
+ }
}
}
@@ -3436,6 +3477,21 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
return child;
}
+ private static View getAndVerifyPreorderedView(ArrayList<View> preorderedList,
+ ArrayList<View> children, int childIndex) {
+ final View child;
+ if (preorderedList != null) {
+ child = preorderedList.get(childIndex);
+ if (child == null) {
+ throw new RuntimeException("Invalid preorderedList contained null child at index "
+ + childIndex);
+ }
+ } else {
+ child = children.get(childIndex);
+ }
+ return child;
+ }
+
/** @hide */
@Override
public void onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info) {
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index 4757757bfe70..3fbeb0323a1a 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -630,6 +630,12 @@ public class WebView extends AbsoluteLayout
protected WebView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes,
Map<String, Object> javaScriptInterfaces, boolean privateBrowsing) {
super(context, attrs, defStyleAttr, defStyleRes);
+
+ // WebView is important by default, unless app developer overrode attribute.
+ if (getImportantForAutofill() == IMPORTANT_FOR_AUTOFILL_AUTO) {
+ setImportantForAutofill(IMPORTANT_FOR_AUTOFILL_YES);
+ }
+
if (context == null) {
throw new IllegalArgumentException("Invalid context argument");
}
diff --git a/core/java/android/widget/AbsSpinner.java b/core/java/android/widget/AbsSpinner.java
index bc3dfffc1411..e6cd56607edc 100644
--- a/core/java/android/widget/AbsSpinner.java
+++ b/core/java/android/widget/AbsSpinner.java
@@ -26,6 +26,8 @@ import android.util.AttributeSet;
import android.util.SparseArray;
import android.view.View;
import android.view.ViewGroup;
+import android.view.ViewStructure;
+import android.view.autofill.AutoFillValue;
import com.android.internal.R;
@@ -68,6 +70,12 @@ public abstract class AbsSpinner extends AdapterView<SpinnerAdapter> {
public AbsSpinner(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
+
+ // Spinner is important by default, unless app developer overrode attribute.
+ if (getImportantForAutofill() == IMPORTANT_FOR_AUTOFILL_AUTO) {
+ setImportantForAutofill(IMPORTANT_FOR_AUTOFILL_YES);
+ }
+
initAbsSpinner();
final TypedArray a = context.obtainStyledAttributes(
@@ -480,4 +488,43 @@ public abstract class AbsSpinner extends AdapterView<SpinnerAdapter> {
public CharSequence getAccessibilityClassName() {
return AbsSpinner.class.getName();
}
+
+ // TODO(b/33197203): add unit/CTS tests for auto-fill methods (and make sure they handle enable)
+
+ @Override
+ public void onProvideAutoFillStructure(ViewStructure structure, int flags) {
+ super.onProvideAutoFillStructure(structure, flags);
+
+ if (getAdapter() == null) return;
+
+ // TODO(b/33197203): implement sanitization so initial value is only sanitized when coming
+ // from resources.
+
+ final int count = getAdapter().getCount();
+ if (count > 0) {
+ final String[] options = new String[count];
+ for (int i = 0; i < count; i++) {
+ options[i] = getAdapter().getItem(i).toString();
+ }
+ structure.setAutoFillOptions(options);
+ }
+ }
+
+ @Override
+ public void autoFill(AutoFillValue value) {
+ if (!isEnabled()) return;
+
+ final int position = value.getListValue();
+ setSelection(position);
+ }
+
+ @Override
+ public @AutofillType int getAutofillType() {
+ return isEnabled() ? AUTOFILL_TYPE_LIST : AUTOFILL_TYPE_NONE;
+ }
+
+ @Override
+ public AutoFillValue getAutoFillValue() {
+ return isEnabled() ? AutoFillValue.forList(getSelectedItemPosition()) : null;
+ }
}
diff --git a/core/java/android/widget/DatePicker.java b/core/java/android/widget/DatePicker.java
index 00253c7e2918..c90517295b4f 100644
--- a/core/java/android/widget/DatePicker.java
+++ b/core/java/android/widget/DatePicker.java
@@ -145,6 +145,11 @@ public class DatePicker extends FrameLayout {
public DatePicker(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
+ // DatePicker is important by default, unless app developer overrode attribute.
+ if (getImportantForAutofill() == IMPORTANT_FOR_AUTOFILL_AUTO) {
+ setImportantForAutofill(IMPORTANT_FOR_AUTOFILL_YES);
+ }
+
final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.DatePicker,
defStyleAttr, defStyleRes);
final boolean isDialogMode = a.getBoolean(R.styleable.DatePicker_dialogMode, false);
diff --git a/core/java/android/widget/ImageView.java b/core/java/android/widget/ImageView.java
index cd806510ed51..7e6f2e4305b4 100644
--- a/core/java/android/widget/ImageView.java
+++ b/core/java/android/widget/ImageView.java
@@ -156,6 +156,11 @@ public class ImageView extends View {
initImageView();
+ // ImageView is not important by default, unless app developer overrode attribute.
+ if (getImportantForAutofill() == IMPORTANT_FOR_AUTOFILL_AUTO) {
+ setImportantForAutofill(IMPORTANT_FOR_AUTOFILL_NO);
+ }
+
final TypedArray a = context.obtainStyledAttributes(
attrs, R.styleable.ImageView, defStyleAttr, defStyleRes);
diff --git a/core/java/android/widget/RadioGroup.java b/core/java/android/widget/RadioGroup.java
index 9b79858dea93..bb8cd2829caa 100644
--- a/core/java/android/widget/RadioGroup.java
+++ b/core/java/android/widget/RadioGroup.java
@@ -85,6 +85,11 @@ public class RadioGroup extends LinearLayout {
public RadioGroup(Context context, AttributeSet attrs) {
super(context, attrs);
+ // RadioGroup is important by default, unless app developer overrode attribute.
+ if (getImportantForAutofill() == IMPORTANT_FOR_AUTOFILL_AUTO) {
+ setImportantForAutofill(IMPORTANT_FOR_AUTOFILL_YES);
+ }
+
// retrieve selected radio button as requested by the user in the
// XML layout file
TypedArray attributes = context.obtainStyledAttributes(
diff --git a/core/java/android/widget/Spinner.java b/core/java/android/widget/Spinner.java
index a427bc57b903..3811e1adee61 100644
--- a/core/java/android/widget/Spinner.java
+++ b/core/java/android/widget/Spinner.java
@@ -45,7 +45,6 @@ import android.view.ViewStructure;
import android.view.ViewTreeObserver;
import android.view.ViewTreeObserver.OnGlobalLayoutListener;
import android.view.accessibility.AccessibilityNodeInfo;
-import android.view.autofill.AutoFillValue;
import android.widget.PopupWindow.OnDismissListener;
import com.android.internal.R;
@@ -936,24 +935,6 @@ public class Spinner extends AbsSpinner implements OnClickListener {
}
}
- @Override
- public void autoFill(AutoFillValue value) {
- if (!isEnabled()) return;
-
- final int position = value.getListValue();
- setSelection(position);
- }
-
- @Override
- public @AutofillType int getAutofillType() {
- return isEnabled() ? AUTOFILL_TYPE_LIST : AUTOFILL_TYPE_NONE;
- }
-
- @Override
- public AutoFillValue getAutoFillValue() {
- return isEnabled() ? AutoFillValue.forList(getSelectedItemPosition()) : null;
- }
-
static class SavedState extends AbsSpinner.SavedState {
boolean showDropdown;
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index edf016596ebe..b901ab4f1e4c 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -789,6 +789,11 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
+ // TextView is important by default, unless app developer overrode attribute.
+ if (getImportantForAutofill() == IMPORTANT_FOR_AUTOFILL_AUTO) {
+ setImportantForAutofill(IMPORTANT_FOR_AUTOFILL_YES);
+ }
+
mText = "";
final Resources res = getResources();
diff --git a/core/java/android/widget/TimePicker.java b/core/java/android/widget/TimePicker.java
index 327efa4f112d..3a19f2104e2c 100644
--- a/core/java/android/widget/TimePicker.java
+++ b/core/java/android/widget/TimePicker.java
@@ -111,6 +111,11 @@ public class TimePicker extends FrameLayout {
public TimePicker(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
+ // DatePicker is important by default, unless app developer overrode attribute.
+ if (getImportantForAutofill() == IMPORTANT_FOR_AUTOFILL_AUTO) {
+ setImportantForAutofill(IMPORTANT_FOR_AUTOFILL_YES);
+ }
+
final TypedArray a = context.obtainStyledAttributes(
attrs, R.styleable.TimePicker, defStyleAttr, defStyleRes);
final boolean isDialogMode = a.getBoolean(R.styleable.TimePicker_dialogMode, false);
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index f8d5241b8467..b3cb2c7d548e 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -2339,6 +2339,17 @@
<flag name="creditCardExpirationDay" value="0x1000" />
</attr>
+ <!-- Hints the Android System whether the view node associated with this View should be
+ included in a view structure used for autofill purposes. -->
+ <attr name="importantForAutofill">
+ <!-- Let the Android System use its heuristics to determine if the view is important for autofill. -->
+ <flag name="auto" value="0" />
+ <!-- Hint the Android System that this view is important for autofill. -->
+ <flag name="yes" value="0x1" />
+ <!-- Hint the Android System that this view is *not* important for autofill. -->
+ <flag name="no" value="0x2" />
+ </attr>
+
<!-- Boolean that controls whether a view can take focus while in touch mode.
If this is true for a view, that view can gain focus when clicked on, and can keep
focus if another view is clicked on that doesn't have this attribute set to true. -->
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index a1b1d7c50429..b664448ad712 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2804,6 +2804,7 @@
<public name="requiredNotFeature" />
<public name="autoFillHint" />
<public name="fontProviderPackage" />
+ <public name="importantForAutofill" />
</public-group>
<public-group type="style" first-id="0x010302e0">