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