From 9a7afc92164da1760ccc41b8db64ff35c8c90e7a Mon Sep 17 00:00:00 2001 From: Milton Wu Date: Tue, 11 Jul 2023 14:54:56 +0800 Subject: [PATCH] [BiometricsV2] Refactor EnrollmentViewModel Refactor FingerprintEnrollmentViewModel to kotlin Bug: 286198096 Test: atest FingerprintEnrollmentViewModelTest Test: atest FingerprintEnrollmentActivityTest Test: atest biometrics-enrollment-test Test: manually test enrollment Change-Id: If1b87fa115db1c3fde853ac13fe6204879d34ca8 --- .../ui/view/FingerprintEnrollmentActivity.kt | 36 +- .../FingerprintEnrollmentViewModel.java | 234 ------------ .../FingerprintEnrollmentViewModel.kt | 158 ++++++++ .../FingerprintEnrollmentViewModelTest.java | 338 ------------------ .../FingerprintEnrollmentViewModelTest.kt | 222 ++++++++++++ 5 files changed, 398 insertions(+), 590 deletions(-) delete mode 100644 src/com/android/settings/biometrics2/ui/viewmodel/FingerprintEnrollmentViewModel.java create mode 100644 src/com/android/settings/biometrics2/ui/viewmodel/FingerprintEnrollmentViewModel.kt delete mode 100644 tests/unit/src/com/android/settings/biometrics2/ui/viewmodel/FingerprintEnrollmentViewModelTest.java create mode 100644 tests/unit/src/com/android/settings/biometrics2/ui/viewmodel/FingerprintEnrollmentViewModelTest.kt diff --git a/src/com/android/settings/biometrics2/ui/view/FingerprintEnrollmentActivity.kt b/src/com/android/settings/biometrics2/ui/view/FingerprintEnrollmentActivity.kt index 12aac6a46a3..fa8a3fabaf8 100644 --- a/src/com/android/settings/biometrics2/ui/view/FingerprintEnrollmentActivity.kt +++ b/src/com/android/settings/biometrics2/ui/view/FingerprintEnrollmentActivity.kt @@ -130,6 +130,8 @@ open class FingerprintEnrollmentActivity : FragmentActivity() { viewModelProvider[FingerprintEnrollErrorDialogViewModel::class.java] } + private var isFirstFragmentAdded = false + private val introActionObserver: Observer = Observer { action -> if (DEBUG) { Log.d(TAG, "introActionObserver($action)") @@ -168,7 +170,6 @@ open class FingerprintEnrollmentActivity : FragmentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - viewModel.onRestoreInstanceState(savedInstanceState) autoCredentialViewModel.setCredentialModel(savedInstanceState, intent) // Theme @@ -181,12 +182,12 @@ open class FingerprintEnrollmentActivity : FragmentActivity() { val fragment: Fragment? = supportFragmentManager.findFragmentById( R.id.fragment_container_view ) - if (DEBUG) { - Log.d( - TAG, "onCreate() has savedInstance:" + (savedInstanceState != null) - + ", fragment:" + fragment - ) - } + Log.d( + TAG, + "onCreate() has savedInstance:$(savedInstanceState != null), fragment:$fragment" + ) + + isFirstFragmentAdded = (savedInstanceState != null) if (fragment == null) { checkCredential() if (viewModel.request.isSkipFindSensor) { @@ -255,12 +256,12 @@ open class FingerprintEnrollmentActivity : FragmentActivity() { } private fun startFragment(fragmentClass: Class, tag: String) { - if (!viewModel.isFirstFragmentAdded) { + if (!isFirstFragmentAdded) { supportFragmentManager.beginTransaction() .setReorderingAllowed(true) .replace(R.id.fragment_container_view, fragmentClass, null, tag) .commit() - viewModel.setIsFirstFragmentAdded() + isFirstFragmentAdded = true } else { supportFragmentManager.beginTransaction() .setReorderingAllowed(true) @@ -300,9 +301,9 @@ open class FingerprintEnrollmentActivity : FragmentActivity() { // Always setToken into progressViewModel even it is not necessary action for UDFPS progressViewModel.setToken(autoCredentialViewModel.token) attachFindSensorViewModel() - val fragmentClass: Class = if (viewModel.canAssumeUdfps()) { + val fragmentClass: Class = if (viewModel.canAssumeUdfps) { FingerprintEnrollFindUdfpsFragment::class.java - } else if (viewModel.canAssumeSfps()) { + } else if (viewModel.canAssumeSfps) { FingerprintEnrollFindSfpsFragment::class.java } else { FingerprintEnrollFindRfpsFragment::class.java @@ -327,9 +328,9 @@ open class FingerprintEnrollmentActivity : FragmentActivity() { // Always setToken into progressViewModel even it is not necessary action for SFPS or RFPS progressViewModel.setToken(autoCredentialViewModel.token) attachEnrollingViewModel() - val fragmentClass: Class = if (viewModel.canAssumeUdfps()) { + val fragmentClass: Class = if (viewModel.canAssumeUdfps) { FingerprintEnrollEnrollingUdfpsFragment::class.java - } else if (viewModel.canAssumeSfps()) { + } else if (viewModel.canAssumeSfps) { FingerprintEnrollEnrollingSfpsFragment::class.java } else { FingerprintEnrollEnrollingRfpsFragment::class.java @@ -345,7 +346,7 @@ open class FingerprintEnrollmentActivity : FragmentActivity() { } private fun startFinishFragment() { - viewModel.setIsNewFingerprintAdded() + viewModel.isNewFingerprintAdded = true attachFinishViewModel() if (viewModel.request.isSkipFindSensor) { // Set page to Finish @@ -434,7 +435,7 @@ open class FingerprintEnrollmentActivity : FragmentActivity() { viewModel.request.isSuw, viewModel.request.suwExtras ) - if (!viewModel.isWaitingActivityResult().compareAndSet(false, true)) { + if (!viewModel.isWaitingActivityResult.compareAndSet(false, true)) { Log.w(TAG, "chooseLock, fail to set isWaiting flag to true") } chooseLockLauncher.launch(intent) @@ -452,7 +453,7 @@ open class FingerprintEnrollmentActivity : FragmentActivity() { // is already set. Log.e(TAG, "confirmLock, launched is true") finish() - } else if (!viewModel.isWaitingActivityResult().compareAndSet(false, true)) { + } else if (!viewModel.isWaitingActivityResult.compareAndSet(false, true)) { Log.w(TAG, "confirmLock, fail to set isWaiting flag to true") } return @@ -464,7 +465,7 @@ open class FingerprintEnrollmentActivity : FragmentActivity() { } private fun onChooseOrConfirmLockResult(isChooseLock: Boolean, activityResult: ActivityResult) { - if (!viewModel.isWaitingActivityResult().compareAndSet(true, false)) { + if (!viewModel.isWaitingActivityResult.compareAndSet(true, false)) { Log.w(TAG, "isChooseLock:$isChooseLock, fail to unset waiting flag") } if (autoCredentialViewModel.checkNewCredentialFromActivityResult( @@ -631,7 +632,6 @@ open class FingerprintEnrollmentActivity : FragmentActivity() { override fun onSaveInstanceState(outState: Bundle) { super.onSaveInstanceState(outState) - viewModel.onSaveInstanceState(outState) autoCredentialViewModel.onSaveInstanceState(outState) } diff --git a/src/com/android/settings/biometrics2/ui/viewmodel/FingerprintEnrollmentViewModel.java b/src/com/android/settings/biometrics2/ui/viewmodel/FingerprintEnrollmentViewModel.java deleted file mode 100644 index 4c702fa6f2e..00000000000 --- a/src/com/android/settings/biometrics2/ui/viewmodel/FingerprintEnrollmentViewModel.java +++ /dev/null @@ -1,234 +0,0 @@ -/* - * Copyright (C) 2022 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.biometrics2.ui.viewmodel; - -import static com.android.settings.biometrics.fingerprint.FingerprintEnrollFinish.FINGERPRINT_SUGGESTION_ACTIVITY; -import static com.android.settings.biometrics.fingerprint.SetupFingerprintEnrollIntroduction.EXTRA_FINGERPRINT_ENROLLED_COUNT; - -import android.app.Application; -import android.content.ComponentName; -import android.content.Intent; -import android.content.pm.PackageManager; -import android.os.Bundle; -import android.util.Log; - -import androidx.activity.result.ActivityResult; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.annotation.VisibleForTesting; -import androidx.lifecycle.AndroidViewModel; -import androidx.lifecycle.LiveData; -import androidx.lifecycle.MutableLiveData; - -import com.android.settings.biometrics.BiometricEnrollBase; -import com.android.settings.biometrics2.data.repository.FingerprintRepository; -import com.android.settings.biometrics2.ui.model.EnrollmentRequest; - -import java.util.concurrent.atomic.AtomicBoolean; - -/** - * Fingerprint enrollment view model implementation - */ -public class FingerprintEnrollmentViewModel extends AndroidViewModel { - - private static final String TAG = "FingerprintEnrollmentViewModel"; - - @VisibleForTesting - static final String SAVED_STATE_IS_WAITING_ACTIVITY_RESULT = "is_waiting_activity_result"; - - @VisibleForTesting - static final String SAVED_STATE_IS_NEW_FINGERPRINT_ADDED = "is_new_fingerprint_added"; - - @VisibleForTesting - static final String SAVED_STATE_IS_FIRST_FRAGMENT_ADDED = "is_first_fragment_added"; - - @NonNull private final FingerprintRepository mFingerprintRepository; - - private final AtomicBoolean mIsWaitingActivityResult = new AtomicBoolean(false); - private final MutableLiveData mSetResultLiveData = new MutableLiveData<>(); - @NonNull private final EnrollmentRequest mRequest; - private boolean mIsNewFingerprintAdded = false; - /** Flag for FragmentManager::addToBackStack() */ - private boolean mIsFirstFragmentAdded = false; - - public FingerprintEnrollmentViewModel( - @NonNull Application application, - @NonNull FingerprintRepository fingerprintRepository, - @NonNull EnrollmentRequest request) { - super(application); - mFingerprintRepository = fingerprintRepository; - mRequest = request; - } - - /** - * Get EnrollmentRequest - */ - @NonNull - public EnrollmentRequest getRequest() { - return mRequest; - } - - /** - * Get override activity result as current ViewModel status. - * - * FingerprintEnrollmentActivity supports user enrolls 2nd fingerprint or starts a new flow - * through Deferred-SUW, Portal-SUW, or SUW Suggestion. Use a method to get override activity - * result instead of putting these if-else on every setResult(), . - */ - @NonNull - public ActivityResult getOverrideActivityResult(@NonNull ActivityResult result, - @Nullable Bundle generatingChallengeExtras) { - final int newResultCode = mIsNewFingerprintAdded - ? BiometricEnrollBase.RESULT_FINISHED - : (mRequest.isAfterSuwOrSuwSuggestedAction() - ? BiometricEnrollBase.RESULT_CANCELED - : result.getResultCode()); - - Intent newData = result.getData(); - if (newResultCode == BiometricEnrollBase.RESULT_FINISHED - && generatingChallengeExtras != null) { - if (newData == null) { - newData = new Intent(); - } - newData.putExtras(generatingChallengeExtras); - } - return new ActivityResult(newResultCode, newData); - } - - /** - * Activity calls this method during onPause() to finish itself when back to background. - * - * @param isActivityFinishing Activity has called finish() or not - * @param isChangingConfigurations Activity is finished because of configuration changed or not. - */ - public void checkFinishActivityDuringOnPause(boolean isActivityFinishing, - boolean isChangingConfigurations) { - if (isChangingConfigurations || isActivityFinishing || mRequest.isSuw() - || isWaitingActivityResult().get()) { - return; - } - - mSetResultLiveData.postValue( - new ActivityResult(BiometricEnrollBase.RESULT_TIMEOUT, null)); - } - - /** - * Get Suw fingerprint count extra for statistics - */ - @NonNull - public Bundle getSuwFingerprintCountExtra(int userId) { - final Bundle bundle = new Bundle(); - bundle.putInt(EXTRA_FINGERPRINT_ENROLLED_COUNT, - mFingerprintRepository.getNumOfEnrolledFingerprintsSize(userId)); - return bundle; - } - - @NonNull - public LiveData getSetResultLiveData() { - return mSetResultLiveData; - } - - @NonNull - public AtomicBoolean isWaitingActivityResult() { - return mIsWaitingActivityResult; - } - - /** - * Handle savedInstanceState from activity onCreated() - */ - public void onRestoreInstanceState(@Nullable Bundle savedInstanceState) { - if (savedInstanceState == null) { - return; - } - mIsWaitingActivityResult.set( - savedInstanceState.getBoolean(SAVED_STATE_IS_WAITING_ACTIVITY_RESULT, false) - ); - mIsNewFingerprintAdded = savedInstanceState.getBoolean( - SAVED_STATE_IS_NEW_FINGERPRINT_ADDED, false); - mIsFirstFragmentAdded = savedInstanceState.getBoolean( - SAVED_STATE_IS_FIRST_FRAGMENT_ADDED, false); - } - - /** - * Handle onSaveInstanceState from activity - */ - public void onSaveInstanceState(@NonNull Bundle outState) { - outState.putBoolean(SAVED_STATE_IS_WAITING_ACTIVITY_RESULT, mIsWaitingActivityResult.get()); - outState.putBoolean(SAVED_STATE_IS_NEW_FINGERPRINT_ADDED, mIsNewFingerprintAdded); - outState.putBoolean(SAVED_STATE_IS_FIRST_FRAGMENT_ADDED, mIsFirstFragmentAdded); - } - - /** - * Gets the result about fingerprint enrollable - */ - public boolean isMaxEnrolledReached(int userId) { - return mFingerprintRepository.getMaxFingerprints() - <= mFingerprintRepository.getNumOfEnrolledFingerprintsSize(userId); - } - - /** - * The first sensor type is UDFPS sensor or not - */ - public boolean canAssumeUdfps() { - return mFingerprintRepository.canAssumeUdfps(); - } - - /** - * The first sensor type is side fps sensor or not - */ - public boolean canAssumeSfps() { - return mFingerprintRepository.canAssumeSfps(); - } - - /** - * Sets mIsNewFingerprintAdded to true - */ - public void setIsNewFingerprintAdded() { - mIsNewFingerprintAdded = true; - } - - public boolean isFirstFragmentAdded() { - return mIsFirstFragmentAdded; - } - - /** - * set mIsFirstFragmentAdded to true, this flag will be used during adding fragment - */ - public void setIsFirstFragmentAdded() { - mIsFirstFragmentAdded = true; - } - - /** - * Update FINGERPRINT_SUGGESTION_ACTIVITY into package manager - */ - public void updateFingerprintSuggestionEnableState(int userId) { - final int enrolled = mFingerprintRepository.getNumOfEnrolledFingerprintsSize(userId); - - // Only show "Add another fingerprint" if the user already enrolled one. - // "Add fingerprint" will be shown in the main flow if the user hasn't enrolled any - // fingerprints. If the user already added more than one fingerprint, they already know - // to add multiple fingerprints so we don't show the suggestion. - final int flag = (enrolled == 1) ? PackageManager.COMPONENT_ENABLED_STATE_ENABLED - : PackageManager.COMPONENT_ENABLED_STATE_DISABLED; - - ComponentName componentName = new ComponentName(getApplication(), - FINGERPRINT_SUGGESTION_ACTIVITY); - getApplication().getPackageManager().setComponentEnabledSetting(componentName, flag, - PackageManager.DONT_KILL_APP); - Log.d(TAG, FINGERPRINT_SUGGESTION_ACTIVITY + " enabled state = " + (enrolled == 1)); - } -} diff --git a/src/com/android/settings/biometrics2/ui/viewmodel/FingerprintEnrollmentViewModel.kt b/src/com/android/settings/biometrics2/ui/viewmodel/FingerprintEnrollmentViewModel.kt new file mode 100644 index 00000000000..33e1bb6f8ff --- /dev/null +++ b/src/com/android/settings/biometrics2/ui/viewmodel/FingerprintEnrollmentViewModel.kt @@ -0,0 +1,158 @@ +/* + * 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.biometrics2.ui.viewmodel + +import android.app.Application +import android.content.ComponentName +import android.content.Intent +import android.content.pm.PackageManager +import android.os.Bundle +import android.util.Log +import androidx.activity.result.ActivityResult +import androidx.lifecycle.AndroidViewModel +import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData +import com.android.settings.biometrics.BiometricEnrollBase +import com.android.settings.biometrics.fingerprint.FingerprintEnrollFinish.FINGERPRINT_SUGGESTION_ACTIVITY +import com.android.settings.biometrics.fingerprint.SetupFingerprintEnrollIntroduction +import com.android.settings.biometrics2.data.repository.FingerprintRepository +import com.android.settings.biometrics2.ui.model.EnrollmentRequest +import kotlinx.atomicfu.AtomicBoolean +import kotlinx.atomicfu.atomic + +/** + * Fingerprint enrollment view model implementation + */ +class FingerprintEnrollmentViewModel( + application: Application, + private val fingerprintRepository: FingerprintRepository, + val request: EnrollmentRequest +) : AndroidViewModel(application) { + + val isWaitingActivityResult: AtomicBoolean = atomic(false) + + private val _setResultLiveData = MutableLiveData() + val setResultLiveData: LiveData + get() = _setResultLiveData + + var isNewFingerprintAdded = false + set(value) { + // Only allow changing this value from false to true + if (!field) { + field = value + } + } + + /** + * Get override activity result as current ViewModel status. + * + * FingerprintEnrollmentActivity supports user enrolls 2nd fingerprint or starts a new flow + * through Deferred-SUW, Portal-SUW, or SUW Suggestion. Use a method to get override activity + * result instead of putting these if-else on every setResult(), . + */ + fun getOverrideActivityResult( + result: ActivityResult, + generatingChallengeExtras: Bundle? + ): ActivityResult { + val newResultCode = if (isNewFingerprintAdded) + BiometricEnrollBase.RESULT_FINISHED + else if (request.isAfterSuwOrSuwSuggestedAction) + BiometricEnrollBase.RESULT_CANCELED + else + result.resultCode + + var newData = result.data + if (newResultCode == BiometricEnrollBase.RESULT_FINISHED + && generatingChallengeExtras != null + ) { + if (newData == null) { + newData = Intent() + } + newData.putExtras(generatingChallengeExtras) + } + return ActivityResult(newResultCode, newData) + } + + /** + * Activity calls this method during onPause() to finish itself when back to background. + * + * @param isActivityFinishing Activity has called finish() or not + * @param isChangingConfigurations Activity is finished because of configuration changed or not. + */ + fun checkFinishActivityDuringOnPause( + isActivityFinishing: Boolean, + isChangingConfigurations: Boolean + ) { + if (isChangingConfigurations || isActivityFinishing || request.isSuw + || isWaitingActivityResult.value + ) { + return + } + _setResultLiveData.postValue( + ActivityResult(BiometricEnrollBase.RESULT_TIMEOUT, null) + ) + } + + /** + * Get Suw fingerprint count extra for statistics + */ + fun getSuwFingerprintCountExtra(userId: Int) = Bundle().also { + it.putInt( + SetupFingerprintEnrollIntroduction.EXTRA_FINGERPRINT_ENROLLED_COUNT, + fingerprintRepository.getNumOfEnrolledFingerprintsSize(userId) + ) + } + + /** + * Gets the result about fingerprint enrollable + */ + fun isMaxEnrolledReached(userId: Int): Boolean = with(fingerprintRepository) { + maxFingerprints <= getNumOfEnrolledFingerprintsSize(userId) + } + + val canAssumeUdfps: Boolean + get() = fingerprintRepository.canAssumeUdfps() + + val canAssumeSfps: Boolean + get() = fingerprintRepository.canAssumeSfps() + + /** + * Update FINGERPRINT_SUGGESTION_ACTIVITY into package manager + */ + fun updateFingerprintSuggestionEnableState(userId: Int) { + val enrolled = fingerprintRepository.getNumOfEnrolledFingerprintsSize(userId) + // Only show "Add another fingerprint" if the user already enrolled one. + // "Add fingerprint" will be shown in the main flow if the user hasn't enrolled any + // fingerprints. If the user already added more than one fingerprint, they already know + // to add multiple fingerprints so we don't show the suggestion. + getApplication().packageManager.setComponentEnabledSetting( + ComponentName( + getApplication(), + FINGERPRINT_SUGGESTION_ACTIVITY + ), + if (enrolled == 1) + PackageManager.COMPONENT_ENABLED_STATE_ENABLED + else + PackageManager.COMPONENT_ENABLED_STATE_DISABLED, + PackageManager.DONT_KILL_APP + ) + Log.d(TAG, "$FINGERPRINT_SUGGESTION_ACTIVITY enabled state = ${enrolled == 1}") + } + + companion object { + private const val TAG = "FingerprintEnrollmentViewModel" + } +} diff --git a/tests/unit/src/com/android/settings/biometrics2/ui/viewmodel/FingerprintEnrollmentViewModelTest.java b/tests/unit/src/com/android/settings/biometrics2/ui/viewmodel/FingerprintEnrollmentViewModelTest.java deleted file mode 100644 index 73fab791e1a..00000000000 --- a/tests/unit/src/com/android/settings/biometrics2/ui/viewmodel/FingerprintEnrollmentViewModelTest.java +++ /dev/null @@ -1,338 +0,0 @@ -/* - * 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.biometrics2.ui.viewmodel; - -import static android.hardware.fingerprint.FingerprintSensorProperties.TYPE_UDFPS_OPTICAL; - -import static com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollmentViewModel.SAVED_STATE_IS_FIRST_FRAGMENT_ADDED; -import static com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollmentViewModel.SAVED_STATE_IS_NEW_FINGERPRINT_ADDED; -import static com.android.settings.biometrics2.utils.EnrollmentRequestUtils.newAllFalseRequest; -import static com.android.settings.biometrics2.utils.FingerprintRepositoryUtils.newFingerprintRepository; -import static com.android.settings.biometrics2.utils.FingerprintRepositoryUtils.setupFingerprintEnrolledFingerprints; - -import static com.google.common.truth.Truth.assertThat; - -import android.app.Application; -import android.content.Intent; -import android.hardware.fingerprint.FingerprintManager; -import android.os.Bundle; - -import androidx.activity.result.ActivityResult; -import androidx.test.core.app.ApplicationProvider; -import androidx.test.ext.junit.runners.AndroidJUnit4; - -import com.android.settings.biometrics2.data.repository.FingerprintRepository; -import com.android.settings.testutils.InstantTaskExecutorRule; - -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.junit.MockitoJUnit; -import org.mockito.junit.MockitoRule; - -@RunWith(AndroidJUnit4.class) -public class FingerprintEnrollmentViewModelTest { - - @Rule public final MockitoRule mockito = MockitoJUnit.rule(); - @Rule public final InstantTaskExecutorRule mTaskExecutorRule = new InstantTaskExecutorRule(); - - @Mock private FingerprintManager mFingerprintManager; - - private Application mApplication; - private FingerprintRepository mFingerprintRepository; - private FingerprintEnrollmentViewModel mViewModel; - - private FingerprintEnrollmentViewModel newViewModelInstance() { - return new FingerprintEnrollmentViewModel(mApplication, mFingerprintRepository, - newAllFalseRequest(mApplication)); - } - - @Before - public void setUp() { - mApplication = ApplicationProvider.getApplicationContext(); - mFingerprintRepository = newFingerprintRepository(mFingerprintManager, TYPE_UDFPS_OPTICAL, - 5); - mViewModel = newViewModelInstance(); - } - - @Test - public void testGetRequest() { - assertThat(mViewModel.getRequest()).isNotNull(); - } - - @Test - public void testIsWaitingActivityResult() { - // Default false - assertThat(mViewModel.isWaitingActivityResult().get()).isFalse(); - - // false if null bundle - mViewModel = newViewModelInstance(); - mViewModel.onRestoreInstanceState(null); - assertThat(mViewModel.isWaitingActivityResult().get()).isFalse(); - - // false if empty bundle - mViewModel.onRestoreInstanceState(new Bundle()); - assertThat(mViewModel.isWaitingActivityResult().get()).isFalse(); - - // False value can be saved during onSaveInstanceState() and restore after - // onSaveInstanceState() - final Bundle falseSavedInstance = new Bundle(); - mViewModel.onSaveInstanceState(falseSavedInstance); - final FingerprintEnrollmentViewModel falseViewModel = newViewModelInstance(); - falseViewModel.onRestoreInstanceState(falseSavedInstance); - assertThat(falseViewModel.isWaitingActivityResult().get()).isFalse(); - - // True value can be saved during onSaveInstanceState() and restore after - // onSaveInstanceState() - final Bundle trueSavedInstance = new Bundle(); - mViewModel.isWaitingActivityResult().set(true); - mViewModel.onSaveInstanceState(trueSavedInstance); - final FingerprintEnrollmentViewModel trueViewModel = newViewModelInstance(); - trueViewModel.onRestoreInstanceState(trueSavedInstance); - assertThat(trueViewModel.isWaitingActivityResult().get()).isTrue(); - } - - @Test - public void testIsNewFingerprintAdded() { - // Default false - final Bundle outBundle = new Bundle(); - mViewModel.onSaveInstanceState(outBundle); - assertThat(outBundle.containsKey(SAVED_STATE_IS_NEW_FINGERPRINT_ADDED)).isTrue(); - assertThat(outBundle.getBoolean(SAVED_STATE_IS_NEW_FINGERPRINT_ADDED)).isFalse(); - - // false if null bundle - mViewModel = newViewModelInstance(); - mViewModel.onRestoreInstanceState(null); - outBundle.clear(); - mViewModel.onSaveInstanceState(outBundle); - assertThat(outBundle.containsKey(SAVED_STATE_IS_NEW_FINGERPRINT_ADDED)).isTrue(); - assertThat(outBundle.getBoolean(SAVED_STATE_IS_NEW_FINGERPRINT_ADDED)).isFalse(); - - // false if empty bundle - mViewModel = newViewModelInstance(); - mViewModel.onRestoreInstanceState(new Bundle()); - outBundle.clear(); - mViewModel.onSaveInstanceState(outBundle); - assertThat(outBundle.containsKey(SAVED_STATE_IS_NEW_FINGERPRINT_ADDED)).isTrue(); - assertThat(outBundle.getBoolean(SAVED_STATE_IS_NEW_FINGERPRINT_ADDED)).isFalse(); - - // False value can be saved during onSaveInstanceState() and restore after - // onSaveInstanceState() - final Bundle falseSavedInstance = new Bundle(); - falseSavedInstance.putBoolean(SAVED_STATE_IS_NEW_FINGERPRINT_ADDED, false); - mViewModel.onRestoreInstanceState(falseSavedInstance); - outBundle.clear(); - mViewModel.onSaveInstanceState(outBundle); - assertThat(outBundle.containsKey(SAVED_STATE_IS_NEW_FINGERPRINT_ADDED)).isTrue(); - assertThat(outBundle.getBoolean(SAVED_STATE_IS_NEW_FINGERPRINT_ADDED)).isFalse(); - - // True value can be saved during onSaveInstanceState() and restore after - // onSaveInstanceState() - final Bundle trueSavedInstance = new Bundle(); - trueSavedInstance.putBoolean(SAVED_STATE_IS_NEW_FINGERPRINT_ADDED, true); - mViewModel.onRestoreInstanceState(trueSavedInstance); - outBundle.clear(); - mViewModel.onSaveInstanceState(outBundle); - assertThat(outBundle.containsKey(SAVED_STATE_IS_NEW_FINGERPRINT_ADDED)).isTrue(); - assertThat(outBundle.getBoolean(SAVED_STATE_IS_NEW_FINGERPRINT_ADDED)).isTrue(); - - // setIsFirstFragmentAdded() can be saved during onSaveInstanceState() - mViewModel.setIsFirstFragmentAdded(); - mViewModel.onSaveInstanceState(trueSavedInstance); - assertThat(trueSavedInstance.containsKey(SAVED_STATE_IS_NEW_FINGERPRINT_ADDED)).isTrue(); - assertThat(trueSavedInstance.getBoolean(SAVED_STATE_IS_NEW_FINGERPRINT_ADDED)).isTrue(); - } - - @Test - public void testIsFirstFragmentAdded() { - // Default false - final Bundle outBundle = new Bundle(); - mViewModel.onSaveInstanceState(outBundle); - assertThat(outBundle.containsKey(SAVED_STATE_IS_FIRST_FRAGMENT_ADDED)).isTrue(); - assertThat(outBundle.getBoolean(SAVED_STATE_IS_FIRST_FRAGMENT_ADDED)).isFalse(); - - // false if null bundle - mViewModel = newViewModelInstance(); - mViewModel.onRestoreInstanceState(null); - outBundle.clear(); - mViewModel.onSaveInstanceState(outBundle); - assertThat(outBundle.containsKey(SAVED_STATE_IS_FIRST_FRAGMENT_ADDED)).isTrue(); - assertThat(outBundle.getBoolean(SAVED_STATE_IS_FIRST_FRAGMENT_ADDED)).isFalse(); - - // false if empty bundle - mViewModel = newViewModelInstance(); - mViewModel.onRestoreInstanceState(new Bundle()); - outBundle.clear(); - mViewModel.onSaveInstanceState(outBundle); - assertThat(outBundle.containsKey(SAVED_STATE_IS_FIRST_FRAGMENT_ADDED)).isTrue(); - assertThat(outBundle.getBoolean(SAVED_STATE_IS_FIRST_FRAGMENT_ADDED)).isFalse(); - - // False value can be saved during onSaveInstanceState() and restore after - // onSaveInstanceState() - final Bundle falseSavedInstance = new Bundle(); - falseSavedInstance.putBoolean(SAVED_STATE_IS_FIRST_FRAGMENT_ADDED, false); - mViewModel.onRestoreInstanceState(falseSavedInstance); - outBundle.clear(); - mViewModel.onSaveInstanceState(outBundle); - assertThat(outBundle.containsKey(SAVED_STATE_IS_FIRST_FRAGMENT_ADDED)).isTrue(); - assertThat(outBundle.getBoolean(SAVED_STATE_IS_FIRST_FRAGMENT_ADDED)).isFalse(); - - // True value can be saved during onSaveInstanceState() and restore after - // onSaveInstanceState() - final Bundle trueSavedInstance = new Bundle(); - trueSavedInstance.putBoolean(SAVED_STATE_IS_FIRST_FRAGMENT_ADDED, true); - mViewModel.onRestoreInstanceState(trueSavedInstance); - outBundle.clear(); - mViewModel.onSaveInstanceState(outBundle); - assertThat(outBundle.containsKey(SAVED_STATE_IS_FIRST_FRAGMENT_ADDED)).isTrue(); - assertThat(outBundle.getBoolean(SAVED_STATE_IS_FIRST_FRAGMENT_ADDED)).isTrue(); - - // setIsFirstFragmentAdded() can be saved during onSaveInstanceState() - mViewModel.setIsFirstFragmentAdded(); - mViewModel.onSaveInstanceState(trueSavedInstance); - assertThat(trueSavedInstance.containsKey(SAVED_STATE_IS_FIRST_FRAGMENT_ADDED)).isTrue(); - assertThat(trueSavedInstance.getBoolean(SAVED_STATE_IS_FIRST_FRAGMENT_ADDED)).isTrue(); - } - - @Test - public void testOverrideActivityResult_shallKeepNullIntent_woChallengeExtra() { - final ActivityResult retResult = mViewModel.getOverrideActivityResult( - new ActivityResult(22, null), null); - - assertThat(retResult).isNotNull(); - assertThat(retResult.getData()).isNull(); - } - - @Test - public void testOverrideActivityResult_shallKeepNullIntent_noIntent_woChallengeExtra() { - final Intent intent = new Intent(); - - final ActivityResult retResult = mViewModel.getOverrideActivityResult( - new ActivityResult(33, intent), null); - - assertThat(retResult).isNotNull(); - assertThat(retResult.getData()).isEqualTo(intent); - } - - @Test - public void testOverrideActivityResult_shallKeepNull_woAdded_woIntent_withChallenge() { - final Bundle extra = new Bundle(); - extra.putString("test1", "test123"); - - final ActivityResult retResult = mViewModel.getOverrideActivityResult( - new ActivityResult(33, null), extra); - - assertThat(retResult).isNotNull(); - assertThat(retResult.getData()).isNull(); - } - - @Test - public void testOverrideActivityResult_shallCreateNew_woIntent_withChallenge() { - final String key1 = "test1"; - final String key2 = "test2"; - final Bundle extra = new Bundle(); - extra.putString(key1, "test123"); - extra.putInt(key2, 9999); - - mViewModel.setIsNewFingerprintAdded(); - final ActivityResult retResult = mViewModel.getOverrideActivityResult( - new ActivityResult(33, null), extra); - - assertThat(retResult).isNotNull(); - final Intent retIntent = retResult.getData(); - assertThat(retIntent).isNotNull(); - final Bundle retExtra = retIntent.getExtras(); - assertThat(retExtra).isNotNull(); - assertThat(retExtra.getSize()).isEqualTo(extra.getSize()); - assertThat(retExtra.getString(key1)).isEqualTo(extra.getString(key1)); - assertThat(retExtra.getInt(key2)).isEqualTo(extra.getInt(key2)); - } - - @Test - public void testOverrideActivityResult_shallNotMerge_nonAdded_woIntent_withChallenge() { - final Bundle extra = new Bundle(); - extra.putString("test2", "test123"); - - final Intent intent = new Intent(); - final String key2 = "test2"; - intent.putExtra(key2, 3456L); - - final ActivityResult retResult = mViewModel.getOverrideActivityResult( - new ActivityResult(33, intent), extra); - - assertThat(retResult).isNotNull(); - final Intent retIntent = retResult.getData(); - assertThat(retIntent).isNotNull(); - final Bundle retExtra = retIntent.getExtras(); - assertThat(retExtra).isNotNull(); - assertThat(retExtra.getSize()).isEqualTo(intent.getExtras().getSize()); - assertThat(retExtra.getString(key2)).isEqualTo(intent.getExtras().getString(key2)); - } - - @Test - public void testOverrideActivityResult_shallMerge_added_woIntent_withChallenge() { - final String key1 = "test1"; - final String key2 = "test2"; - final Bundle extra = new Bundle(); - extra.putString(key1, "test123"); - extra.putInt(key2, 9999); - - final Intent intent = new Intent(); - final String key3 = "test3"; - intent.putExtra(key3, 3456L); - - mViewModel.setIsNewFingerprintAdded(); - final ActivityResult retResult = mViewModel.getOverrideActivityResult( - new ActivityResult(33, intent), extra); - - assertThat(retResult).isNotNull(); - final Intent retIntent = retResult.getData(); - assertThat(retIntent).isNotNull(); - final Bundle retExtra = retIntent.getExtras(); - assertThat(retExtra).isNotNull(); - assertThat(retExtra.getSize()).isEqualTo(extra.getSize() + intent.getExtras().getSize()); - assertThat(retExtra.getString(key1)).isEqualTo(extra.getString(key1)); - assertThat(retExtra.getInt(key2)).isEqualTo(extra.getInt(key2)); - assertThat(retExtra.getLong(key3)).isEqualTo(intent.getExtras().getLong(key3)); - } - - @Test - public void testIsMaxEnrolledReached() { - final int uid = 100; - mFingerprintRepository = newFingerprintRepository(mFingerprintManager, TYPE_UDFPS_OPTICAL, - 3); - mViewModel = new FingerprintEnrollmentViewModel(mApplication, mFingerprintRepository, - newAllFalseRequest(mApplication)); - - setupFingerprintEnrolledFingerprints(mFingerprintManager, uid, 0); - assertThat(mViewModel.isMaxEnrolledReached(uid)).isFalse(); - - setupFingerprintEnrolledFingerprints(mFingerprintManager, uid, 1); - assertThat(mViewModel.isMaxEnrolledReached(uid)).isFalse(); - - setupFingerprintEnrolledFingerprints(mFingerprintManager, uid, 2); - assertThat(mViewModel.isMaxEnrolledReached(uid)).isFalse(); - - setupFingerprintEnrolledFingerprints(mFingerprintManager, uid, 3); - assertThat(mViewModel.isMaxEnrolledReached(uid)).isTrue(); - - setupFingerprintEnrolledFingerprints(mFingerprintManager, uid, 4); - assertThat(mViewModel.isMaxEnrolledReached(uid)).isTrue(); - } -} diff --git a/tests/unit/src/com/android/settings/biometrics2/ui/viewmodel/FingerprintEnrollmentViewModelTest.kt b/tests/unit/src/com/android/settings/biometrics2/ui/viewmodel/FingerprintEnrollmentViewModelTest.kt new file mode 100644 index 00000000000..9f339de4375 --- /dev/null +++ b/tests/unit/src/com/android/settings/biometrics2/ui/viewmodel/FingerprintEnrollmentViewModelTest.kt @@ -0,0 +1,222 @@ +/* + * 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.biometrics2.ui.viewmodel + +import android.app.Application +import android.content.Intent +import android.hardware.fingerprint.FingerprintManager +import android.hardware.fingerprint.FingerprintSensorProperties +import android.os.Bundle +import androidx.activity.result.ActivityResult +import androidx.test.core.app.ApplicationProvider +import androidx.test.ext.junit.runners.AndroidJUnit4 +import com.android.settings.biometrics2.data.repository.FingerprintRepository +import com.android.settings.biometrics2.utils.EnrollmentRequestUtils.newAllFalseRequest +import com.android.settings.biometrics2.utils.FingerprintRepositoryUtils.newFingerprintRepository +import com.android.settings.biometrics2.utils.FingerprintRepositoryUtils.setupFingerprintEnrolledFingerprints +import com.android.settings.testutils.InstantTaskExecutorRule +import com.google.common.truth.Truth +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.Mock +import org.mockito.junit.MockitoJUnit +import org.mockito.junit.MockitoRule + +@RunWith(AndroidJUnit4::class) +class FingerprintEnrollmentViewModelTest { + + @get:Rule val mockito: MockitoRule = MockitoJUnit.rule() + + @get:Rule val taskExecutorRule = InstantTaskExecutorRule() + + private val application: Application + get() = ApplicationProvider.getApplicationContext() + + @Mock + private lateinit var fingerprintManager: FingerprintManager + + private lateinit var fingerprintRepository: FingerprintRepository + private lateinit var viewModel: FingerprintEnrollmentViewModel + + @Before + fun setUp() { + fingerprintRepository = newFingerprintRepository( + fingerprintManager, + FingerprintSensorProperties.TYPE_UDFPS_OPTICAL, + 5 + ) + viewModel = FingerprintEnrollmentViewModel( + application, + fingerprintRepository, + newAllFalseRequest(application) + ) + } + + @Test + fun testGetRequest() { + Truth.assertThat(viewModel.request).isNotNull() + } + + @Test + fun testIsWaitingActivityResultDefaultFalse() { + Truth.assertThat(viewModel.isWaitingActivityResult.value).isFalse() + } + + + @Test + fun testOverrideActivityResult_shallKeepNullIntent_woChallengeExtra() { + val retResult = viewModel.getOverrideActivityResult( + ActivityResult(22, null), null + ) + Truth.assertThat(retResult).isNotNull() + Truth.assertThat(retResult.data).isNull() + } + + @Test + fun testOverrideActivityResult_shallKeepNullIntent_noIntent_woChallengeExtra() { + val intent = Intent() + val retResult = viewModel.getOverrideActivityResult( + ActivityResult(33, intent), null + ) + Truth.assertThat(retResult).isNotNull() + Truth.assertThat(retResult.data).isEqualTo(intent) + } + + @Test + fun testOverrideActivityResult_shallKeepNull_woAdded_woIntent_withChallenge() { + val extra = Bundle() + extra.putString("test1", "test123") + + val retResult = viewModel.getOverrideActivityResult( + ActivityResult(33, null), extra + ) + + Truth.assertThat(retResult).isNotNull() + Truth.assertThat(retResult.data).isNull() + } + + @Test + fun testOverrideActivityResult_shallCreateNew_woIntent_withChallenge() { + val key1 = "test1" + val key2 = "test2" + val extra = Bundle().apply { + putString(key1, "test123") + putInt(key2, 9999) + } + + viewModel.isNewFingerprintAdded = true + + val retResult = viewModel.getOverrideActivityResult( + ActivityResult(33, null), extra + ) + Truth.assertThat(retResult).isNotNull() + + val retIntent = retResult.data + Truth.assertThat(retIntent).isNotNull() + + val retExtra = retIntent!!.extras + Truth.assertThat(retExtra).isNotNull() + Truth.assertThat(retExtra!!.size).isEqualTo(extra.size) + Truth.assertThat(retExtra.getString(key1)).isEqualTo(extra.getString(key1)) + Truth.assertThat(retExtra.getInt(key2)).isEqualTo(extra.getInt(key2)) + } + + @Test + fun testOverrideActivityResult_shallNotMerge_nonAdded_woIntent_withChallenge() { + val extra = Bundle().apply { + putString("test2", "test123") + } + + val key2 = "test2" + val intent = Intent().apply { + putExtra(key2, 3456L) + } + + val retResult = viewModel.getOverrideActivityResult(ActivityResult(33, intent), extra) + + Truth.assertThat(retResult).isNotNull() + + val retIntent = retResult.data + Truth.assertThat(retIntent).isNotNull() + + val retExtra = retIntent!!.extras + Truth.assertThat(retExtra).isNotNull() + Truth.assertThat(retExtra!!.size).isEqualTo(intent.extras!!.size) + Truth.assertThat(retExtra.getString(key2)).isEqualTo(intent.extras!!.getString(key2)) + } + + @Test + fun testOverrideActivityResult_shallMerge_added_woIntent_withChallenge() { + val key1 = "test1" + val key2 = "test2" + val extra = Bundle().apply { + putString(key1, "test123") + putInt(key2, 9999) + } + + val key3 = "test3" + val intent = Intent().apply { + putExtra(key3, 3456L) + } + + viewModel.isNewFingerprintAdded = true + + val retResult = viewModel.getOverrideActivityResult(ActivityResult(33, intent), extra) + Truth.assertThat(retResult).isNotNull() + + val retIntent = retResult.data + Truth.assertThat(retIntent).isNotNull() + + val retExtra = retIntent!!.extras + Truth.assertThat(retExtra).isNotNull() + Truth.assertThat(retExtra!!.size).isEqualTo(extra.size + intent.extras!!.size) + Truth.assertThat(retExtra.getString(key1)).isEqualTo(extra.getString(key1)) + Truth.assertThat(retExtra.getInt(key2)).isEqualTo(extra.getInt(key2)) + Truth.assertThat(retExtra.getLong(key3)).isEqualTo(intent.extras!!.getLong(key3)) + } + + @Test + fun testIsMaxEnrolledReached() { + val uid = 100 + fingerprintRepository = newFingerprintRepository( + fingerprintManager, + FingerprintSensorProperties.TYPE_UDFPS_OPTICAL, + 3 + ) + viewModel = FingerprintEnrollmentViewModel( + application, + fingerprintRepository, + newAllFalseRequest(application) + ) + + setupFingerprintEnrolledFingerprints(fingerprintManager, uid, 0) + Truth.assertThat(viewModel.isMaxEnrolledReached(uid)).isFalse() + + setupFingerprintEnrolledFingerprints(fingerprintManager, uid, 1) + Truth.assertThat(viewModel.isMaxEnrolledReached(uid)).isFalse() + + setupFingerprintEnrolledFingerprints(fingerprintManager, uid, 2) + Truth.assertThat(viewModel.isMaxEnrolledReached(uid)).isFalse() + + setupFingerprintEnrolledFingerprints(fingerprintManager, uid, 3) + Truth.assertThat(viewModel.isMaxEnrolledReached(uid)).isTrue() + + setupFingerprintEnrolledFingerprints(fingerprintManager, uid, 4) + Truth.assertThat(viewModel.isMaxEnrolledReached(uid)).isTrue() + } +}