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

Commit 3cd0d4fe authored by Matt Pietal's avatar Matt Pietal Committed by Android (Google) Code Review
Browse files

Merge "Evaluate wallpaper luminosity to determine scrim level" into main

parents 0c90f98d 5cf439a1
Loading
Loading
Loading
Loading
+13 −17
Original line number Diff line number Diff line
@@ -64,7 +64,9 @@ class LightRevealScrimRepositoryTest : SysuiTestCase() {
                kosmos.fakeKeyguardRepository,
                context,
                powerRepository,
                mock()
                mock(),
                mock(),
                kosmos.testScope.backgroundScope,
            )
    }

@@ -89,41 +91,35 @@ class LightRevealScrimRepositoryTest : SysuiTestCase() {
        // default reveal despite a biometric unlock.
        fakeKeyguardRepository.setBiometricUnlockState(
            BiometricUnlockMode.WAKE_AND_UNLOCK,
            BiometricUnlockSource.FINGERPRINT_SENSOR
            BiometricUnlockSource.FINGERPRINT_SENSOR,
        )

        runCurrent()
        values.assertEffectsMatchPredicates(
            { it == DEFAULT_REVEAL_EFFECT },
        )
        values.assertEffectsMatchPredicates({ it == DEFAULT_REVEAL_EFFECT })

        // We got a source but still have no sensor locations, so should be sticking with
        // the default effect.
        fakeKeyguardRepository.setBiometricUnlockState(
            BiometricUnlockMode.WAKE_AND_UNLOCK,
            BiometricUnlockSource.FINGERPRINT_SENSOR
            BiometricUnlockSource.FINGERPRINT_SENSOR,
        )

        runCurrent()
        values.assertEffectsMatchPredicates(
            { it == DEFAULT_REVEAL_EFFECT },
        )
        values.assertEffectsMatchPredicates({ it == DEFAULT_REVEAL_EFFECT })

        // We got a location for the face sensor, but we unlocked with fingerprint.
        val faceLocation = Point(250, 0)
        fakeKeyguardRepository.setFaceSensorLocation(faceLocation)

        runCurrent()
        values.assertEffectsMatchPredicates(
            { it == DEFAULT_REVEAL_EFFECT },
        )
        values.assertEffectsMatchPredicates({ it == DEFAULT_REVEAL_EFFECT })

        // Now we have fingerprint sensor locations, and wake and unlock via fingerprint.
        val fingerprintLocation = Point(500, 500)
        fakeKeyguardRepository.setFingerprintSensorLocation(fingerprintLocation)
        fakeKeyguardRepository.setBiometricUnlockState(
            BiometricUnlockMode.WAKE_AND_UNLOCK_PULSING,
            BiometricUnlockSource.FINGERPRINT_SENSOR
            BiometricUnlockSource.FINGERPRINT_SENSOR,
        )

        // We should now have switched to the circle reveal, at the fingerprint location.
@@ -141,18 +137,18 @@ class LightRevealScrimRepositoryTest : SysuiTestCase() {
        val valuesPrevSize = values.size
        fakeKeyguardRepository.setBiometricUnlockState(
            BiometricUnlockMode.WAKE_AND_UNLOCK_PULSING,
            BiometricUnlockSource.FINGERPRINT_SENSOR
            BiometricUnlockSource.FINGERPRINT_SENSOR,
        )
        fakeKeyguardRepository.setBiometricUnlockState(
            BiometricUnlockMode.WAKE_AND_UNLOCK_FROM_DREAM,
            BiometricUnlockSource.FINGERPRINT_SENSOR
            BiometricUnlockSource.FINGERPRINT_SENSOR,
        )
        assertEquals(valuesPrevSize, values.size)

        // Non-biometric unlock, we should return to the default reveal.
        fakeKeyguardRepository.setBiometricUnlockState(
            BiometricUnlockMode.NONE,
            BiometricUnlockSource.FINGERPRINT_SENSOR
            BiometricUnlockSource.FINGERPRINT_SENSOR,
        )

        runCurrent()
@@ -170,7 +166,7 @@ class LightRevealScrimRepositoryTest : SysuiTestCase() {
        // CircleReveal.
        fakeKeyguardRepository.setBiometricUnlockState(
            BiometricUnlockMode.WAKE_AND_UNLOCK,
            BiometricUnlockSource.FACE_SENSOR
            BiometricUnlockSource.FACE_SENSOR,
        )
        runCurrent()

+52 −3
Original line number Diff line number Diff line
@@ -150,15 +150,64 @@ class LightRevealScrimInteractorTest : SysuiTestCase() {
        }

    @Test
    fun supportsAmbientMode() =
    fun maxAlpha_doesNotSupportAmbientMode() =
        kosmos.testScope.runTest {
            val maxAlpha by collectLastValue(underTest.maxAlpha)
            underTest.setWallpaperSupportsAmbientMode(false)

            assertThat(maxAlpha).isEqualTo(1f)
        }

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

            underTest.setWallpaperSupportsAmbientMode(true)
            assertThat(maxAlpha).isLessThan(1f)
            fakeLightRevealScrimRepository.useDarkWallpaperScrim.value = true

            assertThat(maxAlpha).isEqualTo(0.64f)
        }

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

            underTest.setWallpaperSupportsAmbientMode(true)
            fakeLightRevealScrimRepository.useDarkWallpaperScrim.value = false

            assertThat(maxAlpha).isEqualTo(0.4f)
        }

    @Test
    fun maxAlpha_supportsAmbientModeDuringTransitionIsOpaque() =
        kosmos.testScope.runTest {
            val maxAlpha by collectLastValue(underTest.maxAlpha)

            underTest.setWallpaperSupportsAmbientMode(true)
            fakeLightRevealScrimRepository.useDarkWallpaperScrim.value = true

            fakeKeyguardTransitionRepository.sendTransitionSteps(
                listOf(
                    TransitionStep(
                        from = KeyguardState.GONE,
                        to = KeyguardState.AOD,
                        value = 0f,
                        transitionState = TransitionState.STARTED,
                    ),
                    TransitionStep(
                        from = KeyguardState.GONE,
                        to = KeyguardState.AOD,
                        value = 0.4f,
                        transitionState = TransitionState.RUNNING,
                    ),
                ),
                kosmos.testScope,
            )

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

+35 −3
Original line number Diff line number Diff line
@@ -16,12 +16,17 @@

package com.android.systemui.keyguard.data.repository

import android.app.WallpaperColors
import android.app.WallpaperManager
import android.content.Context
import android.graphics.Point
import androidx.core.animation.Animator
import androidx.core.animation.ValueAnimator
import com.android.internal.colorextraction.ColorExtractor
import com.android.keyguard.logging.ScrimLogger
import com.android.systemui.colorextraction.SysuiColorExtractor
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.keyguard.shared.model.BiometricUnlockMode
import com.android.systemui.keyguard.shared.model.BiometricUnlockSource
import com.android.systemui.power.data.repository.PowerRepository
@@ -35,14 +40,18 @@ import com.android.systemui.statusbar.LightRevealEffect
import com.android.systemui.statusbar.PowerButtonReveal
import javax.inject.Inject
import kotlin.math.max
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.callbackFlow
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

val DEFAULT_REVEAL_EFFECT = LiftReveal
const val DEFAULT_REVEAL_DURATION = 500L
@@ -64,8 +73,11 @@ interface LightRevealScrimRepository {

    val isAnimating: Boolean

    /** Limit the max alpha for the scrim to allow for some transparency */
    val maxAlpha: MutableStateFlow<Float>
    /** Allows for transparency to see the wallpaper on AOD */
    val wallpaperSupportsAmbientMode: MutableStateFlow<Boolean>

    /** For brighter wallpapers, add a darker scrim to ensure contrast */
    val useDarkWallpaperScrim: StateFlow<Boolean>

    fun startRevealAmountAnimator(reveal: Boolean, duration: Long = DEFAULT_REVEAL_DURATION)
}
@@ -78,6 +90,8 @@ constructor(
    @ShadeDisplayAware val context: Context,
    powerRepository: PowerRepository,
    private val scrimLogger: ScrimLogger,
    private val colorExtractor: SysuiColorExtractor,
    @Background backgroundScope: CoroutineScope,
) : LightRevealScrimRepository {
    companion object {
        val TAG = LightRevealScrimRepository::class.simpleName!!
@@ -130,7 +144,25 @@ constructor(

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

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

    override val useDarkWallpaperScrim: StateFlow<Boolean> =
        callbackFlow {
                fun sendLuminanceUpdate() {
                    colorExtractor.getWallpaperColors(WallpaperManager.FLAG_LOCK)?.let {
                        val useDarkScrim =
                            (it.getColorHints() and WallpaperColors.HINT_SUPPORTS_DARK_TEXT) > 0
                        trySend(useDarkScrim)
                    }
                }

                val listener =
                    ColorExtractor.OnColorsChangedListener { _, _ -> sendLuminanceUpdate() }
                sendLuminanceUpdate()
                colorExtractor.addOnColorsChangedListener(listener)
                awaitClose { colorExtractor.removeOnColorsChangedListener(listener) }
            }
            .stateIn(backgroundScope, SharingStarted.Eagerly, false)

    override val revealAmount: Flow<Float> = callbackFlow {
        val updateListener =
+27 −21
Original line number Diff line number Diff line
@@ -40,6 +40,7 @@ import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map

@SysUISingleton
class LightRevealScrimInteractor
@@ -98,23 +99,33 @@ constructor(

    /** Limit the max alpha for the scrim to allow for some transparency */
    val maxAlpha: Flow<Float> =
        repository.wallpaperSupportsAmbientMode
            .flatMapLatest { wallpaperSupportsAmbientMode ->
                if (!wallpaperSupportsAmbientMode) {
                    flowOf(1f)
                } else {
                    anyOf(
                            transitionInteractor.isInTransition(
                                edge = Edge.create(Scenes.Gone, KeyguardState.AOD),
                    edgeWithoutSceneContainer = Edge.create(KeyguardState.GONE, KeyguardState.AOD),
                                edgeWithoutSceneContainer =
                                    Edge.create(KeyguardState.GONE, KeyguardState.AOD),
                            ),
                            transitionInteractor.isInTransition(
                                Edge.create(KeyguardState.OCCLUDED, KeyguardState.AOD)
                            ),
                        )
                        .flatMapLatest { isInTransition ->
                // During transitions like GONE->AOD, surfaces like the launcher may be 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.
                            // During transitions like GONE->AOD, surfaces like the launcher may be
                            // 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
                                repository.useDarkWallpaperScrim.map { useDarkScrim ->
                                    if (useDarkScrim) 0.64f else 0.4f
                                }
                            }
                        }
                }
            }
            .flowOn(backgroundDispatcher)
@@ -137,12 +148,7 @@ constructor(

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

    /**
+3 −1
Original line number Diff line number Diff line
@@ -40,7 +40,9 @@ class FakeLightRevealScrimRepository : LightRevealScrimRepository {
    override val isAnimating: Boolean
        get() = false

    override val maxAlpha: MutableStateFlow<Float> = MutableStateFlow(1f)
    override val wallpaperSupportsAmbientMode = MutableStateFlow(false)

    override val useDarkWallpaperScrim = MutableStateFlow(false)

    override fun startRevealAmountAnimator(reveal: Boolean, duration: Long) {
        if (reveal) {