diff options
| author | Svetoslav Ganov <svetoslavganov@google.com> | 2012-05-14 18:26:24 -0700 |
|---|---|---|
| committer | Android (Google) Code Review <android-gerrit@google.com> | 2012-05-14 18:26:24 -0700 |
| commit | 8ce2d78aa89e89e9a5607d8809bf6d248508a531 (patch) | |
| tree | 965c834af75dafaba5d674e949e57193f2bf77cd /core/java/android | |
| parent | a827843f9b46700a187bd3a506498a0f36f474fa (diff) | |
| parent | e5dfa47d84668376b84074c04570fb961870adeb (diff) | |
| download | frameworks_base-8ce2d78aa89e89e9a5607d8809bf6d248508a531.zip frameworks_base-8ce2d78aa89e89e9a5607d8809bf6d248508a531.tar.gz frameworks_base-8ce2d78aa89e89e9a5607d8809bf6d248508a531.tar.bz2 | |
Merge "Improving accessibility focus traversal." into jb-dev
Diffstat (limited to 'core/java/android')
| -rw-r--r-- | core/java/android/view/AccessibilityInteractionController.java | 2 | ||||
| -rw-r--r-- | core/java/android/view/FocusFinder.java | 10 | ||||
| -rw-r--r-- | core/java/android/view/View.java | 75 | ||||
| -rw-r--r-- | core/java/android/view/ViewGroup.java | 38 | ||||
| -rw-r--r-- | core/java/android/view/ViewParent.java | 12 | ||||
| -rw-r--r-- | core/java/android/view/ViewRootImpl.java | 24 | ||||
| -rw-r--r-- | core/java/android/widget/AbsListView.java | 115 | ||||
| -rw-r--r-- | core/java/android/widget/AdapterView.java | 20 |
8 files changed, 209 insertions, 87 deletions
diff --git a/core/java/android/view/AccessibilityInteractionController.java b/core/java/android/view/AccessibilityInteractionController.java index d42757d..e1f1db2 100644 --- a/core/java/android/view/AccessibilityInteractionController.java +++ b/core/java/android/view/AccessibilityInteractionController.java @@ -330,7 +330,7 @@ final class AccessibilityInteractionController { if (provider != null) { List<AccessibilityNodeInfo> infosFromProvider = provider.findAccessibilityNodeInfosByText(text, - virtualDescendantId); + AccessibilityNodeInfo.UNDEFINED); if (infosFromProvider != null) { infos.addAll(infosFromProvider); } diff --git a/core/java/android/view/FocusFinder.java b/core/java/android/view/FocusFinder.java index 6bf1888..9063cea 100644 --- a/core/java/android/view/FocusFinder.java +++ b/core/java/android/view/FocusFinder.java @@ -276,7 +276,10 @@ public class FocusFinder { return focusables.get(position + 1); } } - return focusables.get(0); + if (!focusables.isEmpty()) { + return focusables.get(0); + } + return null; } private static View getBackwardFocusable(ViewGroup root, View focused, @@ -293,7 +296,10 @@ public class FocusFinder { return focusables.get(position - 1); } } - return focusables.get(count - 1); + if (!focusables.isEmpty()) { + return focusables.get(count - 1); + } + return null; } /** diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 2f54615..14523d3 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -6027,8 +6027,7 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal return; } if ((focusableMode & FOCUSABLES_ACCESSIBILITY) == FOCUSABLES_ACCESSIBILITY) { - if (AccessibilityManager.getInstance(mContext).isEnabled() - && includeForAccessibility()) { + if (canTakeAccessibilityFocusFromHover()) { views.add(this); return; } @@ -6181,57 +6180,28 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal } } - /** - * Find the best view to take accessibility focus from a hover. - * This function finds the deepest actionable view and if that - * fails ask the parent to take accessibility focus from hover. - * - * @param x The X hovered location in this view coorditantes. - * @param y The Y hovered location in this view coorditantes. - * @return Whether the request was handled. - * - * @hide - */ - public boolean requestAccessibilityFocusFromHover(float x, float y) { - if (onRequestAccessibilityFocusFromHover(x, y)) { - return true; - } - ViewParent parent = mParent; - if (parent instanceof View) { - View parentView = (View) parent; - - float[] position = mAttachInfo.mTmpTransformLocation; - position[0] = x; - position[1] = y; - - // Compensate for the transformation of the current matrix. - if (!hasIdentityMatrix()) { - getMatrix().mapPoints(position); + private void requestAccessibilityFocusFromHover() { + if (includeForAccessibility() && isActionableForAccessibility()) { + requestAccessibilityFocus(); + } else { + if (mParent != null) { + View nextFocus = mParent.findViewToTakeAccessibilityFocusFromHover(this, this); + if (nextFocus != null) { + nextFocus.requestAccessibilityFocus(); + } } - - // Compensate for the parent scroll and the offset - // of this view stop from the parent top. - position[0] += mLeft - parentView.mScrollX; - position[1] += mTop - parentView.mScrollY; - - return parentView.requestAccessibilityFocusFromHover(position[0], position[1]); } - return false; } /** - * Requests to give this View focus from hover. - * - * @param x The X hovered location in this view coorditantes. - * @param y The Y hovered location in this view coorditantes. - * @return Whether the request was handled. - * * @hide */ - public boolean onRequestAccessibilityFocusFromHover(float x, float y) { - if (includeForAccessibility() - && (isActionableForAccessibility() || hasListenersForAccessibility())) { - return requestAccessibilityFocus(); + public boolean canTakeAccessibilityFocusFromHover() { + if (includeForAccessibility() && isActionableForAccessibility()) { + return true; + } + if (mParent != null) { + return (mParent.findViewToTakeAccessibilityFocusFromHover(this, this) == this); } return false; } @@ -6493,14 +6463,15 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal * important for accessibility are regarded. * * @return Whether to regard the view for accessibility. + * + * @hide */ - boolean includeForAccessibility() { + public boolean includeForAccessibility() { if (mAttachInfo != null) { if (!mAttachInfo.mIncludeNotImportantViews) { return isImportantForAccessibility(); - } else { - return true; } + return true; } return false; } @@ -6511,8 +6482,10 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal * accessiiblity. * * @return True if the view is actionable for accessibility. + * + * @hide */ - private boolean isActionableForAccessibility() { + public boolean isActionableForAccessibility() { return (isClickable() || isLongClickable() || isFocusable()); } @@ -7688,7 +7661,7 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal && pointInView(event.getX(), event.getY())) { sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_HOVER_ENTER); mSendingHoverAccessibilityEvents = true; - requestAccessibilityFocusFromHover((int) event.getX(), (int) event.getY()); + requestAccessibilityFocusFromHover(); } } else { if (action == MotionEvent.ACTION_HOVER_EXIT diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java index f55b7ac..b95ca5e 100644 --- a/core/java/android/view/ViewGroup.java +++ b/core/java/android/view/ViewGroup.java @@ -628,7 +628,11 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager * FOCUS_RIGHT, or 0 for not applicable. */ public View focusSearch(View focused, int direction) { - if (isRootNamespace()) { + // If we are moving accessibility focus we want to consider all + // views no matter if they are on the screen. It is responsibility + // of the accessibility service to check whether the result is in + // the screen. + if (isRootNamespace() && (direction & FOCUS_ACCESSIBILITY) == 0) { // root namespace means we should consider ourselves the top of the // tree for focus searching; otherwise we could be focus searching // into other tabs. see LocalActivityManager and TabHost for more info @@ -857,20 +861,13 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager * {@inheritDoc} */ @Override - public void addFocusables(ArrayList<View> views, int direction) { - addFocusables(views, direction, FOCUSABLES_TOUCH_MODE); - } - - /** - * {@inheritDoc} - */ - @Override public void addFocusables(ArrayList<View> views, int direction, int focusableMode) { final int focusableCount = views.size(); final int descendantFocusability = getDescendantFocusability(); - if (descendantFocusability != FOCUS_BLOCK_DESCENDANTS) { + if (descendantFocusability != FOCUS_BLOCK_DESCENDANTS + || (focusableMode & FOCUSABLES_ACCESSIBILITY) == FOCUSABLES_ACCESSIBILITY) { final int count = mChildrenCount; final View[] children = mChildren; @@ -886,10 +883,11 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager // FOCUS_AFTER_DESCENDANTS and there are some descendants focusable. this is // to avoid the focus search finding layouts when a more precise search // among the focusable children would be more interesting. - if ( - descendantFocusability != FOCUS_AFTER_DESCENDANTS || + if (descendantFocusability != FOCUS_AFTER_DESCENDANTS // No focusable descendants - (focusableCount == views.size())) { + || (focusableCount == views.size()) + // We are collecting accessibility focusables. + || (focusableMode & FOCUSABLES_ACCESSIBILITY) == FOCUSABLES_ACCESSIBILITY) { super.addFocusables(views, direction, focusableMode); } } @@ -1659,6 +1657,20 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager } /** + * @hide + */ + @Override + public View findViewToTakeAccessibilityFocusFromHover(View child, View descendant) { + if (includeForAccessibility() && isActionableForAccessibility()) { + return this; + } + if (mParent != null) { + return mParent.findViewToTakeAccessibilityFocusFromHover(this, descendant); + } + return null; + } + + /** * Implement this method to intercept hover events before they are handled * by child views. * <p> diff --git a/core/java/android/view/ViewParent.java b/core/java/android/view/ViewParent.java index ddff91d..d93b996 100644 --- a/core/java/android/view/ViewParent.java +++ b/core/java/android/view/ViewParent.java @@ -295,4 +295,16 @@ public interface ViewParent { * @hide */ public void childAccessibilityStateChanged(View child); + + /** + * A descendant requests this view to find a candidate to take accessibility + * focus from hover. + * + * @param child The child making the call. + * @param descendant The descendant that made the initial request. + * @return A view to take accessibility focus. + * + * @hide + */ + public View findViewToTakeAccessibilityFocusFromHover(View child, View descendant); } diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 90e6034..f86e036 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -2325,6 +2325,14 @@ public final class ViewRootImpl implements ViewParent, return true; } + @Override + public View findViewToTakeAccessibilityFocusFromHover(View child, View descendant) { + if (descendant.includeForAccessibility()) { + return descendant; + } + return null; + } + /** * We want to draw a highlight around the current accessibility focused. * Since adding a style for all possible view is not a viable option we @@ -2520,6 +2528,20 @@ public final class ViewRootImpl implements ViewParent, return handled; } + /** + * @hide + */ + public View getAccessibilityFocusedHost() { + return mAccessibilityFocusedHost; + } + + /** + * @hide + */ + public AccessibilityNodeInfo getAccessibilityFocusedVirtualView() { + return mAccessibilityFocusedVirtualView; + } + void setAccessibilityFocusedHost(View host) { if (mAccessibilityFocusedHost != null && mAccessibilityFocusedVirtualView == null) { mAccessibilityFocusedHost.clearAccessibilityFocusNoCallbacks(); @@ -2672,7 +2694,7 @@ public final class ViewRootImpl implements ViewParent, /** * Return true if child is an ancestor of parent, (or equal to the parent). */ - static boolean isViewDescendantOf(View child, View parent) { + public static boolean isViewDescendantOf(View child, View parent) { if (child == parent) { return true; } diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java index 3b4ec7d..ab9d370 100644 --- a/core/java/android/widget/AbsListView.java +++ b/core/java/android/widget/AbsListView.java @@ -42,6 +42,7 @@ import android.util.SparseBooleanArray; import android.util.StateSet; import android.view.ActionMode; import android.view.ContextMenu.ContextMenuInfo; +import android.view.FocusFinder; import android.view.Gravity; import android.view.HapticFeedbackConstants; import android.view.InputDevice; @@ -56,6 +57,7 @@ import android.view.ViewConfiguration; import android.view.ViewDebug; import android.view.ViewGroup; import android.view.ViewParent; +import android.view.ViewRootImpl; import android.view.ViewTreeObserver; import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityManager; @@ -1327,6 +1329,119 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te } @Override + public void addFocusables(ArrayList<View> views, int direction, int focusableMode) { + if ((focusableMode & FOCUSABLES_ACCESSIBILITY) == FOCUSABLES_ACCESSIBILITY + && (direction == ACCESSIBILITY_FOCUS_FORWARD + || direction == ACCESSIBILITY_FOCUS_BACKWARD)) { + if (canTakeAccessibilityFocusFromHover()) { + views.add(this); + } + } else { + super.addFocusables(views, direction, focusableMode); + } + } + + @Override + public View focusSearch(int direction) { + return focusSearch(null, direction); + } + + @Override + public View focusSearch(View focused, int direction) { + switch (direction) { + case ACCESSIBILITY_FOCUS_FORWARD: { + ViewRootImpl viewRootImpl = getViewRootImpl(); + if (viewRootImpl == null) { + break; + } + View currentFocus = viewRootImpl.getAccessibilityFocusedHost(); + if (currentFocus == null) { + break; + } + // If we have the focus try giving it to the first child. + if (currentFocus == this) { + final int firstVisiblePosition = getFirstVisiblePosition(); + if (firstVisiblePosition >= 0) { + return getChildAt(0); + } + return null; + } + // Find the item that has accessibility focus. + final int currentPosition = getPositionForView(currentFocus); + if (currentPosition < 0 || currentPosition >= getCount()) { + break; + } + // Try to advance focus in the current item. + View currentItem = getChildAt(currentPosition - getFirstVisiblePosition()); + if (currentItem instanceof ViewGroup) { + ViewGroup currentItemGroup = (ViewGroup) currentItem; + View nextFocus = FocusFinder.getInstance().findNextFocus(currentItemGroup, + currentFocus, direction); + if (nextFocus != null && nextFocus != currentItemGroup + && nextFocus != currentFocus) { + return nextFocus; + } + } + // Try to move focus to the next item. + final int nextPosition = currentPosition - getFirstVisiblePosition() + 1; + if (nextPosition < getChildCount()) { + return getChildAt(nextPosition); + } + } break; + case ACCESSIBILITY_FOCUS_BACKWARD: { + ViewRootImpl viewRootImpl = getViewRootImpl(); + if (viewRootImpl == null) { + break; + } + View currentFocus = viewRootImpl.getAccessibilityFocusedHost(); + if (currentFocus == null) { + break; + } + // If we have the focus do a generic search. + if (currentFocus == this) { + return super.focusSearch(this, direction); + } + // Find the item that has accessibility focus. + final int currentPosition = getPositionForView(currentFocus); + if (currentPosition < 0 || currentPosition >= getCount()) { + break; + } + // Try to advance focus in the current item. + View currentItem = getChildAt(currentPosition - getFirstVisiblePosition()); + if (currentItem instanceof ViewGroup) { + ViewGroup currentItemGroup = (ViewGroup) currentItem; + View nextFocus = FocusFinder.getInstance().findNextFocus(currentItemGroup, + currentFocus, direction); + if (nextFocus != null && nextFocus != currentItemGroup + && nextFocus != currentFocus) { + return nextFocus; + } + } + // Try to move focus to the previous item. + final int nextPosition = currentPosition - getFirstVisiblePosition() - 1; + if (nextPosition >= 0) { + return getChildAt(nextPosition); + } else { + return this; + } + } + } + return super.focusSearch(focused, direction); + } + + /** + * @hide + */ + @Override + public View findViewToTakeAccessibilityFocusFromHover(View child, View descendant) { + final int position = getPositionForView(child); + if (position != INVALID_POSITION) { + return getChildAt(position - mFirstPosition); + } + return super.findViewToTakeAccessibilityFocusFromHover(child, descendant); + } + + @Override public void sendAccessibilityEvent(int eventType) { // Since this class calls onScrollChanged even if the mFirstPosition and the // child count have not changed we will avoid sending duplicate accessibility diff --git a/core/java/android/widget/AdapterView.java b/core/java/android/widget/AdapterView.java index abfc577..502de31 100644 --- a/core/java/android/widget/AdapterView.java +++ b/core/java/android/widget/AdapterView.java @@ -24,7 +24,6 @@ import android.util.AttributeSet; import android.util.SparseArray; import android.view.ContextMenu; import android.view.ContextMenu.ContextMenuInfo; -import android.view.MotionEvent; import android.view.SoundEffectConstants; import android.view.View; import android.view.ViewDebug; @@ -32,6 +31,7 @@ import android.view.ViewGroup; import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityManager; import android.view.accessibility.AccessibilityNodeInfo; +import android.view.accessibility.AccessibilityNodeProvider; /** * An AdapterView is a view whose children are determined by an {@link Adapter}. @@ -957,24 +957,6 @@ public abstract class AdapterView<T extends Adapter> extends ViewGroup { event.setItemCount(getCount()); } - /** - * @hide - */ - @Override - public boolean onRequestAccessibilityFocusFromHover(float x, float y) { - // We prefer to five focus to the child instead of this view. - // Usually the children are not actionable for accessibility, - // and they will not take accessibility focus, so we give it. - final int childCount = getChildCount(); - for (int i = 0; i < childCount; i++) { - View child = getChildAt(i); - if (isTransformedTouchPointInView(x, y, child, null)) { - return child.requestAccessibilityFocus(); - } - } - return super.onRequestAccessibilityFocusFromHover(x, y); - } - private boolean isScrollableForAccessibility() { T adapter = getAdapter(); if (adapter != null) { |
