[BiometricsV2] Rewrite Activity to Kotlin

Refactor FingerprintEnrollmentActivity as kotlin

Bug: 286197261
Test: atest FingerprintEnrollmentActivity
Test: atest biometrics-enrollment-test
Change-Id: I45d2db832b0111cb865b657aee56f84b0b295efa
This commit is contained in:
Milton Wu
2023-06-16 17:21:22 +08:00
parent 5cc530b6d5
commit 940e5d04a6
5 changed files with 1281 additions and 1260 deletions

View File

@@ -1,637 +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.view;
import static androidx.activity.result.contract.ActivityResultContracts.StartActivityForResult;
import static androidx.fragment.app.FragmentManager.POP_BACK_STACK_INCLUSIVE;
import static androidx.lifecycle.ViewModelProvider.AndroidViewModelFactory.APPLICATION_KEY;
import static com.android.settings.biometrics2.factory.BiometricsViewModelFactory.CHALLENGE_GENERATOR_KEY;
import static com.android.settings.biometrics2.factory.BiometricsViewModelFactory.ENROLLMENT_REQUEST_KEY;
import static com.android.settings.biometrics2.factory.BiometricsViewModelFactory.USER_ID_KEY;
import static com.android.settings.biometrics2.ui.viewmodel.AutoCredentialViewModel.CREDENTIAL_FAIL_NEED_TO_CHOOSE_LOCK;
import static com.android.settings.biometrics2.ui.viewmodel.AutoCredentialViewModel.CREDENTIAL_FAIL_NEED_TO_CONFIRM_LOCK;
import static com.android.settings.biometrics2.ui.viewmodel.AutoCredentialViewModel.CREDENTIAL_IS_GENERATING_CHALLENGE;
import static com.android.settings.biometrics2.ui.viewmodel.AutoCredentialViewModel.CREDENTIAL_VALID;
import static com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollEnrollingViewModel.ErrorDialogData;
import static com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollEnrollingViewModel.FINGERPRINT_ENROLL_ENROLLING_ACTION_DONE;
import static com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollEnrollingViewModel.FINGERPRINT_ENROLL_ENROLLING_ACTION_SHOW_ICON_TOUCH_DIALOG;
import static com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollEnrollingViewModel.FINGERPRINT_ENROLL_ENROLLING_CANCELED_BECAUSE_BACK_PRESSED;
import static com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollEnrollingViewModel.FINGERPRINT_ENROLL_ENROLLING_CANCELED_BECAUSE_USER_SKIP;
import static com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollEnrollingViewModel.FINGERPRINT_ERROR_DIALOG_ACTION_SET_RESULT_FINISH;
import static com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollEnrollingViewModel.FINGERPRINT_ERROR_DIALOG_ACTION_SET_RESULT_TIMEOUT;
import static com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollEnrollingViewModel.FingerprintEnrollEnrollingAction;
import static com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollEnrollingViewModel.FingerprintErrorDialogAction;
import static com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollFindSensorViewModel.FINGERPRINT_ENROLL_FIND_SENSOR_ACTION_DIALOG;
import static com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollFindSensorViewModel.FINGERPRINT_ENROLL_FIND_SENSOR_ACTION_SKIP;
import static com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollFindSensorViewModel.FINGERPRINT_ENROLL_FIND_SENSOR_ACTION_START;
import static com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollFindSensorViewModel.FingerprintEnrollFindSensorAction;
import static com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollFinishViewModel.FINGERPRINT_ENROLL_FINISH_ACTION_ADD_BUTTON_CLICK;
import static com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollFinishViewModel.FINGERPRINT_ENROLL_FINISH_ACTION_NEXT_BUTTON_CLICK;
import static com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollFinishViewModel.FingerprintEnrollFinishAction;
import static com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollIntroViewModel.FINGERPRINT_ENROLL_INTRO_ACTION_CONTINUE_ENROLL;
import static com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollIntroViewModel.FINGERPRINT_ENROLL_INTRO_ACTION_DONE_AND_FINISH;
import static com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollIntroViewModel.FINGERPRINT_ENROLL_INTRO_ACTION_SKIP_OR_CANCEL;
import static com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollIntroViewModel.FingerprintEnrollIntroAction;
import android.annotation.StyleRes;
import android.app.Application;
import android.content.Intent;
import android.content.res.ColorStateList;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Color;
import android.os.Bundle;
import android.os.SystemClock;
import android.util.Log;
import androidx.activity.result.ActivityResult;
import androidx.activity.result.ActivityResultCallback;
import androidx.activity.result.ActivityResultLauncher;
import androidx.annotation.ColorInt;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentActivity;
import androidx.lifecycle.Observer;
import androidx.lifecycle.ViewModelProvider;
import androidx.lifecycle.viewmodel.CreationExtras;
import androidx.lifecycle.viewmodel.MutableCreationExtras;
import com.android.settings.R;
import com.android.settings.Utils;
import com.android.settings.biometrics.BiometricEnrollBase;
import com.android.settings.biometrics2.data.repository.FingerprintRepository;
import com.android.settings.biometrics2.factory.BiometricsViewModelFactory;
import com.android.settings.biometrics2.ui.model.CredentialModel;
import com.android.settings.biometrics2.ui.model.EnrollmentRequest;
import com.android.settings.biometrics2.ui.viewmodel.AutoCredentialViewModel;
import com.android.settings.biometrics2.ui.viewmodel.AutoCredentialViewModel.FingerprintChallengeGenerator;
import com.android.settings.biometrics2.ui.viewmodel.DeviceFoldedViewModel;
import com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollEnrollingViewModel;
import com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollFindSensorViewModel;
import com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollFinishViewModel;
import com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollIntroViewModel;
import com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollProgressViewModel;
import com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollmentViewModel;
import com.android.settings.overlay.FeatureFactory;
import com.google.android.setupdesign.util.ThemeHelper;
/**
* Fingerprint enrollment activity implementation
*/
public class FingerprintEnrollmentActivity extends FragmentActivity {
/**
* Setupwizard activity
*/
public static class SetupActivity extends FingerprintEnrollmentActivity {}
/**
* Internal activity for FingerprintSettings
*/
public static class InternalActivity extends FingerprintEnrollmentActivity {}
private static final boolean DEBUG = false;
private static final String TAG = "FingerprintEnrollmentActivity";
private static final String INTRO_TAG = "intro";
private static final String FIND_SENSOR_TAG = "find-sensor";
private static final String ENROLLING_TAG = "enrolling";
private static final String FINISH_TAG = "finish";
private static final String SKIP_SETUP_FIND_FPS_DIALOG_TAG = "skip-setup-dialog";
private static final String ENROLLING_ERROR_DIALOG_TAG = "enrolling-error-dialog";
protected static final int LAUNCH_CONFIRM_LOCK_ACTIVITY = 1;
// This flag is used for addBackStack(), we do not save it in ViewModel because it is just used
// during FragmentManager calls
private boolean mIsFirstFragmentAdded = false;
private ViewModelProvider mViewModelProvider;
private FingerprintEnrollmentViewModel mViewModel;
private AutoCredentialViewModel mAutoCredentialViewModel;
private final Observer<Integer> mIntroActionObserver = action -> {
if (DEBUG) {
Log.d(TAG, "mIntroActionObserver(" + action + ")");
}
if (action != null) {
onIntroAction(action);
}
};
private final Observer<Integer> mFindSensorActionObserver = action -> {
if (DEBUG) {
Log.d(TAG, "mFindSensorActionObserver(" + action + ")");
}
if (action != null) {
onFindSensorAction(action);
}
};
private final Observer<Integer> mEnrollingActionObserver = action -> {
if (DEBUG) {
Log.d(TAG, "mEnrollingActionObserver(" + action + ")");
}
if (action != null) {
onEnrollingAction(action);
}
};
private final Observer<ErrorDialogData> mEnrollingErrorDialogObserver = data -> {
if (DEBUG) {
Log.d(TAG, "mEnrollingErrorDialogObserver(" + data + ")");
}
if (data != null) {
new FingerprintEnrollEnrollingErrorDialog().show(getSupportFragmentManager(),
ENROLLING_ERROR_DIALOG_TAG);
}
};
private final Observer<Integer> mEnrollingErrorDialogActionObserver = action -> {
if (DEBUG) {
Log.d(TAG, "mEnrollingErrorDialogActionObserver(" + action + ")");
}
if (action != null) {
onEnrollingErrorDialogAction(action);
}
};
private final Observer<Integer> mFinishActionObserver = action -> {
if (DEBUG) {
Log.d(TAG, "mFinishActionObserver(" + action + ")");
}
if (action != null) {
onFinishAction(action);
}
};
private final ActivityResultCallback<ActivityResult> mChooseLockResultCallback =
result -> onChooseOrConfirmLockResult(true /* isChooseLock */, result);
private final ActivityResultLauncher<Intent> mChooseLockLauncher =
registerForActivityResult(new StartActivityForResult(), mChooseLockResultCallback);
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mViewModelProvider = new ViewModelProvider(this);
mViewModel = mViewModelProvider.get(FingerprintEnrollmentViewModel.class);
mViewModel.setSavedInstanceState(savedInstanceState);
mAutoCredentialViewModel = mViewModelProvider.get(AutoCredentialViewModel.class);
mAutoCredentialViewModel.setCredentialModel(savedInstanceState, getIntent());
// Theme
setTheme(mViewModel.getRequest().getTheme());
ThemeHelper.trySetDynamicColor(this);
getWindow().setStatusBarColor(android.graphics.Color.TRANSPARENT);
// fragment
setContentView(R.layout.biometric_enrollment_container);
final Fragment fragment = getSupportFragmentManager().findFragmentById(
R.id.fragment_container_view);
if (DEBUG) {
Log.d(TAG, "onCreate() has savedInstance:" + (savedInstanceState != null)
+ ", fragment:" + fragment);
}
if (fragment == null) {
checkCredential();
final EnrollmentRequest request = mViewModel.getRequest();
if (request.isSkipFindSensor()) {
startEnrollingFragment();
} else if (request.isSkipIntro()) {
startFindSensorFragment();
} else {
startIntroFragment();
}
} else {
final String tag = fragment.getTag();
if (INTRO_TAG.equals(tag)) {
attachIntroViewModel();
} else if (FIND_SENSOR_TAG.equals(tag)) {
attachFindSensorViewModel();
attachIntroViewModel();
} else if (ENROLLING_TAG.equals(tag)) {
attachEnrollingViewModel();
attachFindSensorViewModel();
attachIntroViewModel();
} else if (FINISH_TAG.equals(tag)) {
attachFinishViewModel();
attachFindSensorViewModel();
attachIntroViewModel();
} else {
Log.e(TAG, "fragment tag " + tag + " not found");
finish();
return;
}
}
// observe LiveData
mViewModel.getSetResultLiveData().observe(this, this::onSetActivityResult);
mAutoCredentialViewModel.getGenerateChallengeFailedLiveData().observe(this,
this::onGenerateChallengeFailed);
}
private void startFragment(@NonNull Class<? extends Fragment> fragmentClass,
@NonNull String tag) {
if (!mIsFirstFragmentAdded) {
getSupportFragmentManager().beginTransaction()
.setReorderingAllowed(true)
.replace(R.id.fragment_container_view, fragmentClass, null, tag)
.commit();
mIsFirstFragmentAdded = true;
} else {
getSupportFragmentManager().beginTransaction()
.setReorderingAllowed(true)
.setCustomAnimations(R.anim.shared_x_axis_activity_open_enter_dynamic_color,
R.anim.shared_x_axis_activity_open_exit,
R.anim.shared_x_axis_activity_close_enter_dynamic_color,
R.anim.shared_x_axis_activity_close_exit)
.replace(R.id.fragment_container_view, fragmentClass, null, tag)
.addToBackStack(tag)
.commit();
}
}
private void startIntroFragment() {
attachIntroViewModel();
startFragment(FingerprintEnrollIntroFragment.class, INTRO_TAG);
}
private void attachIntroViewModel() {
final EnrollmentRequest request = mViewModel.getRequest();
if (request.isSkipIntro() || request.isSkipFindSensor()) {
return;
}
final FingerprintEnrollIntroViewModel introViewModel =
mViewModelProvider.get(FingerprintEnrollIntroViewModel.class);
// Clear ActionLiveData in FragmentViewModel to prevent getting previous action during
// recreate, like press 'Agree' then press 'back' in FingerprintEnrollFindSensor activity.
introViewModel.clearActionLiveData();
introViewModel.getActionLiveData().observe(this, mIntroActionObserver);
}
// We need to make sure token is valid before entering find sensor page
private void startFindSensorFragment() {
// Always setToken into progressViewModel even it is not necessary action for UDFPS
mViewModelProvider.get(FingerprintEnrollProgressViewModel.class)
.setToken(mAutoCredentialViewModel.getToken());
attachFindSensorViewModel();
final Class<? extends Fragment> fragmentClass;
if (mViewModel.canAssumeUdfps()) {
fragmentClass = FingerprintEnrollFindUdfpsFragment.class;
} else if (mViewModel.canAssumeSfps()) {
fragmentClass = FingerprintEnrollFindSfpsFragment.class;
} else {
fragmentClass = FingerprintEnrollFindRfpsFragment.class;
}
startFragment(fragmentClass, FIND_SENSOR_TAG);
}
private void attachFindSensorViewModel() {
if (mViewModel.getRequest().isSkipFindSensor()) {
return;
}
final FingerprintEnrollFindSensorViewModel findSensorViewModel =
mViewModelProvider.get(FingerprintEnrollFindSensorViewModel.class);
// Clear ActionLiveData in FragmentViewModel to prevent getting previous action during
// recreate, like press 'Start' then press 'back' in FingerprintEnrollEnrolling activity.
findSensorViewModel.clearActionLiveData();
findSensorViewModel.getActionLiveData().observe(this, mFindSensorActionObserver);
}
private void startEnrollingFragment() {
// Always setToken into progressViewModel even it is not necessary action for SFPS or RFPS
mViewModelProvider.get(FingerprintEnrollProgressViewModel.class)
.setToken(mAutoCredentialViewModel.getToken());
attachEnrollingViewModel();
final Class<? extends Fragment> fragmentClass;
if (mViewModel.canAssumeUdfps()) {
fragmentClass = FingerprintEnrollEnrollingUdfpsFragment.class;
} else if (mViewModel.canAssumeSfps()) {
fragmentClass = FingerprintEnrollEnrollingSfpsFragment.class;
} else {
fragmentClass = FingerprintEnrollEnrollingRfpsFragment.class;
}
startFragment(fragmentClass, ENROLLING_TAG);
}
private void attachEnrollingViewModel() {
final FingerprintEnrollEnrollingViewModel enrollingViewModel =
mViewModelProvider.get(FingerprintEnrollEnrollingViewModel.class);
enrollingViewModel.clearActionLiveData();
enrollingViewModel.getActionLiveData().observe(this, mEnrollingActionObserver);
enrollingViewModel.getErrorDialogLiveData().observe(this, mEnrollingErrorDialogObserver);
enrollingViewModel.getErrorDialogActionLiveData().observe(this,
mEnrollingErrorDialogActionObserver);
}
private void startFinishFragment() {
mViewModel.setIsNewFingerprintAdded();
attachFinishViewModel();
if (mViewModel.getRequest().isSkipFindSensor()) {
// Set page to Finish
getSupportFragmentManager().beginTransaction()
.setReorderingAllowed(true)
.setCustomAnimations(R.anim.shared_x_axis_activity_open_enter_dynamic_color,
R.anim.shared_x_axis_activity_open_exit,
R.anim.shared_x_axis_activity_close_enter_dynamic_color,
R.anim.shared_x_axis_activity_close_exit)
.replace(R.id.fragment_container_view, FingerprintEnrollFinishFragment.class,
null, FINISH_TAG)
.commit();
} else {
// Remove Enrolling page
getSupportFragmentManager().popBackStack();
// Remove old Finish page if any
if (getSupportFragmentManager().findFragmentByTag(FINISH_TAG) != null) {
getSupportFragmentManager().popBackStack(FINISH_TAG, POP_BACK_STACK_INCLUSIVE);
}
// Remove FindSensor page if maxEnrolled
if (mViewModel.isMaxEnrolledReached(mAutoCredentialViewModel.getUserId())
&& getSupportFragmentManager().findFragmentByTag(FIND_SENSOR_TAG) != null) {
getSupportFragmentManager().popBackStack(FIND_SENSOR_TAG, POP_BACK_STACK_INCLUSIVE);
}
// Add Finish page
getSupportFragmentManager().beginTransaction()
.setReorderingAllowed(true)
.setCustomAnimations(R.anim.shared_x_axis_activity_open_enter_dynamic_color,
R.anim.shared_x_axis_activity_open_exit,
R.anim.shared_x_axis_activity_close_enter_dynamic_color,
R.anim.shared_x_axis_activity_close_exit)
.replace(R.id.fragment_container_view, FingerprintEnrollFinishFragment.class,
null, FINISH_TAG)
.addToBackStack(FINISH_TAG)
.commit();
}
}
private void attachFinishViewModel() {
final FingerprintEnrollFinishViewModel viewModel =
mViewModelProvider.get(FingerprintEnrollFinishViewModel.class);
viewModel.clearActionLiveData();
viewModel.getActionLiveData().observe(this, mFinishActionObserver);
}
private void onGenerateChallengeFailed(@NonNull Boolean ignoredBoolean) {
onSetActivityResult(new ActivityResult(RESULT_CANCELED, null));
}
private void onSetActivityResult(@NonNull ActivityResult result) {
final Bundle challengeExtras = mAutoCredentialViewModel.createGeneratingChallengeExtras();
final ActivityResult overrideResult = mViewModel.getOverrideActivityResult(
result, challengeExtras);
if (DEBUG) {
Log.d(TAG, "onSetActivityResult(" + result + "), override:" + overrideResult
+ ") challengeExtras:" + challengeExtras);
}
setResult(overrideResult.getResultCode(), overrideResult.getData());
finish();
}
private void checkCredential() {
switch (mAutoCredentialViewModel.checkCredential()) {
case CREDENTIAL_FAIL_NEED_TO_CHOOSE_LOCK: {
final Intent intent = mAutoCredentialViewModel.createChooseLockIntent(this,
mViewModel.getRequest().isSuw(), mViewModel.getRequest().getSuwExtras());
if (!mViewModel.isWaitingActivityResult().compareAndSet(false, true)) {
Log.w(TAG, "chooseLock, fail to set isWaiting flag to true");
}
mChooseLockLauncher.launch(intent);
return;
}
case CREDENTIAL_FAIL_NEED_TO_CONFIRM_LOCK: {
final boolean launched = mAutoCredentialViewModel.createConfirmLockLauncher(
this,
LAUNCH_CONFIRM_LOCK_ACTIVITY,
getString(R.string.security_settings_fingerprint_preference_title)
).launch();
if (!launched) {
// This shouldn't happen, as we should only end up at this step if a lock thingy
// is already set.
Log.e(TAG, "confirmLock, launched is true");
finish();
} else if (!mViewModel.isWaitingActivityResult().compareAndSet(false, true)) {
Log.w(TAG, "confirmLock, fail to set isWaiting flag to true");
}
return;
}
case CREDENTIAL_VALID:
case CREDENTIAL_IS_GENERATING_CHALLENGE: {
// Do nothing
}
}
}
private void onChooseOrConfirmLockResult(boolean isChooseLock,
@NonNull ActivityResult activityResult) {
if (!mViewModel.isWaitingActivityResult().compareAndSet(true, false)) {
Log.w(TAG, "isChooseLock:" + isChooseLock + ", fail to unset waiting flag");
}
if (mAutoCredentialViewModel.checkNewCredentialFromActivityResult(
isChooseLock, activityResult)) {
overridePendingTransition(R.anim.sud_slide_next_in, R.anim.sud_slide_next_out);
} else {
onSetActivityResult(activityResult);
}
}
private void onIntroAction(@FingerprintEnrollIntroAction int action) {
switch (action) {
case FINGERPRINT_ENROLL_INTRO_ACTION_DONE_AND_FINISH: {
onSetActivityResult(
new ActivityResult(BiometricEnrollBase.RESULT_FINISHED, null));
return;
}
case FINGERPRINT_ENROLL_INTRO_ACTION_SKIP_OR_CANCEL: {
onSetActivityResult(
new ActivityResult(BiometricEnrollBase.RESULT_SKIP, null));
return;
}
case FINGERPRINT_ENROLL_INTRO_ACTION_CONTINUE_ENROLL: {
startFindSensorFragment();
}
}
}
private void onFindSensorAction(@FingerprintEnrollFindSensorAction int action) {
switch (action) {
case FINGERPRINT_ENROLL_FIND_SENSOR_ACTION_SKIP: {
onSetActivityResult(new ActivityResult(BiometricEnrollBase.RESULT_SKIP, null));
return;
}
case FINGERPRINT_ENROLL_FIND_SENSOR_ACTION_DIALOG: {
new SkipSetupFindFpsDialog().show(getSupportFragmentManager(),
SKIP_SETUP_FIND_FPS_DIALOG_TAG);
return;
}
case FINGERPRINT_ENROLL_FIND_SENSOR_ACTION_START: {
startEnrollingFragment();
}
}
}
private void onEnrollingAction(@FingerprintEnrollEnrollingAction int action) {
switch (action) {
case FINGERPRINT_ENROLL_ENROLLING_ACTION_DONE: {
startFinishFragment();
break;
}
case FINGERPRINT_ENROLL_ENROLLING_CANCELED_BECAUSE_USER_SKIP: {
onSetActivityResult(new ActivityResult(BiometricEnrollBase.RESULT_SKIP, null));
break;
}
case FINGERPRINT_ENROLL_ENROLLING_ACTION_SHOW_ICON_TOUCH_DIALOG: {
new FingerprintEnrollEnrollingIconTouchDialog().show(getSupportFragmentManager(),
SKIP_SETUP_FIND_FPS_DIALOG_TAG);
break;
}
case FINGERPRINT_ENROLL_ENROLLING_CANCELED_BECAUSE_BACK_PRESSED: {
if (getSupportFragmentManager().getBackStackEntryCount() > 0) {
getSupportFragmentManager().popBackStack();
} else {
onSetActivityResult(new ActivityResult(RESULT_CANCELED, null));
}
break;
}
}
}
private void onEnrollingErrorDialogAction(@FingerprintErrorDialogAction int action) {
switch (action) {
case FINGERPRINT_ERROR_DIALOG_ACTION_SET_RESULT_FINISH:
onSetActivityResult(new ActivityResult(BiometricEnrollBase.RESULT_FINISHED, null));
break;
case FINGERPRINT_ERROR_DIALOG_ACTION_SET_RESULT_TIMEOUT:
onSetActivityResult(new ActivityResult(BiometricEnrollBase.RESULT_TIMEOUT, null));
break;
}
}
private void onFinishAction(@FingerprintEnrollFinishAction int action) {
switch (action) {
case FINGERPRINT_ENROLL_FINISH_ACTION_ADD_BUTTON_CLICK: {
startEnrollingFragment();
break;
}
case FINGERPRINT_ENROLL_FINISH_ACTION_NEXT_BUTTON_CLICK: {
final Intent data;
if (mViewModel.getRequest().isSuw()) {
data = new Intent();
data.putExtras(mViewModel.getSuwFingerprintCountExtra(
mAutoCredentialViewModel.getUserId()));
} else {
data = null;
}
onSetActivityResult(new ActivityResult(BiometricEnrollBase.RESULT_FINISHED, data));
break;
}
}
}
@Override
protected void onPause() {
super.onPause();
mViewModel.checkFinishActivityDuringOnPause(isFinishing(), isChangingConfigurations());
}
@Override
protected void onDestroy() {
mViewModel.updateFingerprintSuggestionEnableState(mAutoCredentialViewModel.getUserId());
super.onDestroy();
}
@Override
protected void onApplyThemeResource(Resources.Theme theme, @StyleRes int resid, boolean first) {
theme.applyStyle(R.style.SetupWizardPartnerResource, true);
super.onApplyThemeResource(theme, resid, first);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
if (requestCode == LAUNCH_CONFIRM_LOCK_ACTIVITY) {
onChooseOrConfirmLockResult(false, new ActivityResult(resultCode, data));
return;
}
super.onActivityResult(requestCode, resultCode, data);
}
@NonNull
@Override
public CreationExtras getDefaultViewModelCreationExtras() {
final Application application =
super.getDefaultViewModelCreationExtras().get(APPLICATION_KEY);
final MutableCreationExtras ret = new MutableCreationExtras();
ret.set(APPLICATION_KEY, application);
final FingerprintRepository repository = FeatureFactory.getFactory(application)
.getBiometricsRepositoryProvider().getFingerprintRepository(application);
ret.set(CHALLENGE_GENERATOR_KEY, new FingerprintChallengeGenerator(repository));
ret.set(ENROLLMENT_REQUEST_KEY, new EnrollmentRequest(getIntent(), getApplicationContext(),
this instanceof SetupActivity));
Bundle extras = getIntent().getExtras();
final CredentialModel credentialModel = new CredentialModel(extras,
SystemClock.elapsedRealtimeClock());
ret.set(USER_ID_KEY, credentialModel.getUserId());
return ret;
}
@NonNull
@Override
public ViewModelProvider.Factory getDefaultViewModelProviderFactory() {
return new BiometricsViewModelFactory();
}
@Override
public void onAttachedToWindow() {
super.onAttachedToWindow();
getWindow().setStatusBarColor(getBackgroundColor());
}
@ColorInt
private int getBackgroundColor() {
final ColorStateList stateList = Utils.getColorAttr(this, android.R.attr.windowBackground);
return stateList != null ? stateList.getDefaultColor() : Color.TRANSPARENT;
}
@Override
public void onConfigurationChanged(@NonNull Configuration newConfig) {
mViewModelProvider.get(DeviceFoldedViewModel.class).onConfigurationChanged(newConfig);
super.onConfigurationChanged(newConfig);
}
@Override
protected void onSaveInstanceState(@NonNull Bundle outState) {
super.onSaveInstanceState(outState);
mViewModel.onSaveInstanceState(outState);
mAutoCredentialViewModel.onSaveInstanceState(outState);
}
}

