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