diff options
| -rw-r--r-- | api/current.txt | 7 | ||||
| -rw-r--r-- | api/system-current.txt | 7 | ||||
| -rw-r--r-- | api/test-current.txt | 7 | ||||
| -rw-r--r-- | core/java/android/view/View.java | 157 | ||||
| -rw-r--r-- | core/java/android/view/ViewGroup.java | 188 | ||||
| -rw-r--r-- | core/java/android/webkit/WebView.java | 6 | ||||
| -rw-r--r-- | core/java/android/widget/AbsSpinner.java | 47 | ||||
| -rw-r--r-- | core/java/android/widget/DatePicker.java | 5 | ||||
| -rw-r--r-- | core/java/android/widget/ImageView.java | 5 | ||||
| -rw-r--r-- | core/java/android/widget/RadioGroup.java | 5 | ||||
| -rw-r--r-- | core/java/android/widget/Spinner.java | 19 | ||||
| -rw-r--r-- | core/java/android/widget/TextView.java | 5 | ||||
| -rw-r--r-- | core/java/android/widget/TimePicker.java | 5 | ||||
| -rw-r--r-- | core/res/res/values/attrs.xml | 11 | ||||
| -rw-r--r-- | core/res/res/values/public.xml | 1 |
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"> |