View File

@@ -0,0 +1,644 @@
/*
* 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.view
import android.annotation.StyleRes
import android.content.Intent
import android.content.res.ColorStateList
import android.content.res.Configuration
import android.content.res.Resources.Theme
import android.graphics.Color
import android.os.Bundle
import android.os.SystemClock
import android.util.Log
import androidx.activity.result.ActivityResult
import androidx.activity.result.ActivityResultCallback
import androidx.activity.result.ActivityResultLauncher
import androidx.activity.result.contract.ActivityResultContracts.StartActivityForResult
import androidx.annotation.ColorInt
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentActivity
import androidx.fragment.app.FragmentManager.POP_BACK_STACK_INCLUSIVE
import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.viewmodel.CreationExtras
import androidx.lifecycle.viewmodel.MutableCreationExtras
import com.android.settings.R
import com.android.settings.Utils
import com.android.settings.biometrics.BiometricEnrollBase
import com.android.settings.biometrics2.data.repository.FingerprintRepository
import com.android.settings.biometrics2.factory.BiometricsViewModelFactory
import com.android.settings.biometrics2.factory.BiometricsViewModelFactory.CHALLENGE_GENERATOR_KEY
import com.android.settings.biometrics2.factory.BiometricsViewModelFactory.ENROLLMENT_REQUEST_KEY
import com.android.settings.biometrics2.factory.BiometricsViewModelFactory.USER_ID_KEY
import com.android.settings.biometrics2.ui.model.CredentialModel
import com.android.settings.biometrics2.ui.model.EnrollmentRequest
import com.android.settings.biometrics2.ui.viewmodel.AutoCredentialViewModel
import com.android.settings.biometrics2.ui.viewmodel.AutoCredentialViewModel.CREDENTIAL_FAIL_NEED_TO_CHOOSE_LOCK
import com.android.settings.biometrics2.ui.viewmodel.AutoCredentialViewModel.CREDENTIAL_FAIL_NEED_TO_CONFIRM_LOCK
import com.android.settings.biometrics2.ui.viewmodel.AutoCredentialViewModel.CREDENTIAL_IS_GENERATING_CHALLENGE
import com.android.settings.biometrics2.ui.viewmodel.AutoCredentialViewModel.CREDENTIAL_VALID
import com.android.settings.biometrics2.ui.viewmodel.AutoCredentialViewModel.FingerprintChallengeGenerator
import com.android.settings.biometrics2.ui.viewmodel.DeviceFoldedViewModel
import com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollEnrollingViewModel
import com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollEnrollingViewModel.ErrorDialogData
import com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollEnrollingViewModel.FINGERPRINT_ENROLL_ENROLLING_ACTION_DONE
import com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollEnrollingViewModel.FINGERPRINT_ENROLL_ENROLLING_ACTION_SHOW_ICON_TOUCH_DIALOG
import com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollEnrollingViewModel.FINGERPRINT_ENROLL_ENROLLING_CANCELED_BECAUSE_BACK_PRESSED
import com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollEnrollingViewModel.FINGERPRINT_ENROLL_ENROLLING_CANCELED_BECAUSE_USER_SKIP
import com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollEnrollingViewModel.FINGERPRINT_ERROR_DIALOG_ACTION_SET_RESULT_FINISH
import com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollEnrollingViewModel.FINGERPRINT_ERROR_DIALOG_ACTION_SET_RESULT_TIMEOUT
import com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollEnrollingViewModel.FingerprintEnrollEnrollingAction
import com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollEnrollingViewModel.FingerprintErrorDialogAction
import com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollFindSensorViewModel
import com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollFindSensorViewModel.FINGERPRINT_ENROLL_FIND_SENSOR_ACTION_DIALOG
import com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollFindSensorViewModel.FINGERPRINT_ENROLL_FIND_SENSOR_ACTION_SKIP
import com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollFindSensorViewModel.FINGERPRINT_ENROLL_FIND_SENSOR_ACTION_START
import com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollFindSensorViewModel.FingerprintEnrollFindSensorAction
import com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollFinishViewModel
import com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollFinishViewModel.FINGERPRINT_ENROLL_FINISH_ACTION_ADD_BUTTON_CLICK
import com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollFinishViewModel.FINGERPRINT_ENROLL_FINISH_ACTION_NEXT_BUTTON_CLICK
import com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollFinishViewModel.FingerprintEnrollFinishAction
import com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollIntroViewModel
import com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollIntroViewModel.FINGERPRINT_ENROLL_INTRO_ACTION_CONTINUE_ENROLL
import com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollIntroViewModel.FINGERPRINT_ENROLL_INTRO_ACTION_DONE_AND_FINISH
import com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollIntroViewModel.FINGERPRINT_ENROLL_INTRO_ACTION_SKIP_OR_CANCEL
import com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollIntroViewModel.FingerprintEnrollIntroAction
import com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollProgressViewModel
import com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollmentViewModel
import com.android.settings.overlay.FeatureFactory
import com.google.android.setupdesign.util.ThemeHelper
/**
* Fingerprint enrollment activity implementation
*/
open class FingerprintEnrollmentActivity : FragmentActivity() {
/** SetupWizard activity*/
class SetupActivity : FingerprintEnrollmentActivity()
/** Internal activity for FingerprintSettings */
class InternalActivity : FingerprintEnrollmentActivity()
/**
* This flag is used for addBackStack(), we do not save it in ViewModel because it is just used
* during FragmentManager calls
*/
private var isFirstFragmentAdded = false
private val viewModelProvider: ViewModelProvider by lazy {
ViewModelProvider(this)
}
private val viewModel: FingerprintEnrollmentViewModel by lazy {
viewModelProvider[FingerprintEnrollmentViewModel::class.java]
}
private val autoCredentialViewModel: AutoCredentialViewModel by lazy {
viewModelProvider[AutoCredentialViewModel::class.java]
}
private val introActionObserver: Observer<Int> = Observer<Int> { action ->
if (DEBUG) {
Log.d(TAG, "introActionObserver($action)")
}
action?.let { onIntroAction(it) }
}
private val findSensorActionObserver: Observer<Int> = Observer<Int> { action ->
if (DEBUG) {
Log.d(TAG, "findSensorActionObserver($action)")
}
action?.let { onFindSensorAction(it) }
}
private val enrollingActionObserver: Observer<Int> = Observer<Int> { action ->
if (DEBUG) {
Log.d(TAG, "enrollingActionObserver($action)")
}
action?.let { onEnrollingAction(it) }
}
private val enrollingErrorDialogObserver: Observer<ErrorDialogData> =
Observer<ErrorDialogData> { data ->
if (DEBUG) {
Log.d(TAG, "enrollingErrorDialogObserver($data)")
}
data?.let {
FingerprintEnrollEnrollingErrorDialog().show(
supportFragmentManager,
ENROLLING_ERROR_DIALOG_TAG
)
}
}
private val enrollingErrorDialogActionObserver: Observer<Int> = Observer<Int> { action ->
if (DEBUG) {
Log.d(TAG, "enrollingErrorDialogActionObserver($action)")
}
action?.let { onEnrollingErrorDialogAction(it) }
}
private val finishActionObserver: Observer<Int> = Observer<Int> { action ->
if (DEBUG) {
Log.d(TAG, "finishActionObserver($action)")
}
action?.let { onFinishAction(it) }
}
private val chooseLockResultCallback: ActivityResultCallback<ActivityResult> =
ActivityResultCallback { result ->
onChooseOrConfirmLockResult(true /* isChooseLock */, result)
}
private val chooseLockLauncher: ActivityResultLauncher<Intent> =
registerForActivityResult(StartActivityForResult(), chooseLockResultCallback)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
viewModel.setSavedInstanceState(savedInstanceState)
autoCredentialViewModel.setCredentialModel(savedInstanceState, intent)
// Theme
setTheme(viewModel.request.theme)
ThemeHelper.trySetDynamicColor(this)
window.statusBarColor = Color.TRANSPARENT
// fragment
setContentView(R.layout.biometric_enrollment_container)
val fragment: Fragment? = supportFragmentManager.findFragmentById(
R.id.fragment_container_view
)
if (DEBUG) {
Log.d(
TAG, "onCreate() has savedInstance:" + (savedInstanceState != null)
+ ", fragment:" + fragment
)
}
if (fragment == null) {
checkCredential()
val request: EnrollmentRequest = viewModel.getRequest()
if (request.isSkipFindSensor) {
startEnrollingFragment()
} else if (request.isSkipIntro) {
startFindSensorFragment()
} else {
startIntroFragment()
}
} else {
val tag: String? = fragment.tag
if (INTRO_TAG == tag) {
attachIntroViewModel()
} else if (FIND_SENSOR_TAG == tag) {
attachFindSensorViewModel()
attachIntroViewModel()
} else if (ENROLLING_TAG == tag) {
attachEnrollingViewModel()
attachFindSensorViewModel()
attachIntroViewModel()
} else if (FINISH_TAG == tag) {
attachFinishViewModel()
attachFindSensorViewModel()
attachIntroViewModel()
} else {
Log.e(TAG, "fragment tag $tag not found")
finish()
return
}
}
// observe LiveData
viewModel.setResultLiveData.observe(this) {
result: ActivityResult -> onSetActivityResult(result)
}
autoCredentialViewModel.generateChallengeFailedLiveData.observe(this) {
_: Boolean -> onGenerateChallengeFailed()
}
}
private fun startFragment(fragmentClass: Class<out Fragment>, tag: String) {
if (!isFirstFragmentAdded) {
supportFragmentManager.beginTransaction()
.setReorderingAllowed(true)
.replace(R.id.fragment_container_view, fragmentClass, null, tag)
.commit()
isFirstFragmentAdded = true
} else {
supportFragmentManager.beginTransaction()
.setReorderingAllowed(true)
.setCustomAnimations(
R.anim.shared_x_axis_activity_open_enter_dynamic_color,
R.anim.shared_x_axis_activity_open_exit,
R.anim.shared_x_axis_activity_close_enter_dynamic_color,
R.anim.shared_x_axis_activity_close_exit
)
.replace(R.id.fragment_container_view, fragmentClass, null, tag)
.addToBackStack(tag)
.commit()
}
}
private fun startIntroFragment() {
attachIntroViewModel()
startFragment(FingerprintEnrollIntroFragment::class.java, INTRO_TAG)
}
private fun attachIntroViewModel() {
val request: EnrollmentRequest = viewModel.request
if (request.isSkipIntro || request.isSkipFindSensor) {
return
}
viewModelProvider[FingerprintEnrollIntroViewModel::class.java].let {
// Clear ActionLiveData in FragmentViewModel to prevent getting previous action during
// recreate, like press 'Agree' then press 'back' in FingerprintEnrollFindSensor
// activity.
it.clearActionLiveData()
it.actionLiveData.observe(this, introActionObserver)
}
}
// We need to make sure token is valid before entering find sensor page
private fun startFindSensorFragment() {
// Always setToken into progressViewModel even it is not necessary action for UDFPS
viewModelProvider[FingerprintEnrollProgressViewModel::class.java]
.setToken(autoCredentialViewModel.token)
attachFindSensorViewModel()
val fragmentClass: Class<out Fragment> = if (viewModel.canAssumeUdfps()) {
FingerprintEnrollFindUdfpsFragment::class.java
} else if (viewModel.canAssumeSfps()) {
FingerprintEnrollFindSfpsFragment::class.java
} else {
FingerprintEnrollFindRfpsFragment::class.java
}
startFragment(fragmentClass, FIND_SENSOR_TAG)
}
private fun attachFindSensorViewModel() {
if (viewModel.request.isSkipFindSensor) {
return
}
viewModelProvider[FingerprintEnrollFindSensorViewModel::class.java].let {
// Clear ActionLiveData in FragmentViewModel to prevent getting previous action during
// recreate, like press 'Start' then press 'back' in FingerprintEnrollEnrolling
// activity.
it.clearActionLiveData()
it.actionLiveData.observe(this, findSensorActionObserver)
}
}
private fun startEnrollingFragment() {
// Always setToken into progressViewModel even it is not necessary action for SFPS or RFPS
viewModelProvider[FingerprintEnrollProgressViewModel::class.java]
.setToken(autoCredentialViewModel.token)
attachEnrollingViewModel()
val fragmentClass: Class<out Fragment> = if (viewModel.canAssumeUdfps()) {
FingerprintEnrollEnrollingUdfpsFragment::class.java
} else if (viewModel.canAssumeSfps()) {
FingerprintEnrollEnrollingSfpsFragment::class.java
} else {
FingerprintEnrollEnrollingRfpsFragment::class.java
}
startFragment(fragmentClass, ENROLLING_TAG)
}
private fun attachEnrollingViewModel() {
viewModelProvider[FingerprintEnrollEnrollingViewModel::class.java].let {
it.clearActionLiveData()
it.actionLiveData.observe(this, enrollingActionObserver)
it.errorDialogLiveData.observe(this, enrollingErrorDialogObserver)
it.errorDialogActionLiveData.observe(
this,
enrollingErrorDialogActionObserver
)
}
}
private fun startFinishFragment() {
viewModel.setIsNewFingerprintAdded()
attachFinishViewModel()
if (viewModel.request.isSkipFindSensor) {
// Set page to Finish
supportFragmentManager.beginTransaction()
.setReorderingAllowed(true)
.setCustomAnimations(
R.anim.shared_x_axis_activity_open_enter_dynamic_color,
R.anim.shared_x_axis_activity_open_exit,
R.anim.shared_x_axis_activity_close_enter_dynamic_color,
R.anim.shared_x_axis_activity_close_exit
)
.replace(
R.id.fragment_container_view,
FingerprintEnrollFinishFragment::class.java,
null,
FINISH_TAG
)
.commit()
} else {
// Remove Enrolling page
supportFragmentManager.popBackStack()
// Remove old Finish page if any
if (supportFragmentManager.findFragmentByTag(FINISH_TAG) != null) {
supportFragmentManager.popBackStack(FINISH_TAG, POP_BACK_STACK_INCLUSIVE)
}
// Remove FindSensor page if maxEnrolled
if (viewModel.isMaxEnrolledReached(autoCredentialViewModel.userId)
&& supportFragmentManager.findFragmentByTag(FIND_SENSOR_TAG) != null
) {
supportFragmentManager.popBackStack(FIND_SENSOR_TAG, POP_BACK_STACK_INCLUSIVE)
}
// Add Finish page
supportFragmentManager.beginTransaction()
.setReorderingAllowed(true)
.setCustomAnimations(
R.anim.shared_x_axis_activity_open_enter_dynamic_color,
R.anim.shared_x_axis_activity_open_exit,
R.anim.shared_x_axis_activity_close_enter_dynamic_color,
R.anim.shared_x_axis_activity_close_exit
)
.replace(
R.id.fragment_container_view,
FingerprintEnrollFinishFragment::class.java,
null,
FINISH_TAG
)
.addToBackStack(FINISH_TAG)
.commit()
}
}
private fun attachFinishViewModel() {
viewModelProvider[FingerprintEnrollFinishViewModel::class.java].let {
it.clearActionLiveData()
it.actionLiveData.observe(this, finishActionObserver)
}
}
private fun onGenerateChallengeFailed() {
onSetActivityResult(ActivityResult(RESULT_CANCELED, null))
}
private fun onSetActivityResult(result: ActivityResult) {
val challengeExtras: Bundle? = autoCredentialViewModel.createGeneratingChallengeExtras()
val overrideResult: ActivityResult = viewModel.getOverrideActivityResult(
result, challengeExtras
)
if (DEBUG) {
Log.d(
TAG, "onSetActivityResult(" + result + "), override:" + overrideResult
+ ") challengeExtras:" + challengeExtras
)
}
setResult(overrideResult.resultCode, overrideResult.data)
finish()
}
private fun checkCredential() {
when (autoCredentialViewModel.checkCredential()) {
CREDENTIAL_FAIL_NEED_TO_CHOOSE_LOCK -> {
val intent: Intent = autoCredentialViewModel.createChooseLockIntent(
this,
viewModel.request.isSuw,
viewModel.request.suwExtras
)
if (!viewModel.isWaitingActivityResult().compareAndSet(false, true)) {
Log.w(TAG, "chooseLock, fail to set isWaiting flag to true")
}
chooseLockLauncher.launch(intent)
return
}
CREDENTIAL_FAIL_NEED_TO_CONFIRM_LOCK -> {
val launched: Boolean = autoCredentialViewModel.createConfirmLockLauncher(
this,
LAUNCH_CONFIRM_LOCK_ACTIVITY,
getString(R.string.security_settings_fingerprint_preference_title)
).launch()
if (!launched) {
// This shouldn't happen, as we should only end up at this step if a lock thingy
// is already set.
Log.e(TAG, "confirmLock, launched is true")
finish()
} else if (!viewModel.isWaitingActivityResult().compareAndSet(false, true)) {
Log.w(TAG, "confirmLock, fail to set isWaiting flag to true")
}
return
}
CREDENTIAL_VALID,
CREDENTIAL_IS_GENERATING_CHALLENGE -> {}
}
}
private fun onChooseOrConfirmLockResult(isChooseLock: Boolean, activityResult: ActivityResult) {
if (!viewModel.isWaitingActivityResult().compareAndSet(true, false)) {
Log.w(TAG, "isChooseLock:$isChooseLock, fail to unset waiting flag")
}
if (autoCredentialViewModel.checkNewCredentialFromActivityResult(
isChooseLock, activityResult
)
) {
overridePendingTransition(R.anim.sud_slide_next_in, R.anim.sud_slide_next_out)
} else {
onSetActivityResult(activityResult)
}
}
private fun onIntroAction(@FingerprintEnrollIntroAction action: Int) {
when (action) {
FINGERPRINT_ENROLL_INTRO_ACTION_DONE_AND_FINISH -> {
onSetActivityResult(
ActivityResult(BiometricEnrollBase.RESULT_FINISHED, null)
)
return
}
FINGERPRINT_ENROLL_INTRO_ACTION_SKIP_OR_CANCEL -> {
onSetActivityResult(
ActivityResult(BiometricEnrollBase.RESULT_SKIP, null)
)
return
}
FINGERPRINT_ENROLL_INTRO_ACTION_CONTINUE_ENROLL -> {
startFindSensorFragment()
}
}
}
private fun onFindSensorAction(@FingerprintEnrollFindSensorAction action: Int) {
when (action) {
FINGERPRINT_ENROLL_FIND_SENSOR_ACTION_SKIP -> {
onSetActivityResult(ActivityResult(BiometricEnrollBase.RESULT_SKIP, null))
return
}
FINGERPRINT_ENROLL_FIND_SENSOR_ACTION_DIALOG -> {
SkipSetupFindFpsDialog().show(
supportFragmentManager,
SKIP_SETUP_FIND_FPS_DIALOG_TAG
)
return
}
FINGERPRINT_ENROLL_FIND_SENSOR_ACTION_START -> {
startEnrollingFragment()
}
}
}
private fun onEnrollingAction(@FingerprintEnrollEnrollingAction action: Int) {
when (action) {
FINGERPRINT_ENROLL_ENROLLING_ACTION_DONE -> {
startFinishFragment()
}
FINGERPRINT_ENROLL_ENROLLING_CANCELED_BECAUSE_USER_SKIP -> {
onSetActivityResult(ActivityResult(BiometricEnrollBase.RESULT_SKIP, null))
}
FINGERPRINT_ENROLL_ENROLLING_ACTION_SHOW_ICON_TOUCH_DIALOG -> {
FingerprintEnrollEnrollingIconTouchDialog().show(
supportFragmentManager,
SKIP_SETUP_FIND_FPS_DIALOG_TAG
)
}
FINGERPRINT_ENROLL_ENROLLING_CANCELED_BECAUSE_BACK_PRESSED -> {
if (supportFragmentManager.backStackEntryCount > 0) {
supportFragmentManager.popBackStack()
} else {
onSetActivityResult(ActivityResult(RESULT_CANCELED, null))
}
}
}
}
private fun onEnrollingErrorDialogAction(@FingerprintErrorDialogAction action: Int) {
when (action) {
FINGERPRINT_ERROR_DIALOG_ACTION_SET_RESULT_FINISH -> onSetActivityResult(
ActivityResult(BiometricEnrollBase.RESULT_FINISHED, null)
)
FINGERPRINT_ERROR_DIALOG_ACTION_SET_RESULT_TIMEOUT -> onSetActivityResult(
ActivityResult(BiometricEnrollBase.RESULT_TIMEOUT, null)
)
}
}
private fun onFinishAction(@FingerprintEnrollFinishAction action: Int) {
when (action) {
FINGERPRINT_ENROLL_FINISH_ACTION_ADD_BUTTON_CLICK -> {
startEnrollingFragment()
}
FINGERPRINT_ENROLL_FINISH_ACTION_NEXT_BUTTON_CLICK -> {
val data: Intent? = if (viewModel.request.isSuw) {
Intent().also {
it.putExtras(
viewModel.getSuwFingerprintCountExtra(
autoCredentialViewModel.userId
)
)
}
} else {
null
}
onSetActivityResult(ActivityResult(BiometricEnrollBase.RESULT_FINISHED, data))
}
}
}
override fun onPause() {
super.onPause()
viewModel.checkFinishActivityDuringOnPause(isFinishing, isChangingConfigurations)
}
override fun onDestroy() {
viewModel.updateFingerprintSuggestionEnableState(autoCredentialViewModel.userId)
super.onDestroy()
}
override fun onApplyThemeResource(theme: Theme, @StyleRes resid: Int, first: Boolean) {
theme.applyStyle(R.style.SetupWizardPartnerResource, true)
super.onApplyThemeResource(theme, resid, first)
}
@Deprecated("Deprecated in Java")
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
if (requestCode == LAUNCH_CONFIRM_LOCK_ACTIVITY) {
onChooseOrConfirmLockResult(false, ActivityResult(resultCode, data))
return
}
super.onActivityResult(requestCode, resultCode, data)
}
override val defaultViewModelCreationExtras: CreationExtras
get() {
val fingerprintRepository = FeatureFactory
.getFactory(application)
.biometricsRepositoryProvider
.getFingerprintRepository(application)!!
val credentialModel = CredentialModel(intent.extras, SystemClock.elapsedRealtimeClock())
return MutableCreationExtras(super.defaultViewModelCreationExtras).also {
it[CHALLENGE_GENERATOR_KEY] = FingerprintChallengeGenerator(fingerprintRepository)
it[ENROLLMENT_REQUEST_KEY] =
EnrollmentRequest(intent, applicationContext, this is SetupActivity)
it[USER_ID_KEY] = credentialModel.userId
}
}
override val defaultViewModelProviderFactory: ViewModelProvider.Factory
get() = BiometricsViewModelFactory()
override fun onAttachedToWindow() {
super.onAttachedToWindow()
window.statusBarColor = backgroundColor
}
@get:ColorInt
private val backgroundColor: Int
get() {
val stateList: ColorStateList? =
Utils.getColorAttr(this, android.R.attr.windowBackground)
return stateList?.defaultColor ?: Color.TRANSPARENT
}
override fun onConfigurationChanged(newConfig: Configuration) {
viewModelProvider[DeviceFoldedViewModel::class.java].onConfigurationChanged(newConfig)
super.onConfigurationChanged(newConfig)
}
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
viewModel.onSaveInstanceState(outState)
autoCredentialViewModel.onSaveInstanceState(outState)
}
companion object {
private const val DEBUG = false
private const val TAG = "FingerprintEnrollmentActivity"
private const val INTRO_TAG = "intro"
private const val FIND_SENSOR_TAG = "find-sensor"
private const val ENROLLING_TAG = "enrolling"
private const val FINISH_TAG = "finish"
private const val SKIP_SETUP_FIND_FPS_DIALOG_TAG = "skip-setup-dialog"
private const val ENROLLING_ERROR_DIALOG_TAG = "enrolling-error-dialog"
protected const val LAUNCH_CONFIRM_LOCK_ACTIVITY = 1
}
}

