blob: 3859ad44b722f908454be781f6b3cd40d9309ade [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 Ganov152e9bb2012-10-12 20:15:29 -070027import android.view.View.AttachInfo;
Svetoslav Ganov42138042012-03-20 11:51:39 -070028import android.view.accessibility.AccessibilityInteractionClient;
29import android.view.accessibility.AccessibilityNodeInfo;
30import android.view.accessibility.AccessibilityNodeProvider;
31import android.view.accessibility.IAccessibilityInteractionConnectionCallback;
32
Svetoslav Ganov758143e2012-08-06 16:40:27 -070033import com.android.internal.os.SomeArgs;
Svetoslav Ganov80943d82013-01-02 10:25:37 -080034import com.android.internal.util.Predicate;
Svetoslav Ganov758143e2012-08-06 16:40:27 -070035
Svetoslav Ganov42138042012-03-20 11:51:39 -070036import java.util.ArrayList;
37import java.util.HashMap;
38import java.util.List;
39import java.util.Map;
40
41/**
42 * Class for managing accessibility interactions initiated from the system
43 * and targeting the view hierarchy. A *ClientThread method is to be
44 * called from the interaction connection ViewAncestor gives the system to
45 * talk to it and a corresponding *UiThread method that is executed on the
46 * UI thread.
47 */
48final class AccessibilityInteractionController {
Svetoslav Ganov42138042012-03-20 11:51:39 -070049
Svetoslav Ganov80943d82013-01-02 10:25:37 -080050 private final ArrayList<AccessibilityNodeInfo> mTempAccessibilityNodeInfoList =
Svetoslav Ganov42138042012-03-20 11:51:39 -070051 new ArrayList<AccessibilityNodeInfo>();
52
Svetoslav Ganov005b83b2012-04-16 18:17:17 -070053 private final Handler mHandler;
Svetoslav Ganov42138042012-03-20 11:51:39 -070054
55 private final ViewRootImpl mViewRootImpl;
56
57 private final AccessibilityNodePrefetcher mPrefetcher;
58
Svetoslav Ganov749e7962012-04-19 17:13:46 -070059 private final long mMyLooperThreadId;
60
61 private final int mMyProcessId;
62
Svetoslav Ganov30ac6452012-06-01 09:10:25 -070063 private final ArrayList<View> mTempArrayList = new ArrayList<View>();
64
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -070065 private final Point mTempPoint = new Point();
Svetoslav Ganovfd8d9c42012-07-09 18:16:55 -070066 private final Rect mTempRect = new Rect();
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -070067 private final Rect mTempRect1 = new Rect();
68 private final Rect mTempRect2 = new Rect();
Svetoslav Ganovfd8d9c42012-07-09 18:16:55 -070069
Svetoslav Ganov80943d82013-01-02 10:25:37 -080070 private AddNodeInfosForViewId mAddNodeInfosForViewId;
71
Svetoslav Ganov42138042012-03-20 11:51:39 -070072 public AccessibilityInteractionController(ViewRootImpl viewRootImpl) {
Svetoslav Ganov749e7962012-04-19 17:13:46 -070073 Looper looper = viewRootImpl.mHandler.getLooper();
74 mMyLooperThreadId = looper.getThread().getId();
75 mMyProcessId = Process.myPid();
Svetoslav Ganov005b83b2012-04-16 18:17:17 -070076 mHandler = new PrivateHandler(looper);
Svetoslav Ganov42138042012-03-20 11:51:39 -070077 mViewRootImpl = viewRootImpl;
78 mPrefetcher = new AccessibilityNodePrefetcher();
79 }
80
Svetoslav Ganov0a1bb6d2012-05-07 11:54:39 -070081 private boolean isShown(View view) {
82 // The first two checks are made also made by isShown() which
83 // however traverses the tree up to the parent to catch that.
84 // Therefore, we do some fail fast check to minimize the up
85 // tree traversal.
86 return (view.mAttachInfo != null
87 && view.mAttachInfo.mWindowVisibility == View.VISIBLE
88 && view.isShown());
89 }
90
Svetoslav Ganov42138042012-03-20 11:51:39 -070091 public void findAccessibilityNodeInfoByAccessibilityIdClientThread(
Svetoslav Ganovc9c9a482012-07-16 08:46:07 -070092 long accessibilityNodeId, int interactionId,
Svetoslav Ganov42138042012-03-20 11:51:39 -070093 IAccessibilityInteractionConnectionCallback callback, int flags, int interrogatingPid,
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -070094 long interrogatingTid, MagnificationSpec spec) {
Svetoslav Ganov42138042012-03-20 11:51:39 -070095 Message message = mHandler.obtainMessage();
96 message.what = PrivateHandler.MSG_FIND_ACCESSIBLITY_NODE_INFO_BY_ACCESSIBILITY_ID;
97 message.arg1 = flags;
Svetoslav Ganov86783472012-06-06 21:12:20 -070098
Svetoslav Ganov758143e2012-08-06 16:40:27 -070099 SomeArgs args = SomeArgs.obtain();
Svetoslav Ganov42138042012-03-20 11:51:39 -0700100 args.argi1 = AccessibilityNodeInfo.getAccessibilityViewId(accessibilityNodeId);
101 args.argi2 = AccessibilityNodeInfo.getVirtualDescendantId(accessibilityNodeId);
102 args.argi3 = interactionId;
103 args.arg1 = callback;
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700104 args.arg2 = spec;
Svetoslav Ganov42138042012-03-20 11:51:39 -0700105 message.obj = args;
Svetoslav Ganov86783472012-06-06 21:12:20 -0700106
Svetoslav Ganov42138042012-03-20 11:51:39 -0700107 // If the interrogation is performed by the same thread as the main UI
108 // thread in this process, set the message as a static reference so
109 // after this call completes the same thread but in the interrogating
110 // client can handle the message to generate the result.
Svetoslav Ganov749e7962012-04-19 17:13:46 -0700111 if (interrogatingPid == mMyProcessId && interrogatingTid == mMyLooperThreadId) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700112 AccessibilityInteractionClient.getInstanceForThread(
113 interrogatingTid).setSameThreadMessage(message);
114 } else {
115 mHandler.sendMessage(message);
116 }
117 }
118
119 private void findAccessibilityNodeInfoByAccessibilityIdUiThread(Message message) {
120 final int flags = message.arg1;
Svetoslav Ganov86783472012-06-06 21:12:20 -0700121
Svetoslav Ganov42138042012-03-20 11:51:39 -0700122 SomeArgs args = (SomeArgs) message.obj;
123 final int accessibilityViewId = args.argi1;
124 final int virtualDescendantId = args.argi2;
125 final int interactionId = args.argi3;
126 final IAccessibilityInteractionConnectionCallback callback =
127 (IAccessibilityInteractionConnectionCallback) args.arg1;
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700128 final MagnificationSpec spec = (MagnificationSpec) args.arg2;
Svetoslav Ganov86783472012-06-06 21:12:20 -0700129
Svetoslav Ganov758143e2012-08-06 16:40:27 -0700130 args.recycle();
Svetoslav Ganov86783472012-06-06 21:12:20 -0700131
Svetoslav Ganov42138042012-03-20 11:51:39 -0700132 List<AccessibilityNodeInfo> infos = mTempAccessibilityNodeInfoList;
133 infos.clear();
134 try {
135 if (mViewRootImpl.mView == null || mViewRootImpl.mAttachInfo == null) {
136 return;
137 }
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800138 mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = flags;
Svetoslav Ganov42138042012-03-20 11:51:39 -0700139 View root = null;
140 if (accessibilityViewId == AccessibilityNodeInfo.UNDEFINED) {
141 root = mViewRootImpl.mView;
142 } else {
143 root = findViewByAccessibilityId(accessibilityViewId);
144 }
Svetoslav Ganov0a1bb6d2012-05-07 11:54:39 -0700145 if (root != null && isShown(root)) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700146 mPrefetcher.prefetchAccessibilityNodeInfos(root, virtualDescendantId, flags, infos);
147 }
148 } finally {
149 try {
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800150 mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = 0;
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700151 applyAppScaleAndMagnificationSpecIfNeeded(infos, spec);
152 if (spec != null) {
153 spec.recycle();
154 }
Svetoslav Ganov42138042012-03-20 11:51:39 -0700155 callback.setFindAccessibilityNodeInfosResult(infos, interactionId);
156 infos.clear();
157 } catch (RemoteException re) {
158 /* ignore - the other side will time out */
159 }
160 }
161 }
162
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800163 public void findAccessibilityNodeInfosByViewIdClientThread(long accessibilityNodeId,
164 String viewId, int interactionId, IAccessibilityInteractionConnectionCallback callback,
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700165 int flags, int interrogatingPid, long interrogatingTid, MagnificationSpec spec) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700166 Message message = mHandler.obtainMessage();
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800167 message.what = PrivateHandler.MSG_FIND_ACCESSIBLITY_NODE_INFOS_BY_VIEW_ID;
Svetoslav Ganov42138042012-03-20 11:51:39 -0700168 message.arg1 = flags;
169 message.arg2 = AccessibilityNodeInfo.getAccessibilityViewId(accessibilityNodeId);
Svetoslav Ganov86783472012-06-06 21:12:20 -0700170
Svetoslav Ganov758143e2012-08-06 16:40:27 -0700171 SomeArgs args = SomeArgs.obtain();
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800172 args.argi1 = interactionId;
Svetoslav Ganov42138042012-03-20 11:51:39 -0700173 args.arg1 = callback;
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700174 args.arg2 = spec;
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800175 args.arg3 = viewId;
Svetoslav Ganov86783472012-06-06 21:12:20 -0700176
Svetoslav Ganov42138042012-03-20 11:51:39 -0700177 message.obj = args;
Svetoslav Ganov86783472012-06-06 21:12:20 -0700178
Svetoslav Ganov42138042012-03-20 11:51:39 -0700179 // If the interrogation is performed by the same thread as the main UI
180 // thread in this process, set the message as a static reference so
181 // after this call completes the same thread but in the interrogating
182 // client can handle the message to generate the result.
Svetoslav Ganov749e7962012-04-19 17:13:46 -0700183 if (interrogatingPid == mMyProcessId && interrogatingTid == mMyLooperThreadId) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700184 AccessibilityInteractionClient.getInstanceForThread(
185 interrogatingTid).setSameThreadMessage(message);
186 } else {
187 mHandler.sendMessage(message);
188 }
189 }
190
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800191 private void findAccessibilityNodeInfosByViewIdUiThread(Message message) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700192 final int flags = message.arg1;
193 final int accessibilityViewId = message.arg2;
Svetoslav Ganov86783472012-06-06 21:12:20 -0700194
Svetoslav Ganov42138042012-03-20 11:51:39 -0700195 SomeArgs args = (SomeArgs) message.obj;
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800196 final int interactionId = args.argi1;
Svetoslav Ganov42138042012-03-20 11:51:39 -0700197 final IAccessibilityInteractionConnectionCallback callback =
198 (IAccessibilityInteractionConnectionCallback) args.arg1;
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700199 final MagnificationSpec spec = (MagnificationSpec) args.arg2;
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800200 final String viewId = (String) args.arg3;
Svetoslav Ganov86783472012-06-06 21:12:20 -0700201
Svetoslav Ganov758143e2012-08-06 16:40:27 -0700202 args.recycle();
Svetoslav Ganov86783472012-06-06 21:12:20 -0700203
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800204 final List<AccessibilityNodeInfo> infos = mTempAccessibilityNodeInfoList;
205 infos.clear();
Svetoslav Ganov42138042012-03-20 11:51:39 -0700206 try {
207 if (mViewRootImpl.mView == null || mViewRootImpl.mAttachInfo == null) {
208 return;
209 }
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800210 mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = flags;
Svetoslav Ganov42138042012-03-20 11:51:39 -0700211 View root = null;
212 if (accessibilityViewId != AccessibilityNodeInfo.UNDEFINED) {
213 root = findViewByAccessibilityId(accessibilityViewId);
214 } else {
215 root = mViewRootImpl.mView;
216 }
217 if (root != null) {
Svetoslava33243e2013-02-11 15:43:46 -0800218 final int resolvedViewId = root.getContext().getResources()
219 .getIdentifier(viewId, null, null);
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800220 if (resolvedViewId <= 0) {
221 return;
222 }
223 if (mAddNodeInfosForViewId == null) {
224 mAddNodeInfosForViewId = new AddNodeInfosForViewId();
225 }
226 mAddNodeInfosForViewId.init(resolvedViewId, infos);
227 root.findViewByPredicate(mAddNodeInfosForViewId);
228 mAddNodeInfosForViewId.reset();
Svetoslav Ganov42138042012-03-20 11:51:39 -0700229 }
230 } finally {
231 try {
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800232 mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = 0;
233 applyAppScaleAndMagnificationSpecIfNeeded(infos, spec);
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700234 if (spec != null) {
235 spec.recycle();
236 }
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800237 callback.setFindAccessibilityNodeInfosResult(infos, interactionId);
Svetoslav Ganov42138042012-03-20 11:51:39 -0700238 } catch (RemoteException re) {
239 /* ignore - the other side will time out */
240 }
241 }
242 }
243
244 public void findAccessibilityNodeInfosByTextClientThread(long accessibilityNodeId,
Svetoslav Ganovc9c9a482012-07-16 08:46:07 -0700245 String text, int interactionId, IAccessibilityInteractionConnectionCallback callback,
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700246 int flags, int interrogatingPid, long interrogatingTid, MagnificationSpec spec) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700247 Message message = mHandler.obtainMessage();
248 message.what = PrivateHandler.MSG_FIND_ACCESSIBLITY_NODE_INFO_BY_TEXT;
249 message.arg1 = flags;
Svetoslav Ganov86783472012-06-06 21:12:20 -0700250
Svetoslav Ganov758143e2012-08-06 16:40:27 -0700251 SomeArgs args = SomeArgs.obtain();
Svetoslav Ganov42138042012-03-20 11:51:39 -0700252 args.arg1 = text;
Svetoslav Ganovc9c9a482012-07-16 08:46:07 -0700253 args.arg2 = callback;
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700254 args.arg3 = spec;
Svetoslav Ganov42138042012-03-20 11:51:39 -0700255 args.argi1 = AccessibilityNodeInfo.getAccessibilityViewId(accessibilityNodeId);
256 args.argi2 = AccessibilityNodeInfo.getVirtualDescendantId(accessibilityNodeId);
257 args.argi3 = interactionId;
Svetoslav Ganov42138042012-03-20 11:51:39 -0700258 message.obj = args;
Svetoslav Ganov86783472012-06-06 21:12:20 -0700259
Svetoslav Ganov42138042012-03-20 11:51:39 -0700260 // If the interrogation is performed by the same thread as the main UI
261 // thread in this process, set the message as a static reference so
262 // after this call completes the same thread but in the interrogating
263 // client can handle the message to generate the result.
Svetoslav Ganov749e7962012-04-19 17:13:46 -0700264 if (interrogatingPid == mMyProcessId && interrogatingTid == mMyLooperThreadId) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700265 AccessibilityInteractionClient.getInstanceForThread(
266 interrogatingTid).setSameThreadMessage(message);
267 } else {
268 mHandler.sendMessage(message);
269 }
270 }
271
272 private void findAccessibilityNodeInfosByTextUiThread(Message message) {
273 final int flags = message.arg1;
Svetoslav Ganov86783472012-06-06 21:12:20 -0700274
Svetoslav Ganov42138042012-03-20 11:51:39 -0700275 SomeArgs args = (SomeArgs) message.obj;
276 final String text = (String) args.arg1;
Svetoslav Ganovc9c9a482012-07-16 08:46:07 -0700277 final IAccessibilityInteractionConnectionCallback callback =
278 (IAccessibilityInteractionConnectionCallback) args.arg2;
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700279 final MagnificationSpec spec = (MagnificationSpec) args.arg3;
Svetoslav Ganov42138042012-03-20 11:51:39 -0700280 final int accessibilityViewId = args.argi1;
281 final int virtualDescendantId = args.argi2;
282 final int interactionId = args.argi3;
Svetoslav Ganov758143e2012-08-06 16:40:27 -0700283 args.recycle();
Svetoslav Ganov86783472012-06-06 21:12:20 -0700284
Svetoslav Ganov42138042012-03-20 11:51:39 -0700285 List<AccessibilityNodeInfo> infos = null;
286 try {
287 if (mViewRootImpl.mView == null || mViewRootImpl.mAttachInfo == null) {
288 return;
289 }
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800290 mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = flags;
Svetoslav Ganov42138042012-03-20 11:51:39 -0700291 View root = null;
292 if (accessibilityViewId != AccessibilityNodeInfo.UNDEFINED) {
293 root = findViewByAccessibilityId(accessibilityViewId);
294 } else {
295 root = mViewRootImpl.mView;
296 }
Svetoslav Ganov0a1bb6d2012-05-07 11:54:39 -0700297 if (root != null && isShown(root)) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700298 AccessibilityNodeProvider provider = root.getAccessibilityNodeProvider();
299 if (provider != null) {
300 infos = provider.findAccessibilityNodeInfosByText(text,
301 virtualDescendantId);
302 } else if (virtualDescendantId == AccessibilityNodeInfo.UNDEFINED) {
Svetoslav Ganov30ac6452012-06-01 09:10:25 -0700303 ArrayList<View> foundViews = mTempArrayList;
Svetoslav Ganov42138042012-03-20 11:51:39 -0700304 foundViews.clear();
305 root.findViewsWithText(foundViews, text, View.FIND_VIEWS_WITH_TEXT
306 | View.FIND_VIEWS_WITH_CONTENT_DESCRIPTION
307 | View.FIND_VIEWS_WITH_ACCESSIBILITY_NODE_PROVIDERS);
308 if (!foundViews.isEmpty()) {
309 infos = mTempAccessibilityNodeInfoList;
310 infos.clear();
311 final int viewCount = foundViews.size();
312 for (int i = 0; i < viewCount; i++) {
313 View foundView = foundViews.get(i);
Svetoslav Ganov0a1bb6d2012-05-07 11:54:39 -0700314 if (isShown(foundView)) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700315 provider = foundView.getAccessibilityNodeProvider();
316 if (provider != null) {
317 List<AccessibilityNodeInfo> infosFromProvider =
318 provider.findAccessibilityNodeInfosByText(text,
Svetoslav Ganove5dfa47d2012-05-08 15:58:32 -0700319 AccessibilityNodeInfo.UNDEFINED);
Svetoslav Ganov42138042012-03-20 11:51:39 -0700320 if (infosFromProvider != null) {
321 infos.addAll(infosFromProvider);
322 }
323 } else {
324 infos.add(foundView.createAccessibilityNodeInfo());
325 }
326 }
327 }
328 }
329 }
330 }
331 } finally {
332 try {
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800333 mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = 0;
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700334 applyAppScaleAndMagnificationSpecIfNeeded(infos, spec);
335 if (spec != null) {
336 spec.recycle();
337 }
Svetoslav Ganov42138042012-03-20 11:51:39 -0700338 callback.setFindAccessibilityNodeInfosResult(infos, interactionId);
339 } catch (RemoteException re) {
340 /* ignore - the other side will time out */
341 }
342 }
343 }
344
Svetoslav Ganovc9c9a482012-07-16 08:46:07 -0700345 public void findFocusClientThread(long accessibilityNodeId, int focusType, int interactionId,
346 IAccessibilityInteractionConnectionCallback callback, int flags, int interogatingPid,
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700347 long interrogatingTid, MagnificationSpec spec) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700348 Message message = mHandler.obtainMessage();
349 message.what = PrivateHandler.MSG_FIND_FOCUS;
350 message.arg1 = flags;
351 message.arg2 = focusType;
Svetoslav Ganov86783472012-06-06 21:12:20 -0700352
Svetoslav Ganov758143e2012-08-06 16:40:27 -0700353 SomeArgs args = SomeArgs.obtain();
Svetoslav Ganov42138042012-03-20 11:51:39 -0700354 args.argi1 = interactionId;
355 args.argi2 = AccessibilityNodeInfo.getAccessibilityViewId(accessibilityNodeId);
356 args.argi3 = AccessibilityNodeInfo.getVirtualDescendantId(accessibilityNodeId);
357 args.arg1 = callback;
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700358 args.arg2 = spec;
Svetoslav Ganov86783472012-06-06 21:12:20 -0700359
Svetoslav Ganov42138042012-03-20 11:51:39 -0700360 message.obj = args;
Svetoslav Ganov86783472012-06-06 21:12:20 -0700361
Svetoslav Ganov42138042012-03-20 11:51:39 -0700362 // If the interrogation is performed by the same thread as the main UI
363 // thread in this process, set the message as a static reference so
364 // after this call completes the same thread but in the interrogating
365 // client can handle the message to generate the result.
Svetoslav Ganov749e7962012-04-19 17:13:46 -0700366 if (interogatingPid == mMyProcessId && interrogatingTid == mMyLooperThreadId) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700367 AccessibilityInteractionClient.getInstanceForThread(
368 interrogatingTid).setSameThreadMessage(message);
369 } else {
370 mHandler.sendMessage(message);
371 }
372 }
373
374 private void findFocusUiThread(Message message) {
375 final int flags = message.arg1;
376 final int focusType = message.arg2;
Svetoslav Ganov86783472012-06-06 21:12:20 -0700377
Svetoslav Ganov42138042012-03-20 11:51:39 -0700378 SomeArgs args = (SomeArgs) message.obj;
379 final int interactionId = args.argi1;
380 final int accessibilityViewId = args.argi2;
381 final int virtualDescendantId = args.argi3;
382 final IAccessibilityInteractionConnectionCallback callback =
383 (IAccessibilityInteractionConnectionCallback) args.arg1;
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700384 final MagnificationSpec spec = (MagnificationSpec) args.arg2;
Svetoslav Ganov758143e2012-08-06 16:40:27 -0700385 args.recycle();
Svetoslav Ganov86783472012-06-06 21:12:20 -0700386
Svetoslav Ganov42138042012-03-20 11:51:39 -0700387 AccessibilityNodeInfo focused = null;
388 try {
389 if (mViewRootImpl.mView == null || mViewRootImpl.mAttachInfo == null) {
390 return;
391 }
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800392 mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = flags;
Svetoslav Ganov42138042012-03-20 11:51:39 -0700393 View root = null;
394 if (accessibilityViewId != AccessibilityNodeInfo.UNDEFINED) {
395 root = findViewByAccessibilityId(accessibilityViewId);
396 } else {
397 root = mViewRootImpl.mView;
398 }
Svetoslav Ganov0a1bb6d2012-05-07 11:54:39 -0700399 if (root != null && isShown(root)) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700400 switch (focusType) {
401 case AccessibilityNodeInfo.FOCUS_ACCESSIBILITY: {
402 View host = mViewRootImpl.mAccessibilityFocusedHost;
403 // If there is no accessibility focus host or it is not a descendant
404 // of the root from which to start the search, then the search failed.
405 if (host == null || !ViewRootImpl.isViewDescendantOf(host, root)) {
406 break;
407 }
Svetoslav6254f482013-06-04 17:22:14 -0700408 // The focused view not shown, we failed.
409 if (!isShown(host)) {
410 break;
411 }
Svetoslav Ganov42138042012-03-20 11:51:39 -0700412 // If the host has a provider ask this provider to search for the
413 // focus instead fetching all provider nodes to do the search here.
414 AccessibilityNodeProvider provider = host.getAccessibilityNodeProvider();
415 if (provider != null) {
Svetoslav Ganov45a02e02012-06-17 15:07:29 -0700416 if (mViewRootImpl.mAccessibilityFocusedVirtualView != null) {
417 focused = AccessibilityNodeInfo.obtain(
418 mViewRootImpl.mAccessibilityFocusedVirtualView);
419 }
Svetoslav Ganov42138042012-03-20 11:51:39 -0700420 } else if (virtualDescendantId == View.NO_ID) {
421 focused = host.createAccessibilityNodeInfo();
422 }
423 } break;
424 case AccessibilityNodeInfo.FOCUS_INPUT: {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700425 View target = root.findFocus();
Alan Viverette2e1e0812013-09-30 13:45:55 -0700426 if (target == null || !isShown(target)) {
427 break;
428 }
429 AccessibilityNodeProvider provider = target.getAccessibilityNodeProvider();
430 if (provider != null) {
431 focused = provider.findFocus(focusType);
432 }
433 if (focused == null) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700434 focused = target.createAccessibilityNodeInfo();
435 }
436 } break;
437 default:
438 throw new IllegalArgumentException("Unknown focus type: " + focusType);
439 }
440 }
441 } finally {
442 try {
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800443 mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = 0;
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700444 applyAppScaleAndMagnificationSpecIfNeeded(focused, spec);
445 if (spec != null) {
446 spec.recycle();
447 }
Svetoslav Ganov42138042012-03-20 11:51:39 -0700448 callback.setFindAccessibilityNodeInfoResult(focused, interactionId);
449 } catch (RemoteException re) {
450 /* ignore - the other side will time out */
451 }
452 }
453 }
454
Svetoslav Ganovc9c9a482012-07-16 08:46:07 -0700455 public void focusSearchClientThread(long accessibilityNodeId, int direction, int interactionId,
456 IAccessibilityInteractionConnectionCallback callback, int flags, int interogatingPid,
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700457 long interrogatingTid, MagnificationSpec spec) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700458 Message message = mHandler.obtainMessage();
459 message.what = PrivateHandler.MSG_FOCUS_SEARCH;
460 message.arg1 = flags;
461 message.arg2 = AccessibilityNodeInfo.getAccessibilityViewId(accessibilityNodeId);
Svetoslav Ganov86783472012-06-06 21:12:20 -0700462
Svetoslav Ganov758143e2012-08-06 16:40:27 -0700463 SomeArgs args = SomeArgs.obtain();
Svetoslav Ganov42138042012-03-20 11:51:39 -0700464 args.argi2 = direction;
465 args.argi3 = interactionId;
466 args.arg1 = callback;
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700467 args.arg2 = spec;
Svetoslav Ganov86783472012-06-06 21:12:20 -0700468
Svetoslav Ganov42138042012-03-20 11:51:39 -0700469 message.obj = args;
Svetoslav Ganov86783472012-06-06 21:12:20 -0700470
Svetoslav Ganov42138042012-03-20 11:51:39 -0700471 // If the interrogation is performed by the same thread as the main UI
472 // thread in this process, set the message as a static reference so
473 // after this call completes the same thread but in the interrogating
474 // client can handle the message to generate the result.
Svetoslav Ganov749e7962012-04-19 17:13:46 -0700475 if (interogatingPid == mMyProcessId && interrogatingTid == mMyLooperThreadId) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700476 AccessibilityInteractionClient.getInstanceForThread(
477 interrogatingTid).setSameThreadMessage(message);
478 } else {
479 mHandler.sendMessage(message);
480 }
481 }
482
483 private void focusSearchUiThread(Message message) {
484 final int flags = message.arg1;
485 final int accessibilityViewId = message.arg2;
Svetoslav Ganov86783472012-06-06 21:12:20 -0700486
Svetoslav Ganov42138042012-03-20 11:51:39 -0700487 SomeArgs args = (SomeArgs) message.obj;
Svetoslav Ganov42138042012-03-20 11:51:39 -0700488 final int direction = args.argi2;
489 final int interactionId = args.argi3;
490 final IAccessibilityInteractionConnectionCallback callback =
491 (IAccessibilityInteractionConnectionCallback) args.arg1;
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700492 final MagnificationSpec spec = (MagnificationSpec) args.arg2;
Svetoslav Ganov86783472012-06-06 21:12:20 -0700493
Svetoslav Ganov758143e2012-08-06 16:40:27 -0700494 args.recycle();
Svetoslav Ganov86783472012-06-06 21:12:20 -0700495
Svetoslav Ganov42138042012-03-20 11:51:39 -0700496 AccessibilityNodeInfo next = null;
497 try {
498 if (mViewRootImpl.mView == null || mViewRootImpl.mAttachInfo == null) {
499 return;
500 }
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800501 mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = flags;
Svetoslav Ganov42138042012-03-20 11:51:39 -0700502 View root = null;
503 if (accessibilityViewId != AccessibilityNodeInfo.UNDEFINED) {
504 root = findViewByAccessibilityId(accessibilityViewId);
505 } else {
506 root = mViewRootImpl.mView;
507 }
Svetoslav Ganov0a1bb6d2012-05-07 11:54:39 -0700508 if (root != null && isShown(root)) {
Svetoslav Ganov27e2da72012-07-02 18:12:00 -0700509 View nextView = root.focusSearch(direction);
510 if (nextView != null) {
511 next = nextView.createAccessibilityNodeInfo();
Svetoslav Ganov42138042012-03-20 11:51:39 -0700512 }
513 }
514 } finally {
515 try {
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800516 mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = 0;
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700517 applyAppScaleAndMagnificationSpecIfNeeded(next, spec);
518 if (spec != null) {
519 spec.recycle();
520 }
Svetoslav Ganov42138042012-03-20 11:51:39 -0700521 callback.setFindAccessibilityNodeInfoResult(next, interactionId);
522 } catch (RemoteException re) {
523 /* ignore - the other side will time out */
524 }
525 }
526 }
527
528 public void performAccessibilityActionClientThread(long accessibilityNodeId, int action,
Svetoslav Ganovaa780c12012-04-19 23:01:39 -0700529 Bundle arguments, int interactionId,
530 IAccessibilityInteractionConnectionCallback callback, int flags, int interogatingPid,
531 long interrogatingTid) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700532 Message message = mHandler.obtainMessage();
533 message.what = PrivateHandler.MSG_PERFORM_ACCESSIBILITY_ACTION;
534 message.arg1 = flags;
535 message.arg2 = AccessibilityNodeInfo.getAccessibilityViewId(accessibilityNodeId);
Svetoslav Ganov86783472012-06-06 21:12:20 -0700536
Svetoslav Ganov758143e2012-08-06 16:40:27 -0700537 SomeArgs args = SomeArgs.obtain();
Svetoslav Ganov42138042012-03-20 11:51:39 -0700538 args.argi1 = AccessibilityNodeInfo.getVirtualDescendantId(accessibilityNodeId);
539 args.argi2 = action;
540 args.argi3 = interactionId;
541 args.arg1 = callback;
Svetoslav Ganovaa780c12012-04-19 23:01:39 -0700542 args.arg2 = arguments;
Svetoslav Ganov86783472012-06-06 21:12:20 -0700543
Svetoslav Ganov42138042012-03-20 11:51:39 -0700544 message.obj = args;
Svetoslav Ganov86783472012-06-06 21:12:20 -0700545
Svetoslav Ganov42138042012-03-20 11:51:39 -0700546 // If the interrogation is performed by the same thread as the main UI
547 // thread in this process, set the message as a static reference so
548 // after this call completes the same thread but in the interrogating
549 // client can handle the message to generate the result.
Svetoslav Ganov749e7962012-04-19 17:13:46 -0700550 if (interogatingPid == mMyProcessId && interrogatingTid == mMyLooperThreadId) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700551 AccessibilityInteractionClient.getInstanceForThread(
552 interrogatingTid).setSameThreadMessage(message);
553 } else {
554 mHandler.sendMessage(message);
555 }
556 }
557
558 private void perfromAccessibilityActionUiThread(Message message) {
559 final int flags = message.arg1;
560 final int accessibilityViewId = message.arg2;
Svetoslav Ganov86783472012-06-06 21:12:20 -0700561
Svetoslav Ganov42138042012-03-20 11:51:39 -0700562 SomeArgs args = (SomeArgs) message.obj;
563 final int virtualDescendantId = args.argi1;
564 final int action = args.argi2;
565 final int interactionId = args.argi3;
566 final IAccessibilityInteractionConnectionCallback callback =
567 (IAccessibilityInteractionConnectionCallback) args.arg1;
Svetoslav Ganovaa780c12012-04-19 23:01:39 -0700568 Bundle arguments = (Bundle) args.arg2;
Svetoslav Ganov86783472012-06-06 21:12:20 -0700569
Svetoslav Ganov758143e2012-08-06 16:40:27 -0700570 args.recycle();
Svetoslav Ganov86783472012-06-06 21:12:20 -0700571
Svetoslav Ganov42138042012-03-20 11:51:39 -0700572 boolean succeeded = false;
573 try {
574 if (mViewRootImpl.mView == null || mViewRootImpl.mAttachInfo == null) {
575 return;
576 }
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800577 mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = flags;
Svetoslav Ganov42138042012-03-20 11:51:39 -0700578 View target = null;
579 if (accessibilityViewId != AccessibilityNodeInfo.UNDEFINED) {
580 target = findViewByAccessibilityId(accessibilityViewId);
581 } else {
582 target = mViewRootImpl.mView;
583 }
Svetoslav Ganov0a1bb6d2012-05-07 11:54:39 -0700584 if (target != null && isShown(target)) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700585 AccessibilityNodeProvider provider = target.getAccessibilityNodeProvider();
586 if (provider != null) {
Svetoslav Ganovaa780c12012-04-19 23:01:39 -0700587 succeeded = provider.performAction(virtualDescendantId, action,
588 arguments);
Svetoslav Ganov42138042012-03-20 11:51:39 -0700589 } else if (virtualDescendantId == View.NO_ID) {
Svetoslav Ganovaa780c12012-04-19 23:01:39 -0700590 succeeded = target.performAccessibilityAction(action, arguments);
Svetoslav Ganov42138042012-03-20 11:51:39 -0700591 }
592 }
593 } finally {
594 try {
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800595 mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = 0;
Svetoslav Ganov42138042012-03-20 11:51:39 -0700596 callback.setPerformAccessibilityActionResult(succeeded, interactionId);
597 } catch (RemoteException re) {
598 /* ignore - the other side will time out */
599 }
600 }
601 }
602
603 private View findViewByAccessibilityId(int accessibilityId) {
604 View root = mViewRootImpl.mView;
605 if (root == null) {
606 return null;
607 }
608 View foundView = root.findViewByAccessibilityId(accessibilityId);
Svetoslav Ganov0a1bb6d2012-05-07 11:54:39 -0700609 if (foundView != null && !isShown(foundView)) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700610 return null;
611 }
612 return foundView;
613 }
614
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700615 private void applyAppScaleAndMagnificationSpecIfNeeded(List<AccessibilityNodeInfo> infos,
616 MagnificationSpec spec) {
Svetoslav Ganovfd8d9c42012-07-09 18:16:55 -0700617 if (infos == null) {
618 return;
619 }
620 final float applicationScale = mViewRootImpl.mAttachInfo.mApplicationScale;
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700621 if (shouldApplyAppScaleAndMagnificationSpec(applicationScale, spec)) {
Svetoslav Ganovfd8d9c42012-07-09 18:16:55 -0700622 final int infoCount = infos.size();
623 for (int i = 0; i < infoCount; i++) {
624 AccessibilityNodeInfo info = infos.get(i);
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700625 applyAppScaleAndMagnificationSpecIfNeeded(info, spec);
Svetoslav Ganovfd8d9c42012-07-09 18:16:55 -0700626 }
627 }
628 }
629
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700630 private void applyAppScaleAndMagnificationSpecIfNeeded(AccessibilityNodeInfo info,
631 MagnificationSpec spec) {
Svetoslav Ganovfd8d9c42012-07-09 18:16:55 -0700632 if (info == null) {
633 return;
634 }
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700635
Svetoslav Ganovfd8d9c42012-07-09 18:16:55 -0700636 final float applicationScale = mViewRootImpl.mAttachInfo.mApplicationScale;
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700637 if (!shouldApplyAppScaleAndMagnificationSpec(applicationScale, spec)) {
638 return;
639 }
640
641 Rect boundsInParent = mTempRect;
642 Rect boundsInScreen = mTempRect1;
643
644 info.getBoundsInParent(boundsInParent);
645 info.getBoundsInScreen(boundsInScreen);
Svetoslav Ganovfd8d9c42012-07-09 18:16:55 -0700646 if (applicationScale != 1.0f) {
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700647 boundsInParent.scale(applicationScale);
648 boundsInScreen.scale(applicationScale);
649 }
650 if (spec != null) {
651 boundsInParent.scale(spec.scale);
652 // boundsInParent must not be offset.
653 boundsInScreen.scale(spec.scale);
654 boundsInScreen.offset((int) spec.offsetX, (int) spec.offsetY);
655 }
656 info.setBoundsInParent(boundsInParent);
657 info.setBoundsInScreen(boundsInScreen);
Svetoslav Ganovfd8d9c42012-07-09 18:16:55 -0700658
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700659 if (spec != null) {
660 AttachInfo attachInfo = mViewRootImpl.mAttachInfo;
661 if (attachInfo.mDisplay == null) {
662 return;
663 }
Svetoslav Ganovfd8d9c42012-07-09 18:16:55 -0700664
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700665 final float scale = attachInfo.mApplicationScale * spec.scale;
666
667 Rect visibleWinFrame = mTempRect1;
668 visibleWinFrame.left = (int) (attachInfo.mWindowLeft * scale + spec.offsetX);
669 visibleWinFrame.top = (int) (attachInfo.mWindowTop * scale + spec.offsetY);
670 visibleWinFrame.right = (int) (visibleWinFrame.left + mViewRootImpl.mWidth * scale);
671 visibleWinFrame.bottom = (int) (visibleWinFrame.top + mViewRootImpl.mHeight * scale);
672
673 attachInfo.mDisplay.getRealSize(mTempPoint);
674 final int displayWidth = mTempPoint.x;
675 final int displayHeight = mTempPoint.y;
676
677 Rect visibleDisplayFrame = mTempRect2;
678 visibleDisplayFrame.set(0, 0, displayWidth, displayHeight);
679
680 visibleWinFrame.intersect(visibleDisplayFrame);
681
682 if (!visibleWinFrame.intersects(boundsInScreen.left, boundsInScreen.top,
683 boundsInScreen.right, boundsInScreen.bottom)) {
684 info.setVisibleToUser(false);
685 }
Svetoslav Ganovfd8d9c42012-07-09 18:16:55 -0700686 }
687 }
688
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -0700689 private boolean shouldApplyAppScaleAndMagnificationSpec(float appScale,
690 MagnificationSpec spec) {
691 return (appScale != 1.0f || (spec != null && !spec.isNop()));
692 }
Svetoslav Ganovfd8d9c42012-07-09 18:16:55 -0700693
Svetoslav Ganov42138042012-03-20 11:51:39 -0700694 /**
Svetoslav Ganov42138042012-03-20 11:51:39 -0700695 * This class encapsulates a prefetching strategy for the accessibility APIs for
696 * querying window content. It is responsible to prefetch a batch of
697 * AccessibilityNodeInfos in addition to the one for a requested node.
698 */
699 private class AccessibilityNodePrefetcher {
700
701 private static final int MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE = 50;
702
Svetoslav Ganov4528b4e2012-05-15 18:24:10 -0700703 private final ArrayList<View> mTempViewList = new ArrayList<View>();
704
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800705 public void prefetchAccessibilityNodeInfos(View view, int virtualViewId, int fetchFlags,
Svetoslav Ganov42138042012-03-20 11:51:39 -0700706 List<AccessibilityNodeInfo> outInfos) {
707 AccessibilityNodeProvider provider = view.getAccessibilityNodeProvider();
708 if (provider == null) {
709 AccessibilityNodeInfo root = view.createAccessibilityNodeInfo();
710 if (root != null) {
711 outInfos.add(root);
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800712 if ((fetchFlags & AccessibilityNodeInfo.FLAG_PREFETCH_PREDECESSORS) != 0) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700713 prefetchPredecessorsOfRealNode(view, outInfos);
714 }
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800715 if ((fetchFlags & AccessibilityNodeInfo.FLAG_PREFETCH_SIBLINGS) != 0) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700716 prefetchSiblingsOfRealNode(view, outInfos);
717 }
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800718 if ((fetchFlags & AccessibilityNodeInfo.FLAG_PREFETCH_DESCENDANTS) != 0) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700719 prefetchDescendantsOfRealNode(view, outInfos);
720 }
721 }
722 } else {
723 AccessibilityNodeInfo root = provider.createAccessibilityNodeInfo(virtualViewId);
724 if (root != null) {
725 outInfos.add(root);
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800726 if ((fetchFlags & AccessibilityNodeInfo.FLAG_PREFETCH_PREDECESSORS) != 0) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700727 prefetchPredecessorsOfVirtualNode(root, view, provider, outInfos);
728 }
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800729 if ((fetchFlags & AccessibilityNodeInfo.FLAG_PREFETCH_SIBLINGS) != 0) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700730 prefetchSiblingsOfVirtualNode(root, view, provider, outInfos);
731 }
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800732 if ((fetchFlags & AccessibilityNodeInfo.FLAG_PREFETCH_DESCENDANTS) != 0) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700733 prefetchDescendantsOfVirtualNode(root, provider, outInfos);
734 }
735 }
736 }
737 }
738
739 private void prefetchPredecessorsOfRealNode(View view,
740 List<AccessibilityNodeInfo> outInfos) {
741 ViewParent parent = view.getParentForAccessibility();
742 while (parent instanceof View
743 && outInfos.size() < MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE) {
744 View parentView = (View) parent;
Svetoslav Ganov42138042012-03-20 11:51:39 -0700745 AccessibilityNodeInfo info = parentView.createAccessibilityNodeInfo();
746 if (info != null) {
747 outInfos.add(info);
748 }
749 parent = parent.getParentForAccessibility();
750 }
751 }
752
753 private void prefetchSiblingsOfRealNode(View current,
754 List<AccessibilityNodeInfo> outInfos) {
755 ViewParent parent = current.getParentForAccessibility();
756 if (parent instanceof ViewGroup) {
757 ViewGroup parentGroup = (ViewGroup) parent;
Svetoslav Ganov4528b4e2012-05-15 18:24:10 -0700758 ArrayList<View> children = mTempViewList;
759 children.clear();
Svetoslav Ganov76f287e2012-04-23 11:02:36 -0700760 try {
Svetoslav Ganov4528b4e2012-05-15 18:24:10 -0700761 parentGroup.addChildrenForAccessibility(children);
762 final int childCount = children.size();
Svetoslav Ganov76f287e2012-04-23 11:02:36 -0700763 for (int i = 0; i < childCount; i++) {
764 if (outInfos.size() >= MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE) {
765 return;
Svetoslav Ganov42138042012-03-20 11:51:39 -0700766 }
Svetoslav Ganov4528b4e2012-05-15 18:24:10 -0700767 View child = children.get(i);
Svetoslav Ganov76f287e2012-04-23 11:02:36 -0700768 if (child.getAccessibilityViewId() != current.getAccessibilityViewId()
Svetoslav Ganov0a1bb6d2012-05-07 11:54:39 -0700769 && isShown(child)) {
Svetoslav Ganov76f287e2012-04-23 11:02:36 -0700770 AccessibilityNodeInfo info = null;
Svetoslav Ganov4528b4e2012-05-15 18:24:10 -0700771 AccessibilityNodeProvider provider =
772 child.getAccessibilityNodeProvider();
Svetoslav Ganov76f287e2012-04-23 11:02:36 -0700773 if (provider == null) {
774 info = child.createAccessibilityNodeInfo();
775 } else {
776 info = provider.createAccessibilityNodeInfo(
777 AccessibilityNodeInfo.UNDEFINED);
778 }
779 if (info != null) {
780 outInfos.add(info);
781 }
Svetoslav Ganov42138042012-03-20 11:51:39 -0700782 }
783 }
Svetoslav Ganov76f287e2012-04-23 11:02:36 -0700784 } finally {
Svetoslav Ganov4528b4e2012-05-15 18:24:10 -0700785 children.clear();
Svetoslav Ganov42138042012-03-20 11:51:39 -0700786 }
Svetoslav Ganov42138042012-03-20 11:51:39 -0700787 }
788 }
789
790 private void prefetchDescendantsOfRealNode(View root,
791 List<AccessibilityNodeInfo> outInfos) {
792 if (!(root instanceof ViewGroup)) {
793 return;
794 }
Svetoslav Ganov42138042012-03-20 11:51:39 -0700795 HashMap<View, AccessibilityNodeInfo> addedChildren =
796 new HashMap<View, AccessibilityNodeInfo>();
Svetoslav Ganov4528b4e2012-05-15 18:24:10 -0700797 ArrayList<View> children = mTempViewList;
798 children.clear();
Svetoslav Ganov76f287e2012-04-23 11:02:36 -0700799 try {
Svetoslav Ganov4528b4e2012-05-15 18:24:10 -0700800 root.addChildrenForAccessibility(children);
801 final int childCount = children.size();
Svetoslav Ganov76f287e2012-04-23 11:02:36 -0700802 for (int i = 0; i < childCount; i++) {
803 if (outInfos.size() >= MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE) {
804 return;
805 }
Svetoslav Ganov4528b4e2012-05-15 18:24:10 -0700806 View child = children.get(i);
Svetoslav Ganov0a1bb6d2012-05-07 11:54:39 -0700807 if (isShown(child)) {
Svetoslav Ganov76f287e2012-04-23 11:02:36 -0700808 AccessibilityNodeProvider provider = child.getAccessibilityNodeProvider();
809 if (provider == null) {
810 AccessibilityNodeInfo info = child.createAccessibilityNodeInfo();
811 if (info != null) {
812 outInfos.add(info);
813 addedChildren.put(child, null);
814 }
815 } else {
816 AccessibilityNodeInfo info = provider.createAccessibilityNodeInfo(
817 AccessibilityNodeInfo.UNDEFINED);
818 if (info != null) {
819 outInfos.add(info);
820 addedChildren.put(child, info);
821 }
Svetoslav Ganov42138042012-03-20 11:51:39 -0700822 }
823 }
824 }
Svetoslav Ganov76f287e2012-04-23 11:02:36 -0700825 } finally {
Svetoslav Ganov4528b4e2012-05-15 18:24:10 -0700826 children.clear();
Svetoslav Ganov42138042012-03-20 11:51:39 -0700827 }
Svetoslav Ganov42138042012-03-20 11:51:39 -0700828 if (outInfos.size() < MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE) {
829 for (Map.Entry<View, AccessibilityNodeInfo> entry : addedChildren.entrySet()) {
830 View addedChild = entry.getKey();
831 AccessibilityNodeInfo virtualRoot = entry.getValue();
832 if (virtualRoot == null) {
833 prefetchDescendantsOfRealNode(addedChild, outInfos);
834 } else {
835 AccessibilityNodeProvider provider =
836 addedChild.getAccessibilityNodeProvider();
837 prefetchDescendantsOfVirtualNode(virtualRoot, provider, outInfos);
838 }
839 }
840 }
841 }
842
843 private void prefetchPredecessorsOfVirtualNode(AccessibilityNodeInfo root,
844 View providerHost, AccessibilityNodeProvider provider,
845 List<AccessibilityNodeInfo> outInfos) {
846 long parentNodeId = root.getParentNodeId();
847 int accessibilityViewId = AccessibilityNodeInfo.getAccessibilityViewId(parentNodeId);
848 while (accessibilityViewId != AccessibilityNodeInfo.UNDEFINED) {
849 if (outInfos.size() >= MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE) {
850 return;
851 }
852 final int virtualDescendantId =
853 AccessibilityNodeInfo.getVirtualDescendantId(parentNodeId);
854 if (virtualDescendantId != AccessibilityNodeInfo.UNDEFINED
855 || accessibilityViewId == providerHost.getAccessibilityViewId()) {
856 AccessibilityNodeInfo parent = provider.createAccessibilityNodeInfo(
857 virtualDescendantId);
858 if (parent != null) {
859 outInfos.add(parent);
860 }
861 parentNodeId = parent.getParentNodeId();
862 accessibilityViewId = AccessibilityNodeInfo.getAccessibilityViewId(
863 parentNodeId);
864 } else {
865 prefetchPredecessorsOfRealNode(providerHost, outInfos);
866 return;
867 }
868 }
869 }
870
871 private void prefetchSiblingsOfVirtualNode(AccessibilityNodeInfo current, View providerHost,
872 AccessibilityNodeProvider provider, List<AccessibilityNodeInfo> outInfos) {
873 final long parentNodeId = current.getParentNodeId();
874 final int parentAccessibilityViewId =
875 AccessibilityNodeInfo.getAccessibilityViewId(parentNodeId);
876 final int parentVirtualDescendantId =
877 AccessibilityNodeInfo.getVirtualDescendantId(parentNodeId);
878 if (parentVirtualDescendantId != AccessibilityNodeInfo.UNDEFINED
879 || parentAccessibilityViewId == providerHost.getAccessibilityViewId()) {
880 AccessibilityNodeInfo parent =
881 provider.createAccessibilityNodeInfo(parentVirtualDescendantId);
882 if (parent != null) {
Alan Viverettef0aed092013-11-06 15:33:03 -0800883 final int childCount = parent.getChildCount();
Svetoslav Ganov42138042012-03-20 11:51:39 -0700884 for (int i = 0; i < childCount; i++) {
885 if (outInfos.size() >= MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE) {
886 return;
887 }
Alan Viverettef0aed092013-11-06 15:33:03 -0800888 final long childNodeId = parent.getChildId(i);
Svetoslav Ganov42138042012-03-20 11:51:39 -0700889 if (childNodeId != current.getSourceNodeId()) {
890 final int childVirtualDescendantId =
891 AccessibilityNodeInfo.getVirtualDescendantId(childNodeId);
892 AccessibilityNodeInfo child = provider.createAccessibilityNodeInfo(
893 childVirtualDescendantId);
894 if (child != null) {
895 outInfos.add(child);
896 }
897 }
898 }
899 }
900 } else {
901 prefetchSiblingsOfRealNode(providerHost, outInfos);
902 }
903 }
904
905 private void prefetchDescendantsOfVirtualNode(AccessibilityNodeInfo root,
906 AccessibilityNodeProvider provider, List<AccessibilityNodeInfo> outInfos) {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700907 final int initialOutInfosSize = outInfos.size();
Alan Viverettef0aed092013-11-06 15:33:03 -0800908 final int childCount = root.getChildCount();
Svetoslav Ganov42138042012-03-20 11:51:39 -0700909 for (int i = 0; i < childCount; i++) {
910 if (outInfos.size() >= MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE) {
911 return;
912 }
Alan Viverettef0aed092013-11-06 15:33:03 -0800913 final long childNodeId = root.getChildId(i);
Svetoslav Ganov42138042012-03-20 11:51:39 -0700914 AccessibilityNodeInfo child = provider.createAccessibilityNodeInfo(
915 AccessibilityNodeInfo.getVirtualDescendantId(childNodeId));
916 if (child != null) {
917 outInfos.add(child);
918 }
919 }
920 if (outInfos.size() < MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE) {
921 final int addedChildCount = outInfos.size() - initialOutInfosSize;
922 for (int i = 0; i < addedChildCount; i++) {
923 AccessibilityNodeInfo child = outInfos.get(initialOutInfosSize + i);
924 prefetchDescendantsOfVirtualNode(child, provider, outInfos);
925 }
926 }
927 }
928 }
929
930 private class PrivateHandler extends Handler {
931 private final static int MSG_PERFORM_ACCESSIBILITY_ACTION = 1;
932 private final static int MSG_FIND_ACCESSIBLITY_NODE_INFO_BY_ACCESSIBILITY_ID = 2;
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800933 private final static int MSG_FIND_ACCESSIBLITY_NODE_INFOS_BY_VIEW_ID = 3;
Svetoslav Ganov42138042012-03-20 11:51:39 -0700934 private final static int MSG_FIND_ACCESSIBLITY_NODE_INFO_BY_TEXT = 4;
935 private final static int MSG_FIND_FOCUS = 5;
936 private final static int MSG_FOCUS_SEARCH = 6;
937
Svetoslav Ganov005b83b2012-04-16 18:17:17 -0700938 public PrivateHandler(Looper looper) {
939 super(looper);
Svetoslav Ganov42138042012-03-20 11:51:39 -0700940 }
941
942 @Override
943 public String getMessageName(Message message) {
944 final int type = message.what;
945 switch (type) {
946 case MSG_PERFORM_ACCESSIBILITY_ACTION:
947 return "MSG_PERFORM_ACCESSIBILITY_ACTION";
948 case MSG_FIND_ACCESSIBLITY_NODE_INFO_BY_ACCESSIBILITY_ID:
949 return "MSG_FIND_ACCESSIBLITY_NODE_INFO_BY_ACCESSIBILITY_ID";
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800950 case MSG_FIND_ACCESSIBLITY_NODE_INFOS_BY_VIEW_ID:
951 return "MSG_FIND_ACCESSIBLITY_NODE_INFOS_BY_VIEW_ID";
Svetoslav Ganov42138042012-03-20 11:51:39 -0700952 case MSG_FIND_ACCESSIBLITY_NODE_INFO_BY_TEXT:
953 return "MSG_FIND_ACCESSIBLITY_NODE_INFO_BY_TEXT";
954 case MSG_FIND_FOCUS:
955 return "MSG_FIND_FOCUS";
956 case MSG_FOCUS_SEARCH:
957 return "MSG_FOCUS_SEARCH";
958 default:
959 throw new IllegalArgumentException("Unknown message type: " + type);
960 }
961 }
962
963 @Override
964 public void handleMessage(Message message) {
965 final int type = message.what;
966 switch (type) {
967 case MSG_FIND_ACCESSIBLITY_NODE_INFO_BY_ACCESSIBILITY_ID: {
968 findAccessibilityNodeInfoByAccessibilityIdUiThread(message);
969 } break;
970 case MSG_PERFORM_ACCESSIBILITY_ACTION: {
971 perfromAccessibilityActionUiThread(message);
972 } break;
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800973 case MSG_FIND_ACCESSIBLITY_NODE_INFOS_BY_VIEW_ID: {
974 findAccessibilityNodeInfosByViewIdUiThread(message);
Svetoslav Ganov42138042012-03-20 11:51:39 -0700975 } break;
976 case MSG_FIND_ACCESSIBLITY_NODE_INFO_BY_TEXT: {
977 findAccessibilityNodeInfosByTextUiThread(message);
978 } break;
979 case MSG_FIND_FOCUS: {
980 findFocusUiThread(message);
981 } break;
982 case MSG_FOCUS_SEARCH: {
983 focusSearchUiThread(message);
984 } break;
985 default:
986 throw new IllegalArgumentException("Unknown message type: " + type);
987 }
988 }
989 }
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800990
991 private final class AddNodeInfosForViewId implements Predicate<View> {
992 private int mViewId = View.NO_ID;
993 private List<AccessibilityNodeInfo> mInfos;
994
995 public void init(int viewId, List<AccessibilityNodeInfo> infos) {
996 mViewId = viewId;
997 mInfos = infos;
998 }
999
1000 public void reset() {
1001 mViewId = View.NO_ID;
1002 mInfos = null;
1003 }
1004
1005 @Override
1006 public boolean apply(View view) {
1007 if (view.getId() == mViewId && isShown(view)) {
1008 mInfos.add(view.createAccessibilityNodeInfo());
1009 }
1010 return false;
1011 }
1012 }
Svetoslav Ganov42138042012-03-20 11:51:39 -07001013}