Skip to content

Commit df82905

Browse files
Dianne HackbornAndroid (Google) Code Review
authored andcommitted
Merge "Update the public APIs for finding views by text to optionally use content description."
2 parents fc0ab4c + ea515ae commit df82905

File tree

9 files changed

+84
-24
lines changed

9 files changed

+84
-24
lines changed

api/current.txt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22920,7 +22920,7 @@ package android.view {
2292022920
method public android.view.View findFocus();
2292122921
method public final android.view.View findViewById(int);
2292222922
method public final android.view.View findViewWithTag(java.lang.Object);
22923-
method public void findViewsWithText(java.util.ArrayList<android.view.View>, java.lang.CharSequence);
22923+
method public void findViewsWithText(java.util.ArrayList<android.view.View>, java.lang.CharSequence, int);
2292422924
method protected boolean fitSystemWindows(android.graphics.Rect);
2292522925
method public boolean fitsSystemWindows();
2292622926
method public android.view.View focusSearch(int);
@@ -23249,6 +23249,8 @@ package android.view {
2324923249
field protected static final int[] ENABLED_SELECTED_WINDOW_FOCUSED_STATE_SET;
2325023250
field protected static final int[] ENABLED_STATE_SET;
2325123251
field protected static final int[] ENABLED_WINDOW_FOCUSED_STATE_SET;
23252+
field public static final int FIND_VIEWS_WITH_CONTENT_DESCRIPTION = 2; // 0x2
23253+
field public static final int FIND_VIEWS_WITH_TEXT = 1; // 0x1
2325223254
field public static final int FOCUSABLES_ALL = 0; // 0x0
2325323255
field public static final int FOCUSABLES_TOUCH_MODE = 1; // 0x1
2325423256
field protected static final int[] FOCUSED_SELECTED_STATE_SET;

core/java/android/view/View.java

Lines changed: 34 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545
import android.os.Parcelable;
4646
import android.os.RemoteException;
4747
import android.os.SystemClock;
48+
import android.text.TextUtils;
4849
import android.util.AttributeSet;
4950
import android.util.FloatProperty;
5051
import android.util.LocaleUtil;
@@ -1927,6 +1928,20 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal
19271928
*/
19281929
public static final int PUBLIC_STATUS_BAR_VISIBILITY_MASK = 0x0000FFFF;
19291930

1931+
/**
1932+
* Find views that render the specified text.
1933+
*
1934+
* @see #findViewsWithText(ArrayList, CharSequence, int)
1935+
*/
1936+
public static final int FIND_VIEWS_WITH_TEXT = 0x00000001;
1937+
1938+
/**
1939+
* Find find views that contain the specified content description.
1940+
*
1941+
* @see #findViewsWithText(ArrayList, CharSequence, int)
1942+
*/
1943+
public static final int FIND_VIEWS_WITH_CONTENT_DESCRIPTION = 0x00000002;
1944+
19301945
/**
19311946
* Controls the over-scroll mode for this view.
19321947
* See {@link #overScrollBy(int, int, int, int, int, int, int, int, boolean)},
@@ -5132,12 +5147,28 @@ public void addFocusables(ArrayList<View> views, int direction, int focusableMod
51325147

51335148
/**
51345149
* Finds the Views that contain given text. The containment is case insensitive.
5135-
* As View's text is considered any text content that View renders.
5150+
* The search is performed by either the text that the View renders or the content
5151+
* description that describes the view for accessibility purposes and the view does
5152+
* not render or both. Clients can specify how the search is to be performed via
5153+
* passing the {@link #FIND_VIEWS_WITH_TEXT} and
5154+
* {@link #FIND_VIEWS_WITH_CONTENT_DESCRIPTION} flags.
51365155
*
51375156
* @param outViews The output list of matching Views.
5138-
* @param text The text to match against.
5157+
* @param searched The text to match against.
5158+
*
5159+
* @see #FIND_VIEWS_WITH_TEXT
5160+
* @see #FIND_VIEWS_WITH_CONTENT_DESCRIPTION
5161+
* @see #setContentDescription(CharSequence)
51395162
*/
5140-
public void findViewsWithText(ArrayList<View> outViews, CharSequence text) {
5163+
public void findViewsWithText(ArrayList<View> outViews, CharSequence searched, int flags) {
5164+
if ((flags & FIND_VIEWS_WITH_CONTENT_DESCRIPTION) != 0 && !TextUtils.isEmpty(searched)
5165+
&& !TextUtils.isEmpty(mContentDescription)) {
5166+
String searchedLowerCase = searched.toString().toLowerCase();
5167+
String contentDescriptionLowerCase = mContentDescription.toString().toLowerCase();
5168+
if (contentDescriptionLowerCase.contains(searchedLowerCase)) {
5169+
outViews.add(this);
5170+
}
5171+
}
51415172
}
51425173

51435174
/**

core/java/android/view/ViewGroup.java

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -802,13 +802,15 @@ public void addFocusables(ArrayList<View> views, int direction, int focusableMod
802802
}
803803

804804
@Override
805-
public void findViewsWithText(ArrayList<View> outViews, CharSequence text) {
805+
public void findViewsWithText(ArrayList<View> outViews, CharSequence text, int flags) {
806+
super.findViewsWithText(outViews, text, flags);
806807
final int childrenCount = mChildrenCount;
807808
final View[] children = mChildren;
808809
for (int i = 0; i < childrenCount; i++) {
809810
View child = children[i];
810-
if ((child.mPrivateFlags & IS_ROOT_NAMESPACE) == 0) {
811-
child.findViewsWithText(outViews, text);
811+
if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE
812+
&& (child.mPrivateFlags & IS_ROOT_NAMESPACE) == 0) {
813+
child.findViewsWithText(outViews, text, flags);
812814
}
813815
}
814816
}

core/java/android/view/ViewRootImpl.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4661,7 +4661,8 @@ public void findAccessibilityNodeInfosByViewTextUiThread(Message message) {
46614661
return;
46624662
}
46634663

4664-
root.findViewsWithText(foundViews, text);
4664+
root.findViewsWithText(foundViews, text, View.FIND_VIEWS_WITH_TEXT
4665+
| View.FIND_VIEWS_WITH_CONTENT_DESCRIPTION);
46654666
if (foundViews.isEmpty()) {
46664667
return;
46674668
}

core/java/android/view/accessibility/AccessibilityNodeInfo.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -261,6 +261,7 @@ public boolean performAction(int action) {
261261
* Finds {@link AccessibilityNodeInfo}s by text. The match is case
262262
* insensitive containment. The search is relative to this info i.e.
263263
* this info is the root of the traversed tree.
264+
*
264265
* <p>
265266
* <strong>Note:</strong> It is a client responsibility to recycle the
266267
* received info by calling {@link AccessibilityNodeInfo#recycle()}

core/java/android/widget/TextView.java

Lines changed: 9 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -8677,18 +8677,15 @@ protected int computeVerticalScrollExtent() {
86778677
}
86788678

86798679
@Override
8680-
public void findViewsWithText(ArrayList<View> outViews, CharSequence searched) {
8681-
if (TextUtils.isEmpty(searched)) {
8682-
return;
8683-
}
8684-
CharSequence thisText = getText();
8685-
if (TextUtils.isEmpty(thisText)) {
8686-
return;
8687-
}
8688-
String searchedLowerCase = searched.toString().toLowerCase();
8689-
String thisTextLowerCase = thisText.toString().toLowerCase();
8690-
if (thisTextLowerCase.contains(searchedLowerCase)) {
8691-
outViews.add(this);
8680+
public void findViewsWithText(ArrayList<View> outViews, CharSequence searched, int flags) {
8681+
super.findViewsWithText(outViews, searched, flags);
8682+
if (!outViews.contains(this) && (flags & FIND_VIEWS_WITH_TEXT) != 0
8683+
&& !TextUtils.isEmpty(searched) && !TextUtils.isEmpty(mText)) {
8684+
String searchedLowerCase = searched.toString().toLowerCase();
8685+
String textLowerCase = mText.toString().toLowerCase();
8686+
if (textLowerCase.contains(searchedLowerCase)) {
8687+
outViews.add(this);
8688+
}
86928689
}
86938690
}
86948691

core/tests/coretests/res/layout/interrogation_activity.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@
7070
android:layout_width="160px"
7171
android:layout_height="100px"
7272
android:text="@string/button6"
73+
android:contentDescription="contentDescription"
7374
/>
7475
</LinearLayout>
7576

core/tests/coretests/src/android/accessibilityservice/InterrogationActivityTest.java

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,29 @@ public void testFindAccessibilityNodeInfoByViewText() throws Exception {
147147
}
148148
}
149149

150+
@LargeTest
151+
public void testFindAccessibilityNodeInfoByViewTextContentDescription() throws Exception {
152+
beforeClassIfNeeded();
153+
final long startTimeMillis = SystemClock.uptimeMillis();
154+
try {
155+
// bring up the activity
156+
getActivity();
157+
158+
// find a view by text
159+
List<AccessibilityNodeInfo> buttons = AccessibilityInteractionClient.getInstance()
160+
.findAccessibilityNodeInfosByViewTextInActiveWindow(getConnection(),
161+
"contentDescription");
162+
assertEquals(1, buttons.size());
163+
} finally {
164+
afterClassIfNeeded();
165+
if (DEBUG) {
166+
final long elapsedTimeMillis = SystemClock.uptimeMillis() - startTimeMillis;
167+
Log.i(LOG_TAG, "testFindAccessibilityNodeInfoByViewTextContentDescription: "
168+
+ elapsedTimeMillis + "ms");
169+
}
170+
}
171+
}
172+
150173
@LargeTest
151174
public void testTraverseAllViews() throws Exception {
152175
beforeClassIfNeeded();

services/java/com/android/server/accessibility/AccessibilityManagerService.java

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -489,14 +489,16 @@ public IAccessibilityServiceConnection registerEventListener(IEventListener list
489489
if (oldService != null) {
490490
tryRemoveServiceLocked(oldService);
491491
}
492+
// Now this service is enabled.
493+
mEnabledServices.add(componentName);
494+
// Also make sure this service is the only one.
495+
Settings.Secure.putString(mContext.getContentResolver(),
496+
Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES,
497+
componentName.flattenToString());
492498
// This API is intended for testing so enable accessibility to make
493499
// sure clients can start poking with the window content.
494500
Settings.Secure.putInt(mContext.getContentResolver(),
495501
Settings.Secure.ACCESSIBILITY_ENABLED, 1);
496-
// Also disable all accessibility services to avoid interference
497-
// with the tests.
498-
Settings.Secure.putString(mContext.getContentResolver(),
499-
Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES, "");
500502
}
501503
AccessibilityServiceInfo accessibilityServiceInfo = new AccessibilityServiceInfo();
502504
accessibilityServiceInfo.eventTypes = AccessibilityEvent.TYPES_ALL_MASK;

0 commit comments

Comments
 (0)