diff options
9 files changed, 84 insertions, 24 deletions
diff --git a/api/current.txt b/api/current.txt index 0b6c245..ce78f9e 100644 --- a/api/current.txt +++ b/api/current.txt @@ -22920,7 +22920,7 @@ package android.view { method public android.view.View findFocus(); method public final android.view.View findViewById(int); method public final android.view.View findViewWithTag(java.lang.Object); - method public void findViewsWithText(java.util.ArrayList<android.view.View>, java.lang.CharSequence); + method public void findViewsWithText(java.util.ArrayList<android.view.View>, java.lang.CharSequence, int); method protected boolean fitSystemWindows(android.graphics.Rect); method public boolean fitsSystemWindows(); method public android.view.View focusSearch(int); @@ -23249,6 +23249,8 @@ package android.view { field protected static final int[] ENABLED_SELECTED_WINDOW_FOCUSED_STATE_SET; field protected static final int[] ENABLED_STATE_SET; field protected static final int[] ENABLED_WINDOW_FOCUSED_STATE_SET; + field public static final int FIND_VIEWS_WITH_CONTENT_DESCRIPTION = 2; // 0x2 + field public static final int FIND_VIEWS_WITH_TEXT = 1; // 0x1 field public static final int FOCUSABLES_ALL = 0; // 0x0 field public static final int FOCUSABLES_TOUCH_MODE = 1; // 0x1 field protected static final int[] FOCUSED_SELECTED_STATE_SET; diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 65e9857..ca06b9c 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -45,6 +45,7 @@ import android.os.Parcel; import android.os.Parcelable; import android.os.RemoteException; import android.os.SystemClock; +import android.text.TextUtils; import android.util.AttributeSet; import android.util.FloatProperty; import android.util.LocaleUtil; @@ -1928,6 +1929,20 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal public static final int PUBLIC_STATUS_BAR_VISIBILITY_MASK = 0x0000FFFF; /** + * Find views that render the specified text. + * + * @see #findViewsWithText(ArrayList, CharSequence, int) + */ + public static final int FIND_VIEWS_WITH_TEXT = 0x00000001; + + /** + * Find find views that contain the specified content description. + * + * @see #findViewsWithText(ArrayList, CharSequence, int) + */ + public static final int FIND_VIEWS_WITH_CONTENT_DESCRIPTION = 0x00000002; + + /** * Controls the over-scroll mode for this view. * See {@link #overScrollBy(int, int, int, int, int, int, int, int, boolean)}, * {@link #OVER_SCROLL_ALWAYS}, {@link #OVER_SCROLL_IF_CONTENT_SCROLLS}, @@ -5132,12 +5147,28 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal /** * Finds the Views that contain given text. The containment is case insensitive. - * As View's text is considered any text content that View renders. + * The search is performed by either the text that the View renders or the content + * description that describes the view for accessibility purposes and the view does + * not render or both. Clients can specify how the search is to be performed via + * passing the {@link #FIND_VIEWS_WITH_TEXT} and + * {@link #FIND_VIEWS_WITH_CONTENT_DESCRIPTION} flags. * * @param outViews The output list of matching Views. - * @param text The text to match against. + * @param searched The text to match against. + * + * @see #FIND_VIEWS_WITH_TEXT + * @see #FIND_VIEWS_WITH_CONTENT_DESCRIPTION + * @see #setContentDescription(CharSequence) */ - public void findViewsWithText(ArrayList<View> outViews, CharSequence text) { + public void findViewsWithText(ArrayList<View> outViews, CharSequence searched, int flags) { + if ((flags & FIND_VIEWS_WITH_CONTENT_DESCRIPTION) != 0 && !TextUtils.isEmpty(searched) + && !TextUtils.isEmpty(mContentDescription)) { + String searchedLowerCase = searched.toString().toLowerCase(); + String contentDescriptionLowerCase = mContentDescription.toString().toLowerCase(); + if (contentDescriptionLowerCase.contains(searchedLowerCase)) { + outViews.add(this); + } + } } /** diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java index 1bd0782..c7b59b8 100644 --- a/core/java/android/view/ViewGroup.java +++ b/core/java/android/view/ViewGroup.java @@ -802,13 +802,15 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager } @Override - public void findViewsWithText(ArrayList<View> outViews, CharSequence text) { + public void findViewsWithText(ArrayList<View> outViews, CharSequence text, int flags) { + super.findViewsWithText(outViews, text, flags); final int childrenCount = mChildrenCount; final View[] children = mChildren; for (int i = 0; i < childrenCount; i++) { View child = children[i]; - if ((child.mPrivateFlags & IS_ROOT_NAMESPACE) == 0) { - child.findViewsWithText(outViews, text); + if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE + && (child.mPrivateFlags & IS_ROOT_NAMESPACE) == 0) { + child.findViewsWithText(outViews, text, flags); } } } diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 81f9d78..4611984 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -4661,7 +4661,8 @@ public final class ViewRootImpl extends Handler implements ViewParent, return; } - root.findViewsWithText(foundViews, text); + root.findViewsWithText(foundViews, text, View.FIND_VIEWS_WITH_TEXT + | View.FIND_VIEWS_WITH_CONTENT_DESCRIPTION); if (foundViews.isEmpty()) { return; } diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java index f0e8005..7671312 100644 --- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java +++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java @@ -261,6 +261,7 @@ public class AccessibilityNodeInfo implements Parcelable { * Finds {@link AccessibilityNodeInfo}s by text. The match is case * insensitive containment. The search is relative to this info i.e. * this info is the root of the traversed tree. + * * <p> * <strong>Note:</strong> It is a client responsibility to recycle the * received info by calling {@link AccessibilityNodeInfo#recycle()} diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index edb1bfc..d78a7a3 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -8677,18 +8677,15 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } @Override - public void findViewsWithText(ArrayList<View> outViews, CharSequence searched) { - if (TextUtils.isEmpty(searched)) { - return; - } - CharSequence thisText = getText(); - if (TextUtils.isEmpty(thisText)) { - return; - } - String searchedLowerCase = searched.toString().toLowerCase(); - String thisTextLowerCase = thisText.toString().toLowerCase(); - if (thisTextLowerCase.contains(searchedLowerCase)) { - outViews.add(this); + public void findViewsWithText(ArrayList<View> outViews, CharSequence searched, int flags) { + super.findViewsWithText(outViews, searched, flags); + if (!outViews.contains(this) && (flags & FIND_VIEWS_WITH_TEXT) != 0 + && !TextUtils.isEmpty(searched) && !TextUtils.isEmpty(mText)) { + String searchedLowerCase = searched.toString().toLowerCase(); + String textLowerCase = mText.toString().toLowerCase(); + if (textLowerCase.contains(searchedLowerCase)) { + outViews.add(this); + } } } diff --git a/core/tests/coretests/res/layout/interrogation_activity.xml b/core/tests/coretests/res/layout/interrogation_activity.xml index 44ed75c..64af321 100644 --- a/core/tests/coretests/res/layout/interrogation_activity.xml +++ b/core/tests/coretests/res/layout/interrogation_activity.xml @@ -70,6 +70,7 @@ android:layout_width="160px" android:layout_height="100px" android:text="@string/button6" + android:contentDescription="contentDescription" /> </LinearLayout> diff --git a/core/tests/coretests/src/android/accessibilityservice/InterrogationActivityTest.java b/core/tests/coretests/src/android/accessibilityservice/InterrogationActivityTest.java index a542a1b..cd8dcb9 100644 --- a/core/tests/coretests/src/android/accessibilityservice/InterrogationActivityTest.java +++ b/core/tests/coretests/src/android/accessibilityservice/InterrogationActivityTest.java @@ -148,6 +148,29 @@ public class InterrogationActivityTest } @LargeTest + public void testFindAccessibilityNodeInfoByViewTextContentDescription() throws Exception { + beforeClassIfNeeded(); + final long startTimeMillis = SystemClock.uptimeMillis(); + try { + // bring up the activity + getActivity(); + + // find a view by text + List<AccessibilityNodeInfo> buttons = AccessibilityInteractionClient.getInstance() + .findAccessibilityNodeInfosByViewTextInActiveWindow(getConnection(), + "contentDescription"); + assertEquals(1, buttons.size()); + } finally { + afterClassIfNeeded(); + if (DEBUG) { + final long elapsedTimeMillis = SystemClock.uptimeMillis() - startTimeMillis; + Log.i(LOG_TAG, "testFindAccessibilityNodeInfoByViewTextContentDescription: " + + elapsedTimeMillis + "ms"); + } + } + } + + @LargeTest public void testTraverseAllViews() throws Exception { beforeClassIfNeeded(); final long startTimeMillis = SystemClock.uptimeMillis(); diff --git a/services/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/java/com/android/server/accessibility/AccessibilityManagerService.java index 10d384b..6830055 100644 --- a/services/java/com/android/server/accessibility/AccessibilityManagerService.java +++ b/services/java/com/android/server/accessibility/AccessibilityManagerService.java @@ -489,14 +489,16 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub if (oldService != null) { tryRemoveServiceLocked(oldService); } + // Now this service is enabled. + mEnabledServices.add(componentName); + // Also make sure this service is the only one. + Settings.Secure.putString(mContext.getContentResolver(), + Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES, + componentName.flattenToString()); // This API is intended for testing so enable accessibility to make // sure clients can start poking with the window content. Settings.Secure.putInt(mContext.getContentResolver(), Settings.Secure.ACCESSIBILITY_ENABLED, 1); - // Also disable all accessibility services to avoid interference - // with the tests. - Settings.Secure.putString(mContext.getContentResolver(), - Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES, ""); } AccessibilityServiceInfo accessibilityServiceInfo = new AccessibilityServiceInfo(); accessibilityServiceInfo.eventTypes = AccessibilityEvent.TYPES_ALL_MASK; |