blob: e835a97081691850aa352641158d80f472747787 [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: {
426 // Input focus cannot go to virtual views.
427 View target = root.findFocus();
Svetoslav Ganov0a1bb6d2012-05-07 11:54:39 -0700428 if (target != null && isShown(target)) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700429 focused = target.createAccessibilityNodeInfo();
430 }
431 } break;
432 default:
433 throw new IllegalArgumentException("Unknown focus type: " + focusType);
434 }
435 }
436 } finally {
437 try {
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800438 mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = 0;
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700439 applyAppScaleAndMagnificationSpecIfNeeded(focused, spec);
440 if (spec != null) {
441 spec.recycle();
442 }
Svetoslav Ganov42138042012-03-20 11:51:39 -0700443 callback.setFindAccessibilityNodeInfoResult(focused, interactionId);
444 } catch (RemoteException re) {
445 /* ignore - the other side will time out */
446 }
447 }
448 }
449
Svetoslav Ganovc9c9a482012-07-16 08:46:07 -0700450 public void focusSearchClientThread(long accessibilityNodeId, int direction, int interactionId,
451 IAccessibilityInteractionConnectionCallback callback, int flags, int interogatingPid,
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700452 long interrogatingTid, MagnificationSpec spec) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700453 Message message = mHandler.obtainMessage();
454 message.what = PrivateHandler.MSG_FOCUS_SEARCH;
455 message.arg1 = flags;
456 message.arg2 = AccessibilityNodeInfo.getAccessibilityViewId(accessibilityNodeId);
Svetoslav Ganov86783472012-06-06 21:12:20 -0700457
Svetoslav Ganov758143e2012-08-06 16:40:27 -0700458 SomeArgs args = SomeArgs.obtain();
Svetoslav Ganov42138042012-03-20 11:51:39 -0700459 args.argi2 = direction;
460 args.argi3 = interactionId;
461 args.arg1 = callback;
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700462 args.arg2 = spec;
Svetoslav Ganov86783472012-06-06 21:12:20 -0700463
Svetoslav Ganov42138042012-03-20 11:51:39 -0700464 message.obj = args;
Svetoslav Ganov86783472012-06-06 21:12:20 -0700465
Svetoslav Ganov42138042012-03-20 11:51:39 -0700466 // If the interrogation is performed by the same thread as the main UI
467 // thread in this process, set the message as a static reference so
468 // after this call completes the same thread but in the interrogating
469 // client can handle the message to generate the result.
Svetoslav Ganov749e7962012-04-19 17:13:46 -0700470 if (interogatingPid == mMyProcessId && interrogatingTid == mMyLooperThreadId) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700471 AccessibilityInteractionClient.getInstanceForThread(
472 interrogatingTid).setSameThreadMessage(message);
473 } else {
474 mHandler.sendMessage(message);
475 }
476 }
477
478 private void focusSearchUiThread(Message message) {
479 final int flags = message.arg1;
480 final int accessibilityViewId = message.arg2;
Svetoslav Ganov86783472012-06-06 21:12:20 -0700481
Svetoslav Ganov42138042012-03-20 11:51:39 -0700482 SomeArgs args = (SomeArgs) message.obj;
Svetoslav Ganov42138042012-03-20 11:51:39 -0700483 final int direction = args.argi2;
484 final int interactionId = args.argi3;
485 final IAccessibilityInteractionConnectionCallback callback =
486 (IAccessibilityInteractionConnectionCallback) args.arg1;
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700487 final MagnificationSpec spec = (MagnificationSpec) args.arg2;
Svetoslav Ganov86783472012-06-06 21:12:20 -0700488
Svetoslav Ganov758143e2012-08-06 16:40:27 -0700489 args.recycle();
Svetoslav Ganov86783472012-06-06 21:12:20 -0700490
Svetoslav Ganov42138042012-03-20 11:51:39 -0700491 AccessibilityNodeInfo next = null;
492 try {
493 if (mViewRootImpl.mView == null || mViewRootImpl.mAttachInfo == null) {
494 return;
495 }
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800496 mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = flags;
Svetoslav Ganov42138042012-03-20 11:51:39 -0700497 View root = null;
498 if (accessibilityViewId != AccessibilityNodeInfo.UNDEFINED) {
499 root = findViewByAccessibilityId(accessibilityViewId);
500 } else {
501 root = mViewRootImpl.mView;
502 }
Svetoslav Ganov0a1bb6d2012-05-07 11:54:39 -0700503 if (root != null && isShown(root)) {
Svetoslav Ganov27e2da72012-07-02 18:12:00 -0700504 View nextView = root.focusSearch(direction);
505 if (nextView != null) {
506 next = nextView.createAccessibilityNodeInfo();
Svetoslav Ganov42138042012-03-20 11:51:39 -0700507 }
508 }
509 } finally {
510 try {
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800511 mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = 0;
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700512 applyAppScaleAndMagnificationSpecIfNeeded(next, spec);
513 if (spec != null) {
514 spec.recycle();
515 }
Svetoslav Ganov42138042012-03-20 11:51:39 -0700516 callback.setFindAccessibilityNodeInfoResult(next, interactionId);
517 } catch (RemoteException re) {
518 /* ignore - the other side will time out */
519 }
520 }
521 }
522
523 public void performAccessibilityActionClientThread(long accessibilityNodeId, int action,
Svetoslav Ganovaa780c12012-04-19 23:01:39 -0700524 Bundle arguments, int interactionId,
525 IAccessibilityInteractionConnectionCallback callback, int flags, int interogatingPid,
526 long interrogatingTid) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700527 Message message = mHandler.obtainMessage();
528 message.what = PrivateHandler.MSG_PERFORM_ACCESSIBILITY_ACTION;
529 message.arg1 = flags;
530 message.arg2 = AccessibilityNodeInfo.getAccessibilityViewId(accessibilityNodeId);
Svetoslav Ganov86783472012-06-06 21:12:20 -0700531
Svetoslav Ganov758143e2012-08-06 16:40:27 -0700532 SomeArgs args = SomeArgs.obtain();
Svetoslav Ganov42138042012-03-20 11:51:39 -0700533 args.argi1 = AccessibilityNodeInfo.getVirtualDescendantId(accessibilityNodeId);
534 args.argi2 = action;
535 args.argi3 = interactionId;
536 args.arg1 = callback;
Svetoslav Ganovaa780c12012-04-19 23:01:39 -0700537 args.arg2 = arguments;
Svetoslav Ganov86783472012-06-06 21:12:20 -0700538
Svetoslav Ganov42138042012-03-20 11:51:39 -0700539 message.obj = args;
Svetoslav Ganov86783472012-06-06 21:12:20 -0700540
Svetoslav Ganov42138042012-03-20 11:51:39 -0700541 // If the interrogation is performed by the same thread as the main UI
542 // thread in this process, set the message as a static reference so
543 // after this call completes the same thread but in the interrogating
544 // client can handle the message to generate the result.
Svetoslav Ganov749e7962012-04-19 17:13:46 -0700545 if (interogatingPid == mMyProcessId && interrogatingTid == mMyLooperThreadId) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700546 AccessibilityInteractionClient.getInstanceForThread(
547 interrogatingTid).setSameThreadMessage(message);
548 } else {
549 mHandler.sendMessage(message);
550 }
551 }
552
553 private void perfromAccessibilityActionUiThread(Message message) {
554 final int flags = message.arg1;
555 final int accessibilityViewId = message.arg2;
Svetoslav Ganov86783472012-06-06 21:12:20 -0700556
Svetoslav Ganov42138042012-03-20 11:51:39 -0700557 SomeArgs args = (SomeArgs) message.obj;
558 final int virtualDescendantId = args.argi1;
559 final int action = args.argi2;
560 final int interactionId = args.argi3;
561 final IAccessibilityInteractionConnectionCallback callback =
562 (IAccessibilityInteractionConnectionCallback) args.arg1;
Svetoslav Ganovaa780c12012-04-19 23:01:39 -0700563 Bundle arguments = (Bundle) args.arg2;
Svetoslav Ganov86783472012-06-06 21:12:20 -0700564
Svetoslav Ganov758143e2012-08-06 16:40:27 -0700565 args.recycle();
Svetoslav Ganov86783472012-06-06 21:12:20 -0700566
Svetoslav Ganov42138042012-03-20 11:51:39 -0700567 boolean succeeded = false;
568 try {
569 if (mViewRootImpl.mView == null || mViewRootImpl.mAttachInfo == null) {
570 return;
571 }
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800572 mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = flags;
Svetoslav Ganov42138042012-03-20 11:51:39 -0700573 View target = null;
574 if (accessibilityViewId != AccessibilityNodeInfo.UNDEFINED) {
575 target = findViewByAccessibilityId(accessibilityViewId);
576 } else {
577 target = mViewRootImpl.mView;
578 }
Svetoslav Ganov0a1bb6d2012-05-07 11:54:39 -0700579 if (target != null && isShown(target)) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700580 AccessibilityNodeProvider provider = target.getAccessibilityNodeProvider();
581 if (provider != null) {
Svetoslav Ganovaa780c12012-04-19 23:01:39 -0700582 succeeded = provider.performAction(virtualDescendantId, action,
583 arguments);
Svetoslav Ganov42138042012-03-20 11:51:39 -0700584 } else if (virtualDescendantId == View.NO_ID) {
Svetoslav Ganovaa780c12012-04-19 23:01:39 -0700585 succeeded = target.performAccessibilityAction(action, arguments);
Svetoslav Ganov42138042012-03-20 11:51:39 -0700586 }
587 }
588 } finally {
589 try {
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800590 mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = 0;
Svetoslav Ganov42138042012-03-20 11:51:39 -0700591 callback.setPerformAccessibilityActionResult(succeeded, interactionId);
592 } catch (RemoteException re) {
593 /* ignore - the other side will time out */
594 }
595 }
596 }
597
598 private View findViewByAccessibilityId(int accessibilityId) {
599 View root = mViewRootImpl.mView;
600 if (root == null) {
601 return null;
602 }
603 View foundView = root.findViewByAccessibilityId(accessibilityId);
Svetoslav Ganov0a1bb6d2012-05-07 11:54:39 -0700604 if (foundView != null && !isShown(foundView)) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700605 return null;
606 }
607 return foundView;
608 }
609
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700610 private void applyAppScaleAndMagnificationSpecIfNeeded(List<AccessibilityNodeInfo> infos,
611 MagnificationSpec spec) {
Svetoslav Ganovfd8d9c42012-07-09 18:16:55 -0700612 if (infos == null) {
613 return;
614 }
615 final float applicationScale = mViewRootImpl.mAttachInfo.mApplicationScale;
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700616 if (shouldApplyAppScaleAndMagnificationSpec(applicationScale, spec)) {
Svetoslav Ganovfd8d9c42012-07-09 18:16:55 -0700617 final int infoCount = infos.size();
618 for (int i = 0; i < infoCount; i++) {
619 AccessibilityNodeInfo info = infos.get(i);
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700620 applyAppScaleAndMagnificationSpecIfNeeded(info, spec);
Svetoslav Ganovfd8d9c42012-07-09 18:16:55 -0700621 }
622 }
623 }
624
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700625 private void applyAppScaleAndMagnificationSpecIfNeeded(AccessibilityNodeInfo info,
626 MagnificationSpec spec) {
Svetoslav Ganovfd8d9c42012-07-09 18:16:55 -0700627 if (info == null) {
628 return;
629 }
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700630
Svetoslav Ganovfd8d9c42012-07-09 18:16:55 -0700631 final float applicationScale = mViewRootImpl.mAttachInfo.mApplicationScale;
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700632 if (!shouldApplyAppScaleAndMagnificationSpec(applicationScale, spec)) {
633 return;
634 }
635
636 Rect boundsInParent = mTempRect;
637 Rect boundsInScreen = mTempRect1;
638
639 info.getBoundsInParent(boundsInParent);
640 info.getBoundsInScreen(boundsInScreen);
Svetoslav Ganovfd8d9c42012-07-09 18:16:55 -0700641 if (applicationScale != 1.0f) {
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700642 boundsInParent.scale(applicationScale);
643 boundsInScreen.scale(applicationScale);
644 }
645 if (spec != null) {
646 boundsInParent.scale(spec.scale);
647 // boundsInParent must not be offset.
648 boundsInScreen.scale(spec.scale);
649 boundsInScreen.offset((int) spec.offsetX, (int) spec.offsetY);
650 }
651 info.setBoundsInParent(boundsInParent);
652 info.setBoundsInScreen(boundsInScreen);
Svetoslav Ganovfd8d9c42012-07-09 18:16:55 -0700653
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700654 if (spec != null) {
655 AttachInfo attachInfo = mViewRootImpl.mAttachInfo;
656 if (attachInfo.mDisplay == null) {
657 return;
658 }
Svetoslav Ganovfd8d9c42012-07-09 18:16:55 -0700659
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700660 final float scale = attachInfo.mApplicationScale * spec.scale;
661
662 Rect visibleWinFrame = mTempRect1;
663 visibleWinFrame.left = (int) (attachInfo.mWindowLeft * scale + spec.offsetX);
664 visibleWinFrame.top = (int) (attachInfo.mWindowTop * scale + spec.offsetY);
665 visibleWinFrame.right = (int) (visibleWinFrame.left + mViewRootImpl.mWidth * scale);
666 visibleWinFrame.bottom = (int) (visibleWinFrame.top + mViewRootImpl.mHeight * scale);
667
668 attachInfo.mDisplay.getRealSize(mTempPoint);
669 final int displayWidth = mTempPoint.x;
670 final int displayHeight = mTempPoint.y;
671
672 Rect visibleDisplayFrame = mTempRect2;
673 visibleDisplayFrame.set(0, 0, displayWidth, displayHeight);
674
675 visibleWinFrame.intersect(visibleDisplayFrame);
676
677 if (!visibleWinFrame.intersects(boundsInScreen.left, boundsInScreen.top,
678 boundsInScreen.right, boundsInScreen.bottom)) {
679 info.setVisibleToUser(false);
680 }
Svetoslav Ganovfd8d9c42012-07-09 18:16:55 -0700681 }
682 }
683
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700684 private boolean shouldApplyAppScaleAndMagnificationSpec(float appScale,
685 MagnificationSpec spec) {
686 return (appScale != 1.0f || (spec != null && !spec.isNop()));
687 }
Svetoslav Ganovfd8d9c42012-07-09 18:16:55 -0700688
Svetoslav Ganov42138042012-03-20 11:51:39 -0700689 /**
Svetoslav Ganov42138042012-03-20 11:51:39 -0700690 * This class encapsulates a prefetching strategy for the accessibility APIs for
691 * querying window content. It is responsible to prefetch a batch of
692 * AccessibilityNodeInfos in addition to the one for a requested node.
693 */
694 private class AccessibilityNodePrefetcher {
695
696 private static final int MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE = 50;
697
Svetoslav Ganov4528b4e2012-05-15 18:24:10 -0700698 private final ArrayList<View> mTempViewList = new ArrayList<View>();
699
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800700 public void prefetchAccessibilityNodeInfos(View view, int virtualViewId, int fetchFlags,
Svetoslav Ganov42138042012-03-20 11:51:39 -0700701 List<AccessibilityNodeInfo> outInfos) {
702 AccessibilityNodeProvider provider = view.getAccessibilityNodeProvider();
703 if (provider == null) {
704 AccessibilityNodeInfo root = view.createAccessibilityNodeInfo();
705 if (root != null) {
706 outInfos.add(root);
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800707 if ((fetchFlags & AccessibilityNodeInfo.FLAG_PREFETCH_PREDECESSORS) != 0) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700708 prefetchPredecessorsOfRealNode(view, outInfos);
709 }
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800710 if ((fetchFlags & AccessibilityNodeInfo.FLAG_PREFETCH_SIBLINGS) != 0) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700711 prefetchSiblingsOfRealNode(view, outInfos);
712 }
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800713 if ((fetchFlags & AccessibilityNodeInfo.FLAG_PREFETCH_DESCENDANTS) != 0) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700714 prefetchDescendantsOfRealNode(view, outInfos);
715 }
716 }
717 } else {
718 AccessibilityNodeInfo root = provider.createAccessibilityNodeInfo(virtualViewId);
719 if (root != null) {
720 outInfos.add(root);
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800721 if ((fetchFlags & AccessibilityNodeInfo.FLAG_PREFETCH_PREDECESSORS) != 0) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700722 prefetchPredecessorsOfVirtualNode(root, view, provider, outInfos);
723 }
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800724 if ((fetchFlags & AccessibilityNodeInfo.FLAG_PREFETCH_SIBLINGS) != 0) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700725 prefetchSiblingsOfVirtualNode(root, view, provider, outInfos);
726 }
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800727 if ((fetchFlags & AccessibilityNodeInfo.FLAG_PREFETCH_DESCENDANTS) != 0) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700728 prefetchDescendantsOfVirtualNode(root, provider, outInfos);
729 }
730 }
731 }
732 }
733
734 private void prefetchPredecessorsOfRealNode(View view,
735 List<AccessibilityNodeInfo> outInfos) {
736 ViewParent parent = view.getParentForAccessibility();
737 while (parent instanceof View
738 && outInfos.size() < MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE) {
739 View parentView = (View) parent;
Svetoslav Ganov42138042012-03-20 11:51:39 -0700740 AccessibilityNodeInfo info = parentView.createAccessibilityNodeInfo();
741 if (info != null) {
742 outInfos.add(info);
743 }
744 parent = parent.getParentForAccessibility();
745 }
746 }
747
748 private void prefetchSiblingsOfRealNode(View current,
749 List<AccessibilityNodeInfo> outInfos) {
750 ViewParent parent = current.getParentForAccessibility();
751 if (parent instanceof ViewGroup) {
752 ViewGroup parentGroup = (ViewGroup) parent;
Svetoslav Ganov4528b4e2012-05-15 18:24:10 -0700753 ArrayList<View> children = mTempViewList;
754 children.clear();
Svetoslav Ganov76f287e2012-04-23 11:02:36 -0700755 try {
Svetoslav Ganov4528b4e2012-05-15 18:24:10 -0700756 parentGroup.addChildrenForAccessibility(children);
757 final int childCount = children.size();
Svetoslav Ganov76f287e2012-04-23 11:02:36 -0700758 for (int i = 0; i < childCount; i++) {
759 if (outInfos.size() >= MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE) {
760 return;
Svetoslav Ganov42138042012-03-20 11:51:39 -0700761 }
Svetoslav Ganov4528b4e2012-05-15 18:24:10 -0700762 View child = children.get(i);
Svetoslav Ganov76f287e2012-04-23 11:02:36 -0700763 if (child.getAccessibilityViewId() != current.getAccessibilityViewId()
Svetoslav Ganov0a1bb6d2012-05-07 11:54:39 -0700764 && isShown(child)) {
Svetoslav Ganov76f287e2012-04-23 11:02:36 -0700765 AccessibilityNodeInfo info = null;
Svetoslav Ganov4528b4e2012-05-15 18:24:10 -0700766 AccessibilityNodeProvider provider =
767 child.getAccessibilityNodeProvider();
Svetoslav Ganov76f287e2012-04-23 11:02:36 -0700768 if (provider == null) {
769 info = child.createAccessibilityNodeInfo();
770 } else {
771 info = provider.createAccessibilityNodeInfo(
772 AccessibilityNodeInfo.UNDEFINED);
773 }
774 if (info != null) {
775 outInfos.add(info);
776 }
Svetoslav Ganov42138042012-03-20 11:51:39 -0700777 }
778 }
Svetoslav Ganov76f287e2012-04-23 11:02:36 -0700779 } finally {
Svetoslav Ganov4528b4e2012-05-15 18:24:10 -0700780 children.clear();
Svetoslav Ganov42138042012-03-20 11:51:39 -0700781 }
Svetoslav Ganov42138042012-03-20 11:51:39 -0700782 }
783 }
784
785 private void prefetchDescendantsOfRealNode(View root,
786 List<AccessibilityNodeInfo> outInfos) {
787 if (!(root instanceof ViewGroup)) {
788 return;
789 }
Svetoslav Ganov42138042012-03-20 11:51:39 -0700790 HashMap<View, AccessibilityNodeInfo> addedChildren =
791 new HashMap<View, AccessibilityNodeInfo>();
Svetoslav Ganov4528b4e2012-05-15 18:24:10 -0700792 ArrayList<View> children = mTempViewList;
793 children.clear();
Svetoslav Ganov76f287e2012-04-23 11:02:36 -0700794 try {
Svetoslav Ganov4528b4e2012-05-15 18:24:10 -0700795 root.addChildrenForAccessibility(children);
796 final int childCount = children.size();
Svetoslav Ganov76f287e2012-04-23 11:02:36 -0700797 for (int i = 0; i < childCount; i++) {
798 if (outInfos.size() >= MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE) {
799 return;
800 }
Svetoslav Ganov4528b4e2012-05-15 18:24:10 -0700801 View child = children.get(i);
Svetoslav Ganov0a1bb6d2012-05-07 11:54:39 -0700802 if (isShown(child)) {
Svetoslav Ganov76f287e2012-04-23 11:02:36 -0700803 AccessibilityNodeProvider provider = child.getAccessibilityNodeProvider();
804 if (provider == null) {
805 AccessibilityNodeInfo info = child.createAccessibilityNodeInfo();
806 if (info != null) {
807 outInfos.add(info);
808 addedChildren.put(child, null);
809 }
810 } else {
811 AccessibilityNodeInfo info = provider.createAccessibilityNodeInfo(
812 AccessibilityNodeInfo.UNDEFINED);
813 if (info != null) {
814 outInfos.add(info);
815 addedChildren.put(child, info);
816 }
Svetoslav Ganov42138042012-03-20 11:51:39 -0700817 }
818 }
819 }
Svetoslav Ganov76f287e2012-04-23 11:02:36 -0700820 } finally {
Svetoslav Ganov4528b4e2012-05-15 18:24:10 -0700821 children.clear();
Svetoslav Ganov42138042012-03-20 11:51:39 -0700822 }
Svetoslav Ganov42138042012-03-20 11:51:39 -0700823 if (outInfos.size() < MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE) {
824 for (Map.Entry<View, AccessibilityNodeInfo> entry : addedChildren.entrySet()) {
825 View addedChild = entry.getKey();
826 AccessibilityNodeInfo virtualRoot = entry.getValue();
827 if (virtualRoot == null) {
828 prefetchDescendantsOfRealNode(addedChild, outInfos);
829 } else {
830 AccessibilityNodeProvider provider =
831 addedChild.getAccessibilityNodeProvider();
832 prefetchDescendantsOfVirtualNode(virtualRoot, provider, outInfos);
833 }
834 }
835 }
836 }
837
838 private void prefetchPredecessorsOfVirtualNode(AccessibilityNodeInfo root,
839 View providerHost, AccessibilityNodeProvider provider,
840 List<AccessibilityNodeInfo> outInfos) {
841 long parentNodeId = root.getParentNodeId();
842 int accessibilityViewId = AccessibilityNodeInfo.getAccessibilityViewId(parentNodeId);
843 while (accessibilityViewId != AccessibilityNodeInfo.UNDEFINED) {
844 if (outInfos.size() >= MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE) {
845 return;
846 }
847 final int virtualDescendantId =
848 AccessibilityNodeInfo.getVirtualDescendantId(parentNodeId);
849 if (virtualDescendantId != AccessibilityNodeInfo.UNDEFINED
850 || accessibilityViewId == providerHost.getAccessibilityViewId()) {
851 AccessibilityNodeInfo parent = provider.createAccessibilityNodeInfo(
852 virtualDescendantId);
853 if (parent != null) {
854 outInfos.add(parent);
855 }
856 parentNodeId = parent.getParentNodeId();
857 accessibilityViewId = AccessibilityNodeInfo.getAccessibilityViewId(
858 parentNodeId);
859 } else {
860 prefetchPredecessorsOfRealNode(providerHost, outInfos);
861 return;
862 }
863 }
864 }
865
866 private void prefetchSiblingsOfVirtualNode(AccessibilityNodeInfo current, View providerHost,
867 AccessibilityNodeProvider provider, List<AccessibilityNodeInfo> outInfos) {
868 final long parentNodeId = current.getParentNodeId();
869 final int parentAccessibilityViewId =
870 AccessibilityNodeInfo.getAccessibilityViewId(parentNodeId);
871 final int parentVirtualDescendantId =
872 AccessibilityNodeInfo.getVirtualDescendantId(parentNodeId);
873 if (parentVirtualDescendantId != AccessibilityNodeInfo.UNDEFINED
874 || parentAccessibilityViewId == providerHost.getAccessibilityViewId()) {
875 AccessibilityNodeInfo parent =
876 provider.createAccessibilityNodeInfo(parentVirtualDescendantId);
877 if (parent != null) {
878 SparseLongArray childNodeIds = parent.getChildNodeIds();
879 final int childCount = childNodeIds.size();
880 for (int i = 0; i < childCount; i++) {
881 if (outInfos.size() >= MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE) {
882 return;
883 }
884 final long childNodeId = childNodeIds.get(i);
885 if (childNodeId != current.getSourceNodeId()) {
886 final int childVirtualDescendantId =
887 AccessibilityNodeInfo.getVirtualDescendantId(childNodeId);
888 AccessibilityNodeInfo child = provider.createAccessibilityNodeInfo(
889 childVirtualDescendantId);
890 if (child != null) {
891 outInfos.add(child);
892 }
893 }
894 }
895 }
896 } else {
897 prefetchSiblingsOfRealNode(providerHost, outInfos);
898 }
899 }
900
901 private void prefetchDescendantsOfVirtualNode(AccessibilityNodeInfo root,
902 AccessibilityNodeProvider provider, List<AccessibilityNodeInfo> outInfos) {
903 SparseLongArray childNodeIds = root.getChildNodeIds();
904 final int initialOutInfosSize = outInfos.size();
905 final int childCount = childNodeIds.size();
906 for (int i = 0; i < childCount; i++) {
907 if (outInfos.size() >= MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE) {
908 return;
909 }
910 final long childNodeId = childNodeIds.get(i);
911 AccessibilityNodeInfo child = provider.createAccessibilityNodeInfo(
912 AccessibilityNodeInfo.getVirtualDescendantId(childNodeId));
913 if (child != null) {
914 outInfos.add(child);
915 }
916 }
917 if (outInfos.size() < MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE) {
918 final int addedChildCount = outInfos.size() - initialOutInfosSize;
919 for (int i = 0; i < addedChildCount; i++) {
920 AccessibilityNodeInfo child = outInfos.get(initialOutInfosSize + i);
921 prefetchDescendantsOfVirtualNode(child, provider, outInfos);
922 }
923 }
924 }
925 }
926
927 private class PrivateHandler extends Handler {
928 private final static int MSG_PERFORM_ACCESSIBILITY_ACTION = 1;
929 private final static int MSG_FIND_ACCESSIBLITY_NODE_INFO_BY_ACCESSIBILITY_ID = 2;
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800930 private final static int MSG_FIND_ACCESSIBLITY_NODE_INFOS_BY_VIEW_ID = 3;
Svetoslav Ganov42138042012-03-20 11:51:39 -0700931 private final static int MSG_FIND_ACCESSIBLITY_NODE_INFO_BY_TEXT = 4;
932 private final static int MSG_FIND_FOCUS = 5;
933 private final static int MSG_FOCUS_SEARCH = 6;
934
Svetoslav Ganov005b83b2012-04-16 18:17:17 -0700935 public PrivateHandler(Looper looper) {
936 super(looper);
Svetoslav Ganov42138042012-03-20 11:51:39 -0700937 }
938
939 @Override
940 public String getMessageName(Message message) {
941 final int type = message.what;
942 switch (type) {
943 case MSG_PERFORM_ACCESSIBILITY_ACTION:
944 return "MSG_PERFORM_ACCESSIBILITY_ACTION";
945 case MSG_FIND_ACCESSIBLITY_NODE_INFO_BY_ACCESSIBILITY_ID:
946 return "MSG_FIND_ACCESSIBLITY_NODE_INFO_BY_ACCESSIBILITY_ID";
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800947 case MSG_FIND_ACCESSIBLITY_NODE_INFOS_BY_VIEW_ID:
948 return "MSG_FIND_ACCESSIBLITY_NODE_INFOS_BY_VIEW_ID";
Svetoslav Ganov42138042012-03-20 11:51:39 -0700949 case MSG_FIND_ACCESSIBLITY_NODE_INFO_BY_TEXT:
950 return "MSG_FIND_ACCESSIBLITY_NODE_INFO_BY_TEXT";
951 case MSG_FIND_FOCUS:
952 return "MSG_FIND_FOCUS";
953 case MSG_FOCUS_SEARCH:
954 return "MSG_FOCUS_SEARCH";
955 default:
956 throw new IllegalArgumentException("Unknown message type: " + type);
957 }
958 }
959
960 @Override
961 public void handleMessage(Message message) {
962 final int type = message.what;
963 switch (type) {
964 case MSG_FIND_ACCESSIBLITY_NODE_INFO_BY_ACCESSIBILITY_ID: {
965 findAccessibilityNodeInfoByAccessibilityIdUiThread(message);
966 } break;
967 case MSG_PERFORM_ACCESSIBILITY_ACTION: {
968 perfromAccessibilityActionUiThread(message);
969 } break;
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800970 case MSG_FIND_ACCESSIBLITY_NODE_INFOS_BY_VIEW_ID: {
971 findAccessibilityNodeInfosByViewIdUiThread(message);
Svetoslav Ganov42138042012-03-20 11:51:39 -0700972 } break;
973 case MSG_FIND_ACCESSIBLITY_NODE_INFO_BY_TEXT: {
974 findAccessibilityNodeInfosByTextUiThread(message);
975 } break;
976 case MSG_FIND_FOCUS: {
977 findFocusUiThread(message);
978 } break;
979 case MSG_FOCUS_SEARCH: {
980 focusSearchUiThread(message);
981 } break;
982 default:
983 throw new IllegalArgumentException("Unknown message type: " + type);
984 }
985 }
986 }
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800987
988 private final class AddNodeInfosForViewId implements Predicate<View> {
989 private int mViewId = View.NO_ID;
990 private List<AccessibilityNodeInfo> mInfos;
991
992 public void init(int viewId, List<AccessibilityNodeInfo> infos) {
993 mViewId = viewId;
994 mInfos = infos;
995 }
996
997 public void reset() {
998 mViewId = View.NO_ID;
999 mInfos = null;
1000 }
1001
1002 @Override
1003 public boolean apply(View view) {
1004 if (view.getId() == mViewId && isShown(view)) {
1005 mInfos.add(view.createAccessibilityNodeInfo());
1006 }
1007 return false;
1008 }
1009 }
Svetoslav Ganov42138042012-03-20 11:51:39 -07001010}