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

Commit feb36453 authored by Hiroki Sato's avatar Hiroki Sato
Browse files

Ensure that a11y window state change event is sent on focus change

Currently when the focus changes, an accessibility event of
TYPE_WINDOW_STATE_CHANGED is dispatched from ViewRootImpl. This logic is
implemented in performTraversals(), but the logic depends on the value
mAttachInfo.mHasWindowFocus, which is updated from an input event
handleWindowFocusChanged(). There's no guarantee that always
performTraversals() is called after handleWindowFocusChanged().

As a result, sometimes accessibility event is not dispatched after focus
change.

This change fixes the issue by explicitly dispatching the event in
handleWindowFocusChanged() if needed.

Bug: 260310672
Bug: 214318644
Test: Accessibility CTS
Change-Id: I95cf3a7a9c7951f8b3862713ed7339688a8427d5
parent 23a3b64b
Loading
Loading
Loading
Loading
+13 −25
Original line number Original line Diff line number Diff line
@@ -470,16 +470,6 @@ public final class ViewRootImpl implements ViewParent,
    private boolean mAppVisibilityChanged;
    private boolean mAppVisibilityChanged;
    int mOrigWindowType = -1;
    int mOrigWindowType = -1;


    /** Whether the window had focus during the most recent traversal. */
    boolean mHadWindowFocus;

    /**
     * Whether the window lost focus during a previous traversal and has not
     * yet gained it back. Used to determine whether a WINDOW_STATE_CHANGE
     * accessibility events should be sent during traversal.
     */
    boolean mLostWindowFocus;

    // Set to true if the owner of this window is in the stopped state,
    // Set to true if the owner of this window is in the stopped state,
    // so the window should no longer be active.
    // so the window should no longer be active.
    @UnsupportedAppUsage
    @UnsupportedAppUsage
@@ -3573,20 +3563,8 @@ public final class ViewRootImpl implements ViewParent,
        }
        }


        final boolean changedVisibility = (viewVisibilityChanged || mFirst) && isViewVisible;
        final boolean changedVisibility = (viewVisibilityChanged || mFirst) && isViewVisible;
        final boolean hasWindowFocus = mAttachInfo.mHasWindowFocus && isViewVisible;
        if (changedVisibility) {
        final boolean regainedFocus = hasWindowFocus && mLostWindowFocus;
            maybeFireAccessibilityWindowStateChangedEvent();
        if (regainedFocus) {
            mLostWindowFocus = false;
        } else if (!hasWindowFocus && mHadWindowFocus) {
            mLostWindowFocus = true;
        }

        if (changedVisibility || regainedFocus) {
            // Toasts are presented as notifications - don't present them as windows as well
            boolean isToast = mWindowAttributes.type == TYPE_TOAST;
            if (!isToast) {
                host.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
            }
        }
        }


        mFirst = false;
        mFirst = false;
@@ -3594,8 +3572,8 @@ public final class ViewRootImpl implements ViewParent,
        mNewSurfaceNeeded = false;
        mNewSurfaceNeeded = false;
        mActivityRelaunched = false;
        mActivityRelaunched = false;
        mViewVisibility = viewVisibility;
        mViewVisibility = viewVisibility;
        mHadWindowFocus = hasWindowFocus;


        final boolean hasWindowFocus = mAttachInfo.mHasWindowFocus && isViewVisible;
        mImeFocusController.onTraversal(hasWindowFocus, mWindowAttributes);
        mImeFocusController.onTraversal(hasWindowFocus, mWindowAttributes);


        if ((relayoutResult & WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME) != 0) {
        if ((relayoutResult & WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME) != 0) {
@@ -3838,6 +3816,8 @@ public final class ViewRootImpl implements ViewParent,
                        ~WindowManager.LayoutParams
                        ~WindowManager.LayoutParams
                                .SOFT_INPUT_IS_FORWARD_NAVIGATION;
                                .SOFT_INPUT_IS_FORWARD_NAVIGATION;


                maybeFireAccessibilityWindowStateChangedEvent();

                // Refocusing a window that has a focused view should fire a
                // Refocusing a window that has a focused view should fire a
                // focus event for the view since the global focused view changed.
                // focus event for the view since the global focused view changed.
                fireAccessibilityFocusEventIfHasFocusedNode();
                fireAccessibilityFocusEventIfHasFocusedNode();
@@ -3865,6 +3845,14 @@ public final class ViewRootImpl implements ViewParent,
        ensureTouchModeLocally(inTouchMode);
        ensureTouchModeLocally(inTouchMode);
    }
    }


    private void maybeFireAccessibilityWindowStateChangedEvent() {
        // Toasts are presented as notifications - don't present them as windows as well.
        boolean isToast = mWindowAttributes != null && (mWindowAttributes.type == TYPE_TOAST);
        if (!isToast && mView != null) {
            mView.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
        }
    }

    private void fireAccessibilityFocusEventIfHasFocusedNode() {
    private void fireAccessibilityFocusEventIfHasFocusedNode() {
        if (!AccessibilityManager.getInstance(mContext).isEnabled()) {
        if (!AccessibilityManager.getInstance(mContext).isEnabled()) {
            return;
            return;