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

Commit 92a2aa70 authored by Justin Weir's avatar Justin Weir Committed by Android (Google) Code Review
Browse files

Merge "Expansion amounts based on scene transitions" into main

parents 3869971d b666194c
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -42,7 +42,7 @@ interface ShadeRepository {

    /**
     * The amount the lockscreen shade has dragged down by the user, [0-1]. 0 means fully collapsed,
     * 1 means fully expanded.
     * 1 means fully expanded. Value resets to 0 when the user finishes dragging.
     */
    val lockscreenShadeExpansion: StateFlow<Float>

+66 −16
Original line number Diff line number Diff line
@@ -20,6 +20,10 @@ import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.keyguard.data.repository.KeyguardRepository
import com.android.systemui.keyguard.shared.model.StatusBarState
import com.android.systemui.scene.domain.interactor.SceneInteractor
import com.android.systemui.scene.shared.flag.SceneContainerFlags
import com.android.systemui.scene.shared.model.ObservableTransitionState
import com.android.systemui.scene.shared.model.SceneKey
import com.android.systemui.shade.data.repository.ShadeRepository
import com.android.systemui.statusbar.disableflags.data.repository.DisableFlagsRepository
import com.android.systemui.statusbar.notification.stack.domain.interactor.SharedNotificationContainerInteractor
@@ -28,22 +32,29 @@ import com.android.systemui.statusbar.policy.DeviceProvisionedController
import com.android.systemui.user.domain.interactor.UserInteractor
import com.android.systemui.util.kotlin.pairwise
import javax.inject.Inject
import javax.inject.Provider
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn

/** Business logic for shade interactions. */
@OptIn(ExperimentalCoroutinesApi::class)
@SysUISingleton
class ShadeInteractor
@Inject
constructor(
    @Application scope: CoroutineScope,
    disableFlagsRepository: DisableFlagsRepository,
    sceneContainerFlags: SceneContainerFlags,
    sceneInteractorProvider: Provider<SceneInteractor>,
    keyguardRepository: KeyguardRepository,
    userSetupRepository: UserSetupRepository,
    deviceProvisionedController: DeviceProvisionedController,
@@ -68,28 +79,45 @@ constructor(

    /** The amount [0-1] that the shade has been opened */
    val shadeExpansion: Flow<Float> =
        if (sceneContainerFlags.isEnabled()) {
            sceneBasedExpansion(sceneInteractorProvider.get(), SceneKey.Shade)
        } else {
            combine(
                    repository.lockscreenShadeExpansion,
                    keyguardRepository.statusBarState,
                    repository.legacyShadeExpansion,
                    repository.qsExpansion,
                    splitShadeEnabled
        ) { dragDownAmount, statusBarState, legacyShadeExpansion, qsExpansion, splitShadeEnabled ->
                ) {
                    lockscreenShadeExpansion,
                    statusBarState,
                    legacyShadeExpansion,
                    qsExpansion,
                    splitShadeEnabled ->
                    when (statusBarState) {
                        // legacyShadeExpansion is 1 instead of 0 when QS is expanded
                        StatusBarState.SHADE ->
                            if (!splitShadeEnabled && qsExpansion > 0f) 0f else legacyShadeExpansion
                StatusBarState.KEYGUARD -> dragDownAmount
                // This is required, as shadeExpansion gets reset to 0f even with the shade open
                        StatusBarState.KEYGUARD -> lockscreenShadeExpansion
                        // dragDownAmount, which drives lockscreenShadeExpansion resets to 0f when
                        // the pointer is lifted and the lockscreen shade is fully expanded
                        StatusBarState.SHADE_LOCKED -> 1f
                    }
                }
                .distinctUntilChanged()
        }

    /**
     * The amount [0-1] QS has been opened. Normal shade with notifications (QQS) visible will
     * report 0f.
     */
    val qsExpansion: StateFlow<Float> = repository.qsExpansion
    val qsExpansion: StateFlow<Float> =
        if (sceneContainerFlags.isEnabled()) {
            sceneBasedExpansion(sceneInteractorProvider.get(), SceneKey.QuickSettings)
                .stateIn(scope, SharingStarted.Eagerly, 0f)
        } else {
            repository.qsExpansion
        }

    /** The amount [0-1] either QS or the shade has been opened */
    val anyExpansion: StateFlow<Float> =
@@ -119,4 +147,26 @@ constructor(
                disableFlags.isQuickSettingsEnabled() &&
                !isDozing
        }

    fun sceneBasedExpansion(sceneInteractor: SceneInteractor, sceneKey: SceneKey) =
        sceneInteractor.transitionState
            .flatMapLatest { state ->
                when (state) {
                    is ObservableTransitionState.Idle ->
                        if (state.scene == sceneKey) {
                            flowOf(1f)
                        } else {
                            flowOf(0f)
                        }
                    is ObservableTransitionState.Transition ->
                        if (state.toScene == sceneKey) {
                            state.progress
                        } else if (state.fromScene == sceneKey) {
                            state.progress.map { progress -> 1 - progress }
                        } else {
                            flowOf(0f)
                        }
                }
            }
            .distinctUntilChanged()
}
+9 −4
Original line number Diff line number Diff line
@@ -28,6 +28,8 @@ import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.scene.SceneTestUtils
import com.android.systemui.scene.shared.flag.FakeSceneContainerFlags
import com.android.systemui.shade.data.repository.FakeShadeRepository
import com.android.systemui.shade.domain.interactor.ShadeInteractor
import com.android.systemui.statusbar.disableflags.data.repository.FakeDisableFlagsRepository
@@ -42,8 +44,6 @@ import com.android.systemui.user.domain.interactor.HeadlessSystemUserMode
import com.android.systemui.user.domain.interactor.RefreshUsersScheduler
import com.android.systemui.user.domain.interactor.UserInteractor
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.StandardTestDispatcher
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Before
@@ -56,12 +56,15 @@ import org.mockito.MockitoAnnotations
@SmallTest
@OptIn(ExperimentalCoroutinesApi::class)
class AuthDialogPanelInteractionDetectorTest : SysuiTestCase() {
    private val utils = SceneTestUtils(this)
    private val testScope = utils.testScope
    private val testDispatcher = utils.testDispatcher
    private val disableFlagsRepository = FakeDisableFlagsRepository()
    private val featureFlags = FakeFeatureFlags()
    private val keyguardRepository = FakeKeyguardRepository()
    private val shadeRepository = FakeShadeRepository()
    private val testDispatcher = StandardTestDispatcher()
    private val testScope = TestScope(testDispatcher)
    private val sceneContainerFlags = FakeSceneContainerFlags()
    private val sceneInteractor = utils.sceneInteractor()
    private val userSetupRepository = FakeUserSetupRepository()
    private val userRepository = FakeUserRepository()
    private val configurationRepository = FakeConfigurationRepository()
@@ -126,6 +129,8 @@ class AuthDialogPanelInteractionDetectorTest : SysuiTestCase() {
            ShadeInteractor(
                testScope.backgroundScope,
                disableFlagsRepository,
                sceneContainerFlags,
                { sceneInteractor },
                keyguardRepository,
                userSetupRepository,
                deviceProvisionedController,
+6 −2
Original line number Diff line number Diff line
@@ -34,7 +34,6 @@ import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.UiEventLogger;
import com.android.keyguard.KeyguardStatusView;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.keyguard.TestScopeProvider;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository;
@@ -48,6 +47,8 @@ import com.android.systemui.media.controls.ui.MediaHierarchyManager;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.qs.QS;
import com.android.systemui.qs.QSFragment;
import com.android.systemui.scene.SceneTestUtils;
import com.android.systemui.scene.shared.flag.FakeSceneContainerFlags;
import com.android.systemui.screenrecord.RecordingController;
import com.android.systemui.shade.data.repository.FakeShadeRepository;
import com.android.systemui.shade.domain.interactor.ShadeInteractor;
@@ -99,7 +100,8 @@ public class QuickSettingsControllerBaseTest extends SysuiTestCase {

    protected QuickSettingsController mQsController;

    protected TestScope mTestScope = TestScopeProvider.getTestScope();
    protected SceneTestUtils mUtils = new SceneTestUtils(this);
    protected TestScope mTestScope = mUtils.getTestScope();

    @Mock
    protected Resources mResources;
@@ -172,6 +174,8 @@ public class QuickSettingsControllerBaseTest extends SysuiTestCase {
                new ShadeInteractor(
                        mTestScope.getBackgroundScope(),
                        mDisableFlagsRepository,
                        new FakeSceneContainerFlags(),
                        () -> mUtils.sceneInteractor(),
                        mKeyguardRepository,
                        new FakeUserSetupRepository(),
                        mDeviceProvisionedController,
+155 −6
Original line number Diff line number Diff line
@@ -35,6 +35,10 @@ import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory
import com.android.systemui.keyguard.shared.model.StatusBarState
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.scene.SceneTestUtils
import com.android.systemui.scene.shared.flag.FakeSceneContainerFlags
import com.android.systemui.scene.shared.model.ObservableTransitionState
import com.android.systemui.scene.shared.model.SceneKey
import com.android.systemui.shade.domain.interactor.ShadeInteractor
import com.android.systemui.statusbar.disableflags.data.model.DisableFlagsModel
import com.android.systemui.statusbar.disableflags.data.repository.FakeDisableFlagsRepository
@@ -52,9 +56,8 @@ import com.android.systemui.user.domain.interactor.UserInteractor
import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.test.StandardTestDispatcher
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Before
@@ -67,9 +70,11 @@ import org.mockito.MockitoAnnotations
class ShadeInteractorTest : SysuiTestCase() {
    private lateinit var underTest: ShadeInteractor

    private val testDispatcher = StandardTestDispatcher()
    private val testScope = TestScope(testDispatcher)
    private val utils = SceneTestUtils(this)
    private val testScope = utils.testScope
    private val featureFlags = FakeFeatureFlags()
    private val sceneContainerFlags = FakeSceneContainerFlags()
    private val sceneInteractor = utils.sceneInteractor()
    private val userSetupRepository = FakeUserSetupRepository()
    private val userRepository = FakeUserRepository()
    private val disableFlagsRepository = FakeDisableFlagsRepository()
@@ -103,7 +108,7 @@ class ShadeInteractorTest : SysuiTestCase() {
        val refreshUsersScheduler =
            RefreshUsersScheduler(
                applicationScope = testScope.backgroundScope,
                mainDispatcher = testDispatcher,
                mainDispatcher = utils.testDispatcher,
                repository = userRepository,
            )

@@ -141,7 +146,7 @@ class ShadeInteractorTest : SysuiTestCase() {
                    ),
                broadcastDispatcher = fakeBroadcastDispatcher,
                keyguardUpdateMonitor = keyguardUpdateMonitor,
                backgroundDispatcher = testDispatcher,
                backgroundDispatcher = utils.testDispatcher,
                activityManager = activityManager,
                refreshUsersScheduler = refreshUsersScheduler,
                guestUserInteractor = guestInteractor,
@@ -151,6 +156,8 @@ class ShadeInteractorTest : SysuiTestCase() {
            ShadeInteractor(
                testScope.backgroundScope,
                disableFlagsRepository,
                sceneContainerFlags,
                { sceneInteractor },
                keyguardRepository,
                userSetupRepository,
                deviceProvisionedController,
@@ -557,4 +564,146 @@ class ShadeInteractorTest : SysuiTestCase() {
            // THEN anyExpanding is false
            assertThat(actual).isFalse()
        }

    @Test
    fun lockscreenShadeExpansion_idle_onScene() =
        testScope.runTest() {
            // GIVEN an expansion flow based on transitions to and from a scene
            val key = SceneKey.Shade
            val expansion = underTest.sceneBasedExpansion(sceneInteractor, key)
            val expansionAmount by collectLastValue(expansion)

            // WHEN transition state is idle on the scene
            val transitionState =
                MutableStateFlow<ObservableTransitionState>(ObservableTransitionState.Idle(key))
            sceneInteractor.setTransitionState(transitionState)

            // THEN expansion is 1
            assertThat(expansionAmount).isEqualTo(1f)
        }

    @Test
    fun lockscreenShadeExpansion_idle_onDifferentScene() =
        testScope.runTest() {
            // GIVEN an expansion flow based on transitions to and from a scene
            val expansion = underTest.sceneBasedExpansion(sceneInteractor, SceneKey.Shade)
            val expansionAmount by collectLastValue(expansion)

            // WHEN transition state is idle on a different scene
            val transitionState =
                MutableStateFlow<ObservableTransitionState>(
                    ObservableTransitionState.Idle(SceneKey.Lockscreen)
                )
            sceneInteractor.setTransitionState(transitionState)

            // THEN expansion is 0
            assertThat(expansionAmount).isEqualTo(0f)
        }

    @Test
    fun lockscreenShadeExpansion_transitioning_toScene() =
        testScope.runTest() {
            // GIVEN an expansion flow based on transitions to and from a scene
            val key = SceneKey.QuickSettings
            val expansion = underTest.sceneBasedExpansion(sceneInteractor, key)
            val expansionAmount by collectLastValue(expansion)

            // WHEN transition state is starting to move to the scene
            val progress = MutableStateFlow(0f)
            val transitionState =
                MutableStateFlow<ObservableTransitionState>(
                    ObservableTransitionState.Transition(
                        fromScene = SceneKey.Lockscreen,
                        toScene = key,
                        progress = progress,
                    )
                )
            sceneInteractor.setTransitionState(transitionState)

            // THEN expansion is 0
            assertThat(expansionAmount).isEqualTo(0f)

            // WHEN transition state is partially to the scene
            progress.value = .4f

            // THEN expansion matches the progress
            assertThat(expansionAmount).isEqualTo(.4f)

            // WHEN transition completes
            progress.value = 1f

            // THEN expansion is 1
            assertThat(expansionAmount).isEqualTo(1f)
        }

    @Test
    fun lockscreenShadeExpansion_transitioning_fromScene() =
        testScope.runTest() {
            // GIVEN an expansion flow based on transitions to and from a scene
            val key = SceneKey.QuickSettings
            val expansion = underTest.sceneBasedExpansion(sceneInteractor, key)
            val expansionAmount by collectLastValue(expansion)

            // WHEN transition state is starting to move to the scene
            val progress = MutableStateFlow(0f)
            val transitionState =
                MutableStateFlow<ObservableTransitionState>(
                    ObservableTransitionState.Transition(
                        fromScene = key,
                        toScene = SceneKey.Lockscreen,
                        progress = progress,
                    )
                )
            sceneInteractor.setTransitionState(transitionState)

            // THEN expansion is 1
            assertThat(expansionAmount).isEqualTo(1f)

            // WHEN transition state is partially to the scene
            progress.value = .4f

            // THEN expansion reflects the progress
            assertThat(expansionAmount).isEqualTo(.6f)

            // WHEN transition completes
            progress.value = 1f

            // THEN expansion is 0
            assertThat(expansionAmount).isEqualTo(0f)
        }

    @Test
    fun lockscreenShadeExpansion_transitioning_toAndFromDifferentScenes() =
        testScope.runTest() {
            // GIVEN an expansion flow based on transitions to and from a scene
            val expansion = underTest.sceneBasedExpansion(sceneInteractor, SceneKey.QuickSettings)
            val expansionAmount by collectLastValue(expansion)

            // WHEN transition state is starting to between different scenes
            val progress = MutableStateFlow(0f)
            val transitionState =
                MutableStateFlow<ObservableTransitionState>(
                    ObservableTransitionState.Transition(
                        fromScene = SceneKey.Lockscreen,
                        toScene = SceneKey.Shade,
                        progress = progress,
                    )
                )
            sceneInteractor.setTransitionState(transitionState)

            // THEN expansion is 0
            assertThat(expansionAmount).isEqualTo(0f)

            // WHEN transition state is partially complete
            progress.value = .4f

            // THEN expansion is still 0
            assertThat(expansionAmount).isEqualTo(0f)

            // WHEN transition completes
            progress.value = 1f

            // THEN expansion is still 0
            assertThat(expansionAmount).isEqualTo(0f)
        }
}
Loading