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