Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 6254f480 authored by Svetoslav's avatar Svetoslav
Browse files

Optimizing AccessibilityNodeInfo caching.

1. Before we were firing an accessibility event from the common
   predecessor of views with accessibility related state changes
   every X amount of time. These events designate that the tree
   rooted at the source is invalid and should not be cached.
   However, some of the state changes do not affect the view tree
   structure and we can just refresh the node instead of evicting
   and recaching nodes infos for views that did not change. Hence,
   we need a way to distinguish between a subtree changed over a
   node changed.

   Adding a new event type will not work since if say two siblings
   have local changes and their predecessor fires a window state
   change event, the client will drop the subtree rooted at the
   parent including the two views with changes. Subsequent, more
   specialized events emitted from the two changed siblings will
   be useless since the parent which did not changed is already
   evicted from the cache. Conversely, if the specialized events
   are fired from the two siblings with local changes and they
   are refreshed in the cache the subsequent window state change
   event from the common predecessor will force the refreshed
   nodes to be evicted.

   Hence, to enable distinction between node being changed and
   a subtree baing changed while not changing existing behavior,
   we will fire only window content change event with an additional
   argument specifying what changed - node or a subtree for now.
   Also if the changes are local to a view we fire the window
   content changed event from the view. So, the two siblings will
   fire such an event independently and the client will know that
   these are local changes and can just refresh the node. If the
   changes are structural, then we fire the window state change
   event from the common predecessor.

2. Added the input type of a text view as one of the properties
   reported by an AccessibilityNodeInfo. It is nice to prompt the
   user what input is expected.

3. Added a bundle for optional information to AccessiiblityNodeInfo.
   For example, it will be used for putting web specific properties
   that do not map cleanly to Android specific ones in WebView.

4. AccessibilityInteractionController was not taking into account
   whether the current accessibility focused node is shown before
   returing it. Hence, a disconnected node would be returned and
   caching it puts our cahche in an inconsistent state.

