blob: c1c9174c0f9f8cdad9959c99f7b5c28981ca7ac7 [file] [log] [blame]
Svetoslav8e3feb12014-02-24 13:46:47 -08001/*
2 * Copyright (C) 2014 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.accessibility;
18
Phil Weaver155edc62016-06-09 10:24:53 -070019import android.annotation.Nullable;
Phil Weaver62d20fa2016-09-15 11:05:55 -070020import android.annotation.TestApi;
Svetoslav8e3feb12014-02-24 13:46:47 -080021import android.graphics.Rect;
22import android.os.Parcel;
23import android.os.Parcelable;
Phil Weaverbb2f28a2017-12-22 09:44:28 -080024import android.text.TextUtils;
Svetoslav8e3feb12014-02-24 13:46:47 -080025import android.util.LongArray;
26import android.util.Pools.SynchronizedPool;
Phil Weaverbb2f28a2017-12-22 09:44:28 -080027import android.view.accessibility.AccessibilityEvent.WindowsChangeTypes;
Svetoslav8e3feb12014-02-24 13:46:47 -080028
Phil Weaverbb2f28a2017-12-22 09:44:28 -080029import java.util.Objects;
Phil Weaverb010b122016-08-17 17:47:48 -070030import java.util.concurrent.atomic.AtomicInteger;
31
Svetoslav8e3feb12014-02-24 13:46:47 -080032/**
33 * This class represents a state snapshot of a window for accessibility
34 * purposes. The screen content contains one or more windows where some
35 * windows can be descendants of other windows, which is the windows are
36 * hierarchically ordered. Note that there is no root window. Hence, the
37 * screen content can be seen as a collection of window trees.
38 */
39public final class AccessibilityWindowInfo implements Parcelable {
40
41 private static final boolean DEBUG = false;
42
43 /**
44 * Window type: This is an application window. Such a window shows UI for
45 * interacting with an application.
46 */
47 public static final int TYPE_APPLICATION = 1;
48
49 /**
50 * Window type: This is an input method window. Such a window shows UI for
51 * inputting text such as keyboard, suggestions, etc.
52 */
53 public static final int TYPE_INPUT_METHOD = 2;
54
55 /**
56 * Window type: This is an system window. Such a window shows UI for
57 * interacting with the system.
58 */
59 public static final int TYPE_SYSTEM = 3;
60
Svetoslav3a5c7212014-10-14 09:54:26 -070061 /**
62 * Window type: Windows that are overlaid <em>only</em> by an {@link
63 * android.accessibilityservice.AccessibilityService} for interception of
64 * user interactions without changing the windows an accessibility service
65 * can introspect. In particular, an accessibility service can introspect
66 * only windows that a sighted user can interact with which they can touch
67 * these windows or can type into these windows. For example, if there
68 * is a full screen accessibility overlay that is touchable, the windows
69 * below it will be introspectable by an accessibility service regardless
70 * they are covered by a touchable window.
71 */
72 public static final int TYPE_ACCESSIBILITY_OVERLAY = 4;
73
Phil Weaver315c34e2016-02-19 15:12:29 -080074 /**
75 * Window type: A system window used to divide the screen in split-screen mode.
76 * This type of window is present only in split-screen mode.
77 */
78 public static final int TYPE_SPLIT_SCREEN_DIVIDER = 5;
79
Phil Weaverf00cd142017-03-03 13:44:00 -080080 /* Special values for window IDs */
81 /** @hide */
82 public static final int ACTIVE_WINDOW_ID = Integer.MAX_VALUE;
83 /** @hide */
84 public static final int UNDEFINED_WINDOW_ID = -1;
85 /** @hide */
86 public static final int ANY_WINDOW_ID = -2;
87 /** @hide */
88 public static final int PICTURE_IN_PICTURE_ACTION_REPLACER_WINDOW_ID = -3;
Svetoslav8e3feb12014-02-24 13:46:47 -080089
90 private static final int BOOLEAN_PROPERTY_ACTIVE = 1 << 0;
91 private static final int BOOLEAN_PROPERTY_FOCUSED = 1 << 1;
Svetoslav3a5c7212014-10-14 09:54:26 -070092 private static final int BOOLEAN_PROPERTY_ACCESSIBILITY_FOCUSED = 1 << 2;
Phil Weaver75dce7c2017-12-15 17:48:33 -080093 private static final int BOOLEAN_PROPERTY_PICTURE_IN_PICTURE = 1 << 3;
Svetoslav8e3feb12014-02-24 13:46:47 -080094
95 // Housekeeping.
96 private static final int MAX_POOL_SIZE = 10;
97 private static final SynchronizedPool<AccessibilityWindowInfo> sPool =
98 new SynchronizedPool<AccessibilityWindowInfo>(MAX_POOL_SIZE);
Phil Weaver62d20fa2016-09-15 11:05:55 -070099 private static AtomicInteger sNumInstancesInUse;
Svetoslav8e3feb12014-02-24 13:46:47 -0800100
101 // Data.
Phil Weaverf00cd142017-03-03 13:44:00 -0800102 private int mType = UNDEFINED_WINDOW_ID;
103 private int mLayer = UNDEFINED_WINDOW_ID;
Svetoslav8e3feb12014-02-24 13:46:47 -0800104 private int mBooleanProperties;
Phil Weaverf00cd142017-03-03 13:44:00 -0800105 private int mId = UNDEFINED_WINDOW_ID;
106 private int mParentId = UNDEFINED_WINDOW_ID;
Svetoslav8e3feb12014-02-24 13:46:47 -0800107 private final Rect mBoundsInScreen = new Rect();
108 private LongArray mChildIds;
Phil Weaver396d5492016-03-22 17:53:50 -0700109 private CharSequence mTitle;
Phil Weaver75dce7c2017-12-15 17:48:33 -0800110 private long mAnchorId = AccessibilityNodeInfo.UNDEFINED_NODE_ID;
Svetoslav8e3feb12014-02-24 13:46:47 -0800111
Phil Weaverf00cd142017-03-03 13:44:00 -0800112 private int mConnectionId = UNDEFINED_WINDOW_ID;
Svetoslav8e3feb12014-02-24 13:46:47 -0800113
114 private AccessibilityWindowInfo() {
115 /* do nothing - hide constructor */
116 }
117
118 /**
Phil Weaver396d5492016-03-22 17:53:50 -0700119 * Gets the title of the window.
120 *
Phil Weaver155edc62016-06-09 10:24:53 -0700121 * @return The title of the window, or {@code null} if none is available.
Phil Weaver396d5492016-03-22 17:53:50 -0700122 */
Phil Weaver155edc62016-06-09 10:24:53 -0700123 @Nullable
Phil Weaver396d5492016-03-22 17:53:50 -0700124 public CharSequence getTitle() {
125 return mTitle;
126 }
127
128 /**
129 * Sets the title of the window.
130 *
131 * @param title The title.
132 *
133 * @hide
134 */
135 public void setTitle(CharSequence title) {
136 mTitle = title;
137 }
138
139 /**
Svetoslav8e3feb12014-02-24 13:46:47 -0800140 * Gets the type of the window.
141 *
142 * @return The type.
143 *
144 * @see #TYPE_APPLICATION
145 * @see #TYPE_INPUT_METHOD
146 * @see #TYPE_SYSTEM
Svetoslav3a5c7212014-10-14 09:54:26 -0700147 * @see #TYPE_ACCESSIBILITY_OVERLAY
Svetoslav8e3feb12014-02-24 13:46:47 -0800148 */
149 public int getType() {
150 return mType;
151 }
152
153 /**
154 * Sets the type of the window.
155 *
Svetoslav3a5c7212014-10-14 09:54:26 -0700156 * @param type The type
Svetoslav8e3feb12014-02-24 13:46:47 -0800157 *
158 * @hide
159 */
160 public void setType(int type) {
161 mType = type;
162 }
163
164 /**
165 * Gets the layer which determines the Z-order of the window. Windows
166 * with greater layer appear on top of windows with lesser layer.
167 *
168 * @return The window layer.
169 */
170 public int getLayer() {
171 return mLayer;
172 }
173
174 /**
175 * Sets the layer which determines the Z-order of the window. Windows
176 * with greater layer appear on top of windows with lesser layer.
177 *
Svetoslav3a5c7212014-10-14 09:54:26 -0700178 * @param layer The window layer.
Svetoslav8e3feb12014-02-24 13:46:47 -0800179 *
180 * @hide
181 */
182 public void setLayer(int layer) {
183 mLayer = layer;
184 }
185
186 /**
187 * Gets the root node in the window's hierarchy.
188 *
189 * @return The root node.
190 */
191 public AccessibilityNodeInfo getRoot() {
Phil Weaverf00cd142017-03-03 13:44:00 -0800192 if (mConnectionId == UNDEFINED_WINDOW_ID) {
Svetoslav8e3feb12014-02-24 13:46:47 -0800193 return null;
194 }
195 AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance();
196 return client.findAccessibilityNodeInfoByAccessibilityId(mConnectionId,
197 mId, AccessibilityNodeInfo.ROOT_NODE_ID,
Phil Weaverc2e28932016-12-08 12:29:25 -0800198 true, AccessibilityNodeInfo.FLAG_PREFETCH_DESCENDANTS, null);
Svetoslav8e3feb12014-02-24 13:46:47 -0800199 }
200
201 /**
Phil Weaver396d5492016-03-22 17:53:50 -0700202 * Sets the anchor node's ID.
Svetoslav8e3feb12014-02-24 13:46:47 -0800203 *
Phil Weaver396d5492016-03-22 17:53:50 -0700204 * @param anchorId The anchor's accessibility id in its window.
205 *
206 * @hide
207 */
Phil Weaver75dce7c2017-12-15 17:48:33 -0800208 public void setAnchorId(long anchorId) {
Phil Weaver396d5492016-03-22 17:53:50 -0700209 mAnchorId = anchorId;
210 }
211
212 /**
213 * Gets the node that anchors this window to another.
214 *
215 * @return The anchor node, or {@code null} if none exists.
216 */
217 public AccessibilityNodeInfo getAnchor() {
Phil Weaver75dce7c2017-12-15 17:48:33 -0800218 if ((mConnectionId == UNDEFINED_WINDOW_ID)
219 || (mAnchorId == AccessibilityNodeInfo.UNDEFINED_NODE_ID)
Phil Weaverf00cd142017-03-03 13:44:00 -0800220 || (mParentId == UNDEFINED_WINDOW_ID)) {
Phil Weaver396d5492016-03-22 17:53:50 -0700221 return null;
222 }
223
224 AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance();
225 return client.findAccessibilityNodeInfoByAccessibilityId(mConnectionId,
Phil Weaverc2e28932016-12-08 12:29:25 -0800226 mParentId, mAnchorId, true, 0, null);
Phil Weaver396d5492016-03-22 17:53:50 -0700227 }
228
Phil Weaverf00cd142017-03-03 13:44:00 -0800229 /** @hide */
230 public void setPictureInPicture(boolean pictureInPicture) {
Phil Weaver75dce7c2017-12-15 17:48:33 -0800231 setBooleanProperty(BOOLEAN_PROPERTY_PICTURE_IN_PICTURE, pictureInPicture);
Phil Weaverbe2922f2017-04-28 14:58:35 -0700232 }
233
234 /**
235 * Check if the window is in picture-in-picture mode.
236 *
237 * @return {@code true} if the window is in picture-in-picture mode, {@code false} otherwise.
238 */
239 public boolean isInPictureInPictureMode() {
Phil Weaver75dce7c2017-12-15 17:48:33 -0800240 return getBooleanProperty(BOOLEAN_PROPERTY_PICTURE_IN_PICTURE);
Phil Weaverf00cd142017-03-03 13:44:00 -0800241 }
242
Phil Weaver396d5492016-03-22 17:53:50 -0700243 /**
244 * Gets the parent window.
245 *
246 * @return The parent window, or {@code null} if none exists.
Svetoslav8e3feb12014-02-24 13:46:47 -0800247 */
248 public AccessibilityWindowInfo getParent() {
Phil Weaverf00cd142017-03-03 13:44:00 -0800249 if (mConnectionId == UNDEFINED_WINDOW_ID || mParentId == UNDEFINED_WINDOW_ID) {
Svetoslav8e3feb12014-02-24 13:46:47 -0800250 return null;
251 }
252 AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance();
253 return client.getWindow(mConnectionId, mParentId);
254 }
255
256 /**
257 * Sets the parent window id.
258 *
259 * @param parentId The parent id.
260 *
261 * @hide
262 */
263 public void setParentId(int parentId) {
264 mParentId = parentId;
265 }
266
267 /**
268 * Gets the unique window id.
269 *
270 * @return windowId The window id.
271 */
272 public int getId() {
273 return mId;
274 }
275
276 /**
277 * Sets the unique window id.
278 *
Svetoslav3a5c7212014-10-14 09:54:26 -0700279 * @param id The window id.
Svetoslav8e3feb12014-02-24 13:46:47 -0800280 *
281 * @hide
282 */
283 public void setId(int id) {
284 mId = id;
285 }
286
287 /**
288 * Sets the unique id of the IAccessibilityServiceConnection over which
289 * this instance can send requests to the system.
290 *
291 * @param connectionId The connection id.
292 *
293 * @hide
294 */
295 public void setConnectionId(int connectionId) {
296 mConnectionId = connectionId;
297 }
298
299 /**
300 * Gets the bounds of this window in the screen.
301 *
302 * @param outBounds The out window bounds.
303 */
304 public void getBoundsInScreen(Rect outBounds) {
305 outBounds.set(mBoundsInScreen);
306 }
307
308 /**
309 * Sets the bounds of this window in the screen.
310 *
311 * @param bounds The out window bounds.
312 *
313 * @hide
314 */
315 public void setBoundsInScreen(Rect bounds) {
316 mBoundsInScreen.set(bounds);
317 }
318
319 /**
320 * Gets if this window is active. An active window is the one
321 * the user is currently touching or the window has input focus
322 * and the user is not touching any window.
323 *
324 * @return Whether this is the active window.
325 */
326 public boolean isActive() {
327 return getBooleanProperty(BOOLEAN_PROPERTY_ACTIVE);
328 }
329
330 /**
331 * Sets if this window is active, which is this is the window
332 * the user is currently touching or the window has input focus
333 * and the user is not touching any window.
334 *
Svetoslav3a5c7212014-10-14 09:54:26 -0700335 * @param active Whether this is the active window.
Svetoslav8e3feb12014-02-24 13:46:47 -0800336 *
337 * @hide
338 */
339 public void setActive(boolean active) {
340 setBooleanProperty(BOOLEAN_PROPERTY_ACTIVE, active);
341 }
342
343 /**
344 * Gets if this window has input focus.
345 *
346 * @return Whether has input focus.
347 */
348 public boolean isFocused() {
349 return getBooleanProperty(BOOLEAN_PROPERTY_FOCUSED);
350 }
351
352 /**
353 * Sets if this window has input focus.
354 *
Svetoslav3a5c7212014-10-14 09:54:26 -0700355 * @param focused Whether has input focus.
Svetoslav8e3feb12014-02-24 13:46:47 -0800356 *
357 * @hide
358 */
359 public void setFocused(boolean focused) {
360 setBooleanProperty(BOOLEAN_PROPERTY_FOCUSED, focused);
361 }
362
363 /**
Svetoslav04cab1b2014-08-25 18:35:57 -0700364 * Gets if this window has accessibility focus.
365 *
366 * @return Whether has accessibility focus.
367 */
368 public boolean isAccessibilityFocused() {
Svetoslav3a5c7212014-10-14 09:54:26 -0700369 return getBooleanProperty(BOOLEAN_PROPERTY_ACCESSIBILITY_FOCUSED);
Svetoslav04cab1b2014-08-25 18:35:57 -0700370 }
371
372 /**
373 * Sets if this window has accessibility focus.
374 *
Svetoslav3a5c7212014-10-14 09:54:26 -0700375 * @param focused Whether has accessibility focus.
Svetoslav04cab1b2014-08-25 18:35:57 -0700376 *
377 * @hide
378 */
379 public void setAccessibilityFocused(boolean focused) {
Svetoslav3a5c7212014-10-14 09:54:26 -0700380 setBooleanProperty(BOOLEAN_PROPERTY_ACCESSIBILITY_FOCUSED, focused);
Svetoslav04cab1b2014-08-25 18:35:57 -0700381 }
382
383 /**
Svetoslav8e3feb12014-02-24 13:46:47 -0800384 * Gets the number of child windows.
385 *
386 * @return The child count.
387 */
388 public int getChildCount() {
389 return (mChildIds != null) ? mChildIds.size() : 0;
390 }
391
392 /**
393 * Gets the child window at a given index.
394 *
395 * @param index The index.
396 * @return The child.
397 */
398 public AccessibilityWindowInfo getChild(int index) {
399 if (mChildIds == null) {
400 throw new IndexOutOfBoundsException();
401 }
Phil Weaverf00cd142017-03-03 13:44:00 -0800402 if (mConnectionId == UNDEFINED_WINDOW_ID) {
Svetoslav8e3feb12014-02-24 13:46:47 -0800403 return null;
404 }
405 final int childId = (int) mChildIds.get(index);
406 AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance();
407 return client.getWindow(mConnectionId, childId);
408 }
409
410 /**
411 * Adds a child window.
412 *
413 * @param childId The child window id.
414 *
415 * @hide
416 */
417 public void addChild(int childId) {
418 if (mChildIds == null) {
419 mChildIds = new LongArray();
420 }
421 mChildIds.add(childId);
422 }
423
424 /**
425 * Returns a cached instance if such is available or a new one is
426 * created.
427 *
428 * @return An instance.
429 */
430 public static AccessibilityWindowInfo obtain() {
431 AccessibilityWindowInfo info = sPool.acquire();
432 if (info == null) {
433 info = new AccessibilityWindowInfo();
434 }
Phil Weaver62d20fa2016-09-15 11:05:55 -0700435 if (sNumInstancesInUse != null) {
436 sNumInstancesInUse.incrementAndGet();
437 }
Svetoslav8e3feb12014-02-24 13:46:47 -0800438 return info;
439 }
440
441 /**
442 * Returns a cached instance if such is available or a new one is
443 * created. The returned instance is initialized from the given
444 * <code>info</code>.
445 *
446 * @param info The other info.
447 * @return An instance.
448 */
449 public static AccessibilityWindowInfo obtain(AccessibilityWindowInfo info) {
450 AccessibilityWindowInfo infoClone = obtain();
451
452 infoClone.mType = info.mType;
453 infoClone.mLayer = info.mLayer;
454 infoClone.mBooleanProperties = info.mBooleanProperties;
455 infoClone.mId = info.mId;
456 infoClone.mParentId = info.mParentId;
457 infoClone.mBoundsInScreen.set(info.mBoundsInScreen);
Phil Weaver396d5492016-03-22 17:53:50 -0700458 infoClone.mTitle = info.mTitle;
459 infoClone.mAnchorId = info.mAnchorId;
Svetoslav8e3feb12014-02-24 13:46:47 -0800460
461 if (info.mChildIds != null && info.mChildIds.size() > 0) {
462 if (infoClone.mChildIds == null) {
463 infoClone.mChildIds = info.mChildIds.clone();
464 } else {
465 infoClone.mChildIds.addAll(info.mChildIds);
466 }
467 }
468
469 infoClone.mConnectionId = info.mConnectionId;
470
471 return infoClone;
472 }
473
474 /**
Phil Weaver62d20fa2016-09-15 11:05:55 -0700475 * Specify a counter that will be incremented on obtain() and decremented on recycle()
Phil Weaverb010b122016-08-17 17:47:48 -0700476 *
477 * @hide
478 */
Phil Weaver62d20fa2016-09-15 11:05:55 -0700479 @TestApi
480 public static void setNumInstancesInUseCounter(AtomicInteger counter) {
481 if (sNumInstancesInUse != null) {
482 sNumInstancesInUse = counter;
483 }
Phil Weaverb010b122016-08-17 17:47:48 -0700484 }
485
486 /**
Svetoslav8e3feb12014-02-24 13:46:47 -0800487 * Return an instance back to be reused.
488 * <p>
489 * <strong>Note:</strong> You must not touch the object after calling this function.
490 * </p>
491 *
492 * @throws IllegalStateException If the info is already recycled.
493 */
494 public void recycle() {
495 clear();
496 sPool.release(this);
Phil Weaver62d20fa2016-09-15 11:05:55 -0700497 if (sNumInstancesInUse != null) {
498 sNumInstancesInUse.decrementAndGet();
499 }
Svetoslav8e3feb12014-02-24 13:46:47 -0800500 }
501
502 @Override
503 public int describeContents() {
504 return 0;
505 }
506
507 @Override
508 public void writeToParcel(Parcel parcel, int flags) {
509 parcel.writeInt(mType);
510 parcel.writeInt(mLayer);
511 parcel.writeInt(mBooleanProperties);
512 parcel.writeInt(mId);
513 parcel.writeInt(mParentId);
514 mBoundsInScreen.writeToParcel(parcel, flags);
Phil Weaver396d5492016-03-22 17:53:50 -0700515 parcel.writeCharSequence(mTitle);
Phil Weaver75dce7c2017-12-15 17:48:33 -0800516 parcel.writeLong(mAnchorId);
Svetoslav8e3feb12014-02-24 13:46:47 -0800517
518 final LongArray childIds = mChildIds;
519 if (childIds == null) {
520 parcel.writeInt(0);
521 } else {
522 final int childCount = childIds.size();
523 parcel.writeInt(childCount);
524 for (int i = 0; i < childCount; i++) {
525 parcel.writeInt((int) childIds.get(i));
526 }
527 }
528
529 parcel.writeInt(mConnectionId);
530 }
531
532 private void initFromParcel(Parcel parcel) {
533 mType = parcel.readInt();
534 mLayer = parcel.readInt();
535 mBooleanProperties = parcel.readInt();
536 mId = parcel.readInt();
537 mParentId = parcel.readInt();
538 mBoundsInScreen.readFromParcel(parcel);
Phil Weaver396d5492016-03-22 17:53:50 -0700539 mTitle = parcel.readCharSequence();
Phil Weaver75dce7c2017-12-15 17:48:33 -0800540 mAnchorId = parcel.readLong();
Svetoslav8e3feb12014-02-24 13:46:47 -0800541
542 final int childCount = parcel.readInt();
543 if (childCount > 0) {
544 if (mChildIds == null) {
545 mChildIds = new LongArray(childCount);
546 }
547 for (int i = 0; i < childCount; i++) {
548 final int childId = parcel.readInt();
549 mChildIds.add(childId);
550 }
551 }
552
553 mConnectionId = parcel.readInt();
554 }
555
556 @Override
557 public int hashCode() {
558 return mId;
559 }
560
561 @Override
562 public boolean equals(Object obj) {
563 if (this == obj) {
564 return true;
565 }
566 if (obj == null) {
567 return false;
568 }
569 if (getClass() != obj.getClass()) {
570 return false;
571 }
572 AccessibilityWindowInfo other = (AccessibilityWindowInfo) obj;
573 return (mId == other.mId);
574 }
575
576 @Override
577 public String toString() {
578 StringBuilder builder = new StringBuilder();
579 builder.append("AccessibilityWindowInfo[");
Phil Weaver396d5492016-03-22 17:53:50 -0700580 builder.append("title=").append(mTitle);
Phil Weaverbb2f28a2017-12-22 09:44:28 -0800581 builder.append(", id=").append(mId);
Svetoslav8e3feb12014-02-24 13:46:47 -0800582 builder.append(", type=").append(typeToString(mType));
583 builder.append(", layer=").append(mLayer);
584 builder.append(", bounds=").append(mBoundsInScreen);
585 builder.append(", focused=").append(isFocused());
586 builder.append(", active=").append(isActive());
Phil Weaver75dce7c2017-12-15 17:48:33 -0800587 builder.append(", pictureInPicture=").append(isInPictureInPictureMode());
Svetoslav8e3feb12014-02-24 13:46:47 -0800588 if (DEBUG) {
589 builder.append(", parent=").append(mParentId);
590 builder.append(", children=[");
591 if (mChildIds != null) {
592 final int childCount = mChildIds.size();
593 for (int i = 0; i < childCount; i++) {
594 builder.append(mChildIds.get(i));
595 if (i < childCount - 1) {
596 builder.append(',');
597 }
598 }
599 } else {
600 builder.append("null");
601 }
602 builder.append(']');
603 } else {
Phil Weaverf00cd142017-03-03 13:44:00 -0800604 builder.append(", hasParent=").append(mParentId != UNDEFINED_WINDOW_ID);
Phil Weaver75dce7c2017-12-15 17:48:33 -0800605 builder.append(", isAnchored=")
606 .append(mAnchorId != AccessibilityNodeInfo.UNDEFINED_NODE_ID);
Svetoslav8e3feb12014-02-24 13:46:47 -0800607 builder.append(", hasChildren=").append(mChildIds != null
608 && mChildIds.size() > 0);
609 }
610 builder.append(']');
611 return builder.toString();
612 }
613
614 /**
615 * Clears the internal state.
616 */
617 private void clear() {
Phil Weaverf00cd142017-03-03 13:44:00 -0800618 mType = UNDEFINED_WINDOW_ID;
619 mLayer = UNDEFINED_WINDOW_ID;
Svetoslav8e3feb12014-02-24 13:46:47 -0800620 mBooleanProperties = 0;
Phil Weaverf00cd142017-03-03 13:44:00 -0800621 mId = UNDEFINED_WINDOW_ID;
622 mParentId = UNDEFINED_WINDOW_ID;
Svetoslav8e3feb12014-02-24 13:46:47 -0800623 mBoundsInScreen.setEmpty();
624 if (mChildIds != null) {
625 mChildIds.clear();
626 }
Phil Weaverf00cd142017-03-03 13:44:00 -0800627 mConnectionId = UNDEFINED_WINDOW_ID;
Phil Weaver75dce7c2017-12-15 17:48:33 -0800628 mAnchorId = AccessibilityNodeInfo.UNDEFINED_NODE_ID;
Phil Weaver396d5492016-03-22 17:53:50 -0700629 mTitle = null;
Svetoslav8e3feb12014-02-24 13:46:47 -0800630 }
631
632 /**
633 * Gets the value of a boolean property.
634 *
635 * @param property The property.
636 * @return The value.
637 */
638 private boolean getBooleanProperty(int property) {
639 return (mBooleanProperties & property) != 0;
640 }
641
642 /**
643 * Sets a boolean property.
644 *
645 * @param property The property.
646 * @param value The value.
647 *
648 * @throws IllegalStateException If called from an AccessibilityService.
649 */
650 private void setBooleanProperty(int property, boolean value) {
651 if (value) {
652 mBooleanProperties |= property;
653 } else {
654 mBooleanProperties &= ~property;
655 }
656 }
657
658 private static String typeToString(int type) {
659 switch (type) {
660 case TYPE_APPLICATION: {
661 return "TYPE_APPLICATION";
662 }
663 case TYPE_INPUT_METHOD: {
664 return "TYPE_INPUT_METHOD";
665 }
666 case TYPE_SYSTEM: {
667 return "TYPE_SYSTEM";
668 }
Svetoslav3a5c7212014-10-14 09:54:26 -0700669 case TYPE_ACCESSIBILITY_OVERLAY: {
670 return "TYPE_ACCESSIBILITY_OVERLAY";
671 }
Phil Weaver315c34e2016-02-19 15:12:29 -0800672 case TYPE_SPLIT_SCREEN_DIVIDER: {
673 return "TYPE_SPLIT_SCREEN_DIVIDER";
674 }
Svetoslav8e3feb12014-02-24 13:46:47 -0800675 default:
676 return "<UNKNOWN>";
677 }
678 }
679
680 /**
681 * Checks whether this window changed. The argument should be
682 * another state of the same window, which is have the same id
683 * and type as they never change.
684 *
685 * @param other The new state.
686 * @return Whether something changed.
687 *
688 * @hide
689 */
690 public boolean changed(AccessibilityWindowInfo other) {
691 if (other.mId != mId) {
692 throw new IllegalArgumentException("Not same window.");
693 }
694 if (other.mType != mType) {
695 throw new IllegalArgumentException("Not same type.");
696 }
Andreas Gampe650989b2015-03-15 18:04:41 -0700697 if (!mBoundsInScreen.equals(other.mBoundsInScreen)) {
Svetoslav8e3feb12014-02-24 13:46:47 -0800698 return true;
699 }
700 if (mLayer != other.mLayer) {
701 return true;
702 }
703 if (mBooleanProperties != other.mBooleanProperties) {
704 return true;
705 }
706 if (mParentId != other.mParentId) {
707 return true;
708 }
709 if (mChildIds == null) {
710 if (other.mChildIds != null) {
711 return true;
712 }
713 } else if (!mChildIds.equals(other.mChildIds)) {
714 return true;
715 }
716 return false;
717 }
718
Phil Weaverbb2f28a2017-12-22 09:44:28 -0800719 /**
720 * Reports how this window differs from a possibly different state of the same window. The
721 * argument must have the same id and type as neither of those properties may change.
722 *
723 * @param other The new state.
724 * @return A set of flags showing how the window has changes, or 0 if the two states are the
725 * same.
726 *
727 * @hide
728 */
729 @WindowsChangeTypes
730 public int differenceFrom(AccessibilityWindowInfo other) {
731 if (other.mId != mId) {
732 throw new IllegalArgumentException("Not same window.");
733 }
734 if (other.mType != mType) {
735 throw new IllegalArgumentException("Not same type.");
736 }
737 int changes = 0;
738 if (!TextUtils.equals(mTitle, other.mTitle)) {
739 changes |= AccessibilityEvent.WINDOWS_CHANGE_TITLE;
740 }
741
742 if (!mBoundsInScreen.equals(other.mBoundsInScreen)) {
743 changes |= AccessibilityEvent.WINDOWS_CHANGE_BOUNDS;
744 }
745 if (mLayer != other.mLayer) {
746 changes |= AccessibilityEvent.WINDOWS_CHANGE_LAYER;
747 }
748 if (getBooleanProperty(BOOLEAN_PROPERTY_ACTIVE)
749 != other.getBooleanProperty(BOOLEAN_PROPERTY_ACTIVE)) {
750 changes |= AccessibilityEvent.WINDOWS_CHANGE_ACTIVE;
751 }
752 if (getBooleanProperty(BOOLEAN_PROPERTY_FOCUSED)
753 != other.getBooleanProperty(BOOLEAN_PROPERTY_FOCUSED)) {
754 changes |= AccessibilityEvent.WINDOWS_CHANGE_FOCUSED;
755 }
756 if (getBooleanProperty(BOOLEAN_PROPERTY_ACCESSIBILITY_FOCUSED)
757 != other.getBooleanProperty(BOOLEAN_PROPERTY_ACCESSIBILITY_FOCUSED)) {
758 changes |= AccessibilityEvent.WINDOWS_CHANGE_ACCESSIBILITY_FOCUSED;
759 }
760 if (getBooleanProperty(BOOLEAN_PROPERTY_PICTURE_IN_PICTURE)
761 != other.getBooleanProperty(BOOLEAN_PROPERTY_PICTURE_IN_PICTURE)) {
762 changes |= AccessibilityEvent.WINDOWS_CHANGE_PIP;
763 }
764 if (mParentId != other.mParentId) {
765 changes |= AccessibilityEvent.WINDOWS_CHANGE_PARENT;
766 }
767 if (!Objects.equals(mChildIds, other.mChildIds)) {
768 changes |= AccessibilityEvent.WINDOWS_CHANGE_CHILDREN;
769 }
770 return changes;
771 }
772
Svetoslav8e3feb12014-02-24 13:46:47 -0800773 public static final Parcelable.Creator<AccessibilityWindowInfo> CREATOR =
774 new Creator<AccessibilityWindowInfo>() {
775 @Override
776 public AccessibilityWindowInfo createFromParcel(Parcel parcel) {
777 AccessibilityWindowInfo info = obtain();
778 info.initFromParcel(parcel);
779 return info;
780 }
781
782 @Override
783 public AccessibilityWindowInfo[] newArray(int size) {
784 return new AccessibilityWindowInfo[size];
785 }
786 };
787}