Merge "Add tests for Education" into main
This commit is contained in:
committed by
Android (Google) Code Review
commit
f17e4138b8
@@ -34,7 +34,6 @@ import com.android.systemui.biometrics.shared.model.FingerprintSensorType
|
|||||||
import com.google.android.setupcompat.template.FooterBarMixin
|
import com.google.android.setupcompat.template.FooterBarMixin
|
||||||
import com.google.android.setupcompat.template.FooterButton
|
import com.google.android.setupcompat.template.FooterButton
|
||||||
import com.google.android.setupdesign.GlifLayout
|
import com.google.android.setupdesign.GlifLayout
|
||||||
import kotlinx.coroutines.flow.collect
|
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
private const val TAG = "FingerprintEnrollFindSensorV2Fragment"
|
private const val TAG = "FingerprintEnrollFindSensorV2Fragment"
|
||||||
@@ -94,12 +93,12 @@ class FingerprintEnrollFindSensorV2Fragment : Fragment() {
|
|||||||
|
|
||||||
// Set up lottie or animation
|
// Set up lottie or animation
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
viewModel.showSfpsLottie.collect { (isFolded, rotation) ->
|
viewModel.sfpsLottieInfo.collect { (isFolded, rotation) ->
|
||||||
setupLottie(view, getSfpsIllustrationLottieAnimation(isFolded, rotation))
|
setupLottie(view, getSfpsIllustrationLottieAnimation(isFolded, rotation))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
viewModel.showUdfpsLottie.collect { isAccessibilityEnabled ->
|
viewModel.udfpsLottieInfo.collect { isAccessibilityEnabled ->
|
||||||
val lottieAnimation =
|
val lottieAnimation =
|
||||||
if (isAccessibilityEnabled) R.raw.udfps_edu_a11y_lottie else R.raw.udfps_edu_lottie
|
if (isAccessibilityEnabled) R.raw.udfps_edu_a11y_lottie else R.raw.udfps_edu_lottie
|
||||||
setupLottie(view, lottieAnimation) { viewModel.proceedToEnrolling() }
|
setupLottie(view, lottieAnimation) { viewModel.proceedToEnrolling() }
|
||||||
|
|||||||
@@ -26,13 +26,12 @@ import com.android.systemui.biometrics.shared.model.FingerprintSensorType
|
|||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.flow.SharingStarted
|
import kotlinx.coroutines.flow.SharingStarted
|
||||||
import kotlinx.coroutines.flow.collect
|
|
||||||
import kotlinx.coroutines.flow.combine
|
import kotlinx.coroutines.flow.combine
|
||||||
import kotlinx.coroutines.flow.combineTransform
|
import kotlinx.coroutines.flow.combineTransform
|
||||||
|
import kotlinx.coroutines.flow.filter
|
||||||
import kotlinx.coroutines.flow.filterNotNull
|
import kotlinx.coroutines.flow.filterNotNull
|
||||||
import kotlinx.coroutines.flow.map
|
import kotlinx.coroutines.flow.map
|
||||||
import kotlinx.coroutines.flow.shareIn
|
import kotlinx.coroutines.flow.shareIn
|
||||||
import kotlinx.coroutines.flow.transform
|
|
||||||
import kotlinx.coroutines.flow.update
|
import kotlinx.coroutines.flow.update
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
@@ -47,41 +46,43 @@ class FingerprintEnrollFindSensorViewModel(
|
|||||||
) : ViewModel() {
|
) : ViewModel() {
|
||||||
/** Represents the stream of sensor type. */
|
/** Represents the stream of sensor type. */
|
||||||
val sensorType: Flow<FingerprintSensorType> =
|
val sensorType: Flow<FingerprintSensorType> =
|
||||||
fingerprintEnrollViewModel.sensorType
|
fingerprintEnrollViewModel.sensorType.shareIn(
|
||||||
.filterWhenEducationIsShown()
|
viewModelScope,
|
||||||
.shareIn(viewModelScope, SharingStarted.WhileSubscribed(), 1)
|
SharingStarted.WhileSubscribed(),
|
||||||
|
1
|
||||||
|
)
|
||||||
private val _isUdfps: Flow<Boolean> =
|
private val _isUdfps: Flow<Boolean> =
|
||||||
sensorType.map {
|
sensorType.map {
|
||||||
it == FingerprintSensorType.UDFPS_OPTICAL || it == FingerprintSensorType.UDFPS_ULTRASONIC
|
it == FingerprintSensorType.UDFPS_OPTICAL || it == FingerprintSensorType.UDFPS_ULTRASONIC
|
||||||
}
|
}
|
||||||
private val _isSfps: Flow<Boolean> = sensorType.map { it == FingerprintSensorType.POWER_BUTTON }
|
private val _isSfps: Flow<Boolean> = sensorType.map { it == FingerprintSensorType.POWER_BUTTON }
|
||||||
private val _isRearSfps: Flow<Boolean> =
|
private val _isRearSfps: Flow<Boolean> = sensorType.map { it == FingerprintSensorType.REAR }
|
||||||
combineTransform(_isSfps, _isUdfps) { v1, v2 -> !v1 && !v2 }
|
|
||||||
|
|
||||||
/** Represents the stream of showing primary button. */
|
/** Represents the stream of showing primary button. */
|
||||||
val showPrimaryButton: Flow<Boolean> = _isUdfps.transform { if (it) emit(true) }
|
val showPrimaryButton: Flow<Boolean> = _isUdfps.filter { it }
|
||||||
|
|
||||||
/** Represents the stream of showing sfps lottie, Pair(isFolded, rotation). */
|
private val _showSfpsLottie = _isSfps.filter { it }
|
||||||
val showSfpsLottie: Flow<Pair<Boolean, Int>> =
|
/** Represents the stream of showing sfps lottie and the information Pair(isFolded, rotation). */
|
||||||
|
val sfpsLottieInfo: Flow<Pair<Boolean, Int>> =
|
||||||
combineTransform(
|
combineTransform(
|
||||||
_isSfps,
|
_showSfpsLottie,
|
||||||
foldStateViewModel.isFolded,
|
foldStateViewModel.isFolded,
|
||||||
orientationStateViewModel.rotation,
|
orientationStateViewModel.rotation,
|
||||||
) { isSfps, isFolded, rotation ->
|
) { _, isFolded, rotation ->
|
||||||
if (isSfps) emit(Pair(isFolded, rotation))
|
emit(Pair(isFolded, rotation))
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Represents the stream of showing udfps lottie. */
|
private val _showUdfpsLottie = _isUdfps.filter { it }
|
||||||
val showUdfpsLottie: Flow<Boolean> =
|
/** Represents the stream of showing udfps lottie and whether accessibility is enabled. */
|
||||||
combineTransform(
|
val udfpsLottieInfo: Flow<Boolean> =
|
||||||
_isUdfps,
|
_showUdfpsLottie.combine(accessibilityViewModel.isAccessibilityEnabled) {
|
||||||
accessibilityViewModel.isAccessibilityEnabled,
|
_,
|
||||||
) { isUdfps, isAccessibilityEnabled ->
|
isAccessibilityEnabled ->
|
||||||
if (isUdfps) emit(isAccessibilityEnabled)
|
isAccessibilityEnabled
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Represents the stream of showing rfps animation. */
|
/** Represents the stream of showing rfps animation. */
|
||||||
val showRfpsAnimation: Flow<Boolean> = _isRearSfps.transform { if (it) emit(true) }
|
val showRfpsAnimation: Flow<Boolean> = _isRearSfps.filter { it }
|
||||||
|
|
||||||
private val _showErrorDialog: MutableStateFlow<Pair<Int, Boolean>?> = MutableStateFlow(null)
|
private val _showErrorDialog: MutableStateFlow<Pair<Int, Boolean>?> = MutableStateFlow(null)
|
||||||
/** Represents the stream of showing error dialog. */
|
/** Represents the stream of showing error dialog. */
|
||||||
@@ -145,16 +146,6 @@ class FingerprintEnrollFindSensorViewModel(
|
|||||||
navigationViewModel.nextStep()
|
navigationViewModel.nextStep()
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: If we decide to remove previous fragment from activity, then we don't need to check
|
|
||||||
// whether education is shown for the flows that are subscribed by
|
|
||||||
// [FingerprintEnrollFindSensorV2Fragment].
|
|
||||||
private fun <T> Flow<T>.filterWhenEducationIsShown() =
|
|
||||||
combineTransform(navigationViewModel.navigationViewModel) { value, navigationViewModel ->
|
|
||||||
if (navigationViewModel.currStep == Education) {
|
|
||||||
emit(value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class FingerprintEnrollFindSensorViewModelFactory(
|
class FingerprintEnrollFindSensorViewModelFactory(
|
||||||
private val navigationViewModel: FingerprintEnrollNavigationViewModel,
|
private val navigationViewModel: FingerprintEnrollNavigationViewModel,
|
||||||
private val fingerprintEnrollViewModel: FingerprintEnrollViewModel,
|
private val fingerprintEnrollViewModel: FingerprintEnrollViewModel,
|
||||||
|
|||||||
@@ -0,0 +1,255 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2023 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.settings.fingerprint2.enrollment.viewmodel
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.res.Configuration
|
||||||
|
import android.view.accessibility.AccessibilityManager
|
||||||
|
import androidx.arch.core.executor.testing.InstantTaskExecutorRule
|
||||||
|
import androidx.test.core.app.ApplicationProvider
|
||||||
|
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.AccessibilityViewModel
|
||||||
|
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.Education
|
||||||
|
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintEnrollFindSensorViewModel
|
||||||
|
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintEnrollNavigationViewModel
|
||||||
|
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintEnrollViewModel
|
||||||
|
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintGatekeeperViewModel
|
||||||
|
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FoldStateViewModel
|
||||||
|
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.NextStepViewModel
|
||||||
|
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.OrientationStateViewModel
|
||||||
|
import com.android.settings.testutils2.FakeFingerprintManagerInteractor
|
||||||
|
import com.android.systemui.biometrics.shared.model.FingerprintSensor
|
||||||
|
import com.android.systemui.biometrics.shared.model.FingerprintSensorType
|
||||||
|
import com.android.systemui.biometrics.shared.model.SensorStrength
|
||||||
|
import com.google.common.truth.Truth.assertThat
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.flow.collectLatest
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.coroutines.test.StandardTestDispatcher
|
||||||
|
import kotlinx.coroutines.test.TestScope
|
||||||
|
import kotlinx.coroutines.test.advanceUntilIdle
|
||||||
|
import kotlinx.coroutines.test.resetMain
|
||||||
|
import kotlinx.coroutines.test.runTest
|
||||||
|
import kotlinx.coroutines.test.setMain
|
||||||
|
import org.junit.After
|
||||||
|
import org.junit.Before
|
||||||
|
import org.junit.Rule
|
||||||
|
import org.junit.Test
|
||||||
|
import org.junit.runner.RunWith
|
||||||
|
import org.mockito.junit.MockitoJUnit
|
||||||
|
import org.mockito.junit.MockitoJUnitRunner
|
||||||
|
|
||||||
|
/** consistent with [ScreenSizeFoldProvider.INNER_SCREEN_SMALLEST_SCREEN_WIDTH_THRESHOLD_DP] */
|
||||||
|
private const val INNER_SCREEN_SMALLEST_SCREEN_WIDTH_THRESHOLD_DP = 600
|
||||||
|
|
||||||
|
@RunWith(MockitoJUnitRunner::class)
|
||||||
|
class FingerprintEnrollFindSensorViewModelV2Test {
|
||||||
|
@JvmField @Rule var rule = MockitoJUnit.rule()
|
||||||
|
@get:Rule val instantTaskRule = InstantTaskExecutorRule()
|
||||||
|
|
||||||
|
private var backgroundDispatcher = StandardTestDispatcher()
|
||||||
|
private var testScope = TestScope(backgroundDispatcher)
|
||||||
|
private lateinit var fakeFingerprintManagerInteractor: FakeFingerprintManagerInteractor
|
||||||
|
private lateinit var gatekeeperViewModel: FingerprintGatekeeperViewModel
|
||||||
|
private lateinit var enrollViewModel: FingerprintEnrollViewModel
|
||||||
|
private lateinit var navigationViewModel: FingerprintEnrollNavigationViewModel
|
||||||
|
private lateinit var accessibilityViewModel: AccessibilityViewModel
|
||||||
|
private lateinit var foldStateViewModel: FoldStateViewModel
|
||||||
|
private lateinit var orientationStateViewModel: OrientationStateViewModel
|
||||||
|
private lateinit var underTest: FingerprintEnrollFindSensorViewModel
|
||||||
|
private val context: Context = ApplicationProvider.getApplicationContext()
|
||||||
|
private val accessibilityManager: AccessibilityManager =
|
||||||
|
context.getSystemService(AccessibilityManager::class.java)!!
|
||||||
|
|
||||||
|
@Before
|
||||||
|
fun setup() {
|
||||||
|
backgroundDispatcher = StandardTestDispatcher()
|
||||||
|
testScope = TestScope(backgroundDispatcher)
|
||||||
|
Dispatchers.setMain(backgroundDispatcher)
|
||||||
|
|
||||||
|
fakeFingerprintManagerInteractor = FakeFingerprintManagerInteractor()
|
||||||
|
gatekeeperViewModel =
|
||||||
|
FingerprintGatekeeperViewModel.FingerprintGatekeeperViewModelFactory(
|
||||||
|
null,
|
||||||
|
fakeFingerprintManagerInteractor
|
||||||
|
)
|
||||||
|
.create(FingerprintGatekeeperViewModel::class.java)
|
||||||
|
navigationViewModel =
|
||||||
|
FingerprintEnrollNavigationViewModel.FingerprintEnrollNavigationViewModelFactory(
|
||||||
|
backgroundDispatcher,
|
||||||
|
fakeFingerprintManagerInteractor,
|
||||||
|
gatekeeperViewModel,
|
||||||
|
canSkipConfirm = true,
|
||||||
|
)
|
||||||
|
.create(FingerprintEnrollNavigationViewModel::class.java)
|
||||||
|
enrollViewModel =
|
||||||
|
FingerprintEnrollViewModel.FingerprintEnrollViewModelFactory(
|
||||||
|
fakeFingerprintManagerInteractor,
|
||||||
|
backgroundDispatcher
|
||||||
|
)
|
||||||
|
.create(FingerprintEnrollViewModel::class.java)
|
||||||
|
accessibilityViewModel =
|
||||||
|
AccessibilityViewModel.AccessibilityViewModelFactory(accessibilityManager)
|
||||||
|
.create(AccessibilityViewModel::class.java)
|
||||||
|
foldStateViewModel =
|
||||||
|
FoldStateViewModel.FoldStateViewModelFactory(context).create(FoldStateViewModel::class.java)
|
||||||
|
orientationStateViewModel =
|
||||||
|
OrientationStateViewModel.OrientationViewModelFactory(context)
|
||||||
|
.create(OrientationStateViewModel::class.java)
|
||||||
|
underTest =
|
||||||
|
FingerprintEnrollFindSensorViewModel.FingerprintEnrollFindSensorViewModelFactory(
|
||||||
|
navigationViewModel,
|
||||||
|
enrollViewModel,
|
||||||
|
gatekeeperViewModel,
|
||||||
|
accessibilityViewModel,
|
||||||
|
foldStateViewModel,
|
||||||
|
orientationStateViewModel
|
||||||
|
)
|
||||||
|
.create(FingerprintEnrollFindSensorViewModel::class.java)
|
||||||
|
|
||||||
|
// Navigate to Education page
|
||||||
|
navigationViewModel.nextStep()
|
||||||
|
}
|
||||||
|
@After
|
||||||
|
fun tearDown() {
|
||||||
|
Dispatchers.resetMain()
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(b/305094585): test enroll() logic
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun currentStepIsEducation() =
|
||||||
|
testScope.runTest {
|
||||||
|
var step: NextStepViewModel? = null
|
||||||
|
val job = launch {
|
||||||
|
navigationViewModel.navigationViewModel.collectLatest { step = it.currStep }
|
||||||
|
}
|
||||||
|
advanceUntilIdle()
|
||||||
|
assertThat(step).isEqualTo(Education)
|
||||||
|
job.cancel()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun udfpsLottieInfo() =
|
||||||
|
testScope.runTest {
|
||||||
|
fakeFingerprintManagerInteractor.sensorProp =
|
||||||
|
FingerprintSensor(
|
||||||
|
0 /* sensorId */,
|
||||||
|
SensorStrength.STRONG,
|
||||||
|
5,
|
||||||
|
FingerprintSensorType.UDFPS_OPTICAL
|
||||||
|
)
|
||||||
|
|
||||||
|
var udfpsLottieInfo: Boolean? = null
|
||||||
|
val job = launch { underTest.udfpsLottieInfo.collect { udfpsLottieInfo = it } }
|
||||||
|
|
||||||
|
advanceUntilIdle()
|
||||||
|
assertThat(udfpsLottieInfo).isNotNull()
|
||||||
|
job.cancel()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun sfpsLottieInfoWhenFolded() =
|
||||||
|
testScope.runTest {
|
||||||
|
var isFolded = false
|
||||||
|
var rotation: Int = -1
|
||||||
|
val job = launch {
|
||||||
|
underTest.sfpsLottieInfo.collect {
|
||||||
|
isFolded = it.first
|
||||||
|
rotation = it.second
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val config = createConfiguration(isFolded = true)
|
||||||
|
foldStateViewModel.onConfigurationChange(config)
|
||||||
|
advanceUntilIdle()
|
||||||
|
assertThat(isFolded).isTrue()
|
||||||
|
assertThat(rotation).isEqualTo(context.display!!.rotation)
|
||||||
|
job.cancel()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun sfpsLottieInfoWhenUnFolded() =
|
||||||
|
testScope.runTest {
|
||||||
|
var isFolded = false
|
||||||
|
var rotation: Int = -1
|
||||||
|
val job = launch {
|
||||||
|
underTest.sfpsLottieInfo.collect {
|
||||||
|
isFolded = it.first
|
||||||
|
rotation = it.second
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val config = createConfiguration(isFolded = false)
|
||||||
|
foldStateViewModel.onConfigurationChange(config)
|
||||||
|
advanceUntilIdle()
|
||||||
|
assertThat(isFolded).isFalse()
|
||||||
|
assertThat(rotation).isEqualTo(context.display!!.rotation)
|
||||||
|
job.cancel()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun rfpsAnimation() =
|
||||||
|
testScope.runTest {
|
||||||
|
fakeFingerprintManagerInteractor.sensorProp =
|
||||||
|
FingerprintSensor(0 /* sensorId */, SensorStrength.STRONG, 5, FingerprintSensorType.REAR)
|
||||||
|
|
||||||
|
var showRfpsAnimation: Boolean? = null
|
||||||
|
val job = launch { underTest.showRfpsAnimation.collect { showRfpsAnimation = it } }
|
||||||
|
|
||||||
|
advanceUntilIdle()
|
||||||
|
assertThat(showRfpsAnimation).isTrue()
|
||||||
|
job.cancel()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun showPrimaryButton_ifUdfps() =
|
||||||
|
testScope.runTest {
|
||||||
|
fakeFingerprintManagerInteractor.sensorProp =
|
||||||
|
FingerprintSensor(
|
||||||
|
0 /* sensorId */,
|
||||||
|
SensorStrength.STRONG,
|
||||||
|
5,
|
||||||
|
FingerprintSensorType.UDFPS_OPTICAL
|
||||||
|
)
|
||||||
|
|
||||||
|
var showPrimaryButton: Boolean? = null
|
||||||
|
val job = launch { underTest.showPrimaryButton.collect { showPrimaryButton = it } }
|
||||||
|
|
||||||
|
advanceUntilIdle()
|
||||||
|
assertThat(showPrimaryButton).isTrue()
|
||||||
|
job.cancel()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun doesNotShowPrimaryButton_ifNonUdfps() =
|
||||||
|
testScope.runTest {
|
||||||
|
var showPrimaryButton: Boolean? = null
|
||||||
|
val job = launch { underTest.showPrimaryButton.collect { showPrimaryButton = it } }
|
||||||
|
|
||||||
|
advanceUntilIdle()
|
||||||
|
assertThat(showPrimaryButton).isNull()
|
||||||
|
job.cancel()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createConfiguration(isFolded: Boolean): Configuration {
|
||||||
|
val config = Configuration()
|
||||||
|
config.smallestScreenWidthDp =
|
||||||
|
if (isFolded) INNER_SCREEN_SMALLEST_SCREEN_WIDTH_THRESHOLD_DP - 1
|
||||||
|
else INNER_SCREEN_SMALLEST_SCREEN_WIDTH_THRESHOLD_DP + 1
|
||||||
|
return config
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user