Change-Id: I8ed19cfb4a70bdd7597c3f105487f1651cffd9e0
parent 85de5f42
Loading
Loading
Loading
Loading
+7 −0
Original line number Original line Diff line number Diff line
@@ -27279,6 +27279,7 @@ package android.view.accessibility {
    method public int describeContents();
    method public int describeContents();
    method public static java.lang.String eventTypeToString(int);
    method public static java.lang.String eventTypeToString(int);
    method public int getAction();
    method public int getAction();
    method public int getContentChangeType();
    method public long getEventTime();
    method public long getEventTime();
    method public int getEventType();
    method public int getEventType();
    method public int getMovementGranularity();
    method public int getMovementGranularity();
@@ -27290,11 +27291,14 @@ package android.view.accessibility {
    method public static android.view.accessibility.AccessibilityEvent obtain(android.view.accessibility.AccessibilityEvent);
    method public static android.view.accessibility.AccessibilityEvent obtain(android.view.accessibility.AccessibilityEvent);
    method public static android.view.accessibility.AccessibilityEvent obtain();
    method public static android.view.accessibility.AccessibilityEvent obtain();
    method public void setAction(int);
    method public void setAction(int);
    method public void setContentChangeType(int);
    method public void setEventTime(long);
    method public void setEventTime(long);
    method public void setEventType(int);
    method public void setEventType(int);
    method public void setMovementGranularity(int);
    method public void setMovementGranularity(int);
    method public void setPackageName(java.lang.CharSequence);
    method public void setPackageName(java.lang.CharSequence);
    method public void writeToParcel(android.os.Parcel, int);
    method public void writeToParcel(android.os.Parcel, int);
    field public static final int CONTENT_CHANGE_TYPE_NODE = 1; // 0x1
    field public static final int CONTENT_CHANGE_TYPE_SUBTREE = 0; // 0x0
    field public static final android.os.Parcelable.Creator CREATOR;
    field public static final android.os.Parcelable.Creator CREATOR;
    field public static final int INVALID_POSITION = -1; // 0xffffffff
    field public static final int INVALID_POSITION = -1; // 0xffffffff
    field public static final deprecated int MAX_TEXT_LENGTH = 500; // 0x1f4
    field public static final deprecated int MAX_TEXT_LENGTH = 500; // 0x1f4
@@ -27356,10 +27360,12 @@ package android.view.accessibility {
    method public int getActions();
    method public int getActions();
    method public void getBoundsInParent(android.graphics.Rect);
    method public void getBoundsInParent(android.graphics.Rect);
    method public void getBoundsInScreen(android.graphics.Rect);
    method public void getBoundsInScreen(android.graphics.Rect);
    method public android.os.Bundle getBundle();
    method public android.view.accessibility.AccessibilityNodeInfo getChild(int);
    method public android.view.accessibility.AccessibilityNodeInfo getChild(int);
    method public int getChildCount();
    method public int getChildCount();
    method public java.lang.CharSequence getClassName();
    method public java.lang.CharSequence getClassName();
    method public java.lang.CharSequence getContentDescription();
    method public java.lang.CharSequence getContentDescription();
    method public int getInputType();
    method public android.view.accessibility.AccessibilityNodeInfo getLabelFor();
    method public android.view.accessibility.AccessibilityNodeInfo getLabelFor();
    method public android.view.accessibility.AccessibilityNodeInfo getLabeledBy();
    method public android.view.accessibility.AccessibilityNodeInfo getLabeledBy();
    method public int getMovementGranularities();
    method public int getMovementGranularities();
@@ -27403,6 +27409,7 @@ package android.view.accessibility {
    method public void setEnabled(boolean);
    method public void setEnabled(boolean);
    method public void setFocusable(boolean);
    method public void setFocusable(boolean);
    method public void setFocused(boolean);
    method public void setFocused(boolean);
    method public void setInputType(int);
    method public void setLabelFor(android.view.View);
    method public void setLabelFor(android.view.View);
    method public void setLabelFor(android.view.View, int);
    method public void setLabelFor(android.view.View, int);
    method public void setLabeledBy(android.view.View);
    method public void setLabeledBy(android.view.View);
+4 −0
Original line number Original line Diff line number Diff line
@@ -406,6 +406,10 @@ final class AccessibilityInteractionController {
                        if (host == null || !ViewRootImpl.isViewDescendantOf(host, root)) {
                        if (host == null || !ViewRootImpl.isViewDescendantOf(host, root)) {
                            break;
                            break;
                        }
                        }
                        // The focused view not shown, we failed.
                        if (!isShown(host)) {
                            break;
                        }
                        // If the host has a provider ask this provider to search for the
                        // If the host has a provider ask this provider to search for the
                        // focus instead fetching all provider nodes to do the search here.
                        // focus instead fetching all provider nodes to do the search here.
                        AccessibilityNodeProvider provider = host.getAccessibilityNodeProvider();
                        AccessibilityNodeProvider provider = host.getAccessibilityNodeProvider();
+110 −54
Original line number Original line Diff line number Diff line
@@ -1564,6 +1564,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
    private int mAccessibilityCursorPosition = ACCESSIBILITY_CURSOR_POSITION_UNDEFINED;
    private int mAccessibilityCursorPosition = ACCESSIBILITY_CURSOR_POSITION_UNDEFINED;
    SendViewStateChangedAccessibilityEvent mSendViewStateChangedAccessibilityEvent;
    /**
    /**
     * The view's tag.
     * The view's tag.
     * {@hide}
     * {@hide}
@@ -2136,13 +2138,12 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
    /**
    /**
     * Flag indicating whether a view has accessibility focus.
     * Flag indicating whether a view has accessibility focus.
     */
     */
    static final int PFLAG2_ACCESSIBILITY_FOCUSED = 0x00000040 << PFLAG2_IMPORTANT_FOR_ACCESSIBILITY_SHIFT;
    static final int PFLAG2_ACCESSIBILITY_FOCUSED = 0x04000000;
    /**
    /**
     * Flag indicating whether a view state for accessibility has changed.
     * Flag whether the accessibility state of the subtree rooted at this view changed.
     */
     */
    static final int PFLAG2_ACCESSIBILITY_STATE_CHANGED = 0x00000080
    static final int PFLAG2_SUBTREE_ACCESSIBILITY_STATE_CHANGED = 0x08000000;
            << PFLAG2_IMPORTANT_FOR_ACCESSIBILITY_SHIFT;
    /**
    /**
     * Flag indicating whether a view failed the quickReject() check in draw(). This condition
     * Flag indicating whether a view failed the quickReject() check in draw(). This condition
@@ -4454,10 +4455,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
            onFocusChanged(true, direction, previouslyFocusedRect);
            onFocusChanged(true, direction, previouslyFocusedRect);
            refreshDrawableState();
            refreshDrawableState();
            if (AccessibilityManager.getInstance(mContext).isEnabled()) {
                notifyAccessibilityStateChanged();
            }
        }
        }
    }
    }
@@ -4561,10 +4558,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
            if (!rootViewRequestFocus()) {
            if (!rootViewRequestFocus()) {
                notifyGlobalFocusCleared(this);
                notifyGlobalFocusCleared(this);
            }
            }
            if (AccessibilityManager.getInstance(mContext).isEnabled()) {
                notifyAccessibilityStateChanged();
            }
        }
        }
    }
    }
@@ -4596,10 +4589,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
            onFocusChanged(false, 0, null);
            onFocusChanged(false, 0, null);
            refreshDrawableState();
            refreshDrawableState();
            if (AccessibilityManager.getInstance(mContext).isEnabled()) {
                notifyAccessibilityStateChanged();
            }
        }
        }
    }
    }
@@ -4650,9 +4639,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
     */
     */
    protected void onFocusChanged(boolean gainFocus, int direction, Rect previouslyFocusedRect) {
    protected void onFocusChanged(boolean gainFocus, int direction, Rect previouslyFocusedRect) {
        if (gainFocus) {
        if (gainFocus) {
            if (AccessibilityManager.getInstance(mContext).isEnabled()) {
            sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED);
            sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED);
            }
        } else {
            notifyViewAccessibilityStateChangedIfNeeded();
        }
        }
        InputMethodManager imm = InputMethodManager.peekInstance();
        InputMethodManager imm = InputMethodManager.peekInstance();
@@ -4709,6 +4698,10 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
     * @see AccessibilityDelegate
     * @see AccessibilityDelegate
     */
     */
    public void sendAccessibilityEvent(int eventType) {
    public void sendAccessibilityEvent(int eventType) {
        // Excluded views do not send accessibility events.
        if (!includeForAccessibility()) {
            return;
        }
        if (mAccessibilityDelegate != null) {
        if (mAccessibilityDelegate != null) {
            mAccessibilityDelegate.sendAccessibilityEvent(this, eventType);
            mAccessibilityDelegate.sendAccessibilityEvent(this, eventType);
        } else {
        } else {
@@ -5368,8 +5361,10 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
        final boolean nonEmptyDesc = contentDescription != null && contentDescription.length() > 0;
        final boolean nonEmptyDesc = contentDescription != null && contentDescription.length() > 0;
        if (nonEmptyDesc && getImportantForAccessibility() == IMPORTANT_FOR_ACCESSIBILITY_AUTO) {
        if (nonEmptyDesc && getImportantForAccessibility() == IMPORTANT_FOR_ACCESSIBILITY_AUTO) {
            setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES);
            setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES);
            notifySubtreeAccessibilityStateChangedIfNeeded();
        } else {
            notifyViewAccessibilityStateChangedIfNeeded();
        }
        }
        notifyAccessibilityStateChanged();
    }
    }
    /**
    /**
@@ -6649,7 +6644,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
            }
            }
            invalidate();
            invalidate();
            sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED);
            sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED);
            notifyAccessibilityStateChanged();
            return true;
            return true;
        }
        }
        return false;
        return false;
@@ -6712,7 +6706,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
            mPrivateFlags2 &= ~PFLAG2_ACCESSIBILITY_FOCUSED;
            mPrivateFlags2 &= ~PFLAG2_ACCESSIBILITY_FOCUSED;
            invalidate();
            invalidate();
            sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED);
            sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED);
            notifyAccessibilityStateChanged();
        }
        }
    }
    }
@@ -6886,11 +6879,16 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
     * @see #IMPORTANT_FOR_ACCESSIBILITY_AUTO
     * @see #IMPORTANT_FOR_ACCESSIBILITY_AUTO
     */
     */
    public void setImportantForAccessibility(int mode) {
    public void setImportantForAccessibility(int mode) {
        final boolean oldIncludeForAccessibility = includeForAccessibility();
        if (mode != getImportantForAccessibility()) {
        if (mode != getImportantForAccessibility()) {
            mPrivateFlags2 &= ~PFLAG2_IMPORTANT_FOR_ACCESSIBILITY_MASK;
            mPrivateFlags2 &= ~PFLAG2_IMPORTANT_FOR_ACCESSIBILITY_MASK;
            mPrivateFlags2 |= (mode << PFLAG2_IMPORTANT_FOR_ACCESSIBILITY_SHIFT)
            mPrivateFlags2 |= (mode << PFLAG2_IMPORTANT_FOR_ACCESSIBILITY_SHIFT)
                    & PFLAG2_IMPORTANT_FOR_ACCESSIBILITY_MASK;
                    & PFLAG2_IMPORTANT_FOR_ACCESSIBILITY_MASK;
            notifyAccessibilityStateChanged();
            if (oldIncludeForAccessibility != includeForAccessibility()) {
                notifySubtreeAccessibilityStateChangedIfNeeded();
            } else {
                notifyViewAccessibilityStateChangedIfNeeded();
            }
        }
        }
    }
    }
@@ -6997,25 +6995,44 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
    }
    }
    /**
    /**
     * Notifies accessibility services that some view's important for
     * Notifies that the accessibility state of this view changed. The change
     * accessibility state has changed. Note that such notifications
     * is local to this view and does not represent structural changes such
     * are made at most once every
     * as children and parent. For example, the view became focusable. The
     * notification is at at most once every
     * {@link ViewConfiguration#getSendRecurringAccessibilityEventsInterval()}
     * {@link ViewConfiguration#getSendRecurringAccessibilityEventsInterval()}
     * to avoid unnecessary load to the system. Also once a view has
     * to avoid unnecessary load to the system. Also once a view has a pending
     * made a notifucation this method is a NOP until the notification has
     * notifucation this method is a NOP until the notification has been sent.
     * been sent to clients.
     *
     *
     * @hide
     * @hide
     */
    public void notifyViewAccessibilityStateChangedIfNeeded() {
        if (!AccessibilityManager.getInstance(mContext).isEnabled()) {
            return;
        }
        if (mSendViewStateChangedAccessibilityEvent == null) {
            mSendViewStateChangedAccessibilityEvent =
                    new SendViewStateChangedAccessibilityEvent();
        }
        mSendViewStateChangedAccessibilityEvent.runOrPost();
    }
    /**
     * Notifies that the accessibility state of this view changed. The change
     * is *not* local to this view and does represent structural changes such
     * as children and parent. For example, the view size changed. The
     * notification is at at most once every
     * {@link ViewConfiguration#getSendRecurringAccessibilityEventsInterval()}
     * to avoid unnecessary load to the system. Also once a view has a pending
     * notifucation this method is a NOP until the notification has been sent.
     *
     *
     * TODO: Makse sure this method is called for any view state change
     * @hide
     *       that is interesting for accessilility purposes.
     */
     */
    public void notifyAccessibilityStateChanged() {
    private void notifySubtreeAccessibilityStateChangedIfNeeded() {
        if (!AccessibilityManager.getInstance(mContext).isEnabled()) {
        if (!AccessibilityManager.getInstance(mContext).isEnabled()) {
            return;
            return;
        }
        }
        if ((mPrivateFlags2 & PFLAG2_ACCESSIBILITY_STATE_CHANGED) == 0) {
        if ((mPrivateFlags2 & PFLAG2_SUBTREE_ACCESSIBILITY_STATE_CHANGED) == 0) {
            mPrivateFlags2 |= PFLAG2_ACCESSIBILITY_STATE_CHANGED;
            mPrivateFlags2 |= PFLAG2_SUBTREE_ACCESSIBILITY_STATE_CHANGED;
            if (mParent != null) {
            if (mParent != null) {
                mParent.childAccessibilityStateChanged(this);
                mParent.childAccessibilityStateChanged(this);
            }
            }
@@ -7023,13 +7040,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
    }
    }
    /**
    /**
     * Reset the state indicating the this view has requested clients
     * Reset the flag indicating the accessibility state of the subtree rooted
     * interested in its accessibility state to be notified.
     * at this view changed.
     *
     * @hide
     */
     */
    public void resetAccessibilityStateChanged() {
    void resetSubtreeAccessibilityStateChanged() {
        mPrivateFlags2 &= ~PFLAG2_ACCESSIBILITY_STATE_CHANGED;
        mPrivateFlags2 &= ~PFLAG2_SUBTREE_ACCESSIBILITY_STATE_CHANGED;
    }
    }
    /**
    /**
@@ -7142,7 +7157,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
                        || getAccessibilitySelectionEnd() != end)
                        || getAccessibilitySelectionEnd() != end)
                        && (start == end)) {
                        && (start == end)) {
                    setAccessibilitySelection(start, end);
                    setAccessibilitySelection(start, end);
                    notifyAccessibilityStateChanged();
                    notifyViewAccessibilityStateChangedIfNeeded();
                    return true;
                    return true;
                }
                }
            } break;
            } break;
@@ -8563,6 +8578,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
     * @param mask Constant indicating the bit range that should be changed
     * @param mask Constant indicating the bit range that should be changed
     */
     */
    void setFlags(int flags, int mask) {
    void setFlags(int flags, int mask) {
        final boolean accessibilityEnabled =
                AccessibilityManager.getInstance(mContext).isEnabled();
        final boolean oldIncludeForAccessibility = accessibilityEnabled
                ? includeForAccessibility() : false;
        int old = mViewFlags;
        int old = mViewFlags;
        mViewFlags = (mViewFlags & ~mask) | (flags & mask);
        mViewFlags = (mViewFlags & ~mask) | (flags & mask);
@@ -8587,9 +8607,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
                 */
                 */
                if (mParent != null) mParent.focusableViewAvailable(this);
                if (mParent != null) mParent.focusableViewAvailable(this);
            }
            }
            if (AccessibilityManager.getInstance(mContext).isEnabled()) {
                notifyAccessibilityStateChanged();
            }
        }
        }
        final int newVisibility = flags & VISIBILITY_MASK;
        final int newVisibility = flags & VISIBILITY_MASK;
@@ -8710,10 +8727,18 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
            }
            }
        }
        }
        if (AccessibilityManager.getInstance(mContext).isEnabled()
        if (accessibilityEnabled) {
                && ((changed & FOCUSABLE) != 0 || (changed & CLICKABLE) != 0
            if ((changed & FOCUSABLE_MASK) != 0 || (changed & VISIBILITY_MASK) != 0
                        || (changed & LONG_CLICKABLE) != 0 || (changed & ENABLED) != 0)) {
                    || (changed & CLICKABLE) != 0 || (changed & LONG_CLICKABLE) != 0) {
            notifyAccessibilityStateChanged();
                if (oldIncludeForAccessibility != includeForAccessibility()) {
                    notifySubtreeAccessibilityStateChangedIfNeeded();
                } else {
                    notifyViewAccessibilityStateChangedIfNeeded();
                }
            }
            if ((changed & ENABLED_MASK) != 0) {
                notifyViewAccessibilityStateChangedIfNeeded();
            }
        }
        }
    }
    }
