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

Commit c244eb65 authored by Grace Cheng's avatar Grace Cheng
Browse files

SFPS indicator and BiometricPrompt MVVM cleanup

Removing stale iconOverlayView references (layout files,
BiometricViewSizeBinder). Also cleans up MVVM structure by moving
dependencies to SideFpsOverlayViewModel and new interactor rather than
SideFpsOverlayViewBinder and updates PromptIconViewModel to access
dependencies directly from parent PromptViewModel instead of passing
them in directly. Moves shared test setup functions to
BiometricTestExtensions.

Flag: EXEMPT cleanup
Fixes: 364937003
Bug: 288173662
Test: atest SideFpsOverlayViewModelTest
Test: atest SideFpsOverlayInteractorTest
Test: atest SideFpsOverlayViewBinderTest
Test: atest PromptViewModelTest
Change-Id: I2bde7a83bc2c5eb626d2a7dfa2f4fb85dede68f9
parent 7c4f62a5
Loading
Loading
Loading
Loading
+75 −0
Original line number Diff line number Diff line
@@ -27,6 +27,17 @@ import android.hardware.face.FaceSensorProperties
import android.hardware.face.FaceSensorPropertiesInternal
import android.hardware.fingerprint.FingerprintSensorProperties
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal
import com.android.keyguard.keyguardUpdateMonitor
import com.android.systemui.SysuiTestableContext
import com.android.systemui.biometrics.data.repository.biometricStatusRepository
import com.android.systemui.biometrics.shared.model.AuthenticationReason
import com.android.systemui.bouncer.data.repository.keyguardBouncerRepository
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.res.R
import com.android.systemui.util.mockito.whenever
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runCurrent

