blob: 1b74c13be5188b83688b5fbdea793df5a1e323a4 [file] [log] [blame]
Svetoslav Ganov42138042012-03-20 11:51:39 -07001/*
2 * Copyright (C) 2012 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.view;
18
Phil Weaver193520e2016-12-13 09:39:06 -080019import static android.view.accessibility.AccessibilityNodeInfo.ACTION_ARGUMENT_ACCESSIBLE_CLICKABLE_SPAN;
Phil Weaverc2e28932016-12-08 12:29:25 -080020import static android.view.accessibility.AccessibilityNodeInfo.EXTRA_DATA_TEXT_CHARACTER_LOCATION_KEY;
Phil Weaver193520e2016-12-13 09:39:06 -080021
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -070022import android.graphics.Point;
Svetoslav Ganovfd8d9c42012-07-09 18:16:55 -070023import android.graphics.Rect;
Phil Weaverc2e28932016-12-08 12:29:25 -080024import android.graphics.RectF;
Svetoslav9ae9ed22014-09-02 16:36:35 -070025import android.graphics.Region;
Nirmal Patel386a8242015-06-02 18:11:32 -070026import android.os.Binder;
Svetoslav Ganovaa780c12012-04-19 23:01:39 -070027import android.os.Bundle;
Svetoslav Ganov42138042012-03-20 11:51:39 -070028import android.os.Handler;
29import android.os.Looper;
30import android.os.Message;
Phil Weaver193520e2016-12-13 09:39:06 -080031import android.os.Parcelable;
Svetoslav Ganov42138042012-03-20 11:51:39 -070032import android.os.Process;
33import android.os.RemoteException;
Phil Weaver193520e2016-12-13 09:39:06 -080034import android.text.style.AccessibilityClickableSpan;
35import android.text.style.ClickableSpan;
Svetoslav8e3feb12014-02-24 13:46:47 -080036import android.util.LongSparseArray;
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -070037import android.view.View.AttachInfo;
Svetoslav Ganov42138042012-03-20 11:51:39 -070038import android.view.accessibility.AccessibilityInteractionClient;
39import android.view.accessibility.AccessibilityNodeInfo;
40import android.view.accessibility.AccessibilityNodeProvider;
41import android.view.accessibility.IAccessibilityInteractionConnectionCallback;
42
Phil Weaver193520e2016-12-13 09:39:06 -080043import com.android.internal.R;
Svetoslav Ganov758143e2012-08-06 16:40:27 -070044import com.android.internal.os.SomeArgs;
45
Svetoslav Ganov42138042012-03-20 11:51:39 -070046import java.util.ArrayList;
47import java.util.HashMap;
Svetoslav8e3feb12014-02-24 13:46:47 -080048import java.util.HashSet;
49import java.util.LinkedList;
Svetoslav Ganov42138042012-03-20 11:51:39 -070050import java.util.List;
51import java.util.Map;
Svetoslav8e3feb12014-02-24 13:46:47 -080052import java.util.Queue;
Paul Duffinca4964c2017-02-07 15:04:10 +000053import java.util.function.Predicate;
Svetoslav Ganov42138042012-03-20 11:51:39 -070054
55/**
56 * Class for managing accessibility interactions initiated from the system
57 * and targeting the view hierarchy. A *ClientThread method is to be
58 * called from the interaction connection ViewAncestor gives the system to
59 * talk to it and a corresponding *UiThread method that is executed on the
60 * UI thread.
61 */
62final class AccessibilityInteractionController {
Svetoslav Ganov42138042012-03-20 11:51:39 -070063
Svetoslavaaa11422014-03-28 13:31:13 -070064 private static final boolean ENFORCE_NODE_TREE_CONSISTENT = false;
Svetoslav8e3feb12014-02-24 13:46:47 -080065
Svetoslav Ganov80943d82013-01-02 10:25:37 -080066 private final ArrayList<AccessibilityNodeInfo> mTempAccessibilityNodeInfoList =
Svetoslav Ganov42138042012-03-20 11:51:39 -070067 new ArrayList<AccessibilityNodeInfo>();
68
Svetoslav Ganov005b83b2012-04-16 18:17:17 -070069 private final Handler mHandler;
Svetoslav Ganov42138042012-03-20 11:51:39 -070070
71 private final ViewRootImpl mViewRootImpl;
72
73 private final AccessibilityNodePrefetcher mPrefetcher;
74
Svetoslav Ganov749e7962012-04-19 17:13:46 -070075 private final long mMyLooperThreadId;
76
77 private final int mMyProcessId;
78
Svetoslav Ganov30ac6452012-06-01 09:10:25 -070079 private final ArrayList<View> mTempArrayList = new ArrayList<View>();
80
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -070081 private final Point mTempPoint = new Point();
Svetoslav Ganovfd8d9c42012-07-09 18:16:55 -070082 private final Rect mTempRect = new Rect();
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -070083 private final Rect mTempRect1 = new Rect();
84 private final Rect mTempRect2 = new Rect();
Svetoslav Ganovfd8d9c42012-07-09 18:16:55 -070085
Svetoslav Ganov80943d82013-01-02 10:25:37 -080086 private AddNodeInfosForViewId mAddNodeInfosForViewId;
87
Svetoslav Ganov42138042012-03-20 11:51:39 -070088 public AccessibilityInteractionController(ViewRootImpl viewRootImpl) {
Svetoslav Ganov749e7962012-04-19 17:13:46 -070089 Looper looper = viewRootImpl.mHandler.getLooper();
90 mMyLooperThreadId = looper.getThread().getId();
91 mMyProcessId = Process.myPid();
Svetoslav Ganov005b83b2012-04-16 18:17:17 -070092 mHandler = new PrivateHandler(looper);
Svetoslav Ganov42138042012-03-20 11:51:39 -070093 mViewRootImpl = viewRootImpl;
94 mPrefetcher = new AccessibilityNodePrefetcher();
95 }
96
Phil Weaverc2e28932016-12-08 12:29:25 -080097 private void scheduleMessage(Message message, int interrogatingPid, long interrogatingTid) {
98 // If the interrogation is performed by the same thread as the main UI
99 // thread in this process, set the message as a static reference so
100 // after this call completes the same thread but in the interrogating
101 // client can handle the message to generate the result.
102 if (interrogatingPid == mMyProcessId && interrogatingTid == mMyLooperThreadId) {
103 AccessibilityInteractionClient.getInstanceForThread(
104 interrogatingTid).setSameThreadMessage(message);
105 } else {
106 mHandler.sendMessage(message);
107 }
108 }
109
Svetoslav Ganov0a1bb6d2012-05-07 11:54:39 -0700110 private boolean isShown(View view) {
111 // The first two checks are made also made by isShown() which
112 // however traverses the tree up to the parent to catch that.
113 // Therefore, we do some fail fast check to minimize the up
114 // tree traversal.
115 return (view.mAttachInfo != null
116 && view.mAttachInfo.mWindowVisibility == View.VISIBLE
117 && view.isShown());
118 }
119
Svetoslav Ganov42138042012-03-20 11:51:39 -0700120 public void findAccessibilityNodeInfoByAccessibilityIdClientThread(
Svetoslav9ae9ed22014-09-02 16:36:35 -0700121 long accessibilityNodeId, Region interactiveRegion, int interactionId,
Svetoslav Ganov42138042012-03-20 11:51:39 -0700122 IAccessibilityInteractionConnectionCallback callback, int flags, int interrogatingPid,
Phil Weaverc2e28932016-12-08 12:29:25 -0800123 long interrogatingTid, MagnificationSpec spec, Bundle arguments) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700124 Message message = mHandler.obtainMessage();
Svet Ganov7498efd2014-09-03 21:33:00 -0700125 message.what = PrivateHandler.MSG_FIND_ACCESSIBILITY_NODE_INFO_BY_ACCESSIBILITY_ID;
Svetoslav Ganov42138042012-03-20 11:51:39 -0700126 message.arg1 = flags;
Svetoslav Ganov86783472012-06-06 21:12:20 -0700127
Svetoslav Ganov758143e2012-08-06 16:40:27 -0700128 SomeArgs args = SomeArgs.obtain();
Svetoslav Ganov42138042012-03-20 11:51:39 -0700129 args.argi1 = AccessibilityNodeInfo.getAccessibilityViewId(accessibilityNodeId);
130 args.argi2 = AccessibilityNodeInfo.getVirtualDescendantId(accessibilityNodeId);
131 args.argi3 = interactionId;
132 args.arg1 = callback;
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700133 args.arg2 = spec;
Svetoslav9ae9ed22014-09-02 16:36:35 -0700134 args.arg3 = interactiveRegion;
Phil Weaverc2e28932016-12-08 12:29:25 -0800135 args.arg4 = arguments;
Svetoslav Ganov42138042012-03-20 11:51:39 -0700136 message.obj = args;
Svetoslav Ganov86783472012-06-06 21:12:20 -0700137
Phil Weaverc2e28932016-12-08 12:29:25 -0800138 scheduleMessage(message, interrogatingPid, interrogatingTid);
Svetoslav Ganov42138042012-03-20 11:51:39 -0700139 }
140
141 private void findAccessibilityNodeInfoByAccessibilityIdUiThread(Message message) {
142 final int flags = message.arg1;
Svetoslav Ganov86783472012-06-06 21:12:20 -0700143
Svetoslav Ganov42138042012-03-20 11:51:39 -0700144 SomeArgs args = (SomeArgs) message.obj;
145 final int accessibilityViewId = args.argi1;
146 final int virtualDescendantId = args.argi2;
147 final int interactionId = args.argi3;
148 final IAccessibilityInteractionConnectionCallback callback =
149 (IAccessibilityInteractionConnectionCallback) args.arg1;
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700150 final MagnificationSpec spec = (MagnificationSpec) args.arg2;
Svetoslav9ae9ed22014-09-02 16:36:35 -0700151 final Region interactiveRegion = (Region) args.arg3;
Phil Weaverc2e28932016-12-08 12:29:25 -0800152 final Bundle arguments = (Bundle) args.arg4;
Svetoslav Ganov86783472012-06-06 21:12:20 -0700153
Svetoslav Ganov758143e2012-08-06 16:40:27 -0700154 args.recycle();
Svetoslav Ganov86783472012-06-06 21:12:20 -0700155
Svetoslav Ganov42138042012-03-20 11:51:39 -0700156 List<AccessibilityNodeInfo> infos = mTempAccessibilityNodeInfoList;
157 infos.clear();
158 try {
159 if (mViewRootImpl.mView == null || mViewRootImpl.mAttachInfo == null) {
160 return;
161 }
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800162 mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = flags;
Svetoslav Ganov42138042012-03-20 11:51:39 -0700163 View root = null;
Phil Weaverf00cd142017-03-03 13:44:00 -0800164 if (accessibilityViewId == AccessibilityNodeInfo.ROOT_ITEM_ID) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700165 root = mViewRootImpl.mView;
166 } else {
167 root = findViewByAccessibilityId(accessibilityViewId);
168 }
Svetoslav Ganov0a1bb6d2012-05-07 11:54:39 -0700169 if (root != null && isShown(root)) {
Phil Weaverc2e28932016-12-08 12:29:25 -0800170 mPrefetcher.prefetchAccessibilityNodeInfos(
171 root, virtualDescendantId, flags, infos, arguments);
Svetoslav Ganov42138042012-03-20 11:51:39 -0700172 }
173 } finally {
Phil Weaverc2e28932016-12-08 12:29:25 -0800174 updateInfosForViewportAndReturnFindNodeResult(
175 infos, callback, interactionId, spec, interactiveRegion);
Svetoslav Ganov42138042012-03-20 11:51:39 -0700176 }
177 }
178
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800179 public void findAccessibilityNodeInfosByViewIdClientThread(long accessibilityNodeId,
Svetoslav9ae9ed22014-09-02 16:36:35 -0700180 String viewId, Region interactiveRegion, int interactionId,
181 IAccessibilityInteractionConnectionCallback callback, int flags, int interrogatingPid,
182 long interrogatingTid, MagnificationSpec spec) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700183 Message message = mHandler.obtainMessage();
Svet Ganov7498efd2014-09-03 21:33:00 -0700184 message.what = PrivateHandler.MSG_FIND_ACCESSIBILITY_NODE_INFOS_BY_VIEW_ID;
Svetoslav Ganov42138042012-03-20 11:51:39 -0700185 message.arg1 = flags;
186 message.arg2 = AccessibilityNodeInfo.getAccessibilityViewId(accessibilityNodeId);
Svetoslav Ganov86783472012-06-06 21:12:20 -0700187
Svetoslav Ganov758143e2012-08-06 16:40:27 -0700188 SomeArgs args = SomeArgs.obtain();
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800189 args.argi1 = interactionId;
Svetoslav Ganov42138042012-03-20 11:51:39 -0700190 args.arg1 = callback;
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700191 args.arg2 = spec;
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800192 args.arg3 = viewId;
Svetoslav9ae9ed22014-09-02 16:36:35 -0700193 args.arg4 = interactiveRegion;
Svetoslav Ganov42138042012-03-20 11:51:39 -0700194 message.obj = args;
Svetoslav Ganov86783472012-06-06 21:12:20 -0700195
Phil Weaverc2e28932016-12-08 12:29:25 -0800196 scheduleMessage(message, interrogatingPid, interrogatingTid);
Svetoslav Ganov42138042012-03-20 11:51:39 -0700197 }
198
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800199 private void findAccessibilityNodeInfosByViewIdUiThread(Message message) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700200 final int flags = message.arg1;
201 final int accessibilityViewId = message.arg2;
Svetoslav Ganov86783472012-06-06 21:12:20 -0700202
Svetoslav Ganov42138042012-03-20 11:51:39 -0700203 SomeArgs args = (SomeArgs) message.obj;
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800204 final int interactionId = args.argi1;
Svetoslav Ganov42138042012-03-20 11:51:39 -0700205 final IAccessibilityInteractionConnectionCallback callback =
206 (IAccessibilityInteractionConnectionCallback) args.arg1;
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700207 final MagnificationSpec spec = (MagnificationSpec) args.arg2;
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800208 final String viewId = (String) args.arg3;
Svetoslav9ae9ed22014-09-02 16:36:35 -0700209 final Region interactiveRegion = (Region) args.arg4;
Svetoslav Ganov758143e2012-08-06 16:40:27 -0700210 args.recycle();
Svetoslav Ganov86783472012-06-06 21:12:20 -0700211
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800212 final List<AccessibilityNodeInfo> infos = mTempAccessibilityNodeInfoList;
213 infos.clear();
Svetoslav Ganov42138042012-03-20 11:51:39 -0700214 try {
215 if (mViewRootImpl.mView == null || mViewRootImpl.mAttachInfo == null) {
216 return;
217 }
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800218 mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = flags;
Svetoslav Ganov42138042012-03-20 11:51:39 -0700219 View root = null;
Phil Weaverf00cd142017-03-03 13:44:00 -0800220 if (accessibilityViewId != AccessibilityNodeInfo.ROOT_ITEM_ID) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700221 root = findViewByAccessibilityId(accessibilityViewId);
222 } else {
223 root = mViewRootImpl.mView;
224 }
225 if (root != null) {
Svetoslava33243e2013-02-11 15:43:46 -0800226 final int resolvedViewId = root.getContext().getResources()
227 .getIdentifier(viewId, null, null);
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800228 if (resolvedViewId <= 0) {
229 return;
230 }
231 if (mAddNodeInfosForViewId == null) {
232 mAddNodeInfosForViewId = new AddNodeInfosForViewId();
233 }
234 mAddNodeInfosForViewId.init(resolvedViewId, infos);
235 root.findViewByPredicate(mAddNodeInfosForViewId);
236 mAddNodeInfosForViewId.reset();
Svetoslav Ganov42138042012-03-20 11:51:39 -0700237 }
238 } finally {
Phil Weaverc2e28932016-12-08 12:29:25 -0800239 updateInfosForViewportAndReturnFindNodeResult(
240 infos, callback, interactionId, spec, interactiveRegion);
Svetoslav Ganov42138042012-03-20 11:51:39 -0700241 }
242 }
243
244 public void findAccessibilityNodeInfosByTextClientThread(long accessibilityNodeId,
Svetoslav9ae9ed22014-09-02 16:36:35 -0700245 String text, Region interactiveRegion, int interactionId,
246 IAccessibilityInteractionConnectionCallback callback, int flags, int interrogatingPid,
247 long interrogatingTid, MagnificationSpec spec) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700248 Message message = mHandler.obtainMessage();
Svet Ganov7498efd2014-09-03 21:33:00 -0700249 message.what = PrivateHandler.MSG_FIND_ACCESSIBILITY_NODE_INFO_BY_TEXT;
Svetoslav Ganov42138042012-03-20 11:51:39 -0700250 message.arg1 = flags;
Svetoslav Ganov86783472012-06-06 21:12:20 -0700251
Svetoslav Ganov758143e2012-08-06 16:40:27 -0700252 SomeArgs args = SomeArgs.obtain();
Svetoslav Ganov42138042012-03-20 11:51:39 -0700253 args.arg1 = text;
Svetoslav Ganovc9c9a482012-07-16 08:46:07 -0700254 args.arg2 = callback;
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700255 args.arg3 = spec;
Svetoslav Ganov42138042012-03-20 11:51:39 -0700256 args.argi1 = AccessibilityNodeInfo.getAccessibilityViewId(accessibilityNodeId);
257 args.argi2 = AccessibilityNodeInfo.getVirtualDescendantId(accessibilityNodeId);
258 args.argi3 = interactionId;
Svetoslav9ae9ed22014-09-02 16:36:35 -0700259 args.arg4 = interactiveRegion;
Svetoslav Ganov42138042012-03-20 11:51:39 -0700260 message.obj = args;
Svetoslav Ganov86783472012-06-06 21:12:20 -0700261
Phil Weaverc2e28932016-12-08 12:29:25 -0800262 scheduleMessage(message, interrogatingPid, interrogatingTid);
Svetoslav Ganov42138042012-03-20 11:51:39 -0700263 }
264
265 private void findAccessibilityNodeInfosByTextUiThread(Message message) {
266 final int flags = message.arg1;
Svetoslav Ganov86783472012-06-06 21:12:20 -0700267
Svetoslav Ganov42138042012-03-20 11:51:39 -0700268 SomeArgs args = (SomeArgs) message.obj;
269 final String text = (String) args.arg1;
Svetoslav Ganovc9c9a482012-07-16 08:46:07 -0700270 final IAccessibilityInteractionConnectionCallback callback =
271 (IAccessibilityInteractionConnectionCallback) args.arg2;
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700272 final MagnificationSpec spec = (MagnificationSpec) args.arg3;
Svetoslav Ganov42138042012-03-20 11:51:39 -0700273 final int accessibilityViewId = args.argi1;
274 final int virtualDescendantId = args.argi2;
275 final int interactionId = args.argi3;
Svetoslav9ae9ed22014-09-02 16:36:35 -0700276 final Region interactiveRegion = (Region) args.arg4;
Svetoslav Ganov758143e2012-08-06 16:40:27 -0700277 args.recycle();
Svetoslav Ganov86783472012-06-06 21:12:20 -0700278
Svetoslav Ganov42138042012-03-20 11:51:39 -0700279 List<AccessibilityNodeInfo> infos = null;
280 try {
281 if (mViewRootImpl.mView == null || mViewRootImpl.mAttachInfo == null) {
282 return;
283 }
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800284 mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = flags;
Svetoslav Ganov42138042012-03-20 11:51:39 -0700285 View root = null;
Phil Weaverf00cd142017-03-03 13:44:00 -0800286 if (accessibilityViewId != AccessibilityNodeInfo.ROOT_ITEM_ID) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700287 root = findViewByAccessibilityId(accessibilityViewId);
288 } else {
289 root = mViewRootImpl.mView;
290 }
Svetoslav Ganov0a1bb6d2012-05-07 11:54:39 -0700291 if (root != null && isShown(root)) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700292 AccessibilityNodeProvider provider = root.getAccessibilityNodeProvider();
293 if (provider != null) {
Phil Weaverf00cd142017-03-03 13:44:00 -0800294 infos = provider.findAccessibilityNodeInfosByText(text,
295 virtualDescendantId);
296 } else if (virtualDescendantId == AccessibilityNodeProvider.HOST_VIEW_ID) {
Svetoslav Ganov30ac6452012-06-01 09:10:25 -0700297 ArrayList<View> foundViews = mTempArrayList;
Svetoslav Ganov42138042012-03-20 11:51:39 -0700298 foundViews.clear();
299 root.findViewsWithText(foundViews, text, View.FIND_VIEWS_WITH_TEXT
300 | View.FIND_VIEWS_WITH_CONTENT_DESCRIPTION
301 | View.FIND_VIEWS_WITH_ACCESSIBILITY_NODE_PROVIDERS);
302 if (!foundViews.isEmpty()) {
303 infos = mTempAccessibilityNodeInfoList;
304 infos.clear();
305 final int viewCount = foundViews.size();
306 for (int i = 0; i < viewCount; i++) {
307 View foundView = foundViews.get(i);
Svetoslav Ganov0a1bb6d2012-05-07 11:54:39 -0700308 if (isShown(foundView)) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700309 provider = foundView.getAccessibilityNodeProvider();
310 if (provider != null) {
311 List<AccessibilityNodeInfo> infosFromProvider =
312 provider.findAccessibilityNodeInfosByText(text,
Svetoslav8e3feb12014-02-24 13:46:47 -0800313 AccessibilityNodeProvider.HOST_VIEW_ID);
Svetoslav Ganov42138042012-03-20 11:51:39 -0700314 if (infosFromProvider != null) {
315 infos.addAll(infosFromProvider);
316 }
317 } else {
318 infos.add(foundView.createAccessibilityNodeInfo());
319 }
320 }
321 }
322 }
323 }
324 }
325 } finally {
Phil Weaverc2e28932016-12-08 12:29:25 -0800326 updateInfosForViewportAndReturnFindNodeResult(
327 infos, callback, interactionId, spec, interactiveRegion);
Svetoslav Ganov42138042012-03-20 11:51:39 -0700328 }
329 }
330
Svetoslav9ae9ed22014-09-02 16:36:35 -0700331 public void findFocusClientThread(long accessibilityNodeId, int focusType,
332 Region interactiveRegion, int interactionId,
Phil Weaverc2e28932016-12-08 12:29:25 -0800333 IAccessibilityInteractionConnectionCallback callback, int flags, int interrogatingPid,
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700334 long interrogatingTid, MagnificationSpec spec) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700335 Message message = mHandler.obtainMessage();
336 message.what = PrivateHandler.MSG_FIND_FOCUS;
337 message.arg1 = flags;
338 message.arg2 = focusType;
Svetoslav Ganov86783472012-06-06 21:12:20 -0700339
Svetoslav Ganov758143e2012-08-06 16:40:27 -0700340 SomeArgs args = SomeArgs.obtain();
Svetoslav Ganov42138042012-03-20 11:51:39 -0700341 args.argi1 = interactionId;
342 args.argi2 = AccessibilityNodeInfo.getAccessibilityViewId(accessibilityNodeId);
343 args.argi3 = AccessibilityNodeInfo.getVirtualDescendantId(accessibilityNodeId);
344 args.arg1 = callback;
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700345 args.arg2 = spec;
Svetoslav9ae9ed22014-09-02 16:36:35 -0700346 args.arg3 = interactiveRegion;
Svetoslav Ganov86783472012-06-06 21:12:20 -0700347
Svetoslav Ganov42138042012-03-20 11:51:39 -0700348 message.obj = args;
Svetoslav Ganov86783472012-06-06 21:12:20 -0700349
Phil Weaverc2e28932016-12-08 12:29:25 -0800350 scheduleMessage(message, interrogatingPid, interrogatingTid);
Svetoslav Ganov42138042012-03-20 11:51:39 -0700351 }
352
353 private void findFocusUiThread(Message message) {
354 final int flags = message.arg1;
355 final int focusType = message.arg2;
Svetoslav Ganov86783472012-06-06 21:12:20 -0700356
Svetoslav Ganov42138042012-03-20 11:51:39 -0700357 SomeArgs args = (SomeArgs) message.obj;
358 final int interactionId = args.argi1;
359 final int accessibilityViewId = args.argi2;
360 final int virtualDescendantId = args.argi3;
361 final IAccessibilityInteractionConnectionCallback callback =
362 (IAccessibilityInteractionConnectionCallback) args.arg1;
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700363 final MagnificationSpec spec = (MagnificationSpec) args.arg2;
Svetoslav9ae9ed22014-09-02 16:36:35 -0700364 final Region interactiveRegion = (Region) args.arg3;
Svetoslav Ganov758143e2012-08-06 16:40:27 -0700365 args.recycle();
Svetoslav Ganov86783472012-06-06 21:12:20 -0700366
Svetoslav Ganov42138042012-03-20 11:51:39 -0700367 AccessibilityNodeInfo focused = null;
368 try {
369 if (mViewRootImpl.mView == null || mViewRootImpl.mAttachInfo == null) {
370 return;
371 }
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800372 mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = flags;
Svetoslav Ganov42138042012-03-20 11:51:39 -0700373 View root = null;
Phil Weaverf00cd142017-03-03 13:44:00 -0800374 if (accessibilityViewId != AccessibilityNodeInfo.ROOT_ITEM_ID) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700375 root = findViewByAccessibilityId(accessibilityViewId);
376 } else {
377 root = mViewRootImpl.mView;
378 }
Svetoslav Ganov0a1bb6d2012-05-07 11:54:39 -0700379 if (root != null && isShown(root)) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700380 switch (focusType) {
381 case AccessibilityNodeInfo.FOCUS_ACCESSIBILITY: {
382 View host = mViewRootImpl.mAccessibilityFocusedHost;
383 // If there is no accessibility focus host or it is not a descendant
384 // of the root from which to start the search, then the search failed.
385 if (host == null || !ViewRootImpl.isViewDescendantOf(host, root)) {
386 break;
387 }
Svetoslav6254f482013-06-04 17:22:14 -0700388 // The focused view not shown, we failed.
389 if (!isShown(host)) {
390 break;
391 }
Svetoslav Ganov42138042012-03-20 11:51:39 -0700392 // If the host has a provider ask this provider to search for the
393 // focus instead fetching all provider nodes to do the search here.
394 AccessibilityNodeProvider provider = host.getAccessibilityNodeProvider();
395 if (provider != null) {
Svetoslav Ganov45a02e02012-06-17 15:07:29 -0700396 if (mViewRootImpl.mAccessibilityFocusedVirtualView != null) {
397 focused = AccessibilityNodeInfo.obtain(
398 mViewRootImpl.mAccessibilityFocusedVirtualView);
399 }
Phil Weaverf00cd142017-03-03 13:44:00 -0800400 } else if (virtualDescendantId == AccessibilityNodeProvider.HOST_VIEW_ID) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700401 focused = host.createAccessibilityNodeInfo();
402 }
403 } break;
404 case AccessibilityNodeInfo.FOCUS_INPUT: {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700405 View target = root.findFocus();
Alan Viverette2e1e0812013-09-30 13:45:55 -0700406 if (target == null || !isShown(target)) {
407 break;
408 }
409 AccessibilityNodeProvider provider = target.getAccessibilityNodeProvider();
410 if (provider != null) {
411 focused = provider.findFocus(focusType);
412 }
413 if (focused == null) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700414 focused = target.createAccessibilityNodeInfo();
415 }
416 } break;
417 default:
418 throw new IllegalArgumentException("Unknown focus type: " + focusType);
419 }
420 }
421 } finally {
Phil Weaverc2e28932016-12-08 12:29:25 -0800422 updateInfoForViewportAndReturnFindNodeResult(
423 focused, callback, interactionId, spec, interactiveRegion);
Svetoslav Ganov42138042012-03-20 11:51:39 -0700424 }
425 }
426
Svetoslav9ae9ed22014-09-02 16:36:35 -0700427 public void focusSearchClientThread(long accessibilityNodeId, int direction,
428 Region interactiveRegion, int interactionId,
Phil Weaverc2e28932016-12-08 12:29:25 -0800429 IAccessibilityInteractionConnectionCallback callback, int flags, int interrogatingPid,
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700430 long interrogatingTid, MagnificationSpec spec) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700431 Message message = mHandler.obtainMessage();
432 message.what = PrivateHandler.MSG_FOCUS_SEARCH;
433 message.arg1 = flags;
434 message.arg2 = AccessibilityNodeInfo.getAccessibilityViewId(accessibilityNodeId);
Svetoslav Ganov86783472012-06-06 21:12:20 -0700435
Svetoslav Ganov758143e2012-08-06 16:40:27 -0700436 SomeArgs args = SomeArgs.obtain();
Svetoslav Ganov42138042012-03-20 11:51:39 -0700437 args.argi2 = direction;
438 args.argi3 = interactionId;
439 args.arg1 = callback;
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700440 args.arg2 = spec;
Svetoslav9ae9ed22014-09-02 16:36:35 -0700441 args.arg3 = interactiveRegion;
Svetoslav Ganov86783472012-06-06 21:12:20 -0700442
Svetoslav Ganov42138042012-03-20 11:51:39 -0700443 message.obj = args;
Svetoslav Ganov86783472012-06-06 21:12:20 -0700444
Phil Weaverc2e28932016-12-08 12:29:25 -0800445 scheduleMessage(message, interrogatingPid, interrogatingTid);
Svetoslav Ganov42138042012-03-20 11:51:39 -0700446 }
447
448 private void focusSearchUiThread(Message message) {
449 final int flags = message.arg1;
450 final int accessibilityViewId = message.arg2;
Svetoslav Ganov86783472012-06-06 21:12:20 -0700451
Svetoslav Ganov42138042012-03-20 11:51:39 -0700452 SomeArgs args = (SomeArgs) message.obj;
Svetoslav Ganov42138042012-03-20 11:51:39 -0700453 final int direction = args.argi2;
454 final int interactionId = args.argi3;
455 final IAccessibilityInteractionConnectionCallback callback =
456 (IAccessibilityInteractionConnectionCallback) args.arg1;
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700457 final MagnificationSpec spec = (MagnificationSpec) args.arg2;
Svetoslav9ae9ed22014-09-02 16:36:35 -0700458 final Region interactiveRegion = (Region) args.arg3;
Svetoslav Ganov86783472012-06-06 21:12:20 -0700459
Svetoslav Ganov758143e2012-08-06 16:40:27 -0700460 args.recycle();
Svetoslav Ganov86783472012-06-06 21:12:20 -0700461
Svetoslav Ganov42138042012-03-20 11:51:39 -0700462 AccessibilityNodeInfo next = null;
463 try {
464 if (mViewRootImpl.mView == null || mViewRootImpl.mAttachInfo == null) {
465 return;
466 }
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800467 mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = flags;
Svetoslav Ganov42138042012-03-20 11:51:39 -0700468 View root = null;
Phil Weaverf00cd142017-03-03 13:44:00 -0800469 if (accessibilityViewId != AccessibilityNodeInfo.ROOT_ITEM_ID) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700470 root = findViewByAccessibilityId(accessibilityViewId);
471 } else {
472 root = mViewRootImpl.mView;
473 }
Svetoslav Ganov0a1bb6d2012-05-07 11:54:39 -0700474 if (root != null && isShown(root)) {
Svetoslav Ganov27e2da72012-07-02 18:12:00 -0700475 View nextView = root.focusSearch(direction);
476 if (nextView != null) {
477 next = nextView.createAccessibilityNodeInfo();
Svetoslav Ganov42138042012-03-20 11:51:39 -0700478 }
479 }
480 } finally {
Phil Weaverc2e28932016-12-08 12:29:25 -0800481 updateInfoForViewportAndReturnFindNodeResult(
482 next, callback, interactionId, spec, interactiveRegion);
Svetoslav Ganov42138042012-03-20 11:51:39 -0700483 }
484 }
485
486 public void performAccessibilityActionClientThread(long accessibilityNodeId, int action,
Svetoslav Ganovaa780c12012-04-19 23:01:39 -0700487 Bundle arguments, int interactionId,
Phil Weaverc2e28932016-12-08 12:29:25 -0800488 IAccessibilityInteractionConnectionCallback callback, int flags, int interrogatingPid,
Svetoslav Ganovaa780c12012-04-19 23:01:39 -0700489 long interrogatingTid) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700490 Message message = mHandler.obtainMessage();
491 message.what = PrivateHandler.MSG_PERFORM_ACCESSIBILITY_ACTION;
492 message.arg1 = flags;
493 message.arg2 = AccessibilityNodeInfo.getAccessibilityViewId(accessibilityNodeId);
Svetoslav Ganov86783472012-06-06 21:12:20 -0700494
Svetoslav Ganov758143e2012-08-06 16:40:27 -0700495 SomeArgs args = SomeArgs.obtain();
Svetoslav Ganov42138042012-03-20 11:51:39 -0700496 args.argi1 = AccessibilityNodeInfo.getVirtualDescendantId(accessibilityNodeId);
497 args.argi2 = action;
498 args.argi3 = interactionId;
499 args.arg1 = callback;
Svetoslav Ganovaa780c12012-04-19 23:01:39 -0700500 args.arg2 = arguments;
Svetoslav Ganov86783472012-06-06 21:12:20 -0700501
Svetoslav Ganov42138042012-03-20 11:51:39 -0700502 message.obj = args;
Svetoslav Ganov86783472012-06-06 21:12:20 -0700503
Phil Weaverc2e28932016-12-08 12:29:25 -0800504 scheduleMessage(message, interrogatingPid, interrogatingTid);
Svetoslav Ganov42138042012-03-20 11:51:39 -0700505 }
506
George Mount41725de2015-04-09 08:23:05 -0700507 private void performAccessibilityActionUiThread(Message message) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700508 final int flags = message.arg1;
509 final int accessibilityViewId = message.arg2;
Svetoslav Ganov86783472012-06-06 21:12:20 -0700510
Svetoslav Ganov42138042012-03-20 11:51:39 -0700511 SomeArgs args = (SomeArgs) message.obj;
512 final int virtualDescendantId = args.argi1;
513 final int action = args.argi2;
514 final int interactionId = args.argi3;
515 final IAccessibilityInteractionConnectionCallback callback =
516 (IAccessibilityInteractionConnectionCallback) args.arg1;
Svetoslav Ganovaa780c12012-04-19 23:01:39 -0700517 Bundle arguments = (Bundle) args.arg2;
Svetoslav Ganov86783472012-06-06 21:12:20 -0700518
Svetoslav Ganov758143e2012-08-06 16:40:27 -0700519 args.recycle();
Svetoslav Ganov86783472012-06-06 21:12:20 -0700520
Svetoslav Ganov42138042012-03-20 11:51:39 -0700521 boolean succeeded = false;
522 try {
George Mount41725de2015-04-09 08:23:05 -0700523 if (mViewRootImpl.mView == null || mViewRootImpl.mAttachInfo == null ||
524 mViewRootImpl.mStopped || mViewRootImpl.mPausedForTransition) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700525 return;
526 }
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800527 mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = flags;
Svetoslav Ganov42138042012-03-20 11:51:39 -0700528 View target = null;
Phil Weaverf00cd142017-03-03 13:44:00 -0800529 if (accessibilityViewId != AccessibilityNodeInfo.ROOT_ITEM_ID) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700530 target = findViewByAccessibilityId(accessibilityViewId);
531 } else {
532 target = mViewRootImpl.mView;
533 }
Svetoslav Ganov0a1bb6d2012-05-07 11:54:39 -0700534 if (target != null && isShown(target)) {
Phil Weaver193520e2016-12-13 09:39:06 -0800535 if (action == R.id.accessibilityActionClickOnClickableSpan) {
536 // Handle this hidden action separately
537 succeeded = handleClickableSpanActionUiThread(
538 target, virtualDescendantId, arguments);
539 } else {
540 AccessibilityNodeProvider provider = target.getAccessibilityNodeProvider();
541 if (provider != null) {
Phil Weaverf00cd142017-03-03 13:44:00 -0800542 succeeded = provider.performAction(virtualDescendantId, action,
543 arguments);
544 } else if (virtualDescendantId == AccessibilityNodeProvider.HOST_VIEW_ID) {
Phil Weaver193520e2016-12-13 09:39:06 -0800545 succeeded = target.performAccessibilityAction(action, arguments);
Svetoslav8e3feb12014-02-24 13:46:47 -0800546 }
Svetoslav Ganov42138042012-03-20 11:51:39 -0700547 }
548 }
549 } finally {
550 try {
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800551 mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = 0;
Svetoslav Ganov42138042012-03-20 11:51:39 -0700552 callback.setPerformAccessibilityActionResult(succeeded, interactionId);
553 } catch (RemoteException re) {
554 /* ignore - the other side will time out */
555 }
556 }
557 }
558
559 private View findViewByAccessibilityId(int accessibilityId) {
560 View root = mViewRootImpl.mView;
561 if (root == null) {
562 return null;
563 }
564 View foundView = root.findViewByAccessibilityId(accessibilityId);
Svetoslav Ganov0a1bb6d2012-05-07 11:54:39 -0700565 if (foundView != null && !isShown(foundView)) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700566 return null;
567 }
568 return foundView;
569 }
570
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700571 private void applyAppScaleAndMagnificationSpecIfNeeded(List<AccessibilityNodeInfo> infos,
572 MagnificationSpec spec) {
Svetoslav Ganovfd8d9c42012-07-09 18:16:55 -0700573 if (infos == null) {
574 return;
575 }
576 final float applicationScale = mViewRootImpl.mAttachInfo.mApplicationScale;
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700577 if (shouldApplyAppScaleAndMagnificationSpec(applicationScale, spec)) {
Svetoslav Ganovfd8d9c42012-07-09 18:16:55 -0700578 final int infoCount = infos.size();
579 for (int i = 0; i < infoCount; i++) {
580 AccessibilityNodeInfo info = infos.get(i);
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700581 applyAppScaleAndMagnificationSpecIfNeeded(info, spec);
Svetoslav Ganovfd8d9c42012-07-09 18:16:55 -0700582 }
583 }
584 }
585
Svetoslav9ae9ed22014-09-02 16:36:35 -0700586 private void adjustIsVisibleToUserIfNeeded(List<AccessibilityNodeInfo> infos,
587 Region interactiveRegion) {
588 if (interactiveRegion == null || infos == null) {
589 return;
590 }
591 final int infoCount = infos.size();
592 for (int i = 0; i < infoCount; i++) {
593 AccessibilityNodeInfo info = infos.get(i);
594 adjustIsVisibleToUserIfNeeded(info, interactiveRegion);
595 }
596 }
597
598 private void adjustIsVisibleToUserIfNeeded(AccessibilityNodeInfo info,
599 Region interactiveRegion) {
600 if (interactiveRegion == null || info == null) {
601 return;
602 }
603 Rect boundsInScreen = mTempRect;
604 info.getBoundsInScreen(boundsInScreen);
605 if (interactiveRegion.quickReject(boundsInScreen)) {
606 info.setVisibleToUser(false);
607 }
608 }
609
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700610 private void applyAppScaleAndMagnificationSpecIfNeeded(AccessibilityNodeInfo info,
611 MagnificationSpec spec) {
Svetoslav Ganovfd8d9c42012-07-09 18:16:55 -0700612 if (info == null) {
613 return;
614 }
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700615
Svetoslav Ganovfd8d9c42012-07-09 18:16:55 -0700616 final float applicationScale = mViewRootImpl.mAttachInfo.mApplicationScale;
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700617 if (!shouldApplyAppScaleAndMagnificationSpec(applicationScale, spec)) {
618 return;
619 }
620
621 Rect boundsInParent = mTempRect;
622 Rect boundsInScreen = mTempRect1;
623
624 info.getBoundsInParent(boundsInParent);
625 info.getBoundsInScreen(boundsInScreen);
Svetoslav Ganovfd8d9c42012-07-09 18:16:55 -0700626 if (applicationScale != 1.0f) {
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700627 boundsInParent.scale(applicationScale);
628 boundsInScreen.scale(applicationScale);
629 }
630 if (spec != null) {
631 boundsInParent.scale(spec.scale);
632 // boundsInParent must not be offset.
633 boundsInScreen.scale(spec.scale);
634 boundsInScreen.offset((int) spec.offsetX, (int) spec.offsetY);
635 }
636 info.setBoundsInParent(boundsInParent);
637 info.setBoundsInScreen(boundsInScreen);
Svetoslav Ganovfd8d9c42012-07-09 18:16:55 -0700638
Phil Weaverc2e28932016-12-08 12:29:25 -0800639 // Scale text locations if they are present
640 if (info.hasExtras()) {
641 Bundle extras = info.getExtras();
642 Parcelable[] textLocations =
643 extras.getParcelableArray(EXTRA_DATA_TEXT_CHARACTER_LOCATION_KEY);
644 if (textLocations != null) {
645 for (int i = 0; i < textLocations.length; i++) {
646 // Unchecked cast - an app that puts other objects in this bundle with this
647 // key will crash.
648 RectF textLocation = ((RectF) textLocations[i]);
649 textLocation.scale(applicationScale);
650 if (spec != null) {
651 textLocation.scale(spec.scale);
652 textLocation.offset(spec.offsetX, spec.offsetY);
653 }
654 }
655 }
656 }
657
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700658 if (spec != null) {
659 AttachInfo attachInfo = mViewRootImpl.mAttachInfo;
660 if (attachInfo.mDisplay == null) {
661 return;
662 }
Svetoslav Ganovfd8d9c42012-07-09 18:16:55 -0700663
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700664 final float scale = attachInfo.mApplicationScale * spec.scale;
665
666 Rect visibleWinFrame = mTempRect1;
667 visibleWinFrame.left = (int) (attachInfo.mWindowLeft * scale + spec.offsetX);
668 visibleWinFrame.top = (int) (attachInfo.mWindowTop * scale + spec.offsetY);
669 visibleWinFrame.right = (int) (visibleWinFrame.left + mViewRootImpl.mWidth * scale);
670 visibleWinFrame.bottom = (int) (visibleWinFrame.top + mViewRootImpl.mHeight * scale);
671
672 attachInfo.mDisplay.getRealSize(mTempPoint);
673 final int displayWidth = mTempPoint.x;
674 final int displayHeight = mTempPoint.y;
675
676 Rect visibleDisplayFrame = mTempRect2;
677 visibleDisplayFrame.set(0, 0, displayWidth, displayHeight);
678
Doris Liu9607fbe2015-05-28 17:17:28 -0700679 if (!visibleWinFrame.intersect(visibleDisplayFrame)) {
680 // If there's no intersection with display, set visibleWinFrame empty.
681 visibleDisplayFrame.setEmpty();
682 }
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700683
684 if (!visibleWinFrame.intersects(boundsInScreen.left, boundsInScreen.top,
685 boundsInScreen.right, boundsInScreen.bottom)) {
686 info.setVisibleToUser(false);
687 }
Svetoslav Ganovfd8d9c42012-07-09 18:16:55 -0700688 }
689 }
690
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700691 private boolean shouldApplyAppScaleAndMagnificationSpec(float appScale,
692 MagnificationSpec spec) {
693 return (appScale != 1.0f || (spec != null && !spec.isNop()));
694 }
Svetoslav Ganovfd8d9c42012-07-09 18:16:55 -0700695
Phil Weaverc2e28932016-12-08 12:29:25 -0800696 private void updateInfosForViewportAndReturnFindNodeResult(List<AccessibilityNodeInfo> infos,
697 IAccessibilityInteractionConnectionCallback callback, int interactionId,
698 MagnificationSpec spec, Region interactiveRegion) {
699 try {
700 mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = 0;
701 applyAppScaleAndMagnificationSpecIfNeeded(infos, spec);
702 adjustIsVisibleToUserIfNeeded(infos, interactiveRegion);
703 callback.setFindAccessibilityNodeInfosResult(infos, interactionId);
Phil Weaverf00cd142017-03-03 13:44:00 -0800704 if (infos != null) {
705 infos.clear();
706 }
Phil Weaverc2e28932016-12-08 12:29:25 -0800707 } catch (RemoteException re) {
708 /* ignore - the other side will time out */
709 } finally {
710 recycleMagnificationSpecAndRegionIfNeeded(spec, interactiveRegion);
711 }
712 }
713
714 private void updateInfoForViewportAndReturnFindNodeResult(AccessibilityNodeInfo info,
715 IAccessibilityInteractionConnectionCallback callback, int interactionId,
716 MagnificationSpec spec, Region interactiveRegion) {
717 try {
718 mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = 0;
719 applyAppScaleAndMagnificationSpecIfNeeded(info, spec);
720 adjustIsVisibleToUserIfNeeded(info, interactiveRegion);
721 callback.setFindAccessibilityNodeInfoResult(info, interactionId);
722 } catch (RemoteException re) {
723 /* ignore - the other side will time out */
724 } finally {
725 recycleMagnificationSpecAndRegionIfNeeded(spec, interactiveRegion);
726 }
727 }
728
729 private void recycleMagnificationSpecAndRegionIfNeeded(MagnificationSpec spec, Region region) {
730 if (android.os.Process.myPid() != Binder.getCallingPid()) {
731 // Specs are cached in the system process and obtained from a pool when read from
732 // a parcel, so only recycle the spec if called from another process.
733 if (spec != null) {
734 spec.recycle();
735 }
736 } else {
737 // Regions are obtained in the system process and instantiated when read from
738 // a parcel, so only recycle the region if caled from the same process.
739 if (region != null) {
740 region.recycle();
741 }
742 }
743 }
744
Phil Weaver193520e2016-12-13 09:39:06 -0800745 private boolean handleClickableSpanActionUiThread(
746 View view, int virtualDescendantId, Bundle arguments) {
747 Parcelable span = arguments.getParcelable(ACTION_ARGUMENT_ACCESSIBLE_CLICKABLE_SPAN);
748 if (!(span instanceof AccessibilityClickableSpan)) {
749 return false;
750 }
751
752 // Find the original ClickableSpan if it's still on the screen
753 AccessibilityNodeInfo infoWithSpan = null;
754 AccessibilityNodeProvider provider = view.getAccessibilityNodeProvider();
755 if (provider != null) {
Phil Weaverf00cd142017-03-03 13:44:00 -0800756 infoWithSpan = provider.createAccessibilityNodeInfo(virtualDescendantId);
757 } else if (virtualDescendantId == AccessibilityNodeProvider.HOST_VIEW_ID) {
Phil Weaver193520e2016-12-13 09:39:06 -0800758 infoWithSpan = view.createAccessibilityNodeInfo();
759 }
760 if (infoWithSpan == null) {
761 return false;
762 }
763
764 // Click on the corresponding span
765 ClickableSpan clickableSpan = ((AccessibilityClickableSpan) span).findClickableSpan(
766 infoWithSpan.getOriginalText());
767 if (clickableSpan != null) {
768 clickableSpan.onClick(view);
769 return true;
770 }
771 return false;
772 }
773
Svetoslav Ganov42138042012-03-20 11:51:39 -0700774 /**
Svetoslav Ganov42138042012-03-20 11:51:39 -0700775 * This class encapsulates a prefetching strategy for the accessibility APIs for
776 * querying window content. It is responsible to prefetch a batch of
777 * AccessibilityNodeInfos in addition to the one for a requested node.
778 */
779 private class AccessibilityNodePrefetcher {
780
781 private static final int MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE = 50;
782
Svetoslav Ganov4528b4e2012-05-15 18:24:10 -0700783 private final ArrayList<View> mTempViewList = new ArrayList<View>();
784
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800785 public void prefetchAccessibilityNodeInfos(View view, int virtualViewId, int fetchFlags,
Phil Weaverc2e28932016-12-08 12:29:25 -0800786 List<AccessibilityNodeInfo> outInfos, Bundle arguments) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700787 AccessibilityNodeProvider provider = view.getAccessibilityNodeProvider();
Phil Weaverc2e28932016-12-08 12:29:25 -0800788 // Determine if we'll be populating extra data
789 final String extraDataRequested = (arguments == null) ? null
790 : arguments.getString(AccessibilityNodeInfo.EXTRA_DATA_REQUESTED_KEY);
Svetoslav Ganov42138042012-03-20 11:51:39 -0700791 if (provider == null) {
792 AccessibilityNodeInfo root = view.createAccessibilityNodeInfo();
793 if (root != null) {
Phil Weaverc2e28932016-12-08 12:29:25 -0800794 if (extraDataRequested != null) {
795 view.addExtraDataToAccessibilityNodeInfo(
796 root, extraDataRequested, arguments);
797 }
Svetoslav Ganov42138042012-03-20 11:51:39 -0700798 outInfos.add(root);
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800799 if ((fetchFlags & AccessibilityNodeInfo.FLAG_PREFETCH_PREDECESSORS) != 0) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700800 prefetchPredecessorsOfRealNode(view, outInfos);
801 }
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800802 if ((fetchFlags & AccessibilityNodeInfo.FLAG_PREFETCH_SIBLINGS) != 0) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700803 prefetchSiblingsOfRealNode(view, outInfos);
804 }
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800805 if ((fetchFlags & AccessibilityNodeInfo.FLAG_PREFETCH_DESCENDANTS) != 0) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700806 prefetchDescendantsOfRealNode(view, outInfos);
807 }
808 }
809 } else {
Phil Weaverf00cd142017-03-03 13:44:00 -0800810 final AccessibilityNodeInfo root =
811 provider.createAccessibilityNodeInfo(virtualViewId);
Svetoslav Ganov42138042012-03-20 11:51:39 -0700812 if (root != null) {
Phil Weaverc2e28932016-12-08 12:29:25 -0800813 if (extraDataRequested != null) {
814 provider.addExtraDataToAccessibilityNodeInfo(
Phil Weaverf00cd142017-03-03 13:44:00 -0800815 virtualViewId, root, extraDataRequested, arguments);
Phil Weaverc2e28932016-12-08 12:29:25 -0800816 }
Svetoslav Ganov42138042012-03-20 11:51:39 -0700817 outInfos.add(root);
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800818 if ((fetchFlags & AccessibilityNodeInfo.FLAG_PREFETCH_PREDECESSORS) != 0) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700819 prefetchPredecessorsOfVirtualNode(root, view, provider, outInfos);
820 }
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800821 if ((fetchFlags & AccessibilityNodeInfo.FLAG_PREFETCH_SIBLINGS) != 0) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700822 prefetchSiblingsOfVirtualNode(root, view, provider, outInfos);
823 }
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800824 if ((fetchFlags & AccessibilityNodeInfo.FLAG_PREFETCH_DESCENDANTS) != 0) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700825 prefetchDescendantsOfVirtualNode(root, provider, outInfos);
826 }
827 }
828 }
Svetoslav8e3feb12014-02-24 13:46:47 -0800829 if (ENFORCE_NODE_TREE_CONSISTENT) {
830 enforceNodeTreeConsistent(outInfos);
831 }
832 }
833
834 private void enforceNodeTreeConsistent(List<AccessibilityNodeInfo> nodes) {
835 LongSparseArray<AccessibilityNodeInfo> nodeMap =
836 new LongSparseArray<AccessibilityNodeInfo>();
837 final int nodeCount = nodes.size();
838 for (int i = 0; i < nodeCount; i++) {
839 AccessibilityNodeInfo node = nodes.get(i);
840 nodeMap.put(node.getSourceNodeId(), node);
841 }
842
843 // If the nodes are a tree it does not matter from
844 // which node we start to search for the root.
845 AccessibilityNodeInfo root = nodeMap.valueAt(0);
846 AccessibilityNodeInfo parent = root;
847 while (parent != null) {
848 root = parent;
849 parent = nodeMap.get(parent.getParentNodeId());
850 }
851
852 // Traverse the tree and do some checks.
853 AccessibilityNodeInfo accessFocus = null;
854 AccessibilityNodeInfo inputFocus = null;
855 HashSet<AccessibilityNodeInfo> seen = new HashSet<AccessibilityNodeInfo>();
856 Queue<AccessibilityNodeInfo> fringe = new LinkedList<AccessibilityNodeInfo>();
857 fringe.add(root);
858
859 while (!fringe.isEmpty()) {
860 AccessibilityNodeInfo current = fringe.poll();
861
862 // Check for duplicates
863 if (!seen.add(current)) {
864 throw new IllegalStateException("Duplicate node: "
865 + current + " in window:"
866 + mViewRootImpl.mAttachInfo.mAccessibilityWindowId);
867 }
868
869 // Check for one accessibility focus.
870 if (current.isAccessibilityFocused()) {
871 if (accessFocus != null) {
872 throw new IllegalStateException("Duplicate accessibility focus:"
873 + current
874 + " in window:" + mViewRootImpl.mAttachInfo.mAccessibilityWindowId);
875 } else {
876 accessFocus = current;
877 }
878 }
879
880 // Check for one input focus.
881 if (current.isFocused()) {
882 if (inputFocus != null) {
883 throw new IllegalStateException("Duplicate input focus: "
884 + current + " in window:"
885 + mViewRootImpl.mAttachInfo.mAccessibilityWindowId);
886 } else {
887 inputFocus = current;
888 }
889 }
890
891 final int childCount = current.getChildCount();
892 for (int j = 0; j < childCount; j++) {
893 final long childId = current.getChildId(j);
894 final AccessibilityNodeInfo child = nodeMap.get(childId);
895 if (child != null) {
896 fringe.add(child);
897 }
898 }
899 }
900
901 // Check for disconnected nodes.
902 for (int j = nodeMap.size() - 1; j >= 0; j--) {
903 AccessibilityNodeInfo info = nodeMap.valueAt(j);
904 if (!seen.contains(info)) {
905 throw new IllegalStateException("Disconnected node: " + info);
906 }
907 }
Svetoslav Ganov42138042012-03-20 11:51:39 -0700908 }
909
910 private void prefetchPredecessorsOfRealNode(View view,
911 List<AccessibilityNodeInfo> outInfos) {
912 ViewParent parent = view.getParentForAccessibility();
913 while (parent instanceof View
914 && outInfos.size() < MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE) {
915 View parentView = (View) parent;
Svetoslav Ganov42138042012-03-20 11:51:39 -0700916 AccessibilityNodeInfo info = parentView.createAccessibilityNodeInfo();
917 if (info != null) {
918 outInfos.add(info);
919 }
920 parent = parent.getParentForAccessibility();
921 }
922 }
923
924 private void prefetchSiblingsOfRealNode(View current,
925 List<AccessibilityNodeInfo> outInfos) {
926 ViewParent parent = current.getParentForAccessibility();
927 if (parent instanceof ViewGroup) {
928 ViewGroup parentGroup = (ViewGroup) parent;
Svetoslav Ganov4528b4e2012-05-15 18:24:10 -0700929 ArrayList<View> children = mTempViewList;
930 children.clear();
Svetoslav Ganov76f287e2012-04-23 11:02:36 -0700931 try {
Svetoslav Ganov4528b4e2012-05-15 18:24:10 -0700932 parentGroup.addChildrenForAccessibility(children);
933 final int childCount = children.size();
Svetoslav Ganov76f287e2012-04-23 11:02:36 -0700934 for (int i = 0; i < childCount; i++) {
935 if (outInfos.size() >= MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE) {
936 return;
Svetoslav Ganov42138042012-03-20 11:51:39 -0700937 }
Svetoslav Ganov4528b4e2012-05-15 18:24:10 -0700938 View child = children.get(i);
Svetoslav Ganov76f287e2012-04-23 11:02:36 -0700939 if (child.getAccessibilityViewId() != current.getAccessibilityViewId()
Svetoslav Ganov0a1bb6d2012-05-07 11:54:39 -0700940 && isShown(child)) {
Svetoslav Ganov76f287e2012-04-23 11:02:36 -0700941 AccessibilityNodeInfo info = null;
Svetoslav Ganov4528b4e2012-05-15 18:24:10 -0700942 AccessibilityNodeProvider provider =
943 child.getAccessibilityNodeProvider();
Svetoslav Ganov76f287e2012-04-23 11:02:36 -0700944 if (provider == null) {
945 info = child.createAccessibilityNodeInfo();
946 } else {
947 info = provider.createAccessibilityNodeInfo(
Svetoslav8e3feb12014-02-24 13:46:47 -0800948 AccessibilityNodeProvider.HOST_VIEW_ID);
Svetoslav Ganov76f287e2012-04-23 11:02:36 -0700949 }
950 if (info != null) {
951 outInfos.add(info);
952 }
Svetoslav Ganov42138042012-03-20 11:51:39 -0700953 }
954 }
Svetoslav Ganov76f287e2012-04-23 11:02:36 -0700955 } finally {
Svetoslav Ganov4528b4e2012-05-15 18:24:10 -0700956 children.clear();
Svetoslav Ganov42138042012-03-20 11:51:39 -0700957 }
Svetoslav Ganov42138042012-03-20 11:51:39 -0700958 }
959 }
960
961 private void prefetchDescendantsOfRealNode(View root,
962 List<AccessibilityNodeInfo> outInfos) {
963 if (!(root instanceof ViewGroup)) {
964 return;
965 }
Svetoslav Ganov42138042012-03-20 11:51:39 -0700966 HashMap<View, AccessibilityNodeInfo> addedChildren =
967 new HashMap<View, AccessibilityNodeInfo>();
Svetoslav Ganov4528b4e2012-05-15 18:24:10 -0700968 ArrayList<View> children = mTempViewList;
969 children.clear();
Svetoslav Ganov76f287e2012-04-23 11:02:36 -0700970 try {
Svetoslav Ganov4528b4e2012-05-15 18:24:10 -0700971 root.addChildrenForAccessibility(children);
972 final int childCount = children.size();
Svetoslav Ganov76f287e2012-04-23 11:02:36 -0700973 for (int i = 0; i < childCount; i++) {
974 if (outInfos.size() >= MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE) {
975 return;
976 }
Svetoslav Ganov4528b4e2012-05-15 18:24:10 -0700977 View child = children.get(i);
Svetoslav Ganov0a1bb6d2012-05-07 11:54:39 -0700978 if (isShown(child)) {
Svetoslav Ganov76f287e2012-04-23 11:02:36 -0700979 AccessibilityNodeProvider provider = child.getAccessibilityNodeProvider();
980 if (provider == null) {
981 AccessibilityNodeInfo info = child.createAccessibilityNodeInfo();
982 if (info != null) {
983 outInfos.add(info);
984 addedChildren.put(child, null);
985 }
986 } else {
987 AccessibilityNodeInfo info = provider.createAccessibilityNodeInfo(
Svetoslav8e3feb12014-02-24 13:46:47 -0800988 AccessibilityNodeProvider.HOST_VIEW_ID);
Svetoslav Ganov76f287e2012-04-23 11:02:36 -0700989 if (info != null) {
990 outInfos.add(info);
991 addedChildren.put(child, info);
992 }
Svetoslav Ganov42138042012-03-20 11:51:39 -0700993 }
994 }
995 }
Svetoslav Ganov76f287e2012-04-23 11:02:36 -0700996 } finally {
Svetoslav Ganov4528b4e2012-05-15 18:24:10 -0700997 children.clear();
Svetoslav Ganov42138042012-03-20 11:51:39 -0700998 }
Svetoslav Ganov42138042012-03-20 11:51:39 -0700999 if (outInfos.size() < MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE) {
1000 for (Map.Entry<View, AccessibilityNodeInfo> entry : addedChildren.entrySet()) {
1001 View addedChild = entry.getKey();
1002 AccessibilityNodeInfo virtualRoot = entry.getValue();
1003 if (virtualRoot == null) {
1004 prefetchDescendantsOfRealNode(addedChild, outInfos);
1005 } else {
1006 AccessibilityNodeProvider provider =
1007 addedChild.getAccessibilityNodeProvider();
1008 prefetchDescendantsOfVirtualNode(virtualRoot, provider, outInfos);
1009 }
1010 }
1011 }
1012 }
1013
1014 private void prefetchPredecessorsOfVirtualNode(AccessibilityNodeInfo root,
1015 View providerHost, AccessibilityNodeProvider provider,
1016 List<AccessibilityNodeInfo> outInfos) {
Svet Ganov75e58162016-02-19 16:29:24 -08001017 final int initialResultSize = outInfos.size();
Svetoslav Ganov42138042012-03-20 11:51:39 -07001018 long parentNodeId = root.getParentNodeId();
1019 int accessibilityViewId = AccessibilityNodeInfo.getAccessibilityViewId(parentNodeId);
Svetoslav8e3feb12014-02-24 13:46:47 -08001020 while (accessibilityViewId != AccessibilityNodeInfo.UNDEFINED_ITEM_ID) {
Svetoslav Ganov42138042012-03-20 11:51:39 -07001021 if (outInfos.size() >= MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE) {
1022 return;
1023 }
1024 final int virtualDescendantId =
1025 AccessibilityNodeInfo.getVirtualDescendantId(parentNodeId);
Phil Weaverf00cd142017-03-03 13:44:00 -08001026 if (virtualDescendantId != AccessibilityNodeProvider.HOST_VIEW_ID
Svetoslav Ganov42138042012-03-20 11:51:39 -07001027 || accessibilityViewId == providerHost.getAccessibilityViewId()) {
Svetoslav8e3feb12014-02-24 13:46:47 -08001028 final AccessibilityNodeInfo parent;
Phil Weaverf00cd142017-03-03 13:44:00 -08001029 parent = provider.createAccessibilityNodeInfo(virtualDescendantId);
Alan Viverette84feea112014-11-04 15:59:55 -08001030 if (parent == null) {
Svet Ganov75e58162016-02-19 16:29:24 -08001031 // Going up the parent relation we found a null predecessor,
1032 // so remove these disconnected nodes form the result.
1033 final int currentResultSize = outInfos.size();
1034 for (int i = currentResultSize - 1; i >= initialResultSize; i--) {
1035 outInfos.remove(i);
1036 }
Alan Viverette84feea112014-11-04 15:59:55 -08001037 // Couldn't obtain the parent, which means we have a
1038 // disconnected sub-tree. Abort prefetch immediately.
1039 return;
Svetoslav Ganov42138042012-03-20 11:51:39 -07001040 }
Alan Viverette84feea112014-11-04 15:59:55 -08001041 outInfos.add(parent);
Svetoslav Ganov42138042012-03-20 11:51:39 -07001042 parentNodeId = parent.getParentNodeId();
1043 accessibilityViewId = AccessibilityNodeInfo.getAccessibilityViewId(
1044 parentNodeId);
1045 } else {
1046 prefetchPredecessorsOfRealNode(providerHost, outInfos);
1047 return;
1048 }
1049 }
1050 }
1051
1052 private void prefetchSiblingsOfVirtualNode(AccessibilityNodeInfo current, View providerHost,
1053 AccessibilityNodeProvider provider, List<AccessibilityNodeInfo> outInfos) {
1054 final long parentNodeId = current.getParentNodeId();
1055 final int parentAccessibilityViewId =
1056 AccessibilityNodeInfo.getAccessibilityViewId(parentNodeId);
1057 final int parentVirtualDescendantId =
1058 AccessibilityNodeInfo.getVirtualDescendantId(parentNodeId);
Phil Weaverf00cd142017-03-03 13:44:00 -08001059 if (parentVirtualDescendantId != AccessibilityNodeProvider.HOST_VIEW_ID
Svetoslav Ganov42138042012-03-20 11:51:39 -07001060 || parentAccessibilityViewId == providerHost.getAccessibilityViewId()) {
Phil Weaverf00cd142017-03-03 13:44:00 -08001061 final AccessibilityNodeInfo parent =
1062 provider.createAccessibilityNodeInfo(parentVirtualDescendantId);
Svetoslav Ganov42138042012-03-20 11:51:39 -07001063 if (parent != null) {
Alan Viverettef0aed092013-11-06 15:33:03 -08001064 final int childCount = parent.getChildCount();
Svetoslav Ganov42138042012-03-20 11:51:39 -07001065 for (int i = 0; i < childCount; i++) {
1066 if (outInfos.size() >= MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE) {
1067 return;
1068 }
Alan Viverettef0aed092013-11-06 15:33:03 -08001069 final long childNodeId = parent.getChildId(i);
Svetoslav Ganov42138042012-03-20 11:51:39 -07001070 if (childNodeId != current.getSourceNodeId()) {
1071 final int childVirtualDescendantId =
1072 AccessibilityNodeInfo.getVirtualDescendantId(childNodeId);
1073 AccessibilityNodeInfo child = provider.createAccessibilityNodeInfo(
1074 childVirtualDescendantId);
1075 if (child != null) {
1076 outInfos.add(child);
1077 }
1078 }
1079 }
1080 }
1081 } else {
1082 prefetchSiblingsOfRealNode(providerHost, outInfos);
1083 }
1084 }
1085
1086 private void prefetchDescendantsOfVirtualNode(AccessibilityNodeInfo root,
1087 AccessibilityNodeProvider provider, List<AccessibilityNodeInfo> outInfos) {
Svetoslav Ganov42138042012-03-20 11:51:39 -07001088 final int initialOutInfosSize = outInfos.size();
Alan Viverettef0aed092013-11-06 15:33:03 -08001089 final int childCount = root.getChildCount();
Svetoslav Ganov42138042012-03-20 11:51:39 -07001090 for (int i = 0; i < childCount; i++) {
1091 if (outInfos.size() >= MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE) {
1092 return;
1093 }
Alan Viverettef0aed092013-11-06 15:33:03 -08001094 final long childNodeId = root.getChildId(i);
Svetoslav Ganov42138042012-03-20 11:51:39 -07001095 AccessibilityNodeInfo child = provider.createAccessibilityNodeInfo(
1096 AccessibilityNodeInfo.getVirtualDescendantId(childNodeId));
1097 if (child != null) {
1098 outInfos.add(child);
1099 }
1100 }
1101 if (outInfos.size() < MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE) {
1102 final int addedChildCount = outInfos.size() - initialOutInfosSize;
1103 for (int i = 0; i < addedChildCount; i++) {
1104 AccessibilityNodeInfo child = outInfos.get(initialOutInfosSize + i);
1105 prefetchDescendantsOfVirtualNode(child, provider, outInfos);
1106 }
1107 }
1108 }
1109 }
1110
1111 private class PrivateHandler extends Handler {
Phil Weaverc2e28932016-12-08 12:29:25 -08001112 private static final int MSG_PERFORM_ACCESSIBILITY_ACTION = 1;
1113 private static final int MSG_FIND_ACCESSIBILITY_NODE_INFO_BY_ACCESSIBILITY_ID = 2;
1114 private static final int MSG_FIND_ACCESSIBILITY_NODE_INFOS_BY_VIEW_ID = 3;
1115 private static final int MSG_FIND_ACCESSIBILITY_NODE_INFO_BY_TEXT = 4;
1116 private static final int MSG_FIND_FOCUS = 5;
1117 private static final int MSG_FOCUS_SEARCH = 6;
Svetoslav Ganov42138042012-03-20 11:51:39 -07001118
Svetoslav Ganov005b83b2012-04-16 18:17:17 -07001119 public PrivateHandler(Looper looper) {
1120 super(looper);
Svetoslav Ganov42138042012-03-20 11:51:39 -07001121 }
1122
1123 @Override
1124 public String getMessageName(Message message) {
1125 final int type = message.what;
1126 switch (type) {
1127 case MSG_PERFORM_ACCESSIBILITY_ACTION:
1128 return "MSG_PERFORM_ACCESSIBILITY_ACTION";
Svet Ganov7498efd2014-09-03 21:33:00 -07001129 case MSG_FIND_ACCESSIBILITY_NODE_INFO_BY_ACCESSIBILITY_ID:
1130 return "MSG_FIND_ACCESSIBILITY_NODE_INFO_BY_ACCESSIBILITY_ID";
1131 case MSG_FIND_ACCESSIBILITY_NODE_INFOS_BY_VIEW_ID:
1132 return "MSG_FIND_ACCESSIBILITY_NODE_INFOS_BY_VIEW_ID";
1133 case MSG_FIND_ACCESSIBILITY_NODE_INFO_BY_TEXT:
1134 return "MSG_FIND_ACCESSIBILITY_NODE_INFO_BY_TEXT";
Svetoslav Ganov42138042012-03-20 11:51:39 -07001135 case MSG_FIND_FOCUS:
1136 return "MSG_FIND_FOCUS";
1137 case MSG_FOCUS_SEARCH:
1138 return "MSG_FOCUS_SEARCH";
1139 default:
1140 throw new IllegalArgumentException("Unknown message type: " + type);
1141 }
1142 }
1143
1144 @Override
1145 public void handleMessage(Message message) {
1146 final int type = message.what;
1147 switch (type) {
Svet Ganov7498efd2014-09-03 21:33:00 -07001148 case MSG_FIND_ACCESSIBILITY_NODE_INFO_BY_ACCESSIBILITY_ID: {
Svetoslav Ganov42138042012-03-20 11:51:39 -07001149 findAccessibilityNodeInfoByAccessibilityIdUiThread(message);
1150 } break;
1151 case MSG_PERFORM_ACCESSIBILITY_ACTION: {
George Mount41725de2015-04-09 08:23:05 -07001152 performAccessibilityActionUiThread(message);
Svetoslav Ganov42138042012-03-20 11:51:39 -07001153 } break;
Svet Ganov7498efd2014-09-03 21:33:00 -07001154 case MSG_FIND_ACCESSIBILITY_NODE_INFOS_BY_VIEW_ID: {
Svetoslav Ganov80943d82013-01-02 10:25:37 -08001155 findAccessibilityNodeInfosByViewIdUiThread(message);
Svetoslav Ganov42138042012-03-20 11:51:39 -07001156 } break;
Svet Ganov7498efd2014-09-03 21:33:00 -07001157 case MSG_FIND_ACCESSIBILITY_NODE_INFO_BY_TEXT: {
Svetoslav Ganov42138042012-03-20 11:51:39 -07001158 findAccessibilityNodeInfosByTextUiThread(message);
1159 } break;
1160 case MSG_FIND_FOCUS: {
1161 findFocusUiThread(message);
1162 } break;
1163 case MSG_FOCUS_SEARCH: {
1164 focusSearchUiThread(message);
1165 } break;
1166 default:
1167 throw new IllegalArgumentException("Unknown message type: " + type);
1168 }
1169 }
1170 }
Svetoslav Ganov80943d82013-01-02 10:25:37 -08001171
1172 private final class AddNodeInfosForViewId implements Predicate<View> {
1173 private int mViewId = View.NO_ID;
1174 private List<AccessibilityNodeInfo> mInfos;
1175
1176 public void init(int viewId, List<AccessibilityNodeInfo> infos) {
1177 mViewId = viewId;
1178 mInfos = infos;
1179 }
1180
1181 public void reset() {
1182 mViewId = View.NO_ID;
1183 mInfos = null;
1184 }
1185
1186 @Override
Paul Duffinca4964c2017-02-07 15:04:10 +00001187 public boolean test(View view) {
Svetoslav Ganov80943d82013-01-02 10:25:37 -08001188 if (view.getId() == mViewId && isShown(view)) {
1189 mInfos.add(view.createAccessibilityNodeInfo());
1190 }
1191 return false;
1192 }
1193 }
Svetoslav Ganov42138042012-03-20 11:51:39 -07001194}