@@ -11760,6 +11785,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
        jumpDrawablesToCurrentState();
        jumpDrawablesToCurrentState();
        clearAccessibilityFocus();
        clearAccessibilityFocus();
        resetSubtreeAccessibilityStateChanged();
        if (isFocused()) {
        if (isFocused()) {
            InputMethodManager imm = InputMethodManager.peekInstance();
            InputMethodManager imm = InputMethodManager.peekInstance();
            imm.focusIn(this);
            imm.focusIn(this);
@@ -12055,8 +12082,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
        mCurrentAnimation = null;
        mCurrentAnimation = null;
        mCurrentScene = null;
        mCurrentScene = null;
        resetAccessibilityStateChanged();
    }
    }
    private void cleanupDraw() {
    private void cleanupDraw() {
@@ -14444,6 +14469,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
            mPrivateFlags |= drawn;
            mPrivateFlags |= drawn;
            mBackgroundSizeChanged = true;
            mBackgroundSizeChanged = true;
            notifySubtreeAccessibilityStateChangedIfNeeded();
        }
        }
        return changed;
        return changed;
    }
    }
@@ -15208,9 +15235,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
            invalidate(true);
            invalidate(true);
            refreshDrawableState();
            refreshDrawableState();
            dispatchSetSelected(selected);
            dispatchSetSelected(selected);
            if (AccessibilityManager.getInstance(mContext).isEnabled()) {
            notifyViewAccessibilityStateChangedIfNeeded();
                notifyAccessibilityStateChanged();
            }
        }
        }
    }
    }
