[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
This commit is contained in:
Milton Wu
2023-07-11 14:54:56 +08:00
parent f94932801a
commit 9a7afc9216
5 changed files with 398 additions and 590 deletions

View File

@@ -130,6 +130,8 @@ open class FingerprintEnrollmentActivity : FragmentActivity() {
viewModelProvider[FingerprintEnrollErrorDialogViewModel::class.java]
}
private var isFirstFragmentAdded = false
private val introActionObserver: Observer<Int> = Observer<Int> { 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<out Fragment>, 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<out Fragment> = if (viewModel.canAssumeUdfps()) {
val fragmentClass: Class<out Fragment> = 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<out Fragment> = if (viewModel.canAssumeUdfps()) {
val fragmentClass: Class<out Fragment> = 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)
}

View File

@@ -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<ActivityResult> 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<ActivityResult> 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));
}
}

View File

@@ -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<ActivityResult>()
val setResultLiveData: LiveData<ActivityResult>
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<Application>().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"
}
}

View File

@@ -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();
}
}

View File

@@ -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()
}
}