blob: fb5f5b9e1efcaa6e5dc3dd55b157f57eaab1f1f2 [file] [log] [blame]
svetoslavganov75986cf2009-05-14 22:28:01 -07001/*
2 * Copyright (C) 2009 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.accessibilityservice;
18
Phil Weavera6b64f52015-12-04 15:21:35 -080019import android.accessibilityservice.GestureDescription.MotionEventGenerator;
Anna Galusza9b278112016-01-04 11:37:37 -080020import android.annotation.IntDef;
Alan Viverette7c9746d4e2014-11-19 17:02:16 -080021import android.annotation.NonNull;
Alan Viverette214fb682015-11-17 09:47:11 -050022import android.annotation.Nullable;
svetoslavganov75986cf2009-05-14 22:28:01 -070023import android.app.Service;
Svetoslav Ganov79311c42012-01-17 20:24:26 -080024import android.content.Context;
svetoslavganov75986cf2009-05-14 22:28:01 -070025import android.content.Intent;
Phil Weavera6b64f52015-12-04 15:21:35 -080026import android.content.pm.ParceledListSlice;
Alan Viverette214fb682015-11-17 09:47:11 -050027import android.graphics.Region;
28import android.os.Handler;
svetoslavganov75986cf2009-05-14 22:28:01 -070029import android.os.IBinder;
Svetoslav Ganov79311c42012-01-17 20:24:26 -080030import android.os.Looper;
svetoslavganov75986cf2009-05-14 22:28:01 -070031import android.os.Message;
32import android.os.RemoteException;
Anna Galusza9b278112016-01-04 11:37:37 -080033import android.provider.Settings;
Alan Viverette214fb682015-11-17 09:47:11 -050034import android.util.ArrayMap;
svetoslavganov75986cf2009-05-14 22:28:01 -070035import android.util.Log;
Phil Weavera6b64f52015-12-04 15:21:35 -080036import android.util.Pair;
Alan Viverette214fb682015-11-17 09:47:11 -050037import android.util.Slog;
Phil Weavera6b64f52015-12-04 15:21:35 -080038import android.util.SparseArray;
Svetoslavc4fccd12013-04-09 12:58:41 -070039import android.view.KeyEvent;
Phil Weavera6b64f52015-12-04 15:21:35 -080040import android.view.MotionEvent;
Svetoslav3a5c7212014-10-14 09:54:26 -070041import android.view.WindowManager;
Alan Viverette7c9746d4e2014-11-19 17:02:16 -080042import android.view.WindowManagerImpl;
svetoslavganov75986cf2009-05-14 22:28:01 -070043import android.view.accessibility.AccessibilityEvent;
Svetoslav Ganovd116d7c2011-11-21 18:41:59 -080044import android.view.accessibility.AccessibilityInteractionClient;
Svetoslav Ganov8643aa02011-04-20 12:12:33 -070045import android.view.accessibility.AccessibilityNodeInfo;
Svetoslav8e3feb12014-02-24 13:46:47 -080046import android.view.accessibility.AccessibilityWindowInfo;
svetoslavganov75986cf2009-05-14 22:28:01 -070047
Svetoslav Ganovd116d7c2011-11-21 18:41:59 -080048import com.android.internal.os.HandlerCaller;
Svetoslav3a5c7212014-10-14 09:54:26 -070049import com.android.internal.os.SomeArgs;
Svetoslav Ganovd116d7c2011-11-21 18:41:59 -080050
Anna Galusza9b278112016-01-04 11:37:37 -080051import java.lang.annotation.Retention;
52import java.lang.annotation.RetentionPolicy;
Svetoslav8e3feb12014-02-24 13:46:47 -080053import java.util.List;
54
svetoslavganov75986cf2009-05-14 22:28:01 -070055/**
Phil Weaver40ded282016-01-25 15:49:02 -080056 * Accessibility services are intended to assist users with disabilities in using
57 * Android devices and apps. They run in the background and receive callbacks by the system
svetoslavganov75986cf2009-05-14 22:28:01 -070058 * when {@link AccessibilityEvent}s are fired. Such events denote some state transition
59 * in the user interface, for example, the focus has changed, a button has been clicked,
Svetoslav Ganov38e8b4e2011-06-29 20:00:53 -070060 * etc. Such a service can optionally request the capability for querying the content
Scott Mainb303d832011-10-12 16:45:18 -070061 * of the active window. Development of an accessibility service requires extending this
62 * class and implementing its abstract methods.
Joe Fernandeze1302ed2012-02-06 14:30:15 -080063 *
64 * <div class="special reference">
65 * <h3>Developer Guides</h3>
66 * <p>For more information about creating AccessibilityServices, read the
67 * <a href="{@docRoot}guide/topics/ui/accessibility/index.html">Accessibility</a>
68 * developer guide.</p>
69 * </div>
70 *
Scott Mainb303d832011-10-12 16:45:18 -070071 * <h3>Lifecycle</h3>
svetoslavganov75986cf2009-05-14 22:28:01 -070072 * <p>
Svetoslav Ganov38e8b4e2011-06-29 20:00:53 -070073 * The lifecycle of an accessibility service is managed exclusively by the system and
Phil Weaver40ded282016-01-25 15:49:02 -080074 * follows the established service life cycle. Starting an accessibility service is triggered
75 * exclusively by the user explicitly turning the service on in device settings. After the system
76 * binds to a service, it calls {@link AccessibilityService#onServiceConnected()}. This method can
77 * be overriden by clients that want to perform post binding setup.
78 * </p>
79 * <p>
80 * An accessibility service stops either when the user turns it off in device settings or when
81 * it calls {@link AccessibilityService#disableSelf()}.
Svetoslav Ganov8643aa02011-04-20 12:12:33 -070082 * </p>
Scott Mainb303d832011-10-12 16:45:18 -070083 * <h3>Declaration</h3>
Svetoslav Ganov38e8b4e2011-06-29 20:00:53 -070084 * <p>
Phil Weaver40ded282016-01-25 15:49:02 -080085 * An accessibility is declared as any other service in an AndroidManifest.xml, but it
86 * must do two things:
87 * <ul>
88 * <ol>
89 * Specify that it handles the "android.accessibilityservice.AccessibilityService"
90 * {@link android.content.Intent}.
91 * </ol>
92 * <ol>
93 * Request the {@link android.Manifest.permission#BIND_ACCESSIBILITY_SERVICE} permission to
94 * ensure that only the system can bind to it.
95 * </ol>
96 * </ul>
97 * If either of these items is missing, the system will ignore the accessibility service.
98 * Following is an example declaration:
Svetoslav Ganov38e8b4e2011-06-29 20:00:53 -070099 * </p>
Svetoslav Ganov53e184d2012-05-16 15:57:10 -0700100 * <pre> &lt;service android:name=".MyAccessibilityService"
Scott Main53b0fda2012-08-06 12:50:48 -0700101 * android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE"&gt;
Svetoslav Ganov38e8b4e2011-06-29 20:00:53 -0700102 * &lt;intent-filter&gt;
Scott Mainb303d832011-10-12 16:45:18 -0700103 * &lt;action android:name="android.accessibilityservice.AccessibilityService" /&gt;
Svetoslav Ganov38e8b4e2011-06-29 20:00:53 -0700104 * &lt;/intent-filter&gt;
105 * . . .
Scott Mainb303d832011-10-12 16:45:18 -0700106 * &lt;/service&gt;</pre>
107 * <h3>Configuration</h3>
Svetoslav Ganov38e8b4e2011-06-29 20:00:53 -0700108 * <p>
Svetoslav Ganov8643aa02011-04-20 12:12:33 -0700109 * An accessibility service can be configured to receive specific types of accessibility events,
110 * listen only to specific packages, get events from each type only once in a given time frame,
111 * retrieve window content, specify a settings activity, etc.
Svetoslav Ganovcc4053e2011-05-23 13:37:44 -0700112 * </p>
Svetoslav Ganov38e8b4e2011-06-29 20:00:53 -0700113 * <p>
Svetoslav Ganovcc4053e2011-05-23 13:37:44 -0700114 * There are two approaches for configuring an accessibility service:
Svetoslav Ganov38e8b4e2011-06-29 20:00:53 -0700115 * </p>
Svetoslav Ganovcc4053e2011-05-23 13:37:44 -0700116 * <ul>
Svetoslav Ganov38e8b4e2011-06-29 20:00:53 -0700117 * <li>
118 * Providing a {@link #SERVICE_META_DATA meta-data} entry in the manifest when declaring
119 * the service. A service declaration with a meta-data tag is presented below:
Scott Mainb303d832011-10-12 16:45:18 -0700120 * <pre> &lt;service android:name=".MyAccessibilityService"&gt;
Svetoslav Ganov38e8b4e2011-06-29 20:00:53 -0700121 * &lt;intent-filter&gt;
Scott Mainb303d832011-10-12 16:45:18 -0700122 * &lt;action android:name="android.accessibilityservice.AccessibilityService" /&gt;
Svetoslav Ganov38e8b4e2011-06-29 20:00:53 -0700123 * &lt;/intent-filter&gt;
124 * &lt;meta-data android:name="android.accessibilityservice" android:resource="@xml/accessibilityservice" /&gt;
Scott Mainb303d832011-10-12 16:45:18 -0700125 * &lt;/service&gt;</pre>
126 * <p class="note">
127 * <strong>Note:</strong> This approach enables setting all properties.
Svetoslav Ganov38e8b4e2011-06-29 20:00:53 -0700128 * </p>
129 * <p>
130 * For more details refer to {@link #SERVICE_META_DATA} and
Scott Mainb303d832011-10-12 16:45:18 -0700131 * <code>&lt;{@link android.R.styleable#AccessibilityService accessibility-service}&gt;</code>.
Svetoslav Ganov38e8b4e2011-06-29 20:00:53 -0700132 * </p>
133 * </li>
134 * <li>
135 * Calling {@link AccessibilityService#setServiceInfo(AccessibilityServiceInfo)}. Note
136 * that this method can be called any time to dynamically change the service configuration.
Scott Mainb303d832011-10-12 16:45:18 -0700137 * <p class="note">
Svetoslav Ganov38e8b4e2011-06-29 20:00:53 -0700138 * <strong>Note:</strong> This approach enables setting only dynamically configurable properties:
139 * {@link AccessibilityServiceInfo#eventTypes},
140 * {@link AccessibilityServiceInfo#feedbackType},
141 * {@link AccessibilityServiceInfo#flags},
142 * {@link AccessibilityServiceInfo#notificationTimeout},
143 * {@link AccessibilityServiceInfo#packageNames}
144 * </p>
145 * <p>
146 * For more details refer to {@link AccessibilityServiceInfo}.
147 * </p>
148 * </li>
Svetoslav Ganovcc4053e2011-05-23 13:37:44 -0700149 * </ul>
Scott Mainb303d832011-10-12 16:45:18 -0700150 * <h3>Retrieving window content</h3>
Svetoslav Ganov38e8b4e2011-06-29 20:00:53 -0700151 * <p>
Phil Weaver40ded282016-01-25 15:49:02 -0800152 * A service can specify in its declaration that it can retrieve window
153 * content which is represented as a tree of {@link AccessibilityWindowInfo} and
154 * {@link AccessibilityNodeInfo} objects. Note that
Svetoslav Ganov38e8b4e2011-06-29 20:00:53 -0700155 * declaring this capability requires that the service declares its configuration via
156 * an XML resource referenced by {@link #SERVICE_META_DATA}.
157 * </p>
158 * <p>
Phil Weaver40ded282016-01-25 15:49:02 -0800159 * Window content may be retrieved with
160 * {@link AccessibilityEvent#getSource() AccessibilityEvent.getSource()},
161 * {@link AccessibilityService#findFocus(int)},
162 * {@link AccessibilityService#getWindows()}, or
163 * {@link AccessibilityService#getRootInActiveWindow()}.
Svetoslav Ganov38e8b4e2011-06-29 20:00:53 -0700164 * </p>
Scott Mainb303d832011-10-12 16:45:18 -0700165 * <p class="note">
166 * <strong>Note</strong> An accessibility service may have requested to be notified for
Phil Weaver40ded282016-01-25 15:49:02 -0800167 * a subset of the event types, and thus be unaware when the node hierarchy has changed. It is also
168 * possible for a node to contain outdated information because the window content may change at any
169 * time.
Svetoslav Ganov38e8b4e2011-06-29 20:00:53 -0700170 * </p>
Scott Mainb303d832011-10-12 16:45:18 -0700171 * <h3>Notification strategy</h3>
svetoslavganov75986cf2009-05-14 22:28:01 -0700172 * <p>
Phil Weaver14ed6cf2015-11-20 14:46:28 -0800173 * All accessibility services are notified of all events they have requested, regardless of their
174 * feedback type.
Svetoslav Ganov38e8b4e2011-06-29 20:00:53 -0700175 * </p>
Scott Mainb303d832011-10-12 16:45:18 -0700176 * <p class="note">
Svetoslav Ganov38e8b4e2011-06-29 20:00:53 -0700177 * <strong>Note:</strong> The event notification timeout is useful to avoid propagating
178 * events to the client too frequently since this is accomplished via an expensive
179 * interprocess call. One can think of the timeout as a criteria to determine when
Scott Mainb303d832011-10-12 16:45:18 -0700180 * event generation has settled down.</p>
181 * <h3>Event types</h3>
182 * <ul>
Svetoslav8e3feb12014-02-24 13:46:47 -0800183 * <li>{@link AccessibilityEvent#TYPE_VIEW_CLICKED}</li>
184 * <li>{@link AccessibilityEvent#TYPE_VIEW_LONG_CLICKED}</li>
185 * <li>{@link AccessibilityEvent#TYPE_VIEW_FOCUSED}</li>
186 * <li>{@link AccessibilityEvent#TYPE_VIEW_SELECTED}</li>
187 * <li>{@link AccessibilityEvent#TYPE_VIEW_TEXT_CHANGED}</li>
188 * <li>{@link AccessibilityEvent#TYPE_WINDOW_STATE_CHANGED}</li>
189 * <li>{@link AccessibilityEvent#TYPE_NOTIFICATION_STATE_CHANGED}</li>
190 * <li>{@link AccessibilityEvent#TYPE_TOUCH_EXPLORATION_GESTURE_START}</li>
191 * <li>{@link AccessibilityEvent#TYPE_TOUCH_EXPLORATION_GESTURE_END}</li>
192 * <li>{@link AccessibilityEvent#TYPE_VIEW_HOVER_ENTER}</li>
193 * <li>{@link AccessibilityEvent#TYPE_VIEW_HOVER_EXIT}</li>
194 * <li>{@link AccessibilityEvent#TYPE_VIEW_SCROLLED}</li>
195 * <li>{@link AccessibilityEvent#TYPE_VIEW_TEXT_SELECTION_CHANGED}</li>
196 * <li>{@link AccessibilityEvent#TYPE_WINDOW_CONTENT_CHANGED}</li>
197 * <li>{@link AccessibilityEvent#TYPE_ANNOUNCEMENT}</li>
198 * <li>{@link AccessibilityEvent#TYPE_GESTURE_DETECTION_START}</li>
199 * <li>{@link AccessibilityEvent#TYPE_GESTURE_DETECTION_END}</li>
200 * <li>{@link AccessibilityEvent#TYPE_TOUCH_INTERACTION_START}</li>
201 * <li>{@link AccessibilityEvent#TYPE_TOUCH_INTERACTION_END}</li>
202 * <li>{@link AccessibilityEvent#TYPE_VIEW_ACCESSIBILITY_FOCUSED}</li>
203 * <li>{@link AccessibilityEvent#TYPE_WINDOWS_CHANGED}</li>
204 * <li>{@link AccessibilityEvent#TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED}</li>
Scott Mainb303d832011-10-12 16:45:18 -0700205 * </ul>
206 * <h3>Feedback types</h3>
207 * <ul>
Svetoslav8e3feb12014-02-24 13:46:47 -0800208 * <li>{@link AccessibilityServiceInfo#FEEDBACK_AUDIBLE}</li>
209 * <li>{@link AccessibilityServiceInfo#FEEDBACK_HAPTIC}</li>
210 * <li>{@link AccessibilityServiceInfo#FEEDBACK_AUDIBLE}</li>
211 * <li>{@link AccessibilityServiceInfo#FEEDBACK_VISUAL}</li>
212 * <li>{@link AccessibilityServiceInfo#FEEDBACK_GENERIC}</li>
213 * <li>{@link AccessibilityServiceInfo#FEEDBACK_BRAILLE}</li>
Scott Mainb303d832011-10-12 16:45:18 -0700214 * </ul>
215 * @see AccessibilityEvent
216 * @see AccessibilityServiceInfo
217 * @see android.view.accessibility.AccessibilityManager
svetoslavganov75986cf2009-05-14 22:28:01 -0700218 */
219public abstract class AccessibilityService extends Service {
Svetoslav Ganov42138042012-03-20 11:51:39 -0700220
221 /**
222 * The user has performed a swipe up gesture on the touch screen.
223 */
224 public static final int GESTURE_SWIPE_UP = 1;
225
226 /**
227 * The user has performed a swipe down gesture on the touch screen.
228 */
229 public static final int GESTURE_SWIPE_DOWN = 2;
230
231 /**
232 * The user has performed a swipe left gesture on the touch screen.
233 */
234 public static final int GESTURE_SWIPE_LEFT = 3;
235
236 /**
237 * The user has performed a swipe right gesture on the touch screen.
238 */
239 public static final int GESTURE_SWIPE_RIGHT = 4;
240
241 /**
242 * The user has performed a swipe left and right gesture on the touch screen.
243 */
244 public static final int GESTURE_SWIPE_LEFT_AND_RIGHT = 5;
245
246 /**
247 * The user has performed a swipe right and left gesture on the touch screen.
248 */
249 public static final int GESTURE_SWIPE_RIGHT_AND_LEFT = 6;
250
251 /**
252 * The user has performed a swipe up and down gesture on the touch screen.
253 */
254 public static final int GESTURE_SWIPE_UP_AND_DOWN = 7;
255
256 /**
257 * The user has performed a swipe down and up gesture on the touch screen.
258 */
259 public static final int GESTURE_SWIPE_DOWN_AND_UP = 8;
260
261 /**
Svetoslav Ganov005b83b2012-04-16 18:17:17 -0700262 * The user has performed a left and up gesture on the touch screen.
263 */
Svetoslav Ganov65d98ad2012-05-02 17:57:06 -0700264 public static final int GESTURE_SWIPE_LEFT_AND_UP = 9;
Svetoslav Ganov005b83b2012-04-16 18:17:17 -0700265
266 /**
267 * The user has performed a left and down gesture on the touch screen.
268 */
Svetoslav Ganov65d98ad2012-05-02 17:57:06 -0700269 public static final int GESTURE_SWIPE_LEFT_AND_DOWN = 10;
Svetoslav Ganov005b83b2012-04-16 18:17:17 -0700270
271 /**
272 * The user has performed a right and up gesture on the touch screen.
273 */
Svetoslav Ganov65d98ad2012-05-02 17:57:06 -0700274 public static final int GESTURE_SWIPE_RIGHT_AND_UP = 11;
Svetoslav Ganov005b83b2012-04-16 18:17:17 -0700275
276 /**
277 * The user has performed a right and down gesture on the touch screen.
278 */
Svetoslav Ganov65d98ad2012-05-02 17:57:06 -0700279 public static final int GESTURE_SWIPE_RIGHT_AND_DOWN = 12;
Svetoslav Ganov005b83b2012-04-16 18:17:17 -0700280
281 /**
282 * The user has performed an up and left gesture on the touch screen.
283 */
Svetoslav Ganov65d98ad2012-05-02 17:57:06 -0700284 public static final int GESTURE_SWIPE_UP_AND_LEFT = 13;
Svetoslav Ganov005b83b2012-04-16 18:17:17 -0700285
286 /**
287 * The user has performed an up and right gesture on the touch screen.
288 */
Svetoslav Ganov65d98ad2012-05-02 17:57:06 -0700289 public static final int GESTURE_SWIPE_UP_AND_RIGHT = 14;
Svetoslav Ganov005b83b2012-04-16 18:17:17 -0700290
291 /**
292 * The user has performed an down and left gesture on the touch screen.
293 */
Svetoslav Ganov65d98ad2012-05-02 17:57:06 -0700294 public static final int GESTURE_SWIPE_DOWN_AND_LEFT = 15;
Svetoslav Ganov005b83b2012-04-16 18:17:17 -0700295
296 /**
297 * The user has performed an down and right gesture on the touch screen.
298 */
Svetoslav Ganov65d98ad2012-05-02 17:57:06 -0700299 public static final int GESTURE_SWIPE_DOWN_AND_RIGHT = 16;
Svetoslav Ganov005b83b2012-04-16 18:17:17 -0700300
301 /**
svetoslavganov75986cf2009-05-14 22:28:01 -0700302 * The {@link Intent} that must be declared as handled by the service.
303 */
304 public static final String SERVICE_INTERFACE =
305 "android.accessibilityservice.AccessibilityService";
306
Svetoslav Ganovcc4053e2011-05-23 13:37:44 -0700307 /**
308 * Name under which an AccessibilityService component publishes information
Svetoslav Ganov38e8b4e2011-06-29 20:00:53 -0700309 * about itself. This meta-data must reference an XML resource containing an
Svetoslav Ganovcc4053e2011-05-23 13:37:44 -0700310 * <code>&lt;{@link android.R.styleable#AccessibilityService accessibility-service}&gt;</code>
311 * tag. This is a a sample XML file configuring an accessibility service:
Scott Mainb303d832011-10-12 16:45:18 -0700312 * <pre> &lt;accessibility-service
Svetoslav Ganov38e8b4e2011-06-29 20:00:53 -0700313 * android:accessibilityEventTypes="typeViewClicked|typeViewFocused"
314 * android:packageNames="foo.bar, foo.baz"
315 * android:accessibilityFeedbackType="feedbackSpoken"
316 * android:notificationTimeout="100"
317 * android:accessibilityFlags="flagDefault"
318 * android:settingsActivity="foo.bar.TestBackActivity"
319 * android:canRetrieveWindowContent="true"
Svetoslav688a6972013-04-16 18:55:38 -0700320 * android:canRequestTouchExplorationMode="true"
Svetoslav Ganov38e8b4e2011-06-29 20:00:53 -0700321 * . . .
Scott Mainb303d832011-10-12 16:45:18 -0700322 * /&gt;</pre>
Svetoslav Ganovcc4053e2011-05-23 13:37:44 -0700323 */
324 public static final String SERVICE_META_DATA = "android.accessibilityservice";
325
Svetoslav Ganov005b83b2012-04-16 18:17:17 -0700326 /**
327 * Action to go back.
328 */
329 public static final int GLOBAL_ACTION_BACK = 1;
330
331 /**
332 * Action to go home.
333 */
334 public static final int GLOBAL_ACTION_HOME = 2;
335
336 /**
Phil Weaver315c34e2016-02-19 15:12:29 -0800337 * Action to toggle showing the overview of recent apps
Svetoslav Ganov005b83b2012-04-16 18:17:17 -0700338 */
339 public static final int GLOBAL_ACTION_RECENTS = 3;
340
341 /**
342 * Action to open the notifications.
343 */
344 public static final int GLOBAL_ACTION_NOTIFICATIONS = 4;
345
Svetoslav Ganove20a1772012-09-25 16:07:46 -0700346 /**
347 * Action to open the quick settings.
348 */
349 public static final int GLOBAL_ACTION_QUICK_SETTINGS = 5;
350
Alan Viverettee34560b22014-07-10 14:50:06 -0700351 /**
352 * Action to open the power long-press dialog.
353 */
354 public static final int GLOBAL_ACTION_POWER_DIALOG = 6;
355
Phil Weaver315c34e2016-02-19 15:12:29 -0800356 /**
357 * Action to toggle docking the current app's window
358 */
359 public static final int GLOBAL_ACTION_TOGGLE_SPLIT_SCREEN = 7;
360
svetoslavganov75986cf2009-05-14 22:28:01 -0700361 private static final String LOG_TAG = "AccessibilityService";
362
Svetoslav Ganov80943d82013-01-02 10:25:37 -0800363 /**
364 * @hide
365 */
366 public interface Callbacks {
Svetoslav Ganov79311c42012-01-17 20:24:26 -0800367 public void onAccessibilityEvent(AccessibilityEvent event);
368 public void onInterrupt();
369 public void onServiceConnected();
Svetoslav3a5c7212014-10-14 09:54:26 -0700370 public void init(int connectionId, IBinder windowToken);
Svetoslav Ganovfefd20e2012-04-19 21:44:35 -0700371 public boolean onGesture(int gestureId);
Svetoslavc4fccd12013-04-09 12:58:41 -0700372 public boolean onKeyEvent(KeyEvent event);
Alan Viverette214fb682015-11-17 09:47:11 -0500373 public void onMagnificationChanged(@NonNull Region region,
374 float scale, float centerX, float centerY);
Anna Galusza9b278112016-01-04 11:37:37 -0800375 public void onSoftKeyboardShowModeChanged(int showMode);
Phil Weavera6b64f52015-12-04 15:21:35 -0800376 public void onPerformGestureResult(int sequence, boolean completedSuccessfully);
Svetoslav Ganov79311c42012-01-17 20:24:26 -0800377 }
svetoslavganov75986cf2009-05-14 22:28:01 -0700378
Anna Galusza9b278112016-01-04 11:37:37 -0800379 /**
380 * Annotations for Soft Keyboard show modes so tools can catch invalid show modes.
381 * @hide
382 */
383 @Retention(RetentionPolicy.SOURCE)
384 @IntDef({SHOW_MODE_AUTO, SHOW_MODE_HIDDEN})
385 public @interface SoftKeyboardShowMode {};
386 /**
387 * @hide
388 */
389 public static final int SHOW_MODE_AUTO = 0;
390 /**
391 * @hide
392 */
393 public static final int SHOW_MODE_HIDDEN = 1;
394
Svetoslav Ganovd116d7c2011-11-21 18:41:59 -0800395 private int mConnectionId;
svetoslavganov75986cf2009-05-14 22:28:01 -0700396
Svetoslav Ganov79311c42012-01-17 20:24:26 -0800397 private AccessibilityServiceInfo mInfo;
398
Svetoslav3a5c7212014-10-14 09:54:26 -0700399 private IBinder mWindowToken;
400
401 private WindowManager mWindowManager;
402
Alan Viverette214fb682015-11-17 09:47:11 -0500403 private MagnificationController mMagnificationController;
Anna Galusza9b278112016-01-04 11:37:37 -0800404 private SoftKeyboardController mSoftKeyboardController;
Alan Viverette214fb682015-11-17 09:47:11 -0500405
Phil Weavera6b64f52015-12-04 15:21:35 -0800406 private int mGestureStatusCallbackSequence;
407
408 private SparseArray<GestureResultCallbackInfo> mGestureStatusCallbackInfos;
409
410 private final Object mLock = new Object();
411
svetoslavganov75986cf2009-05-14 22:28:01 -0700412 /**
413 * Callback for {@link android.view.accessibility.AccessibilityEvent}s.
414 *
415 * @param event An event.
416 */
417 public abstract void onAccessibilityEvent(AccessibilityEvent event);
418
419 /**
420 * Callback for interrupting the accessibility feedback.
421 */
422 public abstract void onInterrupt();
423
424 /**
Alan Viverette214fb682015-11-17 09:47:11 -0500425 * Dispatches service connection to internal components first, then the
426 * client code.
427 */
428 private void dispatchServiceConnected() {
429 if (mMagnificationController != null) {
430 mMagnificationController.onServiceConnected();
431 }
432
433 // The client gets to handle service connection last, after we've set
434 // up any state upon which their code may rely.
435 onServiceConnected();
436 }
437
438 /**
svetoslavganov75986cf2009-05-14 22:28:01 -0700439 * This method is a part of the {@link AccessibilityService} lifecycle and is
440 * called after the system has successfully bound to the service. If is
441 * convenient to use this method for setting the {@link AccessibilityServiceInfo}.
442 *
443 * @see AccessibilityServiceInfo
444 * @see #setServiceInfo(AccessibilityServiceInfo)
445 */
446 protected void onServiceConnected() {
447
448 }
449
450 /**
Svetoslav Ganov42138042012-03-20 11:51:39 -0700451 * Called by the system when the user performs a specific gesture on the
Svetoslav Ganove4abc512012-05-09 11:02:38 -0700452 * touch screen.
Svetoslav Ganovfefd20e2012-04-19 21:44:35 -0700453 *
Svetoslav Ganove4abc512012-05-09 11:02:38 -0700454 * <strong>Note:</strong> To receive gestures an accessibility service must
455 * request that the device is in touch exploration mode by setting the
Svetoslav Ganov7b1e0c72012-05-13 11:57:29 -0700456 * {@link android.accessibilityservice.AccessibilityServiceInfo#FLAG_REQUEST_TOUCH_EXPLORATION_MODE}
Svetoslav Ganove4abc512012-05-09 11:02:38 -0700457 * flag.
Svetoslav Ganov42138042012-03-20 11:51:39 -0700458 *
459 * @param gestureId The unique id of the performed gesture.
460 *
Svetoslav Ganovfefd20e2012-04-19 21:44:35 -0700461 * @return Whether the gesture was handled.
462 *
Svetoslav Ganov42138042012-03-20 11:51:39 -0700463 * @see #GESTURE_SWIPE_UP
Svetoslav Ganovfefd20e2012-04-19 21:44:35 -0700464 * @see #GESTURE_SWIPE_UP_AND_LEFT
Svetoslav Ganov42138042012-03-20 11:51:39 -0700465 * @see #GESTURE_SWIPE_UP_AND_DOWN
Svetoslav Ganovfefd20e2012-04-19 21:44:35 -0700466 * @see #GESTURE_SWIPE_UP_AND_RIGHT
467 * @see #GESTURE_SWIPE_DOWN
468 * @see #GESTURE_SWIPE_DOWN_AND_LEFT
Svetoslav Ganov42138042012-03-20 11:51:39 -0700469 * @see #GESTURE_SWIPE_DOWN_AND_UP
Svetoslav Ganovfefd20e2012-04-19 21:44:35 -0700470 * @see #GESTURE_SWIPE_DOWN_AND_RIGHT
471 * @see #GESTURE_SWIPE_LEFT
472 * @see #GESTURE_SWIPE_LEFT_AND_UP
Svetoslav Ganov42138042012-03-20 11:51:39 -0700473 * @see #GESTURE_SWIPE_LEFT_AND_RIGHT
Svetoslav Ganovfefd20e2012-04-19 21:44:35 -0700474 * @see #GESTURE_SWIPE_LEFT_AND_DOWN
475 * @see #GESTURE_SWIPE_RIGHT
476 * @see #GESTURE_SWIPE_RIGHT_AND_UP
Svetoslav Ganov42138042012-03-20 11:51:39 -0700477 * @see #GESTURE_SWIPE_RIGHT_AND_LEFT
Svetoslav Ganovfefd20e2012-04-19 21:44:35 -0700478 * @see #GESTURE_SWIPE_RIGHT_AND_DOWN
Svetoslav Ganov42138042012-03-20 11:51:39 -0700479 */
Svetoslav Ganovfefd20e2012-04-19 21:44:35 -0700480 protected boolean onGesture(int gestureId) {
Svetoslav Ganovfefd20e2012-04-19 21:44:35 -0700481 return false;
Svetoslav Ganov42138042012-03-20 11:51:39 -0700482 }
483
484 /**
Svetoslavc4fccd12013-04-09 12:58:41 -0700485 * Callback that allows an accessibility service to observe the key events
486 * before they are passed to the rest of the system. This means that the events
487 * are first delivered here before they are passed to the device policy, the
488 * input method, or applications.
489 * <p>
490 * <strong>Note:</strong> It is important that key events are handled in such
491 * a way that the event stream that would be passed to the rest of the system
492 * is well-formed. For example, handling the down event but not the up event
493 * and vice versa would generate an inconsistent event stream.
494 * </p>
495 * <p>
496 * <strong>Note:</strong> The key events delivered in this method are copies
497 * and modifying them will have no effect on the events that will be passed
498 * to the system. This method is intended to perform purely filtering
499 * functionality.
500 * <p>
501 *
502 * @param event The event to be processed.
503 * @return If true then the event will be consumed and not delivered to
504 * applications, otherwise it will be delivered as usual.
505 */
506 protected boolean onKeyEvent(KeyEvent event) {
507 return false;
508 }
509
510 /**
Svetoslav8e3feb12014-02-24 13:46:47 -0800511 * Gets the windows on the screen. This method returns only the windows
512 * that a sighted user can interact with, as opposed to all windows.
513 * For example, if there is a modal dialog shown and the user cannot touch
514 * anything behind it, then only the modal window will be reported
515 * (assuming it is the top one). For convenience the returned windows
516 * are ordered in a descending layer order, which is the windows that
Svetoslavf7174e82014-06-12 11:29:35 -0700517 * are higher in the Z-order are reported first. Since the user can always
518 * interact with the window that has input focus by typing, the focused
519 * window is always returned (even if covered by a modal window).
Svetoslav8e3feb12014-02-24 13:46:47 -0800520 * <p>
521 * <strong>Note:</strong> In order to access the windows your service has
522 * to declare the capability to retrieve window content by setting the
523 * {@link android.R.styleable#AccessibilityService_canRetrieveWindowContent}
524 * property in its meta-data. For details refer to {@link #SERVICE_META_DATA}.
525 * Also the service has to opt-in to retrieve the interactive windows by
526 * setting the {@link AccessibilityServiceInfo#FLAG_RETRIEVE_INTERACTIVE_WINDOWS}
527 * flag.
528 * </p>
529 *
530 * @return The windows if there are windows and the service is can retrieve
531 * them, otherwise an empty list.
532 */
533 public List<AccessibilityWindowInfo> getWindows() {
534 return AccessibilityInteractionClient.getInstance().getWindows(mConnectionId);
535 }
536
537 /**
Svetoslav Ganov0846e292012-04-18 18:47:13 -0700538 * Gets the root node in the currently active window if this service
Svetoslav8e3feb12014-02-24 13:46:47 -0800539 * can retrieve window content. The active window is the one that the user
540 * is currently touching or the window with input focus, if the user is not
541 * touching any window.
542 * <p>
Phil Weaver40ded282016-01-25 15:49:02 -0800543 * The currently active window is defined as the window that most recently fired one
544 * of the following events:
545 * {@link AccessibilityEvent#TYPE_WINDOW_STATE_CHANGED},
546 * {@link AccessibilityEvent#TYPE_VIEW_HOVER_ENTER},
547 * {@link AccessibilityEvent#TYPE_VIEW_HOVER_EXIT}.
548 * In other words, the last window shown that also has input focus.
549 * </p>
550 * <p>
Svetoslav8e3feb12014-02-24 13:46:47 -0800551 * <strong>Note:</strong> In order to access the root node your service has
552 * to declare the capability to retrieve window content by setting the
553 * {@link android.R.styleable#AccessibilityService_canRetrieveWindowContent}
554 * property in its meta-data. For details refer to {@link #SERVICE_META_DATA}.
555 * </p>
Svetoslav Ganov0846e292012-04-18 18:47:13 -0700556 *
557 * @return The root node if this service can retrieve window content.
558 */
559 public AccessibilityNodeInfo getRootInActiveWindow() {
Svetoslav Ganovfefd20e2012-04-19 21:44:35 -0700560 return AccessibilityInteractionClient.getInstance().getRootInActiveWindow(mConnectionId);
Svetoslav Ganov0846e292012-04-18 18:47:13 -0700561 }
562
563 /**
Phil Weaver40ded282016-01-25 15:49:02 -0800564 * Disables the service. After calling this method, the service will be disabled and settings
565 * will show that it is turned off.
Jinsong Mua9e7a3b2015-12-14 15:58:41 -0800566 */
567 public final void disableSelf() {
568 final IAccessibilityServiceConnection connection =
569 AccessibilityInteractionClient.getInstance().getConnection(mConnectionId);
570 if (connection != null) {
571 try {
572 connection.disableSelf();
573 } catch (RemoteException re) {
574 throw new RuntimeException(re);
575 }
576 }
577 }
578
579 /**
Alan Viverette214fb682015-11-17 09:47:11 -0500580 * Returns the magnification controller, which may be used to query and
581 * modify the state of display magnification.
582 * <p>
583 * <strong>Note:</strong> In order to control magnification, your service
584 * must declare the capability by setting the
585 * {@link android.R.styleable#AccessibilityService_canControlMagnification}
586 * property in its meta-data. For more information, see
587 * {@link #SERVICE_META_DATA}.
588 *
589 * @return the magnification controller
590 */
591 @NonNull
592 public final MagnificationController getMagnificationController() {
Anna Galusza9b278112016-01-04 11:37:37 -0800593 synchronized (mLock) {
594 if (mMagnificationController == null) {
595 mMagnificationController = new MagnificationController(this, mLock);
596 }
597 return mMagnificationController;
Alan Viverette214fb682015-11-17 09:47:11 -0500598 }
Alan Viverette214fb682015-11-17 09:47:11 -0500599 }
600
Phil Weavera6b64f52015-12-04 15:21:35 -0800601 /**
602 * Dispatch a gesture to the touch screen. Any gestures currently in progress, whether from
603 * the user, this service, or another service, will be cancelled.
604 * <p>
605 * <strong>Note:</strong> In order to dispatch gestures, your service
606 * must declare the capability by setting the
607 * {@link android.R.styleable#AccessibilityService_canPerformGestures}
608 * property in its meta-data. For more information, see
609 * {@link #SERVICE_META_DATA}.
610 *
611 * @param gesture The gesture to dispatch
612 * @param callback The object to call back when the status of the gesture is known. If
613 * {@code null}, no status is reported.
614 * @param handler The handler on which to call back the {@code callback} object. If
615 * {@code null}, the object is called back on the service's main thread.
616 *
617 * @return {@code true} if the gesture is dispatched, {@code false} if not.
618 */
619 public final boolean dispatchGesture(@NonNull GestureDescription gesture,
620 @Nullable GestureResultCallback callback,
621 @Nullable Handler handler) {
622 final IAccessibilityServiceConnection connection =
623 AccessibilityInteractionClient.getInstance().getConnection(
624 mConnectionId);
625 if (connection == null) {
626 return false;
627 }
628 List<MotionEvent> events = MotionEventGenerator.getMotionEventsFromGestureDescription(
629 gesture, 100);
630 try {
631 synchronized (mLock) {
Phil Weaver78d2e2d2016-02-02 14:47:44 -0800632 mGestureStatusCallbackSequence++;
Phil Weavera6b64f52015-12-04 15:21:35 -0800633 if (callback != null) {
634 if (mGestureStatusCallbackInfos == null) {
635 mGestureStatusCallbackInfos = new SparseArray<>();
636 }
637 GestureResultCallbackInfo callbackInfo = new GestureResultCallbackInfo(gesture,
638 callback, handler);
639 mGestureStatusCallbackInfos.put(mGestureStatusCallbackSequence, callbackInfo);
640 }
Phil Weaver78d2e2d2016-02-02 14:47:44 -0800641 connection.sendMotionEvents(mGestureStatusCallbackSequence,
642 new ParceledListSlice<>(events));
Phil Weavera6b64f52015-12-04 15:21:35 -0800643 }
644 } catch (RemoteException re) {
645 throw new RuntimeException(re);
646 }
647 return true;
648 }
649
650 void onPerformGestureResult(int sequence, final boolean completedSuccessfully) {
651 if (mGestureStatusCallbackInfos == null) {
652 return;
653 }
654 GestureResultCallbackInfo callbackInfo;
655 synchronized (mLock) {
656 callbackInfo = mGestureStatusCallbackInfos.get(sequence);
657 }
658 final GestureResultCallbackInfo finalCallbackInfo = callbackInfo;
659 if ((callbackInfo != null) && (callbackInfo.gestureDescription != null)
660 && (callbackInfo.callback != null)) {
661 if (callbackInfo.handler != null) {
662 callbackInfo.handler.post(new Runnable() {
663 @Override
664 public void run() {
665 if (completedSuccessfully) {
666 finalCallbackInfo.callback
667 .onCompleted(finalCallbackInfo.gestureDescription);
668 } else {
669 finalCallbackInfo.callback
670 .onCancelled(finalCallbackInfo.gestureDescription);
671 }
672 }
673 });
674 return;
675 }
676 if (completedSuccessfully) {
677 callbackInfo.callback.onCompleted(callbackInfo.gestureDescription);
678 } else {
679 callbackInfo.callback.onCancelled(callbackInfo.gestureDescription);
680 }
681 }
682 }
683
Alan Viverette214fb682015-11-17 09:47:11 -0500684 private void onMagnificationChanged(@NonNull Region region, float scale,
685 float centerX, float centerY) {
686 if (mMagnificationController != null) {
687 mMagnificationController.dispatchMagnificationChanged(
688 region, scale, centerX, centerY);
689 }
690 }
691
692 /**
693 * Used to control and query the state of display magnification.
694 */
695 public static final class MagnificationController {
696 private final AccessibilityService mService;
697
698 /**
699 * Map of listeners to their handlers. Lazily created when adding the
700 * first magnification listener.
701 */
702 private ArrayMap<OnMagnificationChangedListener, Handler> mListeners;
Anna Galusza9b278112016-01-04 11:37:37 -0800703 private final Object mLock;
Alan Viverette214fb682015-11-17 09:47:11 -0500704
Anna Galusza9b278112016-01-04 11:37:37 -0800705 MagnificationController(@NonNull AccessibilityService service, @NonNull Object lock) {
Alan Viverette214fb682015-11-17 09:47:11 -0500706 mService = service;
Anna Galusza9b278112016-01-04 11:37:37 -0800707 mLock = lock;
Alan Viverette214fb682015-11-17 09:47:11 -0500708 }
709
710 /**
711 * Called when the service is connected.
712 */
713 void onServiceConnected() {
Anna Galusza9b278112016-01-04 11:37:37 -0800714 synchronized (mLock) {
715 if (mListeners != null && !mListeners.isEmpty()) {
716 setMagnificationCallbackEnabled(true);
717 }
Alan Viverette214fb682015-11-17 09:47:11 -0500718 }
719 }
720
721 /**
722 * Adds the specified change listener to the list of magnification
723 * change listeners. The callback will occur on the service's main
724 * thread.
725 *
726 * @param listener the listener to add, must be non-{@code null}
727 */
728 public void addListener(@NonNull OnMagnificationChangedListener listener) {
729 addListener(listener, null);
730 }
731
732 /**
733 * Adds the specified change listener to the list of magnification
734 * change listeners. The callback will occur on the specified
735 * {@link Handler}'s thread, or on the service's main thread if the
736 * handler is {@code null}.
737 *
738 * @param listener the listener to add, must be non-null
739 * @param handler the handler on which the callback should execute, or
740 * {@code null} to execute on the service's main thread
741 */
742 public void addListener(@NonNull OnMagnificationChangedListener listener,
743 @Nullable Handler handler) {
Anna Galusza9b278112016-01-04 11:37:37 -0800744 synchronized (mLock) {
745 if (mListeners == null) {
746 mListeners = new ArrayMap<>();
747 }
Alan Viverette214fb682015-11-17 09:47:11 -0500748
Anna Galusza9b278112016-01-04 11:37:37 -0800749 final boolean shouldEnableCallback = mListeners.isEmpty();
750 mListeners.put(listener, handler);
Alan Viverette214fb682015-11-17 09:47:11 -0500751
Anna Galusza9b278112016-01-04 11:37:37 -0800752 if (shouldEnableCallback) {
753 // This may fail if the service is not connected yet, but if we
754 // still have listeners when it connects then we can try again.
755 setMagnificationCallbackEnabled(true);
756 }
Alan Viverette214fb682015-11-17 09:47:11 -0500757 }
758 }
759
760 /**
761 * Removes all instances of the specified change listener from the list
762 * of magnification change listeners.
763 *
764 * @param listener the listener to remove, must be non-null
765 * @return {@code true} if at least one instance of the listener was
766 * removed
767 */
768 public boolean removeListener(@NonNull OnMagnificationChangedListener listener) {
769 if (mListeners == null) {
770 return false;
771 }
772
Anna Galusza9b278112016-01-04 11:37:37 -0800773 synchronized (mLock) {
774 final int keyIndex = mListeners.indexOfKey(listener);
775 final boolean hasKey = keyIndex >= 0;
776 if (hasKey) {
777 mListeners.removeAt(keyIndex);
778 }
Alan Viverette214fb682015-11-17 09:47:11 -0500779
Anna Galusza9b278112016-01-04 11:37:37 -0800780 if (hasKey && mListeners.isEmpty()) {
781 // We just removed the last listener, so we don't need
782 // callbacks from the service anymore.
783 setMagnificationCallbackEnabled(false);
784 }
Alan Viverette214fb682015-11-17 09:47:11 -0500785
Anna Galusza9b278112016-01-04 11:37:37 -0800786 return hasKey;
787 }
Alan Viverette214fb682015-11-17 09:47:11 -0500788 }
789
790 private void setMagnificationCallbackEnabled(boolean enabled) {
791 final IAccessibilityServiceConnection connection =
792 AccessibilityInteractionClient.getInstance().getConnection(
793 mService.mConnectionId);
794 if (connection != null) {
795 try {
796 connection.setMagnificationCallbackEnabled(enabled);
797 } catch (RemoteException re) {
798 throw new RuntimeException(re);
799 }
800 }
801 }
802
803 /**
804 * Dispatches magnification changes to any registered listeners. This
805 * should be called on the service's main thread.
806 */
807 void dispatchMagnificationChanged(final @NonNull Region region, final float scale,
808 final float centerX, final float centerY) {
Anna Galusza9b278112016-01-04 11:37:37 -0800809 final ArrayMap<OnMagnificationChangedListener, Handler> entries;
810 synchronized (mLock) {
811 if (mListeners == null || mListeners.isEmpty()) {
812 Slog.d(LOG_TAG, "Received magnification changed "
813 + "callback with no listeners registered!");
814 setMagnificationCallbackEnabled(false);
815 return;
816 }
Alan Viverette214fb682015-11-17 09:47:11 -0500817
Anna Galusza9b278112016-01-04 11:37:37 -0800818 // Listeners may remove themselves. Perform a shallow copy to avoid concurrent
819 // modification.
820 entries = new ArrayMap<>(mListeners);
821 }
Alan Viverette214fb682015-11-17 09:47:11 -0500822
823 for (int i = 0, count = entries.size(); i < count; i++) {
824 final OnMagnificationChangedListener listener = entries.keyAt(i);
825 final Handler handler = entries.valueAt(i);
826 if (handler != null) {
827 handler.post(new Runnable() {
828 @Override
829 public void run() {
830 listener.onMagnificationChanged(MagnificationController.this,
831 region, scale, centerX, centerY);
832 }
833 });
834 } else {
835 // We're already on the main thread, just run the listener.
836 listener.onMagnificationChanged(this, region, scale, centerX, centerY);
837 }
838 }
839 }
840
841 /**
842 * Returns the current magnification scale.
843 * <p>
844 * <strong>Note:</strong> If the service is not yet connected (e.g.
845 * {@link AccessibilityService#onServiceConnected()} has not yet been
846 * called) or the service has been disconnected, this method will
847 * return a default value of {@code 1.0f}.
848 *
849 * @return the current magnification scale
850 */
851 public float getScale() {
852 final IAccessibilityServiceConnection connection =
853 AccessibilityInteractionClient.getInstance().getConnection(
854 mService.mConnectionId);
855 if (connection != null) {
856 try {
857 return connection.getMagnificationScale();
858 } catch (RemoteException re) {
859 Log.w(LOG_TAG, "Failed to obtain scale", re);
860 }
861 }
862 return 1.0f;
863 }
864
865 /**
866 * Returns the unscaled screen-relative X coordinate of the focal
867 * center of the magnified region. This is the point around which
868 * zooming occurs and is guaranteed to lie within the magnified
869 * region.
870 * <p>
871 * <strong>Note:</strong> If the service is not yet connected (e.g.
872 * {@link AccessibilityService#onServiceConnected()} has not yet been
873 * called) or the service has been disconnected, this method will
874 * return a default value of {@code 0.0f}.
875 *
876 * @return the unscaled screen-relative X coordinate of the center of
877 * the magnified region
878 */
879 public float getCenterX() {
880 final IAccessibilityServiceConnection connection =
881 AccessibilityInteractionClient.getInstance().getConnection(
882 mService.mConnectionId);
883 if (connection != null) {
884 try {
885 return connection.getMagnificationCenterX();
886 } catch (RemoteException re) {
887 Log.w(LOG_TAG, "Failed to obtain center X", re);
888 }
889 }
890 return 0.0f;
891 }
892
893 /**
894 * Returns the unscaled screen-relative Y coordinate of the focal
895 * center of the magnified region. This is the point around which
896 * zooming occurs and is guaranteed to lie within the magnified
897 * region.
898 * <p>
899 * <strong>Note:</strong> If the service is not yet connected (e.g.
900 * {@link AccessibilityService#onServiceConnected()} has not yet been
901 * called) or the service has been disconnected, this method will
902 * return a default value of {@code 0.0f}.
903 *
904 * @return the unscaled screen-relative Y coordinate of the center of
905 * the magnified region
906 */
907 public float getCenterY() {
908 final IAccessibilityServiceConnection connection =
909 AccessibilityInteractionClient.getInstance().getConnection(
910 mService.mConnectionId);
911 if (connection != null) {
912 try {
913 return connection.getMagnificationCenterY();
914 } catch (RemoteException re) {
915 Log.w(LOG_TAG, "Failed to obtain center Y", re);
916 }
917 }
918 return 0.0f;
919 }
920
921 /**
922 * Returns the region of the screen currently being magnified. If
923 * magnification is not enabled, the returned region will be empty.
924 * <p>
925 * <strong>Note:</strong> If the service is not yet connected (e.g.
926 * {@link AccessibilityService#onServiceConnected()} has not yet been
927 * called) or the service has been disconnected, this method will
928 * return an empty region.
929 *
930 * @return the screen-relative bounds of the magnified region
931 */
932 @NonNull
933 public Region getMagnifiedRegion() {
934 final IAccessibilityServiceConnection connection =
935 AccessibilityInteractionClient.getInstance().getConnection(
936 mService.mConnectionId);
937 if (connection != null) {
938 try {
939 return connection.getMagnifiedRegion();
940 } catch (RemoteException re) {
941 Log.w(LOG_TAG, "Failed to obtain magnified region", re);
942 }
943 }
944 return Region.obtain();
945 }
946
947 /**
948 * Resets magnification scale and center to their default (e.g. no
949 * magnification) values.
950 * <p>
951 * <strong>Note:</strong> If the service is not yet connected (e.g.
952 * {@link AccessibilityService#onServiceConnected()} has not yet been
953 * called) or the service has been disconnected, this method will have
954 * no effect and return {@code false}.
955 *
956 * @param animate {@code true} to animate from the current scale and
957 * center or {@code false} to reset the scale and center
958 * immediately
959 * @return {@code true} on success, {@code false} on failure
960 */
961 public boolean reset(boolean animate) {
962 final IAccessibilityServiceConnection connection =
963 AccessibilityInteractionClient.getInstance().getConnection(
964 mService.mConnectionId);
965 if (connection != null) {
966 try {
967 return connection.resetMagnification(animate);
968 } catch (RemoteException re) {
969 Log.w(LOG_TAG, "Failed to reset", re);
970 }
971 }
972 return false;
973 }
974
975 /**
976 * Sets the magnification scale.
977 * <p>
978 * <strong>Note:</strong> If the service is not yet connected (e.g.
979 * {@link AccessibilityService#onServiceConnected()} has not yet been
980 * called) or the service has been disconnected, this method will have
981 * no effect and return {@code false}.
982 *
983 * @param scale the magnification scale to set, must be >= 1 and <= 5
984 * @param animate {@code true} to animate from the current scale or
985 * {@code false} to set the scale immediately
986 * @return {@code true} on success, {@code false} on failure
987 */
988 public boolean setScale(float scale, boolean animate) {
989 final IAccessibilityServiceConnection connection =
990 AccessibilityInteractionClient.getInstance().getConnection(
991 mService.mConnectionId);
992 if (connection != null) {
993 try {
994 return connection.setMagnificationScaleAndCenter(
995 scale, Float.NaN, Float.NaN, animate);
996 } catch (RemoteException re) {
997 Log.w(LOG_TAG, "Failed to set scale", re);
998 }
999 }
1000 return false;
1001 }
1002
1003 /**
1004 * Sets the center of the magnified viewport.
1005 * <p>
1006 * <strong>Note:</strong> If the service is not yet connected (e.g.
1007 * {@link AccessibilityService#onServiceConnected()} has not yet been
1008 * called) or the service has been disconnected, this method will have
1009 * no effect and return {@code false}.
1010 *
1011 * @param centerX the unscaled screen-relative X coordinate on which to
1012 * center the viewport
1013 * @param centerY the unscaled screen-relative Y coordinate on which to
1014 * center the viewport
1015 * @param animate {@code true} to animate from the current viewport
1016 * center or {@code false} to set the center immediately
1017 * @return {@code true} on success, {@code false} on failure
1018 */
1019 public boolean setCenter(float centerX, float centerY, boolean animate) {
1020 final IAccessibilityServiceConnection connection =
1021 AccessibilityInteractionClient.getInstance().getConnection(
1022 mService.mConnectionId);
1023 if (connection != null) {
1024 try {
1025 return connection.setMagnificationScaleAndCenter(
1026 Float.NaN, centerX, centerY, animate);
1027 } catch (RemoteException re) {
1028 Log.w(LOG_TAG, "Failed to set center", re);
1029 }
1030 }
1031 return false;
1032 }
1033
1034 /**
1035 * Listener for changes in the state of magnification.
1036 */
1037 public interface OnMagnificationChangedListener {
1038 /**
1039 * Called when the magnified region, scale, or center changes.
1040 *
1041 * @param controller the magnification controller
1042 * @param region the new magnified region, may be empty if
1043 * magnification is not enabled (e.g. scale is 1)
1044 * @param scale the new scale
1045 * @param centerX the new X coordinate around which magnification is focused
1046 * @param centerY the new Y coordinate around which magnification is focused
1047 */
1048 void onMagnificationChanged(@NonNull MagnificationController controller,
1049 @NonNull Region region, float scale, float centerX, float centerY);
1050 }
1051 }
1052
1053 /**
Anna Galusza9b278112016-01-04 11:37:37 -08001054 * Returns the soft keyboard controller, which may be used to query and modify the soft keyboard
1055 * show mode.
1056 *
1057 * @return the soft keyboard controller
1058 */
1059 @NonNull
1060 public final SoftKeyboardController getSoftKeyboardController() {
1061 synchronized (mLock) {
1062 if (mSoftKeyboardController == null) {
1063 mSoftKeyboardController = new SoftKeyboardController(this, mLock);
1064 }
1065 return mSoftKeyboardController;
1066 }
1067 }
1068
1069 private void onSoftKeyboardShowModeChanged(int showMode) {
1070 if (mSoftKeyboardController != null) {
1071 mSoftKeyboardController.dispatchSoftKeyboardShowModeChanged(showMode);
1072 }
1073 }
1074
1075 /**
1076 * Used to control and query the soft keyboard show mode.
1077 */
1078 public static final class SoftKeyboardController {
1079 private final AccessibilityService mService;
1080
1081 /**
1082 * Map of listeners to their handlers. Lazily created when adding the first
1083 * soft keyboard change listener.
1084 */
1085 private ArrayMap<OnShowModeChangedListener, Handler> mListeners;
1086 private final Object mLock;
1087
1088 SoftKeyboardController(@NonNull AccessibilityService service, @NonNull Object lock) {
1089 mService = service;
1090 mLock = lock;
1091 }
1092
1093 /**
1094 * Called when the service is connected.
1095 */
1096 void onServiceConnected() {
1097 synchronized(mLock) {
1098 if (mListeners != null && !mListeners.isEmpty()) {
1099 setSoftKeyboardCallbackEnabled(true);
1100 }
1101 }
1102 }
1103
1104 /**
1105 * Adds the specified change listener to the list of show mode change listeners. The
1106 * callback will occur on the service's main thread. Listener is not called on registration.
1107 */
1108 public void addOnShowModeChangedListener(@NonNull OnShowModeChangedListener listener) {
1109 addOnShowModeChangedListener(listener, null);
1110 }
1111
1112 /**
1113 * Adds the specified change listener to the list of soft keyboard show mode change
1114 * listeners. The callback will occur on the specified {@link Handler}'s thread, or on the
1115 * services's main thread if the handler is {@code null}.
1116 *
1117 * @param listener the listener to add, must be non-null
1118 * @param handler the handler on which to callback should execute, or {@code null} to
1119 * execute on the service's main thread
1120 */
1121 public void addOnShowModeChangedListener(@NonNull OnShowModeChangedListener listener,
1122 @Nullable Handler handler) {
1123 synchronized (mLock) {
1124 if (mListeners == null) {
1125 mListeners = new ArrayMap<>();
1126 }
1127
1128 final boolean shouldEnableCallback = mListeners.isEmpty();
1129 mListeners.put(listener, handler);
1130
1131 if (shouldEnableCallback) {
1132 // This may fail if the service is not connected yet, but if we still have
1133 // listeners when it connects, we can try again.
1134 setSoftKeyboardCallbackEnabled(true);
1135 }
1136 }
1137 }
1138
1139 /**
1140 * Removes all instances of the specified change listener from teh list of magnification
1141 * change listeners.
1142 *
1143 * @param listener the listener to remove, must be non-null
1144 * @return {@code true} if at least one instance of the listener was removed
1145 */
1146 public boolean removeOnShowModeChangedListener(@NonNull OnShowModeChangedListener listener) {
1147 if (mListeners == null) {
1148 return false;
1149 }
1150
1151 synchronized (mLock) {
1152 final int keyIndex = mListeners.indexOfKey(listener);
1153 final boolean hasKey = keyIndex >= 0;
1154 if (hasKey) {
1155 mListeners.removeAt(keyIndex);
1156 }
1157
1158 if (hasKey && mListeners.isEmpty()) {
1159 // We just removed the last listener, so we don't need callbacks from the
1160 // service anymore.
1161 setSoftKeyboardCallbackEnabled(false);
1162 }
1163
1164 return hasKey;
1165 }
1166 }
1167
1168 private void setSoftKeyboardCallbackEnabled(boolean enabled) {
1169 final IAccessibilityServiceConnection connection =
1170 AccessibilityInteractionClient.getInstance().getConnection(
1171 mService.mConnectionId);
1172 if (connection != null) {
1173 try {
1174 connection.setSoftKeyboardCallbackEnabled(enabled);
1175 } catch (RemoteException re) {
1176 throw new RuntimeException(re);
1177 }
1178 }
1179 }
1180
1181 /**
1182 * Dispatches the soft keyboard show mode change to any registered listeners. This should
1183 * be called on the service's main thread.
1184 */
1185 void dispatchSoftKeyboardShowModeChanged(final int showMode) {
1186 final ArrayMap<OnShowModeChangedListener, Handler> entries;
1187 synchronized (mLock) {
1188 if (mListeners == null || mListeners.isEmpty()) {
1189 Slog.d(LOG_TAG, "Received soft keyboard show mode changed callback"
1190 + " with no listeners registered!");
1191 setSoftKeyboardCallbackEnabled(false);
1192 return;
1193 }
1194
1195 // Listeners may remove themselves. Perform a shallow copy to avoid concurrent
1196 // modification.
1197 entries = new ArrayMap<>(mListeners);
1198 }
1199
1200 for (int i = 0, count = entries.size(); i < count; i++) {
1201 final OnShowModeChangedListener listener = entries.keyAt(i);
1202 final Handler handler = entries.valueAt(i);
1203 if (handler != null) {
1204 handler.post(new Runnable() {
1205 @Override
1206 public void run() {
1207 listener.onShowModeChanged(SoftKeyboardController.this, showMode);
1208 }
1209 });
1210 } else {
1211 // We're already on the main thread, just run the listener.
1212 listener.onShowModeChanged(this, showMode);
1213 }
1214 }
1215 }
1216
1217 /**
1218 * Returns the show mode of the soft keyboard. The default show mode is
1219 * {@code Settings.Secure.SHOW_MODE_AUTO}, where the soft keyboard is shown when a text
1220 * input field is focused. An AccessibilityService can also request the show mode
1221 * {@code Settings.Secure.SHOW_MODE_HIDDEN}, where the soft keyboard is never shown.
1222 *
1223 * @return the current soft keyboard show mode
1224 *
1225 * @see Settings#Secure#SHOW_MODE_AUTO
1226 * @see Settings#Secure#SHOW_MODE_HIDDEN
1227 */
1228 @SoftKeyboardShowMode
1229 public int getShowMode() {
1230 try {
1231 return Settings.Secure.getInt(mService.getContentResolver(),
1232 Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE);
1233 } catch (Settings.SettingNotFoundException e) {
1234 Log.v(LOG_TAG, "Failed to obtain the soft keyboard mode", e);
1235 // The settings hasn't been changed yet, so it's value is null. Return the default.
1236 return 0;
1237 }
1238 }
1239
1240 /**
1241 * Sets the soft keyboard show mode. The default show mode is
1242 * {@code Settings.Secure.SHOW_MODE_AUTO}, where the soft keyboard is shown when a text
1243 * input field is focused. An AccessibilityService can also request the show mode
1244 * {@code Settings.Secure.SHOW_MODE_HIDDEN}, where the soft keyboard is never shown. The
1245 * The lastto this method will be honored, regardless of any previous calls (including those
1246 * made by other AccessibilityServices).
1247 * <p>
1248 * <strong>Note:</strong> If the service is not yet conected (e.g.
1249 * {@link AccessibilityService#onServiceConnected()} has not yet been called) or the
1250 * service has been disconnected, this method will hav no effect and return {@code false}.
1251 *
1252 * @param showMode the new show mode for the soft keyboard
1253 * @return {@code true} on success
1254 *
1255 * @see Settings#Secure#SHOW_MODE_AUTO
1256 * @see Settings#Secure#SHOW_MODE_HIDDEN
1257 */
1258 public boolean setShowMode(@SoftKeyboardShowMode int showMode) {
1259 final IAccessibilityServiceConnection connection =
1260 AccessibilityInteractionClient.getInstance().getConnection(
1261 mService.mConnectionId);
1262 if (connection != null) {
1263 try {
1264 return connection.setSoftKeyboardShowMode(showMode);
1265 } catch (RemoteException re) {
1266 Log.w(LOG_TAG, "Falied to set soft keyboard behavior", re);
1267 }
1268 }
1269 return false;
1270 }
1271
1272 /**
1273 * Listener for changes in the soft keyboard show mode.
1274 */
1275 public interface OnShowModeChangedListener {
1276 /**
1277 * Called when the soft keyboard behavior changes. The default show mode is
1278 * {@code Settings.Secure.SHOW_MODE_AUTO}, where the soft keyboard is shown when a text
1279 * input field is focused. An AccessibilityService can also request the show mode
1280 * {@code Settings.Secure.SHOW_MODE_HIDDEN}, where the soft keyboard is never shown.
1281 *
1282 * @param controller the soft keyboard controller
1283 * @param showMode the current soft keyboard show mode
1284 */
1285 void onShowModeChanged(@NonNull SoftKeyboardController controller,
1286 @SoftKeyboardShowMode int showMode);
1287 }
1288 }
1289
1290 /**
Svetoslav Ganov005b83b2012-04-16 18:17:17 -07001291 * Performs a global action. Such an action can be performed
1292 * at any moment regardless of the current application or user
1293 * location in that application. For example going back, going
1294 * home, opening recents, etc.
1295 *
1296 * @param action The action to perform.
1297 * @return Whether the action was successfully performed.
1298 *
1299 * @see #GLOBAL_ACTION_BACK
1300 * @see #GLOBAL_ACTION_HOME
1301 * @see #GLOBAL_ACTION_NOTIFICATIONS
1302 * @see #GLOBAL_ACTION_RECENTS
1303 */
1304 public final boolean performGlobalAction(int action) {
1305 IAccessibilityServiceConnection connection =
1306 AccessibilityInteractionClient.getInstance().getConnection(mConnectionId);
1307 if (connection != null) {
1308 try {
Svetoslav Ganovfefd20e2012-04-19 21:44:35 -07001309 return connection.performGlobalAction(action);
Svetoslav Ganov005b83b2012-04-16 18:17:17 -07001310 } catch (RemoteException re) {
1311 Log.w(LOG_TAG, "Error while calling performGlobalAction", re);
1312 }
1313 }
1314 return false;
1315 }
1316
1317 /**
Svetoslav1e0d4af2014-04-10 17:41:29 -07001318 * Find the view that has the specified focus type. The search is performed
1319 * across all windows.
1320 * <p>
1321 * <strong>Note:</strong> In order to access the windows your service has
1322 * to declare the capability to retrieve window content by setting the
1323 * {@link android.R.styleable#AccessibilityService_canRetrieveWindowContent}
1324 * property in its meta-data. For details refer to {@link #SERVICE_META_DATA}.
1325 * Also the service has to opt-in to retrieve the interactive windows by
1326 * setting the {@link AccessibilityServiceInfo#FLAG_RETRIEVE_INTERACTIVE_WINDOWS}
Phil Weaver40ded282016-01-25 15:49:02 -08001327 * flag. Otherwise, the search will be performed only in the active window.
Svetoslav1e0d4af2014-04-10 17:41:29 -07001328 * </p>
1329 *
1330 * @param focus The focus to find. One of {@link AccessibilityNodeInfo#FOCUS_INPUT} or
1331 * {@link AccessibilityNodeInfo#FOCUS_ACCESSIBILITY}.
1332 * @return The node info of the focused view or null.
1333 *
1334 * @see AccessibilityNodeInfo#FOCUS_INPUT
1335 * @see AccessibilityNodeInfo#FOCUS_ACCESSIBILITY
1336 */
1337 public AccessibilityNodeInfo findFocus(int focus) {
1338 return AccessibilityInteractionClient.getInstance().findFocus(mConnectionId,
1339 AccessibilityNodeInfo.ANY_WINDOW_ID, AccessibilityNodeInfo.ROOT_NODE_ID, focus);
1340 }
1341
1342 /**
Svetoslav Ganov42138042012-03-20 11:51:39 -07001343 * Gets the an {@link AccessibilityServiceInfo} describing this
1344 * {@link AccessibilityService}. This method is useful if one wants
1345 * to change some of the dynamically configurable properties at
1346 * runtime.
1347 *
1348 * @return The accessibility service info.
1349 *
Svetoslavbbfa5852013-02-11 19:38:12 -08001350 * @see AccessibilityServiceInfo
Svetoslav Ganov42138042012-03-20 11:51:39 -07001351 */
1352 public final AccessibilityServiceInfo getServiceInfo() {
1353 IAccessibilityServiceConnection connection =
1354 AccessibilityInteractionClient.getInstance().getConnection(mConnectionId);
1355 if (connection != null) {
1356 try {
1357 return connection.getServiceInfo();
1358 } catch (RemoteException re) {
1359 Log.w(LOG_TAG, "Error while getting AccessibilityServiceInfo", re);
1360 }
1361 }
1362 return null;
1363 }
1364
1365 /**
svetoslavganov75986cf2009-05-14 22:28:01 -07001366 * Sets the {@link AccessibilityServiceInfo} that describes this service.
1367 * <p>
1368 * Note: You can call this method any time but the info will be picked up after
1369 * the system has bound to this service and when this method is called thereafter.
1370 *
1371 * @param info The info.
1372 */
1373 public final void setServiceInfo(AccessibilityServiceInfo info) {
1374 mInfo = info;
1375 sendServiceInfo();
1376 }
1377
1378 /**
1379 * Sets the {@link AccessibilityServiceInfo} for this service if the latter is
1380 * properly set and there is an {@link IAccessibilityServiceConnection} to the
1381 * AccessibilityManagerService.
1382 */
1383 private void sendServiceInfo() {
Svetoslav Ganovd116d7c2011-11-21 18:41:59 -08001384 IAccessibilityServiceConnection connection =
1385 AccessibilityInteractionClient.getInstance().getConnection(mConnectionId);
1386 if (mInfo != null && connection != null) {
svetoslavganov75986cf2009-05-14 22:28:01 -07001387 try {
Svetoslav Ganovd116d7c2011-11-21 18:41:59 -08001388 connection.setServiceInfo(mInfo);
Svetoslav Ganov42138042012-03-20 11:51:39 -07001389 mInfo = null;
1390 AccessibilityInteractionClient.getInstance().clearCache();
svetoslavganov75986cf2009-05-14 22:28:01 -07001391 } catch (RemoteException re) {
1392 Log.w(LOG_TAG, "Error while setting AccessibilityServiceInfo", re);
1393 }
1394 }
1395 }
1396
Alan Viverette7c9746d4e2014-11-19 17:02:16 -08001397 @Override
1398 public Object getSystemService(@ServiceName @NonNull String name) {
1399 if (getBaseContext() == null) {
1400 throw new IllegalStateException(
1401 "System services not available to Activities before onCreate()");
1402 }
1403
1404 // Guarantee that we always return the same window manager instance.
1405 if (WINDOW_SERVICE.equals(name)) {
1406 if (mWindowManager == null) {
1407 mWindowManager = (WindowManager) getBaseContext().getSystemService(name);
1408 }
1409 return mWindowManager;
1410 }
1411 return super.getSystemService(name);
1412 }
1413
Dianne Hackborn7f205432009-07-28 00:13:47 -07001414 /**
1415 * Implement to return the implementation of the internal accessibility
Svetoslav Ganov8643aa02011-04-20 12:12:33 -07001416 * service interface.
Dianne Hackborn7f205432009-07-28 00:13:47 -07001417 */
svetoslavganov75986cf2009-05-14 22:28:01 -07001418 @Override
1419 public final IBinder onBind(Intent intent) {
Svetoslav Ganov42138042012-03-20 11:51:39 -07001420 return new IAccessibilityServiceClientWrapper(this, getMainLooper(), new Callbacks() {
Svetoslav Ganov79311c42012-01-17 20:24:26 -08001421 @Override
1422 public void onServiceConnected() {
Alan Viverette214fb682015-11-17 09:47:11 -05001423 AccessibilityService.this.dispatchServiceConnected();
Svetoslav Ganov79311c42012-01-17 20:24:26 -08001424 }
1425
1426 @Override
1427 public void onInterrupt() {
1428 AccessibilityService.this.onInterrupt();
1429 }
1430
1431 @Override
1432 public void onAccessibilityEvent(AccessibilityEvent event) {
1433 AccessibilityService.this.onAccessibilityEvent(event);
1434 }
1435
1436 @Override
Svetoslav3a5c7212014-10-14 09:54:26 -07001437 public void init(int connectionId, IBinder windowToken) {
Svetoslav Ganov79311c42012-01-17 20:24:26 -08001438 mConnectionId = connectionId;
Svetoslav3a5c7212014-10-14 09:54:26 -07001439 mWindowToken = windowToken;
Alan Viveretted2fa5142014-11-04 17:40:29 -08001440
Alan Viverette7c9746d4e2014-11-19 17:02:16 -08001441 // The client may have already obtained the window manager, so
1442 // update the default token on whatever manager we gave them.
1443 final WindowManagerImpl wm = (WindowManagerImpl) getSystemService(WINDOW_SERVICE);
1444 wm.setDefaultToken(windowToken);
Svetoslav Ganov79311c42012-01-17 20:24:26 -08001445 }
Svetoslav Ganov42138042012-03-20 11:51:39 -07001446
1447 @Override
Svetoslav Ganovfefd20e2012-04-19 21:44:35 -07001448 public boolean onGesture(int gestureId) {
1449 return AccessibilityService.this.onGesture(gestureId);
Svetoslav Ganov42138042012-03-20 11:51:39 -07001450 }
Svetoslavc4fccd12013-04-09 12:58:41 -07001451
1452 @Override
1453 public boolean onKeyEvent(KeyEvent event) {
1454 return AccessibilityService.this.onKeyEvent(event);
1455 }
Alan Viverette214fb682015-11-17 09:47:11 -05001456
1457 @Override
1458 public void onMagnificationChanged(@NonNull Region region,
1459 float scale, float centerX, float centerY) {
1460 AccessibilityService.this.onMagnificationChanged(region, scale, centerX, centerY);
1461 }
Phil Weavera6b64f52015-12-04 15:21:35 -08001462
1463 @Override
Anna Galusza9b278112016-01-04 11:37:37 -08001464 public void onSoftKeyboardShowModeChanged(int showMode) {
1465 AccessibilityService.this.onSoftKeyboardShowModeChanged(showMode);
1466 }
1467
1468 @Override
Phil Weavera6b64f52015-12-04 15:21:35 -08001469 public void onPerformGestureResult(int sequence, boolean completedSuccessfully) {
1470 AccessibilityService.this.onPerformGestureResult(sequence, completedSuccessfully);
1471 }
Svetoslav Ganov79311c42012-01-17 20:24:26 -08001472 });
svetoslavganov75986cf2009-05-14 22:28:01 -07001473 }
1474
1475 /**
Svetoslav Ganov42138042012-03-20 11:51:39 -07001476 * Implements the internal {@link IAccessibilityServiceClient} interface to convert
svetoslavganov75986cf2009-05-14 22:28:01 -07001477 * incoming calls to it back to calls on an {@link AccessibilityService}.
Svetoslav Ganov80943d82013-01-02 10:25:37 -08001478 *
1479 * @hide
svetoslavganov75986cf2009-05-14 22:28:01 -07001480 */
Svetoslav Ganov80943d82013-01-02 10:25:37 -08001481 public static class IAccessibilityServiceClientWrapper extends IAccessibilityServiceClient.Stub
svetoslavganov75986cf2009-05-14 22:28:01 -07001482 implements HandlerCaller.Callback {
Svetoslav3a5c7212014-10-14 09:54:26 -07001483 private static final int DO_INIT = 1;
Svetoslav8e3feb12014-02-24 13:46:47 -08001484 private static final int DO_ON_INTERRUPT = 2;
1485 private static final int DO_ON_ACCESSIBILITY_EVENT = 3;
1486 private static final int DO_ON_GESTURE = 4;
1487 private static final int DO_CLEAR_ACCESSIBILITY_CACHE = 5;
1488 private static final int DO_ON_KEY_EVENT = 6;
Alan Viverette214fb682015-11-17 09:47:11 -05001489 private static final int DO_ON_MAGNIFICATION_CHANGED = 7;
Anna Galusza9b278112016-01-04 11:37:37 -08001490 private static final int DO_ON_SOFT_KEYBOARD_SHOW_MODE_CHANGED = 8;
1491 private static final int DO_GESTURE_COMPLETE = 9;
svetoslavganov75986cf2009-05-14 22:28:01 -07001492
1493 private final HandlerCaller mCaller;
1494
Svetoslav Ganov79311c42012-01-17 20:24:26 -08001495 private final Callbacks mCallback;
svetoslavganov75986cf2009-05-14 22:28:01 -07001496
Svetoslavc4fccd12013-04-09 12:58:41 -07001497 private int mConnectionId;
1498
Svetoslav Ganov42138042012-03-20 11:51:39 -07001499 public IAccessibilityServiceClientWrapper(Context context, Looper looper,
1500 Callbacks callback) {
Svetoslav Ganov79311c42012-01-17 20:24:26 -08001501 mCallback = callback;
Mita Yuned218c72012-12-06 17:18:25 -08001502 mCaller = new HandlerCaller(context, looper, this, true /*asyncHandler*/);
svetoslavganov75986cf2009-05-14 22:28:01 -07001503 }
1504
Svetoslav3a5c7212014-10-14 09:54:26 -07001505 public void init(IAccessibilityServiceConnection connection, int connectionId,
1506 IBinder windowToken) {
1507 Message message = mCaller.obtainMessageIOO(DO_INIT, connectionId,
1508 connection, windowToken);
svetoslavganov75986cf2009-05-14 22:28:01 -07001509 mCaller.sendMessage(message);
1510 }
1511
1512 public void onInterrupt() {
1513 Message message = mCaller.obtainMessage(DO_ON_INTERRUPT);
1514 mCaller.sendMessage(message);
1515 }
1516
1517 public void onAccessibilityEvent(AccessibilityEvent event) {
1518 Message message = mCaller.obtainMessageO(DO_ON_ACCESSIBILITY_EVENT, event);
1519 mCaller.sendMessage(message);
1520 }
1521
Svetoslav Ganov7b1e0c72012-05-13 11:57:29 -07001522 public void onGesture(int gestureId) {
1523 Message message = mCaller.obtainMessageI(DO_ON_GESTURE, gestureId);
Svetoslav Ganov42138042012-03-20 11:51:39 -07001524 mCaller.sendMessage(message);
1525 }
1526
Svetoslav8e3feb12014-02-24 13:46:47 -08001527 public void clearAccessibilityCache() {
1528 Message message = mCaller.obtainMessage(DO_CLEAR_ACCESSIBILITY_CACHE);
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -07001529 mCaller.sendMessage(message);
1530 }
1531
Svetoslavc4fccd12013-04-09 12:58:41 -07001532 @Override
1533 public void onKeyEvent(KeyEvent event, int sequence) {
1534 Message message = mCaller.obtainMessageIO(DO_ON_KEY_EVENT, sequence, event);
1535 mCaller.sendMessage(message);
1536 }
1537
Alan Viverette214fb682015-11-17 09:47:11 -05001538 public void onMagnificationChanged(@NonNull Region region,
1539 float scale, float centerX, float centerY) {
1540 final SomeArgs args = SomeArgs.obtain();
1541 args.arg1 = region;
1542 args.arg2 = scale;
1543 args.arg3 = centerX;
1544 args.arg4 = centerY;
1545
1546 final Message message = mCaller.obtainMessageO(DO_ON_MAGNIFICATION_CHANGED, args);
1547 mCaller.sendMessage(message);
1548 }
1549
Anna Galusza9b278112016-01-04 11:37:37 -08001550 public void onSoftKeyboardShowModeChanged(int showMode) {
1551 final Message message =
1552 mCaller.obtainMessageI(DO_ON_SOFT_KEYBOARD_SHOW_MODE_CHANGED, showMode);
1553 mCaller.sendMessage(message);
1554 }
1555
Phil Weavera6b64f52015-12-04 15:21:35 -08001556 public void onPerformGestureResult(int sequence, boolean successfully) {
1557 Message message = mCaller.obtainMessageII(DO_GESTURE_COMPLETE, sequence,
1558 successfully ? 1 : 0);
1559 mCaller.sendMessage(message);
1560 }
1561
Svetoslav8e3feb12014-02-24 13:46:47 -08001562 @Override
svetoslavganov75986cf2009-05-14 22:28:01 -07001563 public void executeMessage(Message message) {
1564 switch (message.what) {
Svetoslavc4fccd12013-04-09 12:58:41 -07001565 case DO_ON_ACCESSIBILITY_EVENT: {
svetoslavganov75986cf2009-05-14 22:28:01 -07001566 AccessibilityEvent event = (AccessibilityEvent) message.obj;
Dianne Hackborn7ed6ee52009-09-10 18:41:28 -07001567 if (event != null) {
Svetoslav Ganov79311c42012-01-17 20:24:26 -08001568 AccessibilityInteractionClient.getInstance().onAccessibilityEvent(event);
1569 mCallback.onAccessibilityEvent(event);
Svetoslav8e3feb12014-02-24 13:46:47 -08001570 // Make sure the event is recycled.
1571 try {
1572 event.recycle();
1573 } catch (IllegalStateException ise) {
1574 /* ignore - best effort */
1575 }
Charles Chen85f6fb72009-08-28 11:32:23 -07001576 }
Svetoslavc4fccd12013-04-09 12:58:41 -07001577 } return;
Svetoslav8e3feb12014-02-24 13:46:47 -08001578
Svetoslavc4fccd12013-04-09 12:58:41 -07001579 case DO_ON_INTERRUPT: {
Svetoslav Ganov79311c42012-01-17 20:24:26 -08001580 mCallback.onInterrupt();
Svetoslavc4fccd12013-04-09 12:58:41 -07001581 } return;
Svetoslav8e3feb12014-02-24 13:46:47 -08001582
Svetoslav3a5c7212014-10-14 09:54:26 -07001583 case DO_INIT: {
Svetoslavc4fccd12013-04-09 12:58:41 -07001584 mConnectionId = message.arg1;
Svetoslav3a5c7212014-10-14 09:54:26 -07001585 SomeArgs args = (SomeArgs) message.obj;
Svetoslav Ganovd116d7c2011-11-21 18:41:59 -08001586 IAccessibilityServiceConnection connection =
Svetoslav3a5c7212014-10-14 09:54:26 -07001587 (IAccessibilityServiceConnection) args.arg1;
1588 IBinder windowToken = (IBinder) args.arg2;
1589 args.recycle();
Svetoslav Ganovd116d7c2011-11-21 18:41:59 -08001590 if (connection != null) {
Svetoslavc4fccd12013-04-09 12:58:41 -07001591 AccessibilityInteractionClient.getInstance().addConnection(mConnectionId,
Svetoslav Ganovd116d7c2011-11-21 18:41:59 -08001592 connection);
Svetoslav3a5c7212014-10-14 09:54:26 -07001593 mCallback.init(mConnectionId, windowToken);
Svetoslav Ganov79311c42012-01-17 20:24:26 -08001594 mCallback.onServiceConnected();
Svetoslav Ganovd116d7c2011-11-21 18:41:59 -08001595 } else {
Svetoslav8e3feb12014-02-24 13:46:47 -08001596 AccessibilityInteractionClient.getInstance().removeConnection(
1597 mConnectionId);
Svetoslav3a5c7212014-10-14 09:54:26 -07001598 mConnectionId = AccessibilityInteractionClient.NO_ID;
Svetoslavf7e50992013-01-22 18:15:40 -08001599 AccessibilityInteractionClient.getInstance().clearCache();
Svetoslav3a5c7212014-10-14 09:54:26 -07001600 mCallback.init(AccessibilityInteractionClient.NO_ID, null);
Svetoslav Ganovd116d7c2011-11-21 18:41:59 -08001601 }
Svetoslavc4fccd12013-04-09 12:58:41 -07001602 } return;
Svetoslav8e3feb12014-02-24 13:46:47 -08001603
Svetoslavc4fccd12013-04-09 12:58:41 -07001604 case DO_ON_GESTURE: {
Svetoslav Ganov42138042012-03-20 11:51:39 -07001605 final int gestureId = message.arg1;
Svetoslav Ganov7b1e0c72012-05-13 11:57:29 -07001606 mCallback.onGesture(gestureId);
Svetoslavc4fccd12013-04-09 12:58:41 -07001607 } return;
Svetoslav8e3feb12014-02-24 13:46:47 -08001608
1609 case DO_CLEAR_ACCESSIBILITY_CACHE: {
Svetoslav Ganov152e9bb2012-10-12 20:15:29 -07001610 AccessibilityInteractionClient.getInstance().clearCache();
Svetoslavc4fccd12013-04-09 12:58:41 -07001611 } return;
Svetoslav8e3feb12014-02-24 13:46:47 -08001612
Svetoslavc4fccd12013-04-09 12:58:41 -07001613 case DO_ON_KEY_EVENT: {
1614 KeyEvent event = (KeyEvent) message.obj;
1615 try {
1616 IAccessibilityServiceConnection connection = AccessibilityInteractionClient
1617 .getInstance().getConnection(mConnectionId);
1618 if (connection != null) {
1619 final boolean result = mCallback.onKeyEvent(event);
1620 final int sequence = message.arg1;
1621 try {
1622 connection.setOnKeyEventResult(result, sequence);
1623 } catch (RemoteException re) {
1624 /* ignore */
1625 }
1626 }
1627 } finally {
Svetoslav8e3feb12014-02-24 13:46:47 -08001628 // Make sure the event is recycled.
1629 try {
1630 event.recycle();
1631 } catch (IllegalStateException ise) {
1632 /* ignore - best effort */
1633 }
Svetoslavc4fccd12013-04-09 12:58:41 -07001634 }
1635 } return;
Svetoslav8e3feb12014-02-24 13:46:47 -08001636
Alan Viverette214fb682015-11-17 09:47:11 -05001637 case DO_ON_MAGNIFICATION_CHANGED: {
1638 final SomeArgs args = (SomeArgs) message.obj;
1639 final Region region = (Region) args.arg1;
1640 final float scale = (float) args.arg2;
1641 final float centerX = (float) args.arg3;
1642 final float centerY = (float) args.arg4;
1643 mCallback.onMagnificationChanged(region, scale, centerX, centerY);
1644 } return;
1645
Anna Galusza9b278112016-01-04 11:37:37 -08001646 case DO_ON_SOFT_KEYBOARD_SHOW_MODE_CHANGED: {
1647 final int showMode = (int) message.arg1;
1648 mCallback.onSoftKeyboardShowModeChanged(showMode);
1649 } return;
1650
Phil Weavera6b64f52015-12-04 15:21:35 -08001651 case DO_GESTURE_COMPLETE: {
1652 final boolean successfully = message.arg2 == 1;
1653 mCallback.onPerformGestureResult(message.arg1, successfully);
1654 } return;
1655
svetoslavganov75986cf2009-05-14 22:28:01 -07001656 default :
1657 Log.w(LOG_TAG, "Unknown message type " + message.what);
1658 }
1659 }
1660 }
Phil Weavera6b64f52015-12-04 15:21:35 -08001661
1662 /**
1663 * Class used to report status of dispatched gestures
1664 */
1665 public static abstract class GestureResultCallback {
1666 /** Called when the gesture has completed successfully
1667 *
1668 * @param gestureDescription The description of the gesture that completed.
1669 */
1670 public void onCompleted(GestureDescription gestureDescription) {
1671 }
1672
1673 /** Called when the gesture was cancelled
1674 *
1675 * @param gestureDescription The description of the gesture that was cancelled.
1676 */
1677 public void onCancelled(GestureDescription gestureDescription) {
1678 }
1679 }
1680
1681 /* Object to keep track of gesture result callbacks */
1682 private static class GestureResultCallbackInfo {
1683 GestureDescription gestureDescription;
1684 GestureResultCallback callback;
1685 Handler handler;
1686
1687 GestureResultCallbackInfo(GestureDescription gestureDescription,
1688 GestureResultCallback callback, Handler handler) {
1689 this.gestureDescription = gestureDescription;
1690 this.callback = callback;
1691 this.handler = handler;
1692 }
1693 }
svetoslavganov75986cf2009-05-14 22:28:01 -07001694}