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

Commit 202eb460 authored by William Xiao's avatar William Xiao Committed by Android (Google) Code Review
Browse files

Merge "Zoom out notifications when transitioning to glanceable hub" into main

parents cfd01f61 b73b5518
Loading
Loading
Loading
Loading
+77 −0
Original line number Diff line number Diff line
@@ -20,6 +20,8 @@ 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.Flags.FLAG_STATUS_BAR_FOR_DESKTOP
@@ -27,6 +29,7 @@ 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.desktop.domain.interactor.enableUsingDesktopStatusBar
@@ -79,6 +82,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
@@ -1618,6 +1622,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
@@ -236,26 +238,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")
        }

    private fun dozingToLockscreenAlpha(viewState: ViewStateAccessor) =
        alphaOnShadeExpansion
+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
@@ -30,6 +30,7 @@ import com.android.systemui.biometrics.Utils.getInsetsOf
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
@@ -106,8 +107,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
@@ -143,6 +146,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,
@@ -762,6 +766,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.
@@ -1009,4 +1039,8 @@ constructor(
         */
        data class MiddleToEdge(val maxWidth: Int = Int.MAX_VALUE) : 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,