@@ -2086,7 +2086,8 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal
20862086 // Accessiblity constants for mPrivateFlags2
20872087
20882088 /**
2089- * Shift for accessibility related bits in {@link #mPrivateFlags2}.
2089+ * Shift for the bits in {@link #mPrivateFlags2} related to the
2090+ * "importantForAccessibility" attribute.
20902091 */
20912092 static final int IMPORTANT_FOR_ACCESSIBILITY_SHIFT = 20;
20922093
@@ -2142,6 +2143,72 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal
21422143 */
21432144 static final int VIEW_QUICK_REJECTED = 0x20000000;
21442145
2146+ // Accessiblity constants for mPrivateFlags2
2147+
2148+ /**
2149+ * Shift for the bits in {@link #mPrivateFlags2} related to the
2150+ * "accessibilityFocusable" attribute.
2151+ */
2152+ static final int ACCESSIBILITY_FOCUSABLE_SHIFT = 30;
2153+
2154+ /**
2155+ * The system determines whether the view can take accessibility focus - default (recommended).
2156+ * <p>
2157+ * Such a view is consideted by the focus search if it is:
2158+ * <ul>
2159+ * <li>
2160+ * Important for accessibility and actionable (clickable, long clickable, focusable)
2161+ * </li>
2162+ * <li>
2163+ * Important for accessibility, not actionable (clickable, long clickable, focusable),
2164+ * and does not have an actionable predecessor.
2165+ * </li>
2166+ * </ul>
2167+ * An accessibility srvice can request putting accessibility focus on such a view.
2168+ * </p>
2169+ *
2170+ * @hide
2171+ */
2172+ public static final int ACCESSIBILITY_FOCUSABLE_AUTO = 0x00000000;
2173+
2174+ /**
2175+ * The view can take accessibility focus.
2176+ * <p>
2177+ * A view that can take accessibility focus is always considered during focus
2178+ * search and an accessibility service can request putting accessibility focus
2179+ * on it.
2180+ * </p>
2181+ *
2182+ * @hide
2183+ */
2184+ public static final int ACCESSIBILITY_FOCUSABLE_YES = 0x00000001;
2185+
2186+ /**
2187+ * The view can not take accessibility focus.
2188+ * <p>
2189+ * A view that can not take accessibility focus is never considered during focus
2190+ * search and an accessibility service can not request putting accessibility focus
2191+ * on it.
2192+ * </p>
2193+ *
2194+ * @hide
2195+ */
2196+ public static final int ACCESSIBILITY_FOCUSABLE_NO = 0x00000002;
2197+
2198+ /**
2199+ * The default whether the view is accessiblity focusable.
2200+ */
2201+ static final int ACCESSIBILITY_FOCUSABLE_DEFAULT = ACCESSIBILITY_FOCUSABLE_AUTO;
2202+
2203+ /**
2204+ * Mask for obtainig the bits which specifies how to determine
2205+ * whether a view is accessibility focusable.
2206+ */
2207+ static final int ACCESSIBILITY_FOCUSABLE_MASK = (ACCESSIBILITY_FOCUSABLE_AUTO
2208+ | ACCESSIBILITY_FOCUSABLE_YES | ACCESSIBILITY_FOCUSABLE_NO)
2209+ << ACCESSIBILITY_FOCUSABLE_SHIFT;
2210+
2211+
21452212 /* End of masks for mPrivateFlags2 */
21462213
21472214 static final int DRAG_MASK = DRAG_CAN_ACCEPT | DRAG_HOVERED;
@@ -3132,7 +3199,8 @@ public View(Context context) {
31323199 mPrivateFlags2 = (LAYOUT_DIRECTION_DEFAULT << LAYOUT_DIRECTION_MASK_SHIFT) |
31333200 (TEXT_DIRECTION_DEFAULT << TEXT_DIRECTION_MASK_SHIFT) |
31343201 (TEXT_ALIGNMENT_DEFAULT << TEXT_ALIGNMENT_MASK_SHIFT) |
3135- (IMPORTANT_FOR_ACCESSIBILITY_DEFAULT << IMPORTANT_FOR_ACCESSIBILITY_SHIFT);
3202+ (IMPORTANT_FOR_ACCESSIBILITY_DEFAULT << IMPORTANT_FOR_ACCESSIBILITY_SHIFT) |
3203+ (ACCESSIBILITY_FOCUSABLE_DEFAULT << ACCESSIBILITY_FOCUSABLE_SHIFT);
31363204 mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
31373205 setOverScrollMode(OVER_SCROLL_IF_CONTENT_SCROLLS);
31383206 mUserPaddingStart = -1;
@@ -4788,7 +4856,10 @@ void onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info) {
47884856 }
47894857
47904858 if (!isAccessibilityFocused()) {
4791- info.addAction(AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS);
4859+ final int mode = getAccessibilityFocusable();
4860+ if (mode == ACCESSIBILITY_FOCUSABLE_YES || mode == ACCESSIBILITY_FOCUSABLE_AUTO) {
4861+ info.addAction(AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS);
4862+ }
47924863 } else {
47934864 info.addAction(AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS);
47944865 }
@@ -6069,7 +6140,7 @@ public void addFocusables(ArrayList<View> views, int direction, int focusableMod
60696140 return;
60706141 }
60716142 if ((focusableMode & FOCUSABLES_ACCESSIBILITY) == FOCUSABLES_ACCESSIBILITY) {
6072- if (canTakeAccessibilityFocusFromHover() || getAccessibilityNodeProvider() != null ) {
6143+ if (isAccessibilityFocusable() ) {
60736144 views.add(this);
60746145 return;
60756146 }
@@ -6403,12 +6474,9 @@ private boolean hasAncestorThatBlocksDescendantFocus() {
64036474 * @see #IMPORTANT_FOR_ACCESSIBILITY_AUTO
64046475 */
64056476 @ViewDebug.ExportedProperty(category = "accessibility", mapping = {
6406- @ViewDebug.IntToString(from = IMPORTANT_FOR_ACCESSIBILITY_AUTO,
6407- to = "IMPORTANT_FOR_ACCESSIBILITY_AUTO"),
6408- @ViewDebug.IntToString(from = IMPORTANT_FOR_ACCESSIBILITY_YES,
6409- to = "IMPORTANT_FOR_ACCESSIBILITY_YES"),
6410- @ViewDebug.IntToString(from = IMPORTANT_FOR_ACCESSIBILITY_NO,
6411- to = "IMPORTANT_FOR_ACCESSIBILITY_NO")
6477+ @ViewDebug.IntToString(from = IMPORTANT_FOR_ACCESSIBILITY_AUTO, to = "auto"),
6478+ @ViewDebug.IntToString(from = IMPORTANT_FOR_ACCESSIBILITY_YES, to = "yes"),
6479+ @ViewDebug.IntToString(from = IMPORTANT_FOR_ACCESSIBILITY_NO, to = "no")
64126480 })
64136481 public int getImportantForAccessibility() {
64146482 return (mPrivateFlags2 & IMPORTANT_FOR_ACCESSIBILITY_MASK)
@@ -6460,6 +6528,73 @@ public boolean isImportantForAccessibility() {
64606528 }
64616529 }
64626530
6531+ /**
6532+ * Gets the mode for determining whether this View can take accessibility focus.
6533+ *
6534+ * @return The mode for determining whether a View can take accessibility focus.
6535+ *
6536+ * @attr ref android.R.styleable#View_accessibilityFocusable
6537+ *
6538+ * @see #ACCESSIBILITY_FOCUSABLE_YES
6539+ * @see #ACCESSIBILITY_FOCUSABLE_NO
6540+ * @see #ACCESSIBILITY_FOCUSABLE_AUTO
6541+ *
6542+ * @hide
6543+ */
6544+ @ViewDebug.ExportedProperty(category = "accessibility", mapping = {
6545+ @ViewDebug.IntToString(from = ACCESSIBILITY_FOCUSABLE_AUTO, to = "auto"),
6546+ @ViewDebug.IntToString(from = ACCESSIBILITY_FOCUSABLE_YES, to = "yes"),
6547+ @ViewDebug.IntToString(from = ACCESSIBILITY_FOCUSABLE_NO, to = "no")
6548+ })
6549+ public int getAccessibilityFocusable() {
6550+ return (mPrivateFlags2 & ACCESSIBILITY_FOCUSABLE_MASK) >>> ACCESSIBILITY_FOCUSABLE_SHIFT;
6551+ }
6552+
6553+ /**
6554+ * Sets how to determine whether this view can take accessibility focus.
6555+ *
6556+ * @param mode How to determine whether this view can take accessibility focus.
6557+ *
6558+ * @attr ref android.R.styleable#View_accessibilityFocusable
6559+ *
6560+ * @see #ACCESSIBILITY_FOCUSABLE_YES
6561+ * @see #ACCESSIBILITY_FOCUSABLE_NO
6562+ * @see #ACCESSIBILITY_FOCUSABLE_AUTO
6563+ *
6564+ * @hide
6565+ */
6566+ public void setAccessibilityFocusable(int mode) {
6567+ if (mode != getAccessibilityFocusable()) {
6568+ mPrivateFlags2 &= ~ACCESSIBILITY_FOCUSABLE_MASK;
6569+ mPrivateFlags2 |= (mode << ACCESSIBILITY_FOCUSABLE_SHIFT)
6570+ & ACCESSIBILITY_FOCUSABLE_MASK;
6571+ notifyAccessibilityStateChanged();
6572+ }
6573+ }
6574+
6575+ /**
6576+ * Gets whether this view can take accessibility focus.
6577+ *
6578+ * @return Whether the view can take accessibility focus.
6579+ *
6580+ * @hide
6581+ */
6582+ public boolean isAccessibilityFocusable() {
6583+ final int mode = (mPrivateFlags2 & ACCESSIBILITY_FOCUSABLE_MASK)
6584+ >>> ACCESSIBILITY_FOCUSABLE_SHIFT;
6585+ switch (mode) {
6586+ case ACCESSIBILITY_FOCUSABLE_YES:
6587+ return true;
6588+ case ACCESSIBILITY_FOCUSABLE_NO:
6589+ return false;
6590+ case ACCESSIBILITY_FOCUSABLE_AUTO:
6591+ return canTakeAccessibilityFocusFromHover()
6592+ || getAccessibilityNodeProvider() != null;
6593+ default:
6594+ throw new IllegalArgumentException("Unknow accessibility focusable mode: " + mode);
6595+ }
6596+ }
6597+
64636598 /**
64646599 * Gets the parent for accessibility purposes. Note that the parent for
64656600 * accessibility is not necessary the immediate parent. It is the first
@@ -6641,7 +6776,10 @@ boolean performAccessibilityActionInternal(int action, Bundle arguments) {
66416776 }
66426777 } break;
66436778 case AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS: {
6644- if (!isAccessibilityFocused()) {
6779+ final int mode = getAccessibilityFocusable();
6780+ if (!isAccessibilityFocused()
6781+ && (mode == ACCESSIBILITY_FOCUSABLE_YES
6782+ || mode == ACCESSIBILITY_FOCUSABLE_AUTO)) {
66456783 return requestAccessibilityFocus();
66466784 }
66476785 } break;
0 commit comments