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();
|
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
|
* Get max possible number of fingerprints for a user
|
||||||
*/
|
*/
|
||||||
@@ -78,6 +86,7 @@ public class FingerprintRepository {
|
|||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
private FingerprintSensorPropertiesInternal getFirstFingerprintSensorPropertiesInternal() {
|
private FingerprintSensorPropertiesInternal getFirstFingerprintSensorPropertiesInternal() {
|
||||||
|
// TODO(b/264827022) use API addAuthenticatorsRegisteredCallback
|
||||||
final List<FingerprintSensorPropertiesInternal> props =
|
final List<FingerprintSensorPropertiesInternal> props =
|
||||||
mFingerprintManager.getSensorPropertiesInternal();
|
mFingerprintManager.getSensorPropertiesInternal();
|
||||||
return props.size() > 0 ? props.get(0) : null;
|
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 {
|
public class BiometricsRepositoryProviderImpl implements BiometricsRepositoryProvider {
|
||||||
|
|
||||||
|
private static volatile FingerprintRepository sFingerprintRepository;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get FingerprintRepository
|
* Get FingerprintRepository
|
||||||
*/
|
*/
|
||||||
@@ -41,6 +43,13 @@ public class BiometricsRepositoryProviderImpl implements BiometricsRepositoryPro
|
|||||||
if (fingerprintManager == null) {
|
if (fingerprintManager == null) {
|
||||||
return 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 androidx.lifecycle.viewmodel.CreationExtras;
|
||||||
|
|
||||||
import com.android.internal.widget.LockPatternUtils;
|
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.data.repository.FingerprintRepository;
|
||||||
import com.android.settings.biometrics2.ui.viewmodel.AutoCredentialViewModel;
|
import com.android.settings.biometrics2.ui.viewmodel.AutoCredentialViewModel;
|
||||||
import com.android.settings.biometrics2.ui.viewmodel.AutoCredentialViewModel.ChallengeGenerator;
|
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.FingerprintEnrollIntroViewModel;
|
||||||
|
import com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollProgressViewModel;
|
||||||
import com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollmentViewModel;
|
import com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollmentViewModel;
|
||||||
import com.android.settings.overlay.FeatureFactory;
|
import com.android.settings.overlay.FeatureFactory;
|
||||||
|
import com.android.systemui.unfold.compat.ScreenSizeFoldProvider;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* View model factory for biometric enrollment fragment
|
* View model factory for biometric enrollment fragment
|
||||||
@@ -42,7 +48,7 @@ public class BiometricsViewModelFactory implements ViewModelProvider.Factory {
|
|||||||
private static final String TAG = "BiometricsViewModelFact";
|
private static final String TAG = "BiometricsViewModelFact";
|
||||||
|
|
||||||
public static final CreationExtras.Key<ChallengeGenerator> CHALLENGE_GENERATOR =
|
public static final CreationExtras.Key<ChallengeGenerator> CHALLENGE_GENERATOR =
|
||||||
new CreationExtras.Key<ChallengeGenerator>() {};
|
new CreationExtras.Key<>() {};
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
@Override
|
@Override
|
||||||
@@ -59,7 +65,22 @@ public class BiometricsViewModelFactory implements ViewModelProvider.Factory {
|
|||||||
final BiometricsRepositoryProvider provider = FeatureFactory.getFactory(application)
|
final BiometricsRepositoryProvider provider = FeatureFactory.getFactory(application)
|
||||||
.getBiometricsRepositoryProvider();
|
.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);
|
final FingerprintRepository repository = provider.getFingerprintRepository(application);
|
||||||
if (repository != null) {
|
if (repository != null) {
|
||||||
return (T) new FingerprintEnrollIntroViewModel(application, repository);
|
return (T) new FingerprintEnrollIntroViewModel(application, repository);
|
||||||
@@ -70,14 +91,9 @@ public class BiometricsViewModelFactory implements ViewModelProvider.Factory {
|
|||||||
return (T) new FingerprintEnrollmentViewModel(application, repository,
|
return (T) new FingerprintEnrollmentViewModel(application, repository,
|
||||||
application.getSystemService(KeyguardManager.class));
|
application.getSystemService(KeyguardManager.class));
|
||||||
}
|
}
|
||||||
} else if (modelClass.isAssignableFrom(AutoCredentialViewModel.class)) {
|
} else if (modelClass.isAssignableFrom(FingerprintEnrollProgressViewModel.class)) {
|
||||||
final LockPatternUtils lockPatternUtils =
|
return (T) new FingerprintEnrollProgressViewModel(application,
|
||||||
featureFactory.getSecurityFeatureProvider().getLockPatternUtils(application);
|
new FingerprintUpdater(application));
|
||||||
final ChallengeGenerator challengeGenerator = extras.get(CHALLENGE_GENERATOR);
|
|
||||||
if (challengeGenerator != null) {
|
|
||||||
return (T) new AutoCredentialViewModel(application, lockPatternUtils,
|
|
||||||
challengeGenerator);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return create(modelClass);
|
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 static com.google.android.setupdesign.util.DynamicColorPalette.ColorType.ACCENT;
|
||||||
|
|
||||||
import android.app.Activity;
|
|
||||||
import android.app.admin.DevicePolicyManager;
|
import android.app.admin.DevicePolicyManager;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.graphics.PorterDuff;
|
import android.graphics.PorterDuff;
|
||||||
import android.graphics.PorterDuffColorFilter;
|
import android.graphics.PorterDuffColorFilter;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.text.Html;
|
import android.text.Html;
|
||||||
import android.text.TextUtils;
|
|
||||||
import android.text.method.LinkMovementMethod;
|
import android.text.method.LinkMovementMethod;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
import android.view.View.OnClickListener;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.widget.ImageView;
|
import android.widget.ImageView;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
@@ -69,6 +68,9 @@ public class FingerprintEnrollIntroFragment extends Fragment {
|
|||||||
private View mView = null;
|
private View mView = null;
|
||||||
private FooterButton mPrimaryFooterButton = null;
|
private FooterButton mPrimaryFooterButton = null;
|
||||||
private FooterButton mSecondaryFooterButton = null;
|
private FooterButton mSecondaryFooterButton = null;
|
||||||
|
private final OnClickListener mOnNextClickListener = (v) -> mViewModel.onNextButtonClick();
|
||||||
|
private final OnClickListener mOnSkipOrCancelClickListener =
|
||||||
|
(v) -> mViewModel.onSkipOrCancelButtonClick();
|
||||||
private ImageView mIconShield = null;
|
private ImageView mIconShield = null;
|
||||||
private TextView mFooterMessage6 = null;
|
private TextView mFooterMessage6 = null;
|
||||||
@Nullable private PorterDuffColorFilter mIconColorFilter;
|
@Nullable private PorterDuffColorFilter mIconColorFilter;
|
||||||
@@ -150,8 +152,8 @@ public class FingerprintEnrollIntroFragment extends Fragment {
|
|||||||
|
|
||||||
final Context context = view.getContext();
|
final Context context = view.getContext();
|
||||||
|
|
||||||
mPrimaryFooterButton.setOnClickListener(mViewModel::onNextButtonClick);
|
mPrimaryFooterButton.setOnClickListener(mOnNextClickListener);
|
||||||
mSecondaryFooterButton.setOnClickListener(mViewModel::onSkipOrCancelButtonClick);
|
mSecondaryFooterButton.setOnClickListener(mOnSkipOrCancelClickListener);
|
||||||
|
|
||||||
if (mViewModel.canAssumeUdfps()) {
|
if (mViewModel.canAssumeUdfps()) {
|
||||||
mFooterMessage6.setVisibility(View.VISIBLE);
|
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_cancel
|
||||||
: R.string.security_settings_fingerprint_enroll_introduction_no_thanks);
|
: R.string.security_settings_fingerprint_enroll_introduction_no_thanks);
|
||||||
|
|
||||||
|
final GlifLayoutHelper glifLayoutHelper = new GlifLayoutHelper(getActivity(), getLayout());
|
||||||
if (mViewModel.isBiometricUnlockDisabledByAdmin()
|
if (mViewModel.isBiometricUnlockDisabledByAdmin()
|
||||||
&& !mViewModel.isParentalConsentRequired()) {
|
&& !mViewModel.isParentalConsentRequired()) {
|
||||||
setHeaderText(
|
glifLayoutHelper.setHeaderText(
|
||||||
getActivity(),
|
|
||||||
R.string.security_settings_fingerprint_enroll_introduction_title_unlock_disabled
|
R.string.security_settings_fingerprint_enroll_introduction_title_unlock_disabled
|
||||||
);
|
);
|
||||||
getLayout().setDescriptionText(getDescriptionDisabledByAdmin(context));
|
glifLayoutHelper.setDescriptionText(getDescriptionDisabledByAdmin(context));
|
||||||
} else {
|
} else {
|
||||||
setHeaderText(getActivity(),
|
glifLayoutHelper.setHeaderText(
|
||||||
R.string.security_settings_fingerprint_enroll_introduction_title);
|
R.string.security_settings_fingerprint_enroll_introduction_title);
|
||||||
}
|
}
|
||||||
observePageStatusLiveDataIfNeed();
|
observePageStatusLiveDataIfNeed();
|
||||||
@@ -192,7 +194,7 @@ public class FingerprintEnrollIntroFragment extends Fragment {
|
|||||||
final RequireScrollMixin requireScrollMixin = getLayout()
|
final RequireScrollMixin requireScrollMixin = getLayout()
|
||||||
.getMixin(RequireScrollMixin.class);
|
.getMixin(RequireScrollMixin.class);
|
||||||
requireScrollMixin.requireScrollWithButton(getActivity(), mPrimaryFooterButton,
|
requireScrollMixin.requireScrollWithButton(getActivity(), mPrimaryFooterButton,
|
||||||
getMoreButtonTextRes(), mViewModel::onNextButtonClick);
|
getMoreButtonTextRes(), mOnNextClickListener);
|
||||||
|
|
||||||
// Always set true to setHasScrolledToBottom() before registering listener through
|
// Always set true to setHasScrolledToBottom() before registering listener through
|
||||||
// setOnRequireScrollStateChangedListener(), because listener will not be called if first
|
// 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) {
|
void updateFooterButtons(@NonNull FingerprintEnrollIntroStatus status) {
|
||||||
@StringRes final int scrollToBottomPrimaryResId =
|
@StringRes final int scrollToBottomPrimaryResId =
|
||||||
status.getEnrollableStatus() == FINGERPRINT_ENROLLABLE_OK
|
status.getEnrollableStatus() == FINGERPRINT_ENROLLABLE_OK
|
||||||
|
@@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
package com.android.settings.biometrics2.ui.view;
|
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 androidx.lifecycle.ViewModelProvider.AndroidViewModelFactory.APPLICATION_KEY;
|
||||||
|
|
||||||
import static com.android.settings.biometrics2.factory.BiometricsViewModelFactory.CHALLENGE_GENERATOR;
|
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_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_IS_GENERATING_CHALLENGE;
|
||||||
import static com.android.settings.biometrics2.ui.viewmodel.AutoCredentialViewModel.CREDENTIAL_VALID;
|
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_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_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.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.app.Application;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.res.ColorStateList;
|
import android.content.res.ColorStateList;
|
||||||
|
import android.content.res.Resources;
|
||||||
import android.graphics.Color;
|
import android.graphics.Color;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
import androidx.activity.result.ActivityResult;
|
import androidx.activity.result.ActivityResult;
|
||||||
|
import androidx.activity.result.ActivityResultCallback;
|
||||||
import androidx.activity.result.ActivityResultLauncher;
|
import androidx.activity.result.ActivityResultLauncher;
|
||||||
import androidx.activity.result.contract.ActivityResultContracts;
|
|
||||||
import androidx.annotation.ColorInt;
|
import androidx.annotation.ColorInt;
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.fragment.app.Fragment;
|
||||||
import androidx.fragment.app.FragmentActivity;
|
import androidx.fragment.app.FragmentActivity;
|
||||||
|
import androidx.fragment.app.FragmentManager;
|
||||||
|
import androidx.lifecycle.Observer;
|
||||||
import androidx.lifecycle.ViewModelProvider;
|
import androidx.lifecycle.ViewModelProvider;
|
||||||
import androidx.lifecycle.viewmodel.CreationExtras;
|
import androidx.lifecycle.viewmodel.CreationExtras;
|
||||||
import androidx.lifecycle.viewmodel.MutableCreationExtras;
|
import androidx.lifecycle.viewmodel.MutableCreationExtras;
|
||||||
@@ -48,14 +59,16 @@ import androidx.lifecycle.viewmodel.MutableCreationExtras;
|
|||||||
import com.android.settings.R;
|
import com.android.settings.R;
|
||||||
import com.android.settings.Utils;
|
import com.android.settings.Utils;
|
||||||
import com.android.settings.biometrics.BiometricEnrollBase;
|
import com.android.settings.biometrics.BiometricEnrollBase;
|
||||||
import com.android.settings.biometrics.fingerprint.FingerprintEnrollFindSensor;
|
import com.android.settings.biometrics.fingerprint.FingerprintEnrollEnrolling;
|
||||||
import com.android.settings.biometrics.fingerprint.SetupFingerprintEnrollFindSensor;
|
import com.android.settings.biometrics.fingerprint.SetupFingerprintEnrollEnrolling;
|
||||||
import com.android.settings.biometrics2.data.repository.FingerprintRepository;
|
import com.android.settings.biometrics2.data.repository.FingerprintRepository;
|
||||||
import com.android.settings.biometrics2.factory.BiometricsViewModelFactory;
|
import com.android.settings.biometrics2.factory.BiometricsViewModelFactory;
|
||||||
import com.android.settings.biometrics2.ui.model.EnrollmentRequest;
|
import com.android.settings.biometrics2.ui.model.EnrollmentRequest;
|
||||||
import com.android.settings.biometrics2.ui.viewmodel.AutoCredentialViewModel;
|
import com.android.settings.biometrics2.ui.viewmodel.AutoCredentialViewModel;
|
||||||
import com.android.settings.biometrics2.ui.viewmodel.AutoCredentialViewModel.FingerprintChallengeGenerator;
|
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.FingerprintEnrollIntroViewModel;
|
||||||
|
import com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollProgressViewModel;
|
||||||
import com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollmentViewModel;
|
import com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollmentViewModel;
|
||||||
import com.android.settings.overlay.FeatureFactory;
|
import com.android.settings.overlay.FeatureFactory;
|
||||||
|
|
||||||
@@ -66,37 +79,57 @@ import com.google.android.setupdesign.util.ThemeHelper;
|
|||||||
*/
|
*/
|
||||||
public class FingerprintEnrollmentActivity extends FragmentActivity {
|
public class FingerprintEnrollmentActivity extends FragmentActivity {
|
||||||
|
|
||||||
|
private static final boolean DEBUG = false;
|
||||||
private static final String TAG = "FingerprintEnrollmentActivity";
|
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;
|
protected static final int LAUNCH_CONFIRM_LOCK_ACTIVITY = 1;
|
||||||
|
|
||||||
|
private ViewModelProvider mViewModelProvider;
|
||||||
private FingerprintEnrollmentViewModel mViewModel;
|
private FingerprintEnrollmentViewModel mViewModel;
|
||||||
private AutoCredentialViewModel mAutoCredentialViewModel;
|
private AutoCredentialViewModel mAutoCredentialViewModel;
|
||||||
private ActivityResultLauncher<Intent> mNextActivityLauncher;
|
private final Observer<Integer> mIntroActionObserver = action -> {
|
||||||
private ActivityResultLauncher<Intent> mChooseLockLauncher;
|
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
|
@Override
|
||||||
protected void onCreate(@Nullable Bundle savedInstanceState) {
|
protected void onCreate(@Nullable Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
mNextActivityLauncher = registerForActivityResult(
|
mViewModelProvider = new ViewModelProvider(this);
|
||||||
new ActivityResultContracts.StartActivityForResult(),
|
|
||||||
(it) -> mViewModel.onContinueEnrollActivityResult(
|
|
||||||
it,
|
|
||||||
mAutoCredentialViewModel.getUserId())
|
|
||||||
);
|
|
||||||
mChooseLockLauncher = registerForActivityResult(
|
|
||||||
new ActivityResultContracts.StartActivityForResult(),
|
|
||||||
(it) -> onChooseOrConfirmLockResult(true, it)
|
|
||||||
);
|
|
||||||
|
|
||||||
ViewModelProvider viewModelProvider = new ViewModelProvider(this);
|
mViewModel = mViewModelProvider.get(FingerprintEnrollmentViewModel.class);
|
||||||
|
|
||||||
mViewModel = viewModelProvider.get(FingerprintEnrollmentViewModel.class);
|
|
||||||
mViewModel.setRequest(new EnrollmentRequest(getIntent(), getApplicationContext()));
|
mViewModel.setRequest(new EnrollmentRequest(getIntent(), getApplicationContext()));
|
||||||
mViewModel.setSavedInstanceState(savedInstanceState);
|
mViewModel.setSavedInstanceState(savedInstanceState);
|
||||||
|
|
||||||
mAutoCredentialViewModel = viewModelProvider.get(AutoCredentialViewModel.class);
|
mAutoCredentialViewModel = mViewModelProvider.get(AutoCredentialViewModel.class);
|
||||||
mAutoCredentialViewModel.setCredentialModel(savedInstanceState, getIntent());
|
mAutoCredentialViewModel.setCredentialModel(savedInstanceState, getIntent());
|
||||||
|
|
||||||
// Theme
|
// Theme
|
||||||
@@ -106,20 +139,37 @@ public class FingerprintEnrollmentActivity extends FragmentActivity {
|
|||||||
|
|
||||||
// fragment
|
// fragment
|
||||||
setContentView(R.layout.biometric_enrollment_container);
|
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) {
|
if (savedInstanceState == null) {
|
||||||
checkCredential();
|
checkCredential();
|
||||||
|
startIntroFragment();
|
||||||
final String tag = "FingerprintEnrollIntroFragment";
|
} else {
|
||||||
getSupportFragmentManager().beginTransaction()
|
final FragmentManager manager = getSupportFragmentManager();
|
||||||
.setReorderingAllowed(true)
|
String[] tags = new String[] {
|
||||||
.add(R.id.fragment_container_view, FingerprintEnrollIntroFragment.class, null,
|
FIND_UDFPS_TAG,
|
||||||
tag)
|
FIND_SFPS_TAG,
|
||||||
.commit();
|
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
|
// observe LiveData
|
||||||
@@ -128,11 +178,80 @@ public class FingerprintEnrollmentActivity extends FragmentActivity {
|
|||||||
|
|
||||||
mAutoCredentialViewModel.getGenerateChallengeFailedLiveData().observe(this,
|
mAutoCredentialViewModel.getGenerateChallengeFailedLiveData().observe(this,
|
||||||
this::onGenerateChallengeFailed);
|
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
|
// 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.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) {
|
private void onGenerateChallengeFailed(@NonNull Boolean ignoredBoolean) {
|
||||||
@@ -217,10 +336,7 @@ public class FingerprintEnrollmentActivity extends FragmentActivity {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void observeIntroAction(@Nullable Integer action) {
|
private void onIntroAction(@FingerprintEnrollIntroAction int action) {
|
||||||
if (action == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
switch (action) {
|
switch (action) {
|
||||||
case FINGERPRINT_ENROLL_INTRO_ACTION_DONE_AND_FINISH: {
|
case FINGERPRINT_ENROLL_INTRO_ACTION_DONE_AND_FINISH: {
|
||||||
onSetActivityResult(
|
onSetActivityResult(
|
||||||
@@ -233,13 +349,30 @@ public class FingerprintEnrollmentActivity extends FragmentActivity {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
case FINGERPRINT_ENROLL_INTRO_ACTION_CONTINUE_ENROLL: {
|
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();
|
final boolean isSuw = mViewModel.getRequest().isSuw();
|
||||||
if (!mViewModel.isWaitingActivityResult().compareAndSet(false, true)) {
|
if (!mViewModel.isWaitingActivityResult().compareAndSet(false, true)) {
|
||||||
Log.w(TAG, "startNext, isSuw:" + isSuw + ", fail to set isWaiting flag");
|
Log.w(TAG, "startNext, isSuw:" + isSuw + ", fail to set isWaiting flag");
|
||||||
}
|
}
|
||||||
final Intent intent = new Intent(this, isSuw
|
Intent intent = new Intent(this, isSuw
|
||||||
? SetupFingerprintEnrollFindSensor.class
|
? SetupFingerprintEnrollEnrolling.class
|
||||||
: FingerprintEnrollFindSensor.class);
|
: FingerprintEnrollEnrolling.class);
|
||||||
intent.putExtras(mAutoCredentialViewModel.createCredentialIntentExtra());
|
intent.putExtras(mAutoCredentialViewModel.createCredentialIntentExtra());
|
||||||
intent.putExtras(mViewModel.getNextActivityBaseIntentExtras());
|
intent.putExtras(mViewModel.getNextActivityBaseIntentExtras());
|
||||||
mNextActivityLauncher.launch(intent);
|
mNextActivityLauncher.launch(intent);
|
||||||
@@ -253,6 +386,12 @@ public class FingerprintEnrollmentActivity extends FragmentActivity {
|
|||||||
mViewModel.checkFinishActivityDuringOnPause(isFinishing(), isChangingConfigurations());
|
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
|
@Override
|
||||||
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
|
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
|
||||||
if (requestCode == LAUNCH_CONFIRM_LOCK_ACTIVITY) {
|
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()) {
|
if (isUnspecifiedPassword()) {
|
||||||
return CREDENTIAL_FAIL_NEED_TO_CHOOSE_LOCK;
|
return CREDENTIAL_FAIL_NEED_TO_CHOOSE_LOCK;
|
||||||
} else if (mCredentialModel.isValidGkPwHandle()) {
|
} 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;
|
mIsGeneratingChallengeDuringCheckingCredential = true;
|
||||||
return CREDENTIAL_IS_GENERATING_CHALLENGE;
|
return CREDENTIAL_IS_GENERATING_CHALLENGE;
|
||||||
} else {
|
} 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) -> {
|
mChallengeGenerator.setCallback((sensorId, userId, challenge) -> {
|
||||||
try {
|
try {
|
||||||
final byte[] newToken = requestGatekeeperHat(gkPwHandle, challenge, userId);
|
final byte[] newToken = requestGatekeeperHat(gkPwHandle, challenge, userId);
|
||||||
@@ -274,11 +278,13 @@ public class AutoCredentialViewModel extends AndroidViewModel {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
mLockPatternUtils.removeGatekeeperPasswordHandle(gkPwHandle);
|
if (revokeGkPwHandle) {
|
||||||
mCredentialModel.clearGkPwHandle();
|
mLockPatternUtils.removeGatekeeperPasswordHandle(gkPwHandle);
|
||||||
|
}
|
||||||
|
|
||||||
if (DEBUG) {
|
if (DEBUG) {
|
||||||
Log.d(TAG, "generateChallenge " + mCredentialModel);
|
Log.d(TAG, "generateChallenge(), model:" + mCredentialModel
|
||||||
|
+ ", revokeGkPwHandle:" + revokeGkPwHandle);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check credential again
|
// Check credential again
|
||||||
@@ -314,7 +320,9 @@ public class AutoCredentialViewModel extends AndroidViewModel {
|
|||||||
if (data != null) {
|
if (data != null) {
|
||||||
final long gkPwHandle = result.getData().getLongExtra(
|
final long gkPwHandle = result.getData().getLongExtra(
|
||||||
EXTRA_KEY_GK_PW_HANDLE, INVALID_GK_PW_HANDLE);
|
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;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -328,6 +336,14 @@ public class AutoCredentialViewModel extends AndroidViewModel {
|
|||||||
return mCredentialModel.getUserId();
|
return mCredentialModel.getUserId();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get userId for this credential
|
||||||
|
*/
|
||||||
|
@Nullable
|
||||||
|
public byte[] getToken() {
|
||||||
|
return mCredentialModel.getToken();
|
||||||
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
private byte[] requestGatekeeperHat(long gkPwHandle, long challenge, int userId)
|
private byte[] requestGatekeeperHat(long gkPwHandle, long challenge, int userId)
|
||||||
throws IllegalStateException {
|
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.app.Application;
|
||||||
import android.os.UserHandle;
|
import android.os.UserHandle;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.view.View;
|
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.lifecycle.AndroidViewModel;
|
import androidx.lifecycle.AndroidViewModel;
|
||||||
@@ -189,7 +188,7 @@ public class FingerprintEnrollIntroViewModel extends AndroidViewModel
|
|||||||
/**
|
/**
|
||||||
* User clicks next button
|
* User clicks next button
|
||||||
*/
|
*/
|
||||||
public void onNextButtonClick(View ignoredView) {
|
public void onNextButtonClick() {
|
||||||
final Integer status = mEnrollableStatusLiveData.getValue();
|
final Integer status = mEnrollableStatusLiveData.getValue();
|
||||||
switch (status != null ? status : ENROLLABLE_STATUS_DEFAULT) {
|
switch (status != null ? status : ENROLLABLE_STATUS_DEFAULT) {
|
||||||
case FINGERPRINT_ENROLLABLE_ERROR_REACH_MAX:
|
case FINGERPRINT_ENROLLABLE_ERROR_REACH_MAX:
|
||||||
@@ -206,7 +205,7 @@ public class FingerprintEnrollIntroViewModel extends AndroidViewModel
|
|||||||
/**
|
/**
|
||||||
* User clicks skip/cancel button
|
* User clicks skip/cancel button
|
||||||
*/
|
*/
|
||||||
public void onSkipOrCancelButtonClick(View ignoredView) {
|
public void onSkipOrCancelButtonClick() {
|
||||||
mActionLiveData.postValue(FINGERPRINT_ENROLL_INTRO_ACTION_SKIP_OR_CANCEL);
|
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() {
|
private boolean isKeyguardSecure() {
|
||||||
return mKeyguardManager != null && mKeyguardManager.isKeyguardSecure();
|
return mKeyguardManager != null && mKeyguardManager.isKeyguardSecure();
|
||||||
}
|
}
|
||||||
@@ -182,4 +180,18 @@ public class FingerprintEnrollmentViewModel extends AndroidViewModel implements
|
|||||||
public void onSaveInstanceState(@NonNull Bundle outState) {
|
public void onSaveInstanceState(@NonNull Bundle outState) {
|
||||||
outState.putBoolean(SAVED_STATE_IS_WAITING_ACTIVITY_RESULT, mIsWaitingActivityResult.get());
|
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
|
@Test
|
||||||
public void lunchWithoutCredential() {
|
public void launchWithoutCredential() {
|
||||||
launchFingerprintEnrollActivity(true);
|
launchFingerprintEnrollActivity(true);
|
||||||
Assert.assertNotNull(mDevice.wait(Until.hasObject(
|
Assert.assertNotNull(mDevice.wait(Until.hasObject(
|
||||||
By.text("Choose your backup screen lock method")), IDLE_TIMEOUT));
|
By.text("Choose your backup screen lock method")), IDLE_TIMEOUT));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void lunchWithCredential() {
|
public void launchWithCredential() {
|
||||||
LockScreenUtil.setLockscreen(LockScreenUtil.LockscreenType.PIN, "1234", true);
|
LockScreenUtil.setLockscreen(LockScreenUtil.LockscreenType.PIN, "1234", true);
|
||||||
launchFingerprintEnrollActivity(true);
|
launchFingerprintEnrollActivity(true);
|
||||||
Assert.assertNotNull(mDevice.wait(Until.hasObject(
|
for (long i = 0; i < IDLE_TIMEOUT; i += 100L) {
|
||||||
By.text("More")), IDLE_TIMEOUT));
|
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
|
//click more btn at most twice and the introduction should stay in the last page
|
||||||
UiObject2 moreBtn = mDevice.findObject(By.text("More"));
|
UiObject2 moreBtn;
|
||||||
moreBtn.click();
|
for (int i = 0; i < 2 && (moreBtn = mDevice.findObject(By.text("More"))) != null; ++i) {
|
||||||
Assert.assertNotNull(mDevice.wait(Until.hasObject(
|
moreBtn.click();
|
||||||
By.text("More")), IDLE_TIMEOUT));
|
mDevice.wait(Until.hasObject(By.text("More")), IDLE_TIMEOUT);
|
||||||
moreBtn = mDevice.findObject(By.text("More"));
|
}
|
||||||
moreBtn.click();
|
|
||||||
|
|
||||||
Assert.assertNotNull(mDevice.wait(Until.hasObject(
|
Assert.assertNotNull(mDevice.wait(Until.hasObject(
|
||||||
By.text("I agree")), IDLE_TIMEOUT));
|
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);
|
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||||
mContext.startActivity(intent);
|
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_UDFPS_ULTRASONIC;
|
||||||
import static android.hardware.fingerprint.FingerprintSensorProperties.TYPE_UNKNOWN;
|
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 com.google.common.truth.Truth.assertThat;
|
||||||
|
|
||||||
import static org.mockito.Mockito.when;
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.res.Resources;
|
import android.content.res.Resources;
|
||||||
|
import android.hardware.biometrics.SensorProperties;
|
||||||
|
import android.hardware.fingerprint.Fingerprint;
|
||||||
import android.hardware.fingerprint.FingerprintManager;
|
import android.hardware.fingerprint.FingerprintManager;
|
||||||
|
import android.hardware.fingerprint.FingerprintSensorProperties;
|
||||||
|
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.test.core.app.ApplicationProvider;
|
import androidx.test.core.app.ApplicationProvider;
|
||||||
@@ -48,6 +49,8 @@ import org.mockito.Mock;
|
|||||||
import org.mockito.junit.MockitoJUnit;
|
import org.mockito.junit.MockitoJUnit;
|
||||||
import org.mockito.junit.MockitoRule;
|
import org.mockito.junit.MockitoRule;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
@RunWith(AndroidJUnit4.class)
|
@RunWith(AndroidJUnit4.class)
|
||||||
public class FingerprintRepositoryTest {
|
public class FingerprintRepositoryTest {
|
||||||
|
|
||||||
@@ -66,31 +69,49 @@ public class FingerprintRepositoryTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testCanAssumeSensorType() {
|
public void testCanAssumeSensorType_forUnknownSensor() {
|
||||||
setupFingerprintFirstSensor(mFingerprintManager, TYPE_UNKNOWN, 1);
|
setupFingerprintFirstSensor(mFingerprintManager, TYPE_UNKNOWN, 1);
|
||||||
assertThat(mFingerprintRepository.canAssumeUdfps()).isFalse();
|
assertThat(mFingerprintRepository.canAssumeUdfps()).isFalse();
|
||||||
|
assertThat(mFingerprintRepository.canAssumeSfps()).isFalse();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCanAssumeSensorType_forRearSensor() {
|
||||||
setupFingerprintFirstSensor(mFingerprintManager, TYPE_REAR, 1);
|
setupFingerprintFirstSensor(mFingerprintManager, TYPE_REAR, 1);
|
||||||
assertThat(mFingerprintRepository.canAssumeUdfps()).isFalse();
|
assertThat(mFingerprintRepository.canAssumeUdfps()).isFalse();
|
||||||
|
assertThat(mFingerprintRepository.canAssumeSfps()).isFalse();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCanAssumeSensorType_forUdfpsUltrasonicSensor() {
|
||||||
setupFingerprintFirstSensor(mFingerprintManager, TYPE_UDFPS_ULTRASONIC, 1);
|
setupFingerprintFirstSensor(mFingerprintManager, TYPE_UDFPS_ULTRASONIC, 1);
|
||||||
assertThat(mFingerprintRepository.canAssumeUdfps()).isTrue();
|
assertThat(mFingerprintRepository.canAssumeUdfps()).isTrue();
|
||||||
|
assertThat(mFingerprintRepository.canAssumeSfps()).isFalse();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCanAssumeSensorType_forUdfpsOpticalSensor() {
|
||||||
setupFingerprintFirstSensor(mFingerprintManager, TYPE_UDFPS_OPTICAL, 1);
|
setupFingerprintFirstSensor(mFingerprintManager, TYPE_UDFPS_OPTICAL, 1);
|
||||||
assertThat(mFingerprintRepository.canAssumeUdfps()).isTrue();
|
assertThat(mFingerprintRepository.canAssumeUdfps()).isTrue();
|
||||||
|
assertThat(mFingerprintRepository.canAssumeSfps()).isFalse();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCanAssumeSensorType_forPowerButtonSensor() {
|
||||||
setupFingerprintFirstSensor(mFingerprintManager, TYPE_POWER_BUTTON, 1);
|
setupFingerprintFirstSensor(mFingerprintManager, TYPE_POWER_BUTTON, 1);
|
||||||
assertThat(mFingerprintRepository.canAssumeUdfps()).isFalse();
|
assertThat(mFingerprintRepository.canAssumeUdfps()).isFalse();
|
||||||
|
assertThat(mFingerprintRepository.canAssumeSfps()).isTrue();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCanAssumeSensorType_forHomeButtonSensor() {
|
||||||
setupFingerprintFirstSensor(mFingerprintManager, TYPE_HOME_BUTTON, 1);
|
setupFingerprintFirstSensor(mFingerprintManager, TYPE_HOME_BUTTON, 1);
|
||||||
assertThat(mFingerprintRepository.canAssumeUdfps()).isFalse();
|
assertThat(mFingerprintRepository.canAssumeUdfps()).isFalse();
|
||||||
|
assertThat(mFingerprintRepository.canAssumeSfps()).isFalse();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testGetMaxFingerprints() {
|
public void testGetMaxFingerprints() {
|
||||||
setupFingerprintFirstSensor(mFingerprintManager, TYPE_UNKNOWN, 44);
|
|
||||||
assertThat(mFingerprintRepository.getMaxFingerprints()).isEqualTo(44);
|
|
||||||
|
|
||||||
setupFingerprintFirstSensor(mFingerprintManager, TYPE_UNKNOWN, 999);
|
setupFingerprintFirstSensor(mFingerprintManager, TYPE_UNKNOWN, 999);
|
||||||
assertThat(mFingerprintRepository.getMaxFingerprints()).isEqualTo(999);
|
assertThat(mFingerprintRepository.getMaxFingerprints()).isEqualTo(999);
|
||||||
}
|
}
|
||||||
@@ -122,4 +143,31 @@ public class FingerprintRepositoryTest {
|
|||||||
"suw_max_fingerprints_enrollable");
|
"suw_max_fingerprints_enrollable");
|
||||||
when(mockedResources.getInteger(resId)).thenReturn(numOfFp);
|
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.assertThat;
|
||||||
import static com.google.common.truth.Truth.assertWithMessage;
|
import static com.google.common.truth.Truth.assertWithMessage;
|
||||||
|
|
||||||
|
import static org.mockito.Mockito.doAnswer;
|
||||||
import static org.mockito.Mockito.when;
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
import android.annotation.NonNull;
|
import android.annotation.NonNull;
|
||||||
@@ -71,6 +72,8 @@ import org.mockito.Mock;
|
|||||||
import org.mockito.junit.MockitoJUnit;
|
import org.mockito.junit.MockitoJUnit;
|
||||||
import org.mockito.junit.MockitoRule;
|
import org.mockito.junit.MockitoRule;
|
||||||
|
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
|
||||||
@RunWith(AndroidJUnit4.class)
|
@RunWith(AndroidJUnit4.class)
|
||||||
public class AutoCredentialViewModelTest {
|
public class AutoCredentialViewModelTest {
|
||||||
|
|
||||||
@@ -299,13 +302,18 @@ public class AutoCredentialViewModelTest {
|
|||||||
when(mLockPatternUtils.verifyGatekeeperPasswordHandle(gkPwHandle, newChallenge, userId))
|
when(mLockPatternUtils.verifyGatekeeperPasswordHandle(gkPwHandle, newChallenge, userId))
|
||||||
.thenReturn(newGoodCredential(gkPwHandle, new byte[] { 1 }));
|
.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
|
// Run credential check
|
||||||
@CredentialAction final int action = mViewModel.checkCredential();
|
@CredentialAction final int action = mViewModel.checkCredential();
|
||||||
|
|
||||||
// Check viewModel behavior
|
// Check viewModel behavior
|
||||||
assertThat(action).isEqualTo(CREDENTIAL_IS_GENERATING_CHALLENGE);
|
assertThat(action).isEqualTo(CREDENTIAL_IS_GENERATING_CHALLENGE);
|
||||||
assertThat(mViewModel.getGenerateChallengeFailedLiveData().getValue()).isNull();
|
assertThat(mViewModel.getGenerateChallengeFailedLiveData().getValue()).isNull();
|
||||||
assertThat(mChallengeGenerator.mCallbackRunCount).isEqualTo(1);
|
|
||||||
|
|
||||||
// Check data inside CredentialModel
|
// Check data inside CredentialModel
|
||||||
final Bundle extras = mViewModel.createCredentialIntentExtra();
|
final Bundle extras = mViewModel.createCredentialIntentExtra();
|
||||||
@@ -314,6 +322,8 @@ public class AutoCredentialViewModelTest {
|
|||||||
assertThat(extras.getByteArray(EXTRA_KEY_CHALLENGE_TOKEN)).isNotNull();
|
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_GK_PW_HANDLE)).isEqualTo(INVALID_GK_PW_HANDLE);
|
||||||
assertThat(extras.getLong(EXTRA_KEY_CHALLENGE)).isNotEqualTo(INVALID_CHALLENGE);
|
assertThat(extras.getLong(EXTRA_KEY_CHALLENGE)).isNotEqualTo(INVALID_CHALLENGE);
|
||||||
|
assertThat(mChallengeGenerator.mCallbackRunCount).isEqualTo(1);
|
||||||
|
assertThat(hasCalledRemoveGkPwHandle.get()).isFalse();
|
||||||
|
|
||||||
// Check createGeneratingChallengeExtras()
|
// Check createGeneratingChallengeExtras()
|
||||||
final Bundle generatingChallengeExtras = mViewModel.createGeneratingChallengeExtras();
|
final Bundle generatingChallengeExtras = mViewModel.createGeneratingChallengeExtras();
|
||||||
@@ -511,6 +521,12 @@ public class AutoCredentialViewModelTest {
|
|||||||
when(mLockPatternUtils.verifyGatekeeperPasswordHandle(gkPwHandle, newChallenge, userId))
|
when(mLockPatternUtils.verifyGatekeeperPasswordHandle(gkPwHandle, newChallenge, userId))
|
||||||
.thenReturn(newGoodCredential(gkPwHandle, new byte[] { 1 }));
|
.thenReturn(newGoodCredential(gkPwHandle, new byte[] { 1 }));
|
||||||
|
|
||||||
|
final AtomicBoolean hasCalledRemoveGkPwHandle = new AtomicBoolean();
|
||||||
|
doAnswer(invocation -> {
|
||||||
|
hasCalledRemoveGkPwHandle.set(true);
|
||||||
|
return null;
|
||||||
|
}).when(mLockPatternUtils).removeGatekeeperPasswordHandle(gkPwHandle);
|
||||||
|
|
||||||
// Run checkNewCredentialFromActivityResult()
|
// Run checkNewCredentialFromActivityResult()
|
||||||
final Intent intent = new Intent().putExtra(EXTRA_KEY_GK_PW_HANDLE, gkPwHandle);
|
final Intent intent = new Intent().putExtra(EXTRA_KEY_GK_PW_HANDLE, gkPwHandle);
|
||||||
final boolean ret = mViewModel.checkNewCredentialFromActivityResult(true,
|
final boolean ret = mViewModel.checkNewCredentialFromActivityResult(true,
|
||||||
@@ -524,6 +540,7 @@ public class AutoCredentialViewModelTest {
|
|||||||
assertThat(extras.getByteArray(EXTRA_KEY_CHALLENGE_TOKEN)).isNotNull();
|
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_GK_PW_HANDLE)).isEqualTo(INVALID_GK_PW_HANDLE);
|
||||||
assertThat(mChallengeGenerator.mCallbackRunCount).isEqualTo(1);
|
assertThat(mChallengeGenerator.mCallbackRunCount).isEqualTo(1);
|
||||||
|
assertThat(hasCalledRemoveGkPwHandle.get()).isTrue();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -541,6 +558,12 @@ public class AutoCredentialViewModelTest {
|
|||||||
when(mLockPatternUtils.verifyGatekeeperPasswordHandle(gkPwHandle, newChallenge, userId))
|
when(mLockPatternUtils.verifyGatekeeperPasswordHandle(gkPwHandle, newChallenge, userId))
|
||||||
.thenReturn(newGoodCredential(gkPwHandle, new byte[] { 1 }));
|
.thenReturn(newGoodCredential(gkPwHandle, new byte[] { 1 }));
|
||||||
|
|
||||||
|
final AtomicBoolean hasCalledRemoveGkPwHandle = new AtomicBoolean();
|
||||||
|
doAnswer(invocation -> {
|
||||||
|
hasCalledRemoveGkPwHandle.set(true);
|
||||||
|
return null;
|
||||||
|
}).when(mLockPatternUtils).removeGatekeeperPasswordHandle(gkPwHandle);
|
||||||
|
|
||||||
// Run checkNewCredentialFromActivityResult()
|
// Run checkNewCredentialFromActivityResult()
|
||||||
final Intent intent = new Intent().putExtra(EXTRA_KEY_GK_PW_HANDLE, gkPwHandle);
|
final Intent intent = new Intent().putExtra(EXTRA_KEY_GK_PW_HANDLE, gkPwHandle);
|
||||||
final boolean ret = mViewModel.checkNewCredentialFromActivityResult(false,
|
final boolean ret = mViewModel.checkNewCredentialFromActivityResult(false,
|
||||||
@@ -554,6 +577,7 @@ public class AutoCredentialViewModelTest {
|
|||||||
assertThat(extras.getByteArray(EXTRA_KEY_CHALLENGE_TOKEN)).isNotNull();
|
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_GK_PW_HANDLE)).isEqualTo(INVALID_GK_PW_HANDLE);
|
||||||
assertThat(mChallengeGenerator.mCallbackRunCount).isEqualTo(1);
|
assertThat(mChallengeGenerator.mCallbackRunCount).isEqualTo(1);
|
||||||
|
assertThat(hasCalledRemoveGkPwHandle.get()).isTrue();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class TestChallengeGenerator implements ChallengeGenerator {
|
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_OPTICAL;
|
||||||
import static android.hardware.fingerprint.FingerprintSensorProperties.TYPE_UDFPS_ULTRASONIC;
|
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.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_ERROR_REACH_MAX;
|
||||||
import static com.android.settings.biometrics2.ui.model.FingerprintEnrollIntroStatus.FINGERPRINT_ENROLLABLE_OK;
|
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.newIsSuwPortalRequest;
|
||||||
import static com.android.settings.biometrics2.util.EnrollmentRequestUtil.newIsSuwRequest;
|
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.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;
|
import static com.google.common.truth.Truth.assertThat;
|
||||||
|
|
||||||
@@ -134,26 +134,47 @@ public class FingerprintEnrollIntroViewModelTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testOnStartToUpdateEnrollableStatus_isNotSuw() {
|
public void testOnStartToUpdateEnrollableStatusOk_isNotSuw() {
|
||||||
testOnStartToUpdateEnrollableStatus(newAllFalseRequest(mApplication));
|
testOnStartToUpdateEnrollableStatusOk(newAllFalseRequest(mApplication));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testOnStartToUpdateEnrollableStatus_isSuwDeferred() {
|
public void testOnStartToUpdateEnrollableStatusReachMax_isNotSuw() {
|
||||||
testOnStartToUpdateEnrollableStatus(newIsSuwDeferredRequest(mApplication));
|
testOnStartToUpdateEnrollableStatusReachMax(newAllFalseRequest(mApplication));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testOnStartToUpdateEnrollableStatus_isSuwPortal() {
|
public void testOnStartToUpdateEnrollableStatusOk_isSuwDeferred() {
|
||||||
testOnStartToUpdateEnrollableStatus(newIsSuwPortalRequest(mApplication));
|
testOnStartToUpdateEnrollableStatusOk(newIsSuwDeferredRequest(mApplication));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testOnStartToUpdateEnrollableStatus_isSuwSuggestedActionFlow() {
|
public void testOnStartToUpdateEnrollableStatusReachMax_isSuwDeferred() {
|
||||||
testOnStartToUpdateEnrollableStatus(newIsSuwSuggestedActionFlowRequest(mApplication));
|
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;
|
final int userId = 45;
|
||||||
mViewModel.setUserId(userId);
|
mViewModel.setUserId(userId);
|
||||||
mViewModel.setEnrollmentRequest(request);
|
mViewModel.setEnrollmentRequest(request);
|
||||||
@@ -163,19 +184,28 @@ public class FingerprintEnrollIntroViewModelTest {
|
|||||||
mViewModel.onStart(mLifecycleOwner);
|
mViewModel.onStart(mLifecycleOwner);
|
||||||
FingerprintEnrollIntroStatus status = mViewModel.getPageStatusLiveData().getValue();
|
FingerprintEnrollIntroStatus status = mViewModel.getPageStatusLiveData().getValue();
|
||||||
assertThat(status.getEnrollableStatus()).isEqualTo(FINGERPRINT_ENROLLABLE_OK);
|
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);
|
setupFingerprintEnrolledFingerprints(mFingerprintManager, userId, 5);
|
||||||
setupFingerprintFirstSensor(mFingerprintManager, TYPE_UDFPS_OPTICAL, 5);
|
setupFingerprintFirstSensor(mFingerprintManager, TYPE_UDFPS_OPTICAL, 5);
|
||||||
mViewModel.onStart(mLifecycleOwner);
|
mViewModel.onStart(mLifecycleOwner);
|
||||||
status = mViewModel.getPageStatusLiveData().getValue();
|
FingerprintEnrollIntroStatus status = mViewModel.getPageStatusLiveData().getValue();
|
||||||
assertThat(status.getEnrollableStatus()).isEqualTo(FINGERPRINT_ENROLLABLE_ERROR_REACH_MAX);
|
assertThat(status.getEnrollableStatus()).isEqualTo(FINGERPRINT_ENROLLABLE_ERROR_REACH_MAX);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void textCanAssumeUdfps() {
|
public void textCanAssumeUdfps_forUdfpsUltrasonicSensor() {
|
||||||
setupFingerprintFirstSensor(mFingerprintManager, TYPE_UDFPS_ULTRASONIC, 1);
|
setupFingerprintFirstSensor(mFingerprintManager, TYPE_UDFPS_ULTRASONIC, 1);
|
||||||
assertThat(mViewModel.canAssumeUdfps()).isEqualTo(true);
|
assertThat(mViewModel.canAssumeUdfps()).isEqualTo(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void textCanAssumeUdfps_forRearSensor() {
|
||||||
setupFingerprintFirstSensor(mFingerprintManager, TYPE_REAR, 1);
|
setupFingerprintFirstSensor(mFingerprintManager, TYPE_REAR, 1);
|
||||||
assertThat(mViewModel.canAssumeUdfps()).isEqualTo(false);
|
assertThat(mViewModel.canAssumeUdfps()).isEqualTo(false);
|
||||||
}
|
}
|
||||||
@@ -238,7 +268,7 @@ public class FingerprintEnrollIntroViewModelTest {
|
|||||||
assertThat(status.getEnrollableStatus()).isEqualTo(FINGERPRINT_ENROLLABLE_OK);
|
assertThat(status.getEnrollableStatus()).isEqualTo(FINGERPRINT_ENROLLABLE_OK);
|
||||||
|
|
||||||
// Perform click on `next`
|
// Perform click on `next`
|
||||||
mViewModel.onNextButtonClick(null);
|
mViewModel.onNextButtonClick();
|
||||||
|
|
||||||
assertThat(mViewModel.getActionLiveData().getValue())
|
assertThat(mViewModel.getActionLiveData().getValue())
|
||||||
.isEqualTo(FINGERPRINT_ENROLL_INTRO_ACTION_CONTINUE_ENROLL);
|
.isEqualTo(FINGERPRINT_ENROLL_INTRO_ACTION_CONTINUE_ENROLL);
|
||||||
@@ -258,7 +288,7 @@ public class FingerprintEnrollIntroViewModelTest {
|
|||||||
assertThat(status.getEnrollableStatus()).isEqualTo(FINGERPRINT_ENROLLABLE_ERROR_REACH_MAX);
|
assertThat(status.getEnrollableStatus()).isEqualTo(FINGERPRINT_ENROLLABLE_ERROR_REACH_MAX);
|
||||||
|
|
||||||
// Perform click on `next`
|
// Perform click on `next`
|
||||||
mViewModel.onNextButtonClick(null);
|
mViewModel.onNextButtonClick();
|
||||||
|
|
||||||
assertThat(mViewModel.getActionLiveData().getValue())
|
assertThat(mViewModel.getActionLiveData().getValue())
|
||||||
.isEqualTo(FINGERPRINT_ENROLL_INTRO_ACTION_DONE_AND_FINISH);
|
.isEqualTo(FINGERPRINT_ENROLL_INTRO_ACTION_DONE_AND_FINISH);
|
||||||
@@ -266,7 +296,7 @@ public class FingerprintEnrollIntroViewModelTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testOnSkipOrCancelButtonClick() {
|
public void testOnSkipOrCancelButtonClick() {
|
||||||
mViewModel.onSkipOrCancelButtonClick(null);
|
mViewModel.onSkipOrCancelButtonClick();
|
||||||
|
|
||||||
assertThat(mViewModel.getActionLiveData().getValue())
|
assertThat(mViewModel.getActionLiveData().getValue())
|
||||||
.isEqualTo(FINGERPRINT_ENROLL_INTRO_ACTION_SKIP_OR_CANCEL);
|
.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_SKIP;
|
||||||
import static com.android.settings.biometrics.BiometricEnrollBase.RESULT_TIMEOUT;
|
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.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.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.newAllFalseRequest;
|
||||||
import static com.android.settings.biometrics2.util.EnrollmentRequestUtil.newIsSuwRequest;
|
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;
|
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