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

Commit b73b5518 authored by William Xiao's avatar William Xiao
Browse files

Zoom out notifications when transitioning to glanceable hub

This zoom was already implemented for keyguard UI, adding it to the
notifications on keyguard too.

Bug: 404535174
Fixes: 404535174
Test: atest SharedNotificationContainerViewModelTest
Flag: com.android.systemui.gesture_between_hub_and_lockscreen_motion
Change-Id: I9ec9e5b7f1a9d39756a22b56e590986b7c93a69b
parent f3af41ce
Loading
Loading
Loading
Loading
+77 −0
Original line number Diff line number Diff line
@@ -20,12 +20,15 @@ package com.android.systemui.statusbar.notification.stack.ui.viewmodel
import android.platform.test.annotations.EnableFlags
import android.platform.test.flag.junit.FlagsParameterization
import androidx.test.filters.SmallTest
import com.android.compose.animation.scene.ObservableTransitionState
import com.android.systemui.Flags.FLAG_GESTURE_BETWEEN_HUB_AND_LOCKSCREEN_MOTION
import com.android.systemui.Flags.FLAG_GLANCEABLE_HUB_V2
import com.android.systemui.Flags.FLAG_LOCKSCREEN_SHADE_TO_DREAM_TRANSITION_FIX
import com.android.systemui.SysuiTestCase
import com.android.systemui.bouncer.data.repository.keyguardBouncerRepository
import com.android.systemui.common.shared.model.NotificationContainerBounds
import com.android.systemui.common.ui.data.repository.fakeConfigurationRepository
import com.android.systemui.communal.data.repository.communalSceneRepository
import com.android.systemui.communal.domain.interactor.communalSceneInteractor
import com.android.systemui.communal.shared.model.CommunalScenes
import com.android.systemui.flags.BrokenWithSceneContainer
@@ -77,6 +80,7 @@ import com.android.systemui.shade.shadeTestUtil
import com.android.systemui.statusbar.notification.data.repository.activeNotificationListRepository
import com.android.systemui.statusbar.notification.data.repository.setActiveNotifs
import com.android.systemui.statusbar.notification.stack.domain.interactor.sharedNotificationContainerInteractor
import com.android.systemui.statusbar.notification.stack.ui.viewmodel.SharedNotificationContainerViewModel.Companion.PUSHBACK_SCALE
import com.android.systemui.statusbar.notification.stack.ui.viewmodel.SharedNotificationContainerViewModel.HorizontalPosition
import com.android.systemui.testKosmos
import com.android.systemui.window.ui.viewmodel.fakeBouncerTransitions
@@ -1569,6 +1573,79 @@ class SharedNotificationContainerViewModelTest(flags: FlagsParameterization) : S
            assertThat(blurRadius).isEqualTo(40.0f)
        }

    @Test
    @DisableSceneContainer
    @EnableFlags(FLAG_GESTURE_BETWEEN_HUB_AND_LOCKSCREEN_MOTION)
    fun glanceableHubViewScale_transitionFromLockscreenToHubAndBack() =
        kosmos.runTest {
            val scale by collectLastValue(underTest.viewScale)
            showLockscreen()
            assertThat(scale).isEqualTo(1.0f)

            val transitionState: MutableStateFlow<ObservableTransitionState> =
                MutableStateFlow(
                    ObservableTransitionState.Transition(
                        fromScene = CommunalScenes.Blank,
                        toScene = CommunalScenes.Communal,
                        currentScene = flowOf(CommunalScenes.Blank),
                        progress = flowOf(0f),
                        isInitiatedByUserInput = true,
                        isUserInputOngoing = flowOf(false),
                    )
                )

            // Start transition to communal.
            communalSceneRepository.setTransitionState(transitionState)

            // Transition to the glanceable hub and back.
            keyguardTransitionRepository.sendTransitionSteps(
                from = LOCKSCREEN,
                to = GLANCEABLE_HUB,
                testScope,
            )
            transitionState.value = ObservableTransitionState.Idle(CommunalScenes.Communal)

            assertThat(scale).isEqualTo(1 - PUSHBACK_SCALE)

            // Start transitioning back.
            keyguardTransitionRepository.sendTransitionSteps(
                from = GLANCEABLE_HUB,
                to = LOCKSCREEN,
                testScope,
            )
            transitionState.value = ObservableTransitionState.Idle(CommunalScenes.Blank)

            assertThat(scale).isEqualTo(1f)
        }

    @Test
    @DisableSceneContainer
    @EnableFlags(FLAG_GESTURE_BETWEEN_HUB_AND_LOCKSCREEN_MOTION)
    fun glanceableHubViewScale_reset_transitionedAwayFromHub() =
        kosmos.runTest {
            val scale by collectLastValue(underTest.viewScale)

            val transitionState: MutableStateFlow<ObservableTransitionState> =
                MutableStateFlow(ObservableTransitionState.Idle(CommunalScenes.Blank))

            // Transition to the glanceable hub and then to bouncer.
            keyguardTransitionRepository.sendTransitionSteps(
                from = LOCKSCREEN,
                to = GLANCEABLE_HUB,
                testScope,
            )
            transitionState.value = ObservableTransitionState.Idle(CommunalScenes.Communal)

            keyguardTransitionRepository.sendTransitionSteps(
                from = GLANCEABLE_HUB,
                to = PRIMARY_BOUNCER,
                testScope,
            )
            transitionState.value = ObservableTransitionState.Idle(CommunalScenes.Blank)

            assertThat(scale).isEqualTo(1f)
        }

    private suspend fun Kosmos.showLockscreen() {
        shadeTestUtil.setQsExpansion(0f)
        shadeTestUtil.setLockscreenShadeExpansion(0f)
+21 −17
Original line number Diff line number Diff line
@@ -59,9 +59,11 @@ import com.android.systemui.util.ui.AnimatableEvent
import com.android.systemui.util.ui.AnimatedValue
import com.android.systemui.util.ui.toAnimatedValueFlow
import com.android.systemui.util.ui.zip
import com.android.systemui.utils.coroutines.flow.flatMapLatestConflated
import java.util.Optional
import javax.inject.Inject
import kotlin.math.max
import kotlin.math.round
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.SharingStarted
@@ -235,26 +237,28 @@ constructor(
        if (!Flags.gestureBetweenHubAndLockscreenMotion()) {
                emptyFlow()
            } else {
            combine(
                    communalInteractor.isCommunalVisible,
                // Use flatMapLatestConflated so the animation flows aren't collected at all when
                // communal is not visible.
                communalInteractor.isCommunalVisible.flatMapLatestConflated { isCommunalVisible ->
                    if (!isCommunalVisible) {
                        // reset zoom out once we've exited the communal scene
                        flowOf(0f)
                    } else {
                        merge(
                                lockscreenToGlanceableHubTransitionViewModel.zoomOut,
                                glanceableHubToLockscreenTransitionViewModel.zoomOut,
                                aodToGlanceableHubTransitionViewModel.zoomOut,
                                glanceableHubToAodTransitionViewModel.zoomOut,
                    ),
                ) { isCommunalVisible, zoomOut ->
                    if (!isCommunalVisible) {
                        // reset zoom out once we've exited the communal scene
                        0f
                    } else {
                            )
                            .map {
                                // rate limit the zoom out by 5% step to avoid jank
                        ((zoomOut * 20).toInt() / 20f).coerceIn(0f, 1f)
                                (round(it * 20) / 20f).coerceIn(0f, 1f)
                            }
                    }
                }
            }
            .distinctUntilChanged()
            .dumpWhileCollecting("zoomOutFromGlanceableHub")
        }

    /** Last point that the root view was tapped */
    val lastRootViewTapPosition: Flow<Point?> =
+10 −1
Original line number Diff line number Diff line
@@ -18,7 +18,6 @@ package com.android.systemui.statusbar.notification.stack.ui.viewbinder

import androidx.lifecycle.Lifecycle
import androidx.lifecycle.repeatOnLifecycle
import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.systemui.Flags
import com.android.systemui.common.ui.view.onLayoutChanged
import com.android.systemui.communal.domain.interactor.CommunalSettingsInteractor
@@ -39,6 +38,7 @@ import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.DisposableHandle
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.launch

/** Binds the shared notification container to its view-model. */
@SysUISingleton
@@ -195,6 +195,15 @@ constructor(
                                controller.setMaxAlphaForGlanceableHub(it)
                            }
                        }

                        if (Flags.gestureBetweenHubAndLockscreenMotion()) {
                            launch {
                                viewModel.viewScale.collect {
                                    view.scaleX = it
                                    view.scaleY = it
                                }
                            }
                        }
                    }
                }
            }
