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

Commit c45b7074 authored by Alina Zaidi's avatar Alina Zaidi Committed by Android (Google) Code Review
Browse files

Merge "Make sure status bar is non interactable on secondary display when...

Merge "Make sure status bar is non interactable on secondary display when shade_goes_around_flag is on but "default_display" policy is set." into main
parents 2e8802c3 fb6c8389
Loading
Loading
Loading
Loading
+26 −15
Original line number Diff line number Diff line
@@ -16,8 +16,6 @@

package com.android.systemui.statusbar.phone;

import static android.view.Display.DEFAULT_DISPLAY;

import android.annotation.Nullable;
import android.content.Context;
import android.content.res.Configuration;
@@ -43,8 +41,8 @@ import com.android.systemui.Gefingerpoken;
import com.android.systemui.res.R;
import com.android.systemui.shade.ShadeExpandsOnStatusBarLongPress;
import com.android.systemui.shade.StatusBarLongPressGestureDetector;
import com.android.systemui.shade.shared.flag.ShadeWindowGoesAround;
import com.android.systemui.statusbar.core.StatusBarConnectedDisplays;
import com.android.systemui.statusbar.phone.PhoneStatusBarViewController.PhoneStatusBarViewInteractionsGate;
import com.android.systemui.statusbar.phone.userswitcher.StatusBarUserSwitcherContainer;
import com.android.systemui.statusbar.window.StatusBarWindowControllerStore;
import com.android.systemui.user.ui.binder.StatusBarUserChipViewBinder;
@@ -57,8 +55,6 @@ public class PhoneStatusBarView extends FrameLayout {
    private static final String TAG = "PhoneStatusBarView";
    private final StatusBarWindowControllerStore mStatusBarWindowControllerStore;

    private final boolean mShouldAllowInteractions;

    private int mRotationOrientation = -1;
    @Nullable
    private View mCutoutSpace;
@@ -70,6 +66,8 @@ public class PhoneStatusBarView extends FrameLayout {
    @Nullable
    private Gefingerpoken mTouchEventHandler;
    @Nullable
    private PhoneStatusBarViewInteractionsGate mInteractionGate;
    @Nullable
    private HasCornerCutoutFetcher mHasCornerCutoutFetcher;
    @Nullable
    private InsetsFetcher mInsetsFetcher;
@@ -85,10 +83,6 @@ public class PhoneStatusBarView extends FrameLayout {
    public PhoneStatusBarView(Context context, AttributeSet attrs) {
        super(context, attrs);
        mStatusBarWindowControllerStore = Dependency.get(StatusBarWindowControllerStore.class);
        // With the StatusBarConnectedDisplays changes, status bar elements are only interactive
        // if touch is on default display or the shade window can change displays.
        mShouldAllowInteractions = !StatusBarConnectedDisplays.isEnabled()
                || mContext.getDisplayId() == DEFAULT_DISPLAY || ShadeWindowGoesAround.isEnabled();
    }

    void setLongPressGestureDetector(
@@ -102,6 +96,10 @@ public class PhoneStatusBarView extends FrameLayout {
        mTouchEventHandler = handler;
    }

    void setInteractionGate(PhoneStatusBarViewInteractionsGate gate) {
        mInteractionGate = gate;
    }

    void setHasCornerCutoutFetcher(@NonNull HasCornerCutoutFetcher cornerCutoutFetcher) {
        mHasCornerCutoutFetcher = cornerCutoutFetcher;
        updateCutoutLocation();
@@ -216,17 +214,30 @@ public class PhoneStatusBarView extends FrameLayout {
        return false;
    }

    /** Whether this status bar and its elements should be interactable. */
    public boolean shouldAllowInteractions() {
        return mShouldAllowInteractions;
    @Override
    public boolean dispatchHoverEvent(MotionEvent event) {
        if (mInteractionGate != null && !mInteractionGate.shouldAllowInteractions()) {
            // Consume the event to prevent any calls to #onHoverEvent on status bar view or its
            // components, essentially making the status bar and its children completely
            // non-interactive.
            return true;
        }
        return super.dispatchHoverEvent(event);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        if (!shouldAllowInteractions()) {
            return false;
    public boolean dispatchTouchEvent(MotionEvent event) {
        if (mInteractionGate != null && !mInteractionGate.shouldAllowInteractions()) {
            // Consume the event to prevent any calls to #onTouchEvent on status bar view or its
            // components, essentially making the status bar and its children completely
            // non-interactive.
            return true;
        }
        return super.dispatchTouchEvent(event);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        if (ShadeExpandsOnStatusBarLongPress.isEnabled()
                && mStatusBarLongPressGestureDetector != null) {
            mStatusBarLongPressGestureDetector.handleTouch(event);
+36 −9
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@ package com.android.systemui.statusbar.phone
import android.app.StatusBarManager.WINDOW_STATUS_BAR
import android.graphics.Point
import android.util.Log
import android.view.Display.DEFAULT_DISPLAY
import android.view.InputDevice
import android.view.MotionEvent
import android.view.View
@@ -39,6 +40,7 @@ import com.android.systemui.shade.ShadeExpandsOnStatusBarLongPress
import com.android.systemui.shade.ShadeLogger
import com.android.systemui.shade.ShadeViewController
import com.android.systemui.shade.StatusBarLongPressGestureDetector
import com.android.systemui.shade.data.repository.ShadeDisplaysRepository
import com.android.systemui.shade.display.StatusBarTouchShadeDisplayPolicy
import com.android.systemui.shade.domain.interactor.PanelExpansionInteractor
import com.android.systemui.shade.domain.interactor.ShadeModeInteractor
@@ -87,6 +89,7 @@ private constructor(
    private val darkIconDispatcher: DarkIconDispatcher,
    private val statusBarContentInsetsProviderStore: StatusBarContentInsetsProviderStore,
    private val lazyStatusBarShadeDisplayPolicy: Lazy<StatusBarTouchShadeDisplayPolicy>,
    private val lazyShadeDisplaysRepository: Lazy<ShadeDisplaysRepository>,
) : ViewController<PhoneStatusBarView>(view) {

    private lateinit var battery: BatteryMeterView
@@ -136,14 +139,21 @@ private constructor(
    override fun onViewAttached() {
        clock = mView.requireViewById(R.id.clock)
        battery = mView.requireViewById(R.id.battery)

        addDarkReceivers()
        if (mView.shouldAllowInteractions()) {
            addCursorSupportToIconContainers()

        if (
            StatusBarConnectedDisplays.isEnabled && mView.context.getDisplayId() != DEFAULT_DISPLAY
        ) {
            // With the StatusBarConnectedDisplays changes, external status bar elements are not
            // interactive when the shade window can't change displays.
            mView.setInteractionGate(PhoneStatusBarViewInteractionsGate())
        }

        addCursorSupportToIconContainers()
        if (ShadeExpandsOnStatusBarLongPress.isEnabled) {
            mView.setLongPressGestureDetector(statusBarLongPressGestureDetector.get())
        }
        }

        progressProvider?.setReadyToHandleTransition(true)
        configurationController.addCallback(configurationListener)
@@ -205,10 +215,8 @@ private constructor(
    @VisibleForTesting
    public override fun onViewDetached() {
        removeDarkReceivers()
        if (mView.shouldAllowInteractions()) {
        startSideContainer.setOnHoverListener(null)
        endSideContainer.setOnHoverListener(null)
        }
        progressProvider?.setReadyToHandleTransition(false)
        moveFromCenterAnimationController?.onViewDetached()
        configurationController.removeCallback(configurationListener)
@@ -271,6 +279,23 @@ private constructor(
        darkIconDispatcher.removeDarkReceiver(clock)
    }

    /**
     * Determines whether user interaction (e.g., touch and hover events with the phone's status bar
     * view should be allowed.
     */
    inner class PhoneStatusBarViewInteractionsGate {
        /** Checks if user interactions with the status bar are currently permitted. */
        fun shouldAllowInteractions(): Boolean {
            // With the StatusBarConnectedDisplays changes, external status bar elements are not
            // interactive when the shade window can't change displays.
            val shadeDisplayPolicy =
                if (ShadeWindowGoesAround.isEnabled) {
                    lazyShadeDisplaysRepository.get().currentPolicy
                } else null
            return shadeDisplayPolicy is StatusBarTouchShadeDisplayPolicy
        }
    }

    inner class PhoneStatusBarViewTouchHandler : Gefingerpoken {
        override fun onInterceptTouchEvent(event: MotionEvent): Boolean {
            if (event.action == MotionEvent.ACTION_DOWN) {
@@ -384,6 +409,7 @@ private constructor(
        @DisplaySpecific private val darkIconDispatcher: DarkIconDispatcher,
        private val statusBarContentInsetsProviderStore: StatusBarContentInsetsProviderStore,
        private val lazyStatusBarShadeDisplayPolicy: Lazy<StatusBarTouchShadeDisplayPolicy>,
        private val lazyShadeDisplaysRepository: Lazy<ShadeDisplaysRepository>,
    ) {
        fun create(view: PhoneStatusBarView): PhoneStatusBarViewController {
            val statusBarMoveFromCenterAnimationController =
@@ -424,6 +450,7 @@ private constructor(
                darkIconDispatcher,
                statusBarContentInsetsProviderStore,
                lazyStatusBarShadeDisplayPolicy,
                lazyShadeDisplaysRepository,
            )
        }
    }
+144 −82

File changed.

Preview size limit exceeded, changes collapsed.

+55 −40
Original line number Diff line number Diff line
@@ -38,7 +38,6 @@ import android.view.View
import android.view.WindowInsets
import android.widget.FrameLayout
import androidx.test.filters.SmallTest
import com.android.systemui.Flags.FLAG_SHADE_WINDOW_GOES_AROUND
import com.android.systemui.Flags.FLAG_STATUS_BAR_SWIPE_OVER_CHIP
import com.android.systemui.Gefingerpoken
import com.android.systemui.SysuiTestCase
@@ -73,6 +72,9 @@ class PhoneStatusBarViewTest : SysuiTestCase() {
    @Mock private lateinit var windowController: StatusBarWindowController
    @Mock private lateinit var windowControllerStore: StatusBarWindowControllerStore
    @Mock private lateinit var longPressGestureDetector: StatusBarLongPressGestureDetector
    @Mock
    private lateinit var interactionGate:
        PhoneStatusBarViewController.PhoneStatusBarViewInteractionsGate

    @Before
    fun setUp() {
@@ -101,83 +103,96 @@ class PhoneStatusBarViewTest : SysuiTestCase() {
    }

    @Test
    fun shouldAllowInteractions_primaryDisplay_returnsTrue() {
        assertThat(view.shouldAllowInteractions()).isTrue()
    }

    @Test
    @DisableFlags(FLAG_STATUS_BAR_CONNECTED_DISPLAYS)
    fun shouldAllowInteractions_secondaryDisplay_statusBarConnectedDisplaysDisabled_returnsTrue() {
        assertThat(viewForSecondaryDisplay.shouldAllowInteractions()).isTrue()
    }
    fun dispatchTouchEvent_noInteractionGate_listenersNotified() {
        val handler = TestTouchEventHandler()
        viewForSecondaryDisplay.setTouchEventHandler(handler)
        viewForSecondaryDisplay.setLongPressGestureDetector(longPressGestureDetector)

    @Test
    @EnableFlags(FLAG_STATUS_BAR_CONNECTED_DISPLAYS, FLAG_SHADE_WINDOW_GOES_AROUND)
    fun shouldAllowInteractions_secondaryDisplay_statusBarConnectedDisplaysEnabled_shadeWindowGoesAroundEnabled_returnsTrue() {
        assertThat(viewForSecondaryDisplay.shouldAllowInteractions()).isTrue()
    }
        val event = MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 0f, 0)
        viewForSecondaryDisplay.dispatchTouchEvent(event)

    @Test
    @EnableFlags(FLAG_STATUS_BAR_CONNECTED_DISPLAYS)
    @DisableFlags(FLAG_SHADE_WINDOW_GOES_AROUND)
    fun shouldAllowInteractions_secondaryDisplay_statusBarConnectedDisplaysEnabled_shadeWindowGoesAroundDisabled_returnsFalse() {
        assertThat(viewForSecondaryDisplay.shouldAllowInteractions()).isFalse()
        assertThat(handler.lastInterceptEvent).isEqualTo(event)
        assertThat(handler.lastEvent).isEqualTo(event)
        verify(longPressGestureDetector).handleTouch(eq(event))
    }

    @Test
    fun onTouchEvent_listenersNotified() {
    fun dispatchTouchEvent_shouldAllowInteractions_listenersNotified() {
        val handler = TestTouchEventHandler()
        view.setTouchEventHandler(handler)
        view.setLongPressGestureDetector(longPressGestureDetector)
        viewForSecondaryDisplay.setTouchEventHandler(handler)
        viewForSecondaryDisplay.setLongPressGestureDetector(longPressGestureDetector)
        whenever(interactionGate.shouldAllowInteractions()).thenReturn(true)
        viewForSecondaryDisplay.setInteractionGate(interactionGate)

        val event = MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 0f, 0)
        view.onTouchEvent(event)
        viewForSecondaryDisplay.dispatchTouchEvent(event)

        assertThat(handler.lastInterceptEvent).isEqualTo(event)
        assertThat(handler.lastEvent).isEqualTo(event)
        verify(longPressGestureDetector).handleTouch(eq(event))
    }

    @Test
    @DisableFlags(FLAG_STATUS_BAR_CONNECTED_DISPLAYS)
    fun onTouchEvent_touchOnSecondaryDisplay_statusBarConnectedDisplaysDisabled_listenersNotified() {
    fun dispatchTouchEvent_shouldNotAllowInteractions_consumesEventAndListenersNotNotified() {
        val handler = TestTouchEventHandler()
        viewForSecondaryDisplay.setTouchEventHandler(handler)
        viewForSecondaryDisplay.setLongPressGestureDetector(longPressGestureDetector)
        whenever(interactionGate.shouldAllowInteractions()).thenReturn(false)
        viewForSecondaryDisplay.setInteractionGate(interactionGate)

        val event = MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 0f, 0)
        viewForSecondaryDisplay.onTouchEvent(event)

        assertThat(handler.lastEvent).isEqualTo(event)
        verify(longPressGestureDetector).handleTouch(eq(event))
        assertThat(viewForSecondaryDisplay.dispatchTouchEvent(event)).isEqualTo(true)
        assertThat(handler.lastInterceptEvent).isNull()
        assertThat(handler.lastEvent).isNull()
        verify(longPressGestureDetector, never()).handleTouch(eq(event))
    }

    @Test
    @EnableFlags(FLAG_STATUS_BAR_CONNECTED_DISPLAYS, FLAG_SHADE_WINDOW_GOES_AROUND)
    fun onTouchEvent_touchOnSecondaryDisplay_statusBarConnectedDisplaysEnabled_shadeWindowGoesAroundEnabled_listenersNotified() {
    fun dispatchHoverEvent_noInteractionGate_doesntConsumeEvent() {
        val handler = TestTouchEventHandler()
        viewForSecondaryDisplay.setTouchEventHandler(handler)
        viewForSecondaryDisplay.setLongPressGestureDetector(longPressGestureDetector)

        val event = MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 0f, 0)
        viewForSecondaryDisplay.onTouchEvent(event)
        assertThat(viewForSecondaryDisplay.dispatchHoverEvent(event)).isEqualTo(false)
    }

        assertThat(handler.lastEvent).isEqualTo(event)
        verify(longPressGestureDetector).handleTouch(eq(event))
    @Test
    fun dispatchHoverEvent_shouldAllowInteractions_doesntConsumeEvent() {
        val handler = TestTouchEventHandler()
        viewForSecondaryDisplay.setTouchEventHandler(handler)
        viewForSecondaryDisplay.setLongPressGestureDetector(longPressGestureDetector)
        whenever(interactionGate.shouldAllowInteractions()).thenReturn(true)
        viewForSecondaryDisplay.setInteractionGate(interactionGate)

        val event = MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 0f, 0)
        assertThat(viewForSecondaryDisplay.dispatchHoverEvent(event)).isEqualTo(false)
    }

    @Test
    @EnableFlags(FLAG_STATUS_BAR_CONNECTED_DISPLAYS)
    @DisableFlags(FLAG_SHADE_WINDOW_GOES_AROUND)
    fun onTouchEvent_touchOnSecondaryDisplay_statusBarConnectedDisplaysEnabled_shadeWindowGoesAroundDisabled_listenersNotNotified() {
    fun dispatchHoverEvent_shouldNotAllowInteractions_consumesEvent() {
        val handler = TestTouchEventHandler()
        viewForSecondaryDisplay.setTouchEventHandler(handler)
        viewForSecondaryDisplay.setLongPressGestureDetector(longPressGestureDetector)
        whenever(interactionGate.shouldAllowInteractions()).thenReturn(false)
        viewForSecondaryDisplay.setInteractionGate(interactionGate)

        val event = MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 0f, 0)
        assertThat(viewForSecondaryDisplay.dispatchHoverEvent(event)).isEqualTo(true)
    }

    @Test
    fun onTouchEvent_listenersNotified() {
        val handler = TestTouchEventHandler()
        view.setTouchEventHandler(handler)
        view.setLongPressGestureDetector(longPressGestureDetector)

        val event = MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 0f, 0)
        viewForSecondaryDisplay.onTouchEvent(event)
        view.onTouchEvent(event)

        assertThat(handler.lastEvent).isNull()
        verify(longPressGestureDetector, never()).handleTouch(eq(event))
        assertThat(handler.lastEvent).isEqualTo(event)
        verify(longPressGestureDetector).handleTouch(eq(event))
    }

    @Test