blob: 2d6453e67b5d5aa17e55975c31ac0a3dc341afd8 [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;
Svetoslav Ganovaa780c12012-04-19 23:01:39 -070021import android.os.Bundle;
Svetoslav Ganov42138042012-03-20 11:51:39 -070022import android.os.Handler;
23import android.os.Looper;
24import android.os.Message;
25import android.os.Process;
26import android.os.RemoteException;
Svetoslav Ganov42138042012-03-20 11:51:39 -070027import android.util.SparseLongArray;
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -070028import android.view.View.AttachInfo;
Svetoslav Ganov42138042012-03-20 11:51:39 -070029import android.view.accessibility.AccessibilityInteractionClient;
30import android.view.accessibility.AccessibilityNodeInfo;
31import android.view.accessibility.AccessibilityNodeProvider;
32import android.view.accessibility.IAccessibilityInteractionConnectionCallback;
33
Svetoslav Ganov758143e2012-08-06 16:40:27 -070034import com.android.internal.os.SomeArgs;
Svetoslav Ganov80943d82013-01-02 10:25:37 -080035import com.android.internal.util.Predicate;
Svetoslav Ganov758143e2012-08-06 16:40:27 -070036
Svetoslav Ganov42138042012-03-20 11:51:39 -070037import java.util.ArrayList;
38import java.util.HashMap;
39import java.util.List;
40import java.util.Map;
41
42/**
43 * Class for managing accessibility interactions initiated from the system
44 * and targeting the view hierarchy. A *ClientThread method is to be
45 * called from the interaction connection ViewAncestor gives the system to
46 * talk to it and a corresponding *UiThread method that is executed on the
47 * UI thread.
48 */
49final class AccessibilityInteractionController {
Svetoslav Ganov42138042012-03-20 11:51:39 -070050
Svetoslav Ganov80943d82013-01-02 10:25:37 -080051 private final ArrayList<AccessibilityNodeInfo> mTempAccessibilityNodeInfoList =
Svetoslav Ganov42138042012-03-20 11:51:39 -070052 new ArrayList<AccessibilityNodeInfo>();
53
Svetoslav Ganov005b83b2012-04-16 18:17:17 -070054 private final Handler mHandler;
Svetoslav Ganov42138042012-03-20 11:51:39 -070055
56 private final ViewRootImpl mViewRootImpl;
57
58 private final AccessibilityNodePrefetcher mPrefetcher;
59
Svetoslav Ganov749e7962012-04-19 17:13:46 -070060 private final long mMyLooperThreadId;
61
62 private final int mMyProcessId;
63
Svetoslav Ganov30ac6452012-06-01 09:10:25 -070064 private final ArrayList<View> mTempArrayList = new ArrayList<View>();
65
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -070066 private final Point mTempPoint = new Point();
Svetoslav Ganovfd8d9c42012-07-09 18:16:55 -070067 private final Rect mTempRect = new Rect();
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -070068 private final Rect mTempRect1 = new Rect();
69 private final Rect mTempRect2 = new Rect();
Svetoslav Ganovfd8d9c42012-07-09 18:16:55 -070070
Svetoslav Ganov80943d82013-01-02 10:25:37 -080071 private AddNodeInfosForViewId mAddNodeInfosForViewId;
72
Svetoslav Ganov42138042012-03-20 11:51:39 -070073 public AccessibilityInteractionController(ViewRootImpl viewRootImpl) {
Svetoslav Ganov749e7962012-04-19 17:13:46 -070074 Looper looper = viewRootImpl.mHandler.getLooper();
75 mMyLooperThreadId = looper.getThread().getId();
76 mMyProcessId = Process.myPid();
Svetoslav Ganov005b83b2012-04-16 18:17:17 -070077 mHandler = new PrivateHandler(looper);
Svetoslav Ganov42138042012-03-20 11:51:39 -070078 mViewRootImpl = viewRootImpl;
79 mPrefetcher = new AccessibilityNodePrefetcher();
80 }
81
Svetoslav Ganov0a1bb6d2012-05-07 11:54:39 -070082 private boolean isShown(View view) {
83 // The first two checks are made also made by isShown() which
84 // however traverses the tree up to the parent to catch that.
85 // Therefore, we do some fail fast check to minimize the up
86 // tree traversal.
87 return (view.mAttachInfo != null
88 && view.mAttachInfo.mWindowVisibility == View.VISIBLE
89 && view.isShown());
90 }
91
Svetoslav Ganov42138042012-03-20 11:51:39 -070092 public void findAccessibilityNodeInfoByAccessibilityIdClientThread(
Svetoslav Ganovc9c9a482012-07-16 08:46:07 -070093 long accessibilityNodeId, int interactionId,
Svetoslav Ganov42138042012-03-20 11:51:39 -070094 IAccessibilityInteractionConnectionCallback callback, int flags, int interrogatingPid,
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -070095 long interrogatingTid, MagnificationSpec spec) {
Svetoslav Ganov42138042012-03-20 11:51:39 -070096 Message message = mHandler.obtainMessage();
97 message.what = PrivateHandler.MSG_FIND_ACCESSIBLITY_NODE_INFO_BY_ACCESSIBILITY_ID;
98 message.arg1 = flags;
Svetoslav Ganov86783472012-06-06 21:12:20 -070099
Svetoslav Ganov758143e2012-08-06 16:40:27 -0700100 SomeArgs args = SomeArgs.obtain();
Svetoslav Ganov42138042012-03-20 11:51:39 -0700101 args.argi1 = AccessibilityNodeInfo.getAccessibilityViewId(accessibilityNodeId);
102 args.argi2 = AccessibilityNodeInfo.getVirtualDescendantId(accessibilityNodeId);
103 args.argi3 = interactionId;
104 args.arg1 = callback;
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700105 args.arg2 = spec;
Svetoslav Ganov42138042012-03-20 11:51:39 -0700106 message.obj = args;
Svetoslav Ganov86783472012-06-06 21:12:20 -0700107
Svetoslav Ganov42138042012-03-20 11:51:39 -0700108 // If the interrogation is performed by the same thread as the main UI
109 // thread in this process, set the message as a static reference so
110 // after this call completes the same thread but in the interrogating
111 // client can handle the message to generate the result.
Svetoslav Ganov749e7962012-04-19 17:13:46 -0700112 if (interrogatingPid == mMyProcessId && interrogatingTid == mMyLooperThreadId) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700113 AccessibilityInteractionClient.getInstanceForThread(
114 interrogatingTid).setSameThreadMessage(message);
115 } else {
116 mHandler.sendMessage(message);
117 }
118 }
119
120 private void findAccessibilityNodeInfoByAccessibilityIdUiThread(Message message) {
121 final int flags = message.arg1;
Svetoslav Ganov86783472012-06-06 21:12:20 -0700122
Svetoslav Ganov42138042012-03-20 11:51:39 -0700123 SomeArgs args = (SomeArgs) message.obj;
124 final int accessibilityViewId = args.argi1;
125 final int virtualDescendantId = args.argi2;
126 final int interactionId = args.argi3;
127 final IAccessibilityInteractionConnectionCallback callback =
128 (IAccessibilityInteractionConnectionCallback) args.arg1;
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700129 final MagnificationSpec spec = (MagnificationSpec) args.arg2;
Svetoslav Ganov86783472012-06-06 21:12:20 -0700130
Svetoslav Ganov758143e2012-08-06 16:40:27 -0700131 args.recycle();
Svetoslav Ganov86783472012-06-06 21:12:20 -0700132
Svetoslav Ganov42138042012-03-20 11:51:39 -0700133 List<AccessibilityNodeInfo> infos = mTempAccessibilityNodeInfoList;
134 infos.clear();
135 try {
136 if (mViewRootImpl.mView == null || mViewRootImpl.mAttachInfo == null) {
137 return;
138 }
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800139 mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = flags;
Svetoslav Ganov42138042012-03-20 11:51:39 -0700140 View root = null;
141 if (accessibilityViewId == AccessibilityNodeInfo.UNDEFINED) {
142 root = mViewRootImpl.mView;
143 } else {
144 root = findViewByAccessibilityId(accessibilityViewId);
145 }
Svetoslav Ganov0a1bb6d2012-05-07 11:54:39 -0700146 if (root != null && isShown(root)) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700147 mPrefetcher.prefetchAccessibilityNodeInfos(root, virtualDescendantId, flags, infos);
148 }
149 } finally {
150 try {
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800151 mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = 0;
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700152 applyAppScaleAndMagnificationSpecIfNeeded(infos, spec);
153 if (spec != null) {
154 spec.recycle();
155 }
Svetoslav Ganov42138042012-03-20 11:51:39 -0700156 callback.setFindAccessibilityNodeInfosResult(infos, interactionId);
157 infos.clear();
158 } catch (RemoteException re) {
159 /* ignore - the other side will time out */
160 }
161 }
162 }
163
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800164 public void findAccessibilityNodeInfosByViewIdClientThread(long accessibilityNodeId,
165 String viewId, int interactionId, IAccessibilityInteractionConnectionCallback callback,
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700166 int flags, int interrogatingPid, long interrogatingTid, MagnificationSpec spec) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700167 Message message = mHandler.obtainMessage();
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800168 message.what = PrivateHandler.MSG_FIND_ACCESSIBLITY_NODE_INFOS_BY_VIEW_ID;
Svetoslav Ganov42138042012-03-20 11:51:39 -0700169 message.arg1 = flags;
170 message.arg2 = AccessibilityNodeInfo.getAccessibilityViewId(accessibilityNodeId);
Svetoslav Ganov86783472012-06-06 21:12:20 -0700171
Svetoslav Ganov758143e2012-08-06 16:40:27 -0700172 SomeArgs args = SomeArgs.obtain();
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800173 args.argi1 = interactionId;
Svetoslav Ganov42138042012-03-20 11:51:39 -0700174 args.arg1 = callback;
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700175 args.arg2 = spec;
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800176 args.arg3 = viewId;
Svetoslav Ganov86783472012-06-06 21:12:20 -0700177
Svetoslav Ganov42138042012-03-20 11:51:39 -0700178 message.obj = args;
Svetoslav Ganov86783472012-06-06 21:12:20 -0700179
Svetoslav Ganov42138042012-03-20 11:51:39 -0700180 // If the interrogation is performed by the same thread as the main UI
181 // thread in this process, set the message as a static reference so
182 // after this call completes the same thread but in the interrogating
183 // client can handle the message to generate the result.
Svetoslav Ganov749e7962012-04-19 17:13:46 -0700184 if (interrogatingPid == mMyProcessId && interrogatingTid == mMyLooperThreadId) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700185 AccessibilityInteractionClient.getInstanceForThread(
186 interrogatingTid).setSameThreadMessage(message);
187 } else {
188 mHandler.sendMessage(message);
189 }
190 }
191
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800192 private void findAccessibilityNodeInfosByViewIdUiThread(Message message) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700193 final int flags = message.arg1;
194 final int accessibilityViewId = message.arg2;
Svetoslav Ganov86783472012-06-06 21:12:20 -0700195
Svetoslav Ganov42138042012-03-20 11:51:39 -0700196 SomeArgs args = (SomeArgs) message.obj;
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800197 final int interactionId = args.argi1;
Svetoslav Ganov42138042012-03-20 11:51:39 -0700198 final IAccessibilityInteractionConnectionCallback callback =
199 (IAccessibilityInteractionConnectionCallback) args.arg1;
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700200 final MagnificationSpec spec = (MagnificationSpec) args.arg2;
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800201 final String viewId = (String) args.arg3;
Svetoslav Ganov86783472012-06-06 21:12:20 -0700202
Svetoslav Ganov758143e2012-08-06 16:40:27 -0700203 args.recycle();
Svetoslav Ganov86783472012-06-06 21:12:20 -0700204
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800205 final List<AccessibilityNodeInfo> infos = mTempAccessibilityNodeInfoList;
206 infos.clear();
Svetoslav Ganov42138042012-03-20 11:51:39 -0700207 try {
208 if (mViewRootImpl.mView == null || mViewRootImpl.mAttachInfo == null) {
209 return;
210 }
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800211 mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = flags;
Svetoslav Ganov42138042012-03-20 11:51:39 -0700212 View root = null;
213 if (accessibilityViewId != AccessibilityNodeInfo.UNDEFINED) {
214 root = findViewByAccessibilityId(accessibilityViewId);
215 } else {
216 root = mViewRootImpl.mView;
217 }
218 if (root != null) {
Svetoslava33243e2013-02-11 15:43:46 -0800219 final int resolvedViewId = root.getContext().getResources()
220 .getIdentifier(viewId, null, null);
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800221 if (resolvedViewId <= 0) {
222 return;
223 }
224 if (mAddNodeInfosForViewId == null) {
225 mAddNodeInfosForViewId = new AddNodeInfosForViewId();
226 }
227 mAddNodeInfosForViewId.init(resolvedViewId, infos);
228 root.findViewByPredicate(mAddNodeInfosForViewId);
229 mAddNodeInfosForViewId.reset();
Svetoslav Ganov42138042012-03-20 11:51:39 -0700230 }
231 } finally {
232 try {
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800233 mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = 0;
234 applyAppScaleAndMagnificationSpecIfNeeded(infos, spec);
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700235 if (spec != null) {
236 spec.recycle();
237 }
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800238 callback.setFindAccessibilityNodeInfosResult(infos, interactionId);
Svetoslav Ganov42138042012-03-20 11:51:39 -0700239 } catch (RemoteException re) {
240 /* ignore - the other side will time out */
241 }
242 }
243 }
244
245 public void findAccessibilityNodeInfosByTextClientThread(long accessibilityNodeId,
Svetoslav Ganovc9c9a482012-07-16 08:46:07 -0700246 String text, int interactionId, IAccessibilityInteractionConnectionCallback callback,
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700247 int flags, int interrogatingPid, long interrogatingTid, MagnificationSpec spec) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700248 Message message = mHandler.obtainMessage();
249 message.what = PrivateHandler.MSG_FIND_ACCESSIBLITY_NODE_INFO_BY_TEXT;
250 message.arg1 = flags;
Svetoslav Ganov86783472012-06-06 21:12:20 -0700251
Svetoslav Ganov758143e2012-08-06 16:40:27 -0700252 SomeArgs args = SomeArgs.obtain();
Svetoslav Ganov42138042012-03-20 11:51:39 -0700253 args.arg1 = text;
Svetoslav Ganovc9c9a482012-07-16 08:46:07 -0700254 args.arg2 = callback;
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700255 args.arg3 = spec;
Svetoslav Ganov42138042012-03-20 11:51:39 -0700256 args.argi1 = AccessibilityNodeInfo.getAccessibilityViewId(accessibilityNodeId);
257 args.argi2 = AccessibilityNodeInfo.getVirtualDescendantId(accessibilityNodeId);
258 args.argi3 = interactionId;
Svetoslav Ganov42138042012-03-20 11:51:39 -0700259 message.obj = args;
Svetoslav Ganov86783472012-06-06 21:12:20 -0700260
Svetoslav Ganov42138042012-03-20 11:51:39 -0700261 // If the interrogation is performed by the same thread as the main UI
262 // thread in this process, set the message as a static reference so
263 // after this call completes the same thread but in the interrogating
264 // client can handle the message to generate the result.
Svetoslav Ganov749e7962012-04-19 17:13:46 -0700265 if (interrogatingPid == mMyProcessId && interrogatingTid == mMyLooperThreadId) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700266 AccessibilityInteractionClient.getInstanceForThread(
267 interrogatingTid).setSameThreadMessage(message);
268 } else {
269 mHandler.sendMessage(message);
270 }
271 }
272
273 private void findAccessibilityNodeInfosByTextUiThread(Message message) {
274 final int flags = message.arg1;
Svetoslav Ganov86783472012-06-06 21:12:20 -0700275
Svetoslav Ganov42138042012-03-20 11:51:39 -0700276 SomeArgs args = (SomeArgs) message.obj;
277 final String text = (String) args.arg1;
Svetoslav Ganovc9c9a482012-07-16 08:46:07 -0700278 final IAccessibilityInteractionConnectionCallback callback =
279 (IAccessibilityInteractionConnectionCallback) args.arg2;
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700280 final MagnificationSpec spec = (MagnificationSpec) args.arg3;
Svetoslav Ganov42138042012-03-20 11:51:39 -0700281 final int accessibilityViewId = args.argi1;
282 final int virtualDescendantId = args.argi2;
283 final int interactionId = args.argi3;
Svetoslav Ganov758143e2012-08-06 16:40:27 -0700284 args.recycle();
Svetoslav Ganov86783472012-06-06 21:12:20 -0700285
Svetoslav Ganov42138042012-03-20 11:51:39 -0700286 List<AccessibilityNodeInfo> infos = null;
287 try {
288 if (mViewRootImpl.mView == null || mViewRootImpl.mAttachInfo == null) {
289 return;
290 }
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800291 mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = flags;
Svetoslav Ganov42138042012-03-20 11:51:39 -0700292 View root = null;
293 if (accessibilityViewId != AccessibilityNodeInfo.UNDEFINED) {
294 root = findViewByAccessibilityId(accessibilityViewId);
295 } else {
296 root = mViewRootImpl.mView;
297 }
Svetoslav Ganov0a1bb6d2012-05-07 11:54:39 -0700298 if (root != null && isShown(root)) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700299 AccessibilityNodeProvider provider = root.getAccessibilityNodeProvider();
300 if (provider != null) {
301 infos = provider.findAccessibilityNodeInfosByText(text,
302 virtualDescendantId);
303 } else if (virtualDescendantId == AccessibilityNodeInfo.UNDEFINED) {
Svetoslav Ganov30ac6452012-06-01 09:10:25 -0700304 ArrayList<View> foundViews = mTempArrayList;
Svetoslav Ganov42138042012-03-20 11:51:39 -0700305 foundViews.clear();
306 root.findViewsWithText(foundViews, text, View.FIND_VIEWS_WITH_TEXT
307 | View.FIND_VIEWS_WITH_CONTENT_DESCRIPTION
308 | View.FIND_VIEWS_WITH_ACCESSIBILITY_NODE_PROVIDERS);
309 if (!foundViews.isEmpty()) {
310 infos = mTempAccessibilityNodeInfoList;
311 infos.clear();
312 final int viewCount = foundViews.size();
313 for (int i = 0; i < viewCount; i++) {
314 View foundView = foundViews.get(i);
Svetoslav Ganov0a1bb6d2012-05-07 11:54:39 -0700315 if (isShown(foundView)) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700316 provider = foundView.getAccessibilityNodeProvider();
317 if (provider != null) {
318 List<AccessibilityNodeInfo> infosFromProvider =
319 provider.findAccessibilityNodeInfosByText(text,
Svetoslav Ganove5dfa47d2012-05-08 15:58:32 -0700320 AccessibilityNodeInfo.UNDEFINED);
Svetoslav Ganov42138042012-03-20 11:51:39 -0700321 if (infosFromProvider != null) {
322 infos.addAll(infosFromProvider);
323 }
324 } else {
325 infos.add(foundView.createAccessibilityNodeInfo());
326 }
327 }
328 }
329 }
330 }
331 }
332 } finally {
333 try {
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800334 mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = 0;
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700335 applyAppScaleAndMagnificationSpecIfNeeded(infos, spec);
336 if (spec != null) {
337 spec.recycle();
338 }
Svetoslav Ganov42138042012-03-20 11:51:39 -0700339 callback.setFindAccessibilityNodeInfosResult(infos, interactionId);
340 } catch (RemoteException re) {
341 /* ignore - the other side will time out */
342 }
343 }
344 }
345
Svetoslav Ganovc9c9a482012-07-16 08:46:07 -0700346 public void findFocusClientThread(long accessibilityNodeId, int focusType, int interactionId,
347 IAccessibilityInteractionConnectionCallback callback, int flags, int interogatingPid,
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700348 long interrogatingTid, MagnificationSpec spec) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700349 Message message = mHandler.obtainMessage();
350 message.what = PrivateHandler.MSG_FIND_FOCUS;
351 message.arg1 = flags;
352 message.arg2 = focusType;
Svetoslav Ganov86783472012-06-06 21:12:20 -0700353
Svetoslav Ganov758143e2012-08-06 16:40:27 -0700354 SomeArgs args = SomeArgs.obtain();
Svetoslav Ganov42138042012-03-20 11:51:39 -0700355 args.argi1 = interactionId;
356 args.argi2 = AccessibilityNodeInfo.getAccessibilityViewId(accessibilityNodeId);
357 args.argi3 = AccessibilityNodeInfo.getVirtualDescendantId(accessibilityNodeId);
358 args.arg1 = callback;
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700359 args.arg2 = spec;
Svetoslav Ganov86783472012-06-06 21:12:20 -0700360
Svetoslav Ganov42138042012-03-20 11:51:39 -0700361 message.obj = args;
Svetoslav Ganov86783472012-06-06 21:12:20 -0700362
Svetoslav Ganov42138042012-03-20 11:51:39 -0700363 // If the interrogation is performed by the same thread as the main UI
364 // thread in this process, set the message as a static reference so
365 // after this call completes the same thread but in the interrogating
366 // client can handle the message to generate the result.
Svetoslav Ganov749e7962012-04-19 17:13:46 -0700367 if (interogatingPid == mMyProcessId && interrogatingTid == mMyLooperThreadId) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700368 AccessibilityInteractionClient.getInstanceForThread(
369 interrogatingTid).setSameThreadMessage(message);
370 } else {
371 mHandler.sendMessage(message);
372 }
373 }
374
375 private void findFocusUiThread(Message message) {
376 final int flags = message.arg1;
377 final int focusType = message.arg2;
Svetoslav Ganov86783472012-06-06 21:12:20 -0700378
Svetoslav Ganov42138042012-03-20 11:51:39 -0700379 SomeArgs args = (SomeArgs) message.obj;
380 final int interactionId = args.argi1;
381 final int accessibilityViewId = args.argi2;
382 final int virtualDescendantId = args.argi3;
383 final IAccessibilityInteractionConnectionCallback callback =
384 (IAccessibilityInteractionConnectionCallback) args.arg1;
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700385 final MagnificationSpec spec = (MagnificationSpec) args.arg2;
Svetoslav Ganov758143e2012-08-06 16:40:27 -0700386 args.recycle();
Svetoslav Ganov86783472012-06-06 21:12:20 -0700387
Svetoslav Ganov42138042012-03-20 11:51:39 -0700388 AccessibilityNodeInfo focused = null;
389 try {
390 if (mViewRootImpl.mView == null || mViewRootImpl.mAttachInfo == null) {
391 return;
392 }
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800393 mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = flags;
Svetoslav Ganov42138042012-03-20 11:51:39 -0700394 View root = null;
395 if (accessibilityViewId != AccessibilityNodeInfo.UNDEFINED) {
396 root = findViewByAccessibilityId(accessibilityViewId);
397 } else {
398 root = mViewRootImpl.mView;
399 }
Svetoslav Ganov0a1bb6d2012-05-07 11:54:39 -0700400 if (root != null && isShown(root)) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700401 switch (focusType) {
402 case AccessibilityNodeInfo.FOCUS_ACCESSIBILITY: {
403 View host = mViewRootImpl.mAccessibilityFocusedHost;
404 // If there is no accessibility focus host or it is not a descendant
405 // of the root from which to start the search, then the search failed.
406 if (host == null || !ViewRootImpl.isViewDescendantOf(host, root)) {
407 break;
408 }
409 // If the host has a provider ask this provider to search for the
410 // focus instead fetching all provider nodes to do the search here.
411 AccessibilityNodeProvider provider = host.getAccessibilityNodeProvider();
412 if (provider != null) {
Svetoslav Ganov45a02e02012-06-17 15:07:29 -0700413 if (mViewRootImpl.mAccessibilityFocusedVirtualView != null) {
414 focused = AccessibilityNodeInfo.obtain(
415 mViewRootImpl.mAccessibilityFocusedVirtualView);
416 }
Svetoslav Ganov42138042012-03-20 11:51:39 -0700417 } else if (virtualDescendantId == View.NO_ID) {
418 focused = host.createAccessibilityNodeInfo();
419 }
420 } break;
421 case AccessibilityNodeInfo.FOCUS_INPUT: {
422 // Input focus cannot go to virtual views.
423 View target = root.findFocus();
Svetoslav Ganov0a1bb6d2012-05-07 11:54:39 -0700424 if (target != null && isShown(target)) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700425 focused = target.createAccessibilityNodeInfo();
426 }
427 } break;
428 default:
429 throw new IllegalArgumentException("Unknown focus type: " + focusType);
430 }
431 }
432 } finally {
433 try {
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800434 mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = 0;
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700435 applyAppScaleAndMagnificationSpecIfNeeded(focused, spec);
436 if (spec != null) {
437 spec.recycle();
438 }
Svetoslav Ganov42138042012-03-20 11:51:39 -0700439 callback.setFindAccessibilityNodeInfoResult(focused, interactionId);
440 } catch (RemoteException re) {
441 /* ignore - the other side will time out */
442 }
443 }
444 }
445
Svetoslav Ganovc9c9a482012-07-16 08:46:07 -0700446 public void focusSearchClientThread(long accessibilityNodeId, int direction, int interactionId,
447 IAccessibilityInteractionConnectionCallback callback, int flags, int interogatingPid,
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700448 long interrogatingTid, MagnificationSpec spec) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700449 Message message = mHandler.obtainMessage();
450 message.what = PrivateHandler.MSG_FOCUS_SEARCH;
451 message.arg1 = flags;
452 message.arg2 = AccessibilityNodeInfo.getAccessibilityViewId(accessibilityNodeId);
Svetoslav Ganov86783472012-06-06 21:12:20 -0700453
Svetoslav Ganov758143e2012-08-06 16:40:27 -0700454 SomeArgs args = SomeArgs.obtain();
Svetoslav Ganov42138042012-03-20 11:51:39 -0700455 args.argi2 = direction;
456 args.argi3 = interactionId;
457 args.arg1 = callback;
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700458 args.arg2 = spec;
Svetoslav Ganov86783472012-06-06 21:12:20 -0700459
Svetoslav Ganov42138042012-03-20 11:51:39 -0700460 message.obj = args;
Svetoslav Ganov86783472012-06-06 21:12:20 -0700461
Svetoslav Ganov42138042012-03-20 11:51:39 -0700462 // If the interrogation is performed by the same thread as the main UI
463 // thread in this process, set the message as a static reference so
464 // after this call completes the same thread but in the interrogating
465 // client can handle the message to generate the result.
Svetoslav Ganov749e7962012-04-19 17:13:46 -0700466 if (interogatingPid == mMyProcessId && interrogatingTid == mMyLooperThreadId) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700467 AccessibilityInteractionClient.getInstanceForThread(
468 interrogatingTid).setSameThreadMessage(message);
469 } else {
470 mHandler.sendMessage(message);
471 }
472 }
473
474 private void focusSearchUiThread(Message message) {
475 final int flags = message.arg1;
476 final int accessibilityViewId = message.arg2;
Svetoslav Ganov86783472012-06-06 21:12:20 -0700477
Svetoslav Ganov42138042012-03-20 11:51:39 -0700478 SomeArgs args = (SomeArgs) message.obj;
Svetoslav Ganov42138042012-03-20 11:51:39 -0700479 final int direction = args.argi2;
480 final int interactionId = args.argi3;
481 final IAccessibilityInteractionConnectionCallback callback =
482 (IAccessibilityInteractionConnectionCallback) args.arg1;
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700483 final MagnificationSpec spec = (MagnificationSpec) args.arg2;
Svetoslav Ganov86783472012-06-06 21:12:20 -0700484
Svetoslav Ganov758143e2012-08-06 16:40:27 -0700485 args.recycle();
Svetoslav Ganov86783472012-06-06 21:12:20 -0700486
Svetoslav Ganov42138042012-03-20 11:51:39 -0700487 AccessibilityNodeInfo next = null;
488 try {
489 if (mViewRootImpl.mView == null || mViewRootImpl.mAttachInfo == null) {
490 return;
491 }
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800492 mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = flags;
Svetoslav Ganov42138042012-03-20 11:51:39 -0700493 View root = null;
494 if (accessibilityViewId != AccessibilityNodeInfo.UNDEFINED) {
495 root = findViewByAccessibilityId(accessibilityViewId);
496 } else {
497 root = mViewRootImpl.mView;
498 }
Svetoslav Ganov0a1bb6d2012-05-07 11:54:39 -0700499 if (root != null && isShown(root)) {
Svetoslav Ganov27e2da72012-07-02 18:12:00 -0700500 View nextView = root.focusSearch(direction);
501 if (nextView != null) {
502 next = nextView.createAccessibilityNodeInfo();
Svetoslav Ganov42138042012-03-20 11:51:39 -0700503 }
504 }
505 } finally {
506 try {
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800507 mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = 0;
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700508 applyAppScaleAndMagnificationSpecIfNeeded(next, spec);
509 if (spec != null) {
510 spec.recycle();
511 }
Svetoslav Ganov42138042012-03-20 11:51:39 -0700512 callback.setFindAccessibilityNodeInfoResult(next, interactionId);
513 } catch (RemoteException re) {
514 /* ignore - the other side will time out */
515 }
516 }
517 }
518
519 public void performAccessibilityActionClientThread(long accessibilityNodeId, int action,
Svetoslav Ganovaa780c12012-04-19 23:01:39 -0700520 Bundle arguments, int interactionId,
521 IAccessibilityInteractionConnectionCallback callback, int flags, int interogatingPid,
522 long interrogatingTid) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700523 Message message = mHandler.obtainMessage();
524 message.what = PrivateHandler.MSG_PERFORM_ACCESSIBILITY_ACTION;
525 message.arg1 = flags;
526 message.arg2 = AccessibilityNodeInfo.getAccessibilityViewId(accessibilityNodeId);
Svetoslav Ganov86783472012-06-06 21:12:20 -0700527
Svetoslav Ganov758143e2012-08-06 16:40:27 -0700528 SomeArgs args = SomeArgs.obtain();
Svetoslav Ganov42138042012-03-20 11:51:39 -0700529 args.argi1 = AccessibilityNodeInfo.getVirtualDescendantId(accessibilityNodeId);
530 args.argi2 = action;
531 args.argi3 = interactionId;
532 args.arg1 = callback;
Svetoslav Ganovaa780c12012-04-19 23:01:39 -0700533 args.arg2 = arguments;
Svetoslav Ganov86783472012-06-06 21:12:20 -0700534
Svetoslav Ganov42138042012-03-20 11:51:39 -0700535 message.obj = args;
Svetoslav Ganov86783472012-06-06 21:12:20 -0700536
Svetoslav Ganov42138042012-03-20 11:51:39 -0700537 // If the interrogation is performed by the same thread as the main UI
538 // thread in this process, set the message as a static reference so
539 // after this call completes the same thread but in the interrogating
540 // client can handle the message to generate the result.
Svetoslav Ganov749e7962012-04-19 17:13:46 -0700541 if (interogatingPid == mMyProcessId && interrogatingTid == mMyLooperThreadId) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700542 AccessibilityInteractionClient.getInstanceForThread(
543 interrogatingTid).setSameThreadMessage(message);
544 } else {
545 mHandler.sendMessage(message);
546 }
547 }
548
549 private void perfromAccessibilityActionUiThread(Message message) {
550 final int flags = message.arg1;
551 final int accessibilityViewId = message.arg2;
Svetoslav Ganov86783472012-06-06 21:12:20 -0700552
Svetoslav Ganov42138042012-03-20 11:51:39 -0700553 SomeArgs args = (SomeArgs) message.obj;
554 final int virtualDescendantId = args.argi1;
555 final int action = args.argi2;
556 final int interactionId = args.argi3;
557 final IAccessibilityInteractionConnectionCallback callback =
558 (IAccessibilityInteractionConnectionCallback) args.arg1;
Svetoslav Ganovaa780c12012-04-19 23:01:39 -0700559 Bundle arguments = (Bundle) args.arg2;
Svetoslav Ganov86783472012-06-06 21:12:20 -0700560
Svetoslav Ganov758143e2012-08-06 16:40:27 -0700561 args.recycle();
Svetoslav Ganov86783472012-06-06 21:12:20 -0700562
Svetoslav Ganov42138042012-03-20 11:51:39 -0700563 boolean succeeded = false;
564 try {
565 if (mViewRootImpl.mView == null || mViewRootImpl.mAttachInfo == null) {
566 return;
567 }
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800568 mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = flags;
Svetoslav Ganov42138042012-03-20 11:51:39 -0700569 View target = null;
570 if (accessibilityViewId != AccessibilityNodeInfo.UNDEFINED) {
571 target = findViewByAccessibilityId(accessibilityViewId);
572 } else {
573 target = mViewRootImpl.mView;
574 }
Svetoslav Ganov0a1bb6d2012-05-07 11:54:39 -0700575 if (target != null && isShown(target)) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700576 AccessibilityNodeProvider provider = target.getAccessibilityNodeProvider();
577 if (provider != null) {
Svetoslav Ganovaa780c12012-04-19 23:01:39 -0700578 succeeded = provider.performAction(virtualDescendantId, action,
579 arguments);
Svetoslav Ganov42138042012-03-20 11:51:39 -0700580 } else if (virtualDescendantId == View.NO_ID) {
Svetoslav Ganovaa780c12012-04-19 23:01:39 -0700581 succeeded = target.performAccessibilityAction(action, arguments);
Svetoslav Ganov42138042012-03-20 11:51:39 -0700582 }
583 }
584 } finally {
585 try {
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800586 mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = 0;
Svetoslav Ganov42138042012-03-20 11:51:39 -0700587 callback.setPerformAccessibilityActionResult(succeeded, interactionId);
588 } catch (RemoteException re) {
589 /* ignore - the other side will time out */
590 }
591 }
592 }
593
594 private View findViewByAccessibilityId(int accessibilityId) {
595 View root = mViewRootImpl.mView;
596 if (root == null) {
597 return null;
598 }
599 View foundView = root.findViewByAccessibilityId(accessibilityId);
Svetoslav Ganov0a1bb6d2012-05-07 11:54:39 -0700600 if (foundView != null && !isShown(foundView)) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700601 return null;
602 }
603 return foundView;
604 }
605
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700606 private void applyAppScaleAndMagnificationSpecIfNeeded(List<AccessibilityNodeInfo> infos,
607 MagnificationSpec spec) {
Svetoslav Ganovfd8d9c42012-07-09 18:16:55 -0700608 if (infos == null) {
609 return;
610 }
611 final float applicationScale = mViewRootImpl.mAttachInfo.mApplicationScale;
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700612 if (shouldApplyAppScaleAndMagnificationSpec(applicationScale, spec)) {
Svetoslav Ganovfd8d9c42012-07-09 18:16:55 -0700613 final int infoCount = infos.size();
614 for (int i = 0; i < infoCount; i++) {
615 AccessibilityNodeInfo info = infos.get(i);
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700616 applyAppScaleAndMagnificationSpecIfNeeded(info, spec);
Svetoslav Ganovfd8d9c42012-07-09 18:16:55 -0700617 }
618 }
619 }
620
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700621 private void applyAppScaleAndMagnificationSpecIfNeeded(AccessibilityNodeInfo info,
622 MagnificationSpec spec) {
Svetoslav Ganovfd8d9c42012-07-09 18:16:55 -0700623 if (info == null) {
624 return;
625 }
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700626
Svetoslav Ganovfd8d9c42012-07-09 18:16:55 -0700627 final float applicationScale = mViewRootImpl.mAttachInfo.mApplicationScale;
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700628 if (!shouldApplyAppScaleAndMagnificationSpec(applicationScale, spec)) {
629 return;
630 }
631
632 Rect boundsInParent = mTempRect;
633 Rect boundsInScreen = mTempRect1;
634
635 info.getBoundsInParent(boundsInParent);
636 info.getBoundsInScreen(boundsInScreen);
Svetoslav Ganovfd8d9c42012-07-09 18:16:55 -0700637 if (applicationScale != 1.0f) {
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700638 boundsInParent.scale(applicationScale);
639 boundsInScreen.scale(applicationScale);
640 }
641 if (spec != null) {
642 boundsInParent.scale(spec.scale);
643 // boundsInParent must not be offset.
644 boundsInScreen.scale(spec.scale);
645 boundsInScreen.offset((int) spec.offsetX, (int) spec.offsetY);
646 }
647 info.setBoundsInParent(boundsInParent);
648 info.setBoundsInScreen(boundsInScreen);
Svetoslav Ganovfd8d9c42012-07-09 18:16:55 -0700649
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700650 if (spec != null) {
651 AttachInfo attachInfo = mViewRootImpl.mAttachInfo;
652 if (attachInfo.mDisplay == null) {
653 return;
654 }
Svetoslav Ganovfd8d9c42012-07-09 18:16:55 -0700655
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700656 final float scale = attachInfo.mApplicationScale * spec.scale;
657
658 Rect visibleWinFrame = mTempRect1;
659 visibleWinFrame.left = (int) (attachInfo.mWindowLeft * scale + spec.offsetX);
660 visibleWinFrame.top = (int) (attachInfo.mWindowTop * scale + spec.offsetY);
661 visibleWinFrame.right = (int) (visibleWinFrame.left + mViewRootImpl.mWidth * scale);
662 visibleWinFrame.bottom = (int) (visibleWinFrame.top + mViewRootImpl.mHeight * scale);
663
664 attachInfo.mDisplay.getRealSize(mTempPoint);
665 final int displayWidth = mTempPoint.x;
666 final int displayHeight = mTempPoint.y;
667
668 Rect visibleDisplayFrame = mTempRect2;
669 visibleDisplayFrame.set(0, 0, displayWidth, displayHeight);
670
671 visibleWinFrame.intersect(visibleDisplayFrame);
672
673 if (!visibleWinFrame.intersects(boundsInScreen.left, boundsInScreen.top,
674 boundsInScreen.right, boundsInScreen.bottom)) {
675 info.setVisibleToUser(false);
676 }
Svetoslav Ganovfd8d9c42012-07-09 18:16:55 -0700677 }
678 }
679
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700680 private boolean shouldApplyAppScaleAndMagnificationSpec(float appScale,
681 MagnificationSpec spec) {
682 return (appScale != 1.0f || (spec != null && !spec.isNop()));
683 }
Svetoslav Ganovfd8d9c42012-07-09 18:16:55 -0700684
Svetoslav Ganov42138042012-03-20 11:51:39 -0700685 /**
Svetoslav Ganov42138042012-03-20 11:51:39 -0700686 * This class encapsulates a prefetching strategy for the accessibility APIs for
687 * querying window content. It is responsible to prefetch a batch of
688 * AccessibilityNodeInfos in addition to the one for a requested node.
689 */
690 private class AccessibilityNodePrefetcher {
691
692 private static final int MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE = 50;
693
Svetoslav Ganov4528b4e2012-05-15 18:24:10 -0700694 private final ArrayList<View> mTempViewList = new ArrayList<View>();
695
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800696 public void prefetchAccessibilityNodeInfos(View view, int virtualViewId, int fetchFlags,
Svetoslav Ganov42138042012-03-20 11:51:39 -0700697 List<AccessibilityNodeInfo> outInfos) {
698 AccessibilityNodeProvider provider = view.getAccessibilityNodeProvider();
699 if (provider == null) {
700 AccessibilityNodeInfo root = view.createAccessibilityNodeInfo();
701 if (root != null) {
702 outInfos.add(root);
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800703 if ((fetchFlags & AccessibilityNodeInfo.FLAG_PREFETCH_PREDECESSORS) != 0) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700704 prefetchPredecessorsOfRealNode(view, outInfos);
705 }
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800706 if ((fetchFlags & AccessibilityNodeInfo.FLAG_PREFETCH_SIBLINGS) != 0) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700707 prefetchSiblingsOfRealNode(view, outInfos);
708 }
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800709 if ((fetchFlags & AccessibilityNodeInfo.FLAG_PREFETCH_DESCENDANTS) != 0) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700710 prefetchDescendantsOfRealNode(view, outInfos);
711 }
712 }
713 } else {
714 AccessibilityNodeInfo root = provider.createAccessibilityNodeInfo(virtualViewId);
715 if (root != null) {
716 outInfos.add(root);
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800717 if ((fetchFlags & AccessibilityNodeInfo.FLAG_PREFETCH_PREDECESSORS) != 0) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700718 prefetchPredecessorsOfVirtualNode(root, view, provider, outInfos);
719 }
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800720 if ((fetchFlags & AccessibilityNodeInfo.FLAG_PREFETCH_SIBLINGS) != 0) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700721 prefetchSiblingsOfVirtualNode(root, view, provider, outInfos);
722 }
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800723 if ((fetchFlags & AccessibilityNodeInfo.FLAG_PREFETCH_DESCENDANTS) != 0) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700724 prefetchDescendantsOfVirtualNode(root, provider, outInfos);
725 }
726 }
727 }
728 }
729
730 private void prefetchPredecessorsOfRealNode(View view,
731 List<AccessibilityNodeInfo> outInfos) {
732 ViewParent parent = view.getParentForAccessibility();
733 while (parent instanceof View
734 && outInfos.size() < MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE) {
735 View parentView = (View) parent;
Svetoslav Ganov42138042012-03-20 11:51:39 -0700736 AccessibilityNodeInfo info = parentView.createAccessibilityNodeInfo();
737 if (info != null) {
738 outInfos.add(info);
739 }
740 parent = parent.getParentForAccessibility();
741 }
742 }
743
744 private void prefetchSiblingsOfRealNode(View current,
745 List<AccessibilityNodeInfo> outInfos) {
746 ViewParent parent = current.getParentForAccessibility();
747 if (parent instanceof ViewGroup) {
748 ViewGroup parentGroup = (ViewGroup) parent;
Svetoslav Ganov4528b4e2012-05-15 18:24:10 -0700749 ArrayList<View> children = mTempViewList;
750 children.clear();
Svetoslav Ganov76f287e2012-04-23 11:02:36 -0700751 try {
Svetoslav Ganov4528b4e2012-05-15 18:24:10 -0700752 parentGroup.addChildrenForAccessibility(children);
753 final int childCount = children.size();
Svetoslav Ganov76f287e2012-04-23 11:02:36 -0700754 for (int i = 0; i < childCount; i++) {
755 if (outInfos.size() >= MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE) {
756 return;
Svetoslav Ganov42138042012-03-20 11:51:39 -0700757 }
Svetoslav Ganov4528b4e2012-05-15 18:24:10 -0700758 View child = children.get(i);
Svetoslav Ganov76f287e2012-04-23 11:02:36 -0700759 if (child.getAccessibilityViewId() != current.getAccessibilityViewId()
Svetoslav Ganov0a1bb6d2012-05-07 11:54:39 -0700760 && isShown(child)) {
Svetoslav Ganov76f287e2012-04-23 11:02:36 -0700761 AccessibilityNodeInfo info = null;
Svetoslav Ganov4528b4e2012-05-15 18:24:10 -0700762 AccessibilityNodeProvider provider =
763 child.getAccessibilityNodeProvider();
Svetoslav Ganov76f287e2012-04-23 11:02:36 -0700764 if (provider == null) {
765 info = child.createAccessibilityNodeInfo();
766 } else {
767 info = provider.createAccessibilityNodeInfo(
768 AccessibilityNodeInfo.UNDEFINED);
769 }
770 if (info != null) {
771 outInfos.add(info);
772 }
Svetoslav Ganov42138042012-03-20 11:51:39 -0700773 }
774 }
Svetoslav Ganov76f287e2012-04-23 11:02:36 -0700775 } finally {
Svetoslav Ganov4528b4e2012-05-15 18:24:10 -0700776 children.clear();
Svetoslav Ganov42138042012-03-20 11:51:39 -0700777 }
Svetoslav Ganov42138042012-03-20 11:51:39 -0700778 }
779 }
780
781 private void prefetchDescendantsOfRealNode(View root,
782 List<AccessibilityNodeInfo> outInfos) {
783 if (!(root instanceof ViewGroup)) {
784 return;
785 }
Svetoslav Ganov42138042012-03-20 11:51:39 -0700786 HashMap<View, AccessibilityNodeInfo> addedChildren =
787 new HashMap<View, AccessibilityNodeInfo>();
Svetoslav Ganov4528b4e2012-05-15 18:24:10 -0700788 ArrayList<View> children = mTempViewList;
789 children.clear();
Svetoslav Ganov76f287e2012-04-23 11:02:36 -0700790 try {
Svetoslav Ganov4528b4e2012-05-15 18:24:10 -0700791 root.addChildrenForAccessibility(children);
792 final int childCount = children.size();
Svetoslav Ganov76f287e2012-04-23 11:02:36 -0700793 for (int i = 0; i < childCount; i++) {
794 if (outInfos.size() >= MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE) {
795 return;
796 }
Svetoslav Ganov4528b4e2012-05-15 18:24:10 -0700797 View child = children.get(i);
Svetoslav Ganov0a1bb6d2012-05-07 11:54:39 -0700798 if (isShown(child)) {
Svetoslav Ganov76f287e2012-04-23 11:02:36 -0700799 AccessibilityNodeProvider provider = child.getAccessibilityNodeProvider();
800 if (provider == null) {
801 AccessibilityNodeInfo info = child.createAccessibilityNodeInfo();
802 if (info != null) {
803 outInfos.add(info);
804 addedChildren.put(child, null);
805 }
806 } else {
807 AccessibilityNodeInfo info = provider.createAccessibilityNodeInfo(
808 AccessibilityNodeInfo.UNDEFINED);
809 if (info != null) {
810 outInfos.add(info);
811 addedChildren.put(child, info);
812 }
Svetoslav Ganov42138042012-03-20 11:51:39 -0700813 }
814 }
815 }
Svetoslav Ganov76f287e2012-04-23 11:02:36 -0700816 } finally {
Svetoslav Ganov4528b4e2012-05-15 18:24:10 -0700817 children.clear();
Svetoslav Ganov42138042012-03-20 11:51:39 -0700818 }
Svetoslav Ganov42138042012-03-20 11:51:39 -0700819 if (outInfos.size() < MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE) {
820 for (Map.Entry<View, AccessibilityNodeInfo> entry : addedChildren.entrySet()) {
821 View addedChild = entry.getKey();
822 AccessibilityNodeInfo virtualRoot = entry.getValue();
823 if (virtualRoot == null) {
824 prefetchDescendantsOfRealNode(addedChild, outInfos);
825 } else {
826 AccessibilityNodeProvider provider =
827 addedChild.getAccessibilityNodeProvider();
828 prefetchDescendantsOfVirtualNode(virtualRoot, provider, outInfos);
829 }
830 }
831 }
832 }
833
834 private void prefetchPredecessorsOfVirtualNode(AccessibilityNodeInfo root,
835 View providerHost, AccessibilityNodeProvider provider,
836 List<AccessibilityNodeInfo> outInfos) {
837 long parentNodeId = root.getParentNodeId();
838 int accessibilityViewId = AccessibilityNodeInfo.getAccessibilityViewId(parentNodeId);
839 while (accessibilityViewId != AccessibilityNodeInfo.UNDEFINED) {
840 if (outInfos.size() >= MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE) {
841 return;
842 }
843 final int virtualDescendantId =
844 AccessibilityNodeInfo.getVirtualDescendantId(parentNodeId);
845 if (virtualDescendantId != AccessibilityNodeInfo.UNDEFINED
846 || accessibilityViewId == providerHost.getAccessibilityViewId()) {
847 AccessibilityNodeInfo parent = provider.createAccessibilityNodeInfo(
848 virtualDescendantId);
849 if (parent != null) {
850 outInfos.add(parent);
851 }
852 parentNodeId = parent.getParentNodeId();
853 accessibilityViewId = AccessibilityNodeInfo.getAccessibilityViewId(
854 parentNodeId);
855 } else {
856 prefetchPredecessorsOfRealNode(providerHost, outInfos);
857 return;
858 }
859 }
860 }
861
862 private void prefetchSiblingsOfVirtualNode(AccessibilityNodeInfo current, View providerHost,
863 AccessibilityNodeProvider provider, List<AccessibilityNodeInfo> outInfos) {
864 final long parentNodeId = current.getParentNodeId();
865 final int parentAccessibilityViewId =
866 AccessibilityNodeInfo.getAccessibilityViewId(parentNodeId);
867 final int parentVirtualDescendantId =
868 AccessibilityNodeInfo.getVirtualDescendantId(parentNodeId);
869 if (parentVirtualDescendantId != AccessibilityNodeInfo.UNDEFINED
870 || parentAccessibilityViewId == providerHost.getAccessibilityViewId()) {
871 AccessibilityNodeInfo parent =
872 provider.createAccessibilityNodeInfo(parentVirtualDescendantId);
873 if (parent != null) {
874 SparseLongArray childNodeIds = parent.getChildNodeIds();
875 final int childCount = childNodeIds.size();
876 for (int i = 0; i < childCount; i++) {
877 if (outInfos.size() >= MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE) {
878 return;
879 }
880 final long childNodeId = childNodeIds.get(i);
881 if (childNodeId != current.getSourceNodeId()) {
882 final int childVirtualDescendantId =
883 AccessibilityNodeInfo.getVirtualDescendantId(childNodeId);
884 AccessibilityNodeInfo child = provider.createAccessibilityNodeInfo(
885 childVirtualDescendantId);
886 if (child != null) {
887 outInfos.add(child);
888 }
889 }
890 }
891 }
892 } else {
893 prefetchSiblingsOfRealNode(providerHost, outInfos);
894 }
895 }
896
897 private void prefetchDescendantsOfVirtualNode(AccessibilityNodeInfo root,
898 AccessibilityNodeProvider provider, List<AccessibilityNodeInfo> outInfos) {
899 SparseLongArray childNodeIds = root.getChildNodeIds();
900 final int initialOutInfosSize = outInfos.size();
901 final int childCount = childNodeIds.size();
902 for (int i = 0; i < childCount; i++) {
903 if (outInfos.size() >= MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE) {
904 return;
905 }
906 final long childNodeId = childNodeIds.get(i);
907 AccessibilityNodeInfo child = provider.createAccessibilityNodeInfo(
908 AccessibilityNodeInfo.getVirtualDescendantId(childNodeId));
909 if (child != null) {
910 outInfos.add(child);
911 }
912 }
913 if (outInfos.size() < MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE) {
914 final int addedChildCount = outInfos.size() - initialOutInfosSize;
915 for (int i = 0; i < addedChildCount; i++) {
916 AccessibilityNodeInfo child = outInfos.get(initialOutInfosSize + i);
917 prefetchDescendantsOfVirtualNode(child, provider, outInfos);
918 }
919 }
920 }
921 }
922
923 private class PrivateHandler extends Handler {
924 private final static int MSG_PERFORM_ACCESSIBILITY_ACTION = 1;
925 private final static int MSG_FIND_ACCESSIBLITY_NODE_INFO_BY_ACCESSIBILITY_ID = 2;
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800926 private final static int MSG_FIND_ACCESSIBLITY_NODE_INFOS_BY_VIEW_ID = 3;
Svetoslav Ganov42138042012-03-20 11:51:39 -0700927 private final static int MSG_FIND_ACCESSIBLITY_NODE_INFO_BY_TEXT = 4;
928 private final static int MSG_FIND_FOCUS = 5;
929 private final static int MSG_FOCUS_SEARCH = 6;
930
Svetoslav Ganov005b83b2012-04-16 18:17:17 -0700931 public PrivateHandler(Looper looper) {
932 super(looper);
Svetoslav Ganov42138042012-03-20 11:51:39 -0700933 }
934
935 @Override
936 public String getMessageName(Message message) {
937 final int type = message.what;
938 switch (type) {
939 case MSG_PERFORM_ACCESSIBILITY_ACTION:
940 return "MSG_PERFORM_ACCESSIBILITY_ACTION";
941 case MSG_FIND_ACCESSIBLITY_NODE_INFO_BY_ACCESSIBILITY_ID:
942 return "MSG_FIND_ACCESSIBLITY_NODE_INFO_BY_ACCESSIBILITY_ID";
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800943 case MSG_FIND_ACCESSIBLITY_NODE_INFOS_BY_VIEW_ID:
944 return "MSG_FIND_ACCESSIBLITY_NODE_INFOS_BY_VIEW_ID";
Svetoslav Ganov42138042012-03-20 11:51:39 -0700945 case MSG_FIND_ACCESSIBLITY_NODE_INFO_BY_TEXT:
946 return "MSG_FIND_ACCESSIBLITY_NODE_INFO_BY_TEXT";
947 case MSG_FIND_FOCUS:
948 return "MSG_FIND_FOCUS";
949 case MSG_FOCUS_SEARCH:
950 return "MSG_FOCUS_SEARCH";
951 default:
952 throw new IllegalArgumentException("Unknown message type: " + type);
953 }
954 }
955
956 @Override
957 public void handleMessage(Message message) {
958 final int type = message.what;
959 switch (type) {
960 case MSG_FIND_ACCESSIBLITY_NODE_INFO_BY_ACCESSIBILITY_ID: {
961 findAccessibilityNodeInfoByAccessibilityIdUiThread(message);
962 } break;
963 case MSG_PERFORM_ACCESSIBILITY_ACTION: {
964 perfromAccessibilityActionUiThread(message);
965 } break;
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800966 case MSG_FIND_ACCESSIBLITY_NODE_INFOS_BY_VIEW_ID: {
967 findAccessibilityNodeInfosByViewIdUiThread(message);
Svetoslav Ganov42138042012-03-20 11:51:39 -0700968 } break;
969 case MSG_FIND_ACCESSIBLITY_NODE_INFO_BY_TEXT: {
970 findAccessibilityNodeInfosByTextUiThread(message);
971 } break;
972 case MSG_FIND_FOCUS: {
973 findFocusUiThread(message);
974 } break;
975 case MSG_FOCUS_SEARCH: {
976 focusSearchUiThread(message);
977 } break;
978 default:
979 throw new IllegalArgumentException("Unknown message type: " + type);
980 }
981 }
982 }
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800983
984 private final class AddNodeInfosForViewId implements Predicate<View> {
985 private int mViewId = View.NO_ID;
986 private List<AccessibilityNodeInfo> mInfos;
987
988 public void init(int viewId, List<AccessibilityNodeInfo> infos) {
989 mViewId = viewId;
990 mInfos = infos;
991 }
992
993 public void reset() {
994 mViewId = View.NO_ID;
995 mInfos = null;
996 }
997
998 @Override
999 public boolean apply(View view) {
1000 if (view.getId() == mViewId && isShown(view)) {
1001 mInfos.add(view.createAccessibilityNodeInfo());
1002 }
1003 return false;
1004 }
1005 }
Svetoslav Ganov42138042012-03-20 11:51:39 -07001006}