Skip to content

Commit f9817f7

Browse files
committed
Adding accessibility focusable attribute (hidden for now).
1. This attribute specifies whether a view can take accessibility focus. It has three values: 1) auto - the system determines based on whether the view is actionable and has actionable predecessor. Accessibility services can put accessibility focus on such a node at will; 2) yes ; this view always takes access focus; 3) no - the view cannot takes accessibility focus and accessibility services cannot put accessibility focus on it. Change-Id: I2ebf4e7c75bf6b39e1742b6868b37ccdd4cc7d28
1 parent a587b89 commit f9817f7

File tree

2 files changed

+185
-12
lines changed

2 files changed

+185
-12
lines changed

core/java/android/view/View.java

Lines changed: 149 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -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;

core/res/res/values/attrs.xml

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2136,14 +2136,49 @@
21362136
query the screen. Note: While not recommended, an accessibility service may
21372137
decide to ignore this attribute and operate on all views in the view tree. -->
21382138
<attr name="importantForAccessibility" format="integer">
2139-
<!-- The system determines whether the view is important for accessibility (recommended). -->
2139+
<!-- The system determines whether the view is important for accessibility - default
2140+
(recommended). -->
21402141
<enum name="auto" value="0" />
21412142
<!-- The view is important for accessibility. -->
21422143
<enum name="yes" value="1" />
21432144
<!-- The view is not important for accessibility. -->
21442145
<enum name="no" value="2" />
21452146
</attr>
21462147

2148+
<!-- @hide Controls whether this view can take accessibility focus. -->
2149+
<attr name="accessibilityFocusable" format="integer">
2150+
<!-- The system determines whether the view can take accessibility focus - default
2151+
(recommended).
2152+
<p>
2153+
Such a view is consideted by the focus search if it is:
2154+
<ul>
2155+
<li>
2156+
Important for accessibility and actionable (clickable, long clickable, focusable)
2157+
</li>
2158+
<li>
2159+
Important for accessibility, not actionable (clickable, long clickable, focusable),
2160+
and does not have an actionable predecessor.
2161+
</li>
2162+
</ul>
2163+
An accessibility srvice can request putting accessibility focus on such a view.
2164+
</p> -->
2165+
<enum name="auto" value="0" />
2166+
<!-- The view can take accessibility focus.
2167+
<p>
2168+
A view that can take accessibility focus is always considered during focus
2169+
search and an accessibility service can request putting accessibility focus
2170+
on it.
2171+
</p> -->
2172+
<enum name="yes" value="1" />
2173+
<!-- The view can not take accessibility focus.
2174+
<p>
2175+
A view that can not take accessibility focus is never considered during focus
2176+
search and an accessibility service can not request putting accessibility focus
2177+
on it.
2178+
</p> -->
2179+
<enum name="no" value="2" />
2180+
</attr>
2181+
21472182
</declare-styleable>
21482183

21492184
<!-- Attributes that can be used with a {@link android.view.ViewGroup} or any

0 commit comments

Comments
 (0)