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