View File

@@ -26,7 +26,10 @@ android_test {
platform_apis: true,
certificate: "platform",
test_suites: ["device-tests"],
srcs: ["src/**/*.java"],
srcs: [
"src/**/*.java",
"src/**/*.kt",
],
libs: [
"android.test.runner",
@@ -34,6 +37,7 @@ android_test {
],
static_libs: [
"androidx.test.ext.junit",
"androidx.test.rules",
"androidx.test.uiautomator_uiautomator",
"app-helpers-core",

View File

@@ -1,622 +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.view;
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assume.assumeFalse;
import static org.junit.Assume.assumeTrue;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.hardware.fingerprint.FingerprintManager;
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
import android.hardware.fingerprint.IFingerprintAuthenticatorsRegisteredCallback;
import android.os.UserHandle;
import android.support.test.uiautomator.By;
import android.support.test.uiautomator.UiDevice;
import android.support.test.uiautomator.UiObject2;
import android.support.test.uiautomator.Until;
import android.util.Log;
import androidx.annotation.NonNull;
import androidx.test.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
import com.android.internal.widget.LockPatternChecker;
import com.android.internal.widget.LockPatternUtils;
import com.android.internal.widget.LockscreenCredential;
import com.android.settings.biometrics2.utils.LockScreenUtil;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import java.io.IOException;
import java.util.List;
@RunWith(AndroidJUnit4.class)
public class FingerprintEnrollmentActivityTest {
private static final String TAG = "FingerprintEnrollmentActivityTest";
private static final String SETTINGS_PACKAGE_NAME = "com.android.settings";
private static final String ACTIVITY_CLASS_NAME =
"com.android.settings.biometrics2.ui.view.FingerprintEnrollmentActivity";
private static final String SUW_ACTIVITY_CLASS_NAME = ACTIVITY_CLASS_NAME + "$SetupActivity";
private static final String EXTRA_IS_SETUP_FLOW = "isSetupFlow";
private static final String EXTRA_SKIP_INTRO = "skip_intro";
private static final String EXTRA_SKIP_FIND_SENSOR = "skip_find_sensor";
private static final String EXTRA_PAGE_TRANSITION_TYPE = "page_transition_type";
private static final String EXTRA_KEY_GK_PW_HANDLE = "gk_pw_handle";
private static final String TEST_PIN = "1234";
private static final String DO_IT_LATER = "Do it later";
private static final String UDFPS_ENROLLING_TITLE = "Touch & hold the fingerprint sensor";
private static final String SFPS_ENROLLING_TITLE =
"Lift, then touch. Move your finger slightly each time.";
private static final String RFPS_ENROLLING_TITLE = "Lift, then touch again";
private UiDevice mDevice;
private byte[] mToken = new byte[]{};
private Context mContext;
private boolean mFingerprintPropCallbackLaunched;
private boolean mCanAssumeUdfps;
private boolean mCanAssumeSfps;
private String mEnrollingTitle;
private static final int IDLE_TIMEOUT = 10000;
@Before
public void setUp() throws InterruptedException {
mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
mContext = InstrumentationRegistry.getContext();
// Stop every test if it is not a fingerprint device
assumeTrue(mContext.getPackageManager().hasSystemFeature(
PackageManager.FEATURE_FINGERPRINT));
final FingerprintManager fingerprintManager = mContext.getSystemService(
FingerprintManager.class);
mFingerprintPropCallbackLaunched = false;
fingerprintManager.addAuthenticatorsRegisteredCallback(
new IFingerprintAuthenticatorsRegisteredCallback.Stub() {
@Override
public void onAllAuthenticatorsRegistered(
List<FingerprintSensorPropertiesInternal> list) {
mFingerprintPropCallbackLaunched = true;
assertThat(list).isNotNull();
assertThat(list).isNotEmpty();
final FingerprintSensorPropertiesInternal prop = list.get(0);
mCanAssumeUdfps = prop.isAnyUdfpsType();
mCanAssumeSfps = prop.isAnySidefpsType();
if (mCanAssumeUdfps) {
mEnrollingTitle = UDFPS_ENROLLING_TITLE;
} else if (mCanAssumeSfps) {
mEnrollingTitle = SFPS_ENROLLING_TITLE;
} else {
mEnrollingTitle = RFPS_ENROLLING_TITLE;
}
}
});
for (long i = 0; i < IDLE_TIMEOUT && !mFingerprintPropCallbackLaunched; i += 100L) {
Thread.sleep(100L);
}
assertThat(mFingerprintPropCallbackLaunched).isTrue();
mDevice.pressHome();
// Stop settings before performing test
try {
mDevice.executeShellCommand("am force-stop " + SETTINGS_PACKAGE_NAME);
} catch (IOException e) {
Log.e(TAG, "Fail to stop settings app", e);
}
}
@After
public void tearDown() throws Exception {
LockScreenUtil.resetLockscreen(TEST_PIN);
mDevice.pressHome();
}
@Test
public void testIntroChooseLock() {
final Intent intent = newActivityIntent(false);
mContext.startActivity(intent);
assertThat(mDevice.wait(Until.hasObject(By.text("Choose your backup screen lock method")),
IDLE_TIMEOUT)).isTrue();
}
private void verifyIntroPage() {
mDevice.waitForIdle();
for (long i = 0; i < IDLE_TIMEOUT; i += 100L) {
if (mDevice.wait(Until.hasObject(By.text("More")), 50L)) {
break;
} else if (mDevice.wait(Until.hasObject(By.text("I agree")), 50L)) {
break;
}
}
// Click more btn at most twice and the introduction should stay in the last page
UiObject2 moreBtn;
for (int i = 0; i < 2 && (moreBtn = mDevice.findObject(By.text("More"))) != null; ++i) {
moreBtn.click();
mDevice.waitForIdle();
mDevice.wait(Until.hasObject(By.text("More")), IDLE_TIMEOUT);
}
assertThat(mDevice.wait(Until.hasObject(By.text("No thanks")), IDLE_TIMEOUT)).isTrue();
assertThat(mDevice.wait(Until.hasObject(By.text("I agree")), IDLE_TIMEOUT)).isTrue();
}
@Test
public void testIntroWithGkPwHandle_withUdfps_clickStart() {
assumeTrue(mCanAssumeUdfps);
LockScreenUtil.setLockscreen(LockScreenUtil.LockscreenType.PIN, TEST_PIN, true);
launchIntroWithGkPwHandle(false);
// Intro page
verifyIntroPage();
final UiObject2 agreeBtn = mDevice.findObject(By.text("I agree"));
assertThat(agreeBtn).isNotNull();
agreeBtn.click();
// FindUdfps page
assertThat(mDevice.wait(Until.hasObject(By.text(DO_IT_LATER)), IDLE_TIMEOUT)).isTrue();
final UiObject2 lottie = mDevice.findObject(By.res(SETTINGS_PACKAGE_NAME,
"illustration_lottie"));
assertThat(lottie).isNotNull();
assertThat(lottie.isClickable()).isTrue();
final UiObject2 startBtn = mDevice.findObject(By.text("Start"));
assertThat(startBtn.isClickable()).isTrue();
startBtn.click();
// Enrolling page
assertThat(mDevice.wait(Until.hasObject(By.text(mEnrollingTitle)), IDLE_TIMEOUT)).isTrue();
}
@Test
public void testIntroWithGkPwHandle_withUdfps_clickLottie() {
assumeTrue(mCanAssumeUdfps);
LockScreenUtil.setLockscreen(LockScreenUtil.LockscreenType.PIN, TEST_PIN, true);
launchIntroWithGkPwHandle(false);
// Intro page
verifyIntroPage();
final UiObject2 agreeBtn = mDevice.findObject(By.text("I agree"));
assertThat(agreeBtn).isNotNull();
agreeBtn.click();
// FindUdfps page
assertThat(mDevice.wait(Until.hasObject(By.text(DO_IT_LATER)), IDLE_TIMEOUT)).isTrue();
final UiObject2 lottie = mDevice.findObject(By.res(SETTINGS_PACKAGE_NAME,
"illustration_lottie"));
assertThat(lottie).isNotNull();
assertThat(lottie.isClickable()).isTrue();
final UiObject2 startBtn = mDevice.findObject(By.text("Start"));
assertThat(startBtn.isClickable()).isTrue();
lottie.click();
// Enrolling page
assertThat(mDevice.wait(Until.hasObject(By.text(mEnrollingTitle)), IDLE_TIMEOUT)).isTrue();
}
@Test
public void testIntroWithGkPwHandle_withSfps() {
assumeTrue(mCanAssumeSfps);
LockScreenUtil.setLockscreen(LockScreenUtil.LockscreenType.PIN, TEST_PIN, true);
launchIntroWithGkPwHandle(false);
// Intro page
verifyIntroPage();
final UiObject2 agreeBtn = mDevice.findObject(By.text("I agree"));
assertThat(agreeBtn).isNotNull();
agreeBtn.click();
// FindSfps page
assertThat(mDevice.wait(Until.hasObject(By.text(DO_IT_LATER)), IDLE_TIMEOUT)).isTrue();
final UiObject2 lottie = mDevice.findObject(By.res(SETTINGS_PACKAGE_NAME,
"illustration_lottie"));
assertThat(lottie).isNotNull();
// We don't have view which can be clicked to run to next page, stop at here.
}
@Test
public void testIntroWithGkPwHandle_withRfps() {
assumeFalse(mCanAssumeUdfps || mCanAssumeSfps);
LockScreenUtil.setLockscreen(LockScreenUtil.LockscreenType.PIN, TEST_PIN, true);
launchIntroWithGkPwHandle(false);
// Intro page
verifyIntroPage();
final UiObject2 agreeBtn = mDevice.findObject(By.text("I agree"));
assertThat(agreeBtn).isNotNull();
agreeBtn.click();
// FindRfps page
assertThat(mDevice.wait(Until.hasObject(By.text(DO_IT_LATER)), IDLE_TIMEOUT)).isTrue();
final UiObject2 lottie = mDevice.findObject(By.res(SETTINGS_PACKAGE_NAME,
"illustration_lottie"));
if (lottie == null) {
// FindSfps page shall have an animation view if no lottie view
assertThat(mDevice.findObject(By.res(SETTINGS_PACKAGE_NAME,
"fingerprint_sensor_location_animation"))).isNotNull();
}
}
@Test
public void testIntroWithGkPwHandle_clickNoThanksInIntroPage() {
LockScreenUtil.setLockscreen(LockScreenUtil.LockscreenType.PIN, TEST_PIN, true);
launchIntroWithGkPwHandle(false);
// Intro page
verifyIntroPage();
final UiObject2 noThanksBtn = mDevice.findObject(By.text("No thanks"));
assertThat(noThanksBtn).isNotNull();
noThanksBtn.click();
// Back to home
mDevice.waitForWindowUpdate("com.android.settings", IDLE_TIMEOUT);
assertThat(mDevice.findObject(By.text("No thanks"))).isNull();
}
@Test
public void testIntroWithGkPwHandle_clickSkipInFindSensor() {
LockScreenUtil.setLockscreen(LockScreenUtil.LockscreenType.PIN, TEST_PIN, true);
launchIntroWithGkPwHandle(false);
// Intro page
verifyIntroPage();
final UiObject2 agreeBtn = mDevice.findObject(By.text("I agree"));
assertThat(agreeBtn).isNotNull();
agreeBtn.click();
// FindSensor page
assertThat(mDevice.wait(Until.hasObject(By.text(DO_IT_LATER)), IDLE_TIMEOUT)).isTrue();
final UiObject2 doItLaterBtn = mDevice.findObject(By.text(DO_IT_LATER));
assertThat(doItLaterBtn).isNotNull();
assertThat(doItLaterBtn.isClickable()).isTrue();
doItLaterBtn.click();
// Back to home
mDevice.waitForWindowUpdate("com.android.settings", IDLE_TIMEOUT);
assertThat(mDevice.findObject(By.text(DO_IT_LATER))).isNull();
}
@Test
public void testIntroWithGkPwHandle_clickSkipAnywayInFindFpsDialog_whenIsSuw() {
LockScreenUtil.setLockscreen(LockScreenUtil.LockscreenType.PIN, TEST_PIN, true);
launchIntroWithGkPwHandle(true);
// Intro page
verifyIntroPage();
final UiObject2 agreeBtn = mDevice.findObject(By.text("I agree"));
assertThat(agreeBtn).isNotNull();
agreeBtn.click();
// FindSensor page
assertThat(mDevice.wait(Until.hasObject(By.text(DO_IT_LATER)), IDLE_TIMEOUT)).isTrue();
final UiObject2 doItLaterBtn = mDevice.findObject(By.text(DO_IT_LATER));
assertThat(doItLaterBtn).isNotNull();
assertThat(doItLaterBtn.isClickable()).isTrue();
doItLaterBtn.click();
// SkipSetupFindFpsDialog
assertThat(mDevice.wait(Until.hasObject(By.text("Skip fingerprint?")),
IDLE_TIMEOUT)).isTrue();
final UiObject2 skipAnywayBtn = mDevice.findObject(By.text("Skip anyway"));
assertThat(skipAnywayBtn).isNotNull();
assertThat(skipAnywayBtn.isClickable()).isTrue();
skipAnywayBtn.click();
// Back to home
mDevice.waitForWindowUpdate("com.android.settings", IDLE_TIMEOUT);
assertThat(mDevice.findObject(By.text("Skip anyway"))).isNull();
assertThat(mDevice.findObject(By.text(DO_IT_LATER))).isNull();
}
@Test
public void testIntroWithGkPwHandle_clickGoBackInFindFpsDialog_whenIsSuw() {
LockScreenUtil.setLockscreen(LockScreenUtil.LockscreenType.PIN, TEST_PIN, true);
launchIntroWithGkPwHandle(true);
// Intro page
verifyIntroPage();
final UiObject2 agreeBtn = mDevice.findObject(By.text("I agree"));
assertThat(agreeBtn).isNotNull();
agreeBtn.click();
// FindSensor page
assertThat(mDevice.wait(Until.hasObject(By.text(DO_IT_LATER)), IDLE_TIMEOUT)).isTrue();
final UiObject2 doItLaterBtn = mDevice.findObject(By.text(DO_IT_LATER));
assertThat(doItLaterBtn).isNotNull();
assertThat(doItLaterBtn.isClickable()).isTrue();
doItLaterBtn.click();
// SkipSetupFindFpsDialog
assertThat(mDevice.wait(Until.hasObject(By.text("Skip fingerprint?")), IDLE_TIMEOUT))
.isTrue();
final UiObject2 goBackBtn = mDevice.findObject(By.text("Go back"));
assertThat(goBackBtn).isNotNull();
assertThat(goBackBtn.isClickable()).isTrue();
goBackBtn.click();
// FindSensor page again
assertThat(mDevice.wait(Until.hasObject(By.text(DO_IT_LATER)), IDLE_TIMEOUT)).isTrue();
}
@Test
public void testIntroCheckPin() {
LockScreenUtil.setLockscreen(LockScreenUtil.LockscreenType.PIN, TEST_PIN, true);
final Intent intent = newActivityIntent(false);
mContext.startActivity(intent);
assertThat(mDevice.wait(Until.hasObject(By.text("Enter your device PIN to continue")),
IDLE_TIMEOUT)).isTrue();
}
@Test
public void testEnrollingWithGkPwHandle() {
LockScreenUtil.setLockscreen(LockScreenUtil.LockscreenType.PIN, TEST_PIN, true);
launchEnrollingWithGkPwHandle();
// Enrolling screen
mDevice.waitForIdle();
assertThat(mDevice.wait(Until.hasObject(By.text(mEnrollingTitle)), IDLE_TIMEOUT)).isTrue();
}
@Test
public void testEnrollingIconTouchDialog_withSfps() {
assumeTrue(mCanAssumeSfps);
LockScreenUtil.setLockscreen(LockScreenUtil.LockscreenType.PIN, TEST_PIN, true);
launchEnrollingWithGkPwHandle();
// Enrolling screen
mDevice.waitForIdle();
assertThat(mDevice.wait(Until.hasObject(By.text(mEnrollingTitle)), IDLE_TIMEOUT)).isTrue();
final UiObject2 lottie = mDevice.findObject(By.res(SETTINGS_PACKAGE_NAME,
"illustration_lottie"));
assertThat(lottie).isNotNull();
lottie.click();
lottie.click();
lottie.click();
// IconTouchDialog
mDevice.waitForIdle();
assertThat(mDevice.wait(Until.hasObject(By.text("Touch the sensor instead")), IDLE_TIMEOUT))
.isTrue();
final UiObject2 okButton = mDevice.findObject(By.text("OK"));
assertThat(okButton).isNotNull();
okButton.click();
// Enrolling screen again
mDevice.waitForIdle();
assertThat(mDevice.wait(Until.hasObject(By.text(mEnrollingTitle)), IDLE_TIMEOUT)).isTrue();
}
@Test
public void testEnrollingIconTouchDialog_withRfps() {
assumeFalse(mCanAssumeUdfps || mCanAssumeSfps);
LockScreenUtil.setLockscreen(LockScreenUtil.LockscreenType.PIN, TEST_PIN, true);
launchEnrollingWithGkPwHandle();
// Enrolling screen
mDevice.waitForIdle();
assertThat(mDevice.wait(Until.hasObject(By.text(mEnrollingTitle)), IDLE_TIMEOUT)).isTrue();
final UiObject2 lottie = mDevice.findObject(By.res(SETTINGS_PACKAGE_NAME,
"fingerprint_progress_bar"));
assertThat(lottie).isNotNull();
lottie.click();
lottie.click();
lottie.click();
// IconTouchDialog
mDevice.waitForIdle();
assertThat(mDevice.wait(Until.hasObject(By.text("Whoops, that\u2019s not the sensor")),
IDLE_TIMEOUT)).isTrue();
final UiObject2 okButton = mDevice.findObject(By.text("OK"));
assertThat(okButton).isNotNull();
okButton.click();
// Enrolling screen again
mDevice.waitForIdle();
assertThat(mDevice.wait(Until.hasObject(By.text(mEnrollingTitle)), IDLE_TIMEOUT)).isTrue();
}
@Test
public void testFindUdfpsWithGkPwHandle_clickStart() {
assumeTrue(mCanAssumeUdfps);
LockScreenUtil.setLockscreen(LockScreenUtil.LockscreenType.PIN, TEST_PIN, true);
launchFindSensorWithGkPwHandle();
// FindUdfps page
assertThat(mDevice.wait(Until.hasObject(By.text(DO_IT_LATER)), IDLE_TIMEOUT)).isTrue();
final UiObject2 lottie = mDevice.findObject(By.res(SETTINGS_PACKAGE_NAME,
"illustration_lottie"));
assertThat(lottie).isNotNull();
assertThat(lottie.isClickable()).isTrue();
final UiObject2 startBtn = mDevice.findObject(By.text("Start"));
assertThat(startBtn.isClickable()).isTrue();
startBtn.click();
// Enrolling page
assertThat(mDevice.wait(Until.hasObject(By.text(mEnrollingTitle)), IDLE_TIMEOUT)).isTrue();
}
@Test
public void testFindUdfpsWithGkPwHandle_clickLottie() {
assumeTrue(mCanAssumeUdfps);
LockScreenUtil.setLockscreen(LockScreenUtil.LockscreenType.PIN, TEST_PIN, true);
launchFindSensorWithGkPwHandle();
// FindUdfps page
assertThat(mDevice.wait(Until.hasObject(By.text(DO_IT_LATER)), IDLE_TIMEOUT)).isTrue();
final UiObject2 lottie = mDevice.findObject(By.res(SETTINGS_PACKAGE_NAME,
"illustration_lottie"));
assertThat(lottie).isNotNull();
assertThat(lottie.isClickable()).isTrue();
final UiObject2 startBtn = mDevice.findObject(By.text("Start"));
assertThat(startBtn.isClickable()).isTrue();
lottie.click();
// Enrolling page
assertThat(mDevice.wait(Until.hasObject(By.text(mEnrollingTitle)), IDLE_TIMEOUT)).isTrue();
}
@Test
public void testFindSfpsWithGkPwHandle() {
assumeTrue(mCanAssumeSfps);
LockScreenUtil.setLockscreen(LockScreenUtil.LockscreenType.PIN, TEST_PIN, true);
launchFindSensorWithGkPwHandle();
// FindSfps page
assertThat(mDevice.wait(Until.hasObject(By.text(DO_IT_LATER)), IDLE_TIMEOUT)).isTrue();
final UiObject2 lottie = mDevice.findObject(By.res(SETTINGS_PACKAGE_NAME,
"illustration_lottie"));
assertThat(lottie).isNotNull();
// We don't have view which can be clicked to run to next page, stop at here.
}
@Test
public void testFindRfpsWithGkPwHandle() {
assumeFalse(mCanAssumeUdfps || mCanAssumeSfps);
LockScreenUtil.setLockscreen(LockScreenUtil.LockscreenType.PIN, TEST_PIN, true);
launchFindSensorWithGkPwHandle();
// FindRfps page
assertThat(mDevice.wait(Until.hasObject(By.text(DO_IT_LATER)), IDLE_TIMEOUT)).isTrue();
final UiObject2 lottie = mDevice.findObject(By.res(SETTINGS_PACKAGE_NAME,
"illustration_lottie"));
if (lottie == null) {
// FindSfps page shall have an animation view if no lottie view
assertThat(mDevice.findObject(By.res(SETTINGS_PACKAGE_NAME,
"fingerprint_sensor_location_animation"))).isNotNull();
}
}
@Test
public void testFindSensorWithGkPwHandle_clickSkipInFindSensor() {
LockScreenUtil.setLockscreen(LockScreenUtil.LockscreenType.PIN, TEST_PIN, true);
launchFindSensorWithGkPwHandle();
// FindSensor page
assertThat(mDevice.wait(Until.hasObject(By.text(DO_IT_LATER)), IDLE_TIMEOUT)).isTrue();
final UiObject2 doItLaterBtn = mDevice.findObject(By.text(DO_IT_LATER));
assertThat(doItLaterBtn).isNotNull();
assertThat(doItLaterBtn.isClickable()).isTrue();
doItLaterBtn.click();
// Back to home
mDevice.waitForWindowUpdate("com.android.settings", IDLE_TIMEOUT);
assertThat(mDevice.wait(Until.gone(By.text(DO_IT_LATER)), IDLE_TIMEOUT)).isTrue();
}
private void launchIntroWithGkPwHandle(boolean isSuw) {
LockPatternUtils lockPatternUtils = new LockPatternUtils(mContext);
final LockscreenCredential lockscreenCredential = LockscreenCredential.createPin(TEST_PIN);
final int userId = UserHandle.myUserId();
final LockPatternChecker.OnVerifyCallback onVerifyCallback = (response, timeoutMs) -> {
final Intent intent = newActivityIntent(isSuw);
intent.putExtra(EXTRA_KEY_GK_PW_HANDLE, response.getGatekeeperPasswordHandle());
mContext.startActivity(intent);
};
LockPatternChecker.verifyCredential(lockPatternUtils, lockscreenCredential,
userId, LockPatternUtils.VERIFY_FLAG_REQUEST_GK_PW_HANDLE, onVerifyCallback);
}
private void launchFindSensorWithGkPwHandle() {
LockPatternUtils lockPatternUtils = new LockPatternUtils(mContext);
final LockscreenCredential lockscreenCredential = LockscreenCredential.createPin(TEST_PIN);
final int userId = UserHandle.myUserId();
final LockPatternChecker.OnVerifyCallback onVerifyCallback = (response, timeoutMs) -> {
final Intent intent = newActivityIntent(false);
intent.putExtra(EXTRA_SKIP_INTRO, true);
intent.putExtra(EXTRA_KEY_GK_PW_HANDLE, response.getGatekeeperPasswordHandle());
mContext.startActivity(intent);
};
LockPatternChecker.verifyCredential(lockPatternUtils, lockscreenCredential,
userId, LockPatternUtils.VERIFY_FLAG_REQUEST_GK_PW_HANDLE, onVerifyCallback);
}
private void launchEnrollingWithGkPwHandle() {
LockPatternUtils lockPatternUtils = new LockPatternUtils(mContext);
final LockscreenCredential lockscreenCredential = LockscreenCredential.createPin(TEST_PIN);
final int userId = UserHandle.myUserId();
final LockPatternChecker.OnVerifyCallback onVerifyCallback = (response, timeoutMs) -> {
final Intent intent = newActivityIntent(false);
intent.putExtra(EXTRA_SKIP_FIND_SENSOR, true);
intent.putExtra(EXTRA_KEY_GK_PW_HANDLE, response.getGatekeeperPasswordHandle());
mContext.startActivity(intent);
};
LockPatternChecker.verifyCredential(lockPatternUtils, lockscreenCredential,
userId, LockPatternUtils.VERIFY_FLAG_REQUEST_GK_PW_HANDLE, onVerifyCallback);
}
@NonNull
private Intent newActivityIntent(boolean isSuw) {
Intent intent = new Intent();
intent.setClassName(SETTINGS_PACKAGE_NAME,
isSuw ? SUW_ACTIVITY_CLASS_NAME : ACTIVITY_CLASS_NAME);
if (isSuw) {
intent.putExtra(EXTRA_IS_SETUP_FLOW, true);
}
intent.putExtra(EXTRA_PAGE_TRANSITION_TYPE, 1);
intent.putExtra(Intent.EXTRA_USER_ID, mContext.getUserId());
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK);
return intent;
}
}

View File

@@ -0,0 +1,632 @@
/*
* 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.view
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager.FEATURE_FINGERPRINT
import android.hardware.fingerprint.FingerprintManager
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal
import android.hardware.fingerprint.IFingerprintAuthenticatorsRegisteredCallback
import android.os.UserHandle
import android.support.test.uiautomator.By
import android.support.test.uiautomator.UiDevice
import android.support.test.uiautomator.UiObject2
import android.support.test.uiautomator.Until
import android.util.Log
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.platform.app.InstrumentationRegistry
import com.android.internal.widget.LockPatternChecker
import com.android.internal.widget.LockPatternUtils
import com.android.internal.widget.LockscreenCredential
import com.android.internal.widget.VerifyCredentialResponse
import com.android.settings.biometrics2.utils.LockScreenUtil
import com.google.common.truth.Truth.assertThat
import org.junit.After
import org.junit.Assume
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import java.io.IOException
@RunWith(AndroidJUnit4::class)
class FingerprintEnrollmentActivityTest {
private val context: Context by lazy {
InstrumentationRegistry.getInstrumentation().context
}
private val fingerprintManager: FingerprintManager by lazy {
context.getSystemService(FingerprintManager::class.java)!!
}
private var fingerprintPropCallbackLaunched = false
private var canAssumeUdfps = false
private var canAssumeSfps = false
private var enrollingPageTitle: String = ""
private val device: UiDevice by lazy {
UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
}
@Before
@Throws(InterruptedException::class)
fun setUp() {
// Stop every test if it is not a fingerprint device
Assume.assumeTrue(context.packageManager.hasSystemFeature(FEATURE_FINGERPRINT))
fingerprintPropCallbackLaunched = false
fingerprintManager.addAuthenticatorsRegisteredCallback(
object : IFingerprintAuthenticatorsRegisteredCallback.Stub() {
override fun onAllAuthenticatorsRegistered(
list: List<FingerprintSensorPropertiesInternal>
) {
fingerprintPropCallbackLaunched = true
assertThat(list).isNotNull()
assertThat(list).isNotEmpty()
val prop = list[0]
canAssumeUdfps = prop.isAnyUdfpsType
canAssumeSfps = prop.isAnySidefpsType
enrollingPageTitle = if (canAssumeUdfps) {
UDFPS_ENROLLING_TITLE
} else if (canAssumeSfps) {
SFPS_ENROLLING_TITLE
} else {
RFPS_ENROLLING_TITLE
}
}
})
var i: Long = 0
while (i < IDLE_TIMEOUT && !fingerprintPropCallbackLaunched) {
Thread.sleep(100L)
i += 100L
}
assertThat(fingerprintPropCallbackLaunched).isTrue()
device.pressHome()
// Stop settings before performing test
try {
device.executeShellCommand("am force-stop $SETTINGS_PACKAGE_NAME")
} catch (e: IOException) {
Log.e(TAG, "Fail to stop settings app", e)
}
}
@After
@Throws(Exception::class)
fun tearDown() {
LockScreenUtil.resetLockscreen(TEST_PIN)
device.pressHome()
}
@Test
fun testIntroChooseLock() {
val intent = newActivityIntent(false)
context.startActivity(intent)
assertThat(
device.wait(
Until.hasObject(By.text("Choose your backup screen lock method")),
IDLE_TIMEOUT
)
).isTrue()
}
private fun verifyIntroPage() {
device.waitForIdle()
run {
var i: Long = 0
while (i < IDLE_TIMEOUT) {
if (device.wait(Until.hasObject(By.text("More")), 50L)) {
break
} else if (device.wait(Until.hasObject(By.text("I agree")), 50L)) {
break
}
i += 100L
}
}
// Click more btn at most twice and the introduction should stay in the last page
var moreBtn: UiObject2? = null
var i = 0
while (i < 2 && device.findObject(By.text("More")).also { moreBtn = it } != null) {
moreBtn!!.click()
device.waitForIdle()
device.wait(Until.hasObject(By.text("More")), IDLE_TIMEOUT)
++i
}
assertThat(device.wait(Until.hasObject(By.text("No thanks")), IDLE_TIMEOUT)).isTrue()
assertThat(device.wait(Until.hasObject(By.text("I agree")), IDLE_TIMEOUT)).isTrue()
}
@Test
fun testIntroWithGkPwHandle_withUdfps_clickStart() {
Assume.assumeTrue(canAssumeUdfps)
LockScreenUtil.setLockscreen(LockScreenUtil.LockscreenType.PIN, TEST_PIN, true)
launchIntroWithGkPwHandle(false)
// Intro page
verifyIntroPage()
val agreeBtn = device.findObject(By.text("I agree"))
assertThat(agreeBtn).isNotNull()
agreeBtn.click()
// FindUdfps page
assertThat(device.wait(Until.hasObject(By.text(DO_IT_LATER)), IDLE_TIMEOUT)).isTrue()
val lottie = device.findObject(
By.res(SETTINGS_PACKAGE_NAME, "illustration_lottie")
)
assertThat(lottie).isNotNull()
assertThat(lottie.isClickable).isTrue()
val startBtn = device.findObject(By.text("Start"))
assertThat(startBtn.isClickable).isTrue()
startBtn.click()
// Enrolling page
assertThat(device.wait(Until.hasObject(By.text(enrollingPageTitle)), IDLE_TIMEOUT)).isTrue()
}
@Test
fun testIntroWithGkPwHandle_withUdfps_clickLottie() {
Assume.assumeTrue(canAssumeUdfps)
LockScreenUtil.setLockscreen(LockScreenUtil.LockscreenType.PIN, TEST_PIN, true)
launchIntroWithGkPwHandle(false)
// Intro page
verifyIntroPage()
val agreeBtn = device.findObject(By.text("I agree"))
assertThat(agreeBtn).isNotNull()
agreeBtn.click()
// FindUdfps page
assertThat(device.wait(Until.hasObject(By.text(DO_IT_LATER)), IDLE_TIMEOUT)).isTrue()
val lottie = device.findObject(By.res(SETTINGS_PACKAGE_NAME, "illustration_lottie"))
assertThat(lottie).isNotNull()
assertThat(lottie.isClickable).isTrue()
val startBtn = device.findObject(By.text("Start"))
assertThat(startBtn.isClickable).isTrue()
lottie.click()
// Enrolling page
assertThat(device.wait(Until.hasObject(By.text(enrollingPageTitle)), IDLE_TIMEOUT)).isTrue()
}
@Test
fun testIntroWithGkPwHandle_withSfps() {
Assume.assumeTrue(canAssumeSfps)
LockScreenUtil.setLockscreen(LockScreenUtil.LockscreenType.PIN, TEST_PIN, true)
launchIntroWithGkPwHandle(false)
// Intro page
verifyIntroPage()
val agreeBtn = device.findObject(By.text("I agree"))
assertThat(agreeBtn).isNotNull()
agreeBtn.click()
// FindSfps page
assertThat(device.wait(Until.hasObject(By.text(DO_IT_LATER)), IDLE_TIMEOUT)).isTrue()
val lottie = device.findObject(
By.res(SETTINGS_PACKAGE_NAME,"illustration_lottie")
)
assertThat(lottie).isNotNull()
// We don't have view which can be clicked to run to next page, stop at here.
}
@Test
fun testIntroWithGkPwHandle_withRfps() {
Assume.assumeFalse(canAssumeUdfps || canAssumeSfps)
LockScreenUtil.setLockscreen(LockScreenUtil.LockscreenType.PIN, TEST_PIN, true)
launchIntroWithGkPwHandle(false)
// Intro page
verifyIntroPage()
val agreeBtn = device.findObject(By.text("I agree"))
assertThat(agreeBtn).isNotNull()
agreeBtn.click()
// FindRfps page
assertThat(device.wait(Until.hasObject(By.text(DO_IT_LATER)), IDLE_TIMEOUT)).isTrue()
val lottie = device.findObject(
By.res(SETTINGS_PACKAGE_NAME, "illustration_lottie")
)
if (lottie == null) {
// FindSfps page shall have an animation view if no lottie view
assertThat(
device.findObject(
By.res(SETTINGS_PACKAGE_NAME, "fingerprint_sensor_location_animation")
)
).isNotNull()
}
}
@Test
fun testIntroWithGkPwHandle_clickNoThanksInIntroPage() {
LockScreenUtil.setLockscreen(LockScreenUtil.LockscreenType.PIN, TEST_PIN, true)
launchIntroWithGkPwHandle(false)
// Intro page
verifyIntroPage()
val noThanksBtn = device.findObject(By.text("No thanks"))
assertThat(noThanksBtn).isNotNull()
noThanksBtn.click()
// Back to home
device.waitForWindowUpdate(SETTINGS_PACKAGE_NAME, IDLE_TIMEOUT)
assertThat(device.findObject(By.text("No thanks"))).isNull()
}
@Test
fun testIntroWithGkPwHandle_clickSkipInFindSensor() {
LockScreenUtil.setLockscreen(LockScreenUtil.LockscreenType.PIN, TEST_PIN, true)
launchIntroWithGkPwHandle(false)
// Intro page
verifyIntroPage()
val agreeBtn = device.findObject(By.text("I agree"))
assertThat(agreeBtn).isNotNull()
agreeBtn.click()
// FindSensor page
assertThat(device.wait(Until.hasObject(By.text(DO_IT_LATER)), IDLE_TIMEOUT)).isTrue()
val doItLaterBtn = device.findObject(By.text(DO_IT_LATER))
assertThat(doItLaterBtn).isNotNull()
assertThat(doItLaterBtn.isClickable).isTrue()
doItLaterBtn.click()
// Back to home
device.waitForWindowUpdate(SETTINGS_PACKAGE_NAME, IDLE_TIMEOUT)
assertThat(device.findObject(By.text(DO_IT_LATER))).isNull()
}
@Test
fun testIntroWithGkPwHandle_clickSkipAnywayInFindFpsDialog_whenIsSuw() {
LockScreenUtil.setLockscreen(LockScreenUtil.LockscreenType.PIN, TEST_PIN, true)
launchIntroWithGkPwHandle(true)
// Intro page
verifyIntroPage()
val agreeBtn = device.findObject(By.text("I agree"))
assertThat(agreeBtn).isNotNull()
agreeBtn.click()
// FindSensor page
assertThat(device.wait(Until.hasObject(By.text(DO_IT_LATER)), IDLE_TIMEOUT)).isTrue()
val doItLaterBtn = device.findObject(By.text(DO_IT_LATER))
assertThat(doItLaterBtn).isNotNull()
assertThat(doItLaterBtn.isClickable).isTrue()
doItLaterBtn.click()
// SkipSetupFindFpsDialog
assertThat(device.wait(Until.hasObject(By.text("Skip fingerprint?")), IDLE_TIMEOUT)).isTrue()
val skipAnywayBtn = device.findObject(By.text("Skip anyway"))
assertThat(skipAnywayBtn).isNotNull()
assertThat(skipAnywayBtn.isClickable).isTrue()
skipAnywayBtn.click()
// Back to home
device.waitForWindowUpdate(SETTINGS_PACKAGE_NAME, IDLE_TIMEOUT)
assertThat(device.findObject(By.text("Skip anyway"))).isNull()
assertThat(device.findObject(By.text(DO_IT_LATER))).isNull()
}
@Test
fun testIntroWithGkPwHandle_clickGoBackInFindFpsDialog_whenIsSuw() {
LockScreenUtil.setLockscreen(LockScreenUtil.LockscreenType.PIN, TEST_PIN, true)
launchIntroWithGkPwHandle(true)
// Intro page
verifyIntroPage()
val agreeBtn = device.findObject(By.text("I agree"))
assertThat(agreeBtn).isNotNull()
agreeBtn.click()
// FindSensor page
assertThat(device.wait(Until.hasObject(By.text(DO_IT_LATER)), IDLE_TIMEOUT)).isTrue()
val doItLaterBtn = device.findObject(By.text(DO_IT_LATER))
assertThat(doItLaterBtn).isNotNull()
assertThat(doItLaterBtn.isClickable).isTrue()
doItLaterBtn.click()
// SkipSetupFindFpsDialog
assertThat(device.wait(Until.hasObject(By.text("Skip fingerprint?")), IDLE_TIMEOUT)).isTrue()
val goBackBtn = device.findObject(By.text("Go back"))
assertThat(goBackBtn).isNotNull()
assertThat(goBackBtn.isClickable).isTrue()
goBackBtn.click()
// FindSensor page again
assertThat(device.wait(Until.hasObject(By.text(DO_IT_LATER)), IDLE_TIMEOUT)).isTrue()
}
@Test
fun testIntroCheckPin() {
LockScreenUtil.setLockscreen(LockScreenUtil.LockscreenType.PIN, TEST_PIN, true)
val intent = newActivityIntent(false)
context.startActivity(intent)
assertThat(
device.wait(
Until.hasObject(By.text("Enter your device PIN to continue")),
IDLE_TIMEOUT
)
).isTrue()
}
@Test
fun testEnrollingWithGkPwHandle() {
LockScreenUtil.setLockscreen(LockScreenUtil.LockscreenType.PIN, TEST_PIN, true)
launchEnrollingWithGkPwHandle()
// Enrolling screen
device.waitForIdle()
assertThat(device.wait(Until.hasObject(By.text(enrollingPageTitle)), IDLE_TIMEOUT)).isTrue()
}
@Test
fun testEnrollingIconTouchDialog_withSfps() {
Assume.assumeTrue(canAssumeSfps)
LockScreenUtil.setLockscreen(LockScreenUtil.LockscreenType.PIN, TEST_PIN, true)
launchEnrollingWithGkPwHandle()
// Enrolling screen
device.waitForIdle()
assertThat(device.wait(Until.hasObject(By.text(enrollingPageTitle)), IDLE_TIMEOUT)).isTrue()
val lottie = device.findObject(
By.res(SETTINGS_PACKAGE_NAME, "illustration_lottie")
)
assertThat(lottie).isNotNull()
lottie.click()
lottie.click()
lottie.click()
// IconTouchDialog
device.waitForIdle()
assertThat(
device.wait(
Until.hasObject(By.text("Touch the sensor instead")),
IDLE_TIMEOUT
)
)
.isTrue()
val okButton = device.findObject(By.text("OK"))
assertThat(okButton).isNotNull()
okButton.click()
// Enrolling screen again
device.waitForIdle()
assertThat(device.wait(Until.hasObject(By.text(enrollingPageTitle)), IDLE_TIMEOUT)).isTrue()
}
@Test
fun testEnrollingIconTouchDialog_withRfps() {
Assume.assumeFalse(canAssumeUdfps || canAssumeSfps)
LockScreenUtil.setLockscreen(LockScreenUtil.LockscreenType.PIN, TEST_PIN, true)
launchEnrollingWithGkPwHandle()
// Enrolling screen
device.waitForIdle()
assertThat(device.wait(Until.hasObject(By.text(enrollingPageTitle)), IDLE_TIMEOUT)).isTrue()
val lottie = device.findObject(
By.res(SETTINGS_PACKAGE_NAME, "fingerprint_progress_bar")
)
assertThat(lottie).isNotNull()
lottie.click()
lottie.click()
lottie.click()
// IconTouchDialog
device.waitForIdle()
assertThat(
device.wait(
Until.hasObject(By.text("Whoops, that\u2019s not the sensor")),
IDLE_TIMEOUT
)
).isTrue()
val okButton = device.findObject(By.text("OK"))
assertThat(okButton).isNotNull()
okButton.click()
// Enrolling screen again
device.waitForIdle()
assertThat(device.wait(Until.hasObject(By.text(enrollingPageTitle)), IDLE_TIMEOUT)).isTrue()
}
@Test
fun testFindUdfpsWithGkPwHandle_clickStart() {
Assume.assumeTrue(canAssumeUdfps)
LockScreenUtil.setLockscreen(LockScreenUtil.LockscreenType.PIN, TEST_PIN, true)
launchFindSensorWithGkPwHandle()
// FindUdfps page
assertThat(device.wait(Until.hasObject(By.text(DO_IT_LATER)), IDLE_TIMEOUT)).isTrue()
val lottie = device.findObject(
By.res(SETTINGS_PACKAGE_NAME, "illustration_lottie")
)
assertThat(lottie).isNotNull()
assertThat(lottie.isClickable).isTrue()
val startBtn = device.findObject(By.text("Start"))
assertThat(startBtn.isClickable).isTrue()
startBtn.click()
// Enrolling page
assertThat(device.wait(Until.hasObject(By.text(enrollingPageTitle)), IDLE_TIMEOUT)).isTrue()
}
@Test
fun testFindUdfpsWithGkPwHandle_clickLottie() {
Assume.assumeTrue(canAssumeUdfps)
LockScreenUtil.setLockscreen(LockScreenUtil.LockscreenType.PIN, TEST_PIN, true)
launchFindSensorWithGkPwHandle()
// FindUdfps page
assertThat(device.wait(Until.hasObject(By.text(DO_IT_LATER)), IDLE_TIMEOUT)).isTrue()
val lottie = device.findObject(
By.res(SETTINGS_PACKAGE_NAME, "illustration_lottie")
)
assertThat(lottie).isNotNull()
assertThat(lottie.isClickable).isTrue()
val startBtn = device.findObject(By.text("Start"))
assertThat(startBtn.isClickable).isTrue()
lottie.click()
// Enrolling page
assertThat(device.wait(Until.hasObject(By.text(enrollingPageTitle)), IDLE_TIMEOUT)).isTrue()
}
@Test
fun testFindSfpsWithGkPwHandle() {
Assume.assumeTrue(canAssumeSfps)
LockScreenUtil.setLockscreen(LockScreenUtil.LockscreenType.PIN, TEST_PIN, true)
launchFindSensorWithGkPwHandle()
// FindSfps page
assertThat(device.wait(Until.hasObject(By.text(DO_IT_LATER)), IDLE_TIMEOUT)).isTrue()
val lottie = device.findObject(
By.res(SETTINGS_PACKAGE_NAME, "illustration_lottie")
)
assertThat(lottie).isNotNull()
// We don't have view which can be clicked to run to next page, stop at here.
}
@Test
fun testFindRfpsWithGkPwHandle() {
Assume.assumeFalse(canAssumeUdfps || canAssumeSfps)
LockScreenUtil.setLockscreen(LockScreenUtil.LockscreenType.PIN, TEST_PIN, true)
launchFindSensorWithGkPwHandle()
// FindRfps page
assertThat(device.wait(Until.hasObject(By.text(DO_IT_LATER)), IDLE_TIMEOUT)).isTrue()
val lottie = device.findObject(
By.res(
SETTINGS_PACKAGE_NAME,
"illustration_lottie"
)
)
if (lottie == null) {
// FindSfps page shall have an animation view if no lottie view
assertThat(
device.findObject(
By.res(
SETTINGS_PACKAGE_NAME,
"fingerprint_sensor_location_animation"
)
)
).isNotNull()
}
}
@Test
fun testFindSensorWithGkPwHandle_clickSkipInFindSensor() {
LockScreenUtil.setLockscreen(LockScreenUtil.LockscreenType.PIN, TEST_PIN, true)
launchFindSensorWithGkPwHandle()
// FindSensor page
assertThat(device.wait(Until.hasObject(By.text(DO_IT_LATER)), IDLE_TIMEOUT)).isTrue()
val doItLaterBtn = device.findObject(By.text(DO_IT_LATER))
assertThat(doItLaterBtn).isNotNull()
assertThat(doItLaterBtn.isClickable).isTrue()
doItLaterBtn.click()
// Back to home
device.waitForWindowUpdate(SETTINGS_PACKAGE_NAME, IDLE_TIMEOUT)
assertThat(device.wait(Until.gone(By.text(DO_IT_LATER)), IDLE_TIMEOUT)).isTrue()
}
private fun launchIntroWithGkPwHandle(isSuw: Boolean) {
val lockPatternUtils = LockPatternUtils(context)
val lockscreenCredential = LockscreenCredential.createPin(TEST_PIN)
val userId = UserHandle.myUserId()
val onVerifyCallback =
LockPatternChecker.OnVerifyCallback { response: VerifyCredentialResponse, _: Int ->
val intent = newActivityIntent(isSuw)
intent.putExtra(EXTRA_KEY_GK_PW_HANDLE, response.gatekeeperPasswordHandle)
context.startActivity(intent)
}
LockPatternChecker.verifyCredential(
lockPatternUtils, lockscreenCredential,
userId, LockPatternUtils.VERIFY_FLAG_REQUEST_GK_PW_HANDLE, onVerifyCallback
)
}
private fun launchFindSensorWithGkPwHandle() {
val lockPatternUtils = LockPatternUtils(context)
val lockscreenCredential = LockscreenCredential.createPin(TEST_PIN)
val userId = UserHandle.myUserId()
val onVerifyCallback =
LockPatternChecker.OnVerifyCallback { response: VerifyCredentialResponse, _: Int ->
val intent = newActivityIntent(false)
intent.putExtra(EXTRA_SKIP_INTRO, true)
intent.putExtra(EXTRA_KEY_GK_PW_HANDLE, response.gatekeeperPasswordHandle)
context.startActivity(intent)
}
LockPatternChecker.verifyCredential(
lockPatternUtils, lockscreenCredential,
userId, LockPatternUtils.VERIFY_FLAG_REQUEST_GK_PW_HANDLE, onVerifyCallback
)
}
private fun launchEnrollingWithGkPwHandle() {
val lockPatternUtils = LockPatternUtils(context)
val lockscreenCredential = LockscreenCredential.createPin(TEST_PIN)
val userId = UserHandle.myUserId()
val onVerifyCallback =
LockPatternChecker.OnVerifyCallback { response: VerifyCredentialResponse, _: Int ->
val intent = newActivityIntent(false)
intent.putExtra(EXTRA_SKIP_FIND_SENSOR, true)
intent.putExtra(EXTRA_KEY_GK_PW_HANDLE, response.gatekeeperPasswordHandle)
context.startActivity(intent)
}
LockPatternChecker.verifyCredential(
lockPatternUtils, lockscreenCredential,
userId, LockPatternUtils.VERIFY_FLAG_REQUEST_GK_PW_HANDLE, onVerifyCallback
)
}
private fun newActivityIntent(isSuw: Boolean): Intent {
val intent = Intent()
intent.setClassName(
SETTINGS_PACKAGE_NAME,
if (isSuw) SUW_ACTIVITY_CLASS_NAME else ACTIVITY_CLASS_NAME
)
if (isSuw) {
intent.putExtra(EXTRA_IS_SETUP_FLOW, true)
}
intent.putExtra(EXTRA_PAGE_TRANSITION_TYPE, 1)
intent.putExtra(Intent.EXTRA_USER_ID, context.userId)
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_NEW_TASK)
return intent
}
companion object {
private const val TAG = "FingerprintEnrollmentActivityTest"
const val SETTINGS_PACKAGE_NAME = "com.android.settings"
private const val ACTIVITY_CLASS_NAME =
"com.android.settings.biometrics2.ui.view.FingerprintEnrollmentActivity"
private const val SUW_ACTIVITY_CLASS_NAME = "$ACTIVITY_CLASS_NAME\$SetupActivity"
private const val EXTRA_IS_SETUP_FLOW = "isSetupFlow"
private const val EXTRA_SKIP_INTRO = "skip_intro"
private const val EXTRA_SKIP_FIND_SENSOR = "skip_find_sensor"
private const val EXTRA_PAGE_TRANSITION_TYPE = "page_transition_type"
private const val EXTRA_KEY_GK_PW_HANDLE = "gk_pw_handle"
private const val TEST_PIN = "1234"
private const val DO_IT_LATER = "Do it later"
private const val UDFPS_ENROLLING_TITLE = "Touch & hold the fingerprint sensor"
private const val SFPS_ENROLLING_TITLE =
"Lift, then touch. Move your finger slightly each time."
private const val RFPS_ENROLLING_TITLE = "Lift, then touch again"
private const val IDLE_TIMEOUT = 10000L
}
}