@@ -18825,6 +18850,37 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
        }
        }
    }
    }
    private class SendViewStateChangedAccessibilityEvent implements Runnable {
        private boolean mPosted;
        private long mLastEventTimeMillis;
        public void run() {
            mPosted = false;
            mLastEventTimeMillis = SystemClock.uptimeMillis();
            if (AccessibilityManager.getInstance(mContext).isEnabled()) {
                AccessibilityEvent event = AccessibilityEvent.obtain();
                event.setEventType(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
                event.setContentChangeType(AccessibilityEvent.CONTENT_CHANGE_TYPE_NODE);
                sendAccessibilityEventUnchecked(event);
            }
        }
        public void runOrPost() {
            if (mPosted) {
                return;
            }
            final long timeSinceLastMillis = SystemClock.uptimeMillis() - mLastEventTimeMillis;
            final long minEventIntevalMillis =
                    ViewConfiguration.getSendRecurringAccessibilityEventsInterval();
            if (timeSinceLastMillis >= minEventIntevalMillis) {
                run();
            } else {
                postDelayed(this, minEventIntevalMillis - timeSinceLastMillis);
                mPosted = true;
            }
        }
    }
    /**
    /**
     * Dump all private flags in readable format, useful for documentation and
     * Dump all private flags in readable format, useful for documentation and
     * sanity checking.
     * sanity checking.
+21 −17
Original line number Original line Diff line number Diff line
@@ -1697,16 +1697,6 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
        }
        }
    }
    }


    /**
     * @hide
     */
    @Override
    public void childAccessibilityStateChanged(View child) {
        if (mParent != null) {
            mParent.childAccessibilityStateChanged(child);
        }
    }

    /**
    /**
     * Implement this method to intercept hover events before they are handled
     * Implement this method to intercept hover events before they are handled
     * by child views.
     * by child views.
@@ -2534,13 +2524,19 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
     * @hide
     * @hide
     */
     */
    @Override
    @Override
    public void resetAccessibilityStateChanged() {
    public void childAccessibilityStateChanged(View root) {
        super.resetAccessibilityStateChanged();
        if (mParent != null) {
            mParent.childAccessibilityStateChanged(root);
        }
    }

    @Override
    void resetSubtreeAccessibilityStateChanged() {
        super.resetSubtreeAccessibilityStateChanged();
        View[] children = mChildren;
        View[] children = mChildren;
        final int childCount = mChildrenCount;
        final int childCount = mChildrenCount;
        for (int i = 0; i < childCount; i++) {
        for (int i = 0; i < childCount; i++) {
            View child = children[i];
            children[i].resetSubtreeAccessibilityStateChanged();
            child.resetAccessibilityStateChanged();
        }
        }
    }
    }


@@ -3459,7 +3455,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
    }
    }


    private void clearCachedLayoutMode() {
    private void clearCachedLayoutMode() {
        if (!getBooleanFlag(FLAG_LAYOUT_MODE_WAS_EXPLICITLY_SET)) {
        if (!hasBooleanFlag(FLAG_LAYOUT_MODE_WAS_EXPLICITLY_SET)) {
           mLayoutMode = LAYOUT_MODE_UNDEFINED;
           mLayoutMode = LAYOUT_MODE_UNDEFINED;
        }
        }
    }
    }
