blob: 41d37001b84889252f03066e9dd5cbd60fb8de5c [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 }
Svetoslav6254f482013-06-04 17:22:14 -0700409 // The focused view not shown, we failed.
410 if (!isShown(host)) {
411 break;
412 }
Svetoslav Ganov42138042012-03-20 11:51:39 -0700413 // If the host has a provider ask this provider to search for the
414 // focus instead fetching all provider nodes to do the search here.
415 AccessibilityNodeProvider provider = host.getAccessibilityNodeProvider();
416 if (provider != null) {
Svetoslav Ganov45a02e02012-06-17 15:07:29 -0700417 if (mViewRootImpl.mAccessibilityFocusedVirtualView != null) {
418 focused = AccessibilityNodeInfo.obtain(
419 mViewRootImpl.mAccessibilityFocusedVirtualView);
420 }
Svetoslav Ganov42138042012-03-20 11:51:39 -0700421 } else if (virtualDescendantId == View.NO_ID) {
422 focused = host.createAccessibilityNodeInfo();
423 }
424 } break;
425 case AccessibilityNodeInfo.FOCUS_INPUT: {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700426 View target = root.findFocus();
Alan Viverette2e1e0812013-09-30 13:45:55 -0700427 if (target == null || !isShown(target)) {
428 break;
429 }
430 AccessibilityNodeProvider provider = target.getAccessibilityNodeProvider();
431 if (provider != null) {
432 focused = provider.findFocus(focusType);
433 }
434 if (focused == null) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700435 focused = target.createAccessibilityNodeInfo();
436 }
437 } break;
438 default:
439 throw new IllegalArgumentException("Unknown focus type: " + focusType);
440 }
441 }
442 } finally {
443 try {
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800444 mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = 0;
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700445 applyAppScaleAndMagnificationSpecIfNeeded(focused, spec);
446 if (spec != null) {
447 spec.recycle();
448 }
Svetoslav Ganov42138042012-03-20 11:51:39 -0700449 callback.setFindAccessibilityNodeInfoResult(focused, interactionId);
450 } catch (RemoteException re) {
451 /* ignore - the other side will time out */
452 }
453 }
454 }
455
Svetoslav Ganovc9c9a482012-07-16 08:46:07 -0700456 public void focusSearchClientThread(long accessibilityNodeId, int direction, int interactionId,
457 IAccessibilityInteractionConnectionCallback callback, int flags, int interogatingPid,
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700458 long interrogatingTid, MagnificationSpec spec) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700459 Message message = mHandler.obtainMessage();
460 message.what = PrivateHandler.MSG_FOCUS_SEARCH;
461 message.arg1 = flags;
462 message.arg2 = AccessibilityNodeInfo.getAccessibilityViewId(accessibilityNodeId);
Svetoslav Ganov86783472012-06-06 21:12:20 -0700463
Svetoslav Ganov758143e2012-08-06 16:40:27 -0700464 SomeArgs args = SomeArgs.obtain();
Svetoslav Ganov42138042012-03-20 11:51:39 -0700465 args.argi2 = direction;
466 args.argi3 = interactionId;
467 args.arg1 = callback;
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700468 args.arg2 = spec;
Svetoslav Ganov86783472012-06-06 21:12:20 -0700469
Svetoslav Ganov42138042012-03-20 11:51:39 -0700470 message.obj = args;
Svetoslav Ganov86783472012-06-06 21:12:20 -0700471
Svetoslav Ganov42138042012-03-20 11:51:39 -0700472 // If the interrogation is performed by the same thread as the main UI
473 // thread in this process, set the message as a static reference so
474 // after this call completes the same thread but in the interrogating
475 // client can handle the message to generate the result.
Svetoslav Ganov749e7962012-04-19 17:13:46 -0700476 if (interogatingPid == mMyProcessId && interrogatingTid == mMyLooperThreadId) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700477 AccessibilityInteractionClient.getInstanceForThread(
478 interrogatingTid).setSameThreadMessage(message);
479 } else {
480 mHandler.sendMessage(message);
481 }
482 }
483
484 private void focusSearchUiThread(Message message) {
485 final int flags = message.arg1;
486 final int accessibilityViewId = message.arg2;
Svetoslav Ganov86783472012-06-06 21:12:20 -0700487
Svetoslav Ganov42138042012-03-20 11:51:39 -0700488 SomeArgs args = (SomeArgs) message.obj;
Svetoslav Ganov42138042012-03-20 11:51:39 -0700489 final int direction = args.argi2;
490 final int interactionId = args.argi3;
491 final IAccessibilityInteractionConnectionCallback callback =
492 (IAccessibilityInteractionConnectionCallback) args.arg1;
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700493 final MagnificationSpec spec = (MagnificationSpec) args.arg2;
Svetoslav Ganov86783472012-06-06 21:12:20 -0700494
Svetoslav Ganov758143e2012-08-06 16:40:27 -0700495 args.recycle();
Svetoslav Ganov86783472012-06-06 21:12:20 -0700496
Svetoslav Ganov42138042012-03-20 11:51:39 -0700497 AccessibilityNodeInfo next = null;
498 try {
499 if (mViewRootImpl.mView == null || mViewRootImpl.mAttachInfo == null) {
500 return;
501 }
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800502 mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = flags;
Svetoslav Ganov42138042012-03-20 11:51:39 -0700503 View root = null;
504 if (accessibilityViewId != AccessibilityNodeInfo.UNDEFINED) {
505 root = findViewByAccessibilityId(accessibilityViewId);
506 } else {
507 root = mViewRootImpl.mView;
508 }
Svetoslav Ganov0a1bb6d2012-05-07 11:54:39 -0700509 if (root != null && isShown(root)) {
Svetoslav Ganov27e2da72012-07-02 18:12:00 -0700510 View nextView = root.focusSearch(direction);
511 if (nextView != null) {
512 next = nextView.createAccessibilityNodeInfo();
Svetoslav Ganov42138042012-03-20 11:51:39 -0700513 }
514 }
515 } finally {
516 try {
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800517 mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = 0;
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700518 applyAppScaleAndMagnificationSpecIfNeeded(next, spec);
519 if (spec != null) {
520 spec.recycle();
521 }
Svetoslav Ganov42138042012-03-20 11:51:39 -0700522 callback.setFindAccessibilityNodeInfoResult(next, interactionId);
523 } catch (RemoteException re) {
524 /* ignore - the other side will time out */
525 }
526 }
527 }
528
529 public void performAccessibilityActionClientThread(long accessibilityNodeId, int action,
Svetoslav Ganovaa780c12012-04-19 23:01:39 -0700530 Bundle arguments, int interactionId,
531 IAccessibilityInteractionConnectionCallback callback, int flags, int interogatingPid,
532 long interrogatingTid) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700533 Message message = mHandler.obtainMessage();
534 message.what = PrivateHandler.MSG_PERFORM_ACCESSIBILITY_ACTION;
535 message.arg1 = flags;
536 message.arg2 = AccessibilityNodeInfo.getAccessibilityViewId(accessibilityNodeId);
Svetoslav Ganov86783472012-06-06 21:12:20 -0700537
Svetoslav Ganov758143e2012-08-06 16:40:27 -0700538 SomeArgs args = SomeArgs.obtain();
Svetoslav Ganov42138042012-03-20 11:51:39 -0700539 args.argi1 = AccessibilityNodeInfo.getVirtualDescendantId(accessibilityNodeId);
540 args.argi2 = action;
541 args.argi3 = interactionId;
542 args.arg1 = callback;
Svetoslav Ganovaa780c12012-04-19 23:01:39 -0700543 args.arg2 = arguments;
Svetoslav Ganov86783472012-06-06 21:12:20 -0700544
Svetoslav Ganov42138042012-03-20 11:51:39 -0700545 message.obj = args;
Svetoslav Ganov86783472012-06-06 21:12:20 -0700546
Svetoslav Ganov42138042012-03-20 11:51:39 -0700547 // If the interrogation is performed by the same thread as the main UI
548 // thread in this process, set the message as a static reference so
549 // after this call completes the same thread but in the interrogating
550 // client can handle the message to generate the result.
Svetoslav Ganov749e7962012-04-19 17:13:46 -0700551 if (interogatingPid == mMyProcessId && interrogatingTid == mMyLooperThreadId) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700552 AccessibilityInteractionClient.getInstanceForThread(
553 interrogatingTid).setSameThreadMessage(message);
554 } else {
555 mHandler.sendMessage(message);
556 }
557 }
558
559 private void perfromAccessibilityActionUiThread(Message message) {
560 final int flags = message.arg1;
561 final int accessibilityViewId = message.arg2;
Svetoslav Ganov86783472012-06-06 21:12:20 -0700562
Svetoslav Ganov42138042012-03-20 11:51:39 -0700563 SomeArgs args = (SomeArgs) message.obj;
564 final int virtualDescendantId = args.argi1;
565 final int action = args.argi2;
566 final int interactionId = args.argi3;
567 final IAccessibilityInteractionConnectionCallback callback =
568 (IAccessibilityInteractionConnectionCallback) args.arg1;
Svetoslav Ganovaa780c12012-04-19 23:01:39 -0700569 Bundle arguments = (Bundle) args.arg2;
Svetoslav Ganov86783472012-06-06 21:12:20 -0700570
Svetoslav Ganov758143e2012-08-06 16:40:27 -0700571 args.recycle();
Svetoslav Ganov86783472012-06-06 21:12:20 -0700572
Svetoslav Ganov42138042012-03-20 11:51:39 -0700573 boolean succeeded = false;
574 try {
575 if (mViewRootImpl.mView == null || mViewRootImpl.mAttachInfo == null) {
576 return;
577 }
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800578 mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = flags;
Svetoslav Ganov42138042012-03-20 11:51:39 -0700579 View target = null;
580 if (accessibilityViewId != AccessibilityNodeInfo.UNDEFINED) {
581 target = findViewByAccessibilityId(accessibilityViewId);
582 } else {
583 target = mViewRootImpl.mView;
584 }
Svetoslav Ganov0a1bb6d2012-05-07 11:54:39 -0700585 if (target != null && isShown(target)) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700586 AccessibilityNodeProvider provider = target.getAccessibilityNodeProvider();
587 if (provider != null) {
Svetoslav Ganovaa780c12012-04-19 23:01:39 -0700588 succeeded = provider.performAction(virtualDescendantId, action,
589 arguments);
Svetoslav Ganov42138042012-03-20 11:51:39 -0700590 } else if (virtualDescendantId == View.NO_ID) {
Svetoslav Ganovaa780c12012-04-19 23:01:39 -0700591 succeeded = target.performAccessibilityAction(action, arguments);
Svetoslav Ganov42138042012-03-20 11:51:39 -0700592 }
593 }
594 } finally {
595 try {
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800596 mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = 0;
Svetoslav Ganov42138042012-03-20 11:51:39 -0700597 callback.setPerformAccessibilityActionResult(succeeded, interactionId);
598 } catch (RemoteException re) {
599 /* ignore - the other side will time out */
600 }
601 }
602 }
603
604 private View findViewByAccessibilityId(int accessibilityId) {
605 View root = mViewRootImpl.mView;
606 if (root == null) {
607 return null;
608 }
609 View foundView = root.findViewByAccessibilityId(accessibilityId);
Svetoslav Ganov0a1bb6d2012-05-07 11:54:39 -0700610 if (foundView != null && !isShown(foundView)) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700611 return null;
612 }
613 return foundView;
614 }
615
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700616 private void applyAppScaleAndMagnificationSpecIfNeeded(List<AccessibilityNodeInfo> infos,
617 MagnificationSpec spec) {
Svetoslav Ganovfd8d9c42012-07-09 18:16:55 -0700618 if (infos == null) {
619 return;
620 }
621 final float applicationScale = mViewRootImpl.mAttachInfo.mApplicationScale;
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700622 if (shouldApplyAppScaleAndMagnificationSpec(applicationScale, spec)) {
Svetoslav Ganovfd8d9c42012-07-09 18:16:55 -0700623 final int infoCount = infos.size();
624 for (int i = 0; i < infoCount; i++) {
625 AccessibilityNodeInfo info = infos.get(i);
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700626 applyAppScaleAndMagnificationSpecIfNeeded(info, spec);
Svetoslav Ganovfd8d9c42012-07-09 18:16:55 -0700627 }
628 }
629 }
630
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700631 private void applyAppScaleAndMagnificationSpecIfNeeded(AccessibilityNodeInfo info,
632 MagnificationSpec spec) {
Svetoslav Ganovfd8d9c42012-07-09 18:16:55 -0700633 if (info == null) {
634 return;
635 }
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700636
Svetoslav Ganovfd8d9c42012-07-09 18:16:55 -0700637 final float applicationScale = mViewRootImpl.mAttachInfo.mApplicationScale;
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700638 if (!shouldApplyAppScaleAndMagnificationSpec(applicationScale, spec)) {
639 return;
640 }
641
642 Rect boundsInParent = mTempRect;
643 Rect boundsInScreen = mTempRect1;
644
645 info.getBoundsInParent(boundsInParent);
646 info.getBoundsInScreen(boundsInScreen);
Svetoslav Ganovfd8d9c42012-07-09 18:16:55 -0700647 if (applicationScale != 1.0f) {
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700648 boundsInParent.scale(applicationScale);
649 boundsInScreen.scale(applicationScale);
650 }
651 if (spec != null) {
652 boundsInParent.scale(spec.scale);
653 // boundsInParent must not be offset.
654 boundsInScreen.scale(spec.scale);
655 boundsInScreen.offset((int) spec.offsetX, (int) spec.offsetY);
656 }
657 info.setBoundsInParent(boundsInParent);
658 info.setBoundsInScreen(boundsInScreen);
Svetoslav Ganovfd8d9c42012-07-09 18:16:55 -0700659
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700660 if (spec != null) {
661 AttachInfo attachInfo = mViewRootImpl.mAttachInfo;
662 if (attachInfo.mDisplay == null) {
663 return;
664 }
Svetoslav Ganovfd8d9c42012-07-09 18:16:55 -0700665
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700666 final float scale = attachInfo.mApplicationScale * spec.scale;
667
668 Rect visibleWinFrame = mTempRect1;
669 visibleWinFrame.left = (int) (attachInfo.mWindowLeft * scale + spec.offsetX);
670 visibleWinFrame.top = (int) (attachInfo.mWindowTop * scale + spec.offsetY);
671 visibleWinFrame.right = (int) (visibleWinFrame.left + mViewRootImpl.mWidth * scale);
672 visibleWinFrame.bottom = (int) (visibleWinFrame.top + mViewRootImpl.mHeight * scale);
673
674 attachInfo.mDisplay.getRealSize(mTempPoint);
675 final int displayWidth = mTempPoint.x;
676 final int displayHeight = mTempPoint.y;
677
678 Rect visibleDisplayFrame = mTempRect2;
679 visibleDisplayFrame.set(0, 0, displayWidth, displayHeight);
680
681 visibleWinFrame.intersect(visibleDisplayFrame);
682
683 if (!visibleWinFrame.intersects(boundsInScreen.left, boundsInScreen.top,
684 boundsInScreen.right, boundsInScreen.bottom)) {
685 info.setVisibleToUser(false);
686 }
Svetoslav Ganovfd8d9c42012-07-09 18:16:55 -0700687 }
688 }
689
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700690 private boolean shouldApplyAppScaleAndMagnificationSpec(float appScale,
691 MagnificationSpec spec) {
692 return (appScale != 1.0f || (spec != null && !spec.isNop()));
693 }
Svetoslav Ganovfd8d9c42012-07-09 18:16:55 -0700694
Svetoslav Ganov42138042012-03-20 11:51:39 -0700695 /**
Svetoslav Ganov42138042012-03-20 11:51:39 -0700696 * This class encapsulates a prefetching strategy for the accessibility APIs for
697 * querying window content. It is responsible to prefetch a batch of
698 * AccessibilityNodeInfos in addition to the one for a requested node.
699 */
700 private class AccessibilityNodePrefetcher {
701
702 private static final int MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE = 50;
703
Svetoslav Ganov4528b4e2012-05-15 18:24:10 -0700704 private final ArrayList<View> mTempViewList = new ArrayList<View>();
705
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800706 public void prefetchAccessibilityNodeInfos(View view, int virtualViewId, int fetchFlags,
Svetoslav Ganov42138042012-03-20 11:51:39 -0700707 List<AccessibilityNodeInfo> outInfos) {
708 AccessibilityNodeProvider provider = view.getAccessibilityNodeProvider();
709 if (provider == null) {
710 AccessibilityNodeInfo root = view.createAccessibilityNodeInfo();
711 if (root != null) {
712 outInfos.add(root);
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800713 if ((fetchFlags & AccessibilityNodeInfo.FLAG_PREFETCH_PREDECESSORS) != 0) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700714 prefetchPredecessorsOfRealNode(view, outInfos);
715 }
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800716 if ((fetchFlags & AccessibilityNodeInfo.FLAG_PREFETCH_SIBLINGS) != 0) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700717 prefetchSiblingsOfRealNode(view, outInfos);
718 }
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800719 if ((fetchFlags & AccessibilityNodeInfo.FLAG_PREFETCH_DESCENDANTS) != 0) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700720 prefetchDescendantsOfRealNode(view, outInfos);
721 }
722 }
723 } else {
724 AccessibilityNodeInfo root = provider.createAccessibilityNodeInfo(virtualViewId);
725 if (root != null) {
726 outInfos.add(root);
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800727 if ((fetchFlags & AccessibilityNodeInfo.FLAG_PREFETCH_PREDECESSORS) != 0) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700728 prefetchPredecessorsOfVirtualNode(root, view, provider, outInfos);
729 }
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800730 if ((fetchFlags & AccessibilityNodeInfo.FLAG_PREFETCH_SIBLINGS) != 0) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700731 prefetchSiblingsOfVirtualNode(root, view, provider, outInfos);
732 }
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800733 if ((fetchFlags & AccessibilityNodeInfo.FLAG_PREFETCH_DESCENDANTS) != 0) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700734 prefetchDescendantsOfVirtualNode(root, provider, outInfos);
735 }
736 }
737 }
738 }
739
740 private void prefetchPredecessorsOfRealNode(View view,
741 List<AccessibilityNodeInfo> outInfos) {
742 ViewParent parent = view.getParentForAccessibility();
743 while (parent instanceof View
744 && outInfos.size() < MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE) {
745 View parentView = (View) parent;
Svetoslav Ganov42138042012-03-20 11:51:39 -0700746 AccessibilityNodeInfo info = parentView.createAccessibilityNodeInfo();
747 if (info != null) {
748 outInfos.add(info);
749 }
750 parent = parent.getParentForAccessibility();
751 }
752 }
753
754 private void prefetchSiblingsOfRealNode(View current,
755 List<AccessibilityNodeInfo> outInfos) {
756 ViewParent parent = current.getParentForAccessibility();
757 if (parent instanceof ViewGroup) {
758 ViewGroup parentGroup = (ViewGroup) parent;
Svetoslav Ganov4528b4e2012-05-15 18:24:10 -0700759 ArrayList<View> children = mTempViewList;
760 children.clear();
Svetoslav Ganov76f287e2012-04-23 11:02:36 -0700761 try {
Svetoslav Ganov4528b4e2012-05-15 18:24:10 -0700762 parentGroup.addChildrenForAccessibility(children);
763 final int childCount = children.size();
Svetoslav Ganov76f287e2012-04-23 11:02:36 -0700764 for (int i = 0; i < childCount; i++) {
765 if (outInfos.size() >= MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE) {
766 return;
Svetoslav Ganov42138042012-03-20 11:51:39 -0700767 }
Svetoslav Ganov4528b4e2012-05-15 18:24:10 -0700768 View child = children.get(i);
Svetoslav Ganov76f287e2012-04-23 11:02:36 -0700769 if (child.getAccessibilityViewId() != current.getAccessibilityViewId()
Svetoslav Ganov0a1bb6d2012-05-07 11:54:39 -0700770 && isShown(child)) {
Svetoslav Ganov76f287e2012-04-23 11:02:36 -0700771 AccessibilityNodeInfo info = null;
Svetoslav Ganov4528b4e2012-05-15 18:24:10 -0700772 AccessibilityNodeProvider provider =
773 child.getAccessibilityNodeProvider();
Svetoslav Ganov76f287e2012-04-23 11:02:36 -0700774 if (provider == null) {
775 info = child.createAccessibilityNodeInfo();
776 } else {
777 info = provider.createAccessibilityNodeInfo(
778 AccessibilityNodeInfo.UNDEFINED);
779 }
780 if (info != null) {
781 outInfos.add(info);
782 }
Svetoslav Ganov42138042012-03-20 11:51:39 -0700783 }
784 }
Svetoslav Ganov76f287e2012-04-23 11:02:36 -0700785 } finally {
Svetoslav Ganov4528b4e2012-05-15 18:24:10 -0700786 children.clear();
Svetoslav Ganov42138042012-03-20 11:51:39 -0700787 }
Svetoslav Ganov42138042012-03-20 11:51:39 -0700788 }
789 }
790
791 private void prefetchDescendantsOfRealNode(View root,
792 List<AccessibilityNodeInfo> outInfos) {
793 if (!(root instanceof ViewGroup)) {
794 return;
795 }
Svetoslav Ganov42138042012-03-20 11:51:39 -0700796 HashMap<View, AccessibilityNodeInfo> addedChildren =
797 new HashMap<View, AccessibilityNodeInfo>();
Svetoslav Ganov4528b4e2012-05-15 18:24:10 -0700798 ArrayList<View> children = mTempViewList;
799 children.clear();
Svetoslav Ganov76f287e2012-04-23 11:02:36 -0700800 try {
Svetoslav Ganov4528b4e2012-05-15 18:24:10 -0700801 root.addChildrenForAccessibility(children);
802 final int childCount = children.size();
Svetoslav Ganov76f287e2012-04-23 11:02:36 -0700803 for (int i = 0; i < childCount; i++) {
804 if (outInfos.size() >= MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE) {
805 return;
806 }
Svetoslav Ganov4528b4e2012-05-15 18:24:10 -0700807 View child = children.get(i);
Svetoslav Ganov0a1bb6d2012-05-07 11:54:39 -0700808 if (isShown(child)) {
Svetoslav Ganov76f287e2012-04-23 11:02:36 -0700809 AccessibilityNodeProvider provider = child.getAccessibilityNodeProvider();
810 if (provider == null) {
811 AccessibilityNodeInfo info = child.createAccessibilityNodeInfo();
812 if (info != null) {
813 outInfos.add(info);
814 addedChildren.put(child, null);
815 }
816 } else {
817 AccessibilityNodeInfo info = provider.createAccessibilityNodeInfo(
818 AccessibilityNodeInfo.UNDEFINED);
819 if (info != null) {
820 outInfos.add(info);
821 addedChildren.put(child, info);
822 }
Svetoslav Ganov42138042012-03-20 11:51:39 -0700823 }
824 }
825 }
Svetoslav Ganov76f287e2012-04-23 11:02:36 -0700826 } finally {
Svetoslav Ganov4528b4e2012-05-15 18:24:10 -0700827 children.clear();
Svetoslav Ganov42138042012-03-20 11:51:39 -0700828 }
Svetoslav Ganov42138042012-03-20 11:51:39 -0700829 if (outInfos.size() < MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE) {
830 for (Map.Entry<View, AccessibilityNodeInfo> entry : addedChildren.entrySet()) {
831 View addedChild = entry.getKey();
832 AccessibilityNodeInfo virtualRoot = entry.getValue();
833 if (virtualRoot == null) {
834 prefetchDescendantsOfRealNode(addedChild, outInfos);
835 } else {
836 AccessibilityNodeProvider provider =
837 addedChild.getAccessibilityNodeProvider();
838 prefetchDescendantsOfVirtualNode(virtualRoot, provider, outInfos);
839 }
840 }
841 }
842 }
843
844 private void prefetchPredecessorsOfVirtualNode(AccessibilityNodeInfo root,
845 View providerHost, AccessibilityNodeProvider provider,
846 List<AccessibilityNodeInfo> outInfos) {
847 long parentNodeId = root.getParentNodeId();
848 int accessibilityViewId = AccessibilityNodeInfo.getAccessibilityViewId(parentNodeId);
849 while (accessibilityViewId != AccessibilityNodeInfo.UNDEFINED) {
850 if (outInfos.size() >= MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE) {
851 return;
852 }
853 final int virtualDescendantId =
854 AccessibilityNodeInfo.getVirtualDescendantId(parentNodeId);
855 if (virtualDescendantId != AccessibilityNodeInfo.UNDEFINED
856 || accessibilityViewId == providerHost.getAccessibilityViewId()) {
857 AccessibilityNodeInfo parent = provider.createAccessibilityNodeInfo(
858 virtualDescendantId);
859 if (parent != null) {
860 outInfos.add(parent);
861 }
862 parentNodeId = parent.getParentNodeId();
863 accessibilityViewId = AccessibilityNodeInfo.getAccessibilityViewId(
864 parentNodeId);
865 } else {
866 prefetchPredecessorsOfRealNode(providerHost, outInfos);
867 return;
868 }
869 }
870 }
871
872 private void prefetchSiblingsOfVirtualNode(AccessibilityNodeInfo current, View providerHost,
873 AccessibilityNodeProvider provider, List<AccessibilityNodeInfo> outInfos) {
874 final long parentNodeId = current.getParentNodeId();
875 final int parentAccessibilityViewId =
876 AccessibilityNodeInfo.getAccessibilityViewId(parentNodeId);
877 final int parentVirtualDescendantId =
878 AccessibilityNodeInfo.getVirtualDescendantId(parentNodeId);
879 if (parentVirtualDescendantId != AccessibilityNodeInfo.UNDEFINED
880 || parentAccessibilityViewId == providerHost.getAccessibilityViewId()) {
881 AccessibilityNodeInfo parent =
882 provider.createAccessibilityNodeInfo(parentVirtualDescendantId);
883 if (parent != null) {
884 SparseLongArray childNodeIds = parent.getChildNodeIds();
885 final int childCount = childNodeIds.size();
886 for (int i = 0; i < childCount; i++) {
887 if (outInfos.size() >= MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE) {
888 return;
889 }
890 final long childNodeId = childNodeIds.get(i);
891 if (childNodeId != current.getSourceNodeId()) {
892 final int childVirtualDescendantId =
893 AccessibilityNodeInfo.getVirtualDescendantId(childNodeId);
894 AccessibilityNodeInfo child = provider.createAccessibilityNodeInfo(
895 childVirtualDescendantId);
896 if (child != null) {
897 outInfos.add(child);
898 }
899 }
900 }
901 }
902 } else {
903 prefetchSiblingsOfRealNode(providerHost, outInfos);
904 }
905 }
906
907 private void prefetchDescendantsOfVirtualNode(AccessibilityNodeInfo root,
908 AccessibilityNodeProvider provider, List<AccessibilityNodeInfo> outInfos) {
909 SparseLongArray childNodeIds = root.getChildNodeIds();
910 final int initialOutInfosSize = outInfos.size();
911 final int childCount = childNodeIds.size();
912 for (int i = 0; i < childCount; i++) {
913 if (outInfos.size() >= MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE) {
914 return;
915 }
916 final long childNodeId = childNodeIds.get(i);
917 AccessibilityNodeInfo child = provider.createAccessibilityNodeInfo(
918 AccessibilityNodeInfo.getVirtualDescendantId(childNodeId));
919 if (child != null) {
920 outInfos.add(child);
921 }
922 }
923 if (outInfos.size() < MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE) {
924 final int addedChildCount = outInfos.size() - initialOutInfosSize;
925 for (int i = 0; i < addedChildCount; i++) {
926 AccessibilityNodeInfo child = outInfos.get(initialOutInfosSize + i);
927 prefetchDescendantsOfVirtualNode(child, provider, outInfos);
928 }
929 }
930 }
931 }
932
933 private class PrivateHandler extends Handler {
934 private final static int MSG_PERFORM_ACCESSIBILITY_ACTION = 1;
935 private final static int MSG_FIND_ACCESSIBLITY_NODE_INFO_BY_ACCESSIBILITY_ID = 2;
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800936 private final static int MSG_FIND_ACCESSIBLITY_NODE_INFOS_BY_VIEW_ID = 3;
Svetoslav Ganov42138042012-03-20 11:51:39 -0700937 private final static int MSG_FIND_ACCESSIBLITY_NODE_INFO_BY_TEXT = 4;
938 private final static int MSG_FIND_FOCUS = 5;
939 private final static int MSG_FOCUS_SEARCH = 6;
940
Svetoslav Ganov005b83b2012-04-16 18:17:17 -0700941 public PrivateHandler(Looper looper) {
942 super(looper);
Svetoslav Ganov42138042012-03-20 11:51:39 -0700943 }
944
945 @Override
946 public String getMessageName(Message message) {
947 final int type = message.what;
948 switch (type) {
949 case MSG_PERFORM_ACCESSIBILITY_ACTION:
950 return "MSG_PERFORM_ACCESSIBILITY_ACTION";
951 case MSG_FIND_ACCESSIBLITY_NODE_INFO_BY_ACCESSIBILITY_ID:
952 return "MSG_FIND_ACCESSIBLITY_NODE_INFO_BY_ACCESSIBILITY_ID";
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800953 case MSG_FIND_ACCESSIBLITY_NODE_INFOS_BY_VIEW_ID:
954 return "MSG_FIND_ACCESSIBLITY_NODE_INFOS_BY_VIEW_ID";
Svetoslav Ganov42138042012-03-20 11:51:39 -0700955 case MSG_FIND_ACCESSIBLITY_NODE_INFO_BY_TEXT:
956 return "MSG_FIND_ACCESSIBLITY_NODE_INFO_BY_TEXT";
957 case MSG_FIND_FOCUS:
958 return "MSG_FIND_FOCUS";
959 case MSG_FOCUS_SEARCH:
960 return "MSG_FOCUS_SEARCH";
961 default:
962 throw new IllegalArgumentException("Unknown message type: " + type);
963 }
964 }
965
966 @Override
967 public void handleMessage(Message message) {
968 final int type = message.what;
969 switch (type) {
970 case MSG_FIND_ACCESSIBLITY_NODE_INFO_BY_ACCESSIBILITY_ID: {
971 findAccessibilityNodeInfoByAccessibilityIdUiThread(message);
972 } break;
973 case MSG_PERFORM_ACCESSIBILITY_ACTION: {
974 perfromAccessibilityActionUiThread(message);
975 } break;
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800976 case MSG_FIND_ACCESSIBLITY_NODE_INFOS_BY_VIEW_ID: {
977 findAccessibilityNodeInfosByViewIdUiThread(message);
Svetoslav Ganov42138042012-03-20 11:51:39 -0700978 } break;
979 case MSG_FIND_ACCESSIBLITY_NODE_INFO_BY_TEXT: {
980 findAccessibilityNodeInfosByTextUiThread(message);
981 } break;
982 case MSG_FIND_FOCUS: {
983 findFocusUiThread(message);
984 } break;
985 case MSG_FOCUS_SEARCH: {
986 focusSearchUiThread(message);
987 } break;
988 default:
989 throw new IllegalArgumentException("Unknown message type: " + type);
990 }
991 }
992 }
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800993
994 private final class AddNodeInfosForViewId implements Predicate<View> {
995 private int mViewId = View.NO_ID;
996 private List<AccessibilityNodeInfo> mInfos;
997
998 public void init(int viewId, List<AccessibilityNodeInfo> infos) {
999 mViewId = viewId;
1000 mInfos = infos;
1001 }
1002
1003 public void reset() {
1004 mViewId = View.NO_ID;
1005 mInfos = null;
1006 }
1007
1008 @Override
1009 public boolean apply(View view) {
1010 if (view.getId() == mViewId && isShown(view)) {
1011 mInfos.add(view.createAccessibilityNodeInfo());
1012 }
1013 return false;
1014 }
1015 }
Svetoslav Ganov42138042012-03-20 11:51:39 -07001016}