blob: a283b91fb686d04333843c06774e4eb507e363f3 [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;
Svetoslav Ganovaa780c12012-04-19 23:01:39 -070022import android.os.Bundle;
Svetoslav Ganov42138042012-03-20 11:51:39 -070023import android.os.Handler;
24import android.os.Looper;
25import android.os.Message;
26import android.os.Process;
27import android.os.RemoteException;
Svetoslav8e3feb12014-02-24 13:46:47 -080028import android.util.LongSparseArray;
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -070029import android.view.View.AttachInfo;
Svetoslav Ganov42138042012-03-20 11:51:39 -070030import android.view.accessibility.AccessibilityInteractionClient;
31import android.view.accessibility.AccessibilityNodeInfo;
32import android.view.accessibility.AccessibilityNodeProvider;
33import android.view.accessibility.IAccessibilityInteractionConnectionCallback;
34
Svetoslav Ganov758143e2012-08-06 16:40:27 -070035import com.android.internal.os.SomeArgs;
Svetoslav Ganov80943d82013-01-02 10:25:37 -080036import com.android.internal.util.Predicate;
Svetoslav Ganov758143e2012-08-06 16:40:27 -070037
Svetoslav Ganov42138042012-03-20 11:51:39 -070038import java.util.ArrayList;
39import java.util.HashMap;
Svetoslav8e3feb12014-02-24 13:46:47 -080040import java.util.HashSet;
41import java.util.LinkedList;
Svetoslav Ganov42138042012-03-20 11:51:39 -070042import java.util.List;
43import java.util.Map;
Svetoslav8e3feb12014-02-24 13:46:47 -080044import java.util.Queue;
Svetoslav Ganov42138042012-03-20 11:51:39 -070045
46/**
47 * Class for managing accessibility interactions initiated from the system
48 * and targeting the view hierarchy. A *ClientThread method is to be
49 * called from the interaction connection ViewAncestor gives the system to
50 * talk to it and a corresponding *UiThread method that is executed on the
51 * UI thread.
52 */
53final class AccessibilityInteractionController {
Svetoslav Ganov42138042012-03-20 11:51:39 -070054
Svetoslavaaa11422014-03-28 13:31:13 -070055 private static final boolean ENFORCE_NODE_TREE_CONSISTENT = false;
Svetoslav8e3feb12014-02-24 13:46:47 -080056
Svetoslav Ganov80943d82013-01-02 10:25:37 -080057 private final ArrayList<AccessibilityNodeInfo> mTempAccessibilityNodeInfoList =
Svetoslav Ganov42138042012-03-20 11:51:39 -070058 new ArrayList<AccessibilityNodeInfo>();
59
Svetoslav Ganov005b83b2012-04-16 18:17:17 -070060 private final Handler mHandler;
Svetoslav Ganov42138042012-03-20 11:51:39 -070061
62 private final ViewRootImpl mViewRootImpl;
63
64 private final AccessibilityNodePrefetcher mPrefetcher;
65
Svetoslav Ganov749e7962012-04-19 17:13:46 -070066 private final long mMyLooperThreadId;
67
68 private final int mMyProcessId;
69
Svetoslav Ganov30ac6452012-06-01 09:10:25 -070070 private final ArrayList<View> mTempArrayList = new ArrayList<View>();
71
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -070072 private final Point mTempPoint = new Point();
Svetoslav Ganovfd8d9c42012-07-09 18:16:55 -070073 private final Rect mTempRect = new Rect();
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -070074 private final Rect mTempRect1 = new Rect();
75 private final Rect mTempRect2 = new Rect();
Svetoslav Ganovfd8d9c42012-07-09 18:16:55 -070076
Svetoslav Ganov80943d82013-01-02 10:25:37 -080077 private AddNodeInfosForViewId mAddNodeInfosForViewId;
78
Svetoslav Ganov42138042012-03-20 11:51:39 -070079 public AccessibilityInteractionController(ViewRootImpl viewRootImpl) {
Svetoslav Ganov749e7962012-04-19 17:13:46 -070080 Looper looper = viewRootImpl.mHandler.getLooper();
81 mMyLooperThreadId = looper.getThread().getId();
82 mMyProcessId = Process.myPid();
Svetoslav Ganov005b83b2012-04-16 18:17:17 -070083 mHandler = new PrivateHandler(looper);
Svetoslav Ganov42138042012-03-20 11:51:39 -070084 mViewRootImpl = viewRootImpl;
85 mPrefetcher = new AccessibilityNodePrefetcher();
86 }
87
Svetoslav Ganov0a1bb6d2012-05-07 11:54:39 -070088 private boolean isShown(View view) {
89 // The first two checks are made also made by isShown() which
90 // however traverses the tree up to the parent to catch that.
91 // Therefore, we do some fail fast check to minimize the up
92 // tree traversal.
93 return (view.mAttachInfo != null
94 && view.mAttachInfo.mWindowVisibility == View.VISIBLE
95 && view.isShown());
96 }
97
Svetoslav Ganov42138042012-03-20 11:51:39 -070098 public void findAccessibilityNodeInfoByAccessibilityIdClientThread(
Svetoslav9ae9ed22014-09-02 16:36:35 -070099 long accessibilityNodeId, Region interactiveRegion, int interactionId,
Svetoslav Ganov42138042012-03-20 11:51:39 -0700100 IAccessibilityInteractionConnectionCallback callback, int flags, int interrogatingPid,
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700101 long interrogatingTid, MagnificationSpec spec) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700102 Message message = mHandler.obtainMessage();
Svet Ganov7498efd2014-09-03 21:33:00 -0700103 message.what = PrivateHandler.MSG_FIND_ACCESSIBILITY_NODE_INFO_BY_ACCESSIBILITY_ID;
Svetoslav Ganov42138042012-03-20 11:51:39 -0700104 message.arg1 = flags;
Svetoslav Ganov86783472012-06-06 21:12:20 -0700105
Svetoslav Ganov758143e2012-08-06 16:40:27 -0700106 SomeArgs args = SomeArgs.obtain();
Svetoslav Ganov42138042012-03-20 11:51:39 -0700107 args.argi1 = AccessibilityNodeInfo.getAccessibilityViewId(accessibilityNodeId);
108 args.argi2 = AccessibilityNodeInfo.getVirtualDescendantId(accessibilityNodeId);
109 args.argi3 = interactionId;
110 args.arg1 = callback;
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700111 args.arg2 = spec;
Svetoslav9ae9ed22014-09-02 16:36:35 -0700112 args.arg3 = interactiveRegion;
Svetoslav Ganov42138042012-03-20 11:51:39 -0700113 message.obj = args;
Svetoslav Ganov86783472012-06-06 21:12:20 -0700114
Svetoslav Ganov42138042012-03-20 11:51:39 -0700115 // If the interrogation is performed by the same thread as the main UI
116 // thread in this process, set the message as a static reference so
117 // after this call completes the same thread but in the interrogating
118 // client can handle the message to generate the result.
Svetoslav Ganov749e7962012-04-19 17:13:46 -0700119 if (interrogatingPid == mMyProcessId && interrogatingTid == mMyLooperThreadId) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700120 AccessibilityInteractionClient.getInstanceForThread(
121 interrogatingTid).setSameThreadMessage(message);
122 } else {
123 mHandler.sendMessage(message);
124 }
125 }
126
127 private void findAccessibilityNodeInfoByAccessibilityIdUiThread(Message message) {
128 final int flags = message.arg1;
Svetoslav Ganov86783472012-06-06 21:12:20 -0700129
Svetoslav Ganov42138042012-03-20 11:51:39 -0700130 SomeArgs args = (SomeArgs) message.obj;
131 final int accessibilityViewId = args.argi1;
132 final int virtualDescendantId = args.argi2;
133 final int interactionId = args.argi3;
134 final IAccessibilityInteractionConnectionCallback callback =
135 (IAccessibilityInteractionConnectionCallback) args.arg1;
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700136 final MagnificationSpec spec = (MagnificationSpec) args.arg2;
Svetoslav9ae9ed22014-09-02 16:36:35 -0700137 final Region interactiveRegion = (Region) args.arg3;
Svetoslav Ganov86783472012-06-06 21:12:20 -0700138
Svetoslav Ganov758143e2012-08-06 16:40:27 -0700139 args.recycle();
Svetoslav Ganov86783472012-06-06 21:12:20 -0700140
Svetoslav Ganov42138042012-03-20 11:51:39 -0700141 List<AccessibilityNodeInfo> infos = mTempAccessibilityNodeInfoList;
142 infos.clear();
143 try {
144 if (mViewRootImpl.mView == null || mViewRootImpl.mAttachInfo == null) {
145 return;
146 }
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800147 mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = flags;
Svetoslav Ganov42138042012-03-20 11:51:39 -0700148 View root = null;
Svetoslav8e3feb12014-02-24 13:46:47 -0800149 if (accessibilityViewId == AccessibilityNodeInfo.UNDEFINED_ITEM_ID) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700150 root = mViewRootImpl.mView;
151 } else {
152 root = findViewByAccessibilityId(accessibilityViewId);
153 }
Svetoslav Ganov0a1bb6d2012-05-07 11:54:39 -0700154 if (root != null && isShown(root)) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700155 mPrefetcher.prefetchAccessibilityNodeInfos(root, virtualDescendantId, flags, infos);
156 }
157 } finally {
158 try {
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800159 mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = 0;
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700160 applyAppScaleAndMagnificationSpecIfNeeded(infos, spec);
161 if (spec != null) {
162 spec.recycle();
163 }
Svetoslav9ae9ed22014-09-02 16:36:35 -0700164 adjustIsVisibleToUserIfNeeded(infos, interactiveRegion);
Svetoslav Ganov42138042012-03-20 11:51:39 -0700165 callback.setFindAccessibilityNodeInfosResult(infos, interactionId);
166 infos.clear();
167 } catch (RemoteException re) {
168 /* ignore - the other side will time out */
169 }
170 }
171 }
172
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800173 public void findAccessibilityNodeInfosByViewIdClientThread(long accessibilityNodeId,
Svetoslav9ae9ed22014-09-02 16:36:35 -0700174 String viewId, Region interactiveRegion, int interactionId,
175 IAccessibilityInteractionConnectionCallback callback, int flags, int interrogatingPid,
176 long interrogatingTid, MagnificationSpec spec) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700177 Message message = mHandler.obtainMessage();
Svet Ganov7498efd2014-09-03 21:33:00 -0700178 message.what = PrivateHandler.MSG_FIND_ACCESSIBILITY_NODE_INFOS_BY_VIEW_ID;
Svetoslav Ganov42138042012-03-20 11:51:39 -0700179 message.arg1 = flags;
180 message.arg2 = AccessibilityNodeInfo.getAccessibilityViewId(accessibilityNodeId);
Svetoslav Ganov86783472012-06-06 21:12:20 -0700181
Svetoslav Ganov758143e2012-08-06 16:40:27 -0700182 SomeArgs args = SomeArgs.obtain();
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800183 args.argi1 = interactionId;
Svetoslav Ganov42138042012-03-20 11:51:39 -0700184 args.arg1 = callback;
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700185 args.arg2 = spec;
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800186 args.arg3 = viewId;
Svetoslav9ae9ed22014-09-02 16:36:35 -0700187 args.arg4 = interactiveRegion;
Svetoslav Ganov86783472012-06-06 21:12:20 -0700188
Svetoslav Ganov42138042012-03-20 11:51:39 -0700189 message.obj = args;
Svetoslav Ganov86783472012-06-06 21:12:20 -0700190
Svetoslav Ganov42138042012-03-20 11:51:39 -0700191 // If the interrogation is performed by the same thread as the main UI
192 // thread in this process, set the message as a static reference so
193 // after this call completes the same thread but in the interrogating
194 // client can handle the message to generate the result.
Svetoslav Ganov749e7962012-04-19 17:13:46 -0700195 if (interrogatingPid == mMyProcessId && interrogatingTid == mMyLooperThreadId) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700196 AccessibilityInteractionClient.getInstanceForThread(
197 interrogatingTid).setSameThreadMessage(message);
198 } else {
199 mHandler.sendMessage(message);
200 }
201 }
202
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800203 private void findAccessibilityNodeInfosByViewIdUiThread(Message message) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700204 final int flags = message.arg1;
205 final int accessibilityViewId = message.arg2;
Svetoslav Ganov86783472012-06-06 21:12:20 -0700206
Svetoslav Ganov42138042012-03-20 11:51:39 -0700207 SomeArgs args = (SomeArgs) message.obj;
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800208 final int interactionId = args.argi1;
Svetoslav Ganov42138042012-03-20 11:51:39 -0700209 final IAccessibilityInteractionConnectionCallback callback =
210 (IAccessibilityInteractionConnectionCallback) args.arg1;
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700211 final MagnificationSpec spec = (MagnificationSpec) args.arg2;
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800212 final String viewId = (String) args.arg3;
Svetoslav9ae9ed22014-09-02 16:36:35 -0700213 final Region interactiveRegion = (Region) args.arg4;
Svetoslav Ganov86783472012-06-06 21:12:20 -0700214
Svetoslav Ganov758143e2012-08-06 16:40:27 -0700215 args.recycle();
Svetoslav Ganov86783472012-06-06 21:12:20 -0700216
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800217 final List<AccessibilityNodeInfo> infos = mTempAccessibilityNodeInfoList;
218 infos.clear();
Svetoslav Ganov42138042012-03-20 11:51:39 -0700219 try {
220 if (mViewRootImpl.mView == null || mViewRootImpl.mAttachInfo == null) {
221 return;
222 }
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800223 mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = flags;
Svetoslav Ganov42138042012-03-20 11:51:39 -0700224 View root = null;
Svetoslav8e3feb12014-02-24 13:46:47 -0800225 if (accessibilityViewId != AccessibilityNodeInfo.UNDEFINED_ITEM_ID) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700226 root = findViewByAccessibilityId(accessibilityViewId);
227 } else {
228 root = mViewRootImpl.mView;
229 }
230 if (root != null) {
Svetoslava33243e2013-02-11 15:43:46 -0800231 final int resolvedViewId = root.getContext().getResources()
232 .getIdentifier(viewId, null, null);
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800233 if (resolvedViewId <= 0) {
234 return;
235 }
236 if (mAddNodeInfosForViewId == null) {
237 mAddNodeInfosForViewId = new AddNodeInfosForViewId();
238 }
239 mAddNodeInfosForViewId.init(resolvedViewId, infos);
240 root.findViewByPredicate(mAddNodeInfosForViewId);
241 mAddNodeInfosForViewId.reset();
Svetoslav Ganov42138042012-03-20 11:51:39 -0700242 }
243 } finally {
244 try {
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800245 mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = 0;
246 applyAppScaleAndMagnificationSpecIfNeeded(infos, spec);
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700247 if (spec != null) {
248 spec.recycle();
249 }
Svetoslav9ae9ed22014-09-02 16:36:35 -0700250 adjustIsVisibleToUserIfNeeded(infos, interactiveRegion);
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800251 callback.setFindAccessibilityNodeInfosResult(infos, interactionId);
Svetoslav Ganov42138042012-03-20 11:51:39 -0700252 } catch (RemoteException re) {
253 /* ignore - the other side will time out */
254 }
255 }
256 }
257
258 public void findAccessibilityNodeInfosByTextClientThread(long accessibilityNodeId,
Svetoslav9ae9ed22014-09-02 16:36:35 -0700259 String text, Region interactiveRegion, int interactionId,
260 IAccessibilityInteractionConnectionCallback callback, int flags, int interrogatingPid,
261 long interrogatingTid, MagnificationSpec spec) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700262 Message message = mHandler.obtainMessage();
Svet Ganov7498efd2014-09-03 21:33:00 -0700263 message.what = PrivateHandler.MSG_FIND_ACCESSIBILITY_NODE_INFO_BY_TEXT;
Svetoslav Ganov42138042012-03-20 11:51:39 -0700264 message.arg1 = flags;
Svetoslav Ganov86783472012-06-06 21:12:20 -0700265
Svetoslav Ganov758143e2012-08-06 16:40:27 -0700266 SomeArgs args = SomeArgs.obtain();
Svetoslav Ganov42138042012-03-20 11:51:39 -0700267 args.arg1 = text;
Svetoslav Ganovc9c9a482012-07-16 08:46:07 -0700268 args.arg2 = callback;
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700269 args.arg3 = spec;
Svetoslav Ganov42138042012-03-20 11:51:39 -0700270 args.argi1 = AccessibilityNodeInfo.getAccessibilityViewId(accessibilityNodeId);
271 args.argi2 = AccessibilityNodeInfo.getVirtualDescendantId(accessibilityNodeId);
272 args.argi3 = interactionId;
Svetoslav9ae9ed22014-09-02 16:36:35 -0700273 args.arg4 = interactiveRegion;
Svetoslav Ganov42138042012-03-20 11:51:39 -0700274 message.obj = args;
Svetoslav Ganov86783472012-06-06 21:12:20 -0700275
Svetoslav Ganov42138042012-03-20 11:51:39 -0700276 // If the interrogation is performed by the same thread as the main UI
277 // thread in this process, set the message as a static reference so
278 // after this call completes the same thread but in the interrogating
279 // client can handle the message to generate the result.
Svetoslav Ganov749e7962012-04-19 17:13:46 -0700280 if (interrogatingPid == mMyProcessId && interrogatingTid == mMyLooperThreadId) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700281 AccessibilityInteractionClient.getInstanceForThread(
282 interrogatingTid).setSameThreadMessage(message);
283 } else {
284 mHandler.sendMessage(message);
285 }
286 }
287
288 private void findAccessibilityNodeInfosByTextUiThread(Message message) {
289 final int flags = message.arg1;
Svetoslav Ganov86783472012-06-06 21:12:20 -0700290
Svetoslav Ganov42138042012-03-20 11:51:39 -0700291 SomeArgs args = (SomeArgs) message.obj;
292 final String text = (String) args.arg1;
Svetoslav Ganovc9c9a482012-07-16 08:46:07 -0700293 final IAccessibilityInteractionConnectionCallback callback =
294 (IAccessibilityInteractionConnectionCallback) args.arg2;
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700295 final MagnificationSpec spec = (MagnificationSpec) args.arg3;
Svetoslav Ganov42138042012-03-20 11:51:39 -0700296 final int accessibilityViewId = args.argi1;
297 final int virtualDescendantId = args.argi2;
298 final int interactionId = args.argi3;
Svetoslav9ae9ed22014-09-02 16:36:35 -0700299 final Region interactiveRegion = (Region) args.arg4;
Svetoslav Ganov758143e2012-08-06 16:40:27 -0700300 args.recycle();
Svetoslav Ganov86783472012-06-06 21:12:20 -0700301
Svetoslav Ganov42138042012-03-20 11:51:39 -0700302 List<AccessibilityNodeInfo> infos = null;
303 try {
304 if (mViewRootImpl.mView == null || mViewRootImpl.mAttachInfo == null) {
305 return;
306 }
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800307 mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = flags;
Svetoslav Ganov42138042012-03-20 11:51:39 -0700308 View root = null;
Svetoslav8e3feb12014-02-24 13:46:47 -0800309 if (accessibilityViewId != AccessibilityNodeInfo.UNDEFINED_ITEM_ID) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700310 root = findViewByAccessibilityId(accessibilityViewId);
311 } else {
312 root = mViewRootImpl.mView;
313 }
Svetoslav Ganov0a1bb6d2012-05-07 11:54:39 -0700314 if (root != null && isShown(root)) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700315 AccessibilityNodeProvider provider = root.getAccessibilityNodeProvider();
316 if (provider != null) {
Svetoslav8e3feb12014-02-24 13:46:47 -0800317 if (virtualDescendantId != AccessibilityNodeInfo.UNDEFINED_ITEM_ID) {
318 infos = provider.findAccessibilityNodeInfosByText(text,
319 virtualDescendantId);
320 } else {
321 infos = provider.findAccessibilityNodeInfosByText(text,
322 AccessibilityNodeProvider.HOST_VIEW_ID);
323 }
324 } else if (virtualDescendantId == AccessibilityNodeInfo.UNDEFINED_ITEM_ID) {
Svetoslav Ganov30ac6452012-06-01 09:10:25 -0700325 ArrayList<View> foundViews = mTempArrayList;
Svetoslav Ganov42138042012-03-20 11:51:39 -0700326 foundViews.clear();
327 root.findViewsWithText(foundViews, text, View.FIND_VIEWS_WITH_TEXT
328 | View.FIND_VIEWS_WITH_CONTENT_DESCRIPTION
329 | View.FIND_VIEWS_WITH_ACCESSIBILITY_NODE_PROVIDERS);
330 if (!foundViews.isEmpty()) {
331 infos = mTempAccessibilityNodeInfoList;
332 infos.clear();
333 final int viewCount = foundViews.size();
334 for (int i = 0; i < viewCount; i++) {
335 View foundView = foundViews.get(i);
Svetoslav Ganov0a1bb6d2012-05-07 11:54:39 -0700336 if (isShown(foundView)) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700337 provider = foundView.getAccessibilityNodeProvider();
338 if (provider != null) {
339 List<AccessibilityNodeInfo> infosFromProvider =
340 provider.findAccessibilityNodeInfosByText(text,
Svetoslav8e3feb12014-02-24 13:46:47 -0800341 AccessibilityNodeProvider.HOST_VIEW_ID);
Svetoslav Ganov42138042012-03-20 11:51:39 -0700342 if (infosFromProvider != null) {
343 infos.addAll(infosFromProvider);
344 }
345 } else {
346 infos.add(foundView.createAccessibilityNodeInfo());
347 }
348 }
349 }
350 }
351 }
352 }
353 } finally {
354 try {
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800355 mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = 0;
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700356 applyAppScaleAndMagnificationSpecIfNeeded(infos, spec);
357 if (spec != null) {
358 spec.recycle();
359 }
Svetoslav9ae9ed22014-09-02 16:36:35 -0700360 adjustIsVisibleToUserIfNeeded(infos, interactiveRegion);
Svetoslav Ganov42138042012-03-20 11:51:39 -0700361 callback.setFindAccessibilityNodeInfosResult(infos, interactionId);
362 } catch (RemoteException re) {
363 /* ignore - the other side will time out */
364 }
365 }
366 }
367
Svetoslav9ae9ed22014-09-02 16:36:35 -0700368 public void findFocusClientThread(long accessibilityNodeId, int focusType,
369 Region interactiveRegion, int interactionId,
Svetoslav Ganovc9c9a482012-07-16 08:46:07 -0700370 IAccessibilityInteractionConnectionCallback callback, int flags, int interogatingPid,
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700371 long interrogatingTid, MagnificationSpec spec) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700372 Message message = mHandler.obtainMessage();
373 message.what = PrivateHandler.MSG_FIND_FOCUS;
374 message.arg1 = flags;
375 message.arg2 = focusType;
Svetoslav Ganov86783472012-06-06 21:12:20 -0700376
Svetoslav Ganov758143e2012-08-06 16:40:27 -0700377 SomeArgs args = SomeArgs.obtain();
Svetoslav Ganov42138042012-03-20 11:51:39 -0700378 args.argi1 = interactionId;
379 args.argi2 = AccessibilityNodeInfo.getAccessibilityViewId(accessibilityNodeId);
380 args.argi3 = AccessibilityNodeInfo.getVirtualDescendantId(accessibilityNodeId);
381 args.arg1 = callback;
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700382 args.arg2 = spec;
Svetoslav9ae9ed22014-09-02 16:36:35 -0700383 args.arg3 = interactiveRegion;
Svetoslav Ganov86783472012-06-06 21:12:20 -0700384
Svetoslav Ganov42138042012-03-20 11:51:39 -0700385 message.obj = args;
Svetoslav Ganov86783472012-06-06 21:12:20 -0700386
Svetoslav Ganov42138042012-03-20 11:51:39 -0700387 // If the interrogation is performed by the same thread as the main UI
388 // thread in this process, set the message as a static reference so
389 // after this call completes the same thread but in the interrogating
390 // client can handle the message to generate the result.
Svetoslav Ganov749e7962012-04-19 17:13:46 -0700391 if (interogatingPid == mMyProcessId && interrogatingTid == mMyLooperThreadId) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700392 AccessibilityInteractionClient.getInstanceForThread(
393 interrogatingTid).setSameThreadMessage(message);
394 } else {
395 mHandler.sendMessage(message);
396 }
397 }
398
399 private void findFocusUiThread(Message message) {
400 final int flags = message.arg1;
401 final int focusType = message.arg2;
Svetoslav Ganov86783472012-06-06 21:12:20 -0700402
Svetoslav Ganov42138042012-03-20 11:51:39 -0700403 SomeArgs args = (SomeArgs) message.obj;
404 final int interactionId = args.argi1;
405 final int accessibilityViewId = args.argi2;
406 final int virtualDescendantId = args.argi3;
407 final IAccessibilityInteractionConnectionCallback callback =
408 (IAccessibilityInteractionConnectionCallback) args.arg1;
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700409 final MagnificationSpec spec = (MagnificationSpec) args.arg2;
Svetoslav9ae9ed22014-09-02 16:36:35 -0700410 final Region interactiveRegion = (Region) args.arg3;
Svetoslav Ganov758143e2012-08-06 16:40:27 -0700411 args.recycle();
Svetoslav Ganov86783472012-06-06 21:12:20 -0700412
Svetoslav Ganov42138042012-03-20 11:51:39 -0700413 AccessibilityNodeInfo focused = null;
414 try {
415 if (mViewRootImpl.mView == null || mViewRootImpl.mAttachInfo == null) {
416 return;
417 }
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800418 mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = flags;
Svetoslav Ganov42138042012-03-20 11:51:39 -0700419 View root = null;
Svetoslav8e3feb12014-02-24 13:46:47 -0800420 if (accessibilityViewId != AccessibilityNodeInfo.UNDEFINED_ITEM_ID) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700421 root = findViewByAccessibilityId(accessibilityViewId);
422 } else {
423 root = mViewRootImpl.mView;
424 }
Svetoslav Ganov0a1bb6d2012-05-07 11:54:39 -0700425 if (root != null && isShown(root)) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700426 switch (focusType) {
427 case AccessibilityNodeInfo.FOCUS_ACCESSIBILITY: {
428 View host = mViewRootImpl.mAccessibilityFocusedHost;
429 // If there is no accessibility focus host or it is not a descendant
430 // of the root from which to start the search, then the search failed.
431 if (host == null || !ViewRootImpl.isViewDescendantOf(host, root)) {
432 break;
433 }
Svetoslav6254f482013-06-04 17:22:14 -0700434 // The focused view not shown, we failed.
435 if (!isShown(host)) {
436 break;
437 }
Svetoslav Ganov42138042012-03-20 11:51:39 -0700438 // If the host has a provider ask this provider to search for the
439 // focus instead fetching all provider nodes to do the search here.
440 AccessibilityNodeProvider provider = host.getAccessibilityNodeProvider();
441 if (provider != null) {
Svetoslav Ganov45a02e02012-06-17 15:07:29 -0700442 if (mViewRootImpl.mAccessibilityFocusedVirtualView != null) {
443 focused = AccessibilityNodeInfo.obtain(
444 mViewRootImpl.mAccessibilityFocusedVirtualView);
445 }
Svetoslav8e3feb12014-02-24 13:46:47 -0800446 } else if (virtualDescendantId == AccessibilityNodeInfo.UNDEFINED_ITEM_ID) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700447 focused = host.createAccessibilityNodeInfo();
448 }
449 } break;
450 case AccessibilityNodeInfo.FOCUS_INPUT: {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700451 View target = root.findFocus();
Alan Viverette2e1e0812013-09-30 13:45:55 -0700452 if (target == null || !isShown(target)) {
453 break;
454 }
455 AccessibilityNodeProvider provider = target.getAccessibilityNodeProvider();
456 if (provider != null) {
457 focused = provider.findFocus(focusType);
458 }
459 if (focused == null) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700460 focused = target.createAccessibilityNodeInfo();
461 }
462 } break;
463 default:
464 throw new IllegalArgumentException("Unknown focus type: " + focusType);
465 }
466 }
467 } finally {
468 try {
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800469 mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = 0;
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700470 applyAppScaleAndMagnificationSpecIfNeeded(focused, spec);
471 if (spec != null) {
472 spec.recycle();
473 }
Svetoslav9ae9ed22014-09-02 16:36:35 -0700474 adjustIsVisibleToUserIfNeeded(focused, interactiveRegion);
Svetoslav Ganov42138042012-03-20 11:51:39 -0700475 callback.setFindAccessibilityNodeInfoResult(focused, interactionId);
476 } catch (RemoteException re) {
477 /* ignore - the other side will time out */
478 }
479 }
480 }
481
Svetoslav9ae9ed22014-09-02 16:36:35 -0700482 public void focusSearchClientThread(long accessibilityNodeId, int direction,
483 Region interactiveRegion, int interactionId,
Svetoslav Ganovc9c9a482012-07-16 08:46:07 -0700484 IAccessibilityInteractionConnectionCallback callback, int flags, int interogatingPid,
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700485 long interrogatingTid, MagnificationSpec spec) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700486 Message message = mHandler.obtainMessage();
487 message.what = PrivateHandler.MSG_FOCUS_SEARCH;
488 message.arg1 = flags;
489 message.arg2 = AccessibilityNodeInfo.getAccessibilityViewId(accessibilityNodeId);
Svetoslav Ganov86783472012-06-06 21:12:20 -0700490
Svetoslav Ganov758143e2012-08-06 16:40:27 -0700491 SomeArgs args = SomeArgs.obtain();
Svetoslav Ganov42138042012-03-20 11:51:39 -0700492 args.argi2 = direction;
493 args.argi3 = interactionId;
494 args.arg1 = callback;
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700495 args.arg2 = spec;
Svetoslav9ae9ed22014-09-02 16:36:35 -0700496 args.arg3 = interactiveRegion;
Svetoslav Ganov86783472012-06-06 21:12:20 -0700497
Svetoslav Ganov42138042012-03-20 11:51:39 -0700498 message.obj = args;
Svetoslav Ganov86783472012-06-06 21:12:20 -0700499
Svetoslav Ganov42138042012-03-20 11:51:39 -0700500 // If the interrogation is performed by the same thread as the main UI
501 // thread in this process, set the message as a static reference so
502 // after this call completes the same thread but in the interrogating
503 // client can handle the message to generate the result.
Svetoslav Ganov749e7962012-04-19 17:13:46 -0700504 if (interogatingPid == mMyProcessId && interrogatingTid == mMyLooperThreadId) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700505 AccessibilityInteractionClient.getInstanceForThread(
506 interrogatingTid).setSameThreadMessage(message);
507 } else {
508 mHandler.sendMessage(message);
509 }
510 }
511
512 private void focusSearchUiThread(Message message) {
513 final int flags = message.arg1;
514 final int accessibilityViewId = message.arg2;
Svetoslav Ganov86783472012-06-06 21:12:20 -0700515
Svetoslav Ganov42138042012-03-20 11:51:39 -0700516 SomeArgs args = (SomeArgs) message.obj;
Svetoslav Ganov42138042012-03-20 11:51:39 -0700517 final int direction = args.argi2;
518 final int interactionId = args.argi3;
519 final IAccessibilityInteractionConnectionCallback callback =
520 (IAccessibilityInteractionConnectionCallback) args.arg1;
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700521 final MagnificationSpec spec = (MagnificationSpec) args.arg2;
Svetoslav9ae9ed22014-09-02 16:36:35 -0700522 final Region interactiveRegion = (Region) args.arg3;
Svetoslav Ganov86783472012-06-06 21:12:20 -0700523
Svetoslav Ganov758143e2012-08-06 16:40:27 -0700524 args.recycle();
Svetoslav Ganov86783472012-06-06 21:12:20 -0700525
Svetoslav Ganov42138042012-03-20 11:51:39 -0700526 AccessibilityNodeInfo next = null;
527 try {
528 if (mViewRootImpl.mView == null || mViewRootImpl.mAttachInfo == null) {
529 return;
530 }
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800531 mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = flags;
Svetoslav Ganov42138042012-03-20 11:51:39 -0700532 View root = null;
Svetoslav8e3feb12014-02-24 13:46:47 -0800533 if (accessibilityViewId != AccessibilityNodeInfo.UNDEFINED_ITEM_ID) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700534 root = findViewByAccessibilityId(accessibilityViewId);
535 } else {
536 root = mViewRootImpl.mView;
537 }
Svetoslav Ganov0a1bb6d2012-05-07 11:54:39 -0700538 if (root != null && isShown(root)) {
Svetoslav Ganov27e2da72012-07-02 18:12:00 -0700539 View nextView = root.focusSearch(direction);
540 if (nextView != null) {
541 next = nextView.createAccessibilityNodeInfo();
Svetoslav Ganov42138042012-03-20 11:51:39 -0700542 }
543 }
544 } finally {
545 try {
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800546 mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = 0;
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700547 applyAppScaleAndMagnificationSpecIfNeeded(next, spec);
548 if (spec != null) {
549 spec.recycle();
550 }
Svetoslav9ae9ed22014-09-02 16:36:35 -0700551 adjustIsVisibleToUserIfNeeded(next, interactiveRegion);
Svetoslav Ganov42138042012-03-20 11:51:39 -0700552 callback.setFindAccessibilityNodeInfoResult(next, interactionId);
553 } catch (RemoteException re) {
554 /* ignore - the other side will time out */
555 }
556 }
557 }
558
559 public void performAccessibilityActionClientThread(long accessibilityNodeId, int action,
Svetoslav Ganovaa780c12012-04-19 23:01:39 -0700560 Bundle arguments, int interactionId,
561 IAccessibilityInteractionConnectionCallback callback, int flags, int interogatingPid,
562 long interrogatingTid) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700563 Message message = mHandler.obtainMessage();
564 message.what = PrivateHandler.MSG_PERFORM_ACCESSIBILITY_ACTION;
565 message.arg1 = flags;
566 message.arg2 = AccessibilityNodeInfo.getAccessibilityViewId(accessibilityNodeId);
Svetoslav Ganov86783472012-06-06 21:12:20 -0700567
Svetoslav Ganov758143e2012-08-06 16:40:27 -0700568 SomeArgs args = SomeArgs.obtain();
Svetoslav Ganov42138042012-03-20 11:51:39 -0700569 args.argi1 = AccessibilityNodeInfo.getVirtualDescendantId(accessibilityNodeId);
570 args.argi2 = action;
571 args.argi3 = interactionId;
572 args.arg1 = callback;
Svetoslav Ganovaa780c12012-04-19 23:01:39 -0700573 args.arg2 = arguments;
Svetoslav Ganov86783472012-06-06 21:12:20 -0700574
Svetoslav Ganov42138042012-03-20 11:51:39 -0700575 message.obj = args;
Svetoslav Ganov86783472012-06-06 21:12:20 -0700576
Svetoslav Ganov42138042012-03-20 11:51:39 -0700577 // If the interrogation is performed by the same thread as the main UI
578 // thread in this process, set the message as a static reference so
579 // after this call completes the same thread but in the interrogating
580 // client can handle the message to generate the result.
Svetoslav Ganov749e7962012-04-19 17:13:46 -0700581 if (interogatingPid == mMyProcessId && interrogatingTid == mMyLooperThreadId) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700582 AccessibilityInteractionClient.getInstanceForThread(
583 interrogatingTid).setSameThreadMessage(message);
584 } else {
585 mHandler.sendMessage(message);
586 }
587 }
588
589 private void perfromAccessibilityActionUiThread(Message message) {
590 final int flags = message.arg1;
591 final int accessibilityViewId = message.arg2;
Svetoslav Ganov86783472012-06-06 21:12:20 -0700592
Svetoslav Ganov42138042012-03-20 11:51:39 -0700593 SomeArgs args = (SomeArgs) message.obj;
594 final int virtualDescendantId = args.argi1;
595 final int action = args.argi2;
596 final int interactionId = args.argi3;
597 final IAccessibilityInteractionConnectionCallback callback =
598 (IAccessibilityInteractionConnectionCallback) args.arg1;
Svetoslav Ganovaa780c12012-04-19 23:01:39 -0700599 Bundle arguments = (Bundle) args.arg2;
Svetoslav Ganov86783472012-06-06 21:12:20 -0700600
Svetoslav Ganov758143e2012-08-06 16:40:27 -0700601 args.recycle();
Svetoslav Ganov86783472012-06-06 21:12:20 -0700602
Svetoslav Ganov42138042012-03-20 11:51:39 -0700603 boolean succeeded = false;
604 try {
605 if (mViewRootImpl.mView == null || mViewRootImpl.mAttachInfo == null) {
606 return;
607 }
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800608 mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = flags;
Svetoslav Ganov42138042012-03-20 11:51:39 -0700609 View target = null;
Svetoslav8e3feb12014-02-24 13:46:47 -0800610 if (accessibilityViewId != AccessibilityNodeInfo.UNDEFINED_ITEM_ID) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700611 target = findViewByAccessibilityId(accessibilityViewId);
612 } else {
613 target = mViewRootImpl.mView;
614 }
Svetoslav Ganov0a1bb6d2012-05-07 11:54:39 -0700615 if (target != null && isShown(target)) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700616 AccessibilityNodeProvider provider = target.getAccessibilityNodeProvider();
617 if (provider != null) {
Svetoslav8e3feb12014-02-24 13:46:47 -0800618 if (virtualDescendantId != AccessibilityNodeInfo.UNDEFINED_ITEM_ID) {
619 succeeded = provider.performAction(virtualDescendantId, action,
620 arguments);
621 } else {
622 succeeded = provider.performAction(AccessibilityNodeProvider.HOST_VIEW_ID,
623 action, arguments);
624 }
625 } else if (virtualDescendantId == AccessibilityNodeInfo.UNDEFINED_ITEM_ID) {
Svetoslav Ganovaa780c12012-04-19 23:01:39 -0700626 succeeded = target.performAccessibilityAction(action, arguments);
Svetoslav Ganov42138042012-03-20 11:51:39 -0700627 }
628 }
629 } finally {
630 try {
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800631 mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = 0;
Svetoslav Ganov42138042012-03-20 11:51:39 -0700632 callback.setPerformAccessibilityActionResult(succeeded, interactionId);
633 } catch (RemoteException re) {
634 /* ignore - the other side will time out */
635 }
636 }
637 }
638
Svet Ganov7498efd2014-09-03 21:33:00 -0700639 public void computeClickPointInScreenClientThread(long accessibilityNodeId,
640 Region interactiveRegion, int interactionId,
641 IAccessibilityInteractionConnectionCallback callback, int interrogatingPid,
642 long interrogatingTid, MagnificationSpec spec) {
643 Message message = mHandler.obtainMessage();
644 message.what = PrivateHandler.MSG_COMPUTE_CLICK_POINT_IN_SCREEN;
645
646 SomeArgs args = SomeArgs.obtain();
647 args.argi1 = AccessibilityNodeInfo.getAccessibilityViewId(accessibilityNodeId);
648 args.argi2 = AccessibilityNodeInfo.getVirtualDescendantId(accessibilityNodeId);
649 args.argi3 = interactionId;
650 args.arg1 = callback;
651 args.arg2 = spec;
652 args.arg3 = interactiveRegion;
653
654 message.obj = args;
655
656 // If the interrogation is performed by the same thread as the main UI
657 // thread in this process, set the message as a static reference so
658 // after this call completes the same thread but in the interrogating
659 // client can handle the message to generate the result.
660 if (interrogatingPid == mMyProcessId && interrogatingTid == mMyLooperThreadId) {
661 AccessibilityInteractionClient.getInstanceForThread(
662 interrogatingTid).setSameThreadMessage(message);
663 } else {
664 mHandler.sendMessage(message);
665 }
666 }
667
668 private void computeClickPointInScreenUiThread(Message message) {
669 SomeArgs args = (SomeArgs) message.obj;
670 final int accessibilityViewId = args.argi1;
671 final int virtualDescendantId = args.argi2;
672 final int interactionId = args.argi3;
673 final IAccessibilityInteractionConnectionCallback callback =
674 (IAccessibilityInteractionConnectionCallback) args.arg1;
675 final MagnificationSpec spec = (MagnificationSpec) args.arg2;
676 final Region interactiveRegion = (Region) args.arg3;
677 args.recycle();
678
679 boolean succeeded = false;
680 Point point = mTempPoint;
681 try {
682 if (mViewRootImpl.mView == null || mViewRootImpl.mAttachInfo == null) {
683 return;
684 }
685 View target = null;
686 if (accessibilityViewId != AccessibilityNodeInfo.UNDEFINED_ITEM_ID) {
687 target = findViewByAccessibilityId(accessibilityViewId);
688 } else {
689 target = mViewRootImpl.mView;
690 }
691 if (target != null && isShown(target)) {
692 AccessibilityNodeProvider provider = target.getAccessibilityNodeProvider();
693 if (provider != null) {
694 // For virtual views just use the center of the bounds in screen.
695 AccessibilityNodeInfo node = null;
696 if (virtualDescendantId != AccessibilityNodeInfo.UNDEFINED_ITEM_ID) {
697 node = provider.createAccessibilityNodeInfo(virtualDescendantId);
698 } else {
699 node = provider.createAccessibilityNodeInfo(
700 AccessibilityNodeProvider.HOST_VIEW_ID);
701 }
702 if (node != null) {
703 succeeded = true;
704 Rect boundsInScreen = mTempRect;
705 node.getBoundsInScreen(boundsInScreen);
706 point.set(boundsInScreen.centerX(), boundsInScreen.centerY());
707 }
708 } else if (virtualDescendantId == AccessibilityNodeInfo.UNDEFINED_ITEM_ID) {
709 // For a real view, ask the view to compute the click point.
710 succeeded = target.computeClickPointInScreenForAccessibility(
711 interactiveRegion, point);
712 }
713 }
714 } finally {
715 try {
716 Point result = null;
717 if (succeeded) {
718 applyAppScaleAndMagnificationSpecIfNeeded(point, spec);
719 result = point;
720 }
721 callback.setComputeClickPointInScreenActionResult(result, interactionId);
722 } catch (RemoteException re) {
723 /* ignore - the other side will time out */
724 }
725 }
726 }
727
Svetoslav Ganov42138042012-03-20 11:51:39 -0700728 private View findViewByAccessibilityId(int accessibilityId) {
729 View root = mViewRootImpl.mView;
730 if (root == null) {
731 return null;
732 }
733 View foundView = root.findViewByAccessibilityId(accessibilityId);
Svetoslav Ganov0a1bb6d2012-05-07 11:54:39 -0700734 if (foundView != null && !isShown(foundView)) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700735 return null;
736 }
737 return foundView;
738 }
739
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700740 private void applyAppScaleAndMagnificationSpecIfNeeded(List<AccessibilityNodeInfo> infos,
741 MagnificationSpec spec) {
Svetoslav Ganovfd8d9c42012-07-09 18:16:55 -0700742 if (infos == null) {
743 return;
744 }
745 final float applicationScale = mViewRootImpl.mAttachInfo.mApplicationScale;
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700746 if (shouldApplyAppScaleAndMagnificationSpec(applicationScale, spec)) {
Svetoslav Ganovfd8d9c42012-07-09 18:16:55 -0700747 final int infoCount = infos.size();
748 for (int i = 0; i < infoCount; i++) {
749 AccessibilityNodeInfo info = infos.get(i);
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700750 applyAppScaleAndMagnificationSpecIfNeeded(info, spec);
Svetoslav Ganovfd8d9c42012-07-09 18:16:55 -0700751 }
752 }
753 }
754
Svetoslav9ae9ed22014-09-02 16:36:35 -0700755 private void adjustIsVisibleToUserIfNeeded(List<AccessibilityNodeInfo> infos,
756 Region interactiveRegion) {
757 if (interactiveRegion == null || infos == null) {
758 return;
759 }
760 final int infoCount = infos.size();
761 for (int i = 0; i < infoCount; i++) {
762 AccessibilityNodeInfo info = infos.get(i);
763 adjustIsVisibleToUserIfNeeded(info, interactiveRegion);
764 }
765 }
766
767 private void adjustIsVisibleToUserIfNeeded(AccessibilityNodeInfo info,
768 Region interactiveRegion) {
769 if (interactiveRegion == null || info == null) {
770 return;
771 }
772 Rect boundsInScreen = mTempRect;
773 info.getBoundsInScreen(boundsInScreen);
774 if (interactiveRegion.quickReject(boundsInScreen)) {
775 info.setVisibleToUser(false);
776 }
777 }
778
Svet Ganov7498efd2014-09-03 21:33:00 -0700779 private void applyAppScaleAndMagnificationSpecIfNeeded(Point point,
780 MagnificationSpec spec) {
781 final float applicationScale = mViewRootImpl.mAttachInfo.mApplicationScale;
782 if (!shouldApplyAppScaleAndMagnificationSpec(applicationScale, spec)) {
783 return;
784 }
785
786 if (applicationScale != 1.0f) {
787 point.x *= applicationScale;
788 point.y *= applicationScale;
789 }
790
791 if (spec != null) {
792 point.x *= spec.scale;
793 point.y *= spec.scale;
794 point.x += (int) spec.offsetX;
795 point.y += (int) spec.offsetY;
796 }
797 }
798
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700799 private void applyAppScaleAndMagnificationSpecIfNeeded(AccessibilityNodeInfo info,
800 MagnificationSpec spec) {
Svetoslav Ganovfd8d9c42012-07-09 18:16:55 -0700801 if (info == null) {
802 return;
803 }
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700804
Svetoslav Ganovfd8d9c42012-07-09 18:16:55 -0700805 final float applicationScale = mViewRootImpl.mAttachInfo.mApplicationScale;
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700806 if (!shouldApplyAppScaleAndMagnificationSpec(applicationScale, spec)) {
807 return;
808 }
809
810 Rect boundsInParent = mTempRect;
811 Rect boundsInScreen = mTempRect1;
812
813 info.getBoundsInParent(boundsInParent);
814 info.getBoundsInScreen(boundsInScreen);
Svetoslav Ganovfd8d9c42012-07-09 18:16:55 -0700815 if (applicationScale != 1.0f) {
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700816 boundsInParent.scale(applicationScale);
817 boundsInScreen.scale(applicationScale);
818 }
819 if (spec != null) {
820 boundsInParent.scale(spec.scale);
821 // boundsInParent must not be offset.
822 boundsInScreen.scale(spec.scale);
823 boundsInScreen.offset((int) spec.offsetX, (int) spec.offsetY);
824 }
825 info.setBoundsInParent(boundsInParent);
826 info.setBoundsInScreen(boundsInScreen);
Svetoslav Ganovfd8d9c42012-07-09 18:16:55 -0700827
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700828 if (spec != null) {
829 AttachInfo attachInfo = mViewRootImpl.mAttachInfo;
830 if (attachInfo.mDisplay == null) {
831 return;
832 }
Svetoslav Ganovfd8d9c42012-07-09 18:16:55 -0700833
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700834 final float scale = attachInfo.mApplicationScale * spec.scale;
835
836 Rect visibleWinFrame = mTempRect1;
837 visibleWinFrame.left = (int) (attachInfo.mWindowLeft * scale + spec.offsetX);
838 visibleWinFrame.top = (int) (attachInfo.mWindowTop * scale + spec.offsetY);
839 visibleWinFrame.right = (int) (visibleWinFrame.left + mViewRootImpl.mWidth * scale);
840 visibleWinFrame.bottom = (int) (visibleWinFrame.top + mViewRootImpl.mHeight * scale);
841
842 attachInfo.mDisplay.getRealSize(mTempPoint);
843 final int displayWidth = mTempPoint.x;
844 final int displayHeight = mTempPoint.y;
845
846 Rect visibleDisplayFrame = mTempRect2;
847 visibleDisplayFrame.set(0, 0, displayWidth, displayHeight);
848
849 visibleWinFrame.intersect(visibleDisplayFrame);
850
851 if (!visibleWinFrame.intersects(boundsInScreen.left, boundsInScreen.top,
852 boundsInScreen.right, boundsInScreen.bottom)) {
853 info.setVisibleToUser(false);
854 }
Svetoslav Ganovfd8d9c42012-07-09 18:16:55 -0700855 }
856 }
857
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700858 private boolean shouldApplyAppScaleAndMagnificationSpec(float appScale,
859 MagnificationSpec spec) {
860 return (appScale != 1.0f || (spec != null && !spec.isNop()));
861 }
Svetoslav Ganovfd8d9c42012-07-09 18:16:55 -0700862
Svetoslav Ganov42138042012-03-20 11:51:39 -0700863 /**
Svetoslav Ganov42138042012-03-20 11:51:39 -0700864 * This class encapsulates a prefetching strategy for the accessibility APIs for
865 * querying window content. It is responsible to prefetch a batch of
866 * AccessibilityNodeInfos in addition to the one for a requested node.
867 */
868 private class AccessibilityNodePrefetcher {
869
870 private static final int MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE = 50;
871
Svetoslav Ganov4528b4e2012-05-15 18:24:10 -0700872 private final ArrayList<View> mTempViewList = new ArrayList<View>();
873
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800874 public void prefetchAccessibilityNodeInfos(View view, int virtualViewId, int fetchFlags,
Svetoslav Ganov42138042012-03-20 11:51:39 -0700875 List<AccessibilityNodeInfo> outInfos) {
876 AccessibilityNodeProvider provider = view.getAccessibilityNodeProvider();
877 if (provider == null) {
878 AccessibilityNodeInfo root = view.createAccessibilityNodeInfo();
879 if (root != null) {
880 outInfos.add(root);
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800881 if ((fetchFlags & AccessibilityNodeInfo.FLAG_PREFETCH_PREDECESSORS) != 0) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700882 prefetchPredecessorsOfRealNode(view, outInfos);
883 }
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800884 if ((fetchFlags & AccessibilityNodeInfo.FLAG_PREFETCH_SIBLINGS) != 0) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700885 prefetchSiblingsOfRealNode(view, outInfos);
886 }
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800887 if ((fetchFlags & AccessibilityNodeInfo.FLAG_PREFETCH_DESCENDANTS) != 0) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700888 prefetchDescendantsOfRealNode(view, outInfos);
889 }
890 }
891 } else {
892 AccessibilityNodeInfo root = provider.createAccessibilityNodeInfo(virtualViewId);
893 if (root != null) {
894 outInfos.add(root);
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800895 if ((fetchFlags & AccessibilityNodeInfo.FLAG_PREFETCH_PREDECESSORS) != 0) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700896 prefetchPredecessorsOfVirtualNode(root, view, provider, outInfos);
897 }
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800898 if ((fetchFlags & AccessibilityNodeInfo.FLAG_PREFETCH_SIBLINGS) != 0) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700899 prefetchSiblingsOfVirtualNode(root, view, provider, outInfos);
900 }
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800901 if ((fetchFlags & AccessibilityNodeInfo.FLAG_PREFETCH_DESCENDANTS) != 0) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700902 prefetchDescendantsOfVirtualNode(root, provider, outInfos);
903 }
904 }
905 }
Svetoslav8e3feb12014-02-24 13:46:47 -0800906 if (ENFORCE_NODE_TREE_CONSISTENT) {
907 enforceNodeTreeConsistent(outInfos);
908 }
909 }
910
911 private void enforceNodeTreeConsistent(List<AccessibilityNodeInfo> nodes) {
912 LongSparseArray<AccessibilityNodeInfo> nodeMap =
913 new LongSparseArray<AccessibilityNodeInfo>();
914 final int nodeCount = nodes.size();
915 for (int i = 0; i < nodeCount; i++) {
916 AccessibilityNodeInfo node = nodes.get(i);
917 nodeMap.put(node.getSourceNodeId(), node);
918 }
919
920 // If the nodes are a tree it does not matter from
921 // which node we start to search for the root.
922 AccessibilityNodeInfo root = nodeMap.valueAt(0);
923 AccessibilityNodeInfo parent = root;
924 while (parent != null) {
925 root = parent;
926 parent = nodeMap.get(parent.getParentNodeId());
927 }
928
929 // Traverse the tree and do some checks.
930 AccessibilityNodeInfo accessFocus = null;
931 AccessibilityNodeInfo inputFocus = null;
932 HashSet<AccessibilityNodeInfo> seen = new HashSet<AccessibilityNodeInfo>();
933 Queue<AccessibilityNodeInfo> fringe = new LinkedList<AccessibilityNodeInfo>();
934 fringe.add(root);
935
936 while (!fringe.isEmpty()) {
937 AccessibilityNodeInfo current = fringe.poll();
938
939 // Check for duplicates
940 if (!seen.add(current)) {
941 throw new IllegalStateException("Duplicate node: "
942 + current + " in window:"
943 + mViewRootImpl.mAttachInfo.mAccessibilityWindowId);
944 }
945
946 // Check for one accessibility focus.
947 if (current.isAccessibilityFocused()) {
948 if (accessFocus != null) {
949 throw new IllegalStateException("Duplicate accessibility focus:"
950 + current
951 + " in window:" + mViewRootImpl.mAttachInfo.mAccessibilityWindowId);
952 } else {
953 accessFocus = current;
954 }
955 }
956
957 // Check for one input focus.
958 if (current.isFocused()) {
959 if (inputFocus != null) {
960 throw new IllegalStateException("Duplicate input focus: "
961 + current + " in window:"
962 + mViewRootImpl.mAttachInfo.mAccessibilityWindowId);
963 } else {
964 inputFocus = current;
965 }
966 }
967
968 final int childCount = current.getChildCount();
969 for (int j = 0; j < childCount; j++) {
970 final long childId = current.getChildId(j);
971 final AccessibilityNodeInfo child = nodeMap.get(childId);
972 if (child != null) {
973 fringe.add(child);
974 }
975 }
976 }
977
978 // Check for disconnected nodes.
979 for (int j = nodeMap.size() - 1; j >= 0; j--) {
980 AccessibilityNodeInfo info = nodeMap.valueAt(j);
981 if (!seen.contains(info)) {
982 throw new IllegalStateException("Disconnected node: " + info);
983 }
984 }
Svetoslav Ganov42138042012-03-20 11:51:39 -0700985 }
986
987 private void prefetchPredecessorsOfRealNode(View view,
988 List<AccessibilityNodeInfo> outInfos) {
989 ViewParent parent = view.getParentForAccessibility();
990 while (parent instanceof View
991 && outInfos.size() < MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE) {
992 View parentView = (View) parent;
Svetoslav Ganov42138042012-03-20 11:51:39 -0700993 AccessibilityNodeInfo info = parentView.createAccessibilityNodeInfo();
994 if (info != null) {
995 outInfos.add(info);
996 }
997 parent = parent.getParentForAccessibility();
998 }
999 }
1000
1001 private void prefetchSiblingsOfRealNode(View current,
1002 List<AccessibilityNodeInfo> outInfos) {
1003 ViewParent parent = current.getParentForAccessibility();
1004 if (parent instanceof ViewGroup) {
1005 ViewGroup parentGroup = (ViewGroup) parent;
Svetoslav Ganov4528b4e2012-05-15 18:24:10 -07001006 ArrayList<View> children = mTempViewList;
1007 children.clear();
Svetoslav Ganov76f287e2012-04-23 11:02:36 -07001008 try {
Svetoslav Ganov4528b4e2012-05-15 18:24:10 -07001009 parentGroup.addChildrenForAccessibility(children);
1010 final int childCount = children.size();
Svetoslav Ganov76f287e2012-04-23 11:02:36 -07001011 for (int i = 0; i < childCount; i++) {
1012 if (outInfos.size() >= MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE) {
1013 return;
Svetoslav Ganov42138042012-03-20 11:51:39 -07001014 }
Svetoslav Ganov4528b4e2012-05-15 18:24:10 -07001015 View child = children.get(i);
Svetoslav Ganov76f287e2012-04-23 11:02:36 -07001016 if (child.getAccessibilityViewId() != current.getAccessibilityViewId()
Svetoslav Ganov0a1bb6d2012-05-07 11:54:39 -07001017 && isShown(child)) {
Svetoslav Ganov76f287e2012-04-23 11:02:36 -07001018 AccessibilityNodeInfo info = null;
Svetoslav Ganov4528b4e2012-05-15 18:24:10 -07001019 AccessibilityNodeProvider provider =
1020 child.getAccessibilityNodeProvider();
Svetoslav Ganov76f287e2012-04-23 11:02:36 -07001021 if (provider == null) {
1022 info = child.createAccessibilityNodeInfo();
1023 } else {
1024 info = provider.createAccessibilityNodeInfo(
Svetoslav8e3feb12014-02-24 13:46:47 -08001025 AccessibilityNodeProvider.HOST_VIEW_ID);
Svetoslav Ganov76f287e2012-04-23 11:02:36 -07001026 }
1027 if (info != null) {
1028 outInfos.add(info);
1029 }
Svetoslav Ganov42138042012-03-20 11:51:39 -07001030 }
1031 }
Svetoslav Ganov76f287e2012-04-23 11:02:36 -07001032 } finally {
Svetoslav Ganov4528b4e2012-05-15 18:24:10 -07001033 children.clear();
Svetoslav Ganov42138042012-03-20 11:51:39 -07001034 }
Svetoslav Ganov42138042012-03-20 11:51:39 -07001035 }
1036 }
1037
1038 private void prefetchDescendantsOfRealNode(View root,
1039 List<AccessibilityNodeInfo> outInfos) {
1040 if (!(root instanceof ViewGroup)) {
1041 return;
1042 }
Svetoslav Ganov42138042012-03-20 11:51:39 -07001043 HashMap<View, AccessibilityNodeInfo> addedChildren =
1044 new HashMap<View, AccessibilityNodeInfo>();
Svetoslav Ganov4528b4e2012-05-15 18:24:10 -07001045 ArrayList<View> children = mTempViewList;
1046 children.clear();
Svetoslav Ganov76f287e2012-04-23 11:02:36 -07001047 try {
Svetoslav Ganov4528b4e2012-05-15 18:24:10 -07001048 root.addChildrenForAccessibility(children);
1049 final int childCount = children.size();
Svetoslav Ganov76f287e2012-04-23 11:02:36 -07001050 for (int i = 0; i < childCount; i++) {
1051 if (outInfos.size() >= MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE) {
1052 return;
1053 }
Svetoslav Ganov4528b4e2012-05-15 18:24:10 -07001054 View child = children.get(i);
Svetoslav Ganov0a1bb6d2012-05-07 11:54:39 -07001055 if (isShown(child)) {
Svetoslav Ganov76f287e2012-04-23 11:02:36 -07001056 AccessibilityNodeProvider provider = child.getAccessibilityNodeProvider();
1057 if (provider == null) {
1058 AccessibilityNodeInfo info = child.createAccessibilityNodeInfo();
1059 if (info != null) {
1060 outInfos.add(info);
1061 addedChildren.put(child, null);
1062 }
1063 } else {
1064 AccessibilityNodeInfo info = provider.createAccessibilityNodeInfo(
Svetoslav8e3feb12014-02-24 13:46:47 -08001065 AccessibilityNodeProvider.HOST_VIEW_ID);
Svetoslav Ganov76f287e2012-04-23 11:02:36 -07001066 if (info != null) {
1067 outInfos.add(info);
1068 addedChildren.put(child, info);
1069 }
Svetoslav Ganov42138042012-03-20 11:51:39 -07001070 }
1071 }
1072 }
Svetoslav Ganov76f287e2012-04-23 11:02:36 -07001073 } finally {
Svetoslav Ganov4528b4e2012-05-15 18:24:10 -07001074 children.clear();
Svetoslav Ganov42138042012-03-20 11:51:39 -07001075 }
Svetoslav Ganov42138042012-03-20 11:51:39 -07001076 if (outInfos.size() < MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE) {
1077 for (Map.Entry<View, AccessibilityNodeInfo> entry : addedChildren.entrySet()) {
1078 View addedChild = entry.getKey();
1079 AccessibilityNodeInfo virtualRoot = entry.getValue();
1080 if (virtualRoot == null) {
1081 prefetchDescendantsOfRealNode(addedChild, outInfos);
1082 } else {
1083 AccessibilityNodeProvider provider =
1084 addedChild.getAccessibilityNodeProvider();
1085 prefetchDescendantsOfVirtualNode(virtualRoot, provider, outInfos);
1086 }
1087 }
1088 }
1089 }
1090
1091 private void prefetchPredecessorsOfVirtualNode(AccessibilityNodeInfo root,
1092 View providerHost, AccessibilityNodeProvider provider,
1093 List<AccessibilityNodeInfo> outInfos) {
1094 long parentNodeId = root.getParentNodeId();
1095 int accessibilityViewId = AccessibilityNodeInfo.getAccessibilityViewId(parentNodeId);
Svetoslav8e3feb12014-02-24 13:46:47 -08001096 while (accessibilityViewId != AccessibilityNodeInfo.UNDEFINED_ITEM_ID) {
Svetoslav Ganov42138042012-03-20 11:51:39 -07001097 if (outInfos.size() >= MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE) {
1098 return;
1099 }
1100 final int virtualDescendantId =
1101 AccessibilityNodeInfo.getVirtualDescendantId(parentNodeId);
Svetoslav8e3feb12014-02-24 13:46:47 -08001102 if (virtualDescendantId != AccessibilityNodeInfo.UNDEFINED_ITEM_ID
Svetoslav Ganov42138042012-03-20 11:51:39 -07001103 || accessibilityViewId == providerHost.getAccessibilityViewId()) {
Svetoslav8e3feb12014-02-24 13:46:47 -08001104 final AccessibilityNodeInfo parent;
1105 if (virtualDescendantId != AccessibilityNodeInfo.UNDEFINED_ITEM_ID) {
1106 parent = provider.createAccessibilityNodeInfo(
1107 virtualDescendantId);
1108 } else {
1109 parent= provider.createAccessibilityNodeInfo(
1110 AccessibilityNodeProvider.HOST_VIEW_ID);
1111 }
Svetoslav Ganov42138042012-03-20 11:51:39 -07001112 if (parent != null) {
1113 outInfos.add(parent);
1114 }
1115 parentNodeId = parent.getParentNodeId();
1116 accessibilityViewId = AccessibilityNodeInfo.getAccessibilityViewId(
1117 parentNodeId);
1118 } else {
1119 prefetchPredecessorsOfRealNode(providerHost, outInfos);
1120 return;
1121 }
1122 }
1123 }
1124
1125 private void prefetchSiblingsOfVirtualNode(AccessibilityNodeInfo current, View providerHost,
1126 AccessibilityNodeProvider provider, List<AccessibilityNodeInfo> outInfos) {
1127 final long parentNodeId = current.getParentNodeId();
1128 final int parentAccessibilityViewId =
1129 AccessibilityNodeInfo.getAccessibilityViewId(parentNodeId);
1130 final int parentVirtualDescendantId =
1131 AccessibilityNodeInfo.getVirtualDescendantId(parentNodeId);
Svetoslav8e3feb12014-02-24 13:46:47 -08001132 if (parentVirtualDescendantId != AccessibilityNodeInfo.UNDEFINED_ITEM_ID
Svetoslav Ganov42138042012-03-20 11:51:39 -07001133 || parentAccessibilityViewId == providerHost.getAccessibilityViewId()) {
Svetoslav8e3feb12014-02-24 13:46:47 -08001134 final AccessibilityNodeInfo parent;
1135 if (parentAccessibilityViewId != AccessibilityNodeInfo.UNDEFINED_ITEM_ID) {
1136 parent = provider.createAccessibilityNodeInfo(parentVirtualDescendantId);
1137 } else {
1138 parent = provider.createAccessibilityNodeInfo(
1139 AccessibilityNodeProvider.HOST_VIEW_ID);
1140 }
Svetoslav Ganov42138042012-03-20 11:51:39 -07001141 if (parent != null) {
Alan Viverettef0aed092013-11-06 15:33:03 -08001142 final int childCount = parent.getChildCount();
Svetoslav Ganov42138042012-03-20 11:51:39 -07001143 for (int i = 0; i < childCount; i++) {
1144 if (outInfos.size() >= MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE) {
1145 return;
1146 }
Alan Viverettef0aed092013-11-06 15:33:03 -08001147 final long childNodeId = parent.getChildId(i);
Svetoslav Ganov42138042012-03-20 11:51:39 -07001148 if (childNodeId != current.getSourceNodeId()) {
1149 final int childVirtualDescendantId =
1150 AccessibilityNodeInfo.getVirtualDescendantId(childNodeId);
1151 AccessibilityNodeInfo child = provider.createAccessibilityNodeInfo(
1152 childVirtualDescendantId);
1153 if (child != null) {
1154 outInfos.add(child);
1155 }
1156 }
1157 }
1158 }
1159 } else {
1160 prefetchSiblingsOfRealNode(providerHost, outInfos);
1161 }
1162 }
1163
1164 private void prefetchDescendantsOfVirtualNode(AccessibilityNodeInfo root,
1165 AccessibilityNodeProvider provider, List<AccessibilityNodeInfo> outInfos) {
Svetoslav Ganov42138042012-03-20 11:51:39 -07001166 final int initialOutInfosSize = outInfos.size();
Alan Viverettef0aed092013-11-06 15:33:03 -08001167 final int childCount = root.getChildCount();
Svetoslav Ganov42138042012-03-20 11:51:39 -07001168 for (int i = 0; i < childCount; i++) {
1169 if (outInfos.size() >= MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE) {
1170 return;
1171 }
Alan Viverettef0aed092013-11-06 15:33:03 -08001172 final long childNodeId = root.getChildId(i);
Svetoslav Ganov42138042012-03-20 11:51:39 -07001173 AccessibilityNodeInfo child = provider.createAccessibilityNodeInfo(
1174 AccessibilityNodeInfo.getVirtualDescendantId(childNodeId));
1175 if (child != null) {
1176 outInfos.add(child);
1177 }
1178 }
1179 if (outInfos.size() < MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE) {
1180 final int addedChildCount = outInfos.size() - initialOutInfosSize;
1181 for (int i = 0; i < addedChildCount; i++) {
1182 AccessibilityNodeInfo child = outInfos.get(initialOutInfosSize + i);
1183 prefetchDescendantsOfVirtualNode(child, provider, outInfos);
1184 }
1185 }
1186 }
1187 }
1188
1189 private class PrivateHandler extends Handler {
1190 private final static int MSG_PERFORM_ACCESSIBILITY_ACTION = 1;
Svet Ganov7498efd2014-09-03 21:33:00 -07001191 private final static int MSG_FIND_ACCESSIBILITY_NODE_INFO_BY_ACCESSIBILITY_ID = 2;
1192 private final static int MSG_FIND_ACCESSIBILITY_NODE_INFOS_BY_VIEW_ID = 3;
1193 private final static int MSG_FIND_ACCESSIBILITY_NODE_INFO_BY_TEXT = 4;
Svetoslav Ganov42138042012-03-20 11:51:39 -07001194 private final static int MSG_FIND_FOCUS = 5;
1195 private final static int MSG_FOCUS_SEARCH = 6;
Svet Ganov7498efd2014-09-03 21:33:00 -07001196 private final static int MSG_COMPUTE_CLICK_POINT_IN_SCREEN = 7;
Svetoslav Ganov42138042012-03-20 11:51:39 -07001197
Svetoslav Ganov005b83b2012-04-16 18:17:17 -07001198 public PrivateHandler(Looper looper) {
1199 super(looper);
Svetoslav Ganov42138042012-03-20 11:51:39 -07001200 }
1201
1202 @Override
1203 public String getMessageName(Message message) {
1204 final int type = message.what;
1205 switch (type) {
1206 case MSG_PERFORM_ACCESSIBILITY_ACTION:
1207 return "MSG_PERFORM_ACCESSIBILITY_ACTION";
Svet Ganov7498efd2014-09-03 21:33:00 -07001208 case MSG_FIND_ACCESSIBILITY_NODE_INFO_BY_ACCESSIBILITY_ID:
1209 return "MSG_FIND_ACCESSIBILITY_NODE_INFO_BY_ACCESSIBILITY_ID";
1210 case MSG_FIND_ACCESSIBILITY_NODE_INFOS_BY_VIEW_ID:
1211 return "MSG_FIND_ACCESSIBILITY_NODE_INFOS_BY_VIEW_ID";
1212 case MSG_FIND_ACCESSIBILITY_NODE_INFO_BY_TEXT:
1213 return "MSG_FIND_ACCESSIBILITY_NODE_INFO_BY_TEXT";
Svetoslav Ganov42138042012-03-20 11:51:39 -07001214 case MSG_FIND_FOCUS:
1215 return "MSG_FIND_FOCUS";
1216 case MSG_FOCUS_SEARCH:
1217 return "MSG_FOCUS_SEARCH";
Svet Ganov7498efd2014-09-03 21:33:00 -07001218 case MSG_COMPUTE_CLICK_POINT_IN_SCREEN:
1219 return "MSG_COMPUTE_CLICK_POINT_IN_SCREEN";
Svetoslav Ganov42138042012-03-20 11:51:39 -07001220 default:
1221 throw new IllegalArgumentException("Unknown message type: " + type);
1222 }
1223 }
1224
1225 @Override
1226 public void handleMessage(Message message) {
1227 final int type = message.what;
1228 switch (type) {
Svet Ganov7498efd2014-09-03 21:33:00 -07001229 case MSG_FIND_ACCESSIBILITY_NODE_INFO_BY_ACCESSIBILITY_ID: {
Svetoslav Ganov42138042012-03-20 11:51:39 -07001230 findAccessibilityNodeInfoByAccessibilityIdUiThread(message);
1231 } break;
1232 case MSG_PERFORM_ACCESSIBILITY_ACTION: {
1233 perfromAccessibilityActionUiThread(message);
1234 } break;
Svet Ganov7498efd2014-09-03 21:33:00 -07001235 case MSG_FIND_ACCESSIBILITY_NODE_INFOS_BY_VIEW_ID: {
Svetoslav Ganov80943d82013-01-02 10:25:37 -08001236 findAccessibilityNodeInfosByViewIdUiThread(message);
Svetoslav Ganov42138042012-03-20 11:51:39 -07001237 } break;
Svet Ganov7498efd2014-09-03 21:33:00 -07001238 case MSG_FIND_ACCESSIBILITY_NODE_INFO_BY_TEXT: {
Svetoslav Ganov42138042012-03-20 11:51:39 -07001239 findAccessibilityNodeInfosByTextUiThread(message);
1240 } break;
1241 case MSG_FIND_FOCUS: {
1242 findFocusUiThread(message);
1243 } break;
1244 case MSG_FOCUS_SEARCH: {
1245 focusSearchUiThread(message);
1246 } break;
Svet Ganov7498efd2014-09-03 21:33:00 -07001247 case MSG_COMPUTE_CLICK_POINT_IN_SCREEN: {
1248 computeClickPointInScreenUiThread(message);
1249 } break;
Svetoslav Ganov42138042012-03-20 11:51:39 -07001250 default:
1251 throw new IllegalArgumentException("Unknown message type: " + type);
1252 }
1253 }
1254 }
Svetoslav Ganov80943d82013-01-02 10:25:37 -08001255
1256 private final class AddNodeInfosForViewId implements Predicate<View> {
1257 private int mViewId = View.NO_ID;
1258 private List<AccessibilityNodeInfo> mInfos;
1259
1260 public void init(int viewId, List<AccessibilityNodeInfo> infos) {
1261 mViewId = viewId;
1262 mInfos = infos;
1263 }
1264
1265 public void reset() {
1266 mViewId = View.NO_ID;
1267 mInfos = null;
1268 }
1269
1270 @Override
1271 public boolean apply(View view) {
1272 if (view.getId() == mViewId && isShown(view)) {
1273 mInfos.add(view.createAccessibilityNodeInfo());
1274 }
1275 return false;
1276 }
1277 }
Svetoslav Ganov42138042012-03-20 11:51:39 -07001278}