summaryrefslogtreecommitdiff
path: root/core/java/android/widget/NumberPicker.java
diff options
context:
space:
mode:
authorSvetoslav Ganov <svetoslavganov@google.com>2012-05-14 15:12:30 -0700
committerSvetoslav Ganov <svetoslavganov@google.com>2012-05-15 00:43:53 -0700
commit791fd31a68c59395952005886ba799169f80a29a (patch)
tree9c703d8731456162bfde906d0e3422f9927f500c /core/java/android/widget/NumberPicker.java
parent8ce2d78aa89e89e9a5607d8809bf6d248508a531 (diff)
Accessibility focus traversal in virtual nodes.
1. Finished the implementation of support for maintaining accessibility focus in view with virtual descendants. 2. Finished the NumberPicker implementation of virtual subtree such that all requred attributes are reported and ensuring that it support accessibility focus in its virtual descentants. 3. Fixed a bug where if a predecessor of the view that is accessiiblity focused is removed the accessibliity focus host in ViewRootImpl is not cleared leading to a crash when trying to draw the accessibility focus highlight.: bug:6472646 bug:6433864 Change-Id: I3645642b87b4a26025c0b2ba9dfaad92d11a48f1
Diffstat (limited to 'core/java/android/widget/NumberPicker.java')
-rw-r--r--core/java/android/widget/NumberPicker.java254
1 files changed, 242 insertions, 12 deletions
diff --git a/core/java/android/widget/NumberPicker.java b/core/java/android/widget/NumberPicker.java
index 7c809b3d2b30..78d570e9d3eb 100644
--- a/core/java/android/widget/NumberPicker.java
+++ b/core/java/android/widget/NumberPicker.java
@@ -950,6 +950,8 @@ public class NumberPicker extends LinearLayout {
provider.sendAccessibilityEventForVirtualView(hoveredVirtualViewId,
AccessibilityEvent.TYPE_VIEW_HOVER_ENTER);
mLastHoveredChildVirtualViewId = hoveredVirtualViewId;
+ provider.performAction(hoveredVirtualViewId,
+ AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS, null);
} break;
case MotionEvent.ACTION_HOVER_MOVE: {
if (mLastHoveredChildVirtualViewId != hoveredVirtualViewId
@@ -960,6 +962,8 @@ public class NumberPicker extends LinearLayout {
provider.sendAccessibilityEventForVirtualView(hoveredVirtualViewId,
AccessibilityEvent.TYPE_VIEW_HOVER_ENTER);
mLastHoveredChildVirtualViewId = hoveredVirtualViewId;
+ provider.performAction(hoveredVirtualViewId,
+ AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS, null);
}
} break;
case MotionEvent.ACTION_HOVER_EXIT: {
@@ -1413,9 +1417,16 @@ public class NumberPicker extends LinearLayout {
}
@Override
- public void sendAccessibilityEvent(int eventType) {
- // Do not send accessibility events - we want the user to
- // perceive this widget as several controls rather as a whole.
+ public void addFocusables(ArrayList<View> views, int direction, int focusableMode) {
+ // We do not want the real descendant to be considered focus search
+ // since it is managed by the accessibility node provider.
+ if ((focusableMode & FOCUSABLES_ACCESSIBILITY) == FOCUSABLES_ACCESSIBILITY) {
+ if (canTakeAccessibilityFocusFromHover() || getAccessibilityNodeProvider() != null) {
+ views.add(this);
+ return;
+ }
+ }
+ super.addFocusables(views, direction, focusableMode);
}
@Override
@@ -2072,7 +2083,12 @@ public class NumberPicker extends LinearLayout {
}
}
+ /**
+ * Class for managing virtual view tree rooted at this picker.
+ */
class AccessibilityNodeProviderImpl extends AccessibilityNodeProvider {
+ private static final int UNDEFINED = Integer.MIN_VALUE;
+
private static final int VIRTUAL_VIEW_ID_INCREMENT = 1;
private static final int VIRTUAL_VIEW_ID_INPUT = 2;
@@ -2083,6 +2099,8 @@ public class NumberPicker extends LinearLayout {
private final int[] mTempArray = new int[2];
+ private int mAccessibilityFocusedView = UNDEFINED;
+
@Override
public AccessibilityNodeInfo createAccessibilityNodeInfo(int virtualViewId) {
switch (virtualViewId) {
@@ -2137,6 +2155,25 @@ public class NumberPicker extends LinearLayout {
@Override
public boolean performAction(int virtualViewId, int action, Bundle arguments) {
switch (virtualViewId) {
+ case View.NO_ID: {
+ switch (action) {
+ case AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS: {
+ if (mAccessibilityFocusedView != virtualViewId) {
+ mAccessibilityFocusedView = virtualViewId;
+ requestAccessibilityFocus();
+ return true;
+ }
+ } return false;
+ case AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS: {
+ if (mAccessibilityFocusedView == virtualViewId) {
+ mAccessibilityFocusedView = UNDEFINED;
+ clearAccessibilityFocus();
+ return true;
+ }
+ return false;
+ }
+ }
+ } break;
case VIRTUAL_VIEW_ID_INPUT: {
switch (action) {
case AccessibilityNodeInfo.ACTION_FOCUS: {
@@ -2149,25 +2186,182 @@ public class NumberPicker extends LinearLayout {
mInputText.clearFocus();
return true;
}
- } break;
+ return false;
+ }
+ case AccessibilityNodeInfo.ACTION_CLICK: {
+ showSoftInput();
+ return true;
+ }
+ case AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS: {
+ if (mAccessibilityFocusedView != virtualViewId) {
+ mAccessibilityFocusedView = virtualViewId;
+ sendAccessibilityEventForVirtualView(virtualViewId,
+ AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED);
+ mInputText.invalidate();
+ return true;
+ }
+ } return false;
+ case AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS: {
+ if (mAccessibilityFocusedView == virtualViewId) {
+ mAccessibilityFocusedView = UNDEFINED;
+ sendAccessibilityEventForVirtualView(virtualViewId,
+ AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED);
+ mInputText.invalidate();
+ return true;
+ }
+ } return false;
+ default: {
+ return mInputText.performAccessibilityAction(action, arguments);
+ }
}
- } break;
+ } return false;
+ case VIRTUAL_VIEW_ID_INCREMENT: {
+ switch (action) {
+ case AccessibilityNodeInfo.ACTION_CLICK: {
+ NumberPicker.this.changeValueByOne(true);
+ sendAccessibilityEventForVirtualView(virtualViewId,
+ AccessibilityEvent.TYPE_VIEW_CLICKED);
+ } return true;
+ case AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS: {
+ if (mAccessibilityFocusedView != virtualViewId) {
+ mAccessibilityFocusedView = virtualViewId;
+ sendAccessibilityEventForVirtualView(virtualViewId,
+ AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED);
+ invalidate(0, mBottomSelectionDividerBottom, mRight, mBottom);
+ return true;
+ }
+ } return false;
+ case AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS: {
+ if (mAccessibilityFocusedView == virtualViewId) {
+ mAccessibilityFocusedView = UNDEFINED;
+ sendAccessibilityEventForVirtualView(virtualViewId,
+ AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED);
+ invalidate(0, mBottomSelectionDividerBottom, mRight, mBottom);
+ return true;
+ }
+ } return false;
+ }
+ } return false;
+ case VIRTUAL_VIEW_ID_DECREMENT: {
+ switch (action) {
+ case AccessibilityNodeInfo.ACTION_CLICK: {
+ final boolean increment = (virtualViewId == VIRTUAL_VIEW_ID_INCREMENT);
+ NumberPicker.this.changeValueByOne(increment);
+ sendAccessibilityEventForVirtualView(virtualViewId,
+ AccessibilityEvent.TYPE_VIEW_CLICKED);
+ } return true;
+ case AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS: {
+ if (mAccessibilityFocusedView != virtualViewId) {
+ mAccessibilityFocusedView = virtualViewId;
+ sendAccessibilityEventForVirtualView(virtualViewId,
+ AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED);
+ invalidate(0, 0, mRight, mTopSelectionDividerTop);
+ return true;
+ }
+ } return false;
+ case AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS: {
+ if (mAccessibilityFocusedView == virtualViewId) {
+ mAccessibilityFocusedView = UNDEFINED;
+ sendAccessibilityEventForVirtualView(virtualViewId,
+ AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED);
+ invalidate(0, 0, mRight, mTopSelectionDividerTop);
+ return true;
+ }
+ } return false;
+ }
+ } return false;
}
return super.performAction(virtualViewId, action, arguments);
}
+ @Override
+ public AccessibilityNodeInfo findAccessibilityFocus(int virtualViewId) {
+ return createAccessibilityNodeInfo(mAccessibilityFocusedView);
+ }
+
+ @Override
+ public AccessibilityNodeInfo accessibilityFocusSearch(int direction, int virtualViewId) {
+ switch (direction) {
+ case View.ACCESSIBILITY_FOCUS_DOWN:
+ case View.ACCESSIBILITY_FOCUS_FORWARD: {
+ switch (mAccessibilityFocusedView) {
+ case UNDEFINED: {
+ return createAccessibilityNodeInfo(View.NO_ID);
+ }
+ case View.NO_ID: {
+ if (hasVirtualDecrementButton()) {
+ return createAccessibilityNodeInfo(VIRTUAL_VIEW_ID_DECREMENT);
+ }
+ }
+ //$FALL-THROUGH$
+ case VIRTUAL_VIEW_ID_DECREMENT: {
+ return createAccessibilityNodeInfo(VIRTUAL_VIEW_ID_INPUT);
+ }
+ case VIRTUAL_VIEW_ID_INPUT: {
+ if (hasVirtualIncrementButton()) {
+ return createAccessibilityNodeInfo(VIRTUAL_VIEW_ID_INCREMENT);
+ }
+ }
+ //$FALL-THROUGH$
+ case VIRTUAL_VIEW_ID_INCREMENT: {
+ View nextFocus = NumberPicker.this.focusSearch(direction);
+ if (nextFocus != null) {
+ return nextFocus.createAccessibilityNodeInfo();
+ }
+ return null;
+ }
+ }
+ } break;
+ case View.ACCESSIBILITY_FOCUS_UP:
+ case View.ACCESSIBILITY_FOCUS_BACKWARD: {
+ switch (mAccessibilityFocusedView) {
+ case UNDEFINED: {
+ return createAccessibilityNodeInfo(View.NO_ID);
+ }
+ case View.NO_ID: {
+ if (hasVirtualIncrementButton()) {
+ return createAccessibilityNodeInfo(VIRTUAL_VIEW_ID_INCREMENT);
+ }
+ }
+ //$FALL-THROUGH$
+ case VIRTUAL_VIEW_ID_INCREMENT: {
+ return createAccessibilityNodeInfo(VIRTUAL_VIEW_ID_INPUT);
+ }
+ case VIRTUAL_VIEW_ID_INPUT: {
+ if (hasVirtualDecrementButton()) {
+ return createAccessibilityNodeInfo(VIRTUAL_VIEW_ID_DECREMENT);
+ }
+ }
+ //$FALL-THROUGH$
+ case VIRTUAL_VIEW_ID_DECREMENT: {
+ View nextFocus = NumberPicker.this.focusSearch(direction);
+ if (nextFocus != null) {
+ return nextFocus.createAccessibilityNodeInfo();
+ }
+ return null;
+ }
+ }
+ } break;
+ }
+ return null;
+ }
+
public void sendAccessibilityEventForVirtualView(int virtualViewId, int eventType) {
switch (virtualViewId) {
case VIRTUAL_VIEW_ID_DECREMENT: {
- sendAccessibilityEventForVirtualButton(virtualViewId, eventType,
- getVirtualDecrementButtonText());
+ if (hasVirtualDecrementButton()) {
+ sendAccessibilityEventForVirtualButton(virtualViewId, eventType,
+ getVirtualDecrementButtonText());
+ }
} break;
case VIRTUAL_VIEW_ID_INPUT: {
sendAccessibilityEventForVirtualText(eventType);
} break;
case VIRTUAL_VIEW_ID_INCREMENT: {
- sendAccessibilityEventForVirtualButton(virtualViewId, eventType,
- getVirtualIncrementButtonText());
+ if (hasVirtualIncrementButton()) {
+ sendAccessibilityEventForVirtualButton(virtualViewId, eventType,
+ getVirtualIncrementButtonText());
+ }
} break;
}
}
@@ -2227,8 +2421,13 @@ public class NumberPicker extends LinearLayout {
private AccessibilityNodeInfo createAccessibiltyNodeInfoForInputText() {
AccessibilityNodeInfo info = mInputText.createAccessibilityNodeInfo();
- info.setLongClickable(true);
info.setSource(NumberPicker.this, VIRTUAL_VIEW_ID_INPUT);
+ if (mAccessibilityFocusedView != VIRTUAL_VIEW_ID_INPUT) {
+ info.addAction(AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS);
+ }
+ if (mAccessibilityFocusedView == VIRTUAL_VIEW_ID_INPUT) {
+ info.addAction(AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS);
+ }
return info;
}
@@ -2252,6 +2451,15 @@ public class NumberPicker extends LinearLayout {
getLocationOnScreen(locationOnScreen);
boundsInScreen.offset(locationOnScreen[0], locationOnScreen[1]);
info.setBoundsInScreen(boundsInScreen);
+
+ if (mAccessibilityFocusedView != virtualViewId) {
+ info.addAction(AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS);
+ }
+ if (mAccessibilityFocusedView == virtualViewId) {
+ info.addAction(AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS);
+ }
+ info.addAction(AccessibilityNodeInfo.ACTION_CLICK);
+
return info;
}
@@ -2261,9 +2469,15 @@ public class NumberPicker extends LinearLayout {
info.setClassName(NumberPicker.class.getName());
info.setPackageName(mContext.getPackageName());
info.setSource(NumberPicker.this);
- info.addChild(NumberPicker.this, VIRTUAL_VIEW_ID_DECREMENT);
+
+ if (hasVirtualDecrementButton()) {
+ info.addChild(NumberPicker.this, VIRTUAL_VIEW_ID_DECREMENT);
+ }
info.addChild(NumberPicker.this, VIRTUAL_VIEW_ID_INPUT);
- info.addChild(NumberPicker.this, VIRTUAL_VIEW_ID_INCREMENT);
+ if (hasVirtualIncrementButton()) {
+ info.addChild(NumberPicker.this, VIRTUAL_VIEW_ID_INCREMENT);
+ }
+
info.setParent((View) getParent());
info.setEnabled(NumberPicker.this.isEnabled());
info.setScrollable(true);
@@ -2276,9 +2490,25 @@ public class NumberPicker extends LinearLayout {
getLocationOnScreen(locationOnScreen);
boundsInScreen.offset(locationOnScreen[0], locationOnScreen[1]);
info.setBoundsInScreen(boundsInScreen);
+
+ if (mAccessibilityFocusedView != View.NO_ID) {
+ info.addAction(AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS);
+ }
+ if (mAccessibilityFocusedView == View.NO_ID) {
+ info.addAction(AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS);
+ }
+
return info;
}
+ private boolean hasVirtualDecrementButton() {
+ return getWrapSelectorWheel() || getValue() > getMinValue();
+ }
+
+ private boolean hasVirtualIncrementButton() {
+ return getWrapSelectorWheel() || getValue() < getMaxValue();
+ }
+
private String getVirtualDecrementButtonText() {
int value = mValue - 1;
if (mWrapSelectorWheel) {