1 /* 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 17 package android.view.accessibility; 18 19 import android.annotation.FlaggedApi; 20 import android.annotation.NonNull; 21 import android.annotation.Nullable; 22 import android.annotation.TestApi; 23 import android.annotation.UptimeMillisLong; 24 import android.app.ActivityTaskManager; 25 import android.graphics.Rect; 26 import android.graphics.Region; 27 import android.os.LocaleList; 28 import android.os.Parcel; 29 import android.os.Parcelable; 30 import android.os.SystemClock; 31 import android.text.TextUtils; 32 import android.util.LongArray; 33 import android.util.Pools.SynchronizedPool; 34 import android.util.SparseArray; 35 import android.view.Display; 36 import android.view.accessibility.AccessibilityEvent.WindowsChangeTypes; 37 38 import java.util.ArrayList; 39 import java.util.List; 40 import java.util.Objects; 41 import java.util.concurrent.atomic.AtomicInteger; 42 43 /** 44 * This class represents a state snapshot of a window for accessibility 45 * purposes. The screen content contains one or more windows where some 46 * windows can be descendants of other windows, which is the windows are 47 * hierarchically ordered. Note that there is no root window. Hence, the 48 * screen content can be seen as a collection of window trees. 49 */ 50 public final class AccessibilityWindowInfo implements Parcelable { 51 52 private static final boolean DEBUG = false; 53 54 /** 55 * Window type: This is an application window. Such a window shows UI for 56 * interacting with an application. 57 */ 58 public static final int TYPE_APPLICATION = 1; 59 60 /** 61 * Window type: This is an input method window. Such a window shows UI for 62 * inputting text such as keyboard, suggestions, etc. 63 */ 64 public static final int TYPE_INPUT_METHOD = 2; 65 66 /** 67 * Window type: This is a system window. Such a window shows UI for 68 * interacting with the system. 69 */ 70 public static final int TYPE_SYSTEM = 3; 71 72 /** 73 * Window type: Windows that are overlaid <em>only</em> by an {@link 74 * android.accessibilityservice.AccessibilityService} for interception of 75 * user interactions without changing the windows an accessibility service 76 * can introspect. In particular, an accessibility service can introspect 77 * only windows that a sighted user can interact with which they can touch 78 * these windows or can type into these windows. For example, if there 79 * is a full screen accessibility overlay that is touchable, the windows 80 * below it will be introspectable by an accessibility service regardless 81 * they are covered by a touchable window. 82 */ 83 public static final int TYPE_ACCESSIBILITY_OVERLAY = 4; 84 85 /** 86 * Window type: A system window used to divide the screen in split-screen mode. 87 * This type of window is present only in split-screen mode. 88 */ 89 public static final int TYPE_SPLIT_SCREEN_DIVIDER = 5; 90 91 /** 92 * Window type: A system window used to show the UI for the interaction with 93 * window-based magnification, which includes the magnified content and the option menu. 94 */ 95 public static final int TYPE_MAGNIFICATION_OVERLAY = 6; 96 97 /** 98 * Window type: A system window that has the function to control an associated window. 99 */ 100 @FlaggedApi(Flags.FLAG_ADD_TYPE_WINDOW_CONTROL) 101 public static final int TYPE_WINDOW_CONTROL = 7; 102 103 /* Special values for window IDs */ 104 /** @hide */ 105 public static final int ACTIVE_WINDOW_ID = Integer.MAX_VALUE; 106 /** @hide */ 107 public static final int UNDEFINED_CONNECTION_ID = -1; 108 /** @hide */ 109 public static final int UNDEFINED_WINDOW_ID = -1; 110 /** @hide */ 111 public static final int ANY_WINDOW_ID = -2; 112 /** @hide */ 113 public static final int PICTURE_IN_PICTURE_ACTION_REPLACER_WINDOW_ID = -3; 114 115 private static final int BOOLEAN_PROPERTY_ACTIVE = 1 << 0; 116 private static final int BOOLEAN_PROPERTY_FOCUSED = 1 << 1; 117 private static final int BOOLEAN_PROPERTY_ACCESSIBILITY_FOCUSED = 1 << 2; 118 private static final int BOOLEAN_PROPERTY_PICTURE_IN_PICTURE = 1 << 3; 119 120 // Housekeeping. 121 private static final int MAX_POOL_SIZE = 10; 122 private static final SynchronizedPool<AccessibilityWindowInfo> sPool = 123 new SynchronizedPool<AccessibilityWindowInfo>(MAX_POOL_SIZE); 124 // TODO(b/129300068): Remove sNumInstancesInUse. 125 private static AtomicInteger sNumInstancesInUse; 126 127 // Data. 128 private int mDisplayId = Display.INVALID_DISPLAY; 129 private int mType = UNDEFINED_WINDOW_ID; 130 private int mLayer = UNDEFINED_WINDOW_ID; 131 private int mBooleanProperties; 132 private int mId = UNDEFINED_WINDOW_ID; 133 private int mParentId = UNDEFINED_WINDOW_ID; 134 private int mTaskId = ActivityTaskManager.INVALID_TASK_ID; 135 private Region mRegionInScreen = new Region(); 136 private LongArray mChildIds; 137 private CharSequence mTitle; 138 private long mAnchorId = AccessibilityNodeInfo.UNDEFINED_NODE_ID; 139 private long mTransitionTime; 140 141 private int mConnectionId = UNDEFINED_CONNECTION_ID; 142 143 private LocaleList mLocales = LocaleList.getEmptyLocaleList(); 144 145 /** 146 * Creates a new {@link AccessibilityWindowInfo}. 147 */ AccessibilityWindowInfo()148 public AccessibilityWindowInfo() { 149 } 150 151 /** 152 * Copy constructor. Creates a new {@link AccessibilityWindowInfo}, and this new instance is 153 * initialized from given <code>info</code>. 154 * 155 * @param info The other info. 156 */ AccessibilityWindowInfo(@onNull AccessibilityWindowInfo info)157 public AccessibilityWindowInfo(@NonNull AccessibilityWindowInfo info) { 158 init(info); 159 } 160 161 /** 162 * Gets the title of the window. 163 * 164 * @return The title of the window, or {@code null} if none is available. 165 */ 166 @Nullable getTitle()167 public CharSequence getTitle() { 168 return mTitle; 169 } 170 171 /** 172 * Sets the title of the window. 173 * 174 * @param title The title. 175 * 176 * @hide 177 */ setTitle(CharSequence title)178 public void setTitle(CharSequence title) { 179 mTitle = title; 180 } 181 182 /** 183 * Gets the type of the window. 184 * 185 * @return The type. 186 * 187 * @see #TYPE_APPLICATION 188 * @see #TYPE_INPUT_METHOD 189 * @see #TYPE_SYSTEM 190 * @see #TYPE_ACCESSIBILITY_OVERLAY 191 */ getType()192 public int getType() { 193 return mType; 194 } 195 196 /** 197 * Sets the type of the window. 198 * 199 * @param type The type 200 * 201 * @hide 202 */ setType(int type)203 public void setType(int type) { 204 mType = type; 205 } 206 207 /** 208 * Gets the layer which determines the Z-order of the window. Windows 209 * with greater layer appear on top of windows with lesser layer. 210 * 211 * @return The window layer. 212 */ getLayer()213 public int getLayer() { 214 return mLayer; 215 } 216 217 /** 218 * Sets the layer which determines the Z-order of the window. Windows 219 * with greater layer appear on top of windows with lesser layer. 220 * 221 * @param layer The window layer. 222 * 223 * @hide 224 */ setLayer(int layer)225 public void setLayer(int layer) { 226 mLayer = layer; 227 } 228 229 /** 230 * Gets the root node in the window's hierarchy. 231 * 232 * @return The root node. 233 */ getRoot()234 public AccessibilityNodeInfo getRoot() { 235 return getRoot(AccessibilityNodeInfo.FLAG_PREFETCH_DESCENDANTS_HYBRID); 236 } 237 238 /** 239 * Gets the root node in the window's hierarchy. 240 * 241 * @param prefetchingStrategy the prefetching strategy. 242 * @return The root node. 243 * 244 * @see AccessibilityNodeInfo#getParent(int) for a description of prefetching. 245 */ 246 @Nullable getRoot( @ccessibilityNodeInfo.PrefetchingStrategy int prefetchingStrategy)247 public AccessibilityNodeInfo getRoot( 248 @AccessibilityNodeInfo.PrefetchingStrategy int prefetchingStrategy) { 249 if (mConnectionId == UNDEFINED_WINDOW_ID) { 250 return null; 251 } 252 AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance(); 253 return client.findAccessibilityNodeInfoByAccessibilityId(mConnectionId, 254 mId, AccessibilityNodeInfo.ROOT_NODE_ID, 255 true, prefetchingStrategy, null); 256 } 257 258 /** 259 * Sets the anchor node's ID. 260 * 261 * @param anchorId The anchor's accessibility id in its window. 262 * 263 * @hide 264 */ setAnchorId(long anchorId)265 public void setAnchorId(long anchorId) { 266 mAnchorId = anchorId; 267 } 268 269 /** 270 * Gets the node that anchors this window to another. 271 * 272 * @return The anchor node, or {@code null} if none exists. 273 */ getAnchor()274 public AccessibilityNodeInfo getAnchor() { 275 if ((mConnectionId == UNDEFINED_WINDOW_ID) 276 || (mAnchorId == AccessibilityNodeInfo.UNDEFINED_NODE_ID) 277 || (mParentId == UNDEFINED_WINDOW_ID)) { 278 return null; 279 } 280 281 AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance(); 282 return client.findAccessibilityNodeInfoByAccessibilityId(mConnectionId, 283 mParentId, mAnchorId, true, 0, null); 284 } 285 286 /** @hide */ setPictureInPicture(boolean pictureInPicture)287 public void setPictureInPicture(boolean pictureInPicture) { 288 setBooleanProperty(BOOLEAN_PROPERTY_PICTURE_IN_PICTURE, pictureInPicture); 289 } 290 291 /** 292 * Check if the window is in picture-in-picture mode. 293 * 294 * @return {@code true} if the window is in picture-in-picture mode, {@code false} otherwise. 295 */ isInPictureInPictureMode()296 public boolean isInPictureInPictureMode() { 297 return getBooleanProperty(BOOLEAN_PROPERTY_PICTURE_IN_PICTURE); 298 } 299 300 /** 301 * Gets the parent window. 302 * 303 * @return The parent window, or {@code null} if none exists. 304 */ getParent()305 public AccessibilityWindowInfo getParent() { 306 if (mConnectionId == UNDEFINED_WINDOW_ID || mParentId == UNDEFINED_WINDOW_ID) { 307 return null; 308 } 309 AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance(); 310 return client.getWindow(mConnectionId, mParentId); 311 } 312 313 /** 314 * Sets the parent window id. 315 * 316 * @param parentId The parent id. 317 * 318 * @hide 319 */ setParentId(int parentId)320 public void setParentId(int parentId) { 321 mParentId = parentId; 322 } 323 324 /** 325 * Gets the unique window id. 326 * 327 * @return windowId The window id. 328 */ getId()329 public int getId() { 330 return mId; 331 } 332 333 /** 334 * Sets the unique window id. 335 * 336 * @param id The window id. 337 * 338 * @hide 339 */ setId(int id)340 public void setId(int id) { 341 mId = id; 342 } 343 344 /** 345 * Gets the task ID. 346 * 347 * @return The task ID. 348 * 349 * @hide 350 */ getTaskId()351 public int getTaskId() { 352 return mTaskId; 353 } 354 355 /** 356 * Sets the task ID. 357 * 358 * @param taskId The task ID. 359 * 360 * @hide 361 */ setTaskId(int taskId)362 public void setTaskId(int taskId) { 363 mTaskId = taskId; 364 } 365 366 /** 367 * Sets the unique id of the IAccessibilityServiceConnection over which 368 * this instance can send requests to the system. 369 * 370 * @param connectionId The connection id. 371 * 372 * @hide 373 */ setConnectionId(int connectionId)374 public void setConnectionId(int connectionId) { 375 mConnectionId = connectionId; 376 } 377 378 /** 379 * Gets the touchable region of this window in the screen. 380 * 381 * @param outRegion The out window region. 382 */ getRegionInScreen(@onNull Region outRegion)383 public void getRegionInScreen(@NonNull Region outRegion) { 384 outRegion.set(mRegionInScreen); 385 } 386 387 /** 388 * Sets the touchable region of this window in the screen. 389 * 390 * @param region The window region. 391 * 392 * @hide 393 */ setRegionInScreen(Region region)394 public void setRegionInScreen(Region region) { 395 mRegionInScreen.set(region); 396 } 397 398 /** 399 * Gets the bounds of this window in the screen. This is equivalent to get the bounds of the 400 * Region from {@link #getRegionInScreen(Region)}. 401 * 402 * @param outBounds The out window bounds. 403 */ getBoundsInScreen(Rect outBounds)404 public void getBoundsInScreen(Rect outBounds) { 405 outBounds.set(mRegionInScreen.getBounds()); 406 } 407 408 /** 409 * Gets if this window is active. An active window is the one 410 * the user is currently touching or the window has input focus 411 * and the user is not touching any window. 412 * <p> 413 * This is defined as the window that most recently fired one 414 * of the following events: 415 * {@link AccessibilityEvent#TYPE_WINDOW_STATE_CHANGED}, 416 * {@link AccessibilityEvent#TYPE_VIEW_HOVER_ENTER}, 417 * {@link AccessibilityEvent#TYPE_VIEW_HOVER_EXIT}. 418 * In other words, the last window shown that also has input focus. 419 * </p> 420 * 421 * @return Whether this is the active window. 422 */ isActive()423 public boolean isActive() { 424 return getBooleanProperty(BOOLEAN_PROPERTY_ACTIVE); 425 } 426 427 /** 428 * Sets if this window is active, which is this is the window 429 * the user is currently touching or the window has input focus 430 * and the user is not touching any window. 431 * 432 * @param active Whether this is the active window. 433 * 434 * @hide 435 */ setActive(boolean active)436 public void setActive(boolean active) { 437 setBooleanProperty(BOOLEAN_PROPERTY_ACTIVE, active); 438 } 439 440 /** 441 * Gets if this window has input focus. 442 * 443 * @return Whether has input focus. 444 */ isFocused()445 public boolean isFocused() { 446 return getBooleanProperty(BOOLEAN_PROPERTY_FOCUSED); 447 } 448 449 /** 450 * Sets if this window has input focus. 451 * 452 * @param focused Whether has input focus. 453 * 454 * @hide 455 */ setFocused(boolean focused)456 public void setFocused(boolean focused) { 457 setBooleanProperty(BOOLEAN_PROPERTY_FOCUSED, focused); 458 } 459 460 /** 461 * Gets if this window has accessibility focus. 462 * 463 * @return Whether has accessibility focus. 464 */ isAccessibilityFocused()465 public boolean isAccessibilityFocused() { 466 return getBooleanProperty(BOOLEAN_PROPERTY_ACCESSIBILITY_FOCUSED); 467 } 468 469 /** 470 * Sets if this window has accessibility focus. 471 * 472 * @param focused Whether has accessibility focus. 473 * 474 * @hide 475 */ setAccessibilityFocused(boolean focused)476 public void setAccessibilityFocused(boolean focused) { 477 setBooleanProperty(BOOLEAN_PROPERTY_ACCESSIBILITY_FOCUSED, focused); 478 } 479 480 /** 481 * Gets the number of child windows. 482 * 483 * @return The child count. 484 */ getChildCount()485 public int getChildCount() { 486 return (mChildIds != null) ? mChildIds.size() : 0; 487 } 488 489 /** 490 * Gets the child window at a given index. 491 * 492 * @param index The index. 493 * @return The child. 494 */ getChild(int index)495 public AccessibilityWindowInfo getChild(int index) { 496 if (mChildIds == null) { 497 throw new IndexOutOfBoundsException(); 498 } 499 if (mConnectionId == UNDEFINED_WINDOW_ID) { 500 return null; 501 } 502 final int childId = (int) mChildIds.get(index); 503 AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance(); 504 return client.getWindow(mConnectionId, childId); 505 } 506 507 /** 508 * Adds a child window. 509 * 510 * @param childId The child window id. 511 * 512 * @hide 513 */ addChild(int childId)514 public void addChild(int childId) { 515 if (mChildIds == null) { 516 mChildIds = new LongArray(); 517 } 518 mChildIds.add(childId); 519 } 520 521 /** 522 * Sets the display Id. 523 * 524 * @param displayId The display id. 525 * 526 * @hide 527 */ setDisplayId(int displayId)528 public void setDisplayId(int displayId) { 529 mDisplayId = displayId; 530 } 531 532 /** 533 * Returns the ID of the display this window is on, for use with 534 * {@link android.hardware.display.DisplayManager#getDisplay(int)}. 535 * 536 * @return The logical display id. 537 */ getDisplayId()538 public int getDisplayId() { 539 return mDisplayId; 540 } 541 542 543 /** 544 * Sets the timestamp of the transition. 545 * 546 * @param transitionTime The timestamp from {@link SystemClock#uptimeMillis()} at which the 547 * transition happens. 548 * 549 * @hide 550 */ 551 @UptimeMillisLong setTransitionTimeMillis(long transitionTime)552 public void setTransitionTimeMillis(long transitionTime) { 553 mTransitionTime = transitionTime; 554 } 555 556 /** 557 * Return the {@link SystemClock#uptimeMillis()} at which the last transition happens. 558 * A transition happens when {@link #getBoundsInScreen(Rect)} is changed. 559 * 560 * @return The transition timestamp. 561 */ 562 @UptimeMillisLong getTransitionTimeMillis()563 public long getTransitionTimeMillis() { 564 return mTransitionTime; 565 } 566 567 /** 568 * Sets the locales of the window. Locales are populated by the view root by default. 569 * 570 * @param locales The {@link android.os.LocaleList}. 571 * 572 * @hide 573 */ setLocales(@onNull LocaleList locales)574 public void setLocales(@NonNull LocaleList locales) { 575 mLocales = locales; 576 } 577 578 /** 579 * Return the {@link android.os.LocaleList} of the window. 580 * 581 * @return the locales of the window. 582 */ getLocales()583 public @NonNull LocaleList getLocales() { 584 return mLocales; 585 } 586 587 /** 588 * Returns a cached instance if such is available or a new one is 589 * created. 590 * 591 * <p>In most situations object pooling is not beneficial. Create a new instance using the 592 * constructor {@link #AccessibilityWindowInfo()} instead. 593 * 594 * @return An instance. 595 */ obtain()596 public static AccessibilityWindowInfo obtain() { 597 AccessibilityWindowInfo info = sPool.acquire(); 598 if (info == null) { 599 info = new AccessibilityWindowInfo(); 600 } 601 if (sNumInstancesInUse != null) { 602 sNumInstancesInUse.incrementAndGet(); 603 } 604 return info; 605 } 606 607 /** 608 * Returns a cached instance if such is available or a new one is 609 * created. The returned instance is initialized from the given 610 * <code>info</code>. 611 * 612 * <p>In most situations object pooling is not beneficial. Create a new instance using the 613 * constructor {@link #AccessibilityWindowInfo(AccessibilityWindowInfo)} instead. 614 * 615 * @param info The other info. 616 * @return An instance. 617 */ obtain(AccessibilityWindowInfo info)618 public static AccessibilityWindowInfo obtain(AccessibilityWindowInfo info) { 619 AccessibilityWindowInfo infoClone = obtain(); 620 infoClone.init(info); 621 return infoClone; 622 } 623 624 /** 625 * Specify a counter that will be incremented on obtain() and decremented on recycle() 626 * 627 * @hide 628 */ 629 @TestApi setNumInstancesInUseCounter(AtomicInteger counter)630 public static void setNumInstancesInUseCounter(AtomicInteger counter) { 631 if (sNumInstancesInUse != null) { 632 sNumInstancesInUse = counter; 633 } 634 } 635 636 /** 637 * Return an instance back to be reused. 638 * <p> 639 * <strong>Note:</strong> You must not touch the object after calling this function. 640 * </p> 641 * 642 * <p>In most situations object pooling is not beneficial, and recycling is not necessary. 643 * 644 * @throws IllegalStateException If the info is already recycled. 645 */ recycle()646 public void recycle() { 647 clear(); 648 sPool.release(this); 649 if (sNumInstancesInUse != null) { 650 sNumInstancesInUse.decrementAndGet(); 651 } 652 } 653 654 /** 655 * Refreshes this window with the latest state of the window it represents. 656 * <p> 657 * <strong>Note:</strong> If this method returns false this info is obsolete 658 * since it represents a window that is no longer exist. 659 * </p> 660 * 661 * @hide 662 */ refresh()663 public boolean refresh() { 664 if (mConnectionId == UNDEFINED_CONNECTION_ID || mId == UNDEFINED_WINDOW_ID) { 665 return false; 666 } 667 final AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance(); 668 final AccessibilityWindowInfo refreshedInfo = client.getWindow(mConnectionId, 669 mId, /* bypassCache */true); 670 if (refreshedInfo == null) { 671 return false; 672 } 673 init(refreshedInfo); 674 refreshedInfo.recycle(); 675 return true; 676 } 677 678 @Override describeContents()679 public int describeContents() { 680 return 0; 681 } 682 683 @Override writeToParcel(Parcel parcel, int flags)684 public void writeToParcel(Parcel parcel, int flags) { 685 parcel.writeInt(mDisplayId); 686 parcel.writeInt(mType); 687 parcel.writeInt(mLayer); 688 parcel.writeInt(mBooleanProperties); 689 parcel.writeInt(mId); 690 parcel.writeInt(mParentId); 691 parcel.writeInt(mTaskId); 692 mRegionInScreen.writeToParcel(parcel, flags); 693 parcel.writeCharSequence(mTitle); 694 parcel.writeLong(mAnchorId); 695 parcel.writeLong(mTransitionTime); 696 697 final LongArray childIds = mChildIds; 698 if (childIds == null) { 699 parcel.writeInt(0); 700 } else { 701 final int childCount = childIds.size(); 702 parcel.writeInt(childCount); 703 for (int i = 0; i < childCount; i++) { 704 parcel.writeInt((int) childIds.get(i)); 705 } 706 } 707 708 parcel.writeInt(mConnectionId); 709 parcel.writeParcelable(mLocales, flags); 710 } 711 712 /** 713 * Initializes this instance from another one. 714 * 715 * @param other The other instance. 716 */ init(AccessibilityWindowInfo other)717 private void init(AccessibilityWindowInfo other) { 718 mDisplayId = other.mDisplayId; 719 mType = other.mType; 720 mLayer = other.mLayer; 721 mBooleanProperties = other.mBooleanProperties; 722 mId = other.mId; 723 mParentId = other.mParentId; 724 mTaskId = other.mTaskId; 725 mRegionInScreen.set(other.mRegionInScreen); 726 mTitle = other.mTitle; 727 mAnchorId = other.mAnchorId; 728 mTransitionTime = other.mTransitionTime; 729 730 if (mChildIds != null) mChildIds.clear(); 731 if (other.mChildIds != null && other.mChildIds.size() > 0) { 732 if (mChildIds == null) { 733 mChildIds = other.mChildIds.clone(); 734 } else { 735 mChildIds.addAll(other.mChildIds); 736 } 737 } 738 739 mConnectionId = other.mConnectionId; 740 mLocales = other.mLocales; 741 } 742 initFromParcel(Parcel parcel)743 private void initFromParcel(Parcel parcel) { 744 mDisplayId = parcel.readInt(); 745 mType = parcel.readInt(); 746 mLayer = parcel.readInt(); 747 mBooleanProperties = parcel.readInt(); 748 mId = parcel.readInt(); 749 mParentId = parcel.readInt(); 750 mTaskId = parcel.readInt(); 751 mRegionInScreen = Region.CREATOR.createFromParcel(parcel); 752 mTitle = parcel.readCharSequence(); 753 mAnchorId = parcel.readLong(); 754 mTransitionTime = parcel.readLong(); 755 756 final int childCount = parcel.readInt(); 757 if (childCount > 0) { 758 if (mChildIds == null) { 759 mChildIds = new LongArray(childCount); 760 } 761 for (int i = 0; i < childCount; i++) { 762 final int childId = parcel.readInt(); 763 mChildIds.add(childId); 764 } 765 } 766 767 mConnectionId = parcel.readInt(); 768 mLocales = parcel.readParcelable(null, LocaleList.class); 769 } 770 771 @Override hashCode()772 public int hashCode() { 773 return mId; 774 } 775 776 @Override equals(@ullable Object obj)777 public boolean equals(@Nullable Object obj) { 778 if (this == obj) { 779 return true; 780 } 781 if (obj == null) { 782 return false; 783 } 784 if (getClass() != obj.getClass()) { 785 return false; 786 } 787 AccessibilityWindowInfo other = (AccessibilityWindowInfo) obj; 788 return (mId == other.mId); 789 } 790 791 @Override toString()792 public String toString() { 793 StringBuilder builder = new StringBuilder(); 794 builder.append("AccessibilityWindowInfo["); 795 builder.append("title=").append(mTitle); 796 builder.append(", displayId=").append(mDisplayId); 797 builder.append(", id=").append(mId); 798 builder.append(", taskId=").append(mTaskId); 799 builder.append(", type=").append(typeToString(mType)); 800 builder.append(", layer=").append(mLayer); 801 builder.append(", region=").append(mRegionInScreen); 802 builder.append(", bounds=").append(mRegionInScreen.getBounds()); 803 builder.append(", focused=").append(isFocused()); 804 builder.append(", active=").append(isActive()); 805 builder.append(", pictureInPicture=").append(isInPictureInPictureMode()); 806 builder.append(", transitionTime=").append(mTransitionTime); 807 if (DEBUG) { 808 builder.append(", parent=").append(mParentId); 809 builder.append(", children=["); 810 if (mChildIds != null) { 811 final int childCount = mChildIds.size(); 812 for (int i = 0; i < childCount; i++) { 813 builder.append(mChildIds.get(i)); 814 if (i < childCount - 1) { 815 builder.append(','); 816 } 817 } 818 } else { 819 builder.append("null"); 820 } 821 builder.append(']'); 822 } else { 823 builder.append(", hasParent=").append(mParentId != UNDEFINED_WINDOW_ID); 824 builder.append(", isAnchored=") 825 .append(mAnchorId != AccessibilityNodeInfo.UNDEFINED_NODE_ID); 826 builder.append(", hasChildren=").append(mChildIds != null 827 && mChildIds.size() > 0); 828 } 829 builder.append(']'); 830 return builder.toString(); 831 } 832 833 /** 834 * Clears the internal state. 835 */ clear()836 private void clear() { 837 mDisplayId = Display.INVALID_DISPLAY; 838 mType = UNDEFINED_WINDOW_ID; 839 mLayer = UNDEFINED_WINDOW_ID; 840 mBooleanProperties = 0; 841 mId = UNDEFINED_WINDOW_ID; 842 mParentId = UNDEFINED_WINDOW_ID; 843 mTaskId = ActivityTaskManager.INVALID_TASK_ID; 844 mRegionInScreen.setEmpty(); 845 mChildIds = null; 846 mConnectionId = UNDEFINED_WINDOW_ID; 847 mAnchorId = AccessibilityNodeInfo.UNDEFINED_NODE_ID; 848 mTitle = null; 849 mTransitionTime = 0; 850 mLocales = LocaleList.getEmptyLocaleList(); 851 } 852 853 /** 854 * Gets the value of a boolean property. 855 * 856 * @param property The property. 857 * @return The value. 858 */ getBooleanProperty(int property)859 private boolean getBooleanProperty(int property) { 860 return (mBooleanProperties & property) != 0; 861 } 862 863 /** 864 * Sets a boolean property. 865 * 866 * @param property The property. 867 * @param value The value. 868 * 869 * @throws IllegalStateException If called from an AccessibilityService. 870 */ setBooleanProperty(int property, boolean value)871 private void setBooleanProperty(int property, boolean value) { 872 if (value) { 873 mBooleanProperties |= property; 874 } else { 875 mBooleanProperties &= ~property; 876 } 877 } 878 879 /** 880 * @hide 881 */ typeToString(int type)882 public static String typeToString(int type) { 883 if (Flags.addTypeWindowControl() && type == TYPE_WINDOW_CONTROL) { 884 return "TYPE_WINDOW_CONTROL"; 885 } 886 887 switch (type) { 888 case TYPE_APPLICATION: { 889 return "TYPE_APPLICATION"; 890 } 891 case TYPE_INPUT_METHOD: { 892 return "TYPE_INPUT_METHOD"; 893 } 894 case TYPE_SYSTEM: { 895 return "TYPE_SYSTEM"; 896 } 897 case TYPE_ACCESSIBILITY_OVERLAY: { 898 return "TYPE_ACCESSIBILITY_OVERLAY"; 899 } 900 case TYPE_SPLIT_SCREEN_DIVIDER: { 901 return "TYPE_SPLIT_SCREEN_DIVIDER"; 902 } 903 case TYPE_MAGNIFICATION_OVERLAY: { 904 return "TYPE_MAGNIFICATION_OVERLAY"; 905 } 906 default: 907 return "<UNKNOWN:" + type + ">"; 908 } 909 } 910 911 /** 912 * Reports how this window differs from a possibly different state of the same window. The 913 * argument must have the same id and type as neither of those properties may change. 914 * 915 * @param other The new state. 916 * @return A set of flags showing how the window has changes, or 0 if the two states are the 917 * same. 918 * 919 * @hide 920 */ 921 @WindowsChangeTypes differenceFrom(AccessibilityWindowInfo other)922 public int differenceFrom(AccessibilityWindowInfo other) { 923 if (other.mId != mId) { 924 throw new IllegalArgumentException("Not same window."); 925 } 926 if (other.mType != mType) { 927 throw new IllegalArgumentException("Not same type."); 928 } 929 int changes = 0; 930 if (!TextUtils.equals(mTitle, other.mTitle)) { 931 changes |= AccessibilityEvent.WINDOWS_CHANGE_TITLE; 932 } 933 if (!mRegionInScreen.equals(other.mRegionInScreen)) { 934 changes |= AccessibilityEvent.WINDOWS_CHANGE_BOUNDS; 935 } 936 if (mLayer != other.mLayer) { 937 changes |= AccessibilityEvent.WINDOWS_CHANGE_LAYER; 938 } 939 if (getBooleanProperty(BOOLEAN_PROPERTY_ACTIVE) 940 != other.getBooleanProperty(BOOLEAN_PROPERTY_ACTIVE)) { 941 changes |= AccessibilityEvent.WINDOWS_CHANGE_ACTIVE; 942 } 943 if (getBooleanProperty(BOOLEAN_PROPERTY_FOCUSED) 944 != other.getBooleanProperty(BOOLEAN_PROPERTY_FOCUSED)) { 945 changes |= AccessibilityEvent.WINDOWS_CHANGE_FOCUSED; 946 } 947 if (getBooleanProperty(BOOLEAN_PROPERTY_ACCESSIBILITY_FOCUSED) 948 != other.getBooleanProperty(BOOLEAN_PROPERTY_ACCESSIBILITY_FOCUSED)) { 949 changes |= AccessibilityEvent.WINDOWS_CHANGE_ACCESSIBILITY_FOCUSED; 950 } 951 if (getBooleanProperty(BOOLEAN_PROPERTY_PICTURE_IN_PICTURE) 952 != other.getBooleanProperty(BOOLEAN_PROPERTY_PICTURE_IN_PICTURE)) { 953 changes |= AccessibilityEvent.WINDOWS_CHANGE_PIP; 954 } 955 if (mParentId != other.mParentId) { 956 changes |= AccessibilityEvent.WINDOWS_CHANGE_PARENT; 957 } 958 if (!Objects.equals(mChildIds, other.mChildIds)) { 959 changes |= AccessibilityEvent.WINDOWS_CHANGE_CHILDREN; 960 } 961 //TODO(b/1338122): Add DISPLAY_CHANGED type for multi-display 962 return changes; 963 } 964 965 public static final @android.annotation.NonNull Parcelable.Creator<AccessibilityWindowInfo> CREATOR = 966 new Creator<AccessibilityWindowInfo>() { 967 @Override 968 public AccessibilityWindowInfo createFromParcel(Parcel parcel) { 969 AccessibilityWindowInfo info = obtain(); 970 info.initFromParcel(parcel); 971 return info; 972 } 973 974 @Override 975 public AccessibilityWindowInfo[] newArray(int size) { 976 return new AccessibilityWindowInfo[size]; 977 } 978 }; 979 980 /** 981 * Transfers a sparsearray with lists having {@link AccessibilityWindowInfo}s across an IPC. 982 * The key of this sparsearray is display Id. 983 * 984 * @hide 985 */ 986 public static final class WindowListSparseArray 987 extends SparseArray<List<AccessibilityWindowInfo>> implements Parcelable { 988 989 @Override describeContents()990 public int describeContents() { 991 return 0; 992 } 993 994 @Override writeToParcel(Parcel dest, int flags)995 public void writeToParcel(Parcel dest, int flags) { 996 final int count = size(); 997 dest.writeInt(count); 998 for (int i = 0; i < count; i++) { 999 dest.writeParcelableList(valueAt(i), 0); 1000 dest.writeInt(keyAt(i)); 1001 } 1002 } 1003 1004 public static final Parcelable.Creator<WindowListSparseArray> CREATOR = 1005 new Parcelable.Creator<WindowListSparseArray>() { 1006 public WindowListSparseArray createFromParcel( 1007 Parcel source) { 1008 final WindowListSparseArray array = new WindowListSparseArray(); 1009 final ClassLoader loader = array.getClass().getClassLoader(); 1010 final int count = source.readInt(); 1011 for (int i = 0; i < count; i++) { 1012 List<AccessibilityWindowInfo> windows = new ArrayList<>(); 1013 source.readParcelableList(windows, loader, android.view.accessibility.AccessibilityWindowInfo.class); 1014 array.put(source.readInt(), windows); 1015 } 1016 return array; 1017 } 1018 1019 public WindowListSparseArray[] newArray(int size) { 1020 return new WindowListSparseArray[size]; 1021 } 1022 }; 1023 } 1024 } 1025