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

Commit 3dd233d2 authored by Matt Pietal's avatar Matt Pietal
Browse files

Ambient AOD support

Enables transparency on the light reveal scrim, if supported by
the current wallpaper. However, while transitioning from
GONE->AOD, always be fully opaque as the underlying surface is
still visible until the device is fully in AOD.

Bug: 373844670
Test: atest LightRevealScrimInteractorTest
Flag: com.android.systemui.shared.ambient_aod
Change-Id: Iaec877432176d5436d284c00e09c7f985927eeab
parent 828f0eb3
Loading
Loading
Loading
Loading
+18 −8
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@ package com.android.systemui.keyguard.domain.interactor
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.coroutines.collectValues
import com.android.systemui.keyguard.data.fakeLightRevealScrimRepository
import com.android.systemui.keyguard.data.repository.DEFAULT_REVEAL_DURATION
@@ -31,7 +32,6 @@ import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.keyguard.shared.model.TransitionStep
import com.android.systemui.kosmos.testScope
import com.android.systemui.power.data.repository.fakePowerRepository
import com.android.systemui.power.domain.interactor.powerInteractor
import com.android.systemui.power.shared.model.WakeSleepReason
import com.android.systemui.power.shared.model.WakefulnessState
import com.android.systemui.statusbar.LightRevealEffect
@@ -98,7 +98,7 @@ class LightRevealScrimInteractorTest : SysuiTestCase() {
            fakeKeyguardTransitionRepository.sendTransitionStep(
                TransitionStep(
                    to = KeyguardState.LOCKSCREEN,
                    transitionState = TransitionState.RUNNING
                    transitionState = TransitionState.RUNNING,
                )
            )
            runCurrent()
@@ -110,7 +110,7 @@ class LightRevealScrimInteractorTest : SysuiTestCase() {
            fakeKeyguardTransitionRepository.sendTransitionStep(
                TransitionStep(
                    to = KeyguardState.LOCKSCREEN,
                    transitionState = TransitionState.STARTED
                    transitionState = TransitionState.STARTED,
                )
            )
            runCurrent()
@@ -147,19 +147,29 @@ class LightRevealScrimInteractorTest : SysuiTestCase() {

            assertThat(fakeLightRevealScrimRepository.revealAnimatorRequests.last())
                .isEqualTo(
                    RevealAnimatorRequest(
                        reveal = false,
                        duration = DEFAULT_REVEAL_DURATION
                    )
                    RevealAnimatorRequest(reveal = false, duration = DEFAULT_REVEAL_DURATION)
                )
        }

    @Test
    fun supportsAmbientMode() =
        kosmos.testScope.runTest {
            val maxAlpha by collectLastValue(underTest.maxAlpha)
            assertThat(maxAlpha).isEqualTo(1f)

            underTest.setWallpaperSupportsAmbientMode(true)
            assertThat(maxAlpha).isLessThan(1f)

            underTest.setWallpaperSupportsAmbientMode(false)
            assertThat(maxAlpha).isEqualTo(1f)
        }

    private fun updateWakefulness(goToSleepReason: WakeSleepReason) {
        fakePowerRepository.updateWakefulness(
            rawState = WakefulnessState.STARTING_TO_SLEEP,
            lastWakeReason = WakeSleepReason.POWER_BUTTON,
            lastSleepReason = goToSleepReason,
            powerButtonLaunchGestureTriggered = false
            powerButtonLaunchGestureTriggered = false,
        )
    }
}
+16 −0
Original line number Diff line number Diff line
@@ -32,6 +32,7 @@ import com.android.keyguard.KeyguardStatusView
import com.android.keyguard.KeyguardStatusViewController
import com.android.keyguard.dagger.KeyguardStatusViewComponent
import com.android.systemui.CoreStartable
import com.android.systemui.Flags.lightRevealMigration
import com.android.systemui.biometrics.ui.binder.DeviceEntryUnlockTrackerViewBinder
import com.android.systemui.common.ui.ConfigurationState
import com.android.systemui.dagger.SysUISingleton
@@ -42,6 +43,7 @@ import com.android.systemui.keyguard.shared.model.LockscreenSceneBlueprint
import com.android.systemui.keyguard.ui.binder.KeyguardBlueprintViewBinder
import com.android.systemui.keyguard.ui.binder.KeyguardIndicationAreaBinder
import com.android.systemui.keyguard.ui.binder.KeyguardRootViewBinder
import com.android.systemui.keyguard.ui.binder.LightRevealScrimViewBinder
import com.android.systemui.keyguard.ui.composable.LockscreenContent
import com.android.systemui.keyguard.ui.composable.blueprint.ComposableLockscreenSceneBlueprint
import com.android.systemui.keyguard.ui.view.KeyguardIndicationArea
@@ -51,6 +53,7 @@ import com.android.systemui.keyguard.ui.viewmodel.KeyguardClockViewModel
import com.android.systemui.keyguard.ui.viewmodel.KeyguardIndicationAreaViewModel
import com.android.systemui.keyguard.ui.viewmodel.KeyguardRootViewModel
import com.android.systemui.keyguard.ui.viewmodel.KeyguardSmartspaceViewModel
import com.android.systemui.keyguard.ui.viewmodel.LightRevealScrimViewModel
import com.android.systemui.keyguard.ui.viewmodel.LockscreenContentViewModel
import com.android.systemui.keyguard.ui.viewmodel.OccludingAppDeviceEntryMessageViewModel
import com.android.systemui.plugins.FalsingManager
@@ -59,11 +62,13 @@ import com.android.systemui.scene.shared.flag.SceneContainerFlag
import com.android.systemui.shade.NotificationShadeWindowView
import com.android.systemui.shade.domain.interactor.ShadeInteractor
import com.android.systemui.statusbar.KeyguardIndicationController
import com.android.systemui.statusbar.LightRevealScrim
import com.android.systemui.statusbar.VibratorHelper
import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationLockscreenScrimViewModel
import com.android.systemui.statusbar.phone.ScreenOffAnimationController
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager
import com.android.systemui.temporarydisplay.chipbar.ChipbarCoordinator
import com.android.systemui.wallpapers.ui.viewmodel.WallpaperViewModel
import com.google.android.msdl.domain.MSDLPlayer
import dagger.Lazy
import java.util.Optional
@@ -105,6 +110,9 @@ constructor(
    private val keyguardViewMediator: KeyguardViewMediator,
    private val deviceEntryUnlockTrackerViewBinder: Optional<DeviceEntryUnlockTrackerViewBinder>,
    private val statusBarKeyguardViewManager: StatusBarKeyguardViewManager,
    private val lightRevealScrimViewModel: LightRevealScrimViewModel,
    private val lightRevealScrim: LightRevealScrim,
    private val wallpaperViewModel: WallpaperViewModel,
    @Main private val mainDispatcher: CoroutineDispatcher,
    private val msdlPlayer: MSDLPlayer,
) : CoreStartable {
@@ -133,6 +141,14 @@ constructor(
        bindKeyguardRootView()
        initializeViews()

        if (lightRevealMigration()) {
            LightRevealScrimViewBinder.bind(
                lightRevealScrim,
                lightRevealScrimViewModel,
                wallpaperViewModel,
            )
        }

        if (!SceneContainerFlag.isEnabled) {
            KeyguardBlueprintViewBinder.bind(
                keyguardRootView,
+7 −0
Original line number Diff line number Diff line
@@ -39,6 +39,7 @@ import kotlin.math.max
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.callbackFlow
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.flatMapLatest
@@ -65,6 +66,9 @@ interface LightRevealScrimRepository {

    val isAnimating: Boolean

    /** Limit the max alpha for the scrim to allow for some transparency */
    val maxAlpha: MutableStateFlow<Float>

    fun startRevealAmountAnimator(reveal: Boolean, duration: Long = DEFAULT_REVEAL_DURATION)
}

@@ -79,6 +83,7 @@ constructor(
) : LightRevealScrimRepository {
    companion object {
        val TAG = LightRevealScrimRepository::class.simpleName!!
        val DEFAULT_MAX_ALPHA = 1f
    }

    /** The reveal effect used if the device was locked/unlocked via the power button. */
@@ -127,6 +132,8 @@ constructor(

    private val revealAmountAnimator = ValueAnimator.ofFloat(0f, 1f)

    override val maxAlpha: MutableStateFlow<Float> = MutableStateFlow(DEFAULT_MAX_ALPHA)

    override val revealAmount: Flow<Float> = callbackFlow {
        val updateListener =
            Animator.AnimatorUpdateListener {
+43 −10
Original line number Diff line number Diff line
@@ -13,6 +13,7 @@
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
@file:OptIn(ExperimentalCoroutinesApi::class)

package com.android.systemui.keyguard.domain.interactor

@@ -21,17 +22,22 @@ import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.keyguard.data.repository.DEFAULT_REVEAL_DURATION
import com.android.systemui.keyguard.data.repository.LightRevealScrimRepository
import com.android.systemui.keyguard.shared.model.Edge
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.power.domain.interactor.PowerInteractor
import com.android.systemui.power.shared.model.ScreenPowerState
import com.android.systemui.power.shared.model.WakeSleepReason
import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.statusbar.LightRevealEffect
import com.android.systemui.util.kotlin.sample
import dagger.Lazy
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.launch

@SysUISingleton
@@ -39,7 +45,7 @@ class LightRevealScrimInteractor
@Inject
constructor(
    private val transitionInteractor: KeyguardTransitionInteractor,
    private val lightRevealScrimRepository: LightRevealScrimRepository,
    private val repository: LightRevealScrimRepository,
    @Application private val scope: CoroutineScope,
    private val scrimLogger: ScrimLogger,
    private val powerInteractor: Lazy<PowerInteractor>,
@@ -63,17 +69,17 @@ constructor(
                        DEFAULT_REVEAL_DURATION
                    }

                lightRevealScrimRepository.startRevealAmountAnimator(
                repository.startRevealAmountAnimator(
                    willBeRevealedInState(it.to),
                    duration = animationDuration
                    duration = animationDuration,
                )
            }
        }
    }

    private val isLastSleepDueToFold: Boolean
        get() = powerInteractor.get().detailedWakefulness.value
            .lastSleepReason == WakeSleepReason.FOLD
        get() =
            powerInteractor.get().detailedWakefulness.value.lastSleepReason == WakeSleepReason.FOLD

    /**
     * Whenever a keyguard transition starts, sample the latest reveal effect from the repository
@@ -86,12 +92,29 @@ constructor(
     * LiftReveal.
     */
    val lightRevealEffect: Flow<LightRevealEffect> =
        transitionInteractor.startedKeyguardTransitionStep.sample(
            lightRevealScrimRepository.revealEffect
        transitionInteractor.startedKeyguardTransitionStep.sample(repository.revealEffect)

    /** Limit the max alpha for the scrim to allow for some transparency */
    val maxAlpha: Flow<Float> =
        transitionInteractor
            .isInTransition(
                edge = Edge.create(Scenes.Gone, KeyguardState.AOD),
                edgeWithoutSceneContainer = Edge.create(KeyguardState.GONE, KeyguardState.AOD),
            )
            .flatMapLatest { isInTransition ->
                // During GONE->AOD transitions, the home screen and wallpaper are still visible
                // until
                // WM is told to hide them, which occurs at the end of the animation. Use an opaque
                // scrim until this transition is complete
                if (isInTransition) {
                    flowOf(1f)
                } else {
                    repository.maxAlpha
                }
            }

    val revealAmount =
        lightRevealScrimRepository.revealAmount.filter {
        repository.revealAmount.filter {
            // When the screen is off we do not want to keep producing frames as this is causing
            // (invisible) jank. However, we need to still pass through 1f and 0f to ensure that the
            // correct end states are respected even if the screen turned off (or was still off)
@@ -104,7 +127,17 @@ constructor(
            powerInteractor.get().screenPowerState.value != ScreenPowerState.SCREEN_TURNING_ON

    val isAnimating: Boolean
        get() = lightRevealScrimRepository.isAnimating
        get() = repository.isAnimating

    /** If the wallpaper supports ambient mode, allow partial transparency */
    fun setWallpaperSupportsAmbientMode(supportsAmbientMode: Boolean) {
        repository.maxAlpha.value =
            if (supportsAmbientMode) {
                0.7f
            } else {
                1f
            }
    }

    /**
     * Whether the light reveal scrim will be fully revealed (revealAmount = 1.0f) in the given
+30 −2
Original line number Diff line number Diff line
@@ -16,21 +16,49 @@

package com.android.systemui.keyguard.ui.binder

import android.animation.ValueAnimator
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.repeatOnLifecycle
import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.systemui.keyguard.ui.viewmodel.LightRevealScrimViewModel
import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.shared.Flags.ambientAod
import com.android.systemui.statusbar.LightRevealScrim
import com.android.systemui.wallpapers.ui.viewmodel.WallpaperViewModel
import kotlinx.coroutines.launch

object LightRevealScrimViewBinder {
    @JvmStatic
    fun bind(revealScrim: LightRevealScrim, viewModel: LightRevealScrimViewModel) {
    fun bind(
        revealScrim: LightRevealScrim,
        viewModel: LightRevealScrimViewModel,
        wallpaperViewModel: WallpaperViewModel,
    ) {
        revealScrim.repeatWhenAttached {
            repeatOnLifecycle(Lifecycle.State.CREATED) {
                if (ambientAod()) {
                    launch("$TAG#wallpaperViewModel.wallpaperSupportsAmbientMode") {
                        wallpaperViewModel.wallpaperSupportsAmbientMode.collect {
                            viewModel.setWallpaperSupportsAmbientMode(it)
                        }
                    }
                    launch("$TAG#viewModel.maxAlpha") {
                        viewModel.maxAlpha.collect { alpha ->
                            if (alpha != revealScrim.alpha) {
                                ValueAnimator.ofFloat(revealScrim.alpha, alpha).apply {
                                    duration = 400
                                    addUpdateListener { animation ->
                                        revealScrim.alpha = animation.getAnimatedValue() as Float
                                    }
                                    start()
                                }
                            }
                        }
                    }
                }

                launch("$TAG#viewModel.revealAmount") {
                    viewModel.revealAmount.collect { amount -> revealScrim.revealAmount = amount }
                    viewModel.revealAmount.collect { revealScrim.revealAmount = it }
                }

                launch("$TAG#viewModel.lightRevealEffect") {
Loading