Home | History | Annotate | Download | only in accessibility
      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 
    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
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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 */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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
    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      */
    494     public void recycle() {
    495         clear();
    496         sPool.release(this);
    497         if (sNumInstancesInUse != null) {
    498             sNumInstancesInUse.decrementAndGet();
    499         }
    500     }
    501 
    502     @Override
    503     public int describeContents() {
    504         return 0;
    505     }
    506 
    507     @Override
    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 
    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
    557     public int hashCode() {
    558         return mId;
    559     }
    560 
    561     @Override
    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
    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      */
    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      */
    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      */
    650     private void setBooleanProperty(int property, boolean value) {
    651         if (value) {
    652             mBooleanProperties |= property;
    653         } else {
    654             mBooleanProperties &= ~property;
    655         }
    656     }
    657 
    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      */
    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
    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