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

Commit e34bc33a authored by yingleiw's avatar yingleiw
Browse files

Delay sending accessibility events a little for temporarily detached view

In RV, a view could be temporarily detached in one draw cycle.
Accessibility events sent during this time will be lost. To prevent the
events from being lost, we will set a flag when a view is detached by
parent. And when sending accessibility events for detached view, we will
delay it a little by
ViewConfiguration.getSendRecurringAccessibilityEventsInterval. This way,
if the view is attached in one draw cycle, the events will be sent out.

Also add more setStateDescription() in CompoundButton to for safer code.
(this is not the cause of this bug though).

Fix: 151125936

Test: tested that the bug is fixed.

Change-Id: Iffca8c87bad4fa2f66862b966e351562d77d6d76
parent 17a63e29
Loading
Loading
Loading
Loading
+52 −2
Original line number Diff line number Diff line
@@ -3506,6 +3506,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
     *                       1          PFLAG4_AUTOFILL_HIDE_HIGHLIGHT
     *                     11           PFLAG4_SCROLL_CAPTURE_HINT_MASK
     *                    1             PFLAG4_ALLOW_CLICK_WHEN_DISABLED
     *                   1              PFLAG4_DETACHED
     * |-------|-------|-------|-------|
     */
@@ -3567,6 +3568,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
     */
    private static final int PFLAG4_ALLOW_CLICK_WHEN_DISABLED = 0x000001000;
    /**
     * Indicates if the view is just detached.
     */
    private static final int PFLAG4_DETACHED = 0x000002000;
    /* End of masks for mPrivateFlags4 */
    /** @hide */
@@ -8330,7 +8336,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
                (event.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
        boolean isWindowDisappearedEvent = isWindowStateChanged && ((event.getContentChangeTypes()
                & AccessibilityEvent.CONTENT_CHANGE_TYPE_PANE_DISAPPEARED) != 0);
        if (!isShown() && !isWindowDisappearedEvent) {
        boolean detached = detached();
        if (!isShown() && !isWindowDisappearedEvent && !detached) {
            return;
        }
        onInitializeAccessibilityEvent(event);
@@ -8341,6 +8348,14 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
        SendAccessibilityEventThrottle throttle = getThrottleForAccessibilityEvent(event);
        if (throttle != null) {
            throttle.post(event);
        } else if (!isWindowDisappearedEvent && detached) {
            // Views could be attached soon later. Accessibility events during this temporarily
            // detached period should be sent too.
            postDelayed(() -> {
                if (AccessibilityManager.getInstance(mContext).isEnabled() && isShown()) {
                    requestParentSendAccessibilityEvent(event);
                }
            }, ViewConfiguration.getSendRecurringAccessibilityEventsInterval());
        } else {
            requestParentSendAccessibilityEvent(event);
        }
@@ -11126,6 +11141,26 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
        return false;
    }
    private boolean detached() {
        View current = this;
        //noinspection ConstantConditions
        do {
            if ((current.mPrivateFlags4 & PFLAG4_DETACHED) != 0) {
                return true;
            }
            ViewParent parent = current.mParent;
            if (parent == null) {
                return false;
            }
            if (!(parent instanceof View)) {
                return false;
            }
            current = (View) parent;
        } while (current != null);
        return false;
    }
    /**
     * Called by the view hierarchy when the content insets for a window have
     * changed, to allow it to adjust its content to fit within those windows.
@@ -29443,7 +29478,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
        @Override
        public void run() {
            if (AccessibilityManager.getInstance(mContext).isEnabled()) {
            if (AccessibilityManager.getInstance(mContext).isEnabled() && isShown()) {
                requestParentSendAccessibilityEvent(mAccessibilityEvent);
            }
            reset();
@@ -30432,4 +30467,19 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
            }
        }
    }
    /**
     * Set the view to be detached or not detached.
     *
     * @param detached Whether the view is detached.
     *
     * @hide
     */
    protected void setDetached(boolean detached) {
        if (detached) {
            mPrivateFlags4 |= PFLAG4_DETACHED;
        } else {
            mPrivateFlags4 &= ~PFLAG4_DETACHED;
        }
    }
}
+11 −0
Original line number Diff line number Diff line
@@ -5811,6 +5811,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
        child.mPrivateFlags = (child.mPrivateFlags & ~PFLAG_DIRTY_MASK
                        & ~PFLAG_DRAWING_CACHE_VALID)
                | PFLAG_DRAWN | PFLAG_INVALIDATED;
        child.setDetached(false);
        this.mPrivateFlags |= PFLAG_INVALIDATED;

        if (child.hasFocus()) {
@@ -5839,6 +5840,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
     * @see #removeDetachedView(View, boolean)
     */
    protected void detachViewFromParent(View child) {
        child.setDetached(true);
        removeFromArray(indexOfChild(child));
    }

@@ -5860,6 +5862,9 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
     * @see #removeDetachedView(View, boolean)
     */
    protected void detachViewFromParent(int index) {
        if (index >= 0 && index < mChildrenCount) {
            mChildren[index].setDetached(true);
        }
        removeFromArray(index);
    }

@@ -5882,6 +5887,11 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
     * @see #removeDetachedView(View, boolean)
     */
    protected void detachViewsFromParent(int start, int count) {
        start = Math.max(0, start);
        final int end = Math.min(mChildrenCount, start + count);
        for (int i = start; i < end; i++) {
            mChildren[i].setDetached(true);
        }
        removeFromArray(start, count);
    }

@@ -5911,6 +5921,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager

        for (int i = count - 1; i >= 0; i--) {
            children[i].mParent = null;
            children[i].setDetached(true);
            children[i] = null;
        }
    }
