Refactor FingerprintEnrollFindSensor
Refactor FingerprintEnrollFindSensor to 3 pages for different sensor types, and apply MVVM for them. Bug: 259664912 Bug: 260957195 Bug: 260957816 Test: atest FingerprintRepositoryTest FingerprintEnrollmentActivityTest AutoCredentialViewModelTest FingerprintEnrollIntroViewModelTest Change-Id: Iace790952567cac13e61e5175e90555d4da7dfe2
This commit is contained in:
@@ -53,6 +53,14 @@ public class FingerprintRepository {
|
||||
return prop != null && prop.isAnyUdfpsType();
|
||||
}
|
||||
|
||||
/**
|
||||
* The first sensor type is Side fps sensor or not
|
||||
*/
|
||||
public boolean canAssumeSfps() {
|
||||
FingerprintSensorPropertiesInternal prop = getFirstFingerprintSensorPropertiesInternal();
|
||||
return prop != null && prop.isAnySidefpsType();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get max possible number of fingerprints for a user
|
||||
*/
|
||||
@@ -78,6 +86,7 @@ public class FingerprintRepository {
|
||||
|
||||
@Nullable
|
||||
private FingerprintSensorPropertiesInternal getFirstFingerprintSensorPropertiesInternal() {
|
||||
// TODO(b/264827022) use API addAuthenticatorsRegisteredCallback
|
||||
final List<FingerprintSensorPropertiesInternal> props =
|
||||
mFingerprintManager.getSensorPropertiesInternal();
|
||||
return props.size() > 0 ? props.get(0) : null;
|
||||
|
@@ -30,6 +30,8 @@ import com.android.settings.biometrics2.data.repository.FingerprintRepository;
|
||||
*/
|
||||
public class BiometricsRepositoryProviderImpl implements BiometricsRepositoryProvider {
|
||||
|
||||
private static volatile FingerprintRepository sFingerprintRepository;
|
||||
|
||||
/**
|
||||
* Get FingerprintRepository
|
||||
*/
|
||||
@@ -41,6 +43,13 @@ public class BiometricsRepositoryProviderImpl implements BiometricsRepositoryPro
|
||||
if (fingerprintManager == null) {
|
||||
return null;
|
||||
}
|
||||
return new FingerprintRepository(fingerprintManager);
|
||||
if (sFingerprintRepository == null) {
|
||||
synchronized (FingerprintRepository.class) {
|
||||
if (sFingerprintRepository == null) {
|
||||
sFingerprintRepository = new FingerprintRepository(fingerprintManager);
|
||||
}
|
||||
}
|
||||
}
|
||||
return sFingerprintRepository;
|
||||
}
|
||||
}
|
||||
|
@@ -27,12 +27,18 @@ import androidx.lifecycle.ViewModelProvider.AndroidViewModelFactory;
|
||||
import androidx.lifecycle.viewmodel.CreationExtras;
|
||||
|
||||
import com.android.internal.widget.LockPatternUtils;
|
||||
import com.android.settings.biometrics.fingerprint.FingerprintUpdater;
|
||||
import com.android.settings.biometrics2.data.repository.FingerprintRepository;
|
||||
import com.android.settings.biometrics2.ui.viewmodel.AutoCredentialViewModel;
|
||||
import com.android.settings.biometrics2.ui.viewmodel.AutoCredentialViewModel.ChallengeGenerator;
|
||||
import com.android.settings.biometrics2.ui.viewmodel.DeviceFoldedViewModel;
|
||||
import com.android.settings.biometrics2.ui.viewmodel.DeviceRotationViewModel;
|
||||
import com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollFindSensorViewModel;
|
||||
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.android.systemui.unfold.compat.ScreenSizeFoldProvider;
|
||||
|
||||
/**
|
||||
* View model factory for biometric enrollment fragment
|
||||
@@ -42,7 +48,7 @@ public class BiometricsViewModelFactory implements ViewModelProvider.Factory {
|
||||
private static final String TAG = "BiometricsViewModelFact";
|
||||
|
||||
public static final CreationExtras.Key<ChallengeGenerator> CHALLENGE_GENERATOR =
|
||||
new CreationExtras.Key<ChallengeGenerator>() {};
|
||||
new CreationExtras.Key<>() {};
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
@@ -59,7 +65,22 @@ public class BiometricsViewModelFactory implements ViewModelProvider.Factory {
|
||||
final BiometricsRepositoryProvider provider = FeatureFactory.getFactory(application)
|
||||
.getBiometricsRepositoryProvider();
|
||||
|
||||
if (modelClass.isAssignableFrom(FingerprintEnrollIntroViewModel.class)) {
|
||||
if (modelClass.isAssignableFrom(AutoCredentialViewModel.class)) {
|
||||
final LockPatternUtils lockPatternUtils =
|
||||
featureFactory.getSecurityFeatureProvider().getLockPatternUtils(application);
|
||||
final ChallengeGenerator challengeGenerator = extras.get(CHALLENGE_GENERATOR);
|
||||
if (challengeGenerator != null) {
|
||||
return (T) new AutoCredentialViewModel(application, lockPatternUtils,
|
||||
challengeGenerator);
|
||||
}
|
||||
} else if (modelClass.isAssignableFrom(DeviceFoldedViewModel.class)) {
|
||||
return (T) new DeviceFoldedViewModel(new ScreenSizeFoldProvider(application),
|
||||
application.getMainExecutor());
|
||||
} else if (modelClass.isAssignableFrom(DeviceRotationViewModel.class)) {
|
||||
return (T) new DeviceRotationViewModel(application);
|
||||
} else if (modelClass.isAssignableFrom(FingerprintEnrollFindSensorViewModel.class)) {
|
||||
return (T) new FingerprintEnrollFindSensorViewModel(application);
|
||||
} else if (modelClass.isAssignableFrom(FingerprintEnrollIntroViewModel.class)) {
|
||||
final FingerprintRepository repository = provider.getFingerprintRepository(application);
|
||||
if (repository != null) {
|
||||
return (T) new FingerprintEnrollIntroViewModel(application, repository);
|
||||
@@ -70,14 +91,9 @@ public class BiometricsViewModelFactory implements ViewModelProvider.Factory {
|
||||
return (T) new FingerprintEnrollmentViewModel(application, repository,
|
||||
application.getSystemService(KeyguardManager.class));
|
||||
}
|
||||
} else if (modelClass.isAssignableFrom(AutoCredentialViewModel.class)) {
|
||||
final LockPatternUtils lockPatternUtils =
|
||||
featureFactory.getSecurityFeatureProvider().getLockPatternUtils(application);
|
||||
final ChallengeGenerator challengeGenerator = extras.get(CHALLENGE_GENERATOR);
|
||||
if (challengeGenerator != null) {
|
||||
return (T) new AutoCredentialViewModel(application, lockPatternUtils,
|
||||
challengeGenerator);
|
||||
}
|
||||
} else if (modelClass.isAssignableFrom(FingerprintEnrollProgressViewModel.class)) {
|
||||
return (T) new FingerprintEnrollProgressViewModel(application,
|
||||
new FingerprintUpdater(application));
|
||||
}
|
||||
return create(modelClass);
|
||||
}
|
||||
|
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* 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.model;
|
||||
|
||||
/**
|
||||
* Biometric Enrollment progress
|
||||
*/
|
||||
public final class EnrollmentProgress {
|
||||
|
||||
public static final int INITIAL_STEPS = -1;
|
||||
public static final int INITIAL_REMAINING = 0;
|
||||
|
||||
private final int mSteps;
|
||||
private final int mRemaining;
|
||||
|
||||
public EnrollmentProgress(int steps, int remaining) {
|
||||
mSteps = steps;
|
||||
mRemaining = remaining;
|
||||
}
|
||||
|
||||
public int getSteps() {
|
||||
return mSteps;
|
||||
}
|
||||
|
||||
public int getRemaining() {
|
||||
return mRemaining;
|
||||
}
|
||||
|
||||
public boolean isInitialStep() {
|
||||
return mSteps == INITIAL_STEPS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return getClass().getSimpleName() + "@" + Integer.toHexString(hashCode())
|
||||
+ "{steps:" + mSteps + ", remaining:" + mRemaining + "}";
|
||||
}
|
||||
}
|
@@ -0,0 +1,209 @@
|
||||
/*
|
||||
* 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 android.hardware.fingerprint.FingerprintManager.ENROLL_FIND_SENSOR;
|
||||
import static android.view.View.OnClickListener;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
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 com.android.settings.R;
|
||||
import com.android.settings.biometrics.fingerprint.FingerprintFindSensorAnimation;
|
||||
import com.android.settings.biometrics2.ui.model.EnrollmentProgress;
|
||||
import com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollFindSensorViewModel;
|
||||
import com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollProgressViewModel;
|
||||
|
||||
import com.google.android.setupcompat.template.FooterBarMixin;
|
||||
import com.google.android.setupcompat.template.FooterButton;
|
||||
import com.google.android.setupdesign.GlifLayout;
|
||||
|
||||
/**
|
||||
* Fragment explaining the side fingerprint sensor location for fingerprint enrollment.
|
||||
* It interacts with ProgressViewModel, and FingerprintFindSensorAnimation.
|
||||
* <pre>
|
||||
| Has | UDFPS | SFPS | Other (Rear FPS) |
|
||||
|---------------------|-------|------|------------------|
|
||||
| Primary button | Yes | No | No |
|
||||
| Illustration Lottie | Yes | Yes | No |
|
||||
| Animation | No | No | Depend on layout |
|
||||
| Progress ViewModel | No | Yes | Yes |
|
||||
| Orientation detect | No | Yes | No |
|
||||
| Foldable detect | No | Yes | No |
|
||||
</pre>
|
||||
*/
|
||||
public class FingerprintEnrollFindRfpsFragment extends Fragment {
|
||||
|
||||
private static final boolean DEBUG = false;
|
||||
private static final String TAG = "FingerprintEnrollFindRfpsFragment";
|
||||
|
||||
private FingerprintEnrollFindSensorViewModel mViewModel;
|
||||
private FingerprintEnrollProgressViewModel mPorgressViewModel;
|
||||
|
||||
private View mView;
|
||||
private GlifLayout mGlifLayout;
|
||||
private FooterBarMixin mFooterBarMixin;
|
||||
private final OnClickListener mOnSkipClickListener = (v) -> mViewModel.onSkipButtonClick();
|
||||
@Nullable private FingerprintFindSensorAnimation mAnimation;
|
||||
|
||||
private final Observer<EnrollmentProgress> mProgressObserver = progress -> {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "mProgressObserver(" + progress + ")");
|
||||
}
|
||||
if (progress != null && !progress.isInitialStep()) {
|
||||
mViewModel.onStartButtonClick();
|
||||
}
|
||||
};
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
|
||||
@Nullable Bundle savedInstanceState) {
|
||||
final Context context = inflater.getContext();
|
||||
mView = inflater.inflate(R.layout.fingerprint_enroll_find_sensor, container, false);
|
||||
mGlifLayout = mView.findViewById(R.id.setup_wizard_layout);
|
||||
mFooterBarMixin = mGlifLayout.getMixin(FooterBarMixin.class);
|
||||
mFooterBarMixin.setSecondaryButton(
|
||||
new FooterButton.Builder(context)
|
||||
.setText(R.string.security_settings_fingerprint_enroll_enrolling_skip)
|
||||
.setButtonType(FooterButton.ButtonType.SKIP)
|
||||
.setTheme(R.style.SudGlifButton_Secondary)
|
||||
.build()
|
||||
);
|
||||
View animationView = mView.findViewById(R.id.fingerprint_sensor_location_animation);
|
||||
if (animationView instanceof FingerprintFindSensorAnimation) {
|
||||
mAnimation = (FingerprintFindSensorAnimation) animationView;
|
||||
}
|
||||
return mView;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
final Activity activity = getActivity();
|
||||
final GlifLayoutHelper glifLayoutHelper = new GlifLayoutHelper(activity, mGlifLayout);
|
||||
glifLayoutHelper.setHeaderText(
|
||||
R.string.security_settings_fingerprint_enroll_find_sensor_title);
|
||||
glifLayoutHelper.setDescriptionText(
|
||||
getText(R.string.security_settings_fingerprint_enroll_find_sensor_message));
|
||||
mFooterBarMixin.getSecondaryButton().setOnClickListener(mOnSkipClickListener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStart() {
|
||||
super.onStart();
|
||||
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "onStart(), start looking for fingerprint, animation exist:"
|
||||
+ (mAnimation != null));
|
||||
}
|
||||
startLookingForFingerprint();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
if (mAnimation != null) {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "onResume(), start animation");
|
||||
}
|
||||
mAnimation.startAnimation();
|
||||
}
|
||||
super.onResume();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPause() {
|
||||
if (mAnimation != null) {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "onPause(), pause animation");
|
||||
}
|
||||
mAnimation.pauseAnimation();
|
||||
}
|
||||
super.onPause();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStop() {
|
||||
super.onStop();
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "onStop(), stop looking for fingerprint, animation exist:"
|
||||
+ (mAnimation != null));
|
||||
}
|
||||
stopLookingForFingerprint();
|
||||
}
|
||||
|
||||
private void startLookingForFingerprint() {
|
||||
if (mPorgressViewModel.isEnrolling()) {
|
||||
Log.d(TAG, "startLookingForFingerprint(), failed because isEnrolling is true before"
|
||||
+ " starting");
|
||||
return;
|
||||
}
|
||||
|
||||
mPorgressViewModel.clearProgressLiveData();
|
||||
mPorgressViewModel.getProgressLiveData().observe(this, mProgressObserver);
|
||||
final boolean startResult = mPorgressViewModel.startEnrollment(ENROLL_FIND_SENSOR);
|
||||
if (!startResult) {
|
||||
Log.e(TAG, "startLookingForFingerprint(), failed to start enrollment");
|
||||
}
|
||||
}
|
||||
|
||||
private void stopLookingForFingerprint() {
|
||||
if (!mPorgressViewModel.isEnrolling()) {
|
||||
Log.d(TAG, "stopLookingForFingerprint(), failed because isEnrolling is false before"
|
||||
+ " stopping");
|
||||
return;
|
||||
}
|
||||
|
||||
mPorgressViewModel.getProgressLiveData().removeObserver(mProgressObserver);
|
||||
final boolean cancelResult = mPorgressViewModel.cancelEnrollment();
|
||||
if (!cancelResult) {
|
||||
Log.e(TAG, "stopLookingForFingerprint(), failed to cancel enrollment");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
if (mAnimation != null) {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "onDestroy(), stop animation");
|
||||
}
|
||||
mAnimation.stopAnimation();
|
||||
}
|
||||
super.onDestroy();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAttach(@NonNull Context context) {
|
||||
final FragmentActivity activity = getActivity();
|
||||
final ViewModelProvider provider = new ViewModelProvider(activity);
|
||||
mViewModel = provider.get(FingerprintEnrollFindSensorViewModel.class);
|
||||
mPorgressViewModel = provider.get(FingerprintEnrollProgressViewModel.class);
|
||||
super.onAttach(context);
|
||||
}
|
||||
}
|
@@ -0,0 +1,251 @@
|
||||
/*
|
||||
* 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 android.hardware.fingerprint.FingerprintManager.ENROLL_FIND_SENSOR;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.Surface;
|
||||
import android.view.View;
|
||||
import android.view.View.OnClickListener;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.RawRes;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.fragment.app.FragmentActivity;
|
||||
import androidx.lifecycle.LiveData;
|
||||
import androidx.lifecycle.Observer;
|
||||
import androidx.lifecycle.ViewModelProvider;
|
||||
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.biometrics2.ui.model.EnrollmentProgress;
|
||||
import com.android.settings.biometrics2.ui.viewmodel.DeviceFoldedViewModel;
|
||||
import com.android.settings.biometrics2.ui.viewmodel.DeviceRotationViewModel;
|
||||
import com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollFindSensorViewModel;
|
||||
import com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollProgressViewModel;
|
||||
import com.android.settingslib.widget.LottieColorUtils;
|
||||
|
||||
import com.airbnb.lottie.LottieAnimationView;
|
||||
import com.google.android.setupcompat.template.FooterBarMixin;
|
||||
import com.google.android.setupcompat.template.FooterButton;
|
||||
import com.google.android.setupdesign.GlifLayout;
|
||||
|
||||
/**
|
||||
* Fragment explaining the side fingerprint sensor location for fingerprint enrollment.
|
||||
* It interacts with ProgressViewModel, FoldCallback (for different lottie), and
|
||||
* LottieAnimationView.
|
||||
* <pre>
|
||||
| Has | UDFPS | SFPS | Other (Rear FPS) |
|
||||
|---------------------|-------|------|------------------|
|
||||
| Primary button | Yes | No | No |
|
||||
| Illustration Lottie | Yes | Yes | No |
|
||||
| Animation | No | No | Depend on layout |
|
||||
| Progress ViewModel | No | Yes | Yes |
|
||||
| Orientation detect | No | Yes | No |
|
||||
| Foldable detect | No | Yes | No |
|
||||
</pre>
|
||||
*/
|
||||
public class FingerprintEnrollFindSfpsFragment extends Fragment {
|
||||
|
||||
private static final boolean DEBUG = false;
|
||||
private static final String TAG = "FingerprintEnrollFindSfpsFragment";
|
||||
|
||||
private FingerprintEnrollFindSensorViewModel mViewModel;
|
||||
private FingerprintEnrollProgressViewModel mProgressViewModel;
|
||||
private DeviceRotationViewModel mRotationViewModel;
|
||||
private DeviceFoldedViewModel mFoldedViewModel;
|
||||
|
||||
private final Observer<Integer> mRotationObserver = rotation -> {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "rotationObserver " + rotation);
|
||||
}
|
||||
if (rotation == null) {
|
||||
return;
|
||||
}
|
||||
onRotationChanged(rotation);
|
||||
};
|
||||
|
||||
@Surface.Rotation private int mAnimationRotation = -1;
|
||||
|
||||
private View mView;
|
||||
private GlifLayout mGlifLayout;
|
||||
private FooterBarMixin mFooterBarMixin;
|
||||
private final OnClickListener mOnSkipClickListener = (v) -> mViewModel.onSkipButtonClick();
|
||||
private LottieAnimationView mIllustrationLottie;
|
||||
|
||||
private final Observer<EnrollmentProgress> mProgressObserver = progress -> {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "mProgressObserver(" + progress + ")");
|
||||
}
|
||||
if (progress != null && !progress.isInitialStep()) {
|
||||
mViewModel.onStartButtonClick();
|
||||
}
|
||||
};
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
|
||||
@Nullable Bundle savedInstanceState) {
|
||||
final Context context = inflater.getContext();
|
||||
mView = inflater.inflate(R.layout.sfps_enroll_find_sensor_layout, container, false);
|
||||
mGlifLayout = mView.findViewById(R.id.setup_wizard_layout);
|
||||
mIllustrationLottie = mView.findViewById(R.id.illustration_lottie);
|
||||
mFooterBarMixin = mGlifLayout.getMixin(FooterBarMixin.class);
|
||||
mFooterBarMixin.setSecondaryButton(
|
||||
new FooterButton.Builder(context)
|
||||
.setText(R.string.security_settings_fingerprint_enroll_enrolling_skip)
|
||||
.setButtonType(FooterButton.ButtonType.SKIP)
|
||||
.setTheme(R.style.SudGlifButton_Secondary)
|
||||
.build()
|
||||
);
|
||||
return mView;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
final Activity activity = getActivity();
|
||||
final GlifLayoutHelper glifLayoutHelper = new GlifLayoutHelper(activity, mGlifLayout);
|
||||
glifLayoutHelper.setHeaderText(R.string.security_settings_sfps_enroll_find_sensor_title);
|
||||
glifLayoutHelper.setDescriptionText(
|
||||
getText(R.string.security_settings_sfps_enroll_find_sensor_message));
|
||||
mFooterBarMixin.getSecondaryButton().setOnClickListener(mOnSkipClickListener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStart() {
|
||||
super.onStart();
|
||||
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "onStart(), start looking for fingerprint");
|
||||
}
|
||||
startLookingForFingerprint();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
final LiveData<Integer> rotationLiveData = mRotationViewModel.getLiveData();
|
||||
playLottieAnimation(rotationLiveData.getValue());
|
||||
rotationLiveData.observe(this, mRotationObserver);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPause() {
|
||||
mRotationViewModel.getLiveData().removeObserver(mRotationObserver);
|
||||
super.onPause();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStop() {
|
||||
super.onStop();
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "onStop(), stop looking for fingerprint");
|
||||
}
|
||||
stopLookingForFingerprint();
|
||||
}
|
||||
|
||||
private void startLookingForFingerprint() {
|
||||
if (mProgressViewModel.isEnrolling()) {
|
||||
Log.d(TAG, "startLookingForFingerprint(), failed because isEnrolling is true before"
|
||||
+ " starting");
|
||||
return;
|
||||
}
|
||||
|
||||
mProgressViewModel.clearProgressLiveData();
|
||||
mProgressViewModel.getProgressLiveData().observe(this, mProgressObserver);
|
||||
final boolean startResult = mProgressViewModel.startEnrollment(ENROLL_FIND_SENSOR);
|
||||
if (!startResult) {
|
||||
Log.e(TAG, "startLookingForFingerprint(), failed to start enrollment");
|
||||
}
|
||||
}
|
||||
|
||||
private void stopLookingForFingerprint() {
|
||||
if (!mProgressViewModel.isEnrolling()) {
|
||||
Log.d(TAG, "stopLookingForFingerprint(), failed because isEnrolling is false before"
|
||||
+ " stopping");
|
||||
return;
|
||||
}
|
||||
|
||||
mProgressViewModel.getProgressLiveData().removeObserver(mProgressObserver);
|
||||
final boolean cancelResult = mProgressViewModel.cancelEnrollment();
|
||||
if (!cancelResult) {
|
||||
Log.e(TAG, "stopLookingForFingerprint(), failed to cancel enrollment");
|
||||
}
|
||||
}
|
||||
|
||||
private void onRotationChanged(@Surface.Rotation int newRotation) {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "onRotationChanged() from " + mAnimationRotation + " to " + newRotation);
|
||||
}
|
||||
if ((newRotation + 2) % 4 == mAnimationRotation) {
|
||||
playLottieAnimation(newRotation);
|
||||
}
|
||||
// Fragment will be re-created if it's changed between landscape and portrait, so no need to
|
||||
// handle other cases.
|
||||
}
|
||||
|
||||
private void playLottieAnimation(@Surface.Rotation int rotation) {
|
||||
@RawRes final int animationRawRes = getSfpsLottieAnimationRawRes(rotation);
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "play lottie animation " + animationRawRes
|
||||
+ ", previous rotation:" + mAnimationRotation + ", new rotation:" + rotation);
|
||||
}
|
||||
|
||||
mAnimationRotation = rotation;
|
||||
mIllustrationLottie.setAnimation(animationRawRes);
|
||||
LottieColorUtils.applyDynamicColors(getActivity(), mIllustrationLottie);
|
||||
mIllustrationLottie.setVisibility(View.VISIBLE);
|
||||
mIllustrationLottie.playAnimation();
|
||||
}
|
||||
|
||||
@RawRes
|
||||
private int getSfpsLottieAnimationRawRes(@Surface.Rotation int rotation) {
|
||||
final boolean isFolded = !Boolean.FALSE.equals(mFoldedViewModel.getLiveData().getValue());
|
||||
switch (rotation) {
|
||||
case Surface.ROTATION_90:
|
||||
return isFolded ? R.raw.fingerprint_edu_lottie_folded_top_left
|
||||
: R.raw.fingerprint_edu_lottie_portrait_top_left;
|
||||
case Surface.ROTATION_180 :
|
||||
return isFolded ? R.raw.fingerprint_edu_lottie_folded_bottom_left
|
||||
: R.raw.fingerprint_edu_lottie_landscape_bottom_left;
|
||||
case Surface.ROTATION_270 :
|
||||
return isFolded ? R.raw.fingerprint_edu_lottie_folded_bottom_right
|
||||
: R.raw.fingerprint_edu_lottie_portrait_bottom_right;
|
||||
default :
|
||||
return isFolded ? R.raw.fingerprint_edu_lottie_folded_top_right
|
||||
: R.raw.fingerprint_edu_lottie_landscape_top_right;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAttach(@NonNull Context context) {
|
||||
final FragmentActivity activity = getActivity();
|
||||
final ViewModelProvider provider = new ViewModelProvider(activity);
|
||||
mViewModel = provider.get(FingerprintEnrollFindSensorViewModel.class);
|
||||
mProgressViewModel = provider.get(FingerprintEnrollProgressViewModel.class);
|
||||
mRotationViewModel = provider.get(DeviceRotationViewModel.class);
|
||||
mFoldedViewModel = provider.get(DeviceFoldedViewModel.class);
|
||||
super.onAttach(context);
|
||||
}
|
||||
}
|
@@ -0,0 +1,115 @@
|
||||
/*
|
||||
* 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 android.view.View.OnClickListener;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.lifecycle.ViewModelProvider;
|
||||
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollFindSensorViewModel;
|
||||
|
||||
import com.airbnb.lottie.LottieAnimationView;
|
||||
import com.google.android.setupcompat.template.FooterBarMixin;
|
||||
import com.google.android.setupcompat.template.FooterButton;
|
||||
import com.google.android.setupdesign.GlifLayout;
|
||||
|
||||
/**
|
||||
* Fragment explaining the under-display fingerprint sensor location for fingerprint enrollment.
|
||||
* It interacts with Primary button, and LottieAnimationView.
|
||||
* <pre>
|
||||
| Has | UDFPS | SFPS | Other (Rear FPS) |
|
||||
|---------------------|-------|------|------------------|
|
||||
| Primary button | Yes | No | No |
|
||||
| Illustration Lottie | Yes | Yes | No |
|
||||
| Animation | No | No | Depend on layout |
|
||||
| Progress ViewModel | No | Yes | Yes |
|
||||
| Orientation detect | No | Yes | No |
|
||||
| Foldable detect | No | Yes | No |
|
||||
</pre>
|
||||
*/
|
||||
public class FingerprintEnrollFindUdfpsFragment extends Fragment {
|
||||
|
||||
private FingerprintEnrollFindSensorViewModel mViewModel;
|
||||
|
||||
private View mView;
|
||||
private GlifLayout mGlifLayout;
|
||||
private FooterBarMixin mFooterBarMixin;
|
||||
private final OnClickListener mOnSkipClickListener = (v) -> mViewModel.onSkipButtonClick();
|
||||
private final OnClickListener mOnStartClickListener = (v) -> mViewModel.onStartButtonClick();
|
||||
private LottieAnimationView mIllustrationLottie;
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
|
||||
@Nullable Bundle savedInstanceState) {
|
||||
final Context context = inflater.getContext();
|
||||
mView = inflater.inflate(R.layout.udfps_enroll_find_sensor_layout, container, false);
|
||||
mGlifLayout = mView.findViewById(R.id.setup_wizard_layout);
|
||||
mIllustrationLottie = mView.findViewById(R.id.illustration_lottie);
|
||||
mFooterBarMixin = mGlifLayout.getMixin(FooterBarMixin.class);
|
||||
mFooterBarMixin.setSecondaryButton(
|
||||
new FooterButton.Builder(context)
|
||||
.setText(R.string.security_settings_fingerprint_enroll_enrolling_skip)
|
||||
.setButtonType(FooterButton.ButtonType.SKIP)
|
||||
.setTheme(R.style.SudGlifButton_Secondary)
|
||||
.build()
|
||||
);
|
||||
mFooterBarMixin.setPrimaryButton(
|
||||
new FooterButton.Builder(context)
|
||||
.setText(R.string.security_settings_udfps_enroll_find_sensor_start_button)
|
||||
.setButtonType(FooterButton.ButtonType.NEXT)
|
||||
.setTheme(R.style.SudGlifButton_Primary)
|
||||
.build()
|
||||
);
|
||||
return mView;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
final Activity activity = getActivity();
|
||||
final GlifLayoutHelper glifLayoutHelper = new GlifLayoutHelper(activity, mGlifLayout);
|
||||
glifLayoutHelper.setHeaderText(R.string.security_settings_udfps_enroll_find_sensor_title);
|
||||
glifLayoutHelper.setDescriptionText(
|
||||
getText(R.string.security_settings_udfps_enroll_find_sensor_message));
|
||||
mFooterBarMixin.getSecondaryButton().setOnClickListener(mOnSkipClickListener);
|
||||
mFooterBarMixin.getPrimaryButton().setOnClickListener(mOnStartClickListener);
|
||||
mIllustrationLottie.setOnClickListener(mOnStartClickListener);
|
||||
|
||||
if (mViewModel.isAccessibilityEnabled()) {
|
||||
mIllustrationLottie.setAnimation(R.raw.udfps_edu_a11y_lottie);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAttach(@NonNull Context context) {
|
||||
mViewModel = new ViewModelProvider(getActivity()).get(
|
||||
FingerprintEnrollFindSensorViewModel.class);
|
||||
super.onAttach(context);
|
||||
}
|
||||
}
|
@@ -24,18 +24,17 @@ import static com.android.settings.biometrics2.ui.model.FingerprintEnrollIntroSt
|
||||
|
||||
import static com.google.android.setupdesign.util.DynamicColorPalette.ColorType.ACCENT;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.admin.DevicePolicyManager;
|
||||
import android.content.Context;
|
||||
import android.graphics.PorterDuff;
|
||||
import android.graphics.PorterDuffColorFilter;
|
||||
import android.os.Bundle;
|
||||
import android.text.Html;
|
||||
import android.text.TextUtils;
|
||||
import android.text.method.LinkMovementMethod;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.View.OnClickListener;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
@@ -69,6 +68,9 @@ public class FingerprintEnrollIntroFragment extends Fragment {
|
||||
private View mView = null;
|
||||
private FooterButton mPrimaryFooterButton = null;
|
||||
private FooterButton mSecondaryFooterButton = null;
|
||||
private final OnClickListener mOnNextClickListener = (v) -> mViewModel.onNextButtonClick();
|
||||
private final OnClickListener mOnSkipOrCancelClickListener =
|
||||
(v) -> mViewModel.onSkipOrCancelButtonClick();
|
||||
private ImageView mIconShield = null;
|
||||
private TextView mFooterMessage6 = null;
|
||||
@Nullable private PorterDuffColorFilter mIconColorFilter;
|
||||
@@ -150,8 +152,8 @@ public class FingerprintEnrollIntroFragment extends Fragment {
|
||||
|
||||
final Context context = view.getContext();
|
||||
|
||||
mPrimaryFooterButton.setOnClickListener(mViewModel::onNextButtonClick);
|
||||
mSecondaryFooterButton.setOnClickListener(mViewModel::onSkipOrCancelButtonClick);
|
||||
mPrimaryFooterButton.setOnClickListener(mOnNextClickListener);
|
||||
mSecondaryFooterButton.setOnClickListener(mOnSkipOrCancelClickListener);
|
||||
|
||||
if (mViewModel.canAssumeUdfps()) {
|
||||
mFooterMessage6.setVisibility(View.VISIBLE);
|
||||
@@ -165,15 +167,15 @@ public class FingerprintEnrollIntroFragment extends Fragment {
|
||||
? R.string.security_settings_fingerprint_enroll_introduction_cancel
|
||||
: R.string.security_settings_fingerprint_enroll_introduction_no_thanks);
|
||||
|
||||
final GlifLayoutHelper glifLayoutHelper = new GlifLayoutHelper(getActivity(), getLayout());
|
||||
if (mViewModel.isBiometricUnlockDisabledByAdmin()
|
||||
&& !mViewModel.isParentalConsentRequired()) {
|
||||
setHeaderText(
|
||||
getActivity(),
|
||||
glifLayoutHelper.setHeaderText(
|
||||
R.string.security_settings_fingerprint_enroll_introduction_title_unlock_disabled
|
||||
);
|
||||
getLayout().setDescriptionText(getDescriptionDisabledByAdmin(context));
|
||||
glifLayoutHelper.setDescriptionText(getDescriptionDisabledByAdmin(context));
|
||||
} else {
|
||||
setHeaderText(getActivity(),
|
||||
glifLayoutHelper.setHeaderText(
|
||||
R.string.security_settings_fingerprint_enroll_introduction_title);
|
||||
}
|
||||
observePageStatusLiveDataIfNeed();
|
||||
@@ -192,7 +194,7 @@ public class FingerprintEnrollIntroFragment extends Fragment {
|
||||
final RequireScrollMixin requireScrollMixin = getLayout()
|
||||
.getMixin(RequireScrollMixin.class);
|
||||
requireScrollMixin.requireScrollWithButton(getActivity(), mPrimaryFooterButton,
|
||||
getMoreButtonTextRes(), mViewModel::onNextButtonClick);
|
||||
getMoreButtonTextRes(), mOnNextClickListener);
|
||||
|
||||
// Always set true to setHasScrolledToBottom() before registering listener through
|
||||
// setOnRequireScrollStateChangedListener(), because listener will not be called if first
|
||||
@@ -253,21 +255,6 @@ public class FingerprintEnrollIntroFragment extends Fragment {
|
||||
}
|
||||
}
|
||||
|
||||
private void setHeaderText(@NonNull Activity activity, int resId) {
|
||||
TextView layoutTitle = getLayout().getHeaderTextView();
|
||||
CharSequence previousTitle = layoutTitle.getText();
|
||||
CharSequence title = activity.getText(resId);
|
||||
if (previousTitle != title) {
|
||||
if (!TextUtils.isEmpty(previousTitle)) {
|
||||
layoutTitle.setAccessibilityLiveRegion(View.ACCESSIBILITY_LIVE_REGION_POLITE);
|
||||
}
|
||||
getLayout().setHeaderText(title);
|
||||
getLayout().getHeaderTextView().setContentDescription(title);
|
||||
activity.setTitle(title);
|
||||
}
|
||||
getLayout().getHeaderTextView().setContentDescription(activity.getText(resId));
|
||||
}
|
||||
|
||||
void updateFooterButtons(@NonNull FingerprintEnrollIntroStatus status) {
|
||||
@StringRes final int scrollToBottomPrimaryResId =
|
||||
status.getEnrollableStatus() == FINGERPRINT_ENROLLABLE_OK
|
||||
|
@@ -16,6 +16,7 @@
|
||||
|
||||
package com.android.settings.biometrics2.ui.view;
|
||||
|
||||
import static androidx.activity.result.contract.ActivityResultContracts.StartActivityForResult;
|
||||
import static androidx.lifecycle.ViewModelProvider.AndroidViewModelFactory.APPLICATION_KEY;
|
||||
|
||||
import static com.android.settings.biometrics2.factory.BiometricsViewModelFactory.CHALLENGE_GENERATOR;
|
||||
@@ -23,24 +24,34 @@ import static com.android.settings.biometrics2.ui.viewmodel.AutoCredentialViewMo
|
||||
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.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.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.Resources;
|
||||
import android.graphics.Color;
|
||||
import android.os.Bundle;
|
||||
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;
|
||||
import androidx.annotation.ColorInt;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.fragment.app.FragmentActivity;
|
||||
import androidx.fragment.app.FragmentManager;
|
||||
import androidx.lifecycle.Observer;
|
||||
import androidx.lifecycle.ViewModelProvider;
|
||||
import androidx.lifecycle.viewmodel.CreationExtras;
|
||||
import androidx.lifecycle.viewmodel.MutableCreationExtras;
|
||||
@@ -48,14 +59,16 @@ 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.biometrics.fingerprint.FingerprintEnrollFindSensor;
|
||||
import com.android.settings.biometrics.fingerprint.SetupFingerprintEnrollFindSensor;
|
||||
import com.android.settings.biometrics.fingerprint.FingerprintEnrollEnrolling;
|
||||
import com.android.settings.biometrics.fingerprint.SetupFingerprintEnrollEnrolling;
|
||||
import com.android.settings.biometrics2.data.repository.FingerprintRepository;
|
||||
import com.android.settings.biometrics2.factory.BiometricsViewModelFactory;
|
||||
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.FingerprintEnrollFindSensorViewModel;
|
||||
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;
|
||||
|
||||
@@ -66,37 +79,57 @@ import com.google.android.setupdesign.util.ThemeHelper;
|
||||
*/
|
||||
public class FingerprintEnrollmentActivity extends FragmentActivity {
|
||||
|
||||
private static final boolean DEBUG = false;
|
||||
private static final String TAG = "FingerprintEnrollmentActivity";
|
||||
|
||||
private static final String INTRO_TAG = "enroll-intro";
|
||||
private static final String FIND_UDFPS_TAG = "enroll-find-udfps";
|
||||
private static final String FIND_SFPS_TAG = "enroll-find-sfps";
|
||||
private static final String FIND_RFPS_TAG = "enroll-find-rfps";
|
||||
private static final String SKIP_SETUP_FIND_FPS_DIALOG_TAG = "skip-setup-dialog";
|
||||
|
||||
protected static final int LAUNCH_CONFIRM_LOCK_ACTIVITY = 1;
|
||||
|
||||
private ViewModelProvider mViewModelProvider;
|
||||
private FingerprintEnrollmentViewModel mViewModel;
|
||||
private AutoCredentialViewModel mAutoCredentialViewModel;
|
||||
private ActivityResultLauncher<Intent> mNextActivityLauncher;
|
||||
private ActivityResultLauncher<Intent> mChooseLockLauncher;
|
||||
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 ActivityResultCallback<ActivityResult> mNextActivityResultCallback =
|
||||
result -> mViewModel.onContinueEnrollActivityResult(result,
|
||||
mAutoCredentialViewModel.getUserId());
|
||||
private final ActivityResultLauncher<Intent> mNextActivityLauncher =
|
||||
registerForActivityResult(new StartActivityForResult(), mNextActivityResultCallback);
|
||||
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);
|
||||
|
||||
mNextActivityLauncher = registerForActivityResult(
|
||||
new ActivityResultContracts.StartActivityForResult(),
|
||||
(it) -> mViewModel.onContinueEnrollActivityResult(
|
||||
it,
|
||||
mAutoCredentialViewModel.getUserId())
|
||||
);
|
||||
mChooseLockLauncher = registerForActivityResult(
|
||||
new ActivityResultContracts.StartActivityForResult(),
|
||||
(it) -> onChooseOrConfirmLockResult(true, it)
|
||||
);
|
||||
mViewModelProvider = new ViewModelProvider(this);
|
||||
|
||||
ViewModelProvider viewModelProvider = new ViewModelProvider(this);
|
||||
|
||||
mViewModel = viewModelProvider.get(FingerprintEnrollmentViewModel.class);
|
||||
mViewModel = mViewModelProvider.get(FingerprintEnrollmentViewModel.class);
|
||||
mViewModel.setRequest(new EnrollmentRequest(getIntent(), getApplicationContext()));
|
||||
mViewModel.setSavedInstanceState(savedInstanceState);
|
||||
|
||||
mAutoCredentialViewModel = viewModelProvider.get(AutoCredentialViewModel.class);
|
||||
mAutoCredentialViewModel = mViewModelProvider.get(AutoCredentialViewModel.class);
|
||||
mAutoCredentialViewModel.setCredentialModel(savedInstanceState, getIntent());
|
||||
|
||||
// Theme
|
||||
@@ -106,20 +139,37 @@ public class FingerprintEnrollmentActivity extends FragmentActivity {
|
||||
|
||||
// fragment
|
||||
setContentView(R.layout.biometric_enrollment_container);
|
||||
final FingerprintEnrollIntroViewModel introViewModel =
|
||||
viewModelProvider.get(FingerprintEnrollIntroViewModel.class);
|
||||
introViewModel.setEnrollmentRequest(mViewModel.getRequest());
|
||||
introViewModel.setUserId(mAutoCredentialViewModel.getUserId());
|
||||
|
||||
if (DEBUG) {
|
||||
Log.e(TAG, "onCreate() has savedInstance:" + (savedInstanceState != null));
|
||||
}
|
||||
if (savedInstanceState == null) {
|
||||
checkCredential();
|
||||
|
||||
final String tag = "FingerprintEnrollIntroFragment";
|
||||
getSupportFragmentManager().beginTransaction()
|
||||
.setReorderingAllowed(true)
|
||||
.add(R.id.fragment_container_view, FingerprintEnrollIntroFragment.class, null,
|
||||
tag)
|
||||
.commit();
|
||||
startIntroFragment();
|
||||
} else {
|
||||
final FragmentManager manager = getSupportFragmentManager();
|
||||
String[] tags = new String[] {
|
||||
FIND_UDFPS_TAG,
|
||||
FIND_SFPS_TAG,
|
||||
FIND_RFPS_TAG,
|
||||
INTRO_TAG
|
||||
};
|
||||
for (String tag: tags) {
|
||||
final Fragment fragment = manager.findFragmentByTag(tag);
|
||||
if (fragment == null) {
|
||||
continue;
|
||||
}
|
||||
if (DEBUG) {
|
||||
Log.e(TAG, "onCreate() currentFragment:" + tag);
|
||||
}
|
||||
if (tag.equals(INTRO_TAG)) {
|
||||
attachIntroViewModel();
|
||||
} else { // FIND_UDFPS_TAG, FIND_SFPS_TAG, FIND_RFPS_TAG
|
||||
attachFindSensorViewModel();
|
||||
attachIntroViewModel();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// observe LiveData
|
||||
@@ -128,11 +178,80 @@ public class FingerprintEnrollmentActivity extends FragmentActivity {
|
||||
|
||||
mAutoCredentialViewModel.getGenerateChallengeFailedLiveData().observe(this,
|
||||
this::onGenerateChallengeFailed);
|
||||
}
|
||||
|
||||
private void startIntroFragment() {
|
||||
attachIntroViewModel();
|
||||
getSupportFragmentManager().beginTransaction()
|
||||
.setReorderingAllowed(true)
|
||||
.replace(R.id.fragment_container_view, FingerprintEnrollIntroFragment.class, null,
|
||||
INTRO_TAG)
|
||||
.commit();
|
||||
}
|
||||
|
||||
private void attachIntroViewModel() {
|
||||
final FingerprintEnrollIntroViewModel introViewModel =
|
||||
mViewModelProvider.get(FingerprintEnrollIntroViewModel.class);
|
||||
|
||||
introViewModel.setEnrollmentRequest(mViewModel.getRequest());
|
||||
introViewModel.setUserId(mAutoCredentialViewModel.getUserId());
|
||||
|
||||
// Clear ActionLiveData in FragmentViewModel to prevent getting previous action during
|
||||
// recreate, like press 'I agree' then press 'back' in FingerprintEnrollFindSensor activity.
|
||||
// recreate, like press 'Agree' then press 'back' in FingerprintEnrollFindSensor activity.
|
||||
introViewModel.clearActionLiveData();
|
||||
introViewModel.getActionLiveData().observe(this, this::observeIntroAction);
|
||||
introViewModel.getActionLiveData().observe(this, mIntroActionObserver);
|
||||
}
|
||||
|
||||
// We need to make sure token is valid before entering find sensor page
|
||||
private void startFindSensorFragment() {
|
||||
attachFindSensorViewModel();
|
||||
if (mViewModel.canAssumeUdfps()) {
|
||||
// UDFPS does not need to start real fingerprint enrolling during finding sensor
|
||||
startFindFpsFragmentWithProgressViewModel(FingerprintEnrollFindUdfpsFragment.class,
|
||||
FIND_UDFPS_TAG, false /* initProgressViewModel */);
|
||||
} else if (mViewModel.canAssumeSfps()) {
|
||||
startFindFpsFragmentWithProgressViewModel(FingerprintEnrollFindSfpsFragment.class,
|
||||
FIND_SFPS_TAG, true /* initProgressViewModel */);
|
||||
} else {
|
||||
startFindFpsFragmentWithProgressViewModel(FingerprintEnrollFindRfpsFragment.class,
|
||||
FIND_RFPS_TAG, true /* initProgressViewModel */);
|
||||
}
|
||||
}
|
||||
|
||||
private void startFindFpsFragmentWithProgressViewModel(
|
||||
@NonNull Class<? extends Fragment> findFpsClass, @NonNull String tag,
|
||||
boolean initProgressViewModel) {
|
||||
if (initProgressViewModel) {
|
||||
final FingerprintEnrollProgressViewModel progressViewModel =
|
||||
mViewModelProvider.get(FingerprintEnrollProgressViewModel.class);
|
||||
progressViewModel.setUserId(mAutoCredentialViewModel.getUserId());
|
||||
progressViewModel.setToken(mAutoCredentialViewModel.getToken());
|
||||
}
|
||||
final FingerprintEnrollFindSensorViewModel findSensorViewModel =
|
||||
mViewModelProvider.get(FingerprintEnrollFindSensorViewModel.class);
|
||||
findSensorViewModel.setIsSuw(mViewModel.getRequest().isSuw());
|
||||
getSupportFragmentManager().beginTransaction()
|
||||
.setReorderingAllowed(true)
|
||||
.setCustomAnimations(R.anim.sud_slide_next_in, R.anim.sud_slide_next_out,
|
||||
R.anim.sud_slide_back_in, R.anim.sud_slide_back_out)
|
||||
.replace(R.id.fragment_container_view, findFpsClass, null, tag)
|
||||
.addToBackStack(tag)
|
||||
.commit();
|
||||
}
|
||||
|
||||
private void attachFindSensorViewModel() {
|
||||
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 startSkipSetupFindFpsDialog() {
|
||||
new SkipSetupFindFpsDialog().show(getSupportFragmentManager(),
|
||||
SKIP_SETUP_FIND_FPS_DIALOG_TAG);
|
||||
}
|
||||
|
||||
private void onGenerateChallengeFailed(@NonNull Boolean ignoredBoolean) {
|
||||
@@ -217,10 +336,7 @@ public class FingerprintEnrollmentActivity extends FragmentActivity {
|
||||
}
|
||||
}
|
||||
|
||||
private void observeIntroAction(@Nullable Integer action) {
|
||||
if (action == null) {
|
||||
return;
|
||||
}
|
||||
private void onIntroAction(@FingerprintEnrollIntroAction int action) {
|
||||
switch (action) {
|
||||
case FINGERPRINT_ENROLL_INTRO_ACTION_DONE_AND_FINISH: {
|
||||
onSetActivityResult(
|
||||
@@ -233,13 +349,30 @@ public class FingerprintEnrollmentActivity extends FragmentActivity {
|
||||
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: {
|
||||
startSkipSetupFindFpsDialog();
|
||||
return;
|
||||
}
|
||||
case FINGERPRINT_ENROLL_FIND_SENSOR_ACTION_START: {
|
||||
final boolean isSuw = mViewModel.getRequest().isSuw();
|
||||
if (!mViewModel.isWaitingActivityResult().compareAndSet(false, true)) {
|
||||
Log.w(TAG, "startNext, isSuw:" + isSuw + ", fail to set isWaiting flag");
|
||||
}
|
||||
final Intent intent = new Intent(this, isSuw
|
||||
? SetupFingerprintEnrollFindSensor.class
|
||||
: FingerprintEnrollFindSensor.class);
|
||||
Intent intent = new Intent(this, isSuw
|
||||
? SetupFingerprintEnrollEnrolling.class
|
||||
: FingerprintEnrollEnrolling.class);
|
||||
intent.putExtras(mAutoCredentialViewModel.createCredentialIntentExtra());
|
||||
intent.putExtras(mViewModel.getNextActivityBaseIntentExtras());
|
||||
mNextActivityLauncher.launch(intent);
|
||||
@@ -253,6 +386,12 @@ public class FingerprintEnrollmentActivity extends FragmentActivity {
|
||||
mViewModel.checkFinishActivityDuringOnPause(isFinishing(), isChangingConfigurations());
|
||||
}
|
||||
|
||||
@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) {
|
||||
|
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* 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 android.app.Activity;
|
||||
import android.text.TextUtils;
|
||||
import android.view.View;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.StringRes;
|
||||
|
||||
import com.google.android.setupdesign.GlifLayout;
|
||||
|
||||
/**
|
||||
* Utils class for GlifLayout
|
||||
*/
|
||||
public class GlifLayoutHelper {
|
||||
|
||||
@NonNull private final Activity mActivity;
|
||||
@NonNull private final GlifLayout mGlifLayout;
|
||||
|
||||
public GlifLayoutHelper(@NonNull Activity activity, @NonNull GlifLayout glifLayout) {
|
||||
mActivity = activity;
|
||||
mGlifLayout = glifLayout;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets header text to GlifLayout
|
||||
*/
|
||||
public void setHeaderText(@StringRes int textResId) {
|
||||
TextView layoutTitle = mGlifLayout.getHeaderTextView();
|
||||
CharSequence previousTitle = layoutTitle.getText();
|
||||
CharSequence title = mActivity.getText(textResId);
|
||||
if (previousTitle != title) {
|
||||
if (!TextUtils.isEmpty(previousTitle)) {
|
||||
layoutTitle.setAccessibilityLiveRegion(View.ACCESSIBILITY_LIVE_REGION_POLITE);
|
||||
}
|
||||
mGlifLayout.setHeaderText(title);
|
||||
mGlifLayout.getHeaderTextView().setContentDescription(title);
|
||||
mActivity.setTitle(title);
|
||||
}
|
||||
mGlifLayout.getHeaderTextView().setContentDescription(title);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets description text to GlifLayout
|
||||
*/
|
||||
public void setDescriptionText(CharSequence description) {
|
||||
CharSequence previousDescription = mGlifLayout.getDescriptionText();
|
||||
// Prevent a11y for re-reading the same string
|
||||
if (!TextUtils.equals(previousDescription, description)) {
|
||||
mGlifLayout.setDescriptionText(description);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* 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 android.content.DialogInterface.OnClickListener;
|
||||
|
||||
import android.app.Dialog;
|
||||
import android.app.settings.SettingsEnums;
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.lifecycle.ViewModelProvider;
|
||||
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollFindSensorViewModel;
|
||||
import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
|
||||
|
||||
/**
|
||||
* Skip dialog which shows when user clicks "Do it later" button in FingerprintFindSensor page.
|
||||
*/
|
||||
public class SkipSetupFindFpsDialog extends InstrumentedDialogFragment {
|
||||
|
||||
private FingerprintEnrollFindSensorViewModel mViewModel;
|
||||
private final OnClickListener mOnSkipClickListener =
|
||||
(d, w) -> mViewModel.onSkipDialogButtonClick();
|
||||
|
||||
@Override
|
||||
public int getMetricsCategory() {
|
||||
return SettingsEnums.DIALOG_FINGERPRINT_SKIP_SETUP;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||
return onCreateDialogBuilder().create();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns builder for this dialog
|
||||
*/
|
||||
@NonNull
|
||||
private AlertDialog.Builder onCreateDialogBuilder() {
|
||||
return new AlertDialog.Builder(getActivity(), R.style.Theme_AlertDialog)
|
||||
.setTitle(R.string.setup_fingerprint_enroll_skip_title)
|
||||
.setPositiveButton(R.string.skip_anyway_button_label, mOnSkipClickListener)
|
||||
.setNegativeButton(R.string.go_back_button_label, null)
|
||||
.setMessage(R.string.setup_fingerprint_enroll_skip_after_adding_lock_text);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAttach(Context context) {
|
||||
mViewModel = new ViewModelProvider(getActivity()).get(
|
||||
FingerprintEnrollFindSensorViewModel.class);
|
||||
super.onAttach(context);
|
||||
}
|
||||
}
|
@@ -253,7 +253,11 @@ public class AutoCredentialViewModel extends AndroidViewModel {
|
||||
if (isUnspecifiedPassword()) {
|
||||
return CREDENTIAL_FAIL_NEED_TO_CHOOSE_LOCK;
|
||||
} else if (mCredentialModel.isValidGkPwHandle()) {
|
||||
generateChallenge(mCredentialModel.getGkPwHandle());
|
||||
final long gkPwHandle = mCredentialModel.getGkPwHandle();
|
||||
mCredentialModel.clearGkPwHandle();
|
||||
// GkPwHandle is got through caller activity, we shall not revoke it after
|
||||
// generateChallenge(). Let caller activity to make decision.
|
||||
generateChallenge(gkPwHandle, false /* revokeGkPwHandle */);
|
||||
mIsGeneratingChallengeDuringCheckingCredential = true;
|
||||
return CREDENTIAL_IS_GENERATING_CHALLENGE;
|
||||
} else {
|
||||
@@ -261,7 +265,7 @@ public class AutoCredentialViewModel extends AndroidViewModel {
|
||||
}
|
||||
}
|
||||
|
||||
private void generateChallenge(long gkPwHandle) {
|
||||
private void generateChallenge(long gkPwHandle, boolean revokeGkPwHandle) {
|
||||
mChallengeGenerator.setCallback((sensorId, userId, challenge) -> {
|
||||
try {
|
||||
final byte[] newToken = requestGatekeeperHat(gkPwHandle, challenge, userId);
|
||||
@@ -274,11 +278,13 @@ public class AutoCredentialViewModel extends AndroidViewModel {
|
||||
return;
|
||||
}
|
||||
|
||||
if (revokeGkPwHandle) {
|
||||
mLockPatternUtils.removeGatekeeperPasswordHandle(gkPwHandle);
|
||||
mCredentialModel.clearGkPwHandle();
|
||||
}
|
||||
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "generateChallenge " + mCredentialModel);
|
||||
Log.d(TAG, "generateChallenge(), model:" + mCredentialModel
|
||||
+ ", revokeGkPwHandle:" + revokeGkPwHandle);
|
||||
}
|
||||
|
||||
// Check credential again
|
||||
@@ -314,7 +320,9 @@ public class AutoCredentialViewModel extends AndroidViewModel {
|
||||
if (data != null) {
|
||||
final long gkPwHandle = result.getData().getLongExtra(
|
||||
EXTRA_KEY_GK_PW_HANDLE, INVALID_GK_PW_HANDLE);
|
||||
generateChallenge(gkPwHandle);
|
||||
// Revoke self requested GkPwHandle because it shall only used once inside this
|
||||
// activity lifecycle.
|
||||
generateChallenge(gkPwHandle, true /* revokeGkPwHandle */);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -328,6 +336,14 @@ public class AutoCredentialViewModel extends AndroidViewModel {
|
||||
return mCredentialModel.getUserId();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get userId for this credential
|
||||
*/
|
||||
@Nullable
|
||||
public byte[] getToken() {
|
||||
return mCredentialModel.getToken();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private byte[] requestGatekeeperHat(long gkPwHandle, long challenge, int userId)
|
||||
throws IllegalStateException {
|
||||
|
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* 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.util.Log;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.lifecycle.LiveData;
|
||||
import androidx.lifecycle.MutableLiveData;
|
||||
import androidx.lifecycle.ViewModel;
|
||||
|
||||
import com.android.systemui.unfold.compat.ScreenSizeFoldProvider;
|
||||
import com.android.systemui.unfold.updates.FoldProvider;
|
||||
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
/**
|
||||
* ViewModel explaining the fingerprint sensor location for fingerprint enrollment.
|
||||
*/
|
||||
public class DeviceFoldedViewModel extends ViewModel {
|
||||
|
||||
private static final String TAG = "DeviceFoldedViewModel";
|
||||
|
||||
@NonNull private final MutableLiveData<Boolean> mLiveData =
|
||||
new MutableLiveData<>(null);
|
||||
|
||||
private final ScreenSizeFoldProvider mScreenSizeFoldProvider;
|
||||
private final FoldProvider.FoldCallback mIsFoldedCallback = isFolded -> {
|
||||
Log.d(TAG, "onFoldUpdated= " + isFolded);
|
||||
mLiveData.postValue(isFolded);
|
||||
};
|
||||
|
||||
public DeviceFoldedViewModel(@NonNull ScreenSizeFoldProvider screenSizeFoldProvider,
|
||||
@NonNull Executor executor) {
|
||||
super();
|
||||
mScreenSizeFoldProvider = screenSizeFoldProvider;
|
||||
mScreenSizeFoldProvider.registerCallback(mIsFoldedCallback, executor);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns FoldedLiveData
|
||||
*/
|
||||
public LiveData<Boolean> getLiveData() {
|
||||
return mLiveData;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCleared() {
|
||||
mScreenSizeFoldProvider.unregisterCallback(mIsFoldedCallback);
|
||||
super.onCleared();
|
||||
}
|
||||
}
|
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
* 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 android.hardware.display.DisplayManager.DisplayListener;
|
||||
|
||||
import android.app.Application;
|
||||
import android.hardware.display.DisplayManager;
|
||||
import android.util.Log;
|
||||
import android.view.DisplayInfo;
|
||||
import android.view.Surface;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.lifecycle.AndroidViewModel;
|
||||
import androidx.lifecycle.LiveData;
|
||||
import androidx.lifecycle.MutableLiveData;
|
||||
|
||||
/**
|
||||
* ViewModel explaining the fingerprint sensor location for fingerprint enrollment.
|
||||
*/
|
||||
public class DeviceRotationViewModel extends AndroidViewModel {
|
||||
|
||||
private static final boolean DEBUG = false;
|
||||
private static final String TAG = "RotationViewModel";
|
||||
|
||||
private final DisplayManager mDisplayManager;
|
||||
@NonNull private final DisplayInfo mDisplayInfo = new DisplayInfo();
|
||||
private final DisplayListener mDisplayListener = new DisplayListener() {
|
||||
@Override
|
||||
public void onDisplayAdded(int displayId) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisplayRemoved(int displayId) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisplayChanged(int displayId) {
|
||||
final int rotation = getRotation();
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "onDisplayChanged(" + displayId + "), rotation:" + rotation);
|
||||
}
|
||||
mLiveData.postValue(rotation);
|
||||
}
|
||||
};
|
||||
|
||||
@NonNull private final MutableLiveData<Integer> mLiveData =
|
||||
new MutableLiveData<>(getRotation());
|
||||
|
||||
public DeviceRotationViewModel(@NonNull Application application) {
|
||||
super(application);
|
||||
mDisplayManager = application.getSystemService(DisplayManager.class);
|
||||
mDisplayManager.registerDisplayListener(mDisplayListener,
|
||||
application.getMainThreadHandler());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns current rotation
|
||||
*/
|
||||
@Surface.Rotation
|
||||
private int getRotation() {
|
||||
getApplication().getDisplay().getDisplayInfo(mDisplayInfo);
|
||||
return mDisplayInfo.rotation;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns RotationLiveData
|
||||
*/
|
||||
public LiveData<Integer> getLiveData() {
|
||||
return mLiveData;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCleared() {
|
||||
mDisplayManager.unregisterDisplayListener(mDisplayListener);
|
||||
super.onCleared();
|
||||
}
|
||||
}
|
@@ -0,0 +1,135 @@
|
||||
/*
|
||||
* 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 android.annotation.IntDef;
|
||||
import android.app.Application;
|
||||
import android.util.Log;
|
||||
import android.view.accessibility.AccessibilityManager;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.lifecycle.AndroidViewModel;
|
||||
import androidx.lifecycle.LiveData;
|
||||
import androidx.lifecycle.MutableLiveData;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
|
||||
/**
|
||||
* ViewModel explaining the fingerprint sensor location for fingerprint enrollment.
|
||||
*/
|
||||
public class FingerprintEnrollFindSensorViewModel extends AndroidViewModel {
|
||||
|
||||
private static final boolean DEBUG = false;
|
||||
private static final String TAG = "FingerprintEnrollFindSensorViewModel";
|
||||
|
||||
/**
|
||||
* User clicks 'Skip' button on this page in Settings
|
||||
*/
|
||||
public static final int FINGERPRINT_ENROLL_FIND_SENSOR_ACTION_SKIP = 0;
|
||||
|
||||
/**
|
||||
* User clicks 'Skip' button on this page in SetupWizard flow
|
||||
*/
|
||||
public static final int FINGERPRINT_ENROLL_FIND_SENSOR_ACTION_DIALOG = 1;
|
||||
|
||||
/**
|
||||
* User clicks 'Start' button on this page
|
||||
*/
|
||||
public static final int FINGERPRINT_ENROLL_FIND_SENSOR_ACTION_START = 2;
|
||||
|
||||
@IntDef(prefix = { "FINGERPRINT_ENROLL_FIND_SENSOR_ACTION_" }, value = {
|
||||
FINGERPRINT_ENROLL_FIND_SENSOR_ACTION_SKIP,
|
||||
FINGERPRINT_ENROLL_FIND_SENSOR_ACTION_DIALOG,
|
||||
FINGERPRINT_ENROLL_FIND_SENSOR_ACTION_START
|
||||
})
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
public @interface FingerprintEnrollFindSensorAction {}
|
||||
|
||||
private final AccessibilityManager mAccessibilityManager;
|
||||
|
||||
private boolean mIsSuw = false;
|
||||
@NonNull private final MutableLiveData<Integer> mActionLiveData = new MutableLiveData<>();
|
||||
|
||||
public FingerprintEnrollFindSensorViewModel(@NonNull Application application) {
|
||||
super(application);
|
||||
mAccessibilityManager = application.getSystemService(AccessibilityManager.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets isSetupWizard or not
|
||||
*/
|
||||
public void setIsSuw(boolean isSuw) {
|
||||
mIsSuw = isSuw;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns action live data that user chooses
|
||||
*/
|
||||
public LiveData<Integer> getActionLiveData() {
|
||||
return mActionLiveData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear ActionLiveData to prevent get obsolete data
|
||||
*/
|
||||
public void clearActionLiveData() {
|
||||
mActionLiveData.setValue(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* User clicks skip button on dialog
|
||||
*/
|
||||
public void onSkipDialogButtonClick() {
|
||||
final int action = FINGERPRINT_ENROLL_FIND_SENSOR_ACTION_SKIP;
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "onSkipDialogButtonClick, post " + action);
|
||||
}
|
||||
mActionLiveData.postValue(action);
|
||||
}
|
||||
|
||||
/**
|
||||
* User clicks skip button
|
||||
*/
|
||||
public void onSkipButtonClick() {
|
||||
final int action = mIsSuw
|
||||
? FINGERPRINT_ENROLL_FIND_SENSOR_ACTION_DIALOG
|
||||
: FINGERPRINT_ENROLL_FIND_SENSOR_ACTION_SKIP;
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "onSkipButtonClick, post action " + action);
|
||||
}
|
||||
mActionLiveData.postValue(action);
|
||||
}
|
||||
|
||||
/**
|
||||
* User clicks start button
|
||||
*/
|
||||
public void onStartButtonClick() {
|
||||
final int action = FINGERPRINT_ENROLL_FIND_SENSOR_ACTION_START;
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "onStartButtonClick, post action " + action);
|
||||
}
|
||||
mActionLiveData.postValue(action);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the info about accessibility is enabled or not
|
||||
*/
|
||||
public boolean isAccessibilityEnabled() {
|
||||
return mAccessibilityManager.isEnabled();
|
||||
}
|
||||
}
|
@@ -24,7 +24,6 @@ import android.annotation.IntDef;
|
||||
import android.app.Application;
|
||||
import android.os.UserHandle;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.lifecycle.AndroidViewModel;
|
||||
@@ -189,7 +188,7 @@ public class FingerprintEnrollIntroViewModel extends AndroidViewModel
|
||||
/**
|
||||
* User clicks next button
|
||||
*/
|
||||
public void onNextButtonClick(View ignoredView) {
|
||||
public void onNextButtonClick() {
|
||||
final Integer status = mEnrollableStatusLiveData.getValue();
|
||||
switch (status != null ? status : ENROLLABLE_STATUS_DEFAULT) {
|
||||
case FINGERPRINT_ENROLLABLE_ERROR_REACH_MAX:
|
||||
@@ -206,7 +205,7 @@ public class FingerprintEnrollIntroViewModel extends AndroidViewModel
|
||||
/**
|
||||
* User clicks skip/cancel button
|
||||
*/
|
||||
public void onSkipOrCancelButtonClick(View ignoredView) {
|
||||
public void onSkipOrCancelButtonClick() {
|
||||
mActionLiveData.postValue(FINGERPRINT_ENROLL_INTRO_ACTION_SKIP_OR_CANCEL);
|
||||
}
|
||||
|
||||
|
@@ -0,0 +1,231 @@
|
||||
/*
|
||||
* 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.biometrics2.ui.model.EnrollmentProgress.INITIAL_REMAINING;
|
||||
import static com.android.settings.biometrics2.ui.model.EnrollmentProgress.INITIAL_STEPS;
|
||||
|
||||
import android.app.Application;
|
||||
import android.content.res.Resources;
|
||||
import android.hardware.fingerprint.FingerprintManager.EnrollReason;
|
||||
import android.hardware.fingerprint.FingerprintManager.EnrollmentCallback;
|
||||
import android.os.CancellationSignal;
|
||||
import android.os.SystemClock;
|
||||
import android.os.UserHandle;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.lifecycle.AndroidViewModel;
|
||||
import androidx.lifecycle.LiveData;
|
||||
import androidx.lifecycle.MutableLiveData;
|
||||
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.biometrics.fingerprint.FingerprintUpdater;
|
||||
import com.android.settings.biometrics.fingerprint.MessageDisplayController;
|
||||
import com.android.settings.biometrics2.ui.model.EnrollmentProgress;
|
||||
|
||||
/**
|
||||
* Progress ViewModel handles the state around biometric enrollment. It manages the state of
|
||||
* enrollment throughout the activity lifecycle so the app can continue after an event like
|
||||
* rotation.
|
||||
*/
|
||||
public class FingerprintEnrollProgressViewModel extends AndroidViewModel {
|
||||
|
||||
private static final boolean DEBUG = false;
|
||||
private static final String TAG = "FingerprintEnrollProgressViewModel";
|
||||
|
||||
private final MutableLiveData<EnrollmentProgress> mProgressLiveData = new MutableLiveData<>(
|
||||
new EnrollmentProgress(INITIAL_STEPS, INITIAL_REMAINING));
|
||||
|
||||
private byte[] mToken = null;
|
||||
private int mUserId = UserHandle.myUserId();
|
||||
|
||||
private final FingerprintUpdater mFingerprintUpdater;
|
||||
private final MessageDisplayController mMessageDisplayController;
|
||||
private EnrollmentHelper mEnrollmentHelper;
|
||||
private final EnrollmentCallback mEnrollmentCallback = new EnrollmentCallback() {
|
||||
|
||||
@Override
|
||||
public void onEnrollmentProgress(int remaining) {
|
||||
final int currentSteps = getSteps();
|
||||
final EnrollmentProgress progress = new EnrollmentProgress(
|
||||
currentSteps == INITIAL_STEPS ? remaining : getSteps(), remaining);
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "onEnrollmentProgress(" + remaining + "), steps: " + currentSteps
|
||||
+ ", post progress as " + progress);
|
||||
}
|
||||
mProgressLiveData.postValue(progress);
|
||||
// TODO set enrolling to false when remaining is 0 during implementing b/260957933
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEnrollmentHelp(int helpMsgId, CharSequence helpString) {
|
||||
// TODO add LiveData for help message during implementing b/260957933
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEnrollmentError(int errMsgId, CharSequence errString) {
|
||||
// TODO add LiveData for error message during implementing b/260957933
|
||||
}
|
||||
};
|
||||
|
||||
public FingerprintEnrollProgressViewModel(@NonNull Application application,
|
||||
@NonNull FingerprintUpdater fingerprintUpdater) {
|
||||
super(application);
|
||||
mFingerprintUpdater = fingerprintUpdater;
|
||||
final Resources res = application.getResources();
|
||||
mMessageDisplayController =
|
||||
res.getBoolean(R.bool.enrollment_message_display_controller_flag)
|
||||
? new MessageDisplayController(
|
||||
application.getMainThreadHandler(),
|
||||
mEnrollmentCallback,
|
||||
SystemClock.elapsedRealtimeClock(),
|
||||
res.getInteger(R.integer.enrollment_help_minimum_time_display),
|
||||
res.getInteger(R.integer.enrollment_progress_minimum_time_display),
|
||||
res.getBoolean(R.bool.enrollment_progress_priority_over_help),
|
||||
res.getBoolean(R.bool.enrollment_prioritize_acquire_messages),
|
||||
res.getInteger(R.integer.enrollment_collect_time)) : null;
|
||||
}
|
||||
|
||||
public void setToken(byte[] token) {
|
||||
mToken = token;
|
||||
}
|
||||
|
||||
public void setUserId(int userId) {
|
||||
mUserId = userId;
|
||||
}
|
||||
|
||||
/**
|
||||
* clear progress
|
||||
*/
|
||||
public void clearProgressLiveData() {
|
||||
mProgressLiveData.setValue(new EnrollmentProgress(INITIAL_STEPS, INITIAL_REMAINING));
|
||||
}
|
||||
|
||||
public LiveData<EnrollmentProgress> getProgressLiveData() {
|
||||
return mProgressLiveData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts enrollment and return latest isEnrolling() result
|
||||
*/
|
||||
public boolean startEnrollment(@EnrollReason int reason) {
|
||||
if (mToken == null) {
|
||||
Log.e(TAG, "Null hardware auth token for enroll");
|
||||
return false;
|
||||
}
|
||||
if (isEnrolling()) {
|
||||
Log.w(TAG, "Enrolling has started, shall not start again");
|
||||
return true;
|
||||
}
|
||||
|
||||
mEnrollmentHelper = new EnrollmentHelper(
|
||||
mMessageDisplayController != null
|
||||
? mMessageDisplayController
|
||||
: mEnrollmentCallback);
|
||||
mEnrollmentHelper.startEnrollment(mFingerprintUpdater, mToken, mUserId, reason);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancels enrollment and return latest isEnrolling result
|
||||
*/
|
||||
public boolean cancelEnrollment() {
|
||||
if (!isEnrolling() || mEnrollmentHelper == null) {
|
||||
Log.e(TAG, "Fail to cancel enrollment, enrollmentController exist:"
|
||||
+ (mEnrollmentHelper != null));
|
||||
return false;
|
||||
}
|
||||
|
||||
mEnrollmentHelper.cancelEnrollment();
|
||||
mEnrollmentHelper = null;
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean isEnrolling() {
|
||||
return (mEnrollmentHelper != null);
|
||||
}
|
||||
|
||||
private int getSteps() {
|
||||
return mProgressLiveData.getValue().getSteps();
|
||||
}
|
||||
|
||||
/**
|
||||
* This class is used to stop latest message from onEnrollmentError() after user cancelled
|
||||
* enrollment. This class will not forward message anymore after mCancellationSignal is sent.
|
||||
*/
|
||||
private static class EnrollmentHelper extends EnrollmentCallback {
|
||||
|
||||
@NonNull private final EnrollmentCallback mEnrollmentCallback;
|
||||
@Nullable private CancellationSignal mCancellationSignal = new CancellationSignal();
|
||||
|
||||
EnrollmentHelper(@NonNull EnrollmentCallback enrollmentCallback) {
|
||||
mEnrollmentCallback = enrollmentCallback;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEnrollmentError(int errMsgId, CharSequence errString) {
|
||||
if (mCancellationSignal == null) {
|
||||
return;
|
||||
}
|
||||
mEnrollmentCallback.onEnrollmentError(errMsgId, errString);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEnrollmentHelp(int helpMsgId, CharSequence helpString) {
|
||||
if (mCancellationSignal == null) {
|
||||
return;
|
||||
}
|
||||
mEnrollmentCallback.onEnrollmentHelp(helpMsgId, helpString);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEnrollmentProgress(int remaining) {
|
||||
if (mCancellationSignal == null) {
|
||||
return;
|
||||
}
|
||||
mEnrollmentCallback.onEnrollmentProgress(remaining);
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts enrollment
|
||||
*/
|
||||
public boolean startEnrollment(@NonNull FingerprintUpdater fingerprintUpdater,
|
||||
@NonNull byte[] token, int userId, @EnrollReason int reason) {
|
||||
if (mCancellationSignal == null) {
|
||||
// Not allow enrolling twice as same instance. Allocate a new instance for second
|
||||
// enrollment.
|
||||
return false;
|
||||
}
|
||||
fingerprintUpdater.enroll(token, mCancellationSignal, userId, this, reason);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancels current enrollment
|
||||
*/
|
||||
public void cancelEnrollment() {
|
||||
final CancellationSignal cancellationSignal = mCancellationSignal;
|
||||
mCancellationSignal = null;
|
||||
|
||||
if (cancellationSignal != null) {
|
||||
cancellationSignal.cancel();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -124,8 +124,6 @@ public class FingerprintEnrollmentViewModel extends AndroidViewModel implements
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
private boolean isKeyguardSecure() {
|
||||
return mKeyguardManager != null && mKeyguardManager.isKeyguardSecure();
|
||||
}
|
||||
@@ -182,4 +180,18 @@ public class FingerprintEnrollmentViewModel extends AndroidViewModel implements
|
||||
public void onSaveInstanceState(@NonNull Bundle outState) {
|
||||
outState.putBoolean(SAVED_STATE_IS_WAITING_ACTIVITY_RESULT, mIsWaitingActivityResult.get());
|
||||
}
|
||||
|
||||
/**
|
||||
* 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();
|
||||
}
|
||||
}
|
||||
|
@@ -58,26 +58,30 @@ public class FingerprintEnrollmentActivityTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void lunchWithoutCredential() {
|
||||
public void launchWithoutCredential() {
|
||||
launchFingerprintEnrollActivity(true);
|
||||
Assert.assertNotNull(mDevice.wait(Until.hasObject(
|
||||
By.text("Choose your backup screen lock method")), IDLE_TIMEOUT));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void lunchWithCredential() {
|
||||
public void launchWithCredential() {
|
||||
LockScreenUtil.setLockscreen(LockScreenUtil.LockscreenType.PIN, "1234", true);
|
||||
launchFingerprintEnrollActivity(true);
|
||||
Assert.assertNotNull(mDevice.wait(Until.hasObject(
|
||||
By.text("More")), IDLE_TIMEOUT));
|
||||
for (long i = 0; i < IDLE_TIMEOUT; i += 100L) {
|
||||
if (mDevice.wait(Until.hasObject(By.text("More")), 50L) != null) {
|
||||
break;
|
||||
} else if (mDevice.wait(Until.hasObject(By.text("I agree")), 50L) != null) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//click more btn twice and the introduction should stay in the last page
|
||||
UiObject2 moreBtn = mDevice.findObject(By.text("More"));
|
||||
moreBtn.click();
|
||||
Assert.assertNotNull(mDevice.wait(Until.hasObject(
|
||||
By.text("More")), IDLE_TIMEOUT));
|
||||
moreBtn = mDevice.findObject(By.text("More"));
|
||||
//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.wait(Until.hasObject(By.text("More")), IDLE_TIMEOUT);
|
||||
}
|
||||
|
||||
Assert.assertNotNull(mDevice.wait(Until.hasObject(
|
||||
By.text("I agree")), IDLE_TIMEOUT));
|
||||
@@ -114,5 +118,4 @@ public class FingerprintEnrollmentActivityTest {
|
||||
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
mContext.startActivity(intent);
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -23,16 +23,17 @@ import static android.hardware.fingerprint.FingerprintSensorProperties.TYPE_UDFP
|
||||
import static android.hardware.fingerprint.FingerprintSensorProperties.TYPE_UDFPS_ULTRASONIC;
|
||||
import static android.hardware.fingerprint.FingerprintSensorProperties.TYPE_UNKNOWN;
|
||||
|
||||
import static com.android.settings.biometrics2.util.FingerprintManagerUtil.setupFingerprintEnrolledFingerprints;
|
||||
import static com.android.settings.biometrics2.util.FingerprintManagerUtil.setupFingerprintFirstSensor;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.hardware.biometrics.SensorProperties;
|
||||
import android.hardware.fingerprint.Fingerprint;
|
||||
import android.hardware.fingerprint.FingerprintManager;
|
||||
import android.hardware.fingerprint.FingerprintSensorProperties;
|
||||
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
@@ -48,6 +49,8 @@ import org.mockito.Mock;
|
||||
import org.mockito.junit.MockitoJUnit;
|
||||
import org.mockito.junit.MockitoRule;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class FingerprintRepositoryTest {
|
||||
|
||||
@@ -66,31 +69,49 @@ public class FingerprintRepositoryTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCanAssumeSensorType() {
|
||||
public void testCanAssumeSensorType_forUnknownSensor() {
|
||||
setupFingerprintFirstSensor(mFingerprintManager, TYPE_UNKNOWN, 1);
|
||||
assertThat(mFingerprintRepository.canAssumeUdfps()).isFalse();
|
||||
assertThat(mFingerprintRepository.canAssumeSfps()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCanAssumeSensorType_forRearSensor() {
|
||||
setupFingerprintFirstSensor(mFingerprintManager, TYPE_REAR, 1);
|
||||
assertThat(mFingerprintRepository.canAssumeUdfps()).isFalse();
|
||||
assertThat(mFingerprintRepository.canAssumeSfps()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCanAssumeSensorType_forUdfpsUltrasonicSensor() {
|
||||
setupFingerprintFirstSensor(mFingerprintManager, TYPE_UDFPS_ULTRASONIC, 1);
|
||||
assertThat(mFingerprintRepository.canAssumeUdfps()).isTrue();
|
||||
assertThat(mFingerprintRepository.canAssumeSfps()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCanAssumeSensorType_forUdfpsOpticalSensor() {
|
||||
setupFingerprintFirstSensor(mFingerprintManager, TYPE_UDFPS_OPTICAL, 1);
|
||||
assertThat(mFingerprintRepository.canAssumeUdfps()).isTrue();
|
||||
assertThat(mFingerprintRepository.canAssumeSfps()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCanAssumeSensorType_forPowerButtonSensor() {
|
||||
setupFingerprintFirstSensor(mFingerprintManager, TYPE_POWER_BUTTON, 1);
|
||||
assertThat(mFingerprintRepository.canAssumeUdfps()).isFalse();
|
||||
assertThat(mFingerprintRepository.canAssumeSfps()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCanAssumeSensorType_forHomeButtonSensor() {
|
||||
setupFingerprintFirstSensor(mFingerprintManager, TYPE_HOME_BUTTON, 1);
|
||||
assertThat(mFingerprintRepository.canAssumeUdfps()).isFalse();
|
||||
assertThat(mFingerprintRepository.canAssumeSfps()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetMaxFingerprints() {
|
||||
setupFingerprintFirstSensor(mFingerprintManager, TYPE_UNKNOWN, 44);
|
||||
assertThat(mFingerprintRepository.getMaxFingerprints()).isEqualTo(44);
|
||||
|
||||
setupFingerprintFirstSensor(mFingerprintManager, TYPE_UNKNOWN, 999);
|
||||
assertThat(mFingerprintRepository.getMaxFingerprints()).isEqualTo(999);
|
||||
}
|
||||
@@ -122,4 +143,31 @@ public class FingerprintRepositoryTest {
|
||||
"suw_max_fingerprints_enrollable");
|
||||
when(mockedResources.getInteger(resId)).thenReturn(numOfFp);
|
||||
}
|
||||
|
||||
public static void setupFingerprintFirstSensor(
|
||||
@NonNull FingerprintManager mockedFingerprintManager,
|
||||
@FingerprintSensorProperties.SensorType int sensorType,
|
||||
int maxEnrollmentsPerUser) {
|
||||
|
||||
final ArrayList<FingerprintSensorPropertiesInternal> props = new ArrayList<>();
|
||||
props.add(new FingerprintSensorPropertiesInternal(
|
||||
0 /* sensorId */,
|
||||
SensorProperties.STRENGTH_STRONG,
|
||||
maxEnrollmentsPerUser,
|
||||
new ArrayList<>() /* componentInfo */,
|
||||
sensorType,
|
||||
true /* resetLockoutRequiresHardwareAuthToken */));
|
||||
when(mockedFingerprintManager.getSensorPropertiesInternal()).thenReturn(props);
|
||||
}
|
||||
|
||||
public static void setupFingerprintEnrolledFingerprints(
|
||||
@NonNull FingerprintManager mockedFingerprintManager,
|
||||
int userId,
|
||||
int enrolledFingerprints) {
|
||||
final ArrayList<Fingerprint> ret = new ArrayList<>();
|
||||
for (int i = 0; i < enrolledFingerprints; ++i) {
|
||||
ret.add(new Fingerprint("name", 0, 0, 0L));
|
||||
}
|
||||
when(mockedFingerprintManager.getEnrolledFingerprints(userId)).thenReturn(ret);
|
||||
}
|
||||
}
|
||||
|
@@ -45,6 +45,7 @@ import static com.android.settings.password.ChooseLockSettingsHelper.EXTRA_KEY_G
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static com.google.common.truth.Truth.assertWithMessage;
|
||||
|
||||
import static org.mockito.Mockito.doAnswer;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
@@ -71,6 +72,8 @@ import org.mockito.Mock;
|
||||
import org.mockito.junit.MockitoJUnit;
|
||||
import org.mockito.junit.MockitoRule;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class AutoCredentialViewModelTest {
|
||||
|
||||
@@ -299,13 +302,18 @@ public class AutoCredentialViewModelTest {
|
||||
when(mLockPatternUtils.verifyGatekeeperPasswordHandle(gkPwHandle, newChallenge, userId))
|
||||
.thenReturn(newGoodCredential(gkPwHandle, new byte[] { 1 }));
|
||||
|
||||
final AtomicBoolean hasCalledRemoveGkPwHandle = new AtomicBoolean();
|
||||
doAnswer(invocation -> {
|
||||
hasCalledRemoveGkPwHandle.set(true);
|
||||
return null;
|
||||
}).when(mLockPatternUtils).removeGatekeeperPasswordHandle(gkPwHandle);
|
||||
|
||||
// Run credential check
|
||||
@CredentialAction final int action = mViewModel.checkCredential();
|
||||
|
||||
// Check viewModel behavior
|
||||
assertThat(action).isEqualTo(CREDENTIAL_IS_GENERATING_CHALLENGE);
|
||||
assertThat(mViewModel.getGenerateChallengeFailedLiveData().getValue()).isNull();
|
||||
assertThat(mChallengeGenerator.mCallbackRunCount).isEqualTo(1);
|
||||
|
||||
// Check data inside CredentialModel
|
||||
final Bundle extras = mViewModel.createCredentialIntentExtra();
|
||||
@@ -314,6 +322,8 @@ public class AutoCredentialViewModelTest {
|
||||
assertThat(extras.getByteArray(EXTRA_KEY_CHALLENGE_TOKEN)).isNotNull();
|
||||
assertThat(extras.getLong(EXTRA_KEY_GK_PW_HANDLE)).isEqualTo(INVALID_GK_PW_HANDLE);
|
||||
assertThat(extras.getLong(EXTRA_KEY_CHALLENGE)).isNotEqualTo(INVALID_CHALLENGE);
|
||||
assertThat(mChallengeGenerator.mCallbackRunCount).isEqualTo(1);
|
||||
assertThat(hasCalledRemoveGkPwHandle.get()).isFalse();
|
||||
|
||||
// Check createGeneratingChallengeExtras()
|
||||
final Bundle generatingChallengeExtras = mViewModel.createGeneratingChallengeExtras();
|
||||
@@ -511,6 +521,12 @@ public class AutoCredentialViewModelTest {
|
||||
when(mLockPatternUtils.verifyGatekeeperPasswordHandle(gkPwHandle, newChallenge, userId))
|
||||
.thenReturn(newGoodCredential(gkPwHandle, new byte[] { 1 }));
|
||||
|
||||
final AtomicBoolean hasCalledRemoveGkPwHandle = new AtomicBoolean();
|
||||
doAnswer(invocation -> {
|
||||
hasCalledRemoveGkPwHandle.set(true);
|
||||
return null;
|
||||
}).when(mLockPatternUtils).removeGatekeeperPasswordHandle(gkPwHandle);
|
||||
|
||||
// Run checkNewCredentialFromActivityResult()
|
||||
final Intent intent = new Intent().putExtra(EXTRA_KEY_GK_PW_HANDLE, gkPwHandle);
|
||||
final boolean ret = mViewModel.checkNewCredentialFromActivityResult(true,
|
||||
@@ -524,6 +540,7 @@ public class AutoCredentialViewModelTest {
|
||||
assertThat(extras.getByteArray(EXTRA_KEY_CHALLENGE_TOKEN)).isNotNull();
|
||||
assertThat(extras.getLong(EXTRA_KEY_GK_PW_HANDLE)).isEqualTo(INVALID_GK_PW_HANDLE);
|
||||
assertThat(mChallengeGenerator.mCallbackRunCount).isEqualTo(1);
|
||||
assertThat(hasCalledRemoveGkPwHandle.get()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -541,6 +558,12 @@ public class AutoCredentialViewModelTest {
|
||||
when(mLockPatternUtils.verifyGatekeeperPasswordHandle(gkPwHandle, newChallenge, userId))
|
||||
.thenReturn(newGoodCredential(gkPwHandle, new byte[] { 1 }));
|
||||
|
||||
final AtomicBoolean hasCalledRemoveGkPwHandle = new AtomicBoolean();
|
||||
doAnswer(invocation -> {
|
||||
hasCalledRemoveGkPwHandle.set(true);
|
||||
return null;
|
||||
}).when(mLockPatternUtils).removeGatekeeperPasswordHandle(gkPwHandle);
|
||||
|
||||
// Run checkNewCredentialFromActivityResult()
|
||||
final Intent intent = new Intent().putExtra(EXTRA_KEY_GK_PW_HANDLE, gkPwHandle);
|
||||
final boolean ret = mViewModel.checkNewCredentialFromActivityResult(false,
|
||||
@@ -554,6 +577,7 @@ public class AutoCredentialViewModelTest {
|
||||
assertThat(extras.getByteArray(EXTRA_KEY_CHALLENGE_TOKEN)).isNotNull();
|
||||
assertThat(extras.getLong(EXTRA_KEY_GK_PW_HANDLE)).isEqualTo(INVALID_GK_PW_HANDLE);
|
||||
assertThat(mChallengeGenerator.mCallbackRunCount).isEqualTo(1);
|
||||
assertThat(hasCalledRemoveGkPwHandle.get()).isTrue();
|
||||
}
|
||||
|
||||
public static class TestChallengeGenerator implements ChallengeGenerator {
|
||||
|
@@ -20,6 +20,8 @@ import static android.hardware.fingerprint.FingerprintSensorProperties.TYPE_REAR
|
||||
import static android.hardware.fingerprint.FingerprintSensorProperties.TYPE_UDFPS_OPTICAL;
|
||||
import static android.hardware.fingerprint.FingerprintSensorProperties.TYPE_UDFPS_ULTRASONIC;
|
||||
|
||||
import static com.android.settings.biometrics2.data.repository.FingerprintRepositoryTest.setupFingerprintEnrolledFingerprints;
|
||||
import static com.android.settings.biometrics2.data.repository.FingerprintRepositoryTest.setupFingerprintFirstSensor;
|
||||
import static com.android.settings.biometrics2.data.repository.FingerprintRepositoryTest.setupSuwMaxFingerprintsEnrollable;
|
||||
import static com.android.settings.biometrics2.ui.model.FingerprintEnrollIntroStatus.FINGERPRINT_ENROLLABLE_ERROR_REACH_MAX;
|
||||
import static com.android.settings.biometrics2.ui.model.FingerprintEnrollIntroStatus.FINGERPRINT_ENROLLABLE_OK;
|
||||
@@ -32,8 +34,6 @@ import static com.android.settings.biometrics2.util.EnrollmentRequestUtil.newIsS
|
||||
import static com.android.settings.biometrics2.util.EnrollmentRequestUtil.newIsSuwPortalRequest;
|
||||
import static com.android.settings.biometrics2.util.EnrollmentRequestUtil.newIsSuwRequest;
|
||||
import static com.android.settings.biometrics2.util.EnrollmentRequestUtil.newIsSuwSuggestedActionFlowRequest;
|
||||
import static com.android.settings.biometrics2.util.FingerprintManagerUtil.setupFingerprintEnrolledFingerprints;
|
||||
import static com.android.settings.biometrics2.util.FingerprintManagerUtil.setupFingerprintFirstSensor;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
@@ -134,26 +134,47 @@ public class FingerprintEnrollIntroViewModelTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOnStartToUpdateEnrollableStatus_isNotSuw() {
|
||||
testOnStartToUpdateEnrollableStatus(newAllFalseRequest(mApplication));
|
||||
public void testOnStartToUpdateEnrollableStatusOk_isNotSuw() {
|
||||
testOnStartToUpdateEnrollableStatusOk(newAllFalseRequest(mApplication));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOnStartToUpdateEnrollableStatus_isSuwDeferred() {
|
||||
testOnStartToUpdateEnrollableStatus(newIsSuwDeferredRequest(mApplication));
|
||||
public void testOnStartToUpdateEnrollableStatusReachMax_isNotSuw() {
|
||||
testOnStartToUpdateEnrollableStatusReachMax(newAllFalseRequest(mApplication));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOnStartToUpdateEnrollableStatus_isSuwPortal() {
|
||||
testOnStartToUpdateEnrollableStatus(newIsSuwPortalRequest(mApplication));
|
||||
public void testOnStartToUpdateEnrollableStatusOk_isSuwDeferred() {
|
||||
testOnStartToUpdateEnrollableStatusOk(newIsSuwDeferredRequest(mApplication));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOnStartToUpdateEnrollableStatus_isSuwSuggestedActionFlow() {
|
||||
testOnStartToUpdateEnrollableStatus(newIsSuwSuggestedActionFlowRequest(mApplication));
|
||||
public void testOnStartToUpdateEnrollableStatusReachMax_isSuwDeferred() {
|
||||
testOnStartToUpdateEnrollableStatusReachMax(newIsSuwDeferredRequest(mApplication));
|
||||
}
|
||||
|
||||
private void testOnStartToUpdateEnrollableStatus(@NonNull EnrollmentRequest request) {
|
||||
@Test
|
||||
public void testOnStartToUpdateEnrollableStatusOk_isSuwPortal() {
|
||||
testOnStartToUpdateEnrollableStatusOk(newIsSuwPortalRequest(mApplication));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOnStartToUpdateEnrollableStatusReachMax_isSuwPortal() {
|
||||
testOnStartToUpdateEnrollableStatusReachMax(newIsSuwPortalRequest(mApplication));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOnStartToUpdateEnrollableStatusOk_isSuwSuggestedActionFlow() {
|
||||
testOnStartToUpdateEnrollableStatusOk(newIsSuwSuggestedActionFlowRequest(mApplication));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOnStartToUpdateEnrollableStatusReachMax_isSuwSuggestedActionFlow() {
|
||||
testOnStartToUpdateEnrollableStatusReachMax(
|
||||
newIsSuwSuggestedActionFlowRequest(mApplication));
|
||||
}
|
||||
|
||||
private void testOnStartToUpdateEnrollableStatusOk(@NonNull EnrollmentRequest request) {
|
||||
final int userId = 45;
|
||||
mViewModel.setUserId(userId);
|
||||
mViewModel.setEnrollmentRequest(request);
|
||||
@@ -163,19 +184,28 @@ public class FingerprintEnrollIntroViewModelTest {
|
||||
mViewModel.onStart(mLifecycleOwner);
|
||||
FingerprintEnrollIntroStatus status = mViewModel.getPageStatusLiveData().getValue();
|
||||
assertThat(status.getEnrollableStatus()).isEqualTo(FINGERPRINT_ENROLLABLE_OK);
|
||||
}
|
||||
|
||||
private void testOnStartToUpdateEnrollableStatusReachMax(@NonNull EnrollmentRequest request) {
|
||||
final int userId = 45;
|
||||
mViewModel.setUserId(userId);
|
||||
mViewModel.setEnrollmentRequest(request);
|
||||
|
||||
setupFingerprintEnrolledFingerprints(mFingerprintManager, userId, 5);
|
||||
setupFingerprintFirstSensor(mFingerprintManager, TYPE_UDFPS_OPTICAL, 5);
|
||||
mViewModel.onStart(mLifecycleOwner);
|
||||
status = mViewModel.getPageStatusLiveData().getValue();
|
||||
FingerprintEnrollIntroStatus status = mViewModel.getPageStatusLiveData().getValue();
|
||||
assertThat(status.getEnrollableStatus()).isEqualTo(FINGERPRINT_ENROLLABLE_ERROR_REACH_MAX);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void textCanAssumeUdfps() {
|
||||
public void textCanAssumeUdfps_forUdfpsUltrasonicSensor() {
|
||||
setupFingerprintFirstSensor(mFingerprintManager, TYPE_UDFPS_ULTRASONIC, 1);
|
||||
assertThat(mViewModel.canAssumeUdfps()).isEqualTo(true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void textCanAssumeUdfps_forRearSensor() {
|
||||
setupFingerprintFirstSensor(mFingerprintManager, TYPE_REAR, 1);
|
||||
assertThat(mViewModel.canAssumeUdfps()).isEqualTo(false);
|
||||
}
|
||||
@@ -238,7 +268,7 @@ public class FingerprintEnrollIntroViewModelTest {
|
||||
assertThat(status.getEnrollableStatus()).isEqualTo(FINGERPRINT_ENROLLABLE_OK);
|
||||
|
||||
// Perform click on `next`
|
||||
mViewModel.onNextButtonClick(null);
|
||||
mViewModel.onNextButtonClick();
|
||||
|
||||
assertThat(mViewModel.getActionLiveData().getValue())
|
||||
.isEqualTo(FINGERPRINT_ENROLL_INTRO_ACTION_CONTINUE_ENROLL);
|
||||
@@ -258,7 +288,7 @@ public class FingerprintEnrollIntroViewModelTest {
|
||||
assertThat(status.getEnrollableStatus()).isEqualTo(FINGERPRINT_ENROLLABLE_ERROR_REACH_MAX);
|
||||
|
||||
// Perform click on `next`
|
||||
mViewModel.onNextButtonClick(null);
|
||||
mViewModel.onNextButtonClick();
|
||||
|
||||
assertThat(mViewModel.getActionLiveData().getValue())
|
||||
.isEqualTo(FINGERPRINT_ENROLL_INTRO_ACTION_DONE_AND_FINISH);
|
||||
@@ -266,7 +296,7 @@ public class FingerprintEnrollIntroViewModelTest {
|
||||
|
||||
@Test
|
||||
public void testOnSkipOrCancelButtonClick() {
|
||||
mViewModel.onSkipOrCancelButtonClick(null);
|
||||
mViewModel.onSkipOrCancelButtonClick();
|
||||
|
||||
assertThat(mViewModel.getActionLiveData().getValue())
|
||||
.isEqualTo(FINGERPRINT_ENROLL_INTRO_ACTION_SKIP_OR_CANCEL);
|
||||
|
@@ -20,10 +20,10 @@ import static com.android.settings.biometrics.BiometricEnrollBase.RESULT_FINISHE
|
||||
import static com.android.settings.biometrics.BiometricEnrollBase.RESULT_SKIP;
|
||||
import static com.android.settings.biometrics.BiometricEnrollBase.RESULT_TIMEOUT;
|
||||
import static com.android.settings.biometrics.fingerprint.SetupFingerprintEnrollIntroduction.EXTRA_FINGERPRINT_ENROLLED_COUNT;
|
||||
import static com.android.settings.biometrics2.data.repository.FingerprintRepositoryTest.setupFingerprintEnrolledFingerprints;
|
||||
import static com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollmentViewModel.SAVED_STATE_IS_WAITING_ACTIVITY_RESULT;
|
||||
import static com.android.settings.biometrics2.util.EnrollmentRequestUtil.newAllFalseRequest;
|
||||
import static com.android.settings.biometrics2.util.EnrollmentRequestUtil.newIsSuwRequest;
|
||||
import static com.android.settings.biometrics2.util.FingerprintManagerUtil.setupFingerprintEnrolledFingerprints;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
|
@@ -1,58 +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.util;
|
||||
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.hardware.biometrics.SensorProperties;
|
||||
import android.hardware.fingerprint.Fingerprint;
|
||||
import android.hardware.fingerprint.FingerprintManager;
|
||||
import android.hardware.fingerprint.FingerprintSensorProperties;
|
||||
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class FingerprintManagerUtil {
|
||||
|
||||
public static void setupFingerprintFirstSensor(
|
||||
@NonNull FingerprintManager mockedFingerprintManager,
|
||||
@FingerprintSensorProperties.SensorType int sensorType,
|
||||
int maxEnrollmentsPerUser) {
|
||||
final ArrayList<FingerprintSensorPropertiesInternal> props = new ArrayList<>();
|
||||
props.add(new FingerprintSensorPropertiesInternal(
|
||||
0 /* sensorId */,
|
||||
SensorProperties.STRENGTH_STRONG,
|
||||
maxEnrollmentsPerUser,
|
||||
new ArrayList<>() /* componentInfo */,
|
||||
sensorType,
|
||||
true /* resetLockoutRequiresHardwareAuthToken */));
|
||||
when(mockedFingerprintManager.getSensorPropertiesInternal()).thenReturn(props);
|
||||
}
|
||||
|
||||
public static void setupFingerprintEnrolledFingerprints(
|
||||
@NonNull FingerprintManager mockedFingerprintManager,
|
||||
int userId,
|
||||
int enrolledFingerprints) {
|
||||
final ArrayList<Fingerprint> ret = new ArrayList<>();
|
||||
for (int i = 0; i < enrolledFingerprints; ++i) {
|
||||
ret.add(new Fingerprint("name", 0, 0, 0L));
|
||||
}
|
||||
when(mockedFingerprintManager.getEnrolledFingerprints(userId)).thenReturn(ret);
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user