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