+5 −3
Original line number Diff line number Diff line
@@ -183,14 +183,14 @@ public abstract class CompoundButton extends Button implements Checkable {
    public void setStateDescription(@Nullable CharSequence stateDescription) {
        mCustomStateDescription = stateDescription;
        if (stateDescription == null) {
            setDefaultStateDescritption();
            setDefaultStateDescription();
        } else {
            super.setStateDescription(stateDescription);
        }
    }

    /** @hide **/
    protected void setDefaultStateDescritption() {
    protected void setDefaultStateDescription() {
        if (mCustomStateDescription == null) {
            super.setStateDescription(getButtonStateDescription());
        }
@@ -210,6 +210,8 @@ public abstract class CompoundButton extends Button implements Checkable {

            // Avoid infinite recursions if setChecked() is called from a listener
            if (mBroadcasting) {
                // setStateDescription will not send out event if the description is unchanged.
                setDefaultStateDescription();
                return;
            }

@@ -228,7 +230,7 @@ public abstract class CompoundButton extends Button implements Checkable {
            mBroadcasting = false;
        }
        // setStateDescription will not send out event if the description is unchanged.
        setDefaultStateDescritption();
        setDefaultStateDescription();
    }

    /**
+3 −3
Original line number Diff line number Diff line
@@ -311,7 +311,7 @@ public class Switch extends CompoundButton {
        refreshDrawableState();
        // Default state is derived from on/off-text, so state has to be updated when on/off-text
        // are updated.
        setDefaultStateDescritption();
        setDefaultStateDescription();
        setChecked(isChecked());
    }

@@ -856,7 +856,7 @@ public class Switch extends CompoundButton {
        requestLayout();
        // Default state is derived from on/off-text, so state has to be updated when on/off-text
        // are updated.
        setDefaultStateDescritption();
        setDefaultStateDescription();
    }

    /**
@@ -879,7 +879,7 @@ public class Switch extends CompoundButton {
        requestLayout();
        // Default state is derived from on/off-text, so state has to be updated when on/off-text
        // are updated.
        setDefaultStateDescritption();
        setDefaultStateDescription();
    }

    /**
+3 −3
Original line number Diff line number Diff line
@@ -60,7 +60,7 @@ public class ToggleButton extends CompoundButton {
        syncTextState();
        // Default state is derived from on/off-text, so state has to be updated when on/off-text
        // are updated.
        setDefaultStateDescritption();
        setDefaultStateDescription();
        a.recycle();
    }

@@ -111,7 +111,7 @@ public class ToggleButton extends CompoundButton {
        mTextOn = textOn;
        // Default state is derived from on/off-text, so state has to be updated when on/off-text
        // are updated.
        setDefaultStateDescritption();
        setDefaultStateDescription();
    }

    /**
@@ -133,7 +133,7 @@ public class ToggleButton extends CompoundButton {
        mTextOff = textOff;
        // Default state is derived from on/off-text, so state has to be updated when on/off-text
        // are updated.
        setDefaultStateDescritption();
        setDefaultStateDescription();
    }

    /**