blob: c7b1d03b120b593cd782a6e66f81a85471711dfb [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
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -070019import android.graphics.Point;
Svetoslav Ganovfd8d9c42012-07-09 18:16:55 -070020import android.graphics.Rect;
Svetoslav9ae9ed22014-09-02 16:36:35 -070021import android.graphics.Region;
Nirmal Patel386a8242015-06-02 18:11:32 -070022import android.os.Binder;
Svetoslav Ganovaa780c12012-04-19 23:01:39 -070023import android.os.Bundle;
Svetoslav Ganov42138042012-03-20 11:51:39 -070024import android.os.Handler;
25import android.os.Looper;
26import android.os.Message;
27import android.os.Process;
28import android.os.RemoteException;
Svetoslav8e3feb12014-02-24 13:46:47 -080029import android.util.LongSparseArray;
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -070030import android.view.View.AttachInfo;
Svetoslav Ganov42138042012-03-20 11:51:39 -070031import android.view.accessibility.AccessibilityInteractionClient;
32import android.view.accessibility.AccessibilityNodeInfo;
33import android.view.accessibility.AccessibilityNodeProvider;
34import android.view.accessibility.IAccessibilityInteractionConnectionCallback;
35
Svetoslav Ganov758143e2012-08-06 16:40:27 -070036import com.android.internal.os.SomeArgs;
Svetoslav Ganov80943d82013-01-02 10:25:37 -080037import com.android.internal.util.Predicate;
Svetoslav Ganov758143e2012-08-06 16:40:27 -070038
Svetoslav Ganov42138042012-03-20 11:51:39 -070039import java.util.ArrayList;
40import java.util.HashMap;
Svetoslav8e3feb12014-02-24 13:46:47 -080041import java.util.HashSet;
42import java.util.LinkedList;
Svetoslav Ganov42138042012-03-20 11:51:39 -070043import java.util.List;
44import java.util.Map;
Svetoslav8e3feb12014-02-24 13:46:47 -080045import java.util.Queue;
Svetoslav Ganov42138042012-03-20 11:51:39 -070046
47/**
48 * Class for managing accessibility interactions initiated from the system
49 * and targeting the view hierarchy. A *ClientThread method is to be
50 * called from the interaction connection ViewAncestor gives the system to
51 * talk to it and a corresponding *UiThread method that is executed on the
52 * UI thread.
53 */
54final class AccessibilityInteractionController {
Svetoslav Ganov42138042012-03-20 11:51:39 -070055
Svetoslavaaa11422014-03-28 13:31:13 -070056 private static final boolean ENFORCE_NODE_TREE_CONSISTENT = false;
Svetoslav8e3feb12014-02-24 13:46:47 -080057
Svetoslav Ganov80943d82013-01-02 10:25:37 -080058 private final ArrayList<AccessibilityNodeInfo> mTempAccessibilityNodeInfoList =
Svetoslav Ganov42138042012-03-20 11:51:39 -070059 new ArrayList<AccessibilityNodeInfo>();
60
Svetoslav Ganov005b83b2012-04-16 18:17:17 -070061 private final Handler mHandler;
Svetoslav Ganov42138042012-03-20 11:51:39 -070062
63 private final ViewRootImpl mViewRootImpl;
64
65 private final AccessibilityNodePrefetcher mPrefetcher;
66
Svetoslav Ganov749e7962012-04-19 17:13:46 -070067 private final long mMyLooperThreadId;
68
69 private final int mMyProcessId;
70
Svetoslav Ganov30ac6452012-06-01 09:10:25 -070071 private final ArrayList<View> mTempArrayList = new ArrayList<View>();
72
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -070073 private final Point mTempPoint = new Point();
Svetoslav Ganovfd8d9c42012-07-09 18:16:55 -070074 private final Rect mTempRect = new Rect();
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -070075 private final Rect mTempRect1 = new Rect();
76 private final Rect mTempRect2 = new Rect();
Svetoslav Ganovfd8d9c42012-07-09 18:16:55 -070077
Svetoslav Ganov80943d82013-01-02 10:25:37 -080078 private AddNodeInfosForViewId mAddNodeInfosForViewId;
79
Svetoslav Ganov42138042012-03-20 11:51:39 -070080 public AccessibilityInteractionController(ViewRootImpl viewRootImpl) {
Svetoslav Ganov749e7962012-04-19 17:13:46 -070081 Looper looper = viewRootImpl.mHandler.getLooper();
82 mMyLooperThreadId = looper.getThread().getId();
83 mMyProcessId = Process.myPid();
Svetoslav Ganov005b83b2012-04-16 18:17:17 -070084 mHandler = new PrivateHandler(looper);
Svetoslav Ganov42138042012-03-20 11:51:39 -070085 mViewRootImpl = viewRootImpl;
86 mPrefetcher = new AccessibilityNodePrefetcher();
87 }
88
Svetoslav Ganov0a1bb6d2012-05-07 11:54:39 -070089 private boolean isShown(View view) {
90 // The first two checks are made also made by isShown() which
91 // however traverses the tree up to the parent to catch that.
92 // Therefore, we do some fail fast check to minimize the up
93 // tree traversal.
94 return (view.mAttachInfo != null
95 && view.mAttachInfo.mWindowVisibility == View.VISIBLE
96 && view.isShown());
97 }
98
Svetoslav Ganov42138042012-03-20 11:51:39 -070099 public void findAccessibilityNodeInfoByAccessibilityIdClientThread(
Svetoslav9ae9ed22014-09-02 16:36:35 -0700100 long accessibilityNodeId, Region interactiveRegion, int interactionId,
Svetoslav Ganov42138042012-03-20 11:51:39 -0700101 IAccessibilityInteractionConnectionCallback callback, int flags, int interrogatingPid,
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700102 long interrogatingTid, MagnificationSpec spec) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700103 Message message = mHandler.obtainMessage();
Svet Ganov7498efd2014-09-03 21:33:00 -0700104 message.what = PrivateHandler.MSG_FIND_ACCESSIBILITY_NODE_INFO_BY_ACCESSIBILITY_ID;
Svetoslav Ganov42138042012-03-20 11:51:39 -0700105 message.arg1 = flags;
Svetoslav Ganov86783472012-06-06 21:12:20 -0700106
Svetoslav Ganov758143e2012-08-06 16:40:27 -0700107 SomeArgs args = SomeArgs.obtain();
Svetoslav Ganov42138042012-03-20 11:51:39 -0700108 args.argi1 = AccessibilityNodeInfo.getAccessibilityViewId(accessibilityNodeId);
109 args.argi2 = AccessibilityNodeInfo.getVirtualDescendantId(accessibilityNodeId);
110 args.argi3 = interactionId;
111 args.arg1 = callback;
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700112 args.arg2 = spec;
Svetoslav9ae9ed22014-09-02 16:36:35 -0700113 args.arg3 = interactiveRegion;
Svetoslav Ganov42138042012-03-20 11:51:39 -0700114 message.obj = args;
Svetoslav Ganov86783472012-06-06 21:12:20 -0700115
Svetoslav Ganov42138042012-03-20 11:51:39 -0700116 // If the interrogation is performed by the same thread as the main UI
117 // thread in this process, set the message as a static reference so
118 // after this call completes the same thread but in the interrogating
119 // client can handle the message to generate the result.
Svetoslav Ganov749e7962012-04-19 17:13:46 -0700120 if (interrogatingPid == mMyProcessId && interrogatingTid == mMyLooperThreadId) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700121 AccessibilityInteractionClient.getInstanceForThread(
122 interrogatingTid).setSameThreadMessage(message);
123 } else {
124 mHandler.sendMessage(message);
125 }
126 }
127
128 private void findAccessibilityNodeInfoByAccessibilityIdUiThread(Message message) {
129 final int flags = message.arg1;
Svetoslav Ganov86783472012-06-06 21:12:20 -0700130
Svetoslav Ganov42138042012-03-20 11:51:39 -0700131 SomeArgs args = (SomeArgs) message.obj;
132 final int accessibilityViewId = args.argi1;
133 final int virtualDescendantId = args.argi2;
134 final int interactionId = args.argi3;
135 final IAccessibilityInteractionConnectionCallback callback =
136 (IAccessibilityInteractionConnectionCallback) args.arg1;
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700137 final MagnificationSpec spec = (MagnificationSpec) args.arg2;
Svetoslav9ae9ed22014-09-02 16:36:35 -0700138 final Region interactiveRegion = (Region) args.arg3;
Svetoslav Ganov86783472012-06-06 21:12:20 -0700139
Svetoslav Ganov758143e2012-08-06 16:40:27 -0700140 args.recycle();
Svetoslav Ganov86783472012-06-06 21:12:20 -0700141
Svetoslav Ganov42138042012-03-20 11:51:39 -0700142 List<AccessibilityNodeInfo> infos = mTempAccessibilityNodeInfoList;
143 infos.clear();
144 try {
145 if (mViewRootImpl.mView == null || mViewRootImpl.mAttachInfo == null) {
146 return;
147 }
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800148 mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = flags;
Svetoslav Ganov42138042012-03-20 11:51:39 -0700149 View root = null;
Svetoslav8e3feb12014-02-24 13:46:47 -0800150 if (accessibilityViewId == AccessibilityNodeInfo.UNDEFINED_ITEM_ID) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700151 root = mViewRootImpl.mView;
152 } else {
153 root = findViewByAccessibilityId(accessibilityViewId);
154 }
Svetoslav Ganov0a1bb6d2012-05-07 11:54:39 -0700155 if (root != null && isShown(root)) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700156 mPrefetcher.prefetchAccessibilityNodeInfos(root, virtualDescendantId, flags, infos);
157 }
158 } finally {
159 try {
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800160 mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = 0;
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700161 applyAppScaleAndMagnificationSpecIfNeeded(infos, spec);
Nirmal Patel386a8242015-06-02 18:11:32 -0700162 // Recycle if called from another process. Specs are cached in the
163 // system process and obtained from a pool when read from parcel.
164 if (spec != null && android.os.Process.myPid() != Binder.getCallingPid()) {
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700165 spec.recycle();
166 }
Svetoslav9ae9ed22014-09-02 16:36:35 -0700167 adjustIsVisibleToUserIfNeeded(infos, interactiveRegion);
Svetoslav Ganov42138042012-03-20 11:51:39 -0700168 callback.setFindAccessibilityNodeInfosResult(infos, interactionId);
169 infos.clear();
170 } catch (RemoteException re) {
171 /* ignore - the other side will time out */
172 }
Nirmal Patel386a8242015-06-02 18:11:32 -0700173
174 // Recycle if called from the same process. Regions are obtained in
175 // the system process and instantiated when read from parcel.
176 if (interactiveRegion != null && android.os.Process.myPid() == Binder.getCallingPid()) {
177 interactiveRegion.recycle();
178 }
Svetoslav Ganov42138042012-03-20 11:51:39 -0700179 }
180 }
181
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800182 public void findAccessibilityNodeInfosByViewIdClientThread(long accessibilityNodeId,
Svetoslav9ae9ed22014-09-02 16:36:35 -0700183 String viewId, Region interactiveRegion, int interactionId,
184 IAccessibilityInteractionConnectionCallback callback, int flags, int interrogatingPid,
185 long interrogatingTid, MagnificationSpec spec) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700186 Message message = mHandler.obtainMessage();
Svet Ganov7498efd2014-09-03 21:33:00 -0700187 message.what = PrivateHandler.MSG_FIND_ACCESSIBILITY_NODE_INFOS_BY_VIEW_ID;
Svetoslav Ganov42138042012-03-20 11:51:39 -0700188 message.arg1 = flags;
189 message.arg2 = AccessibilityNodeInfo.getAccessibilityViewId(accessibilityNodeId);
Svetoslav Ganov86783472012-06-06 21:12:20 -0700190
Svetoslav Ganov758143e2012-08-06 16:40:27 -0700191 SomeArgs args = SomeArgs.obtain();
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800192 args.argi1 = interactionId;
Svetoslav Ganov42138042012-03-20 11:51:39 -0700193 args.arg1 = callback;
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700194 args.arg2 = spec;
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800195 args.arg3 = viewId;
Svetoslav9ae9ed22014-09-02 16:36:35 -0700196 args.arg4 = interactiveRegion;
Svetoslav Ganov86783472012-06-06 21:12:20 -0700197
Svetoslav Ganov42138042012-03-20 11:51:39 -0700198 message.obj = args;
Svetoslav Ganov86783472012-06-06 21:12:20 -0700199
Svetoslav Ganov42138042012-03-20 11:51:39 -0700200 // If the interrogation is performed by the same thread as the main UI
201 // thread in this process, set the message as a static reference so
202 // after this call completes the same thread but in the interrogating
203 // client can handle the message to generate the result.
Svetoslav Ganov749e7962012-04-19 17:13:46 -0700204 if (interrogatingPid == mMyProcessId && interrogatingTid == mMyLooperThreadId) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700205 AccessibilityInteractionClient.getInstanceForThread(
206 interrogatingTid).setSameThreadMessage(message);
207 } else {
208 mHandler.sendMessage(message);
209 }
210 }
211
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800212 private void findAccessibilityNodeInfosByViewIdUiThread(Message message) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700213 final int flags = message.arg1;
214 final int accessibilityViewId = message.arg2;
Svetoslav Ganov86783472012-06-06 21:12:20 -0700215
Svetoslav Ganov42138042012-03-20 11:51:39 -0700216 SomeArgs args = (SomeArgs) message.obj;
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800217 final int interactionId = args.argi1;
Svetoslav Ganov42138042012-03-20 11:51:39 -0700218 final IAccessibilityInteractionConnectionCallback callback =
219 (IAccessibilityInteractionConnectionCallback) args.arg1;
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700220 final MagnificationSpec spec = (MagnificationSpec) args.arg2;
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800221 final String viewId = (String) args.arg3;
Svetoslav9ae9ed22014-09-02 16:36:35 -0700222 final Region interactiveRegion = (Region) args.arg4;
Svetoslav Ganov86783472012-06-06 21:12:20 -0700223
Svetoslav Ganov758143e2012-08-06 16:40:27 -0700224 args.recycle();
Svetoslav Ganov86783472012-06-06 21:12:20 -0700225
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800226 final List<AccessibilityNodeInfo> infos = mTempAccessibilityNodeInfoList;
227 infos.clear();
Svetoslav Ganov42138042012-03-20 11:51:39 -0700228 try {
229 if (mViewRootImpl.mView == null || mViewRootImpl.mAttachInfo == null) {
230 return;
231 }
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800232 mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = flags;
Svetoslav Ganov42138042012-03-20 11:51:39 -0700233 View root = null;
Svetoslav8e3feb12014-02-24 13:46:47 -0800234 if (accessibilityViewId != AccessibilityNodeInfo.UNDEFINED_ITEM_ID) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700235 root = findViewByAccessibilityId(accessibilityViewId);
236 } else {
237 root = mViewRootImpl.mView;
238 }
239 if (root != null) {
Svetoslava33243e2013-02-11 15:43:46 -0800240 final int resolvedViewId = root.getContext().getResources()
241 .getIdentifier(viewId, null, null);
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800242 if (resolvedViewId <= 0) {
243 return;
244 }
245 if (mAddNodeInfosForViewId == null) {
246 mAddNodeInfosForViewId = new AddNodeInfosForViewId();
247 }
248 mAddNodeInfosForViewId.init(resolvedViewId, infos);
249 root.findViewByPredicate(mAddNodeInfosForViewId);
250 mAddNodeInfosForViewId.reset();
Svetoslav Ganov42138042012-03-20 11:51:39 -0700251 }
252 } finally {
253 try {
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800254 mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = 0;
255 applyAppScaleAndMagnificationSpecIfNeeded(infos, spec);
Nirmal Patel386a8242015-06-02 18:11:32 -0700256 // Recycle if called from another process. Specs are cached in the
257 // system process and obtained from a pool when read from parcel.
258 if (spec != null && android.os.Process.myPid() != Binder.getCallingPid()) {
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700259 spec.recycle();
260 }
Svetoslav9ae9ed22014-09-02 16:36:35 -0700261 adjustIsVisibleToUserIfNeeded(infos, interactiveRegion);
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800262 callback.setFindAccessibilityNodeInfosResult(infos, interactionId);
Svetoslav Ganov42138042012-03-20 11:51:39 -0700263 } catch (RemoteException re) {
264 /* ignore - the other side will time out */
265 }
Nirmal Patel386a8242015-06-02 18:11:32 -0700266
267 // Recycle if called from the same process. Regions are obtained in
268 // the system process and instantiated when read from parcel.
269 if (interactiveRegion != null && android.os.Process.myPid() == Binder.getCallingPid()) {
270 interactiveRegion.recycle();
271 }
Svetoslav Ganov42138042012-03-20 11:51:39 -0700272 }
273 }
274
275 public void findAccessibilityNodeInfosByTextClientThread(long accessibilityNodeId,
Svetoslav9ae9ed22014-09-02 16:36:35 -0700276 String text, Region interactiveRegion, int interactionId,
277 IAccessibilityInteractionConnectionCallback callback, int flags, int interrogatingPid,
278 long interrogatingTid, MagnificationSpec spec) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700279 Message message = mHandler.obtainMessage();
Svet Ganov7498efd2014-09-03 21:33:00 -0700280 message.what = PrivateHandler.MSG_FIND_ACCESSIBILITY_NODE_INFO_BY_TEXT;
Svetoslav Ganov42138042012-03-20 11:51:39 -0700281 message.arg1 = flags;
Svetoslav Ganov86783472012-06-06 21:12:20 -0700282
Svetoslav Ganov758143e2012-08-06 16:40:27 -0700283 SomeArgs args = SomeArgs.obtain();
Svetoslav Ganov42138042012-03-20 11:51:39 -0700284 args.arg1 = text;
Svetoslav Ganovc9c9a482012-07-16 08:46:07 -0700285 args.arg2 = callback;
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700286 args.arg3 = spec;
Svetoslav Ganov42138042012-03-20 11:51:39 -0700287 args.argi1 = AccessibilityNodeInfo.getAccessibilityViewId(accessibilityNodeId);
288 args.argi2 = AccessibilityNodeInfo.getVirtualDescendantId(accessibilityNodeId);
289 args.argi3 = interactionId;
Svetoslav9ae9ed22014-09-02 16:36:35 -0700290 args.arg4 = interactiveRegion;
Svetoslav Ganov42138042012-03-20 11:51:39 -0700291 message.obj = args;
Svetoslav Ganov86783472012-06-06 21:12:20 -0700292
Svetoslav Ganov42138042012-03-20 11:51:39 -0700293 // If the interrogation is performed by the same thread as the main UI
294 // thread in this process, set the message as a static reference so
295 // after this call completes the same thread but in the interrogating
296 // client can handle the message to generate the result.
Svetoslav Ganov749e7962012-04-19 17:13:46 -0700297 if (interrogatingPid == mMyProcessId && interrogatingTid == mMyLooperThreadId) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700298 AccessibilityInteractionClient.getInstanceForThread(
299 interrogatingTid).setSameThreadMessage(message);
300 } else {
301 mHandler.sendMessage(message);
302 }
303 }
304
305 private void findAccessibilityNodeInfosByTextUiThread(Message message) {
306 final int flags = message.arg1;
Svetoslav Ganov86783472012-06-06 21:12:20 -0700307
Svetoslav Ganov42138042012-03-20 11:51:39 -0700308 SomeArgs args = (SomeArgs) message.obj;
309 final String text = (String) args.arg1;
Svetoslav Ganovc9c9a482012-07-16 08:46:07 -0700310 final IAccessibilityInteractionConnectionCallback callback =
311 (IAccessibilityInteractionConnectionCallback) args.arg2;
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700312 final MagnificationSpec spec = (MagnificationSpec) args.arg3;
Svetoslav Ganov42138042012-03-20 11:51:39 -0700313 final int accessibilityViewId = args.argi1;
314 final int virtualDescendantId = args.argi2;
315 final int interactionId = args.argi3;
Svetoslav9ae9ed22014-09-02 16:36:35 -0700316 final Region interactiveRegion = (Region) args.arg4;
Svetoslav Ganov758143e2012-08-06 16:40:27 -0700317 args.recycle();
Svetoslav Ganov86783472012-06-06 21:12:20 -0700318
Svetoslav Ganov42138042012-03-20 11:51:39 -0700319 List<AccessibilityNodeInfo> infos = null;
320 try {
321 if (mViewRootImpl.mView == null || mViewRootImpl.mAttachInfo == null) {
322 return;
323 }
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800324 mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = flags;
Svetoslav Ganov42138042012-03-20 11:51:39 -0700325 View root = null;
Svetoslav8e3feb12014-02-24 13:46:47 -0800326 if (accessibilityViewId != AccessibilityNodeInfo.UNDEFINED_ITEM_ID) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700327 root = findViewByAccessibilityId(accessibilityViewId);
328 } else {
329 root = mViewRootImpl.mView;
330 }
Svetoslav Ganov0a1bb6d2012-05-07 11:54:39 -0700331 if (root != null && isShown(root)) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700332 AccessibilityNodeProvider provider = root.getAccessibilityNodeProvider();
333 if (provider != null) {
Svetoslav8e3feb12014-02-24 13:46:47 -0800334 if (virtualDescendantId != AccessibilityNodeInfo.UNDEFINED_ITEM_ID) {
335 infos = provider.findAccessibilityNodeInfosByText(text,
336 virtualDescendantId);
337 } else {
338 infos = provider.findAccessibilityNodeInfosByText(text,
339 AccessibilityNodeProvider.HOST_VIEW_ID);
340 }
341 } else if (virtualDescendantId == AccessibilityNodeInfo.UNDEFINED_ITEM_ID) {
Svetoslav Ganov30ac6452012-06-01 09:10:25 -0700342 ArrayList<View> foundViews = mTempArrayList;
Svetoslav Ganov42138042012-03-20 11:51:39 -0700343 foundViews.clear();
344 root.findViewsWithText(foundViews, text, View.FIND_VIEWS_WITH_TEXT
345 | View.FIND_VIEWS_WITH_CONTENT_DESCRIPTION
346 | View.FIND_VIEWS_WITH_ACCESSIBILITY_NODE_PROVIDERS);
347 if (!foundViews.isEmpty()) {
348 infos = mTempAccessibilityNodeInfoList;
349 infos.clear();
350 final int viewCount = foundViews.size();
351 for (int i = 0; i < viewCount; i++) {
352 View foundView = foundViews.get(i);
Svetoslav Ganov0a1bb6d2012-05-07 11:54:39 -0700353 if (isShown(foundView)) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700354 provider = foundView.getAccessibilityNodeProvider();
355 if (provider != null) {
356 List<AccessibilityNodeInfo> infosFromProvider =
357 provider.findAccessibilityNodeInfosByText(text,
Svetoslav8e3feb12014-02-24 13:46:47 -0800358 AccessibilityNodeProvider.HOST_VIEW_ID);
Svetoslav Ganov42138042012-03-20 11:51:39 -0700359 if (infosFromProvider != null) {
360 infos.addAll(infosFromProvider);
361 }
362 } else {
363 infos.add(foundView.createAccessibilityNodeInfo());
364 }
365 }
366 }
367 }
368 }
369 }
370 } finally {
371 try {
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800372 mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = 0;
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700373 applyAppScaleAndMagnificationSpecIfNeeded(infos, spec);
Nirmal Patel386a8242015-06-02 18:11:32 -0700374 // Recycle if called from another process. Specs are cached in the
375 // system process and obtained from a pool when read from parcel.
376 if (spec != null && android.os.Process.myPid() != Binder.getCallingPid()) {
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700377 spec.recycle();
378 }
Svetoslav9ae9ed22014-09-02 16:36:35 -0700379 adjustIsVisibleToUserIfNeeded(infos, interactiveRegion);
Svetoslav Ganov42138042012-03-20 11:51:39 -0700380 callback.setFindAccessibilityNodeInfosResult(infos, interactionId);
381 } catch (RemoteException re) {
382 /* ignore - the other side will time out */
383 }
Nirmal Patel386a8242015-06-02 18:11:32 -0700384
385 // Recycle if called from the same process. Regions are obtained in
386 // the system process and instantiated when read from parcel.
387 if (interactiveRegion != null && android.os.Process.myPid() == Binder.getCallingPid()) {
388 interactiveRegion.recycle();
389 }
Svetoslav Ganov42138042012-03-20 11:51:39 -0700390 }
391 }
392
Svetoslav9ae9ed22014-09-02 16:36:35 -0700393 public void findFocusClientThread(long accessibilityNodeId, int focusType,
394 Region interactiveRegion, int interactionId,
Svetoslav Ganovc9c9a482012-07-16 08:46:07 -0700395 IAccessibilityInteractionConnectionCallback callback, int flags, int interogatingPid,
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700396 long interrogatingTid, MagnificationSpec spec) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700397 Message message = mHandler.obtainMessage();
398 message.what = PrivateHandler.MSG_FIND_FOCUS;
399 message.arg1 = flags;
400 message.arg2 = focusType;
Svetoslav Ganov86783472012-06-06 21:12:20 -0700401
Svetoslav Ganov758143e2012-08-06 16:40:27 -0700402 SomeArgs args = SomeArgs.obtain();
Svetoslav Ganov42138042012-03-20 11:51:39 -0700403 args.argi1 = interactionId;
404 args.argi2 = AccessibilityNodeInfo.getAccessibilityViewId(accessibilityNodeId);
405 args.argi3 = AccessibilityNodeInfo.getVirtualDescendantId(accessibilityNodeId);
406 args.arg1 = callback;
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700407 args.arg2 = spec;
Svetoslav9ae9ed22014-09-02 16:36:35 -0700408 args.arg3 = interactiveRegion;
Svetoslav Ganov86783472012-06-06 21:12:20 -0700409
Svetoslav Ganov42138042012-03-20 11:51:39 -0700410 message.obj = args;
Svetoslav Ganov86783472012-06-06 21:12:20 -0700411
Svetoslav Ganov42138042012-03-20 11:51:39 -0700412 // If the interrogation is performed by the same thread as the main UI
413 // thread in this process, set the message as a static reference so
414 // after this call completes the same thread but in the interrogating
415 // client can handle the message to generate the result.
Svetoslav Ganov749e7962012-04-19 17:13:46 -0700416 if (interogatingPid == mMyProcessId && interrogatingTid == mMyLooperThreadId) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700417 AccessibilityInteractionClient.getInstanceForThread(
418 interrogatingTid).setSameThreadMessage(message);
419 } else {
420 mHandler.sendMessage(message);
421 }
422 }
423
424 private void findFocusUiThread(Message message) {
425 final int flags = message.arg1;
426 final int focusType = message.arg2;
Svetoslav Ganov86783472012-06-06 21:12:20 -0700427
Svetoslav Ganov42138042012-03-20 11:51:39 -0700428 SomeArgs args = (SomeArgs) message.obj;
429 final int interactionId = args.argi1;
430 final int accessibilityViewId = args.argi2;
431 final int virtualDescendantId = args.argi3;
432 final IAccessibilityInteractionConnectionCallback callback =
433 (IAccessibilityInteractionConnectionCallback) args.arg1;
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700434 final MagnificationSpec spec = (MagnificationSpec) args.arg2;
Svetoslav9ae9ed22014-09-02 16:36:35 -0700435 final Region interactiveRegion = (Region) args.arg3;
Svetoslav Ganov758143e2012-08-06 16:40:27 -0700436 args.recycle();
Svetoslav Ganov86783472012-06-06 21:12:20 -0700437
Svetoslav Ganov42138042012-03-20 11:51:39 -0700438 AccessibilityNodeInfo focused = null;
439 try {
440 if (mViewRootImpl.mView == null || mViewRootImpl.mAttachInfo == null) {
441 return;
442 }
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800443 mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = flags;
Svetoslav Ganov42138042012-03-20 11:51:39 -0700444 View root = null;
Svetoslav8e3feb12014-02-24 13:46:47 -0800445 if (accessibilityViewId != AccessibilityNodeInfo.UNDEFINED_ITEM_ID) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700446 root = findViewByAccessibilityId(accessibilityViewId);
447 } else {
448 root = mViewRootImpl.mView;
449 }
Svetoslav Ganov0a1bb6d2012-05-07 11:54:39 -0700450 if (root != null && isShown(root)) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700451 switch (focusType) {
452 case AccessibilityNodeInfo.FOCUS_ACCESSIBILITY: {
453 View host = mViewRootImpl.mAccessibilityFocusedHost;
454 // If there is no accessibility focus host or it is not a descendant
455 // of the root from which to start the search, then the search failed.
456 if (host == null || !ViewRootImpl.isViewDescendantOf(host, root)) {
457 break;
458 }
Svetoslav6254f482013-06-04 17:22:14 -0700459 // The focused view not shown, we failed.
460 if (!isShown(host)) {
461 break;
462 }
Svetoslav Ganov42138042012-03-20 11:51:39 -0700463 // If the host has a provider ask this provider to search for the
464 // focus instead fetching all provider nodes to do the search here.
465 AccessibilityNodeProvider provider = host.getAccessibilityNodeProvider();
466 if (provider != null) {
Svetoslav Ganov45a02e02012-06-17 15:07:29 -0700467 if (mViewRootImpl.mAccessibilityFocusedVirtualView != null) {
468 focused = AccessibilityNodeInfo.obtain(
469 mViewRootImpl.mAccessibilityFocusedVirtualView);
470 }
Svetoslav8e3feb12014-02-24 13:46:47 -0800471 } else if (virtualDescendantId == AccessibilityNodeInfo.UNDEFINED_ITEM_ID) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700472 focused = host.createAccessibilityNodeInfo();
473 }
474 } break;
475 case AccessibilityNodeInfo.FOCUS_INPUT: {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700476 View target = root.findFocus();
Alan Viverette2e1e0812013-09-30 13:45:55 -0700477 if (target == null || !isShown(target)) {
478 break;
479 }
480 AccessibilityNodeProvider provider = target.getAccessibilityNodeProvider();
481 if (provider != null) {
482 focused = provider.findFocus(focusType);
483 }
484 if (focused == null) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700485 focused = target.createAccessibilityNodeInfo();
486 }
487 } break;
488 default:
489 throw new IllegalArgumentException("Unknown focus type: " + focusType);
490 }
491 }
492 } finally {
493 try {
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800494 mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = 0;
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700495 applyAppScaleAndMagnificationSpecIfNeeded(focused, spec);
Nirmal Patel386a8242015-06-02 18:11:32 -0700496 // Recycle if called from another process. Specs are cached in the
497 // system process and obtained from a pool when read from parcel.
498 if (spec != null && android.os.Process.myPid() != Binder.getCallingPid()) {
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700499 spec.recycle();
500 }
Svetoslav9ae9ed22014-09-02 16:36:35 -0700501 adjustIsVisibleToUserIfNeeded(focused, interactiveRegion);
Svetoslav Ganov42138042012-03-20 11:51:39 -0700502 callback.setFindAccessibilityNodeInfoResult(focused, interactionId);
503 } catch (RemoteException re) {
504 /* ignore - the other side will time out */
505 }
Nirmal Patel386a8242015-06-02 18:11:32 -0700506
507 // Recycle if called from the same process. Regions are obtained in
508 // the system process and instantiated when read from parcel.
509 if (interactiveRegion != null && android.os.Process.myPid() == Binder.getCallingPid()) {
510 interactiveRegion.recycle();
511 }
Svetoslav Ganov42138042012-03-20 11:51:39 -0700512 }
513 }
514
Svetoslav9ae9ed22014-09-02 16:36:35 -0700515 public void focusSearchClientThread(long accessibilityNodeId, int direction,
516 Region interactiveRegion, int interactionId,
Svetoslav Ganovc9c9a482012-07-16 08:46:07 -0700517 IAccessibilityInteractionConnectionCallback callback, int flags, int interogatingPid,
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700518 long interrogatingTid, MagnificationSpec spec) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700519 Message message = mHandler.obtainMessage();
520 message.what = PrivateHandler.MSG_FOCUS_SEARCH;
521 message.arg1 = flags;
522 message.arg2 = AccessibilityNodeInfo.getAccessibilityViewId(accessibilityNodeId);
Svetoslav Ganov86783472012-06-06 21:12:20 -0700523
Svetoslav Ganov758143e2012-08-06 16:40:27 -0700524 SomeArgs args = SomeArgs.obtain();
Svetoslav Ganov42138042012-03-20 11:51:39 -0700525 args.argi2 = direction;
526 args.argi3 = interactionId;
527 args.arg1 = callback;
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700528 args.arg2 = spec;
Svetoslav9ae9ed22014-09-02 16:36:35 -0700529 args.arg3 = interactiveRegion;
Svetoslav Ganov86783472012-06-06 21:12:20 -0700530
Svetoslav Ganov42138042012-03-20 11:51:39 -0700531 message.obj = args;
Svetoslav Ganov86783472012-06-06 21:12:20 -0700532
Svetoslav Ganov42138042012-03-20 11:51:39 -0700533 // If the interrogation is performed by the same thread as the main UI
534 // thread in this process, set the message as a static reference so
535 // after this call completes the same thread but in the interrogating
536 // client can handle the message to generate the result.
Svetoslav Ganov749e7962012-04-19 17:13:46 -0700537 if (interogatingPid == mMyProcessId && interrogatingTid == mMyLooperThreadId) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700538 AccessibilityInteractionClient.getInstanceForThread(
539 interrogatingTid).setSameThreadMessage(message);
540 } else {
541 mHandler.sendMessage(message);
542 }
543 }
544
545 private void focusSearchUiThread(Message message) {
546 final int flags = message.arg1;
547 final int accessibilityViewId = message.arg2;
Svetoslav Ganov86783472012-06-06 21:12:20 -0700548
Svetoslav Ganov42138042012-03-20 11:51:39 -0700549 SomeArgs args = (SomeArgs) message.obj;
Svetoslav Ganov42138042012-03-20 11:51:39 -0700550 final int direction = args.argi2;
551 final int interactionId = args.argi3;
552 final IAccessibilityInteractionConnectionCallback callback =
553 (IAccessibilityInteractionConnectionCallback) args.arg1;
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700554 final MagnificationSpec spec = (MagnificationSpec) args.arg2;
Svetoslav9ae9ed22014-09-02 16:36:35 -0700555 final Region interactiveRegion = (Region) args.arg3;
Svetoslav Ganov86783472012-06-06 21:12:20 -0700556
Svetoslav Ganov758143e2012-08-06 16:40:27 -0700557 args.recycle();
Svetoslav Ganov86783472012-06-06 21:12:20 -0700558
Svetoslav Ganov42138042012-03-20 11:51:39 -0700559 AccessibilityNodeInfo next = null;
560 try {
561 if (mViewRootImpl.mView == null || mViewRootImpl.mAttachInfo == null) {
562 return;
563 }
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800564 mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = flags;
Svetoslav Ganov42138042012-03-20 11:51:39 -0700565 View root = null;
Svetoslav8e3feb12014-02-24 13:46:47 -0800566 if (accessibilityViewId != AccessibilityNodeInfo.UNDEFINED_ITEM_ID) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700567 root = findViewByAccessibilityId(accessibilityViewId);
568 } else {
569 root = mViewRootImpl.mView;
570 }
Svetoslav Ganov0a1bb6d2012-05-07 11:54:39 -0700571 if (root != null && isShown(root)) {
Svetoslav Ganov27e2da72012-07-02 18:12:00 -0700572 View nextView = root.focusSearch(direction);
573 if (nextView != null) {
574 next = nextView.createAccessibilityNodeInfo();
Svetoslav Ganov42138042012-03-20 11:51:39 -0700575 }
576 }
577 } finally {
578 try {
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800579 mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = 0;
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700580 applyAppScaleAndMagnificationSpecIfNeeded(next, spec);
Nirmal Patel386a8242015-06-02 18:11:32 -0700581 // Recycle if called from another process. Specs are cached in the
582 // system process and obtained from a pool when read from parcel.
583 if (spec != null && android.os.Process.myPid() != Binder.getCallingPid()) {
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700584 spec.recycle();
585 }
Svetoslav9ae9ed22014-09-02 16:36:35 -0700586 adjustIsVisibleToUserIfNeeded(next, interactiveRegion);
Svetoslav Ganov42138042012-03-20 11:51:39 -0700587 callback.setFindAccessibilityNodeInfoResult(next, interactionId);
588 } catch (RemoteException re) {
589 /* ignore - the other side will time out */
590 }
Nirmal Patel386a8242015-06-02 18:11:32 -0700591
592 // Recycle if called from the same process. Regions are obtained in
593 // the system process and instantiated when read from parcel.
594 if (interactiveRegion != null && android.os.Process.myPid() == Binder.getCallingPid()) {
595 interactiveRegion.recycle();
596 }
Svetoslav Ganov42138042012-03-20 11:51:39 -0700597 }
598 }
599
600 public void performAccessibilityActionClientThread(long accessibilityNodeId, int action,
Svetoslav Ganovaa780c12012-04-19 23:01:39 -0700601 Bundle arguments, int interactionId,
602 IAccessibilityInteractionConnectionCallback callback, int flags, int interogatingPid,
603 long interrogatingTid) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700604 Message message = mHandler.obtainMessage();
605 message.what = PrivateHandler.MSG_PERFORM_ACCESSIBILITY_ACTION;
606 message.arg1 = flags;
607 message.arg2 = AccessibilityNodeInfo.getAccessibilityViewId(accessibilityNodeId);
Svetoslav Ganov86783472012-06-06 21:12:20 -0700608
Svetoslav Ganov758143e2012-08-06 16:40:27 -0700609 SomeArgs args = SomeArgs.obtain();
Svetoslav Ganov42138042012-03-20 11:51:39 -0700610 args.argi1 = AccessibilityNodeInfo.getVirtualDescendantId(accessibilityNodeId);
611 args.argi2 = action;
612 args.argi3 = interactionId;
613 args.arg1 = callback;
Svetoslav Ganovaa780c12012-04-19 23:01:39 -0700614 args.arg2 = arguments;
Svetoslav Ganov86783472012-06-06 21:12:20 -0700615
Svetoslav Ganov42138042012-03-20 11:51:39 -0700616 message.obj = args;
Svetoslav Ganov86783472012-06-06 21:12:20 -0700617
Svetoslav Ganov42138042012-03-20 11:51:39 -0700618 // If the interrogation is performed by the same thread as the main UI
619 // thread in this process, set the message as a static reference so
620 // after this call completes the same thread but in the interrogating
621 // client can handle the message to generate the result.
Svetoslav Ganov749e7962012-04-19 17:13:46 -0700622 if (interogatingPid == mMyProcessId && interrogatingTid == mMyLooperThreadId) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700623 AccessibilityInteractionClient.getInstanceForThread(
624 interrogatingTid).setSameThreadMessage(message);
625 } else {
626 mHandler.sendMessage(message);
627 }
628 }
629
George Mount41725de2015-04-09 08:23:05 -0700630 private void performAccessibilityActionUiThread(Message message) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700631 final int flags = message.arg1;
632 final int accessibilityViewId = message.arg2;
Svetoslav Ganov86783472012-06-06 21:12:20 -0700633
Svetoslav Ganov42138042012-03-20 11:51:39 -0700634 SomeArgs args = (SomeArgs) message.obj;
635 final int virtualDescendantId = args.argi1;
636 final int action = args.argi2;
637 final int interactionId = args.argi3;
638 final IAccessibilityInteractionConnectionCallback callback =
639 (IAccessibilityInteractionConnectionCallback) args.arg1;
Svetoslav Ganovaa780c12012-04-19 23:01:39 -0700640 Bundle arguments = (Bundle) args.arg2;
Svetoslav Ganov86783472012-06-06 21:12:20 -0700641
Svetoslav Ganov758143e2012-08-06 16:40:27 -0700642 args.recycle();
Svetoslav Ganov86783472012-06-06 21:12:20 -0700643
Svetoslav Ganov42138042012-03-20 11:51:39 -0700644 boolean succeeded = false;
645 try {
George Mount41725de2015-04-09 08:23:05 -0700646 if (mViewRootImpl.mView == null || mViewRootImpl.mAttachInfo == null ||
647 mViewRootImpl.mStopped || mViewRootImpl.mPausedForTransition) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700648 return;
649 }
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800650 mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = flags;
Svetoslav Ganov42138042012-03-20 11:51:39 -0700651 View target = null;
Svetoslav8e3feb12014-02-24 13:46:47 -0800652 if (accessibilityViewId != AccessibilityNodeInfo.UNDEFINED_ITEM_ID) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700653 target = findViewByAccessibilityId(accessibilityViewId);
654 } else {
655 target = mViewRootImpl.mView;
656 }
Svetoslav Ganov0a1bb6d2012-05-07 11:54:39 -0700657 if (target != null && isShown(target)) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700658 AccessibilityNodeProvider provider = target.getAccessibilityNodeProvider();
659 if (provider != null) {
Svetoslav8e3feb12014-02-24 13:46:47 -0800660 if (virtualDescendantId != AccessibilityNodeInfo.UNDEFINED_ITEM_ID) {
661 succeeded = provider.performAction(virtualDescendantId, action,
662 arguments);
663 } else {
664 succeeded = provider.performAction(AccessibilityNodeProvider.HOST_VIEW_ID,
665 action, arguments);
666 }
667 } else if (virtualDescendantId == AccessibilityNodeInfo.UNDEFINED_ITEM_ID) {
Svetoslav Ganovaa780c12012-04-19 23:01:39 -0700668 succeeded = target.performAccessibilityAction(action, arguments);
Svetoslav Ganov42138042012-03-20 11:51:39 -0700669 }
670 }
671 } finally {
672 try {
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800673 mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = 0;
Svetoslav Ganov42138042012-03-20 11:51:39 -0700674 callback.setPerformAccessibilityActionResult(succeeded, interactionId);
675 } catch (RemoteException re) {
676 /* ignore - the other side will time out */
677 }
678 }
679 }
680
681 private View findViewByAccessibilityId(int accessibilityId) {
682 View root = mViewRootImpl.mView;
683 if (root == null) {
684 return null;
685 }
686 View foundView = root.findViewByAccessibilityId(accessibilityId);
Svetoslav Ganov0a1bb6d2012-05-07 11:54:39 -0700687 if (foundView != null && !isShown(foundView)) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700688 return null;
689 }
690 return foundView;
691 }
692
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700693 private void applyAppScaleAndMagnificationSpecIfNeeded(List<AccessibilityNodeInfo> infos,
694 MagnificationSpec spec) {
Svetoslav Ganovfd8d9c42012-07-09 18:16:55 -0700695 if (infos == null) {
696 return;
697 }
698 final float applicationScale = mViewRootImpl.mAttachInfo.mApplicationScale;
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700699 if (shouldApplyAppScaleAndMagnificationSpec(applicationScale, spec)) {
Svetoslav Ganovfd8d9c42012-07-09 18:16:55 -0700700 final int infoCount = infos.size();
701 for (int i = 0; i < infoCount; i++) {
702 AccessibilityNodeInfo info = infos.get(i);
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700703 applyAppScaleAndMagnificationSpecIfNeeded(info, spec);
Svetoslav Ganovfd8d9c42012-07-09 18:16:55 -0700704 }
705 }
706 }
707
Svetoslav9ae9ed22014-09-02 16:36:35 -0700708 private void adjustIsVisibleToUserIfNeeded(List<AccessibilityNodeInfo> infos,
709 Region interactiveRegion) {
710 if (interactiveRegion == null || infos == null) {
711 return;
712 }
713 final int infoCount = infos.size();
714 for (int i = 0; i < infoCount; i++) {
715 AccessibilityNodeInfo info = infos.get(i);
716 adjustIsVisibleToUserIfNeeded(info, interactiveRegion);
717 }
718 }
719
720 private void adjustIsVisibleToUserIfNeeded(AccessibilityNodeInfo info,
721 Region interactiveRegion) {
722 if (interactiveRegion == null || info == null) {
723 return;
724 }
725 Rect boundsInScreen = mTempRect;
726 info.getBoundsInScreen(boundsInScreen);
727 if (interactiveRegion.quickReject(boundsInScreen)) {
728 info.setVisibleToUser(false);
729 }
730 }
731
Svet Ganov7498efd2014-09-03 21:33:00 -0700732 private void applyAppScaleAndMagnificationSpecIfNeeded(Point point,
733 MagnificationSpec spec) {
734 final float applicationScale = mViewRootImpl.mAttachInfo.mApplicationScale;
735 if (!shouldApplyAppScaleAndMagnificationSpec(applicationScale, spec)) {
736 return;
737 }
738
739 if (applicationScale != 1.0f) {
740 point.x *= applicationScale;
741 point.y *= applicationScale;
742 }
743
744 if (spec != null) {
745 point.x *= spec.scale;
746 point.y *= spec.scale;
747 point.x += (int) spec.offsetX;
748 point.y += (int) spec.offsetY;
749 }
750 }
751
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700752 private void applyAppScaleAndMagnificationSpecIfNeeded(AccessibilityNodeInfo info,
753 MagnificationSpec spec) {
Svetoslav Ganovfd8d9c42012-07-09 18:16:55 -0700754 if (info == null) {
755 return;
756 }
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700757
Svetoslav Ganovfd8d9c42012-07-09 18:16:55 -0700758 final float applicationScale = mViewRootImpl.mAttachInfo.mApplicationScale;
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700759 if (!shouldApplyAppScaleAndMagnificationSpec(applicationScale, spec)) {
760 return;
761 }
762
763 Rect boundsInParent = mTempRect;
764 Rect boundsInScreen = mTempRect1;
765
766 info.getBoundsInParent(boundsInParent);
767 info.getBoundsInScreen(boundsInScreen);
Svetoslav Ganovfd8d9c42012-07-09 18:16:55 -0700768 if (applicationScale != 1.0f) {
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700769 boundsInParent.scale(applicationScale);
770 boundsInScreen.scale(applicationScale);
771 }
772 if (spec != null) {
773 boundsInParent.scale(spec.scale);
774 // boundsInParent must not be offset.
775 boundsInScreen.scale(spec.scale);
776 boundsInScreen.offset((int) spec.offsetX, (int) spec.offsetY);
777 }
778 info.setBoundsInParent(boundsInParent);
779 info.setBoundsInScreen(boundsInScreen);
Svetoslav Ganovfd8d9c42012-07-09 18:16:55 -0700780
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700781 if (spec != null) {
782 AttachInfo attachInfo = mViewRootImpl.mAttachInfo;
783 if (attachInfo.mDisplay == null) {
784 return;
785 }
Svetoslav Ganovfd8d9c42012-07-09 18:16:55 -0700786
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700787 final float scale = attachInfo.mApplicationScale * spec.scale;
788
789 Rect visibleWinFrame = mTempRect1;
790 visibleWinFrame.left = (int) (attachInfo.mWindowLeft * scale + spec.offsetX);
791 visibleWinFrame.top = (int) (attachInfo.mWindowTop * scale + spec.offsetY);
792 visibleWinFrame.right = (int) (visibleWinFrame.left + mViewRootImpl.mWidth * scale);
793 visibleWinFrame.bottom = (int) (visibleWinFrame.top + mViewRootImpl.mHeight * scale);
794
795 attachInfo.mDisplay.getRealSize(mTempPoint);
796 final int displayWidth = mTempPoint.x;
797 final int displayHeight = mTempPoint.y;
798
799 Rect visibleDisplayFrame = mTempRect2;
800 visibleDisplayFrame.set(0, 0, displayWidth, displayHeight);
801
Doris Liu9607fbe2015-05-28 17:17:28 -0700802 if (!visibleWinFrame.intersect(visibleDisplayFrame)) {
803 // If there's no intersection with display, set visibleWinFrame empty.
804 visibleDisplayFrame.setEmpty();
805 }
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700806
807 if (!visibleWinFrame.intersects(boundsInScreen.left, boundsInScreen.top,
808 boundsInScreen.right, boundsInScreen.bottom)) {
809 info.setVisibleToUser(false);
810 }
Svetoslav Ganovfd8d9c42012-07-09 18:16:55 -0700811 }
812 }
813
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700814 private boolean shouldApplyAppScaleAndMagnificationSpec(float appScale,
815 MagnificationSpec spec) {
816 return (appScale != 1.0f || (spec != null && !spec.isNop()));
817 }
Svetoslav Ganovfd8d9c42012-07-09 18:16:55 -0700818
Svetoslav Ganov42138042012-03-20 11:51:39 -0700819 /**
Svetoslav Ganov42138042012-03-20 11:51:39 -0700820 * This class encapsulates a prefetching strategy for the accessibility APIs for
821 * querying window content. It is responsible to prefetch a batch of
822 * AccessibilityNodeInfos in addition to the one for a requested node.
823 */
824 private class AccessibilityNodePrefetcher {
825
826 private static final int MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE = 50;
827
Svetoslav Ganov4528b4e2012-05-15 18:24:10 -0700828 private final ArrayList<View> mTempViewList = new ArrayList<View>();
829
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800830 public void prefetchAccessibilityNodeInfos(View view, int virtualViewId, int fetchFlags,
Svetoslav Ganov42138042012-03-20 11:51:39 -0700831 List<AccessibilityNodeInfo> outInfos) {
832 AccessibilityNodeProvider provider = view.getAccessibilityNodeProvider();
833 if (provider == null) {
834 AccessibilityNodeInfo root = view.createAccessibilityNodeInfo();
835 if (root != null) {
836 outInfos.add(root);
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800837 if ((fetchFlags & AccessibilityNodeInfo.FLAG_PREFETCH_PREDECESSORS) != 0) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700838 prefetchPredecessorsOfRealNode(view, outInfos);
839 }
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800840 if ((fetchFlags & AccessibilityNodeInfo.FLAG_PREFETCH_SIBLINGS) != 0) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700841 prefetchSiblingsOfRealNode(view, outInfos);
842 }
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800843 if ((fetchFlags & AccessibilityNodeInfo.FLAG_PREFETCH_DESCENDANTS) != 0) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700844 prefetchDescendantsOfRealNode(view, outInfos);
845 }
846 }
847 } else {
Svetoslavc569d352014-09-25 18:46:22 -0700848 final AccessibilityNodeInfo root;
849 if (virtualViewId != AccessibilityNodeInfo.UNDEFINED_ITEM_ID) {
850 root = provider.createAccessibilityNodeInfo(virtualViewId);
851 } else {
852 root = provider.createAccessibilityNodeInfo(
853 AccessibilityNodeProvider.HOST_VIEW_ID);
854 }
Svetoslav Ganov42138042012-03-20 11:51:39 -0700855 if (root != null) {
856 outInfos.add(root);
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800857 if ((fetchFlags & AccessibilityNodeInfo.FLAG_PREFETCH_PREDECESSORS) != 0) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700858 prefetchPredecessorsOfVirtualNode(root, view, provider, outInfos);
859 }
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800860 if ((fetchFlags & AccessibilityNodeInfo.FLAG_PREFETCH_SIBLINGS) != 0) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700861 prefetchSiblingsOfVirtualNode(root, view, provider, outInfos);
862 }
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800863 if ((fetchFlags & AccessibilityNodeInfo.FLAG_PREFETCH_DESCENDANTS) != 0) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700864 prefetchDescendantsOfVirtualNode(root, provider, outInfos);
865 }
866 }
867 }
Svetoslav8e3feb12014-02-24 13:46:47 -0800868 if (ENFORCE_NODE_TREE_CONSISTENT) {
869 enforceNodeTreeConsistent(outInfos);
870 }
871 }
872
873 private void enforceNodeTreeConsistent(List<AccessibilityNodeInfo> nodes) {
874 LongSparseArray<AccessibilityNodeInfo> nodeMap =
875 new LongSparseArray<AccessibilityNodeInfo>();
876 final int nodeCount = nodes.size();
877 for (int i = 0; i < nodeCount; i++) {
878 AccessibilityNodeInfo node = nodes.get(i);
879 nodeMap.put(node.getSourceNodeId(), node);
880 }
881
882 // If the nodes are a tree it does not matter from
883 // which node we start to search for the root.
884 AccessibilityNodeInfo root = nodeMap.valueAt(0);
885 AccessibilityNodeInfo parent = root;
886 while (parent != null) {
887 root = parent;
888 parent = nodeMap.get(parent.getParentNodeId());
889 }
890
891 // Traverse the tree and do some checks.
892 AccessibilityNodeInfo accessFocus = null;
893 AccessibilityNodeInfo inputFocus = null;
894 HashSet<AccessibilityNodeInfo> seen = new HashSet<AccessibilityNodeInfo>();
895 Queue<AccessibilityNodeInfo> fringe = new LinkedList<AccessibilityNodeInfo>();
896 fringe.add(root);
897
898 while (!fringe.isEmpty()) {
899 AccessibilityNodeInfo current = fringe.poll();
900
901 // Check for duplicates
902 if (!seen.add(current)) {
903 throw new IllegalStateException("Duplicate node: "
904 + current + " in window:"
905 + mViewRootImpl.mAttachInfo.mAccessibilityWindowId);
906 }
907
908 // Check for one accessibility focus.
909 if (current.isAccessibilityFocused()) {
910 if (accessFocus != null) {
911 throw new IllegalStateException("Duplicate accessibility focus:"
912 + current
913 + " in window:" + mViewRootImpl.mAttachInfo.mAccessibilityWindowId);
914 } else {
915 accessFocus = current;
916 }
917 }
918
919 // Check for one input focus.
920 if (current.isFocused()) {
921 if (inputFocus != null) {
922 throw new IllegalStateException("Duplicate input focus: "
923 + current + " in window:"
924 + mViewRootImpl.mAttachInfo.mAccessibilityWindowId);
925 } else {
926 inputFocus = current;
927 }
928 }
929
930 final int childCount = current.getChildCount();
931 for (int j = 0; j < childCount; j++) {
932 final long childId = current.getChildId(j);
933 final AccessibilityNodeInfo child = nodeMap.get(childId);
934 if (child != null) {
935 fringe.add(child);
936 }
937 }
938 }
939
940 // Check for disconnected nodes.
941 for (int j = nodeMap.size() - 1; j >= 0; j--) {
942 AccessibilityNodeInfo info = nodeMap.valueAt(j);
943 if (!seen.contains(info)) {
944 throw new IllegalStateException("Disconnected node: " + info);
945 }
946 }
Svetoslav Ganov42138042012-03-20 11:51:39 -0700947 }
948
949 private void prefetchPredecessorsOfRealNode(View view,
950 List<AccessibilityNodeInfo> outInfos) {
951 ViewParent parent = view.getParentForAccessibility();
952 while (parent instanceof View
953 && outInfos.size() < MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE) {
954 View parentView = (View) parent;
Svetoslav Ganov42138042012-03-20 11:51:39 -0700955 AccessibilityNodeInfo info = parentView.createAccessibilityNodeInfo();
956 if (info != null) {
957 outInfos.add(info);
958 }
959 parent = parent.getParentForAccessibility();
960 }
961 }
962
963 private void prefetchSiblingsOfRealNode(View current,
964 List<AccessibilityNodeInfo> outInfos) {
965 ViewParent parent = current.getParentForAccessibility();
966 if (parent instanceof ViewGroup) {
967 ViewGroup parentGroup = (ViewGroup) parent;
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 parentGroup.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;
Svetoslav Ganov42138042012-03-20 11:51:39 -0700976 }
Svetoslav Ganov4528b4e2012-05-15 18:24:10 -0700977 View child = children.get(i);
Svetoslav Ganov76f287e2012-04-23 11:02:36 -0700978 if (child.getAccessibilityViewId() != current.getAccessibilityViewId()
Svetoslav Ganov0a1bb6d2012-05-07 11:54:39 -0700979 && isShown(child)) {
Svetoslav Ganov76f287e2012-04-23 11:02:36 -0700980 AccessibilityNodeInfo info = null;
Svetoslav Ganov4528b4e2012-05-15 18:24:10 -0700981 AccessibilityNodeProvider provider =
982 child.getAccessibilityNodeProvider();
Svetoslav Ganov76f287e2012-04-23 11:02:36 -0700983 if (provider == null) {
984 info = child.createAccessibilityNodeInfo();
985 } else {
986 info = provider.createAccessibilityNodeInfo(
Svetoslav8e3feb12014-02-24 13:46:47 -0800987 AccessibilityNodeProvider.HOST_VIEW_ID);
Svetoslav Ganov76f287e2012-04-23 11:02:36 -0700988 }
989 if (info != null) {
990 outInfos.add(info);
991 }
Svetoslav Ganov42138042012-03-20 11:51:39 -0700992 }
993 }
Svetoslav Ganov76f287e2012-04-23 11:02:36 -0700994 } finally {
Svetoslav Ganov4528b4e2012-05-15 18:24:10 -0700995 children.clear();
Svetoslav Ganov42138042012-03-20 11:51:39 -0700996 }
Svetoslav Ganov42138042012-03-20 11:51:39 -0700997 }
998 }
999
1000 private void prefetchDescendantsOfRealNode(View root,
1001 List<AccessibilityNodeInfo> outInfos) {
1002 if (!(root instanceof ViewGroup)) {
1003 return;
1004 }
Svetoslav Ganov42138042012-03-20 11:51:39 -07001005 HashMap<View, AccessibilityNodeInfo> addedChildren =
1006 new HashMap<View, AccessibilityNodeInfo>();
Svetoslav Ganov4528b4e2012-05-15 18:24:10 -07001007 ArrayList<View> children = mTempViewList;
1008 children.clear();
Svetoslav Ganov76f287e2012-04-23 11:02:36 -07001009 try {
Svetoslav Ganov4528b4e2012-05-15 18:24:10 -07001010 root.addChildrenForAccessibility(children);
1011 final int childCount = children.size();
Svetoslav Ganov76f287e2012-04-23 11:02:36 -07001012 for (int i = 0; i < childCount; i++) {
1013 if (outInfos.size() >= MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE) {
1014 return;
1015 }
Svetoslav Ganov4528b4e2012-05-15 18:24:10 -07001016 View child = children.get(i);
Svetoslav Ganov0a1bb6d2012-05-07 11:54:39 -07001017 if (isShown(child)) {
Svetoslav Ganov76f287e2012-04-23 11:02:36 -07001018 AccessibilityNodeProvider provider = child.getAccessibilityNodeProvider();
1019 if (provider == null) {
1020 AccessibilityNodeInfo info = child.createAccessibilityNodeInfo();
1021 if (info != null) {
1022 outInfos.add(info);
1023 addedChildren.put(child, null);
1024 }
1025 } else {
1026 AccessibilityNodeInfo info = provider.createAccessibilityNodeInfo(
Svetoslav8e3feb12014-02-24 13:46:47 -08001027 AccessibilityNodeProvider.HOST_VIEW_ID);
Svetoslav Ganov76f287e2012-04-23 11:02:36 -07001028 if (info != null) {
1029 outInfos.add(info);
1030 addedChildren.put(child, info);
1031 }
Svetoslav Ganov42138042012-03-20 11:51:39 -07001032 }
1033 }
1034 }
Svetoslav Ganov76f287e2012-04-23 11:02:36 -07001035 } finally {
Svetoslav Ganov4528b4e2012-05-15 18:24:10 -07001036 children.clear();
Svetoslav Ganov42138042012-03-20 11:51:39 -07001037 }
Svetoslav Ganov42138042012-03-20 11:51:39 -07001038 if (outInfos.size() < MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE) {
1039 for (Map.Entry<View, AccessibilityNodeInfo> entry : addedChildren.entrySet()) {
1040 View addedChild = entry.getKey();
1041 AccessibilityNodeInfo virtualRoot = entry.getValue();
1042 if (virtualRoot == null) {
1043 prefetchDescendantsOfRealNode(addedChild, outInfos);
1044 } else {
1045 AccessibilityNodeProvider provider =
1046 addedChild.getAccessibilityNodeProvider();
1047 prefetchDescendantsOfVirtualNode(virtualRoot, provider, outInfos);
1048 }
1049 }
1050 }
1051 }
1052
1053 private void prefetchPredecessorsOfVirtualNode(AccessibilityNodeInfo root,
1054 View providerHost, AccessibilityNodeProvider provider,
1055 List<AccessibilityNodeInfo> outInfos) {
Svet Ganov75e58162016-02-19 16:29:24 -08001056 final int initialResultSize = outInfos.size();
Svetoslav Ganov42138042012-03-20 11:51:39 -07001057 long parentNodeId = root.getParentNodeId();
1058 int accessibilityViewId = AccessibilityNodeInfo.getAccessibilityViewId(parentNodeId);
Svetoslav8e3feb12014-02-24 13:46:47 -08001059 while (accessibilityViewId != AccessibilityNodeInfo.UNDEFINED_ITEM_ID) {
Svetoslav Ganov42138042012-03-20 11:51:39 -07001060 if (outInfos.size() >= MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE) {
1061 return;
1062 }
1063 final int virtualDescendantId =
1064 AccessibilityNodeInfo.getVirtualDescendantId(parentNodeId);
Svetoslav8e3feb12014-02-24 13:46:47 -08001065 if (virtualDescendantId != AccessibilityNodeInfo.UNDEFINED_ITEM_ID
Svetoslav Ganov42138042012-03-20 11:51:39 -07001066 || accessibilityViewId == providerHost.getAccessibilityViewId()) {
Svetoslav8e3feb12014-02-24 13:46:47 -08001067 final AccessibilityNodeInfo parent;
1068 if (virtualDescendantId != AccessibilityNodeInfo.UNDEFINED_ITEM_ID) {
Alan Viverette84feea112014-11-04 15:59:55 -08001069 parent = provider.createAccessibilityNodeInfo(virtualDescendantId);
Svetoslav8e3feb12014-02-24 13:46:47 -08001070 } else {
Alan Viverette84feea112014-11-04 15:59:55 -08001071 parent = provider.createAccessibilityNodeInfo(
Svetoslav8e3feb12014-02-24 13:46:47 -08001072 AccessibilityNodeProvider.HOST_VIEW_ID);
1073 }
Alan Viverette84feea112014-11-04 15:59:55 -08001074 if (parent == null) {
Svet Ganov75e58162016-02-19 16:29:24 -08001075 // Going up the parent relation we found a null predecessor,
1076 // so remove these disconnected nodes form the result.
1077 final int currentResultSize = outInfos.size();
1078 for (int i = currentResultSize - 1; i >= initialResultSize; i--) {
1079 outInfos.remove(i);
1080 }
Alan Viverette84feea112014-11-04 15:59:55 -08001081 // Couldn't obtain the parent, which means we have a
1082 // disconnected sub-tree. Abort prefetch immediately.
1083 return;
Svetoslav Ganov42138042012-03-20 11:51:39 -07001084 }
Alan Viverette84feea112014-11-04 15:59:55 -08001085 outInfos.add(parent);
Svetoslav Ganov42138042012-03-20 11:51:39 -07001086 parentNodeId = parent.getParentNodeId();
1087 accessibilityViewId = AccessibilityNodeInfo.getAccessibilityViewId(
1088 parentNodeId);
1089 } else {
1090 prefetchPredecessorsOfRealNode(providerHost, outInfos);
1091 return;
1092 }
1093 }
1094 }
1095
1096 private void prefetchSiblingsOfVirtualNode(AccessibilityNodeInfo current, View providerHost,
1097 AccessibilityNodeProvider provider, List<AccessibilityNodeInfo> outInfos) {
1098 final long parentNodeId = current.getParentNodeId();
1099 final int parentAccessibilityViewId =
1100 AccessibilityNodeInfo.getAccessibilityViewId(parentNodeId);
1101 final int parentVirtualDescendantId =
1102 AccessibilityNodeInfo.getVirtualDescendantId(parentNodeId);
Svetoslav8e3feb12014-02-24 13:46:47 -08001103 if (parentVirtualDescendantId != AccessibilityNodeInfo.UNDEFINED_ITEM_ID
Svetoslav Ganov42138042012-03-20 11:51:39 -07001104 || parentAccessibilityViewId == providerHost.getAccessibilityViewId()) {
Svetoslav8e3feb12014-02-24 13:46:47 -08001105 final AccessibilityNodeInfo parent;
Svetoslaved2fdc62014-09-26 19:23:05 -07001106 if (parentVirtualDescendantId != AccessibilityNodeInfo.UNDEFINED_ITEM_ID) {
Svetoslav8e3feb12014-02-24 13:46:47 -08001107 parent = provider.createAccessibilityNodeInfo(parentVirtualDescendantId);
1108 } else {
1109 parent = provider.createAccessibilityNodeInfo(
1110 AccessibilityNodeProvider.HOST_VIEW_ID);
1111 }
Svetoslav Ganov42138042012-03-20 11:51:39 -07001112 if (parent != null) {
Alan Viverettef0aed092013-11-06 15:33:03 -08001113 final int childCount = parent.getChildCount();
Svetoslav Ganov42138042012-03-20 11:51:39 -07001114 for (int i = 0; i < childCount; i++) {
1115 if (outInfos.size() >= MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE) {
1116 return;
1117 }
Alan Viverettef0aed092013-11-06 15:33:03 -08001118 final long childNodeId = parent.getChildId(i);
Svetoslav Ganov42138042012-03-20 11:51:39 -07001119 if (childNodeId != current.getSourceNodeId()) {
1120 final int childVirtualDescendantId =
1121 AccessibilityNodeInfo.getVirtualDescendantId(childNodeId);
1122 AccessibilityNodeInfo child = provider.createAccessibilityNodeInfo(
1123 childVirtualDescendantId);
1124 if (child != null) {
1125 outInfos.add(child);
1126 }
1127 }
1128 }
1129 }
1130 } else {
1131 prefetchSiblingsOfRealNode(providerHost, outInfos);
1132 }
1133 }
1134
1135 private void prefetchDescendantsOfVirtualNode(AccessibilityNodeInfo root,
1136 AccessibilityNodeProvider provider, List<AccessibilityNodeInfo> outInfos) {
Svetoslav Ganov42138042012-03-20 11:51:39 -07001137 final int initialOutInfosSize = outInfos.size();
Alan Viverettef0aed092013-11-06 15:33:03 -08001138 final int childCount = root.getChildCount();
Svetoslav Ganov42138042012-03-20 11:51:39 -07001139 for (int i = 0; i < childCount; i++) {
1140 if (outInfos.size() >= MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE) {
1141 return;
1142 }
Alan Viverettef0aed092013-11-06 15:33:03 -08001143 final long childNodeId = root.getChildId(i);
Svetoslav Ganov42138042012-03-20 11:51:39 -07001144 AccessibilityNodeInfo child = provider.createAccessibilityNodeInfo(
1145 AccessibilityNodeInfo.getVirtualDescendantId(childNodeId));
1146 if (child != null) {
1147 outInfos.add(child);
1148 }
1149 }
1150 if (outInfos.size() < MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE) {
1151 final int addedChildCount = outInfos.size() - initialOutInfosSize;
1152 for (int i = 0; i < addedChildCount; i++) {
1153 AccessibilityNodeInfo child = outInfos.get(initialOutInfosSize + i);
1154 prefetchDescendantsOfVirtualNode(child, provider, outInfos);
1155 }
1156 }
1157 }
1158 }
1159
1160 private class PrivateHandler extends Handler {
1161 private final static int MSG_PERFORM_ACCESSIBILITY_ACTION = 1;
Svet Ganov7498efd2014-09-03 21:33:00 -07001162 private final static int MSG_FIND_ACCESSIBILITY_NODE_INFO_BY_ACCESSIBILITY_ID = 2;
1163 private final static int MSG_FIND_ACCESSIBILITY_NODE_INFOS_BY_VIEW_ID = 3;
1164 private final static int MSG_FIND_ACCESSIBILITY_NODE_INFO_BY_TEXT = 4;
Svetoslav Ganov42138042012-03-20 11:51:39 -07001165 private final static int MSG_FIND_FOCUS = 5;
1166 private final static int MSG_FOCUS_SEARCH = 6;
1167
Svetoslav Ganov005b83b2012-04-16 18:17:17 -07001168 public PrivateHandler(Looper looper) {
1169 super(looper);
Svetoslav Ganov42138042012-03-20 11:51:39 -07001170 }
1171
1172 @Override
1173 public String getMessageName(Message message) {
1174 final int type = message.what;
1175 switch (type) {
1176 case MSG_PERFORM_ACCESSIBILITY_ACTION:
1177 return "MSG_PERFORM_ACCESSIBILITY_ACTION";
Svet Ganov7498efd2014-09-03 21:33:00 -07001178 case MSG_FIND_ACCESSIBILITY_NODE_INFO_BY_ACCESSIBILITY_ID:
1179 return "MSG_FIND_ACCESSIBILITY_NODE_INFO_BY_ACCESSIBILITY_ID";
1180 case MSG_FIND_ACCESSIBILITY_NODE_INFOS_BY_VIEW_ID:
1181 return "MSG_FIND_ACCESSIBILITY_NODE_INFOS_BY_VIEW_ID";
1182 case MSG_FIND_ACCESSIBILITY_NODE_INFO_BY_TEXT:
1183 return "MSG_FIND_ACCESSIBILITY_NODE_INFO_BY_TEXT";
Svetoslav Ganov42138042012-03-20 11:51:39 -07001184 case MSG_FIND_FOCUS:
1185 return "MSG_FIND_FOCUS";
1186 case MSG_FOCUS_SEARCH:
1187 return "MSG_FOCUS_SEARCH";
1188 default:
1189 throw new IllegalArgumentException("Unknown message type: " + type);
1190 }
1191 }
1192
1193 @Override
1194 public void handleMessage(Message message) {
1195 final int type = message.what;
1196 switch (type) {
Svet Ganov7498efd2014-09-03 21:33:00 -07001197 case MSG_FIND_ACCESSIBILITY_NODE_INFO_BY_ACCESSIBILITY_ID: {
Svetoslav Ganov42138042012-03-20 11:51:39 -07001198 findAccessibilityNodeInfoByAccessibilityIdUiThread(message);
1199 } break;
1200 case MSG_PERFORM_ACCESSIBILITY_ACTION: {
George Mount41725de2015-04-09 08:23:05 -07001201 performAccessibilityActionUiThread(message);
Svetoslav Ganov42138042012-03-20 11:51:39 -07001202 } break;
Svet Ganov7498efd2014-09-03 21:33:00 -07001203 case MSG_FIND_ACCESSIBILITY_NODE_INFOS_BY_VIEW_ID: {
Svetoslav Ganov80943d82013-01-02 10:25:37 -08001204 findAccessibilityNodeInfosByViewIdUiThread(message);
Svetoslav Ganov42138042012-03-20 11:51:39 -07001205 } break;
Svet Ganov7498efd2014-09-03 21:33:00 -07001206 case MSG_FIND_ACCESSIBILITY_NODE_INFO_BY_TEXT: {
Svetoslav Ganov42138042012-03-20 11:51:39 -07001207 findAccessibilityNodeInfosByTextUiThread(message);
1208 } break;
1209 case MSG_FIND_FOCUS: {
1210 findFocusUiThread(message);
1211 } break;
1212 case MSG_FOCUS_SEARCH: {
1213 focusSearchUiThread(message);
1214 } break;
1215 default:
1216 throw new IllegalArgumentException("Unknown message type: " + type);
1217 }
1218 }
1219 }
Svetoslav Ganov80943d82013-01-02 10:25:37 -08001220
1221 private final class AddNodeInfosForViewId implements Predicate<View> {
1222 private int mViewId = View.NO_ID;
1223 private List<AccessibilityNodeInfo> mInfos;
1224
1225 public void init(int viewId, List<AccessibilityNodeInfo> infos) {
1226 mViewId = viewId;
1227 mInfos = infos;
1228 }
1229
1230 public void reset() {
1231 mViewId = View.NO_ID;
1232 mInfos = null;
1233 }
1234
1235 @Override
1236 public boolean apply(View view) {
1237 if (view.getId() == mViewId && isShown(view)) {
1238 mInfos.add(view.createAccessibilityNodeInfo());
1239 }
1240 return false;
1241 }
1242 }
Svetoslav Ganov42138042012-03-20 11:51:39 -07001243}