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

Commit 003b2bc6 authored by Jiaming Cheng's avatar Jiaming Cheng Committed by Android (Google) Code Review
Browse files

Merge "[Flexiglass Dual Shade] Handle taps on status bar" into main

parents 50187158 e73db62c
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -1203,4 +1203,7 @@

    <!-- Whether to hide notifications shade when the user presses ALL_APPS key. -->
    <bool name="config_enableHideNotificationsShadeOnAllAppsKey">false</bool>

    <!-- Indicates whether the tapping to open qs panel is supported -->
    <bool name="config_statusBarTapToExpandShade">false</bool>
</resources>
+66 −12
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.util.Log
import android.view.Display.DEFAULT_DISPLAY
import android.view.GestureDetector
import android.view.InputDevice
import android.view.MotionEvent
import android.view.View
@@ -112,6 +113,42 @@ private constructor(
            }
        }

    // Creates a [View.OnTouchListener] that handles mouse clicks and finger taps.
    private fun createClickListener(v: View, onClick: () -> Unit): View.OnTouchListener {
        val gestureDetector =
            GestureDetector(
                mView.context,
                object : GestureDetector.SimpleOnGestureListener() {
                    override fun onDown(e: MotionEvent): Boolean {
                        // Return true here to receive subsequent events, which are then
                        // handled by onSingleTapUp.
                        return true
                    }

                    override fun onSingleTapUp(e: MotionEvent): Boolean {
                        dispatchEventToShadeDisplayPolicy(e)
                        v.performClick()
                        onClick()
                        return true
                    }
                },
            )
        return View.OnTouchListener { _, event ->
            // Handle mouse clicks separately.
            if (event.source == InputDevice.SOURCE_MOUSE) {
                if (event.action == MotionEvent.ACTION_UP) {
                    dispatchEventToShadeDisplayPolicy(event)
                    v.performClick()
                    onClick()
                }
                return@OnTouchListener true
            }

            // For all other (touch) events, delegate to the GestureDetector.
            return@OnTouchListener gestureDetector.onTouchEvent(event)
        }
    }

    private fun dispatchEventToShadeDisplayPolicy(event: MotionEvent) {
        if (ShadeWindowGoesAround.isEnabled) {
            // Notify the shade display policy that the status bar was touched. This may cause
@@ -166,15 +203,14 @@ private constructor(
        endSideContainer.setOnHoverListener(
            statusOverlayHoverListenerFactory.createDarkAwareListener(endSideContainer)
        )

        if (statusBarTapToExpandShadeEnabled()) {
            endSideContainer.setOnTouchListener(
            createMouseClickListener {
                if (shadeModeInteractor.isDualShade) {
                    shadeController.animateExpandQs()
                createClickListener(endSideContainer) { animateExpandQs() }
            )
        } else {
                    shadeController.animateExpandShade()
                }
            endSideContainer.setOnTouchListener(createMouseClickListener { animateExpandQs() })
        }
        )

        startSideContainer = mView.requireViewById(R.id.status_bar_start_side_content)
        startSideContainer.setOnHoverListener(
@@ -184,10 +220,28 @@ private constructor(
                bottomHoverMargin = 6,
            )
        )
        if (statusBarTapToExpandShadeEnabled()) {
            startSideContainer.setOnTouchListener(
                createClickListener(startSideContainer) { shadeController.animateExpandShade() }
            )
        } else {
            startSideContainer.setOnTouchListener(
                createMouseClickListener { shadeController.animateExpandShade() }
            )
        }
    }

    private fun statusBarTapToExpandShadeEnabled(): Boolean {
        return context.resources.getBoolean(R.bool.config_statusBarTapToExpandShade)
    }

    private fun animateExpandQs() {
        if (shadeModeInteractor.isDualShade) {
            shadeController.animateExpandQs()
        } else {
            shadeController.animateExpandShade()
        }
    }

    @VisibleForTesting
    public override fun onViewDetached() {
+124 −9
Original line number Diff line number Diff line
@@ -76,6 +76,7 @@ import com.android.systemui.util.view.ViewUtil
import com.google.common.truth.Truth.assertThat
import java.util.Optional
import java.util.function.BooleanSupplier
import org.junit.Assume
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -147,6 +148,16 @@ class PhoneStatusBarViewControllerTest(flags: FlagsParameterization) : SysuiTest
            .thenReturn(Insets.NONE)
        whenever(mStatusOverlayHoverListenerFactory.createDarkAwareListener(any()))
            .thenReturn(mStatusOverlayHoverListener)
        whenever(
                mStatusOverlayHoverListenerFactory.createDarkAwareListener(
                    any(),
                    eq(0),
                    eq(0),
                    eq(6),
                    eq(6),
                )
            )
            .thenReturn(mStatusOverlayHoverListener)

        val parent = FrameLayout(mContext) // add parent to keep layout params
        view =
@@ -246,14 +257,16 @@ class PhoneStatusBarViewControllerTest(flags: FlagsParameterization) : SysuiTest
        val view = createViewMock(view)
        val endSideContainer = spy(view.requireViewById<View>(R.id.system_icons))
        whenever(view.requireViewById<View>(R.id.system_icons)).thenReturn(endSideContainer)
        val statusContainer = spy(view.requireViewById<View>(R.id.status_bar_start_side_content))
        val startSideContainer = spy(view.requireViewById<View>(R.id.status_bar_start_side_content))
        whenever(view.requireViewById<View>(R.id.status_bar_start_side_content))
            .thenReturn(statusContainer)
            .thenReturn(startSideContainer)

        controller = createAndInitController(view)

        verify(endSideContainer).setOnHoverListener(any())
        verify(statusContainer).setOnTouchListener(any())
        verify(endSideContainer).setOnTouchListener(any())
        verify(startSideContainer).setOnHoverListener(any())
        verify(startSideContainer).setOnTouchListener(any())
    }

    @Test
@@ -712,15 +725,117 @@ class PhoneStatusBarViewControllerTest(flags: FlagsParameterization) : SysuiTest
    }

    @Test
    fun statusIconContainerIsNotHandlingTouchScreenTouches() {
    fun shadeIsExpandedOnStartSideContentTap_flagOn() {
        Assume.assumeTrue(mContext.resources.getBoolean(R.bool.config_statusBarTapToExpandShade))
        val view = createViewMock(view)
        controller = createAndInitController(view)

        val startSideContainer = view.requireViewById<View>(R.id.status_bar_start_side_content)
        startSideContainer.dispatchTouchEvent(
            getActionDownEventFromSource(InputDevice.SOURCE_TOUCHSCREEN)
        )
        startSideContainer.dispatchTouchEvent(
            getActionUpEventFromSource(InputDevice.SOURCE_TOUCHSCREEN)
        )

        verify(shadeControllerImpl).animateExpandShade()
        verify(shadeControllerImpl, never()).animateExpandQs()
    }

    @Test
    fun shadeIsNotExpandedOnStartSideContentTap_flagoff() {
        Assume.assumeFalse(mContext.resources.getBoolean(R.bool.config_statusBarTapToExpandShade))
        val view = createViewMock(view)
        controller = createAndInitController(view)

        val startSideContainer = view.requireViewById<View>(R.id.status_bar_start_side_content)
        startSideContainer.dispatchTouchEvent(
            getActionDownEventFromSource(InputDevice.SOURCE_TOUCHSCREEN)
        )
        startSideContainer.dispatchTouchEvent(
            getActionUpEventFromSource(InputDevice.SOURCE_TOUCHSCREEN)
        )

        verify(shadeControllerImpl, never()).animateExpandShade()
    }

    @Test
    fun statusIconContainerIsHandlingTouchScreenTaps_singleShade_expandsNotificationsShade_flagOn() {
        Assume.assumeTrue(mContext.resources.getBoolean(R.bool.config_statusBarTapToExpandShade))
        kosmos.enableSingleShade()
        val view = createViewMock(view)
        controller = createAndInitController(view)
        val statusContainer = view.requireViewById<View>(R.id.system_icons)
        val handled =
        statusContainer.dispatchTouchEvent(
            getActionDownEventFromSource(InputDevice.SOURCE_TOUCHSCREEN)
        )
        statusContainer.dispatchTouchEvent(
            getActionUpEventFromSource(InputDevice.SOURCE_TOUCHSCREEN)
        )
        assertThat(handled).isFalse()

        verify(shadeControllerImpl).animateExpandShade()
        verify(shadeControllerImpl, never()).animateExpandQs()
    }

    @Test
    @EnableSceneContainer
    fun statusIconContainerIsNotHandlingTouchScreenTaps_flagOff() {
        Assume.assumeFalse(mContext.resources.getBoolean(R.bool.config_statusBarTapToExpandShade))
        kosmos.enableSingleShade()
        val view = createViewMock(view)
        controller = createAndInitController(view)
        val statusContainer = view.requireViewById<View>(R.id.system_icons)
        statusContainer.dispatchTouchEvent(
            getActionDownEventFromSource(InputDevice.SOURCE_TOUCHSCREEN)
        )
        statusContainer.dispatchTouchEvent(
            getActionUpEventFromSource(InputDevice.SOURCE_TOUCHSCREEN)
        )

        verify(shadeControllerImpl, never()).animateExpandShade()
    }

    @Test
    @EnableSceneContainer
    fun statusIconContainerIsHandlingTouchScreenTaps_dualShade_expandsQuickSettingsShade_flagOn() {
        Assume.assumeTrue(mContext.resources.getBoolean(R.bool.config_statusBarTapToExpandShade))
        kosmos.enableDualShade()
        val view = createViewMock(view)
        controller = createAndInitController(view)
        val endSideContainer = view.requireViewById<View>(R.id.system_icons)
        endSideContainer.dispatchTouchEvent(
            getActionDownEventFromSource(InputDevice.SOURCE_TOUCHSCREEN)
        )
        endSideContainer.dispatchTouchEvent(
            getActionUpEventFromSource(InputDevice.SOURCE_TOUCHSCREEN)
        )

        verify(shadeControllerImpl, never()).animateExpandShade()
        verify(shadeControllerImpl).animateExpandQs()
    }

    @Test
    @EnableSceneContainer
    fun statusIconContainerIsNotHandlingTouchScreenTaps_dualShade_flagOff() {
        Assume.assumeFalse(mContext.resources.getBoolean(R.bool.config_statusBarTapToExpandShade))
        kosmos.enableDualShade()
        val view = createViewMock(view)
        controller = createAndInitController(view)
        val endSideContainer = view.requireViewById<View>(R.id.system_icons)
        endSideContainer.dispatchTouchEvent(
            getActionDownEventFromSource(InputDevice.SOURCE_TOUCHSCREEN)
        )
        endSideContainer.dispatchTouchEvent(
            getActionUpEventFromSource(InputDevice.SOURCE_TOUCHSCREEN)
        )

        verify(shadeControllerImpl, never()).animateExpandQs()
    }

    private fun getActionDownEventFromSource(source: Int): MotionEvent {
        val ev = MotionEvent.obtain(0, 0, MotionEvent.ACTION_DOWN, 0f, 0f, 0)
        ev.source = source
        return ev
    }

    private fun getActionUpEventFromSource(source: Int): MotionEvent {