+34 −0
Original line number Diff line number Diff line
@@ -25,6 +25,7 @@ import com.android.systemui.Flags.glanceableHubV2
import com.android.systemui.bouncer.domain.interactor.BouncerInteractor
import com.android.systemui.common.shared.model.NotificationContainerBounds
import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor
import com.android.systemui.communal.domain.interactor.CommunalInteractor
import com.android.systemui.communal.domain.interactor.CommunalSceneInteractor
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
@@ -100,8 +101,10 @@ import com.android.systemui.util.kotlin.FlowDumperImpl
import com.android.systemui.util.kotlin.Utils.Companion.sample as sampleCombine
import com.android.systemui.util.kotlin.sample
import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
import com.android.systemui.utils.coroutines.flow.flatMapLatestConflated
import dagger.Lazy
import javax.inject.Inject
import kotlin.math.round
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.channels.awaitClose
@@ -136,6 +139,7 @@ constructor(
    @Application applicationScope: CoroutineScope,
    @ShadeDisplayAware private val context: Context,
    @ShadeDisplayAware configurationInteractor: ConfigurationInteractor,
    communalInteractor: CommunalInteractor,
    private val keyguardInteractor: KeyguardInteractor,
    private val keyguardTransitionInteractor: KeyguardTransitionInteractor,
    private val shadeInteractor: ShadeInteractor,
@@ -737,6 +741,32 @@ constructor(
            .merge()
            .dumpWhileCollecting("blurRadius")

    /**
     * Flow of view scale values for the zoom animation between the lockscreen and glanceable hub.
     * 1.0f means no visual change to the view.
     */
    val viewScale: Flow<Float> =
        // Use flatMapLatestConflated so the animation flows aren't collected at all when communal
        // is not visible.
        communalInteractor.isCommunalVisible
            .flatMapLatestConflated { isCommunalVisible ->
                if (!isCommunalVisible) {
                    flowOf(1f)
                } else {
                    merge(
                            lockscreenToGlanceableHubTransitionViewModel.zoomOut,
                            glanceableHubToLockscreenTransitionViewModel.zoomOut,
                        )
                        .map {
                            // Rate limit the zoom out by 5% step to avoid jank.
                            val limited = (round(it * 20) / 20f).coerceIn(0f, 1f)
                            1 - limited * PUSHBACK_SCALE
                        }
                }
            }
            .distinctUntilChanged()
            .dumpWhileCollecting("viewScale")

    /**
     * Returns a flow of the expected alpha while running a LOCKSCREEN<->GLANCEABLE_HUB or
     * DREAMING<->GLANCEABLE_HUB transition or idle on the hub.
@@ -981,4 +1011,8 @@ constructor(
        /** The container is laid out from the given [ratio] of the screen width to the end edge. */
        data class MiddleToEdge(val ratio: Float = 0.5f) : HorizontalPosition
    }

    companion object {
        @VisibleForTesting const val PUSHBACK_SCALE = 0.05f
    }
}
+2 −0
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@ package com.android.systemui.statusbar.notification.stack.ui.viewmodel
import android.content.applicationContext
import com.android.systemui.bouncer.domain.interactor.bouncerInteractor
import com.android.systemui.common.ui.domain.interactor.configurationInteractor
import com.android.systemui.communal.domain.interactor.communalInteractor
import com.android.systemui.communal.domain.interactor.communalSceneInteractor
import com.android.systemui.dump.dumpManager
import com.android.systemui.keyguard.domain.interactor.keyguardInteractor
@@ -75,6 +76,7 @@ val Kosmos.sharedNotificationContainerViewModel by Fixture {
        applicationScope = applicationCoroutineScope,
        context = applicationContext,
        configurationInteractor = configurationInteractor,
        communalInteractor = communalInteractor,
        keyguardInteractor = keyguardInteractor,
        keyguardTransitionInteractor = keyguardTransitionInteractor,
        shadeInteractor = shadeInteractor,