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