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

Commit ea5d5fa9 authored by Danny Burakov's avatar Danny Burakov Committed by Android (Google) Code Review
Browse files

Merge changes from topic "lockscreen-notifications-content-key-agnostic" into main

* changes:
  [bc25] Make Lockscreen notifications content-key agnostic.
  [flexiglass] Apply user actions for the top content (overlay or scene).
parents 48f90a3b 52917fc9
Loading
Loading
Loading
Loading
+1 −3
Original line number Diff line number Diff line
@@ -73,9 +73,7 @@ constructor(
        val isShadeLayoutWide by viewModel.isShadeLayoutWide.collectAsStateWithLifecycle()
        val unfoldTranslations by viewModel.unfoldTranslations.collectAsStateWithLifecycle()
        val areNotificationsVisible by
            viewModel
                .areNotificationsVisible(contentKey)
                .collectAsStateWithLifecycle(initialValue = false)
            viewModel.areNotificationsVisible().collectAsStateWithLifecycle(initialValue = false)
        val isBypassEnabled by viewModel.isBypassEnabled.collectAsStateWithLifecycle()

        if (isBypassEnabled) {
+17 −8
Original line number Diff line number Diff line
@@ -84,7 +84,6 @@ fun SceneContainer(
            enableInterruptions = false,
        )
    }
    val currentSceneKey = state.transitionState.currentScene

    DisposableEffect(state) {
        val dataSource = SceneTransitionLayoutDataSource(state, coroutineScope)
@@ -97,19 +96,26 @@ fun SceneContainer(
        onDispose { viewModel.setTransitionState(null) }
    }

    val actionableContentKey =
        viewModel.getActionableContentKey(state.currentScene, state.currentOverlays, overlayByKey)
    val userActionsByContentKey: MutableMap<ContentKey, Map<UserAction, UserActionResult>> =
        remember {
            mutableStateMapOf()
        }
    // TODO(b/359173565): Add overlay user actions when the API is final.
    LaunchedEffect(currentSceneKey) {
    LaunchedEffect(actionableContentKey) {
        try {
            sceneByKey[currentSceneKey]?.userActions?.collectLatest { userActions ->
                userActionsByContentKey[currentSceneKey] =
            val actionableContent: ActionableContent =
                checkNotNull(
                    overlayByKey[actionableContentKey] ?: sceneByKey[actionableContentKey]
                ) {
                    "invalid ContentKey: $actionableContentKey"
                }
            actionableContent.userActions.collectLatest { userActions ->
                userActionsByContentKey[actionableContentKey] =
                    viewModel.resolveSceneFamilies(userActions)
            }
        } finally {
            userActionsByContentKey[currentSceneKey] = emptyMap()
            userActionsByContentKey[actionableContentKey] = emptyMap()
        }
    }

@@ -139,13 +145,16 @@ fun SceneContainer(
                    }
                }
            }
            overlayByKey.forEach { (overlayKey, composableOverlay) ->
            overlayByKey.forEach { (overlayKey, overlay) ->
                overlay(
                    key = overlayKey,
                    userActions = userActionsByContentKey.getOrDefault(overlayKey, emptyMap())
                ) {
                    // Activate the overlay.
                    LaunchedEffect(overlay) { overlay.activate() }

                    // Render the overlay.
                    with(composableOverlay) { this@overlay.Content(Modifier) }
                    with(overlay) { this@overlay.Content(Modifier) }
                }
            }
        }
+4 −36
Original line number Diff line number Diff line
@@ -128,8 +128,7 @@ class LockscreenContentViewModelTest(flags: FlagsParameterization) : SysuiTestCa
    fun areNotificationsVisible_splitShadeTrue_true() =
        with(kosmos) {
            testScope.runTest {
                val areNotificationsVisible by
                    collectLastValue(underTest.areNotificationsVisible(Scenes.Lockscreen))
                val areNotificationsVisible by collectLastValue(underTest.areNotificationsVisible())
                shadeRepository.setShadeLayoutWide(true)
                fakeKeyguardClockRepository.setClockSize(ClockSize.LARGE)

@@ -142,36 +141,7 @@ class LockscreenContentViewModelTest(flags: FlagsParameterization) : SysuiTestCa
    fun areNotificationsVisible_dualShadeWideOnLockscreen_true() =
        with(kosmos) {
            testScope.runTest {
                val areNotificationsVisible by
                    collectLastValue(underTest.areNotificationsVisible(Scenes.Lockscreen))
                shadeRepository.setShadeLayoutWide(true)
                fakeKeyguardClockRepository.setClockSize(ClockSize.LARGE)

                assertThat(areNotificationsVisible).isTrue()
            }
        }

    @Test
    @EnableFlags(DualShade.FLAG_NAME)
    fun areNotificationsVisible_dualShadeWideOnNotificationsShade_false() =
        with(kosmos) {
            testScope.runTest {
                val areNotificationsVisible by
                    collectLastValue(underTest.areNotificationsVisible(Scenes.NotificationsShade))
                shadeRepository.setShadeLayoutWide(true)
                fakeKeyguardClockRepository.setClockSize(ClockSize.LARGE)

                assertThat(areNotificationsVisible).isFalse()
            }
        }

    @Test
    @EnableFlags(DualShade.FLAG_NAME)
    fun areNotificationsVisible_dualShadeWideOnQuickSettingsShade_true() =
        with(kosmos) {
            testScope.runTest {
                val areNotificationsVisible by
                    collectLastValue(underTest.areNotificationsVisible(Scenes.QuickSettingsShade))
                val areNotificationsVisible by collectLastValue(underTest.areNotificationsVisible())
                shadeRepository.setShadeLayoutWide(true)
                fakeKeyguardClockRepository.setClockSize(ClockSize.LARGE)

@@ -184,8 +154,7 @@ class LockscreenContentViewModelTest(flags: FlagsParameterization) : SysuiTestCa
    fun areNotificationsVisible_withSmallClock_true() =
        with(kosmos) {
            testScope.runTest {
                val areNotificationsVisible by
                    collectLastValue(underTest.areNotificationsVisible(Scenes.Lockscreen))
                val areNotificationsVisible by collectLastValue(underTest.areNotificationsVisible())
                fakeKeyguardClockRepository.setClockSize(ClockSize.SMALL)
                assertThat(areNotificationsVisible).isTrue()
            }
@@ -196,8 +165,7 @@ class LockscreenContentViewModelTest(flags: FlagsParameterization) : SysuiTestCa
    fun areNotificationsVisible_withLargeClock_false() =
        with(kosmos) {
            testScope.runTest {
                val areNotificationsVisible by
                    collectLastValue(underTest.areNotificationsVisible(Scenes.Lockscreen))
                val areNotificationsVisible by collectLastValue(underTest.areNotificationsVisible())
                fakeKeyguardClockRepository.setClockSize(ClockSize.LARGE)
                assertThat(areNotificationsVisible).isFalse()
            }
+44 −0
Original line number Diff line number Diff line
@@ -31,8 +31,10 @@ import com.android.systemui.lifecycle.activateIn
import com.android.systemui.power.data.repository.fakePowerRepository
import com.android.systemui.power.domain.interactor.powerInteractor
import com.android.systemui.scene.domain.interactor.sceneInteractor
import com.android.systemui.scene.fakeOverlaysByKeys
import com.android.systemui.scene.sceneContainerConfig
import com.android.systemui.scene.shared.logger.sceneLogger
import com.android.systemui.scene.shared.model.Overlays
import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.scene.shared.model.fakeSceneDataSource
import com.android.systemui.testKosmos
@@ -243,4 +245,46 @@ class SceneContainerViewModelTest : SysuiTestCase() {

            assertThat(underTest.isVisible).isFalse()
        }

    @Test
    fun getActionableContentKey_noOverlays_returnsCurrentScene() =
        testScope.runTest {
            val currentScene by collectLastValue(underTest.currentScene)
            val currentOverlays by collectLastValue(sceneInteractor.currentOverlays)
            assertThat(currentScene).isEqualTo(Scenes.Lockscreen)
            assertThat(currentOverlays).isEmpty()

            val actionableContentKey =
                underTest.getActionableContentKey(
                    currentScene = checkNotNull(currentScene),
                    currentOverlays = checkNotNull(currentOverlays),
                    overlayByKey = kosmos.fakeOverlaysByKeys,
                )

            assertThat(actionableContentKey).isEqualTo(Scenes.Lockscreen)
        }

    @Test
    fun getActionableContentKey_multipleOverlays_returnsTopOverlay() =
        testScope.runTest {
            val currentScene by collectLastValue(underTest.currentScene)
            val currentOverlays by collectLastValue(sceneInteractor.currentOverlays)
            fakeSceneDataSource.showOverlay(Overlays.QuickSettingsShade)
            fakeSceneDataSource.showOverlay(Overlays.NotificationsShade)
            assertThat(currentScene).isEqualTo(Scenes.Lockscreen)
            assertThat(currentOverlays)
                .containsExactly(
                    Overlays.QuickSettingsShade,
                    Overlays.NotificationsShade,
                )

            val actionableContentKey =
                underTest.getActionableContentKey(
                    currentScene = checkNotNull(currentScene),
                    currentOverlays = checkNotNull(currentOverlays),
                    overlayByKey = kosmos.fakeOverlaysByKeys,
                )

            assertThat(actionableContentKey).isEqualTo(Overlays.QuickSettingsShade)
        }
}
+2 −14
Original line number Diff line number Diff line
@@ -17,7 +17,6 @@
package com.android.systemui.keyguard.ui.viewmodel

import android.content.res.Resources
import com.android.compose.animation.scene.ContentKey
import com.android.internal.annotations.VisibleForTesting
import com.android.systemui.biometrics.AuthController
import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor
@@ -27,7 +26,6 @@ import com.android.systemui.keyguard.shared.model.ClockSize
import com.android.systemui.lifecycle.ExclusiveActivatable
import com.android.systemui.res.R
import com.android.systemui.scene.domain.interactor.SceneContainerOcclusionInteractor
import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.shade.domain.interactor.ShadeInteractor
import com.android.systemui.unfold.domain.interactor.UnfoldTransitionInteractor
import dagger.assisted.AssistedFactory
@@ -42,7 +40,6 @@ import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
@@ -103,17 +100,8 @@ constructor(
        }
    }

    /**
     * Returns a flow that indicates whether lockscreen notifications should be rendered in the
     * given [contentKey].
     */
    fun areNotificationsVisible(contentKey: ContentKey): Flow<Boolean> {
        // `Scenes.NotificationsShade` renders its own separate notifications stack, so when it's
        // open we avoid rendering the lockscreen notifications stack.
        if (contentKey == Scenes.NotificationsShade) {
            return flowOf(false)
        }

    /** Returns a flow that indicates whether lockscreen notifications should be rendered. */
    fun areNotificationsVisible(): Flow<Boolean> {
        return combine(
            clockSize,
            shadeInteractor.isShadeLayoutWide,
Loading