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

Commit b049b624 authored by Treehugger Robot's avatar Treehugger Robot Committed by Android (Google) Code Review
Browse files

Merge "AltBouncer => PrimaryBouncer on strong face auth lockout" into main

parents 248f1a24 e9f93cc8
Loading
Loading
Loading
Loading
+5 −4
Original line number Original line Diff line number Diff line
@@ -55,6 +55,7 @@ import com.android.systemui.keyguard.ui.view.DeviceEntryIconView
import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerDependencies
import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerDependencies
import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerMessageAreaViewModel
import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerMessageAreaViewModel
import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerUdfpsIconViewModel
import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerUdfpsIconViewModel
import com.android.systemui.lifecycle.rememberViewModel
import com.android.systemui.log.TouchHandlingViewLogger
import com.android.systemui.log.TouchHandlingViewLogger
import com.android.systemui.res.R
import com.android.systemui.res.R


@@ -64,9 +65,9 @@ fun AlternateBouncer(
    onHideAnimationFinished: () -> Unit,
    onHideAnimationFinished: () -> Unit,
    modifier: Modifier = Modifier,
    modifier: Modifier = Modifier,
) {
) {

    val alternateBouncerViewModel =
    val isVisible by
        rememberViewModel("AlternateBouncerViewModel") { alternateBouncerDependencies.viewModel }
        alternateBouncerDependencies.viewModel.isVisible.collectAsStateWithLifecycle(true)
    val isVisible by alternateBouncerViewModel.isVisible.collectAsStateWithLifecycle(true)
    val visibleState = remember { MutableTransitionState(isVisible) }
    val visibleState = remember { MutableTransitionState(isVisible) }


    // Feeds the isVisible value to the MutableTransitionState used by AnimatedVisibility below.
    // Feeds the isVisible value to the MutableTransitionState used by AnimatedVisibility below.
@@ -98,7 +99,7 @@ fun AlternateBouncer(
                Modifier.background(color = Colors.AlternateBouncerBackgroundColor).pointerInput(
                Modifier.background(color = Colors.AlternateBouncerBackgroundColor).pointerInput(
                    Unit
                    Unit
                ) {
                ) {
                    detectTapGestures(onTap = { alternateBouncerDependencies.viewModel.onTapped() })
                    detectTapGestures(onTap = { alternateBouncerViewModel.onTapped() })
                },
                },
        ) {
        ) {
            StatusMessage(viewModel = alternateBouncerDependencies.messageAreaViewModel)
            StatusMessage(viewModel = alternateBouncerDependencies.messageAreaViewModel)
+52 −0
Original line number Original line Diff line number Diff line
@@ -21,16 +21,21 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import androidx.test.filters.SmallTest
import com.android.internal.policy.IKeyguardDismissCallback
import com.android.internal.policy.IKeyguardDismissCallback
import com.android.systemui.SysuiTestCase
import com.android.systemui.SysuiTestCase
import com.android.systemui.biometrics.data.repository.FaceSensorInfo
import com.android.systemui.biometrics.data.repository.fakeFacePropertyRepository
import com.android.systemui.biometrics.shared.model.SensorStrength
import com.android.systemui.bouncer.domain.interactor.primaryBouncerInteractor
import com.android.systemui.bouncer.domain.interactor.primaryBouncerInteractor
import com.android.systemui.concurrency.fakeExecutor
import com.android.systemui.concurrency.fakeExecutor
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.coroutines.collectValues
import com.android.systemui.coroutines.collectValues
import com.android.systemui.keyguard.data.repository.fakeDeviceEntryFaceAuthRepository
import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
import com.android.systemui.keyguard.dismissCallbackRegistry
import com.android.systemui.keyguard.dismissCallbackRegistry
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.keyguard.shared.model.TransitionStep
import com.android.systemui.keyguard.shared.model.TransitionStep
import com.android.systemui.kosmos.testScope
import com.android.systemui.kosmos.testScope
import com.android.systemui.lifecycle.activateIn
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.statusbar.phone.statusBarKeyguardViewManager
import com.android.systemui.statusbar.phone.statusBarKeyguardViewManager
import com.android.systemui.testKosmos
import com.android.systemui.testKosmos
@@ -38,11 +43,15 @@ import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.eq
import com.android.systemui.util.mockito.eq
import com.google.common.collect.Range
import com.google.common.collect.Range
import com.google.common.truth.Truth.assertThat
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import kotlinx.coroutines.test.runTest
import org.junit.Test
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runner.RunWith
import org.mockito.ArgumentMatchers.anyBoolean
import org.mockito.ArgumentMatchers.anyString
import org.mockito.Mockito.mock
import org.mockito.Mockito.mock
import org.mockito.Mockito.verify
import org.mockito.Mockito.verify
import org.mockito.kotlin.never


@RunWith(AndroidJUnit4::class)
@RunWith(AndroidJUnit4::class)
@SmallTest
@SmallTest
@@ -154,6 +163,49 @@ class AlternateBouncerViewModelTest : SysuiTestCase() {
            assertThat(registerForDismissGestures).isFalse()
            assertThat(registerForDismissGestures).isFalse()
        }
        }


    @Test
    fun strongFaceAuthLockout_showPrimaryBouncer() =
        testScope.runTest {
            underTest.activateIn(this)
            setFaceAuthSensor(strength = SensorStrength.STRONG)
            runCurrent()
            kosmos.fakeDeviceEntryFaceAuthRepository.setLockedOut(true)
            runCurrent()

            verify(kosmos.statusBarKeyguardViewManager)
                .showPrimaryBouncer(anyBoolean(), anyString())
        }

    @Test
    fun weakFaceAuthLockout_doNotShowPrimaryBouncer() =
        testScope.runTest {
            underTest.activateIn(this)
            setFaceAuthSensor(strength = SensorStrength.WEAK)
            runCurrent()
            kosmos.fakeDeviceEntryFaceAuthRepository.setLockedOut(true)
            runCurrent()

            verify(kosmos.statusBarKeyguardViewManager, never())
                .showPrimaryBouncer(anyBoolean(), anyString())
        }

    @Test
    fun convenienceFaceAuthLockout_doNotShowPrimaryBouncer() =
        testScope.runTest {
            underTest.activateIn(this)
            setFaceAuthSensor(strength = SensorStrength.CONVENIENCE)
            runCurrent()
            kosmos.fakeDeviceEntryFaceAuthRepository.setLockedOut(true)
            runCurrent()

            verify(kosmos.statusBarKeyguardViewManager, never())
                .showPrimaryBouncer(anyBoolean(), anyString())
        }

    private fun setFaceAuthSensor(strength: SensorStrength) {
        kosmos.fakeFacePropertyRepository.setSensorInfo(FaceSensorInfo(id = 0, strength = strength))
    }

    private fun stepToAlternateBouncer(
    private fun stepToAlternateBouncer(
        value: Float,
        value: Float,
        state: TransitionState = TransitionState.RUNNING,
        state: TransitionState = TransitionState.RUNNING,
+1 −1
Original line number Original line Diff line number Diff line
@@ -54,7 +54,7 @@ constructor(
     */
     */
    val isFaceLockedOut: StateFlow<Boolean> = deviceEntryFaceAuthInteractor.isLockedOut
    val isFaceLockedOut: StateFlow<Boolean> = deviceEntryFaceAuthInteractor.isLockedOut


    private val isStrongFaceAuth: StateFlow<Boolean> =
    val isStrongFaceAuth: StateFlow<Boolean> =
        facePropertyRepository.sensorInfo
        facePropertyRepository.sensorInfo
            .map { it?.strength == SensorStrength.STRONG }
            .map { it?.strength == SensorStrength.STRONG }
            .stateIn(
            .stateIn(
+12 −0
Original line number Original line Diff line number Diff line
@@ -39,7 +39,9 @@ import com.android.systemui.keyguard.ui.view.DeviceEntryIconView
import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerDependencies
import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerDependencies
import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerUdfpsIconViewModel
import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerUdfpsIconViewModel
import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerWindowViewModel
import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerWindowViewModel
import com.android.systemui.lifecycle.WindowLifecycleState
import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.lifecycle.viewModel
import com.android.systemui.log.TouchHandlingViewLogger
import com.android.systemui.log.TouchHandlingViewLogger
import com.android.systemui.res.R
import com.android.systemui.res.R
import com.android.systemui.scene.shared.flag.SceneContainerFlag
import com.android.systemui.scene.shared.flag.SceneContainerFlag
@@ -215,6 +217,16 @@ constructor(
                }
                }
            }
            }
        }
        }

        view.repeatWhenAttached {
            view.viewModel(
                traceName = "AlternateBouncerViewBinderViewModel",
                minWindowLifecycleState = WindowLifecycleState.ATTACHED,
                factory = { alternateBouncerDependencies.viewModel },
            ) {
                // no-op - currently used to activate viewModel
            }
        }
    }
    }


    private fun optionallyAddUdfpsViews(
    private fun optionallyAddUdfpsViews(
+28 −1
Original line number Original line Diff line number Diff line
@@ -20,18 +20,26 @@ package com.android.systemui.keyguard.ui.viewmodel
import android.graphics.Color
import android.graphics.Color
import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor
import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor
import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor
import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor
import com.android.systemui.deviceentry.domain.interactor.DeviceEntryBiometricsAllowedInteractor
import com.android.systemui.deviceentry.domain.interactor.SystemUIDeviceEntryFaceAuthInteractor
import com.android.systemui.keyguard.DismissCallbackRegistry
import com.android.systemui.keyguard.DismissCallbackRegistry
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
import com.android.systemui.keyguard.shared.model.KeyguardState.ALTERNATE_BOUNCER
import com.android.systemui.keyguard.shared.model.KeyguardState.ALTERNATE_BOUNCER
import com.android.systemui.lifecycle.ExclusiveActivatable
import com.android.systemui.scene.shared.flag.SceneContainerFlag
import com.android.systemui.scene.shared.flag.SceneContainerFlag
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager
import dagger.Lazy
import dagger.Lazy
import javax.inject.Inject
import javax.inject.Inject
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.emptyFlow
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.launch


class AlternateBouncerViewModel
class AlternateBouncerViewModel
@Inject
@Inject
@@ -41,7 +49,9 @@ constructor(
    private val dismissCallbackRegistry: DismissCallbackRegistry,
    private val dismissCallbackRegistry: DismissCallbackRegistry,
    alternateBouncerInteractor: Lazy<AlternateBouncerInteractor>,
    alternateBouncerInteractor: Lazy<AlternateBouncerInteractor>,
    private val primaryBouncerInteractor: PrimaryBouncerInteractor,
    private val primaryBouncerInteractor: PrimaryBouncerInteractor,
) {
    deviceEntryBiometricsAllowedInteractor: DeviceEntryBiometricsAllowedInteractor,
    deviceEntryFaceAuthInteractor: SystemUIDeviceEntryFaceAuthInteractor,
) : ExclusiveActivatable() {
    // When we're fully transitioned to the AlternateBouncer, the alpha of the scrim should be:
    // When we're fully transitioned to the AlternateBouncer, the alpha of the scrim should be:
    private val alternateBouncerScrimAlpha = .66f
    private val alternateBouncerScrimAlpha = .66f


@@ -64,6 +74,19 @@ constructor(
    val registerForDismissGestures: Flow<Boolean> =
    val registerForDismissGestures: Flow<Boolean> =
        transitionToAlternateBouncerProgress.map { it == 1f }.distinctUntilChanged()
        transitionToAlternateBouncerProgress.map { it == 1f }.distinctUntilChanged()


    val strongFaceAuthLockout =
        deviceEntryBiometricsAllowedInteractor.isStrongFaceAuth.flatMapLatest { isStrongFaceAuth ->
            if (isStrongFaceAuth) {
                deviceEntryFaceAuthInteractor.isLockedOut.filter { it }.map {} // Unit
            } else {
                emptyFlow()
            }
        }

    override suspend fun onActivated() {
        coroutineScope { launch { strongFaceAuthLockout.collect { onStrongFaceAuthLockout() } } }
    }

    fun onTapped() {
    fun onTapped() {
        statusBarKeyguardViewManager.showPrimaryBouncer(
        statusBarKeyguardViewManager.showPrimaryBouncer(
            /* scrimmed */ true,
            /* scrimmed */ true,
@@ -80,4 +103,8 @@ constructor(
        dismissCallbackRegistry.notifyDismissCancelled()
        dismissCallbackRegistry.notifyDismissCancelled()
        primaryBouncerInteractor.setDismissAction(null, null)
        primaryBouncerInteractor.setDismissAction(null, null)
    }
    }

    private fun onStrongFaceAuthLockout() {
        statusBarKeyguardViewManager.showPrimaryBouncer(true, "strongFaceAuthLockout")
    }
}
}
Loading