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

Commit f6eb8e4c authored by Luca Zuccarini's avatar Luca Zuccarini Committed by Android (Google) Code Review
Browse files

Merge "Intercept Shade accessibility focus events during launch animations." into main

parents 210690bd 8bf1f140
Loading
Loading
Loading
Loading
+11 −1
Original line number Diff line number Diff line
@@ -1991,3 +1991,13 @@ flag {
      purpose: PURPOSE_BUGFIX
    }
}

flag {
   name: "shade_launch_accessibility"
   namespace: "systemui"
   description: "Intercept accessibility focus events for the Shade during launch animations to avoid stray TalkBack events."
   bug: "379222226"
   metadata {
        purpose: PURPOSE_BUGFIX
   }
}
+22 −1
Original line number Diff line number Diff line
@@ -20,7 +20,7 @@ import android.platform.test.annotations.DisableFlags
import android.platform.test.annotations.EnableFlags
import android.testing.TestableLooper.RunWithLooper
import android.view.Choreographer
import android.view.MotionEvent
import android.view.accessibility.AccessibilityEvent
import android.widget.FrameLayout
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
@@ -45,7 +45,10 @@ import com.android.systemui.res.R
import com.android.systemui.settings.brightness.data.repository.BrightnessMirrorShowingRepository
import com.android.systemui.settings.brightness.domain.interactor.BrightnessMirrorShowingInteractor
import com.android.systemui.shade.NotificationShadeWindowView.InteractionEventHandler
import com.android.systemui.shade.data.repository.ShadeAnimationRepository
import com.android.systemui.shade.data.repository.ShadeRepositoryImpl
import com.android.systemui.shade.domain.interactor.PanelExpansionInteractor
import com.android.systemui.shade.domain.interactor.ShadeAnimationInteractorLegacyImpl
import com.android.systemui.statusbar.BlurUtils
import com.android.systemui.statusbar.DragDownHelper
import com.android.systemui.statusbar.LockscreenShadeTransitionController
@@ -185,6 +188,10 @@ class NotificationShadeWindowViewTest : SysuiTestCase() {
                notificationShadeDepthController,
                underTest,
                shadeViewController,
                ShadeAnimationInteractorLegacyImpl(
                    ShadeAnimationRepository(),
                    ShadeRepositoryImpl(testScope),
                ),
                panelExpansionInteractor,
                ShadeExpansionStateManager(),
                notificationStackScrollLayoutController,
@@ -259,6 +266,20 @@ class NotificationShadeWindowViewTest : SysuiTestCase() {
        verify(configurationForwarder).dispatchOnMovedToDisplay(eq(1), eq(config))
    }

    @Test
    @EnableFlags(AConfigFlags.FLAG_SHADE_LAUNCH_ACCESSIBILITY)
    fun requestSendAccessibilityEvent_duringLaunchAnimation_blocksFocusEvent() {
        underTest.setAnimatingContentLaunch(true)

        assertThat(
                underTest.requestSendAccessibilityEvent(
                    underTest.getChildAt(0),
                    AccessibilityEvent(AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED),
                )
            )
            .isFalse()
    }

    private fun captureInteractionEventHandler() {
        verify(underTest).setInteractionEventHandler(interactionEventHandlerCaptor.capture())
        interactionEventHandler = interactionEventHandlerCaptor.value
+21 −0
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@
package com.android.systemui.shade;

import static android.os.Trace.TRACE_TAG_APP;
import static android.view.accessibility.AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED;

import static com.android.systemui.Flags.enableViewCaptureTracing;
import static com.android.systemui.statusbar.phone.CentralSurfaces.DEBUG;
@@ -49,10 +50,12 @@ import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.view.Window;
import android.view.WindowInsetsController;
import android.view.accessibility.AccessibilityEvent;

import com.android.app.viewcapture.ViewCaptureFactory;
import com.android.internal.view.FloatingActionMode;
import com.android.internal.widget.floatingtoolbar.FloatingToolbar;
import com.android.systemui.Flags;
import com.android.systemui.scene.ui.view.WindowRootView;
import com.android.systemui.shade.shared.flag.ShadeWindowGoesAround;
import com.android.systemui.statusbar.phone.ConfigurationForwarder;
@@ -77,6 +80,8 @@ public class NotificationShadeWindowView extends WindowRootView {

    private SafeCloseable mViewCaptureCloseable;

    private boolean mAnimatingContentLaunch = false;

    public NotificationShadeWindowView(Context context, AttributeSet attrs) {
        super(context, attrs);
        setMotionEventSplittingEnabled(false);
@@ -188,6 +193,22 @@ public class NotificationShadeWindowView extends WindowRootView {
        }
    }

    @Override
    public boolean requestSendAccessibilityEvent(View child, AccessibilityEvent event) {
        if (Flags.shadeLaunchAccessibility() && mAnimatingContentLaunch
                && event.getEventType() == TYPE_VIEW_ACCESSIBILITY_FOCUSED) {
            // Block accessibility focus events during launch animations to avoid stray TalkBack
            // announcements.
            return false;
        }

        return super.requestSendAccessibilityEvent(child, event);
    }

    public void setAnimatingContentLaunch(boolean animating) {
        mAnimatingContentLaunch = animating;
    }

    public void setConfigurationForwarder(ConfigurationForwarder configurationForwarder) {
        ShadeWindowGoesAround.isUnexpectedlyInLegacyMode();
        mConfigurationForwarder = configurationForwarder;
+22 −1
Original line number Diff line number Diff line
@@ -16,10 +16,12 @@

package com.android.systemui.shade;

import static com.android.systemui.Flags.shadeLaunchAccessibility;
import static com.android.systemui.keyguard.shared.model.KeyguardState.DREAMING;
import static com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN;
import static com.android.systemui.statusbar.StatusBarState.KEYGUARD;
import static com.android.systemui.util.kotlin.JavaAdapterKt.collectFlow;
import static com.android.systemui.util.kotlin.JavaAdapterKt.combineFlows;

import android.app.StatusBarManager;
import android.util.Log;
@@ -59,6 +61,7 @@ import com.android.systemui.res.R;
import com.android.systemui.scene.shared.flag.SceneContainerFlag;
import com.android.systemui.settings.brightness.domain.interactor.BrightnessMirrorShowingInteractor;
import com.android.systemui.shade.domain.interactor.PanelExpansionInteractor;
import com.android.systemui.shade.domain.interactor.ShadeAnimationInteractor;
import com.android.systemui.shade.shared.flag.ShadeWindowGoesAround;
import com.android.systemui.shared.animation.DisableSubpixelTextTransitionListener;
import com.android.systemui.statusbar.BlurUtils;
@@ -85,6 +88,7 @@ import com.android.systemui.window.ui.WindowRootViewBinder;
import com.android.systemui.window.ui.viewmodel.WindowRootViewModel;

import kotlinx.coroutines.ExperimentalCoroutinesApi;
import kotlinx.coroutines.flow.Flow;

import java.io.PrintWriter;
import java.util.Optional;
@@ -174,6 +178,7 @@ public class NotificationShadeWindowViewController implements Dumpable {
            NotificationShadeDepthController depthController,
            NotificationShadeWindowView notificationShadeWindowView,
            ShadeViewController shadeViewController,
            ShadeAnimationInteractor shadeAnimationInteractor,
            PanelExpansionInteractor panelExpansionInteractor,
            ShadeExpansionStateManager shadeExpansionStateManager,
            NotificationStackScrollLayoutController notificationStackScrollLayoutController,
@@ -238,9 +243,17 @@ public class NotificationShadeWindowViewController implements Dumpable {
        collectFlow(mView, keyguardTransitionInteractor.transition(
                Edge.create(LOCKSCREEN, DREAMING)),
                mLockscreenToDreamingTransition);
        Flow<Boolean> isLaunchAnimationRunning =
                shadeLaunchAccessibility()
                        ? combineFlows(
                                notificationLaunchAnimationInteractor.isLaunchAnimationRunning(),
                                shadeAnimationInteractor.isLaunchingActivity(),
                                (notificationLaunching, shadeLaunching) ->
                                        notificationLaunching || shadeLaunching)
                        : notificationLaunchAnimationInteractor.isLaunchAnimationRunning();
        collectFlow(
                mView,
                notificationLaunchAnimationInteractor.isLaunchAnimationRunning(),
                isLaunchAnimationRunning,
                this::setExpandAnimationRunning);
        if (QSComposeFragment.isEnabled()) {
            collectFlow(mView,
@@ -726,9 +739,17 @@ public class NotificationShadeWindowViewController implements Dumpable {
            if (ActivityTransitionAnimator.DEBUG_TRANSITION_ANIMATION) {
                Log.d(TAG, "Setting mExpandAnimationRunning=" + running);
            }

            if (running) {
                mLaunchAnimationTimeout = mClock.uptimeMillis() + 5000;
            }

            if (shadeLaunchAccessibility()) {
                // The view needs to know when an animation is ongoing so it can intercept
                // unnecessary accessibility events.
                mView.setAnimatingContentLaunch(running);
            }

            mExpandAnimationRunning = running;
            mNotificationShadeWindowController.setLaunchingActivity(mExpandAnimationRunning);
        }
+14 −5
Original line number Diff line number Diff line
package com.android.systemui.statusbar.phone

import android.view.View
import com.android.systemui.Flags
import com.android.systemui.animation.ActivityTransitionAnimator
import com.android.systemui.animation.TransitionAnimator
import com.android.systemui.animation.TransitionAnimator.Companion.getProgress
@@ -22,7 +23,7 @@ class StatusBarTransitionAnimatorController(
    private val notificationShadeWindowController: NotificationShadeWindowController,
    private val commandQueue: CommandQueue,
    @DisplayId private val displayId: Int,
    private val isLaunchForActivity: Boolean = true
    private val isLaunchForActivity: Boolean = true,
) : ActivityTransitionAnimator.Controller by delegate {
    private var hideIconsDuringLaunchAnimation: Boolean = true

@@ -41,8 +42,16 @@ class StatusBarTransitionAnimatorController(
    }

    override fun onTransitionAnimationStart(isExpandingFullyAbove: Boolean) {
        if (Flags.shadeLaunchAccessibility()) {
            // We set this before calling the delegate to make sure that accessibility is disabled
            // for the whole duration of the transition, so that we don't have stray TalkBack events
            // once the animating view becomes invisible.
            shadeAnimationInteractor.setIsLaunchingActivity(true)
            delegate.onTransitionAnimationStart(isExpandingFullyAbove)
        } else {
            delegate.onTransitionAnimationStart(isExpandingFullyAbove)
            shadeAnimationInteractor.setIsLaunchingActivity(true)
        }
        if (!isExpandingFullyAbove) {
            shadeController.collapseWithDuration(
                ActivityTransitionAnimator.TIMINGS.totalDuration.toInt()
@@ -59,7 +68,7 @@ class StatusBarTransitionAnimatorController(
    override fun onTransitionAnimationProgress(
        state: TransitionAnimator.State,
        progress: Float,
        linearProgress: Float
        linearProgress: Float,
    ) {
        delegate.onTransitionAnimationProgress(state, progress, linearProgress)
        val hideIcons =
@@ -67,7 +76,7 @@ class StatusBarTransitionAnimatorController(
                ActivityTransitionAnimator.TIMINGS,
                linearProgress,
                ANIMATION_DELAY_ICON_FADE_IN,
                100
                100,
            ) == 0.0f
        if (hideIcons != hideIconsDuringLaunchAnimation) {
            hideIconsDuringLaunchAnimation = hideIcons
Loading