@@ -3590,6 +3586,10 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
        if (child.hasTransientState()) {
        if (child.hasTransientState()) {
            childHasTransientStateChanged(child, true);
            childHasTransientStateChanged(child, true);
        }
        }

        if (child.isImportantForAccessibility() && child.getVisibility() != View.GONE) {
            childAccessibilityStateChanged(child);
        }
    }
    }


    private void addInArray(View child, int index) {
    private void addInArray(View child, int index) {
@@ -3829,6 +3829,10 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
        }
        }


        onViewRemoved(view);
        onViewRemoved(view);

        if (view.isImportantForAccessibility() && view.getVisibility() != View.GONE) {
            childAccessibilityStateChanged(view);
        }
    }
    }


    /**
    /**
@@ -4779,7 +4783,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
        setBooleanFlag(FLAG_USE_CHILD_DRAWING_ORDER, enabled);
        setBooleanFlag(FLAG_USE_CHILD_DRAWING_ORDER, enabled);
    }
    }


    private boolean getBooleanFlag(int flag) {
    private boolean hasBooleanFlag(int flag) {
        return (mGroupFlags & flag) == flag;
        return (mGroupFlags & flag) == flag;
    }
    }


@@ -4847,7 +4851,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
    void invalidateInheritedLayoutMode(int layoutModeOfRoot) {
    void invalidateInheritedLayoutMode(int layoutModeOfRoot) {
        if (mLayoutMode == LAYOUT_MODE_UNDEFINED ||
        if (mLayoutMode == LAYOUT_MODE_UNDEFINED ||
            mLayoutMode == layoutModeOfRoot ||
            mLayoutMode == layoutModeOfRoot ||
            getBooleanFlag(FLAG_LAYOUT_MODE_WAS_EXPLICITLY_SET)) {
            hasBooleanFlag(FLAG_LAYOUT_MODE_WAS_EXPLICITLY_SET)) {
            return;
            return;
        }
        }
        setLayoutMode(LAYOUT_MODE_UNDEFINED, false);
        setLayoutMode(LAYOUT_MODE_UNDEFINED, false);
+5 −4
Original line number Original line Diff line number Diff line
@@ -292,13 +292,14 @@ public interface ViewParent {
    public ViewParent getParentForAccessibility();
    public ViewParent getParentForAccessibility();


    /**
    /**
     * A child notifies its parent that its state for accessibility has changed.
     * A child notifies its parent that the accessibility state of a subtree rooted
     * That is some of the child properties reported to accessibility services has
     * at a given node changed. That is the structure of the subtree is different.
     * changed, hence the interested services have to be notified for the new state.
     *
     * @param The root of the changed subtree.
     *
     *
     * @hide
     * @hide
     */
     */
    public void childAccessibilityStateChanged(View child);
    public void childAccessibilityStateChanged(View root);


    /**
    /**
     * Tells if this view parent can resolve the layout direction.
     * Tells if this view parent can resolve the layout direction.
Loading