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

Commit afaaf340 authored by Caitlin Shkuratov's avatar Caitlin Shkuratov Committed by Android (Google) Code Review
Browse files

Merge "[SB] In fullscreen mode, always auto-hide status bar after 2 seconds." into main

parents 3dc7bbbc ef40a94d
Loading
Loading
Loading
Loading
+10 −0
Original line number Diff line number Diff line
@@ -453,6 +453,16 @@ flag {
    }
}

flag {
    name: "status_bar_always_schedule_auto_hide"
    namespace: "systemui"
    description: "Schedule the status bar auto-hide functionality more often for fullscreen mode"
    bug: "428659575"
    metadata {
      purpose: PURPOSE_BUGFIX
    }
}

flag {
    name: "status_bar_app_handle_tracking"
    namespace: "systemui"
+137 −1
Original line number Diff line number Diff line
@@ -16,13 +16,16 @@

package com.android.systemui.statusbar.core

import android.platform.test.annotations.DisableFlags
import android.platform.test.annotations.EnableFlags
import android.view.View
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.Flags
import com.android.systemui.SysuiTestCase
import com.android.systemui.bouncer.data.repository.fakeKeyguardBouncerRepository
import com.android.systemui.dump.dumpManager
import com.android.systemui.kosmos.runTest
import com.android.systemui.kosmos.testScope
import com.android.systemui.kosmos.useUnconfinedTestDispatcher
import com.android.systemui.plugins.DarkIconDispatcher
@@ -36,8 +39,11 @@ import com.android.systemui.statusbar.data.model.StatusBarMode
import com.android.systemui.statusbar.data.model.StatusBarMode.LIGHTS_OUT
import com.android.systemui.statusbar.data.model.StatusBarMode.LIGHTS_OUT_TRANSPARENT
import com.android.systemui.statusbar.data.model.StatusBarMode.OPAQUE
import com.android.systemui.statusbar.data.model.StatusBarMode.SEMI_TRANSPARENT
import com.android.systemui.statusbar.data.model.StatusBarMode.TRANSPARENT
import com.android.systemui.statusbar.data.repository.fakeStatusBarModePerDisplayRepository
import com.android.systemui.statusbar.phone.PhoneStatusBarTransitions
import com.android.systemui.statusbar.phone.mockAutoHideController
import com.android.systemui.statusbar.phone.ui.statusBarIconController
import com.android.systemui.statusbar.policy.fakeConfigurationController
import com.android.systemui.statusbar.window.data.repository.fakeStatusBarWindowStatePerDisplayRepository
@@ -50,7 +56,9 @@ import kotlinx.coroutines.test.runTest
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.kotlin.any
import org.mockito.kotlin.mock
import org.mockito.kotlin.never
import org.mockito.kotlin.reset
import org.mockito.kotlin.times
import org.mockito.kotlin.verify

@@ -72,7 +80,6 @@ class StatusBarOrchestratorTest : SysuiTestCase() {
    private val fakeStatusBarWindowController = kosmos.fakeStatusBarWindowController
    private val fakeStatusBarInitializer = kosmos.fakeStatusBarInitializer
    private val dumpManager = kosmos.dumpManager
    private val statusBarIconRefreshInteractor = kosmos.statusBarIconRefreshInteractor

    private val orchestrator = kosmos.statusBarOrchestrator

@@ -141,6 +148,135 @@ class StatusBarOrchestratorTest : SysuiTestCase() {
            verify(fakeStatusBarInitializer.statusBarTransitions, never()).finishAnimations()
        }

    @Test
    @DisableFlags(Flags.FLAG_STATUS_BAR_ALWAYS_SCHEDULE_AUTO_HIDE)
    fun autoHide_invokedWhenBarModeChanges_flagOff() =
        kosmos.runTest {
            setStatusBarMode(TRANSPARENT)
            orchestrator.start()
            reset(mockAutoHideController)

            setStatusBarMode(SEMI_TRANSPARENT)

            verify(mockAutoHideController).touchAutoHide()

            reset(mockAutoHideController)
            setStatusBarMode(OPAQUE)

            verify(mockAutoHideController).touchAutoHide()
        }

    @Test
    @EnableFlags(Flags.FLAG_STATUS_BAR_ALWAYS_SCHEDULE_AUTO_HIDE)
    fun autoHide_invokedWhenBarModeChanges_flagOn() =
        kosmos.runTest {
            setStatusBarMode(TRANSPARENT)
            orchestrator.start()
            reset(mockAutoHideController)

            setStatusBarMode(SEMI_TRANSPARENT)

            verify(mockAutoHideController).touchAutoHide()

            reset(mockAutoHideController)
            setStatusBarMode(OPAQUE)

            verify(mockAutoHideController).touchAutoHide()
        }

    @Test
    @DisableFlags(Flags.FLAG_STATUS_BAR_ALWAYS_SCHEDULE_AUTO_HIDE)
    fun autoHide_invokedWhenTransitionsChanges_flagOff() =
        kosmos.runTest {
            setStatusBarMode(TRANSPARENT)
            orchestrator.start()
            reset(mockAutoHideController)

            fakeStatusBarInitializer.setNewTransitions(mock<PhoneStatusBarTransitions>())

            verify(mockAutoHideController).touchAutoHide()
        }

    @Test
    @EnableFlags(Flags.FLAG_STATUS_BAR_ALWAYS_SCHEDULE_AUTO_HIDE)
    fun autoHide_invokedWhenTransitionsChanges_flagOn() =
        kosmos.runTest {
            setStatusBarMode(TRANSPARENT)
            orchestrator.start()
            reset(mockAutoHideController)

            fakeStatusBarInitializer.setNewTransitions(mock<PhoneStatusBarTransitions>())

            verify(mockAutoHideController).touchAutoHide()
        }

    @Test
    @DisableFlags(Flags.FLAG_STATUS_BAR_ALWAYS_SCHEDULE_AUTO_HIDE)
    fun autoHide_notInvokedWhenAnimateChanges_flagOff() =
        kosmos.runTest {
            setStatusBarMode(TRANSPARENT)
            setStatusBarWindowState(StatusBarWindowState.Showing)
            orchestrator.start()
            reset(mockAutoHideController)

            // Changing the window state will affect the `shouldAnimateNextBarModeChange` value
            setStatusBarWindowState(StatusBarWindowState.Hidden)

            verify(mockAutoHideController, never()).touchAutoHide()
        }

    @Test
    @EnableFlags(Flags.FLAG_STATUS_BAR_ALWAYS_SCHEDULE_AUTO_HIDE)
    fun autoHide_notInvokedWhenAnimateChanges_flagOn() =
        kosmos.runTest {
            setStatusBarMode(TRANSPARENT)
            setStatusBarWindowState(StatusBarWindowState.Showing)
            orchestrator.start()
            reset(mockAutoHideController)

            // Changing the window state will affect the `shouldAnimateNextBarModeChange` value
            setStatusBarWindowState(StatusBarWindowState.Hidden)

            verify(mockAutoHideController, never()).touchAutoHide()
        }

    @Test
    @DisableFlags(Flags.FLAG_STATUS_BAR_ALWAYS_SCHEDULE_AUTO_HIDE)
    fun autoHide_flagOff_notInvokedWhenTransientShownStateChanges() =
        kosmos.runTest {
            setStatusBarMode(TRANSPARENT)
            orchestrator.start()
            reset(mockAutoHideController)

            setTransientStatusBar()

            verify(mockAutoHideController, never()).touchAutoHide()

            reset(mockAutoHideController)
            abortTransientStatusBar()

            verify(mockAutoHideController, never()).touchAutoHide()
        }

    /** Regression test for b/428659575. */
    @Test
    @EnableFlags(Flags.FLAG_STATUS_BAR_ALWAYS_SCHEDULE_AUTO_HIDE)
    fun autoHide_flagOn_invokedWhenTransientShownStateChanges() =
        kosmos.runTest {
            setStatusBarMode(TRANSPARENT)
            orchestrator.start()
            reset(mockAutoHideController)

            setTransientStatusBar()

            verify(mockAutoHideController).touchAutoHide()

            reset(mockAutoHideController)
            abortTransientStatusBar()

            verify(mockAutoHideController).touchAutoHide()
        }

    @Test
    fun statusBarVisible_notifiesBubbles() =
        testScope.runTest {
+37 −14
Original line number Diff line number Diff line
@@ -20,6 +20,7 @@ import android.view.Display
import android.view.View
import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.systemui.Dumpable
import com.android.systemui.Flags
import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.demomode.DemoModeController
@@ -133,17 +134,33 @@ constructor(
                statusBarWindowState != StatusBarWindowState.Hidden
        }

    private val barModeUpdate =
    private data class BarModeAppearance(
        val animate: Boolean,
        val barTransitions: PhoneStatusBarTransitions,
        val statusBarMode: StatusBarMode,
        val isTransientShown: Boolean,
    )

    private val barModeAppearance =
        combine(
            shouldAnimateNextBarModeChange,
            phoneStatusBarTransitions.filterNotNull(),
            statusBarModeRepository.statusBarMode,
                ::Triple,
            statusBarModeRepository.isTransientShown,
            ::BarModeAppearance,
        )
            .distinctUntilChangedBy { (_, barTransitions, statusBarMode) ->
                // We only want to collect when either bar transitions or status bar mode
                // changed.
                Pair(barTransitions, statusBarMode)

    private val barModeUpdate =
        barModeAppearance.distinctUntilChangedBy {
            // We only want to collect when either bar transitions or status bar mode changed.
            Pair(it.barTransitions, it.statusBarMode)
        }

    private val autoHideUpdate =
        barModeAppearance.distinctUntilChangedBy {
            // Update auto-hide whenever `isTransientShown` changes so that we always hide the
            // transient status bar even if `statusBarMode` hasn't changed. See b/428659575.
            Triple(it.barTransitions, it.statusBarMode, it.isTransientShown)
        }

    /** Starts status bar orchestration. To be called when status bar is created. */
@@ -168,9 +185,12 @@ constructor(
                    }
                    launch { statusBarVisible.collect { updateBubblesVisibility(it) } }
                    launch {
                        barModeUpdate.collect { (animate, barTransitions, statusBarMode) ->
                            updateBarMode(animate, barTransitions, statusBarMode)
                        barModeUpdate.collect {
                            updateBarMode(it.animate, it.barTransitions, it.statusBarMode)
                        }
                    }
                    if (Flags.statusBarAlwaysScheduleAutoHide()) {
                        launch { autoHideUpdate.collect { autoHideController.touchAutoHide() } }
                    }
                }
        createAndAddWindow()
@@ -238,8 +258,11 @@ constructor(
        if (!demoModeController.isInDemoMode) {
            barTransitions.transitionTo(barMode.toTransitionModeInt(), animate)
        }

        if (!Flags.statusBarAlwaysScheduleAutoHide()) {
            autoHideController.touchAutoHide()
        }
    }

    private fun updateBubblesVisibility(statusBarVisible: Boolean) {
        if (displayId != Display.DEFAULT_DISPLAY) {
+9 −1
Original line number Diff line number Diff line
@@ -24,7 +24,7 @@ import org.mockito.kotlin.mock
class FakeStatusBarInitializer : StatusBarInitializer {

    val statusBarViewController = mock<PhoneStatusBarViewController>()
    val statusBarTransitions = mock<PhoneStatusBarTransitions>()
    var statusBarTransitions = mock<PhoneStatusBarTransitions>()

    var startedByCoreStartable: Boolean = false
        private set
@@ -47,4 +47,12 @@ class FakeStatusBarInitializer : StatusBarInitializer {
    override fun start() {
        startedByCoreStartable = true
    }

    fun setNewTransitions(transitions: PhoneStatusBarTransitions) {
        statusBarTransitions = transitions
        statusBarViewUpdatedListener?.onStatusBarViewUpdated(
            statusBarViewController,
            statusBarTransitions,
        )
    }
}