/** Create [FingerprintSensorPropertiesInternal] for a test. */
internal fun fingerprintSensorPropertiesInternal(
@@ -145,3 +156,67 @@ internal fun promptInfo(
    info.negativeButtonText = negativeButton
    return info
}

@OptIn(ExperimentalCoroutinesApi::class)
internal fun TestScope.updateSfpsIndicatorRequests(
    kosmos: Kosmos,
    mContext: SysuiTestableContext,
    primaryBouncerRequest: Boolean? = null,
    alternateBouncerRequest: Boolean? = null,
    biometricPromptRequest: Boolean? = null,
    // TODO(b/365182034): update when rest to unlock feature is implemented
    //    progressBarShowing: Boolean? = null
) {
    biometricPromptRequest?.let { hasBiometricPromptRequest ->
        if (hasBiometricPromptRequest) {
            kosmos.biometricStatusRepository.setFingerprintAuthenticationReason(
                AuthenticationReason.BiometricPromptAuthentication
            )
        } else {
            kosmos.biometricStatusRepository.setFingerprintAuthenticationReason(
                AuthenticationReason.NotRunning
            )
        }
    }

    primaryBouncerRequest?.let { hasPrimaryBouncerRequest ->
        updatePrimaryBouncer(
            kosmos,
            mContext,
            isShowing = hasPrimaryBouncerRequest,
            isAnimatingAway = false,
            fpsDetectionRunning = true,
            isUnlockingWithFpAllowed = true
        )
    }

    alternateBouncerRequest?.let { hasAlternateBouncerRequest ->
        kosmos.keyguardBouncerRepository.setAlternateVisible(hasAlternateBouncerRequest)
    }

    // TODO(b/365182034): set progress bar visibility when rest to unlock feature is implemented

    runCurrent()
}

internal fun updatePrimaryBouncer(
    kosmos: Kosmos,
    mContext: SysuiTestableContext,
    isShowing: Boolean,
    isAnimatingAway: Boolean,
    fpsDetectionRunning: Boolean,
    isUnlockingWithFpAllowed: Boolean,
) {
    kosmos.keyguardBouncerRepository.setPrimaryShow(isShowing)
    kosmos.keyguardBouncerRepository.setPrimaryStartingToHide(false)
    val primaryStartDisappearAnimation = if (isAnimatingAway) Runnable {} else null
    kosmos.keyguardBouncerRepository.setPrimaryStartDisappearAnimation(
        primaryStartDisappearAnimation
    )

    whenever(kosmos.keyguardUpdateMonitor.isFingerprintDetectionRunning)
        .thenReturn(fpsDetectionRunning)
    whenever(kosmos.keyguardUpdateMonitor.isUnlockingWithFingerprintAllowed)
        .thenReturn(isUnlockingWithFpAllowed)
    mContext.orCreateTestableResources.addOverride(R.bool.config_show_sidefps_hint_on_bouncer, true)
}
+174 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2024 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.systemui.biometrics.domain.interactor

import android.testing.TestableLooper
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.biometrics.data.repository.biometricStatusRepository
import com.android.systemui.biometrics.data.repository.fingerprintPropertyRepository
import com.android.systemui.biometrics.shared.model.AuthenticationReason
import com.android.systemui.biometrics.shared.model.FingerprintSensorType
import com.android.systemui.biometrics.shared.model.SensorStrength
import com.android.systemui.biometrics.updateSfpsIndicatorRequests
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.display.data.repository.displayRepository
import com.android.systemui.display.data.repository.displayStateRepository
import com.android.systemui.kosmos.testScope
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Ignore
import org.junit.Test
import org.junit.runner.RunWith

@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@RunWith(AndroidJUnit4::class)
@TestableLooper.RunWithLooper(setAsMainLooper = true)
class SideFpsOverlayInteractorTest : SysuiTestCase() {
    private val kosmos = testKosmos()
    private val underTest = kosmos.sideFpsOverlayInteractor

    @Test
    fun verifyIsShowingFalse_whenInRearDisplayMode() {
        kosmos.testScope.runTest {
            val isShowing by collectLastValue(underTest.isShowing)
            setupTestConfiguration(isInRearDisplayMode = true)

            updateSfpsIndicatorRequests(kosmos, mContext, primaryBouncerRequest = true)
            runCurrent()

            assertThat(isShowing).isFalse()
        }
    }

    @Test
    fun verifyIsShowingUpdates_onPrimaryBouncerShowAndHide() {
        kosmos.testScope.runTest {
            val isShowing by collectLastValue(underTest.isShowing)
            setupTestConfiguration(isInRearDisplayMode = false)

            // Show primary bouncer
            updateSfpsIndicatorRequests(kosmos, mContext, primaryBouncerRequest = true)
            runCurrent()

            assertThat(isShowing).isTrue()

            // Hide primary bouncer
            updateSfpsIndicatorRequests(kosmos, mContext, primaryBouncerRequest = false)
            runCurrent()

            assertThat(isShowing).isFalse()
        }
    }

    @Test
    fun verifyIsShowingUpdates_onAlternateBouncerShowAndHide() {
        kosmos.testScope.runTest {
            val isShowing by collectLastValue(underTest.isShowing)
            setupTestConfiguration(isInRearDisplayMode = false)

            updateSfpsIndicatorRequests(kosmos, mContext, alternateBouncerRequest = true)
            runCurrent()

            assertThat(isShowing).isTrue()

            // Hide alternate bouncer
            updateSfpsIndicatorRequests(kosmos, mContext, alternateBouncerRequest = false)
            runCurrent()

            assertThat(isShowing).isFalse()
        }
    }

    @Test
    fun verifyIsShowingUpdates_onSystemServerAuthenticationStartedAndStopped() {
        kosmos.testScope.runTest {
            val isShowing by collectLastValue(underTest.isShowing)
            setupTestConfiguration(isInRearDisplayMode = false)

            updateSfpsIndicatorRequests(kosmos, mContext, biometricPromptRequest = true)
            runCurrent()

            assertThat(isShowing).isTrue()

            // System server authentication stopped
            updateSfpsIndicatorRequests(kosmos, mContext, biometricPromptRequest = false)
            runCurrent()

            assertThat(isShowing).isFalse()
        }
    }

    // On progress bar shown - hide indicator
    // On progress bar hidden - show indicator
    // TODO(b/365182034): update + enable when rest to unlock feature is implemented
    @Ignore("b/365182034")
    @Test
    fun verifyIsShowingUpdates_onProgressBarInteraction() {
        kosmos.testScope.runTest {
            val isShowing by collectLastValue(underTest.isShowing)
            setupTestConfiguration(isInRearDisplayMode = false)

            updateSfpsIndicatorRequests(kosmos, mContext, primaryBouncerRequest = true)
            runCurrent()

            assertThat(isShowing).isTrue()

            //            updateSfpsIndicatorRequests(
            //                kosmos, mContext, primaryBouncerRequest = true, progressBarShowing =
            // true
            //            )
            runCurrent()

            assertThat(isShowing).isFalse()

            // Set progress bar invisible
            //            updateSfpsIndicatorRequests(
            //                kosmos, mContext, primaryBouncerRequest = true, progressBarShowing =
            // false
            //            )
            runCurrent()

            // Verify indicator shown
            assertThat(isShowing).isTrue()
        }
    }

    private suspend fun TestScope.setupTestConfiguration(isInRearDisplayMode: Boolean) {
        kosmos.fingerprintPropertyRepository.setProperties(
            sensorId = 1,
            strength = SensorStrength.STRONG,
            sensorType = FingerprintSensorType.POWER_BUTTON,
            sensorLocations = emptyMap()
        )

        kosmos.displayStateRepository.setIsInRearDisplayMode(isInRearDisplayMode)
        kosmos.displayRepository.emitDisplayChangeEvent(0)
        runCurrent()

        kosmos.biometricStatusRepository.setFingerprintAuthenticationReason(
            AuthenticationReason.NotRunning
        )
        // TODO(b/365182034): set progress bar visibility once rest to unlock feature is implemented
    }
}
+32 −223

File changed.

Preview size limit exceeded, changes collapsed.

+4 −61
Original line number Diff line number Diff line
@@ -30,23 +30,19 @@ import android.view.windowManager
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.airbnb.lottie.model.KeyPath
import com.android.keyguard.keyguardUpdateMonitor
import com.android.settingslib.Utils
import com.android.systemui.SysuiTestCase
import com.android.systemui.biometrics.FingerprintInteractiveToAuthProvider
import com.android.systemui.biometrics.data.repository.biometricStatusRepository
import com.android.systemui.biometrics.data.repository.fingerprintPropertyRepository
import com.android.systemui.biometrics.domain.interactor.displayStateInteractor
import com.android.systemui.biometrics.shared.model.AuthenticationReason
import com.android.systemui.biometrics.shared.model.DisplayRotation
import com.android.systemui.biometrics.shared.model.FingerprintSensorType
import com.android.systemui.biometrics.shared.model.LottieCallback
import com.android.systemui.biometrics.shared.model.SensorStrength
import com.android.systemui.bouncer.data.repository.keyguardBouncerRepository
import com.android.systemui.biometrics.updateSfpsIndicatorRequests
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.display.data.repository.displayRepository
import com.android.systemui.display.data.repository.displayStateRepository
import com.android.systemui.keyguard.ui.viewmodel.sideFpsProgressBarViewModel
import com.android.systemui.kosmos.testScope
import com.android.systemui.res.R
import com.android.systemui.testKosmos
@@ -284,17 +280,7 @@ class SideFpsOverlayViewModelTest : SysuiTestCase() {
        kosmos.testScope.runTest {
            val lottieCallbacks by collectLastValue(kosmos.sideFpsOverlayViewModel.lottieCallbacks)

            kosmos.biometricStatusRepository.setFingerprintAuthenticationReason(
                AuthenticationReason.NotRunning
            )
            kosmos.sideFpsProgressBarViewModel.setVisible(false)

            updatePrimaryBouncer(
                isShowing = true,
                isAnimatingAway = false,
                fpsDetectionRunning = true,
                isUnlockingWithFpAllowed = true
            )
            updateSfpsIndicatorRequests(kosmos, mContext, primaryBouncerRequest = true)
            runCurrent()

            assertThat(lottieCallbacks)
@@ -312,17 +298,7 @@ class SideFpsOverlayViewModelTest : SysuiTestCase() {
            val lottieCallbacks by collectLastValue(kosmos.sideFpsOverlayViewModel.lottieCallbacks)
            setDarkMode(true)

            kosmos.biometricStatusRepository.setFingerprintAuthenticationReason(
                AuthenticationReason.BiometricPromptAuthentication
            )
            kosmos.sideFpsProgressBarViewModel.setVisible(false)

            updatePrimaryBouncer(
                isShowing = false,
                isAnimatingAway = false,
                fpsDetectionRunning = true,
                isUnlockingWithFpAllowed = true
            )
            updateSfpsIndicatorRequests(kosmos, mContext, biometricPromptRequest = true)
            runCurrent()

            assertThat(lottieCallbacks)
@@ -338,17 +314,7 @@ class SideFpsOverlayViewModelTest : SysuiTestCase() {
            val lottieCallbacks by collectLastValue(kosmos.sideFpsOverlayViewModel.lottieCallbacks)
            setDarkMode(false)

            kosmos.biometricStatusRepository.setFingerprintAuthenticationReason(
                AuthenticationReason.BiometricPromptAuthentication
            )
            kosmos.sideFpsProgressBarViewModel.setVisible(false)

            updatePrimaryBouncer(
                isShowing = false,
                isAnimatingAway = false,
                fpsDetectionRunning = true,
                isUnlockingWithFpAllowed = true
            )
            updateSfpsIndicatorRequests(kosmos, mContext, biometricPromptRequest = true)
            runCurrent()

            assertThat(lottieCallbacks)
@@ -371,29 +337,6 @@ class SideFpsOverlayViewModelTest : SysuiTestCase() {
        mContext.resources.configuration.uiMode = uiMode
    }

    private fun updatePrimaryBouncer(
        isShowing: Boolean,
        isAnimatingAway: Boolean,
        fpsDetectionRunning: Boolean,
        isUnlockingWithFpAllowed: Boolean,
    ) {
        kosmos.keyguardBouncerRepository.setPrimaryShow(isShowing)
        kosmos.keyguardBouncerRepository.setPrimaryStartingToHide(false)
        val primaryStartDisappearAnimation = if (isAnimatingAway) Runnable {} else null
        kosmos.keyguardBouncerRepository.setPrimaryStartDisappearAnimation(
            primaryStartDisappearAnimation
        )

        whenever(kosmos.keyguardUpdateMonitor.isFingerprintDetectionRunning)
            .thenReturn(fpsDetectionRunning)
        whenever(kosmos.keyguardUpdateMonitor.isUnlockingWithFingerprintAllowed)
            .thenReturn(isUnlockingWithFpAllowed)
        mContext.orCreateTestableResources.addOverride(
            R.bool.config_show_sidefps_hint_on_bouncer,
            true
        )
    }

    private suspend fun TestScope.setupTestConfiguration(
        deviceConfig: DeviceConfig,
        rotation: DisplayRotation = DisplayRotation.ROTATION_0,
+0 −13
Original line number Diff line number Diff line
@@ -215,17 +215,4 @@
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintVertical_bias="1.0"
        tools:srcCompat="@tools:sample/avatars" />

    <com.android.systemui.biometrics.BiometricPromptLottieViewWrapper
        android:id="@+id/biometric_icon_overlay"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:layout_gravity="center"
        android:contentDescription="@null"
        android:scaleType="fitXY"
        android:importantForAccessibility="no"
        app:layout_constraintBottom_toBottomOf="@+id/biometric_icon"
        app:layout_constraintEnd_toEndOf="@+id/biometric_icon"
        app:layout_constraintStart_toStartOf="@+id/biometric_icon"
        app:layout_constraintTop_toTopOf="@+id/biometric_icon" />
</androidx.constraintlayout.widget.ConstraintLayout>
Loading