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