Attach FingerprintEnrollEnrollingRfps fragment
Support enrolling RFPS on biomerics v2.
Bug: 260957939
Test: atest FingerprintEnrollFindSensorViewModelTest
FingerprintEnrollProgressViewModelTest
FingerprintEnrollmentViewModelTest
FingerprintEnrollmentActivityTest
Change-Id: Ic04b934592415d03f1b119383bffd40bd5eef2bd
diff --git a/src/com/android/settings/biometrics/fingerprint/FingerprintErrorDialog.java b/src/com/android/settings/biometrics/fingerprint/FingerprintErrorDialog.java
index 870e1bb..8f604e2 100644
--- a/src/com/android/settings/biometrics/fingerprint/FingerprintErrorDialog.java
+++ b/src/com/android/settings/biometrics/fingerprint/FingerprintErrorDialog.java
@@ -123,7 +123,10 @@
dialog.show(fragmentManager, FingerprintErrorDialog.class.getName());
}
- private static int getErrorMessage(int errMsgId) {
+ /**
+ * Gets dialog message as error id inside {@link FingerprintManager}
+ */
+ public static int getErrorMessage(int errMsgId) {
switch (errMsgId) {
case FingerprintManager.FINGERPRINT_ERROR_TIMEOUT:
// This message happens when the underlying crypto layer decides to revoke
@@ -137,7 +140,10 @@
}
}
- private static int getErrorTitle(int errMsgId) {
+ /**
+ * Gets dialog title as error id inside {@link FingerprintManager}
+ */
+ public static int getErrorTitle(int errMsgId) {
switch (errMsgId) {
case FingerprintManager.FINGERPRINT_ERROR_UNABLE_TO_PROCESS:
return R.string
diff --git a/src/com/android/settings/biometrics2/data/repository/FingerprintRepository.java b/src/com/android/settings/biometrics2/data/repository/FingerprintRepository.java
index 8f432e6..6a9892a 100644
--- a/src/com/android/settings/biometrics2/data/repository/FingerprintRepository.java
+++ b/src/com/android/settings/biometrics2/data/repository/FingerprintRepository.java
@@ -98,8 +98,11 @@
return resources.getInteger(R.integer.suw_max_fingerprints_enrollable);
}
+ /**
+ * Gets the first FingerprintSensorPropertiesInternal from FingerprintManager
+ */
@Nullable
- private FingerprintSensorPropertiesInternal getFirstFingerprintSensorPropertiesInternal() {
+ public FingerprintSensorPropertiesInternal getFirstFingerprintSensorPropertiesInternal() {
final List<FingerprintSensorPropertiesInternal> props = mSensorPropertiesCache;
if (props == null) {
// Handle this case if it really happens
diff --git a/src/com/android/settings/biometrics2/factory/BiometricsViewModelFactory.java b/src/com/android/settings/biometrics2/factory/BiometricsViewModelFactory.java
index 7bf9d53..19971a7 100644
--- a/src/com/android/settings/biometrics2/factory/BiometricsViewModelFactory.java
+++ b/src/com/android/settings/biometrics2/factory/BiometricsViewModelFactory.java
@@ -113,13 +113,14 @@
new FingerprintUpdater(application), userId);
}
} else if (modelClass.isAssignableFrom(FingerprintEnrollEnrollingViewModel.class)) {
+ final Integer userId = extras.get(USER_ID_KEY);
final FingerprintRepository fingerprint = provider.getFingerprintRepository(
application);
final AccessibilityRepository accessibility = provider.getAccessibilityRepository(
application);
final VibratorRepository vibrator = provider.getVibratorRepository(application);
if (fingerprint != null && accessibility != null && vibrator != null) {
- return (T) new FingerprintEnrollEnrollingViewModel(application, fingerprint,
+ return (T) new FingerprintEnrollEnrollingViewModel(application, userId, fingerprint,
accessibility, vibrator);
}
}
diff --git a/src/com/android/settings/biometrics2/ui/view/FingerprintEnrollEnrollingErrorDialog.java b/src/com/android/settings/biometrics2/ui/view/FingerprintEnrollEnrollingErrorDialog.java
new file mode 100644
index 0000000..ad34e74
--- /dev/null
+++ b/src/com/android/settings/biometrics2/ui/view/FingerprintEnrollEnrollingErrorDialog.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.biometrics2.ui.view;
+
+import static com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollEnrollingViewModel.ErrorDialogData;
+import static com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollEnrollingViewModel.FINGERPRINT_ERROR_DIALOG_ACTION_RESTART;
+import static com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollEnrollingViewModel.FINGERPRINT_ERROR_DIALOG_ACTION_SET_RESULT_FINISH;
+import static com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollEnrollingViewModel.FINGERPRINT_ERROR_DIALOG_ACTION_SET_RESULT_TIMEOUT;
+
+import android.app.Dialog;
+import android.app.settings.SettingsEnums;
+import android.content.Context;
+import android.hardware.biometrics.BiometricConstants;
+import android.os.Bundle;
+
+import androidx.appcompat.app.AlertDialog;
+import androidx.lifecycle.ViewModelProvider;
+
+import com.android.settings.R;
+import com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollEnrollingViewModel;
+import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
+
+/**
+ * Fingerprint error dialog, will be shown when an error occurs during fingerprint enrollment.
+ */
+public class FingerprintEnrollEnrollingErrorDialog extends InstrumentedDialogFragment {
+
+ private FingerprintEnrollEnrollingViewModel mViewModel;
+
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ final AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
+ final ErrorDialogData data = mViewModel.getErrorDialogLiveData().getValue();
+ final int errMsgId = data.getErrMsgId();
+ final boolean canAssumeUdfps = mViewModel.canAssumeUdfps();
+ final boolean wasTimeout = errMsgId == BiometricConstants.BIOMETRIC_ERROR_TIMEOUT;
+
+ builder.setTitle(data.getErrTitle())
+ .setMessage(data.getErrMsg())
+ .setCancelable(false);
+ if (wasTimeout && canAssumeUdfps) {
+ builder.setPositiveButton(
+ R.string.security_settings_fingerprint_enroll_dialog_try_again,
+ (dialog, which) -> {
+ dialog.dismiss();
+ mViewModel.onErrorDialogAction(FINGERPRINT_ERROR_DIALOG_ACTION_RESTART);
+ });
+ builder.setNegativeButton(
+ R.string.security_settings_fingerprint_enroll_dialog_ok,
+ (dialog, which) -> {
+ dialog.dismiss();
+ mViewModel.onErrorDialogAction(
+ FINGERPRINT_ERROR_DIALOG_ACTION_SET_RESULT_TIMEOUT);
+ });
+ } else {
+ builder.setPositiveButton(
+ R.string.security_settings_fingerprint_enroll_dialog_ok,
+ (dialog, which) -> {
+ dialog.dismiss();
+ mViewModel.onErrorDialogAction(wasTimeout
+ ? FINGERPRINT_ERROR_DIALOG_ACTION_SET_RESULT_TIMEOUT
+ : FINGERPRINT_ERROR_DIALOG_ACTION_SET_RESULT_FINISH);
+ });
+ }
+ final AlertDialog dialog = builder.create();
+ dialog.setCanceledOnTouchOutside(false);
+ return dialog;
+ }
+
+ @Override
+ public int getMetricsCategory() {
+ return SettingsEnums.DIALOG_FINGERPINT_ERROR;
+ }
+
+ @Override
+ public void onAttach(Context context) {
+ mViewModel = new ViewModelProvider(getActivity()).get(
+ FingerprintEnrollEnrollingViewModel.class);
+ super.onAttach(context);
+ }
+}
diff --git a/src/com/android/settings/biometrics2/ui/view/FingerprintEnrollEnrollingRfpsFragment.java b/src/com/android/settings/biometrics2/ui/view/FingerprintEnrollEnrollingRfpsFragment.java
index 30b66a2..30823ea 100644
--- a/src/com/android/settings/biometrics2/ui/view/FingerprintEnrollEnrollingRfpsFragment.java
+++ b/src/com/android/settings/biometrics2/ui/view/FingerprintEnrollEnrollingRfpsFragment.java
@@ -16,15 +16,19 @@
package com.android.settings.biometrics2.ui.view;
+import static android.hardware.fingerprint.FingerprintManager.ENROLL_ENROLL;
+
+import android.animation.Animator;
+import android.animation.ObjectAnimator;
import android.app.Activity;
import android.content.Context;
-import android.content.res.Configuration;
import android.graphics.PorterDuff;
import android.graphics.drawable.Animatable2;
import android.graphics.drawable.AnimatedVectorDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.LayerDrawable;
import android.os.Bundle;
+import android.text.TextUtils;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.MotionEvent;
@@ -35,18 +39,22 @@
import android.widget.ProgressBar;
import android.widget.TextView;
+import androidx.annotation.IdRes;
import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentActivity;
+import androidx.lifecycle.Observer;
import androidx.lifecycle.ViewModelProvider;
+import androidx.transition.Transition;
+import androidx.transition.TransitionSet;
import com.android.settings.R;
-import com.android.settings.biometrics.BiometricUtils;
+import com.android.settings.biometrics.fingerprint.FingerprintErrorDialog;
import com.android.settings.biometrics2.ui.model.EnrollmentProgress;
+import com.android.settings.biometrics2.ui.model.EnrollmentStatusMessage;
import com.android.settings.biometrics2.ui.viewmodel.DeviceRotationViewModel;
import com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollEnrollingViewModel;
import com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollProgressViewModel;
-import com.android.settingslib.display.DisplayDensityUtils;
import com.airbnb.lottie.LottieAnimationView;
import com.google.android.setupcompat.template.FooterBarMixin;
@@ -59,9 +67,16 @@
public class FingerprintEnrollEnrollingRfpsFragment extends Fragment {
private static final String TAG = FingerprintEnrollEnrollingRfpsFragment.class.getSimpleName();
+ private static final boolean DEBUG = false;
+ private static final int PROGRESS_BAR_MAX = 10000;
private static final long ICON_TOUCH_DURATION_UNTIL_DIALOG_SHOWN = 500;
private static final int ICON_TOUCH_COUNT_SHOW_UNTIL_DIALOG_SHOWN = 3;
+
+ /**
+ * If we don't see progress during this time, we show an error message to remind the users that
+ * they need to lift the finger and touch again.
+ */
private static final int HINT_TIMEOUT_DURATION = 2500;
private FingerprintEnrollEnrollingViewModel mEnrollingViewModel;
@@ -73,8 +88,9 @@
private Interpolator mFastOutLinearInInterpolator;
private boolean mAnimationCancelled;
- private View mView;
+ private GlifLayout mView;
private ProgressBar mProgressBar;
+ private ObjectAnimator mProgressAnim;
private TextView mErrorText;
private FooterBarMixin mFooterBarMixin;
private AnimatedVectorDrawable mIconAnimationDrawable;
@@ -90,8 +106,45 @@
private boolean mHaveShownSfpsLeftEdgeLottie;
private boolean mHaveShownSfpsRightEdgeLottie;
- private final View.OnClickListener mOnSkipClickListener =
- (v) -> mEnrollingViewModel.onSkipButtonClick();
+ private final View.OnClickListener mOnSkipClickListener = v -> {
+ mProgressViewModel.cancelEnrollment();
+ mEnrollingViewModel.onSkipButtonClick();
+ };
+
+ private final Observer<EnrollmentProgress> mProgressObserver = progress -> {
+ if (DEBUG) {
+ Log.d(TAG, "mProgressObserver(" + progress + ")");
+ }
+ if (progress != null) {
+ onEnrollmentProgressChange(progress);
+ }
+ };
+
+ private final Observer<EnrollmentStatusMessage> mHelpMessageObserver = helpMessage -> {
+ if (DEBUG) {
+ Log.d(TAG, "mHelpMessageObserver(" + helpMessage + ")");
+ }
+ if (helpMessage != null) {
+ onEnrollmentHelp(helpMessage);
+ }
+ };
+ private final Observer<EnrollmentStatusMessage> mErrorMessageObserver = errorMessage -> {
+ if (DEBUG) {
+ Log.d(TAG, "mErrorMessageObserver(" + errorMessage + ")");
+ }
+ if (errorMessage != null) {
+ onEnrollmentError(errorMessage);
+ }
+ };
+ private final Observer<Boolean> mAcquireObserver = isAcquiredGood -> {
+ // TODO
+ };
+ private final Observer<Integer> mPointerDownObserver = sensorId -> {
+ // TODO
+ };
+ private final Observer<Integer> mPointerUpObserver = sensorId -> {
+ // TODO
+ };
private int mIconTouchCount;
@@ -103,39 +156,74 @@
mRotationViewModel = provider.get(DeviceRotationViewModel.class);
mProgressViewModel = provider.get(FingerprintEnrollProgressViewModel.class);
super.onAttach(context);
+ final TransitionSet transitionSet = (TransitionSet) getSharedElementEnterTransition();
+ if (transitionSet != null) {
+ transitionSet.addListener(new Transition.TransitionListener() {
+ @Override
+ public void onTransitionStart(@NonNull Transition transition) {
+ }
+
+ @Override
+ public void onTransitionEnd(@NonNull Transition transition) {
+ transition.removeListener(this);
+ mAnimationCancelled = false;
+ startIconAnimation();
+ }
+
+ @Override
+ public void onTransitionCancel(@NonNull Transition transition) {
+ }
+
+ @Override
+ public void onTransitionPause(@NonNull Transition transition) {
+ }
+
+ @Override
+ public void onTransitionResume(@NonNull Transition transition) {
+ }
+ });
+ }
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
+ mEnrollingViewModel.restoreSavedState(savedInstanceState);
mIsAccessibilityEnabled = mEnrollingViewModel.isAccessibilityEnabled();
}
@Override
+ public void onSaveInstanceState(@NonNull Bundle outState) {
+ mEnrollingViewModel.onSaveInstanceState(outState);
+ super.onSaveInstanceState(outState);
+ }
+
+ @Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
mView = initRfpsLayout(inflater, container);
return mView;
}
- private View initRfpsLayout(LayoutInflater inflater, ViewGroup container) {
- final View containView = inflater.inflate(R.layout.sfps_enroll_enrolling, container, false);
+ private GlifLayout initRfpsLayout(LayoutInflater inflater, ViewGroup container) {
+ final GlifLayout containView = (GlifLayout) inflater.inflate(
+ R.layout.fingerprint_enroll_enrolling, container, false);
+
final Activity activity = getActivity();
- final GlifLayoutHelper glifLayoutHelper = new GlifLayoutHelper(activity,
- (GlifLayout) containView);
- glifLayoutHelper.setDescriptionText(
- R.string.security_settings_fingerprint_enroll_start_message);
+ final GlifLayoutHelper glifLayoutHelper = new GlifLayoutHelper(activity, containView);
+ glifLayoutHelper.setDescriptionText(getString(
+ R.string.security_settings_fingerprint_enroll_start_message));
glifLayoutHelper.setHeaderText(R.string.security_settings_fingerprint_enroll_repeat_title);
- mShouldShowLottie = shouldShowLottie();
- boolean isLandscape = BiometricUtils.isReverseLandscape(activity)
- || BiometricUtils.isLandscape(activity);
- updateOrientation((isLandscape
- ? Configuration.ORIENTATION_LANDSCAPE : Configuration.ORIENTATION_PORTRAIT));
+// mShouldShowLottie = shouldShowLottie(); // TODO move this call into updateOrientation()?
+// boolean isLandscape = BiometricUtils.isReverseLandscape(activity)
+// || BiometricUtils.isLandscape(activity);
+// updateOrientation(containView, (isLandscape
+// ? Configuration.ORIENTATION_LANDSCAPE : Configuration.ORIENTATION_PORTRAIT));
mErrorText = containView.findViewById(R.id.error_text);
mProgressBar = containView.findViewById(R.id.fingerprint_progress_bar);
- mFooterBarMixin = ((GlifLayout) containView).getMixin(FooterBarMixin.class);
+ mFooterBarMixin = containView.getMixin(FooterBarMixin.class);
mFooterBarMixin.setSecondaryButton(
new FooterButton.Builder(activity)
.setText(R.string.security_settings_fingerprint_enroll_enrolling_skip)
@@ -145,15 +233,12 @@
.build()
);
- final LayerDrawable fingerprintDrawable = mProgressBar != null
- ? (LayerDrawable) mProgressBar.getBackground() : null;
- if (fingerprintDrawable != null) {
- mIconAnimationDrawable = (AnimatedVectorDrawable)
- fingerprintDrawable.findDrawableByLayerId(R.id.fingerprint_animation);
- mIconBackgroundBlinksDrawable = (AnimatedVectorDrawable)
- fingerprintDrawable.findDrawableByLayerId(R.id.fingerprint_background);
- mIconAnimationDrawable.registerAnimationCallback(mIconAnimationCallback);
- }
+ final LayerDrawable fingerprintDrawable = (LayerDrawable) mProgressBar.getBackground();
+ mIconAnimationDrawable = (AnimatedVectorDrawable)
+ fingerprintDrawable.findDrawableByLayerId(R.id.fingerprint_animation);
+ mIconBackgroundBlinksDrawable = (AnimatedVectorDrawable)
+ fingerprintDrawable.findDrawableByLayerId(R.id.fingerprint_background);
+ mIconAnimationDrawable.registerAnimationCallback(mIconAnimationCallback);
mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(
activity, android.R.interpolator.fast_out_slow_in);
@@ -162,58 +247,34 @@
mFastOutLinearInInterpolator = AnimationUtils.loadInterpolator(
activity, android.R.interpolator.fast_out_linear_in);
- if (mProgressBar != null) {
- mProgressBar.setProgressBackgroundTintMode(PorterDuff.Mode.SRC);
- mProgressBar.setOnTouchListener((v, event) -> {
- if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
- mIconTouchCount++;
- if (mIconTouchCount == ICON_TOUCH_COUNT_SHOW_UNTIL_DIALOG_SHOWN) {
- showIconTouchDialog();
- } else {
- mProgressBar.postDelayed(mShowDialogRunnable,
- ICON_TOUCH_DURATION_UNTIL_DIALOG_SHOWN);
- }
- } else if (event.getActionMasked() == MotionEvent.ACTION_CANCEL
- || event.getActionMasked() == MotionEvent.ACTION_UP) {
- mProgressBar.removeCallbacks(mShowDialogRunnable);
+ mProgressBar.setProgressBackgroundTintMode(PorterDuff.Mode.SRC);
+ mProgressBar.setOnTouchListener((v, event) -> {
+ if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
+ mIconTouchCount++;
+ if (mIconTouchCount == ICON_TOUCH_COUNT_SHOW_UNTIL_DIALOG_SHOWN) {
+ showIconTouchDialog();
+ } else {
+ mProgressBar.postDelayed(mShowDialogRunnable,
+ ICON_TOUCH_DURATION_UNTIL_DIALOG_SHOWN);
}
- return true;
- });
- }
+ } else if (event.getActionMasked() == MotionEvent.ACTION_CANCEL
+ || event.getActionMasked() == MotionEvent.ACTION_UP) {
+ mProgressBar.removeCallbacks(mShowDialogRunnable);
+ }
+ return true;
+ });
return containView;
}
- private void updateOrientation(int orientation) {
- switch (orientation) {
- case Configuration.ORIENTATION_LANDSCAPE: {
- mIllustrationLottie = null;
- break;
- }
- case Configuration.ORIENTATION_PORTRAIT: {
- if (mShouldShowLottie) {
- mIllustrationLottie = mView.findViewById(R.id.illustration_lottie);
- }
- break;
- }
- default:
- Log.e(TAG, "Error unhandled configuration change");
- break;
- }
- }
-
- private void updateTitleAndDescription() {
- final Activity activity = getActivity();
- final GlifLayoutHelper glifLayoutHelper = new GlifLayoutHelper(activity,
- (GlifLayout) mView);
-
- EnrollmentProgress progressLiveData = mProgressViewModel.getProgressLiveData().getValue();
- if (progressLiveData == null || progressLiveData.getSteps() == -1) {
- glifLayoutHelper.setDescriptionText(
- R.string.security_settings_fingerprint_enroll_start_message);
- } else {
- glifLayoutHelper.setDescriptionText(
- R.string.security_settings_fingerprint_enroll_repeat_message);
+ @Override
+ public void onStart() {
+ super.onStart();
+ startEnrollment();
+ updateProgress(false /* animate */, mProgressViewModel.getProgressLiveData().getValue());
+ updateTitleAndDescription();
+ if (true /* TODO check mRestoring */) {
+ startIconAnimation();
}
}
@@ -230,25 +291,243 @@
}
}
+ private void onCancelEnrollment(@IdRes int errorMsgId) {
+ // showErrorDialog() will cause onWindowFocusChanged(false), set mIsCanceled to false
+ // before showErrorDialog() to prevent that another error dialog is triggered again.
+// TODO mIsCanceled = true;
+// TODO mIsOrientationChanged = false;
+ mEnrollingViewModel.showErrorDialog(new FingerprintEnrollEnrollingViewModel.ErrorDialogData(
+ mView.getContext().getString(FingerprintErrorDialog.getErrorMessage(errorMsgId)),
+ mView.getContext().getString(FingerprintErrorDialog.getErrorTitle(errorMsgId)),
+ errorMsgId
+ ));
+ cancelEnrollment();
+ stopIconAnimation();
+ }
+
+ @Override
+ public void onStop() {
+ stopIconAnimation();
+ removeEnrollmentObserver();
+ if (!getActivity().isChangingConfigurations()) {
+ mProgressViewModel.cancelEnrollment();
+ }
+ super.onStop();
+ }
+
+ private void removeEnrollmentObserver() {
+ mProgressViewModel.getProgressLiveData().removeObserver(mProgressObserver);
+ mProgressViewModel.getHelpMessageLiveData().removeObserver(mHelpMessageObserver);
+ mProgressViewModel.getErrorMessageLiveData().removeObserver(mErrorMessageObserver);
+ mProgressViewModel.getAcquireLiveData().removeObserver(mAcquireObserver);
+ mProgressViewModel.getPointerDownLiveData().removeObserver(mPointerDownObserver);
+ mProgressViewModel.getPointerUpLiveData().removeObserver(mPointerUpObserver);
+ }
+
+ private void cancelEnrollment() {
+ removeEnrollmentObserver();
+ mProgressViewModel.cancelEnrollment();
+ }
+
+ private void startEnrollment() {
+ mProgressViewModel.getProgressLiveData().observe(this, mProgressObserver);
+ mProgressViewModel.getHelpMessageLiveData().observe(this, mHelpMessageObserver);
+ mProgressViewModel.getErrorMessageLiveData().observe(this, mErrorMessageObserver);
+ mProgressViewModel.getAcquireLiveData().observe(this, mAcquireObserver);
+ mProgressViewModel.getPointerDownLiveData().observe(this, mPointerDownObserver);
+ mProgressViewModel.getPointerUpLiveData().observe(this, mPointerUpObserver);
+ mProgressViewModel.startEnrollment(ENROLL_ENROLL);
+ }
+
+ private void onEnrollmentHelp(@NonNull EnrollmentStatusMessage helpMessage) {
+ final CharSequence helpStr = helpMessage.getStr();
+ if (!TextUtils.isEmpty(helpStr)) {
+ mErrorText.removeCallbacks(mTouchAgainRunnable);
+ showError(helpStr);
+ }
+ }
+
+ private void onEnrollmentError(@NonNull EnrollmentStatusMessage errorMessage) {
+ onCancelEnrollment(errorMessage.getMsgId());
+ }
+
+ private void onEnrollmentProgressChange(@NonNull EnrollmentProgress progress) {
+ updateProgress(true /* animate */, progress);
+ updateTitleAndDescription();
+ animateFlash();
+ mErrorText.removeCallbacks(mTouchAgainRunnable);
+ mErrorText.postDelayed(mTouchAgainRunnable, HINT_TIMEOUT_DURATION);
+ }
+
+ private void updateProgress(boolean animate, @NonNull EnrollmentProgress enrollmentProgress) {
+ if (!mProgressViewModel.isEnrolling()) {
+ Log.d(TAG, "Enrollment not started yet");
+ return;
+ }
+
+ final int progress = getProgress(enrollmentProgress);
+ // Only clear the error when progress has been made.
+ // TODO (b/234772728) Add tests.
+ if (mProgressBar != null && mProgressBar.getProgress() < progress) {
+ clearError();
+ }
+
+ if (animate) {
+ animateProgress(progress);
+ } else {
+ if (mProgressBar != null) {
+ mProgressBar.setProgress(progress);
+ }
+ if (progress >= PROGRESS_BAR_MAX) {
+ mDelayedFinishRunnable.run();
+ }
+ }
+ }
+
+ private int getProgress(@NonNull EnrollmentProgress progress) {
+ if (progress.getSteps() == -1) {
+ return 0;
+ }
+ int displayProgress = Math.max(0, progress.getSteps() + 1 - progress.getRemaining());
+ return PROGRESS_BAR_MAX * displayProgress / (progress.getSteps() + 1);
+ }
+
+ private void showError(CharSequence error) {
+ mErrorText.setText(error);
+ if (mErrorText.getVisibility() == View.INVISIBLE) {
+ mErrorText.setVisibility(View.VISIBLE);
+ mErrorText.setTranslationY(mView.getContext().getResources().getDimensionPixelSize(
+ R.dimen.fingerprint_error_text_appear_distance));
+ mErrorText.setAlpha(0f);
+ mErrorText.animate()
+ .alpha(1f)
+ .translationY(0f)
+ .setDuration(200)
+ .setInterpolator(mLinearOutSlowInInterpolator)
+ .start();
+ } else {
+ mErrorText.animate().cancel();
+ mErrorText.setAlpha(1f);
+ mErrorText.setTranslationY(0f);
+ }
+ if (isResumed() && mEnrollingViewModel.isAccessibilityEnabled()) {
+ mEnrollingViewModel.vibrateError(getClass().getSimpleName() + "::showError");
+ }
+ }
+
+ private void clearError() {
+ if (mErrorText.getVisibility() == View.VISIBLE) {
+ mErrorText.animate()
+ .alpha(0f)
+ .translationY(getResources().getDimensionPixelSize(
+ R.dimen.fingerprint_error_text_disappear_distance))
+ .setDuration(100)
+ .setInterpolator(mFastOutLinearInInterpolator)
+ .withEndAction(() -> mErrorText.setVisibility(View.INVISIBLE))
+ .start();
+ }
+ }
+
+
+ @Override
+ public void onDestroy() {
+ // TODO stopListenOrientationEvent();
+ super.onDestroy();
+ }
+
+ private void animateProgress(int progress) {
+ if (mProgressAnim != null) {
+ mProgressAnim.cancel();
+ }
+ ObjectAnimator anim = ObjectAnimator.ofInt(mProgressBar, "progress",
+ mProgressBar.getProgress(), progress);
+ anim.addListener(mProgressAnimationListener);
+ anim.setInterpolator(mFastOutSlowInInterpolator);
+ anim.setDuration(250);
+ anim.start();
+ mProgressAnim = anim;
+ }
+
+ private final Runnable mTouchAgainRunnable = new Runnable() {
+ @Override
+ public void run() {
+ // Use mView to getString to prevent activity is missing during rotation
+ showError(mView.getContext().getString(
+ R.string.security_settings_fingerprint_enroll_lift_touch_again));
+ }
+ };
+
+// private void updateOrientation(@NonNull GlifLayout glifLayout, int orientation) {
+// switch (orientation) {
+// case Configuration.ORIENTATION_LANDSCAPE: {
+// mIllustrationLottie = null;
+// break;
+// }
+// case Configuration.ORIENTATION_PORTRAIT: {
+// if (shouldShowLottie()) {
+// mIllustrationLottie = glifLayout.findViewById(R.id.illustration_lottie);
+// }
+// break;
+// }
+// default:
+// Log.e(TAG, "Error unhandled configuration change");
+// break;
+// }
+// }
+
+ private void animateFlash() {
+ if (mIconBackgroundBlinksDrawable != null) {
+ mIconBackgroundBlinksDrawable.start();
+ }
+ }
+
+ private void updateTitleAndDescription() {
+ final EnrollmentProgress progressLiveData =
+ mProgressViewModel.getProgressLiveData().getValue();
+ new GlifLayoutHelper(getActivity(), mView).setDescriptionText(mView.getContext().getString(
+ progressLiveData == null || progressLiveData.getSteps() == -1
+ ? R.string.security_settings_fingerprint_enroll_start_message
+ : R.string.security_settings_fingerprint_enroll_repeat_message));
+ }
+
private void showIconTouchDialog() {
mIconTouchCount = 0;
//TODO EnrollingActivity should observe live data and add dialog fragment
mEnrollingViewModel.onIconTouchDialogShow();
}
- private boolean shouldShowLottie() {
- DisplayDensityUtils displayDensity = new DisplayDensityUtils(getContext());
- int currentDensityIndex = displayDensity.getCurrentIndexForDefaultDisplay();
- final int currentDensity = displayDensity.getDefaultDisplayDensityValues()
- [currentDensityIndex];
- final int defaultDensity = displayDensity.getDefaultDensityForDefaultDisplay();
- return defaultDensity == currentDensity;
- }
+ private final Runnable mShowDialogRunnable = () -> showIconTouchDialog();
- private final Runnable mShowDialogRunnable = new Runnable() {
+ private final Animator.AnimatorListener mProgressAnimationListener =
+ new Animator.AnimatorListener() {
+
+ @Override
+ public void onAnimationStart(Animator animation) {
+ startIconAnimation();
+ }
+
+ @Override
+ public void onAnimationRepeat(Animator animation) { }
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ stopIconAnimation();
+
+ if (mProgressBar.getProgress() >= PROGRESS_BAR_MAX) {
+ mProgressBar.postDelayed(mDelayedFinishRunnable, 250L);
+ }
+ }
+
+ @Override
+ public void onAnimationCancel(Animator animation) { }
+ };
+
+ // Give the user a chance to see progress completed before jumping to the next stage.
+ private final Runnable mDelayedFinishRunnable = new Runnable() {
@Override
public void run() {
- showIconTouchDialog();
+ mEnrollingViewModel.onSkipButtonClick();
+ /* TODO launchFinish(); */
}
};
diff --git a/src/com/android/settings/biometrics2/ui/view/FingerprintEnrollEnrollingSfpsFragment.java b/src/com/android/settings/biometrics2/ui/view/FingerprintEnrollEnrollingSfpsFragment.java
index ddeb465..57b8665 100644
--- a/src/com/android/settings/biometrics2/ui/view/FingerprintEnrollEnrollingSfpsFragment.java
+++ b/src/com/android/settings/biometrics2/ui/view/FingerprintEnrollEnrollingSfpsFragment.java
@@ -16,15 +16,20 @@
package com.android.settings.biometrics2.ui.view;
+import static android.hardware.fingerprint.FingerprintManager.ENROLL_ENROLL;
+
+import android.animation.Animator;
+import android.animation.ObjectAnimator;
import android.annotation.RawRes;
import android.app.Activity;
import android.content.Context;
+import android.content.res.ColorStateList;
import android.content.res.Configuration;
import android.graphics.PorterDuff;
+import android.graphics.PorterDuffColorFilter;
import android.graphics.drawable.Animatable2;
import android.graphics.drawable.AnimatedVectorDrawable;
import android.graphics.drawable.Drawable;
-import android.graphics.drawable.LayerDrawable;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
@@ -39,11 +44,14 @@
import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentActivity;
+import androidx.lifecycle.Observer;
import androidx.lifecycle.ViewModelProvider;
+import androidx.transition.Transition;
+import androidx.transition.TransitionSet;
import com.android.settings.R;
-import com.android.settings.biometrics.BiometricUtils;
import com.android.settings.biometrics2.ui.model.EnrollmentProgress;
+import com.android.settings.biometrics2.ui.model.EnrollmentStatusMessage;
import com.android.settings.biometrics2.ui.viewmodel.DeviceRotationViewModel;
import com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollEnrollingViewModel;
import com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollProgressViewModel;
@@ -51,6 +59,8 @@
import com.airbnb.lottie.LottieAnimationView;
import com.airbnb.lottie.LottieCompositionFactory;
+import com.airbnb.lottie.LottieProperty;
+import com.airbnb.lottie.model.KeyPath;
import com.google.android.setupcompat.template.FooterBarMixin;
import com.google.android.setupcompat.template.FooterButton;
import com.google.android.setupdesign.GlifLayout;
@@ -64,9 +74,9 @@
private static final String TAG = FingerprintEnrollEnrollingSfpsFragment.class.getSimpleName();
+ private static final int PROGRESS_BAR_MAX = 10000;
private static final long ICON_TOUCH_DURATION_UNTIL_DIALOG_SHOWN = 500;
private static final int ICON_TOUCH_COUNT_SHOW_UNTIL_DIALOG_SHOWN = 3;
- private static final int HINT_TIMEOUT_DURATION = 2500;
private static final int STAGE_UNKNOWN = -1;
private static final int SFPS_STAGE_NO_ANIMATION = 0;
@@ -84,8 +94,9 @@
private Interpolator mFastOutLinearInInterpolator;
private boolean mAnimationCancelled;
- private View mView;
+ private GlifLayout mView;
private ProgressBar mProgressBar;
+ private ObjectAnimator mProgressAnim;
private TextView mErrorText;
private FooterBarMixin mFooterBarMixin;
private AnimatedVectorDrawable mIconAnimationDrawable;
@@ -103,6 +114,24 @@
private final View.OnClickListener mOnSkipClickListener =
(v) -> mEnrollingViewModel.onSkipButtonClick();
+ private final Observer<EnrollmentProgress> mProgressObserver = progress -> {
+ // TODO
+ };
+ private final Observer<EnrollmentStatusMessage> mHelpMessageObserver = helpMessage -> {
+ // TODO
+ };
+ private final Observer<EnrollmentStatusMessage> mErrorMessageObserver = errorMessage -> {
+ // TODO
+ };
+ private final Observer<Boolean> mAcquireObserver = isAcquiredGood -> {
+ // TODO
+ };
+ private final Observer<Integer> mPointerDownObserver = sensorId -> {
+ // TODO
+ };
+ private final Observer<Integer> mPointerUpObserver = sensorId -> {
+ // TODO
+ };
private int mIconTouchCount;
@@ -114,41 +143,73 @@
mRotationViewModel = provider.get(DeviceRotationViewModel.class);
mProgressViewModel = provider.get(FingerprintEnrollProgressViewModel.class);
super.onAttach(context);
+ final TransitionSet transitionSet = (TransitionSet) getSharedElementEnterTransition();
+ if (transitionSet != null) {
+ transitionSet.addListener(new Transition.TransitionListener() {
+ @Override
+ public void onTransitionStart(@NonNull Transition transition) {
+
+ }
+
+ @Override
+ public void onTransitionEnd(@NonNull Transition transition) {
+ transition.removeListener(this);
+ mAnimationCancelled = false;
+ startIconAnimation();
+ }
+
+ @Override
+ public void onTransitionCancel(@NonNull Transition transition) {
+
+ }
+
+ @Override
+ public void onTransitionPause(@NonNull Transition transition) {
+
+ }
+
+ @Override
+ public void onTransitionResume(@NonNull Transition transition) {
+
+ }
+ });
+ }
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
+ mEnrollingViewModel.restoreSavedState(savedInstanceState);
mIsAccessibilityEnabled = mEnrollingViewModel.isAccessibilityEnabled();
}
@Override
+ public void onSaveInstanceState(@NonNull Bundle outState) {
+ mEnrollingViewModel.onSaveInstanceState(outState);
+ super.onSaveInstanceState(outState);
+ }
+
+ @Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
mView = initSfpsLayout(inflater, container);
- final Configuration config = getActivity().getResources().getConfiguration();
- maybeHideSfpsText(config);
return mView;
}
- private View initSfpsLayout(LayoutInflater inflater, ViewGroup container) {
- final View containView = inflater.inflate(R.layout.sfps_enroll_enrolling, container, false);
+ private GlifLayout initSfpsLayout(LayoutInflater inflater, ViewGroup container) {
+ final GlifLayout containView = (GlifLayout) inflater.inflate(R.layout.sfps_enroll_enrolling,
+ container, false);
final Activity activity = getActivity();
- final GlifLayoutHelper glifLayoutHelper = new GlifLayoutHelper(activity,
- (GlifLayout) containView);
- glifLayoutHelper.setDescriptionText(
- R.string.security_settings_fingerprint_enroll_start_message);
- updateTitleAndDescription();
- mShouldShowLottie = shouldShowLottie();
- boolean isLandscape = BiometricUtils.isReverseLandscape(activity)
- || BiometricUtils.isLandscape(activity);
- updateOrientation((isLandscape
- ? Configuration.ORIENTATION_LANDSCAPE : Configuration.ORIENTATION_PORTRAIT));
+ new GlifLayoutHelper(activity, containView).setDescriptionText(
+ getString(R.string.security_settings_fingerprint_enroll_start_message));
+
+ mShouldShowLottie = shouldShowLottie(); // Move shouldShowLottie into updateOrientation()?
+ mIllustrationLottie = containView.findViewById(R.id.illustration_lottie);
mErrorText = containView.findViewById(R.id.error_text);
mProgressBar = containView.findViewById(R.id.fingerprint_progress_bar);
- mFooterBarMixin = ((GlifLayout) containView).getMixin(FooterBarMixin.class);
+ mFooterBarMixin = containView.getMixin(FooterBarMixin.class);
mFooterBarMixin.setSecondaryButton(
new FooterButton.Builder(activity)
.setText(R.string.security_settings_fingerprint_enroll_enrolling_skip)
@@ -158,16 +219,6 @@
.build()
);
- final LayerDrawable fingerprintDrawable = mProgressBar != null
- ? (LayerDrawable) mProgressBar.getBackground() : null;
- if (fingerprintDrawable != null) {
- mIconAnimationDrawable = (AnimatedVectorDrawable)
- fingerprintDrawable.findDrawableByLayerId(R.id.fingerprint_animation);
- mIconBackgroundBlinksDrawable = (AnimatedVectorDrawable)
- fingerprintDrawable.findDrawableByLayerId(R.id.fingerprint_background);
- mIconAnimationDrawable.registerAnimationCallback(mIconAnimationCallback);
- }
-
mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(
activity, android.R.interpolator.fast_out_slow_in);
mLinearOutSlowInInterpolator = AnimationUtils.loadInterpolator(
@@ -175,37 +226,161 @@
mFastOutLinearInInterpolator = AnimationUtils.loadInterpolator(
activity, android.R.interpolator.fast_out_linear_in);
- if (mProgressBar != null) {
- mProgressBar.setProgressBackgroundTintMode(PorterDuff.Mode.SRC);
- mProgressBar.setOnTouchListener((v, event) -> {
- if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
- mIconTouchCount++;
- if (mIconTouchCount == ICON_TOUCH_COUNT_SHOW_UNTIL_DIALOG_SHOWN) {
- showIconTouchDialog();
- } else {
- mProgressBar.postDelayed(mShowDialogRunnable,
- ICON_TOUCH_DURATION_UNTIL_DIALOG_SHOWN);
- }
- } else if (event.getActionMasked() == MotionEvent.ACTION_CANCEL
- || event.getActionMasked() == MotionEvent.ACTION_UP) {
- mProgressBar.removeCallbacks(mShowDialogRunnable);
+ mProgressBar.setProgressBackgroundTintMode(PorterDuff.Mode.SRC);
+ mProgressBar.setOnTouchListener((v, event) -> {
+ if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
+ mIconTouchCount++;
+ if (mIconTouchCount == ICON_TOUCH_COUNT_SHOW_UNTIL_DIALOG_SHOWN) {
+ showIconTouchDialog();
+ } else {
+ mProgressBar.postDelayed(mShowDialogRunnable,
+ ICON_TOUCH_DURATION_UNTIL_DIALOG_SHOWN);
}
- return true;
- });
- }
+ } else if (event.getActionMasked() == MotionEvent.ACTION_CANCEL
+ || event.getActionMasked() == MotionEvent.ACTION_UP) {
+ mProgressBar.removeCallbacks(mShowDialogRunnable);
+ }
+ return true;
+ });
+ maybeHideSfpsText(activity.getResources().getConfiguration());
return containView;
}
- private void updateTitleAndDescription() {
+ @Override
+ public void onStart() {
+ super.onStart();
+ startEnrollment();
+ updateProgress(false /* animate */);
+ updateTitleAndDescription(new GlifLayoutHelper(getActivity(), mView));
+ if (true /* TODO mRestoring */) {
+ startIconAnimation();
+ }
+ }
- final Activity activity = getActivity();
- final GlifLayoutHelper glifLayoutHelper = new GlifLayoutHelper(activity,
- (GlifLayout) mView);
+ @Override
+ public void onStop() {
+ stopIconAnimation();
+ mProgressViewModel.getProgressLiveData().removeObserver(mProgressObserver);
+ mProgressViewModel.getHelpMessageLiveData().removeObserver(mHelpMessageObserver);
+ mProgressViewModel.getErrorMessageLiveData().removeObserver(mErrorMessageObserver);
+ mProgressViewModel.getAcquireLiveData().removeObserver(mAcquireObserver);
+ mProgressViewModel.getPointerDownLiveData().removeObserver(mPointerDownObserver);
+ mProgressViewModel.getPointerUpLiveData().removeObserver(mPointerUpObserver);
+ if (!getActivity().isChangingConfigurations()) {
+ mProgressViewModel.cancelEnrollment();
+ }
+ super.onStop();
+ }
+ private void startEnrollment() {
+ mProgressViewModel.getProgressLiveData().observe(this, mProgressObserver);
+ mProgressViewModel.getHelpMessageLiveData().observe(this, mHelpMessageObserver);
+ mProgressViewModel.getErrorMessageLiveData().observe(this, mErrorMessageObserver);
+ mProgressViewModel.getAcquireLiveData().observe(this, mAcquireObserver);
+ mProgressViewModel.getPointerDownLiveData().observe(this, mPointerDownObserver);
+ mProgressViewModel.getPointerUpLiveData().observe(this, mPointerUpObserver);
+ mProgressViewModel.startEnrollment(ENROLL_ENROLL);
+ }
+
+ private void updateProgress(boolean animate) {
+ if (!mProgressViewModel.isEnrolling()) {
+ Log.d(TAG, "Enrollment not started yet");
+ return;
+ }
+
+ final EnrollmentProgress enrollmentProgress =
+ mProgressViewModel.getProgressLiveData().getValue();
+ final int progress = getProgress(enrollmentProgress);
+ // Only clear the error when progress has been made.
+ // TODO (b/234772728) Add tests.
+ if (mProgressBar != null && mProgressBar.getProgress() < progress) {
+ clearError();
+ }
+
+ if (animate) {
+ animateProgress(progress);
+ } else {
+ if (mProgressBar != null) {
+ mProgressBar.setProgress(progress);
+ }
+ if (progress >= PROGRESS_BAR_MAX) {
+ mDelayedFinishRunnable.run();
+ }
+ }
+ }
+
+ private int getProgress(@NonNull EnrollmentProgress progress) {
+ if (progress.getSteps() == -1) {
+ return 0;
+ }
+ int displayProgress = Math.max(0, progress.getSteps() + 1 - progress.getRemaining());
+ return PROGRESS_BAR_MAX * displayProgress / (progress.getSteps() + 1);
+ }
+
+ private void clearError() {
+ applySfpsErrorDynamicColors(false);
+ }
+
+ /**
+ * Applies dynamic colors corresponding to showing or clearing errors on the progress bar
+ * and finger lottie for SFPS
+ */
+ private void applySfpsErrorDynamicColors(boolean isError) {
+ applyProgressBarDynamicColor(isError);
+ if (mIllustrationLottie != null) {
+ applyLottieDynamicColor(isError);
+ }
+ }
+
+ private void applyProgressBarDynamicColor(boolean isError) {
+ final Context context = getActivity().getApplicationContext();
+ int error_color = context.getColor(R.color.sfps_enrollment_progress_bar_error_color);
+ int progress_bar_fill_color = context.getColor(
+ R.color.sfps_enrollment_progress_bar_fill_color);
+ ColorStateList fillColor = ColorStateList.valueOf(
+ isError ? error_color : progress_bar_fill_color);
+ mProgressBar.setProgressTintList(fillColor);
+ mProgressBar.setProgressTintMode(PorterDuff.Mode.SRC);
+ mProgressBar.invalidate();
+ }
+
+ private void applyLottieDynamicColor(boolean isError) {
+ final Context context = getActivity().getApplicationContext();
+ int error_color = context.getColor(R.color.sfps_enrollment_fp_error_color);
+ int fp_captured_color = context.getColor(R.color.sfps_enrollment_fp_captured_color);
+ int color = isError ? error_color : fp_captured_color;
+ mIllustrationLottie.addValueCallback(
+ new KeyPath(".blue100", "**"),
+ LottieProperty.COLOR_FILTER,
+ frameInfo -> new PorterDuffColorFilter(color, PorterDuff.Mode.SRC_ATOP)
+ );
+ mIllustrationLottie.invalidate();
+ }
+
+ @Override
+ public void onDestroy() {
+ // TODO stopListenOrientationEvent();
+ super.onDestroy();
+ }
+
+ private void animateProgress(int progress) {
+ if (mProgressAnim != null) {
+ mProgressAnim.cancel();
+ }
+ ObjectAnimator anim = ObjectAnimator.ofInt(mProgressBar, "progress",
+ mProgressBar.getProgress(), progress);
+ anim.addListener(mProgressAnimationListener);
+ anim.setInterpolator(mFastOutSlowInInterpolator);
+ anim.setDuration(250);
+ anim.start();
+ mProgressAnim = anim;
+ }
+
+ private void updateTitleAndDescription(@NonNull GlifLayoutHelper glifLayoutHelper) {
if (mIsAccessibilityEnabled) {
mEnrollingViewModel.clearTalkback();
- ((GlifLayout) mView).getDescriptionTextView().setAccessibilityLiveRegion(
+ glifLayoutHelper.getGlifLayout().getDescriptionTextView().setAccessibilityLiveRegion(
View.ACCESSIBILITY_LIVE_REGION_POLITE);
}
switch (getCurrentSfpsStage()) {
@@ -273,12 +448,13 @@
// announce a different string for a11y upon entering the page.
glifLayoutHelper.setHeaderText(
R.string.security_settings_sfps_enroll_find_sensor_title);
- glifLayoutHelper.setDescriptionText(
- R.string.security_settings_sfps_enroll_start_message);
+ glifLayoutHelper.setDescriptionText(getString(
+ R.string.security_settings_sfps_enroll_start_message));
final CharSequence description = getString(
R.string.security_settings_sfps_enroll_find_sensor_message);
- ((GlifLayout) mView).getHeaderTextView().setContentDescription(description);
- activity.setTitle(description);
+ glifLayoutHelper.getGlifLayout().getHeaderTextView().setContentDescription(
+ description);
+ glifLayoutHelper.getActivity().setTitle(description);
break;
}
@@ -327,8 +503,8 @@
}
private int getStageThresholdSteps(int index) {
-
- EnrollmentProgress progressLiveData = mProgressViewModel.getProgressLiveData().getValue();
+ final EnrollmentProgress progressLiveData =
+ mProgressViewModel.getProgressLiveData().getValue();
if (progressLiveData == null || progressLiveData.getSteps() == -1) {
Log.w(TAG, "getStageThresholdSteps: Enrollment not started yet");
@@ -338,7 +514,7 @@
* mEnrollingViewModel.getEnrollStageThreshold(index));
}
- private void updateOrientation(int orientation) {
+ private void updateOrientation() {
mIllustrationLottie = mView.findViewById(R.id.illustration_lottie);
}
@@ -372,9 +548,7 @@
}
private void configureEnrollmentStage(CharSequence description, @RawRes int lottie) {
- final GlifLayoutHelper glifLayoutHelper = new GlifLayoutHelper(getActivity(),
- (GlifLayout) mView);
- glifLayoutHelper.setDescriptionText(description);
+ new GlifLayoutHelper(getActivity(), mView).setDescriptionText(description);
LottieCompositionFactory.fromRawRes(getActivity(), lottie)
.addListener((c) -> {
mIllustrationLottie.setComposition(c);
@@ -390,6 +564,38 @@
}
};
+ private final Animator.AnimatorListener mProgressAnimationListener =
+ new Animator.AnimatorListener() {
+
+ @Override
+ public void onAnimationStart(Animator animation) {
+ startIconAnimation();
+ }
+
+ @Override
+ public void onAnimationRepeat(Animator animation) { }
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ stopIconAnimation();
+
+ if (mProgressBar.getProgress() >= PROGRESS_BAR_MAX) {
+ mProgressBar.postDelayed(mDelayedFinishRunnable, 250L);
+ }
+ }
+
+ @Override
+ public void onAnimationCancel(Animator animation) { }
+ };
+
+ // Give the user a chance to see progress completed before jumping to the next stage.
+ private final Runnable mDelayedFinishRunnable = new Runnable() {
+ @Override
+ public void run() {
+ /* TODO launchFinish(); */
+ }
+ };
+
private final Animatable2.AnimationCallback mIconAnimationCallback =
new Animatable2.AnimationCallback() {
@Override
diff --git a/src/com/android/settings/biometrics2/ui/view/FingerprintEnrollEnrollingUdfpsFragment.java b/src/com/android/settings/biometrics2/ui/view/FingerprintEnrollEnrollingUdfpsFragment.java
index 89b061f..18a7c25 100644
--- a/src/com/android/settings/biometrics2/ui/view/FingerprintEnrollEnrollingUdfpsFragment.java
+++ b/src/com/android/settings/biometrics2/ui/view/FingerprintEnrollEnrollingUdfpsFragment.java
@@ -16,37 +16,39 @@
package com.android.settings.biometrics2.ui.view;
+import static android.hardware.fingerprint.FingerprintManager.ENROLL_ENROLL;
+
import android.annotation.RawRes;
import android.app.Activity;
import android.content.Context;
import android.content.res.Configuration;
-import android.graphics.PorterDuff;
import android.graphics.drawable.Animatable2;
import android.graphics.drawable.AnimatedVectorDrawable;
import android.graphics.drawable.Drawable;
-import android.graphics.drawable.LayerDrawable;
import android.os.Bundle;
import android.text.TextUtils;
import android.util.Log;
import android.view.LayoutInflater;
-import android.view.MotionEvent;
import android.view.Surface;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.AnimationUtils;
import android.view.animation.Interpolator;
import android.widget.LinearLayout;
-import android.widget.ProgressBar;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentActivity;
+import androidx.lifecycle.Observer;
import androidx.lifecycle.ViewModelProvider;
+import androidx.transition.Transition;
+import androidx.transition.TransitionSet;
import com.android.settings.R;
import com.android.settings.biometrics.BiometricUtils;
import com.android.settings.biometrics2.ui.model.EnrollmentProgress;
+import com.android.settings.biometrics2.ui.model.EnrollmentStatusMessage;
import com.android.settings.biometrics2.ui.viewmodel.DeviceRotationViewModel;
import com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollEnrollingViewModel;
import com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollProgressViewModel;
@@ -67,6 +69,7 @@
private static final String TAG = FingerprintEnrollEnrollingUdfpsFragment.class.getSimpleName();
+ private static final int PROGRESS_BAR_MAX = 10000;
private static final long ICON_TOUCH_DURATION_UNTIL_DIALOG_SHOWN = 500;
private static final int ICON_TOUCH_COUNT_SHOW_UNTIL_DIALOG_SHOWN = 3;
private static final int HINT_TIMEOUT_DURATION = 2500;
@@ -94,8 +97,7 @@
private boolean mHaveShownUdfpsCenterLottie;
private boolean mHaveShownUdfpsGuideLottie;
- private View mView;
- private ProgressBar mProgressBar;
+ private GlifLayout mView;
private TextView mErrorText;
private FooterBarMixin mFooterBarMixin;
private AnimatedVectorDrawable mIconAnimationDrawable;
@@ -106,6 +108,24 @@
private final View.OnClickListener mOnSkipClickListener =
(v) -> mEnrollingViewModel.onSkipButtonClick();
+ private final Observer<EnrollmentProgress> mProgressObserver = progress -> {
+ // TODO
+ };
+ private final Observer<EnrollmentStatusMessage> mHelpMessageObserver = helpMessage -> {
+ // TODO
+ };
+ private final Observer<EnrollmentStatusMessage> mErrorMessageObserver = errorMessage -> {
+ // TODO
+ };
+ private final Observer<Boolean> mAcquireObserver = isAcquiredGood -> {
+ // TODO
+ };
+ private final Observer<Integer> mPointerDownObserver = sensorId -> {
+ // TODO
+ };
+ private final Observer<Integer> mPointerUpObserver = sensorId -> {
+ // TODO
+ };
private int mIconTouchCount;
@@ -117,61 +137,98 @@
mRotationViewModel = provider.get(DeviceRotationViewModel.class);
mProgressViewModel = provider.get(FingerprintEnrollProgressViewModel.class);
super.onAttach(context);
+ final TransitionSet transitionSet = (TransitionSet) getSharedElementEnterTransition();
+ if (transitionSet != null) {
+ transitionSet.addListener(new Transition.TransitionListener() {
+ @Override
+ public void onTransitionStart(@NonNull Transition transition) {
+
+ }
+
+ @Override
+ public void onTransitionEnd(@NonNull Transition transition) {
+ transition.removeListener(this);
+ startEnrollment();
+ mAnimationCancelled = false;
+ startIconAnimation();
+ }
+
+ @Override
+ public void onTransitionCancel(@NonNull Transition transition) {
+
+ }
+
+ @Override
+ public void onTransitionPause(@NonNull Transition transition) {
+
+ }
+
+ @Override
+ public void onTransitionResume(@NonNull Transition transition) {
+
+ }
+ });
+ }
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
+ mEnrollingViewModel.restoreSavedState(savedInstanceState);
mIsAccessibilityEnabled = mEnrollingViewModel.isAccessibilityEnabled();
}
@Override
+ public void onSaveInstanceState(@NonNull Bundle outState) {
+ mEnrollingViewModel.onSaveInstanceState(outState);
+ super.onSaveInstanceState(outState);
+ }
+
+ @Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
mView = initUdfpsLayout(inflater, container);
return mView;
}
- private View initUdfpsLayout(LayoutInflater inflater, ViewGroup container) {
- final View containView = inflater.inflate(R.layout.udfps_enroll_enrolling, container,
- false);
+ private GlifLayout initUdfpsLayout(LayoutInflater inflater, ViewGroup container) {
+ final GlifLayout containView = (GlifLayout) inflater.inflate(
+ R.layout.udfps_enroll_enrolling, container, false);
- final Activity activity = getActivity();
- final GlifLayoutHelper glifLayoutHelper = new GlifLayoutHelper(activity,
- (GlifLayout) containView);
final int rotation = mRotationViewModel.getLiveData().getValue();
- final boolean isLayoutRtl = (TextUtils.getLayoutDirectionFromLocale(
- Locale.getDefault()) == View.LAYOUT_DIRECTION_RTL);
-
-
- //TODO implement b/20653554
if (rotation == Surface.ROTATION_90) {
+ final boolean isLayoutRtl = (TextUtils.getLayoutDirectionFromLocale(
+ Locale.getDefault()) == View.LAYOUT_DIRECTION_RTL);
final LinearLayout layoutContainer = containView.findViewById(
R.id.layout_container);
+ layoutContainer.setPaddingRelative(
+ (int) getResources().getDimension(R.dimen.rotation_90_enroll_padding_start),
+ 0,
+ isLayoutRtl ? 0 : (int) getResources().getDimension(
+ R.dimen.rotation_90_enroll_padding_end),
+ 0);
final LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT,
LinearLayout.LayoutParams.MATCH_PARENT);
lp.setMarginEnd((int) getResources().getDimension(
R.dimen.rotation_90_enroll_margin_end));
- layoutContainer.setPaddingRelative((int) getResources().getDimension(
- R.dimen.rotation_90_enroll_padding_start), 0, isLayoutRtl
- ? 0 : (int) getResources().getDimension(
- R.dimen.rotation_90_enroll_padding_end), 0);
layoutContainer.setLayoutParams(lp);
containView.setLayoutParams(lp);
}
- glifLayoutHelper.setDescriptionText(R.string.security_settings_udfps_enroll_start_message);
+
+ final Activity activity = getActivity();
+ new GlifLayoutHelper(activity, containView).setDescriptionText(
+ getString(R.string.security_settings_udfps_enroll_start_message));
updateTitleAndDescription();
mShouldShowLottie = shouldShowLottie();
boolean isLandscape = BiometricUtils.isReverseLandscape(activity)
|| BiometricUtils.isLandscape(activity);
- updateOrientation((isLandscape
+ updateOrientation(containView, (isLandscape
? Configuration.ORIENTATION_LANDSCAPE : Configuration.ORIENTATION_PORTRAIT));
mErrorText = containView.findViewById(R.id.error_text);
- mProgressBar = containView.findViewById(R.id.fingerprint_progress_bar);
- mFooterBarMixin = ((GlifLayout) containView).getMixin(FooterBarMixin.class);
+ mFooterBarMixin = containView.getMixin(FooterBarMixin.class);
mFooterBarMixin.setSecondaryButton(
new FooterButton.Builder(activity)
.setText(R.string.security_settings_fingerprint_enroll_enrolling_skip)
@@ -181,16 +238,6 @@
.build()
);
- final LayerDrawable fingerprintDrawable = mProgressBar != null
- ? (LayerDrawable) mProgressBar.getBackground() : null;
- if (fingerprintDrawable != null) {
- mIconAnimationDrawable = (AnimatedVectorDrawable)
- fingerprintDrawable.findDrawableByLayerId(R.id.fingerprint_animation);
- mIconBackgroundBlinksDrawable = (AnimatedVectorDrawable)
- fingerprintDrawable.findDrawableByLayerId(R.id.fingerprint_background);
- mIconAnimationDrawable.registerAnimationCallback(mIconAnimationCallback);
- }
-
mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(
activity, android.R.interpolator.fast_out_slow_in);
mLinearOutSlowInInterpolator = AnimationUtils.loadInterpolator(
@@ -198,41 +245,100 @@
mFastOutLinearInInterpolator = AnimationUtils.loadInterpolator(
activity, android.R.interpolator.fast_out_linear_in);
- if (mProgressBar != null) {
- mProgressBar.setProgressBackgroundTintMode(PorterDuff.Mode.SRC);
- mProgressBar.setOnTouchListener((v, event) -> {
- if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
- mIconTouchCount++;
- if (mIconTouchCount == ICON_TOUCH_COUNT_SHOW_UNTIL_DIALOG_SHOWN) {
- showIconTouchDialog();
- } else {
- mProgressBar.postDelayed(mShowDialogRunnable,
- ICON_TOUCH_DURATION_UNTIL_DIALOG_SHOWN);
- }
- } else if (event.getActionMasked() == MotionEvent.ACTION_CANCEL
- || event.getActionMasked() == MotionEvent.ACTION_UP) {
- mProgressBar.removeCallbacks(mShowDialogRunnable);
- }
- return true;
- });
+ return containView;
+ }
+
+ @Override
+ public void onStart() {
+ super.onStart();
+ if (true /* TODO mRestoring && !mIsCanceled */) {
+ startEnrollment();
+ }
+ updateProgress(false /* animate */, mProgressViewModel.getProgressLiveData().getValue());
+ updateTitleAndDescription();
+ if (true /* TODO mRestoring */) {
+ startIconAnimation();
+ }
+ }
+
+ @Override
+ public void onStop() {
+ stopIconAnimation();
+ removeEnrollmentObserver();
+ if (!getActivity().isChangingConfigurations()) {
+ mProgressViewModel.cancelEnrollment();
+ }
+ super.onStop();
+ }
+
+ private void removeEnrollmentObserver() {
+ mProgressViewModel.getProgressLiveData().removeObserver(mProgressObserver);
+ mProgressViewModel.getHelpMessageLiveData().removeObserver(mHelpMessageObserver);
+ mProgressViewModel.getErrorMessageLiveData().removeObserver(mErrorMessageObserver);
+ mProgressViewModel.getAcquireLiveData().removeObserver(mAcquireObserver);
+ mProgressViewModel.getPointerDownLiveData().removeObserver(mPointerDownObserver);
+ mProgressViewModel.getPointerUpLiveData().removeObserver(mPointerUpObserver);
+ }
+
+ private void startEnrollment() {
+ mProgressViewModel.getProgressLiveData().observe(this, mProgressObserver);
+ mProgressViewModel.getHelpMessageLiveData().observe(this, mHelpMessageObserver);
+ mProgressViewModel.getErrorMessageLiveData().observe(this, mErrorMessageObserver);
+ mProgressViewModel.getAcquireLiveData().observe(this, mAcquireObserver);
+ mProgressViewModel.getPointerDownLiveData().observe(this, mPointerDownObserver);
+ mProgressViewModel.getPointerUpLiveData().observe(this, mPointerUpObserver);
+ mProgressViewModel.startEnrollment(ENROLL_ENROLL);
+ }
+
+ private void updateProgress(boolean animate, @NonNull EnrollmentProgress enrollmentProgress) {
+ if (!mProgressViewModel.isEnrolling()) {
+ Log.d(TAG, "Enrollment not started yet");
+ return;
}
- return containView;
+ final int progress = getProgress(enrollmentProgress);
+
+ if (animate) {
+ animateProgress(progress);
+ } else if (progress >= PROGRESS_BAR_MAX) {
+ mDelayedFinishRunnable.run();
+ }
+ }
+
+ private int getProgress(@NonNull EnrollmentProgress progress) {
+ if (progress.getSteps() == -1) {
+ return 0;
+ }
+ int displayProgress = Math.max(0, progress.getSteps() + 1 - progress.getRemaining());
+ return PROGRESS_BAR_MAX * displayProgress / (progress.getSteps() + 1);
+ }
+
+ @Override
+ public void onDestroy() {
+ // TODO stopListenOrientationEvent();
+ super.onDestroy();
+ }
+
+ private void animateProgress(int progress) {
+ // UDFPS animations are owned by SystemUI
+ if (progress >= PROGRESS_BAR_MAX) {
+ // Wait for any animations in SysUI to finish, then proceed to next page
+ getActivity().getMainThreadHandler().postDelayed(mDelayedFinishRunnable, 400L);
+ }
}
private void updateTitleAndDescription() {
final Activity activity = getActivity();
- final GlifLayoutHelper glifLayoutHelper = new GlifLayoutHelper(activity,
- (GlifLayout) mView);
+ final GlifLayoutHelper glifLayoutHelper = new GlifLayoutHelper(activity, mView);
switch (getCurrentStage()) {
case STAGE_CENTER:
glifLayoutHelper.setHeaderText(
R.string.security_settings_fingerprint_enroll_repeat_title);
if (mIsAccessibilityEnabled || mIllustrationLottie == null) {
- glifLayoutHelper.setDescriptionText(
- R.string.security_settings_udfps_enroll_start_message);
+ glifLayoutHelper.setDescriptionText(getString(
+ R.string.security_settings_udfps_enroll_start_message));
} else if (!mHaveShownUdfpsCenterLottie && mIllustrationLottie != null) {
mHaveShownUdfpsCenterLottie = true;
// Note: Update string reference when differentiate in between udfps & sfps
@@ -247,8 +353,8 @@
glifLayoutHelper.setHeaderText(
R.string.security_settings_fingerprint_enroll_repeat_title);
if (mIsAccessibilityEnabled || mIllustrationLottie == null) {
- glifLayoutHelper.setDescriptionText(
- R.string.security_settings_udfps_enroll_repeat_a11y_message);
+ glifLayoutHelper.setDescriptionText(getString(
+ R.string.security_settings_udfps_enroll_repeat_a11y_message));
} else if (!mHaveShownUdfpsGuideLottie && mIllustrationLottie != null) {
mHaveShownUdfpsGuideLottie = true;
mIllustrationLottie.setContentDescription(
@@ -280,11 +386,11 @@
configureEnrollmentStage("", R.raw.udfps_left_edge_hint_lottie);
} else if (mIllustrationLottie == null) {
if (isStageHalfCompleted()) {
- glifLayoutHelper.setDescriptionText(
- R.string.security_settings_fingerprint_enroll_repeat_message);
+ glifLayoutHelper.setDescriptionText(getString(
+ R.string.security_settings_fingerprint_enroll_repeat_message));
} else {
- glifLayoutHelper.setDescriptionText(
- R.string.security_settings_udfps_enroll_edge_message);
+ glifLayoutHelper.setDescriptionText(getString(
+ R.string.security_settings_udfps_enroll_edge_message));
}
}
break;
@@ -300,11 +406,11 @@
} else if (mIllustrationLottie == null) {
if (isStageHalfCompleted()) {
- glifLayoutHelper.setDescriptionText(
- R.string.security_settings_fingerprint_enroll_repeat_message);
+ glifLayoutHelper.setDescriptionText(getString(
+ R.string.security_settings_fingerprint_enroll_repeat_message));
} else {
- glifLayoutHelper.setDescriptionText(
- R.string.security_settings_udfps_enroll_edge_message);
+ glifLayoutHelper.setDescriptionText(getString(
+ R.string.security_settings_udfps_enroll_edge_message));
}
}
break;
@@ -317,11 +423,11 @@
// announce a different string for a11y upon entering the page.
glifLayoutHelper.setHeaderText(
R.string.security_settings_fingerprint_enroll_udfps_title);
- glifLayoutHelper.setDescriptionText(
- R.string.security_settings_udfps_enroll_start_message);
+ glifLayoutHelper.setDescriptionText(getString(
+ R.string.security_settings_udfps_enroll_start_message));
final CharSequence description = getString(
R.string.security_settings_udfps_enroll_a11y);
- ((GlifLayout) mView).getHeaderTextView().setContentDescription(description);
+ mView.getHeaderTextView().setContentDescription(description);
activity.setTitle(description);
break;
@@ -337,7 +443,7 @@
return defaultDensity == currentDensity;
}
- private void updateOrientation(int orientation) {
+ private void updateOrientation(@NonNull GlifLayout glifLayout, int orientation) {
switch (orientation) {
case Configuration.ORIENTATION_LANDSCAPE: {
mIllustrationLottie = null;
@@ -345,7 +451,7 @@
}
case Configuration.ORIENTATION_PORTRAIT: {
if (mShouldShowLottie) {
- mIllustrationLottie = mView.findViewById(R.id.illustration_lottie);
+ mIllustrationLottie = glifLayout.findViewById(R.id.illustration_lottie);
}
break;
}
@@ -390,7 +496,6 @@
}
private boolean isStageHalfCompleted() {
-
EnrollmentProgress progressLiveData = mProgressViewModel.getProgressLiveData().getValue();
if (progressLiveData == null || progressLiveData.getSteps() == -1) {
return false;
@@ -431,9 +536,7 @@
}
private void configureEnrollmentStage(CharSequence description, @RawRes int lottie) {
- final GlifLayoutHelper glifLayoutHelper = new GlifLayoutHelper(getActivity(),
- (GlifLayout) mView);
- glifLayoutHelper.setDescriptionText(description);
+ new GlifLayoutHelper(getActivity(), mView).setDescriptionText(description);
LottieCompositionFactory.fromRawRes(getActivity(), lottie)
.addListener((c) -> {
mIllustrationLottie.setComposition(c);
@@ -449,6 +552,14 @@
}
};
+ // Give the user a chance to see progress completed before jumping to the next stage.
+ private final Runnable mDelayedFinishRunnable = new Runnable() {
+ @Override
+ public void run() {
+ /* TODO launchFinish(); */
+ }
+ };
+
private final Animatable2.AnimationCallback mIconAnimationCallback =
new Animatable2.AnimationCallback() {
@Override
@@ -458,13 +569,14 @@
}
// Start animation after it has ended.
+ /* TODO check mProgressBar?
mProgressBar.post(new Runnable() {
@Override
public void run() {
startIconAnimation();
}
});
+ */
}
};
-
}
diff --git a/src/com/android/settings/biometrics2/ui/view/FingerprintEnrollFindRfpsFragment.java b/src/com/android/settings/biometrics2/ui/view/FingerprintEnrollFindRfpsFragment.java
index d753784..fd181d4 100644
--- a/src/com/android/settings/biometrics2/ui/view/FingerprintEnrollFindRfpsFragment.java
+++ b/src/com/android/settings/biometrics2/ui/view/FingerprintEnrollFindRfpsFragment.java
@@ -21,6 +21,7 @@
import android.app.Activity;
import android.content.Context;
+import android.hardware.fingerprint.FingerprintManager;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
@@ -37,6 +38,7 @@
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.model.EnrollmentStatusMessage;
import com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollFindSensorViewModel;
import com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollProgressViewModel;
@@ -64,7 +66,7 @@
private static final String TAG = "FingerprintEnrollFindRfpsFragment";
private FingerprintEnrollFindSensorViewModel mViewModel;
- private FingerprintEnrollProgressViewModel mPorgressViewModel;
+ private FingerprintEnrollProgressViewModel mProgressViewModel;
private View mView;
private GlifLayout mGlifLayout;
@@ -77,7 +79,21 @@
Log.d(TAG, "mProgressObserver(" + progress + ")");
}
if (progress != null && !progress.isInitialStep()) {
- mViewModel.onStartButtonClick();
+ stopLookingForFingerprint(true);
+ }
+ };
+
+ private final Observer<EnrollmentStatusMessage> mLastCancelMessageObserver = errorMessage -> {
+ if (DEBUG) {
+ Log.d(TAG, "mErrorMessageObserver(" + errorMessage + ")");
+ }
+ if (errorMessage != null) {
+ if (errorMessage.getMsgId() == FingerprintManager.FINGERPRINT_ERROR_CANCELED) {
+ mProgressViewModel.clearProgressLiveData();
+ mViewModel.onStartButtonClick();
+ } else {
+ Log.e(TAG, "mErrorMessageObserver(" + errorMessage + ")");
+ }
}
};
@@ -151,40 +167,48 @@
@Override
public void onStop() {
super.onStop();
+ final boolean isEnrolling = mProgressViewModel.isEnrolling();
if (DEBUG) {
- Log.d(TAG, "onStop(), stop looking for fingerprint, animation exist:"
+ Log.d(TAG, "onStop(), current enrolling: " + isEnrolling + ", animation exist:"
+ (mAnimation != null));
}
- stopLookingForFingerprint();
+ if (!isEnrolling) {
+ stopLookingForFingerprint(false);
+ }
}
private void startLookingForFingerprint() {
- if (mPorgressViewModel.isEnrolling()) {
+ if (mProgressViewModel.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);
+ 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 (!mPorgressViewModel.isEnrolling()) {
+ private void stopLookingForFingerprint(boolean waitForLastCancelErrMsg) {
+ if (!mProgressViewModel.isEnrolling()) {
Log.d(TAG, "stopLookingForFingerprint(), failed because isEnrolling is false before"
+ " stopping");
return;
}
- mPorgressViewModel.getProgressLiveData().removeObserver(mProgressObserver);
- final boolean cancelResult = mPorgressViewModel.cancelEnrollment();
+ mProgressViewModel.getProgressLiveData().removeObserver(mProgressObserver);
+ final boolean cancelResult = mProgressViewModel.cancelEnrollment();
if (!cancelResult) {
Log.e(TAG, "stopLookingForFingerprint(), failed to cancel enrollment");
}
+
+ if (waitForLastCancelErrMsg) {
+ mProgressViewModel.getErrorMessageLiveData().observe(this,
+ mLastCancelMessageObserver);
+ }
}
@Override
@@ -203,7 +227,7 @@
final FragmentActivity activity = getActivity();
final ViewModelProvider provider = new ViewModelProvider(activity);
mViewModel = provider.get(FingerprintEnrollFindSensorViewModel.class);
- mPorgressViewModel = provider.get(FingerprintEnrollProgressViewModel.class);
+ mProgressViewModel = provider.get(FingerprintEnrollProgressViewModel.class);
super.onAttach(context);
}
}
diff --git a/src/com/android/settings/biometrics2/ui/view/FingerprintEnrollFindSfpsFragment.java b/src/com/android/settings/biometrics2/ui/view/FingerprintEnrollFindSfpsFragment.java
index c363f04..aad503e 100644
--- a/src/com/android/settings/biometrics2/ui/view/FingerprintEnrollFindSfpsFragment.java
+++ b/src/com/android/settings/biometrics2/ui/view/FingerprintEnrollFindSfpsFragment.java
@@ -98,6 +98,7 @@
Log.d(TAG, "mProgressObserver(" + progress + ")");
}
if (progress != null && !progress.isInitialStep()) {
+ mProgressViewModel.clearProgressLiveData();
mViewModel.onStartButtonClick();
}
};
diff --git a/src/com/android/settings/biometrics2/ui/view/FingerprintEnrollFinishFragment.java b/src/com/android/settings/biometrics2/ui/view/FingerprintEnrollFinishFragment.java
index 02aa5f2..3f98d25 100644
--- a/src/com/android/settings/biometrics2/ui/view/FingerprintEnrollFinishFragment.java
+++ b/src/com/android/settings/biometrics2/ui/view/FingerprintEnrollFinishFragment.java
@@ -85,15 +85,15 @@
(GlifLayout) mView);
glifLayoutHelper.setHeaderText(R.string.security_settings_fingerprint_enroll_finish_title);
- glifLayoutHelper.setDescriptionText(
- R.string.security_settings_fingerprint_enroll_finish_v2_message);
+ glifLayoutHelper.setDescriptionText(getString(
+ R.string.security_settings_fingerprint_enroll_finish_v2_message));
final int maxEnrollments = mFingerprintEnrollFinishViewModel.getMaxFingerprints();
final int enrolled = mFingerprintEnrollFinishViewModel.getNumOfEnrolledFingerprintsSize();
if (mCanAssumeSfps) {
if (enrolled < maxEnrollments) {
- glifLayoutHelper.setDescriptionText(R.string
- .security_settings_fingerprint_enroll_finish_v2_add_fingerprint_message);
+ glifLayoutHelper.setDescriptionText(getString(R.string
+ .security_settings_fingerprint_enroll_finish_v2_add_fingerprint_message));
}
}
diff --git a/src/com/android/settings/biometrics2/ui/view/FingerprintEnrollmentActivity.java b/src/com/android/settings/biometrics2/ui/view/FingerprintEnrollmentActivity.java
index dddd641..e12e982 100644
--- a/src/com/android/settings/biometrics2/ui/view/FingerprintEnrollmentActivity.java
+++ b/src/com/android/settings/biometrics2/ui/view/FingerprintEnrollmentActivity.java
@@ -26,6 +26,7 @@
import static com.android.settings.biometrics2.ui.viewmodel.AutoCredentialViewModel.CREDENTIAL_FAIL_NEED_TO_CONFIRM_LOCK;
import static com.android.settings.biometrics2.ui.viewmodel.AutoCredentialViewModel.CREDENTIAL_IS_GENERATING_CHALLENGE;
import static com.android.settings.biometrics2.ui.viewmodel.AutoCredentialViewModel.CREDENTIAL_VALID;
+import static com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollEnrollingViewModel.ErrorDialogData;
import static com.android.settings.biometrics2.ui.viewmodel.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;
@@ -63,8 +64,8 @@
import com.android.settings.R;
import com.android.settings.Utils;
import com.android.settings.biometrics.BiometricEnrollBase;
-import com.android.settings.biometrics.fingerprint.FingerprintEnrollEnrolling;
-import com.android.settings.biometrics.fingerprint.SetupFingerprintEnrollEnrolling;
+import com.android.settings.biometrics.fingerprint.FingerprintEnrollFinish;
+import com.android.settings.biometrics.fingerprint.SetupFingerprintEnrollFinish;
import com.android.settings.biometrics2.data.repository.FingerprintRepository;
import com.android.settings.biometrics2.factory.BiometricsViewModelFactory;
import com.android.settings.biometrics2.ui.model.CredentialModel;
@@ -72,6 +73,7 @@
import com.android.settings.biometrics2.ui.viewmodel.AutoCredentialViewModel;
import com.android.settings.biometrics2.ui.viewmodel.AutoCredentialViewModel.FingerprintChallengeGenerator;
import com.android.settings.biometrics2.ui.viewmodel.DeviceFoldedViewModel;
+import com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollEnrollingViewModel;
import com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollFindSensorViewModel;
import com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollIntroViewModel;
import com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollProgressViewModel;
@@ -88,11 +90,16 @@
private static final boolean DEBUG = false;
private static final String TAG = "FingerprintEnrollmentActivity";
- private static final String INTRO_TAG = "enroll-intro";
- private static final String FIND_UDFPS_TAG = "enroll-find-udfps";
- private static final String FIND_SFPS_TAG = "enroll-find-sfps";
- private static final String FIND_RFPS_TAG = "enroll-find-rfps";
+ private static final String INTRO_TAG = "intro";
+ private static final String FIND_UDFPS_TAG = "find-udfps";
+ private static final String FIND_SFPS_TAG = "find-sfps";
+ private static final String FIND_RFPS_TAG = "find-rfps";
+ private static final String ENROLLING_UDFPS_TAG = "enrolling-udfps";
+ private static final String ENROLLING_SFPS_TAG = "enrolling-sfps";
+ private static final String ENROLLING_RFPS_TAG = "enrolling-rfps";
+ private static final String FINISH_TAG = "finish";
private static final String SKIP_SETUP_FIND_FPS_DIALOG_TAG = "skip-setup-dialog";
+ private static final String ENROLLING_ERROR_DIALOG_TAG = "enrolling-error-dialog";
protected static final int LAUNCH_CONFIRM_LOCK_ACTIVITY = 1;
@@ -115,6 +122,22 @@
onFindSensorAction(action);
}
};
+ private final Observer<Boolean> mEnrollingDoneObserver = isDone -> {
+ if (DEBUG) {
+ Log.d(TAG, "mEnrollingDoneObserver(" + isDone + ")");
+ }
+ if (isDone != null) {
+ onEnrollingDone(isDone);
+ }
+ };
+ private final Observer<ErrorDialogData> mEnrollingErrorDialogObserver = data -> {
+ if (DEBUG) {
+ Log.d(TAG, "mEnrollingErrorDialogObserver(" + data + ")");
+ }
+ if (data != null) {
+ startEnrollingErrorDialog();
+ }
+ };
private final ActivityResultCallback<ActivityResult> mNextActivityResultCallback =
result -> mViewModel.onContinueEnrollActivityResult(result,
mAutoCredentialViewModel.getUserId());
@@ -154,6 +177,9 @@
} else {
final FragmentManager manager = getSupportFragmentManager();
String[] tags = new String[] {
+ ENROLLING_UDFPS_TAG,
+ ENROLLING_SFPS_TAG,
+ ENROLLING_RFPS_TAG,
FIND_UDFPS_TAG,
FIND_SFPS_TAG,
FIND_RFPS_TAG,
@@ -169,7 +195,12 @@
}
if (tag.equals(INTRO_TAG)) {
attachIntroViewModel();
- } else { // FIND_UDFPS_TAG, FIND_SFPS_TAG, FIND_RFPS_TAG
+ } else if (tag.equals(FIND_UDFPS_TAG) || tag.equals(FIND_SFPS_TAG)
+ || tag.equals(FIND_RFPS_TAG)) {
+ attachFindSensorViewModel();
+ attachIntroViewModel();
+ } else { // ENROLLING_UDFPS_TAG, ENROLLING_SFPS_TAG, ENROLLING_RFPS_TAG
+ attachEnrollingViewModel();
attachFindSensorViewModel();
attachIntroViewModel();
}
@@ -205,35 +236,29 @@
// 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 */);
- }
- }
+ // Always setToken into progressViewModel even it is not necessary action for UDFPS
+ mViewModelProvider.get(FingerprintEnrollProgressViewModel.class)
+ .setToken(mAutoCredentialViewModel.getToken());
- private void startFindFpsFragmentWithProgressViewModel(
- @NonNull Class<? extends Fragment> findFpsClass, @NonNull String tag,
- boolean initProgressViewModel) {
- if (initProgressViewModel) {
- final FingerprintEnrollProgressViewModel progressViewModel =
- mViewModelProvider.get(FingerprintEnrollProgressViewModel.class);
- progressViewModel.setToken(mAutoCredentialViewModel.getToken());
+ attachFindSensorViewModel();
+
+ final String tag;
+ final Class<? extends Fragment> fragmentClass;
+ if (mViewModel.canAssumeUdfps()) {
+ tag = FIND_UDFPS_TAG;
+ fragmentClass = FingerprintEnrollFindUdfpsFragment.class;
+ } else if (mViewModel.canAssumeSfps()) {
+ tag = FIND_SFPS_TAG;
+ fragmentClass = FingerprintEnrollFindSfpsFragment.class;
+ } else {
+ tag = FIND_RFPS_TAG;
+ fragmentClass = FingerprintEnrollFindRfpsFragment.class;
}
- final FingerprintEnrollFindSensorViewModel findSensorViewModel =
- mViewModelProvider.get(FingerprintEnrollFindSensorViewModel.class);
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)
+ .replace(R.id.fragment_container_view, fragmentClass, null, tag)
.addToBackStack(tag)
.commit();
}
@@ -248,11 +273,68 @@
findSensorViewModel.getActionLiveData().observe(this, mFindSensorActionObserver);
}
+ private void startEnrollingFragment() {
+ // Always setToken into progressViewModel even it is not necessary action for SFPS or RFPS
+ mViewModelProvider.get(FingerprintEnrollProgressViewModel.class)
+ .setToken(mAutoCredentialViewModel.getToken());
+
+ attachEnrollingViewModel();
+
+ final String tag;
+ final Class<? extends Fragment> fragmentClass;
+ if (mViewModel.canAssumeUdfps()) {
+ tag = ENROLLING_UDFPS_TAG;
+ fragmentClass = FingerprintEnrollEnrollingUdfpsFragment.class;
+ } else if (mViewModel.canAssumeSfps()) {
+ tag = ENROLLING_SFPS_TAG;
+ fragmentClass = FingerprintEnrollEnrollingSfpsFragment.class;
+ } else {
+ tag = ENROLLING_RFPS_TAG;
+ fragmentClass = FingerprintEnrollEnrollingRfpsFragment.class;
+ }
+ 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, fragmentClass, null, tag)
+ .addToBackStack(tag)
+ .commit();
+ }
+
+ private void attachEnrollingViewModel() {
+ final FingerprintEnrollEnrollingViewModel enrollingViewModel =
+ mViewModelProvider.get(FingerprintEnrollEnrollingViewModel.class);
+ enrollingViewModel.clearBackPressedData();
+ enrollingViewModel.getErrorDialogLiveData().observe(this, mEnrollingErrorDialogObserver);
+
+ final FingerprintEnrollProgressViewModel progressViewModel =
+ mViewModelProvider.get(FingerprintEnrollProgressViewModel.class);
+ progressViewModel.getDoneLiveData().observe(this, mEnrollingDoneObserver);
+ }
+
+ private void startFinishActivity() {
+ final boolean isSuw = mViewModel.getRequest().isSuw();
+ if (!mViewModel.isWaitingActivityResult().compareAndSet(false, true)) {
+ Log.w(TAG, "startNext, isSuw:" + isSuw + ", fail to set isWaiting flag");
+ }
+ Intent intent = new Intent(this, isSuw
+ ? SetupFingerprintEnrollFinish.class
+ : FingerprintEnrollFinish.class);
+ intent.putExtras(mAutoCredentialViewModel.createCredentialIntentExtra());
+ intent.putExtras(mViewModel.getNextActivityBaseIntentExtras());
+ mNextActivityLauncher.launch(intent);
+ }
+
private void startSkipSetupFindFpsDialog() {
new SkipSetupFindFpsDialog().show(getSupportFragmentManager(),
SKIP_SETUP_FIND_FPS_DIALOG_TAG);
}
+ private void startEnrollingErrorDialog() {
+ new FingerprintEnrollEnrollingErrorDialog().show(getSupportFragmentManager(),
+ ENROLLING_ERROR_DIALOG_TAG);
+ }
+
private void onGenerateChallengeFailed(@NonNull Boolean ignoredBoolean) {
onSetActivityResult(new ActivityResult(RESULT_CANCELED, null));
}
@@ -365,20 +447,22 @@
return;
}
case FINGERPRINT_ENROLL_FIND_SENSOR_ACTION_START: {
- final boolean isSuw = mViewModel.getRequest().isSuw();
- if (!mViewModel.isWaitingActivityResult().compareAndSet(false, true)) {
- Log.w(TAG, "startNext, isSuw:" + isSuw + ", fail to set isWaiting flag");
- }
- Intent intent = new Intent(this, isSuw
- ? SetupFingerprintEnrollEnrolling.class
- : FingerprintEnrollEnrolling.class);
- intent.putExtras(mAutoCredentialViewModel.createCredentialIntentExtra());
- intent.putExtras(mViewModel.getNextActivityBaseIntentExtras());
- mNextActivityLauncher.launch(intent);
+ startEnrollingFragment();
}
}
}
+ private void onEnrollingDone(boolean isDone) {
+ if (!isDone) {
+ return;
+ }
+ final FingerprintEnrollProgressViewModel progressViewModel =
+ mViewModelProvider.get(FingerprintEnrollProgressViewModel.class);
+ progressViewModel.clearProgressLiveData();
+
+ startFinishActivity();
+ }
+
@Override
protected void onPause() {
super.onPause();
@@ -386,6 +470,21 @@
}
@Override
+ public void onBackPressed() {
+ final FragmentManager manager = getSupportFragmentManager();
+ final String[] tags = new String[] {ENROLLING_UDFPS_TAG, ENROLLING_SFPS_TAG,
+ ENROLLING_RFPS_TAG };
+ for (String tag: tags) {
+ final Fragment fragment = manager.findFragmentByTag(tag);
+ if (fragment != null) {
+ mViewModelProvider.get(FingerprintEnrollEnrollingViewModel.class).onBackPressed();
+ break;
+ }
+ }
+ super.onBackPressed();
+ }
+
+ @Override
protected void onApplyThemeResource(Resources.Theme theme, @StyleRes int resid, boolean first) {
theme.applyStyle(R.style.SetupWizardPartnerResource, true);
super.onApplyThemeResource(theme, resid, first);
diff --git a/src/com/android/settings/biometrics2/ui/view/GlifLayoutHelper.java b/src/com/android/settings/biometrics2/ui/view/GlifLayoutHelper.java
index 814f579..6af4928 100644
--- a/src/com/android/settings/biometrics2/ui/view/GlifLayoutHelper.java
+++ b/src/com/android/settings/biometrics2/ui/view/GlifLayoutHelper.java
@@ -68,15 +68,13 @@
}
}
- /**
- * Sets description resId to GlifLayout
- */
- public void setDescriptionText(int resId) {
- CharSequence previousDescription = mGlifLayout.getDescriptionText();
- CharSequence description = mActivity.getString(resId);
- // Prevent a11y for re-reading the same string
- if (!TextUtils.equals(previousDescription, description)) {
- mGlifLayout.setDescriptionText(resId);
- }
+ @NonNull
+ public Activity getActivity() {
+ return mActivity;
+ }
+
+ @NonNull
+ public GlifLayout getGlifLayout() {
+ return mGlifLayout;
}
}
diff --git a/src/com/android/settings/biometrics2/ui/viewmodel/FingerprintEnrollEnrollingViewModel.java b/src/com/android/settings/biometrics2/ui/viewmodel/FingerprintEnrollEnrollingViewModel.java
index 058b50b..2877c9a 100644
--- a/src/com/android/settings/biometrics2/ui/viewmodel/FingerprintEnrollEnrollingViewModel.java
+++ b/src/com/android/settings/biometrics2/ui/viewmodel/FingerprintEnrollEnrollingViewModel.java
@@ -16,26 +16,32 @@
package com.android.settings.biometrics2.ui.viewmodel;
+import android.annotation.IntDef;
import android.app.Application;
+import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
+import android.os.Bundle;
import android.os.VibrationAttributes;
import android.os.VibrationEffect;
import android.util.Log;
import android.view.accessibility.AccessibilityManager;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import androidx.lifecycle.AndroidViewModel;
-import androidx.lifecycle.DefaultLifecycleObserver;
+import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
import com.android.settings.biometrics2.data.repository.AccessibilityRepository;
import com.android.settings.biometrics2.data.repository.FingerprintRepository;
import com.android.settings.biometrics2.data.repository.VibratorRepository;
-import com.android.settings.biometrics2.ui.model.EnrollmentRequest;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
/**
* ViewModel explaining the fingerprint enrolling page
*/
-public class FingerprintEnrollEnrollingViewModel extends AndroidViewModel
- implements DefaultLifecycleObserver {
+public class FingerprintEnrollEnrollingViewModel extends AndroidViewModel {
private static final String TAG = FingerprintEnrollEnrollingViewModel.class.getSimpleName();
private static final boolean DEBUG = false;
@@ -45,35 +51,118 @@
private static final VibrationAttributes FINGERPRINT_ENROLLING_SONFICATION_ATTRIBUTES =
VibrationAttributes.createForUsage(VibrationAttributes.USAGE_ACCESSIBILITY);
- //Enrolling skip
+ /**
+ * Enrolling skipped
+ */
public static final int FINGERPRINT_ENROLL_ENROLLING_ACTION_SKIP = 0;
- //Icon touch dialog show
- public static final int FINGERPRINT_ENROLL_ENROLLING_ACTION_SHOW_DIALOG = 0;
+ /**
+ * Enrolling finished
+ */
+ public static final int FINGERPRINT_ENROLL_ENROLLING_ACTION_DONE = 1;
- //Icon touch dialog dismiss
- public static final int FINGERPRINT_ENROLL_ENROLLING_ACTION_DISMISS_DIALOG = 1;
+ /**
+ * Icon touch dialog show
+ */
+ public static final int FINGERPRINT_ENROLL_ENROLLING_ACTION_SHOW_DIALOG = 2;
+ /**
+ * Icon touch dialog dismiss
+ */
+ public static final int FINGERPRINT_ENROLL_ENROLLING_ACTION_DISMISS_DIALOG = 3;
+
+ @IntDef(prefix = { "FINGERPRINT_ENROLL_ENROLLING_ACTION_" }, value = {
+ FINGERPRINT_ENROLL_ENROLLING_ACTION_SKIP,
+ FINGERPRINT_ENROLL_ENROLLING_ACTION_DONE,
+ FINGERPRINT_ENROLL_ENROLLING_ACTION_SHOW_DIALOG,
+ FINGERPRINT_ENROLL_ENROLLING_ACTION_DISMISS_DIALOG
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface FingerprintEnrollEnrollingAction {}
+
+ /**
+ * Enrolling skipped
+ */
+ public static final int FINGERPRINT_ERROR_DIALOG_ACTION_SET_RESULT_FINISH = 0;
+
+ /**
+ * Enrolling finished
+ */
+ public static final int FINGERPRINT_ERROR_DIALOG_ACTION_SET_RESULT_TIMEOUT = 1;
+
+ /**
+ * Icon touch dialog show
+ */
+ public static final int FINGERPRINT_ERROR_DIALOG_ACTION_RESTART = 2;
+
+ @IntDef(prefix = { "FINGERPRINT_ERROR_DIALOG_ACTION_" }, value = {
+ FINGERPRINT_ERROR_DIALOG_ACTION_SET_RESULT_FINISH,
+ FINGERPRINT_ERROR_DIALOG_ACTION_SET_RESULT_TIMEOUT,
+ FINGERPRINT_ERROR_DIALOG_ACTION_RESTART
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface FingerprintErrorDialogAction {}
+
+
+ private final int mUserId;
private final FingerprintRepository mFingerprintRepository;
private final AccessibilityRepository mAccessibilityRepository;
private final VibratorRepository mVibratorRepository;
- private EnrollmentRequest mEnrollmentRequest = null;
+ private final MutableLiveData<Boolean> mBackPressedLiveData = new MutableLiveData<>(false);
private final MutableLiveData<Integer> mEnrollingLiveData = new MutableLiveData<>();
private final MutableLiveData<Integer> mIconTouchDialogLiveData = new MutableLiveData<>();
-
+ private final MutableLiveData<ErrorDialogData> mErrorDialogLiveData = new MutableLiveData<>();
+ private final MutableLiveData<Integer> mErrorDialogActionLiveData = new MutableLiveData<>();
public FingerprintEnrollEnrollingViewModel(Application application,
+ int userId,
FingerprintRepository fingerprintRepository,
AccessibilityRepository accessibilityRepository,
VibratorRepository vibratorRepository) {
super(application);
+ mUserId = userId;
mFingerprintRepository = fingerprintRepository;
mAccessibilityRepository = accessibilityRepository;
mVibratorRepository = vibratorRepository;
}
/**
+ * Notifies activity to show error dialog
+ */
+ public void showErrorDialog(@NonNull ErrorDialogData errorDialogData) {
+ mErrorDialogLiveData.postValue(errorDialogData);
+ }
+
+ public LiveData<ErrorDialogData> getErrorDialogLiveData() {
+ return mErrorDialogLiveData;
+ }
+
+ /**
+ * Saves new user dialog action to mErrorDialogActionLiveData
+ */
+ public void onErrorDialogAction(@FingerprintErrorDialogAction int action) {
+ if (DEBUG) {
+ Log.d(TAG, "onErrorDialogAction(" + action + ")");
+ }
+ mErrorDialogActionLiveData.postValue(action);
+ }
+
+ /**
+ * Clears back press data
+ */
+ public void clearBackPressedData() {
+ mBackPressedLiveData.setValue(false);
+ }
+
+ /**
+ * User trigger back pressed
+ */
+ public void onBackPressed() {
+ mBackPressedLiveData.postValue(true);
+ }
+
+ /**
* User clicks skip button
*/
public void onSkipButtonClick() {
@@ -85,6 +174,17 @@
}
/**
+ * Is enrolling finished
+ */
+ public void onEnrollingDone() {
+ final int action = FINGERPRINT_ENROLL_ENROLLING_ACTION_SKIP;
+ if (DEBUG) {
+ Log.d(TAG, "onEnrollingDone, post action " + action);
+ }
+ mEnrollingLiveData.postValue(action);
+ }
+
+ /**
* Icon touch dialog show
*/
public void onIconTouchDialogShow() {
@@ -121,20 +221,6 @@
}
/**
- * The first sensor type is UDFPS sensor or not
- */
- public boolean canAssumeUdfps() {
- return mFingerprintRepository.canAssumeUdfps();
- }
-
- /**
- * The first sensor type is SFPS sensor or not
- */
- public boolean canAssumeSfps() {
- return mFingerprintRepository.canAssumeSfps();
- }
-
- /**
* Requests interruption of the accessibility feedback from all accessibility services.
*/
public void clearTalkback() {
@@ -154,8 +240,80 @@
* Like {@link #vibrate(VibrationEffect, VibrationAttributes)}, but allows the
* caller to specify the vibration is owned by someone else and set a reason for vibration.
*/
- public void vibrateError(int uid, String opPkg, String reason) {
- mVibratorRepository.vibrate(uid, opPkg, VIBRATE_EFFECT_ERROR, reason,
- FINGERPRINT_ENROLLING_SONFICATION_ATTRIBUTES);
+ public void vibrateError(String reason) {
+ mVibratorRepository.vibrate(mUserId, getApplication().getOpPackageName(),
+ VIBRATE_EFFECT_ERROR, reason, FINGERPRINT_ENROLLING_SONFICATION_ATTRIBUTES);
+ }
+
+ /**
+ * Gets the first FingerprintSensorPropertiesInternal from FingerprintManager
+ */
+ @Nullable
+ public FingerprintSensorPropertiesInternal getFirstFingerprintSensorPropertiesInternal() {
+ return mFingerprintRepository.getFirstFingerprintSensorPropertiesInternal();
+ }
+
+ /**
+ * The first sensor type is UDFPS sensor or not
+ */
+ public boolean canAssumeUdfps() {
+ return mFingerprintRepository.canAssumeUdfps();
+ }
+
+ /**
+ * Saves current state to outState
+ */
+ public void onSaveInstanceState(@NonNull Bundle outState) {
+ // TODO
+// mRestoring = true;
+// mIsCanceled = savedInstanceState.getBoolean(KEY_STATE_CANCELED, false);
+// mPreviousRotation = savedInstanceState.getInt(KEY_STATE_PREVIOUS_ROTATION,
+// getDisplay().getRotation());
+// mIsOrientationChanged = mPreviousRotation != getDisplay().getRotation();
+ }
+
+ /**
+ * Restores saved state from previous savedInstanceState
+ */
+ public void restoreSavedState(@Nullable Bundle savedInstanceState) {
+ if (savedInstanceState == null) {
+ return;
+ }
+ // TODO
+// outState.putBoolean(KEY_STATE_CANCELED, mIsCanceled);
+// outState.putInt(KEY_STATE_PREVIOUS_ROTATION, mPreviousRotation);
+ }
+
+ /**
+ * Data for passing to FingerprintEnrollEnrollingErrorDialog
+ */
+ public static class ErrorDialogData {
+ @NonNull private final CharSequence mErrMsg;
+ @NonNull private final CharSequence mErrTitle;
+ @NonNull private final int mErrMsgId;
+
+ public ErrorDialogData(@NonNull CharSequence errMsg, @NonNull CharSequence errTitle,
+ int errMsgId) {
+ mErrMsg = errMsg;
+ mErrTitle = errTitle;
+ mErrMsgId = errMsgId;
+ }
+
+ public CharSequence getErrMsg() {
+ return mErrMsg;
+ }
+
+ public CharSequence getErrTitle() {
+ return mErrTitle;
+ }
+
+ public int getErrMsgId() {
+ return mErrMsgId;
+ }
+
+ @Override
+ public String toString() {
+ return ErrorDialogData.class.getSimpleName() + "{id:" + mErrMsgId + "}";
+ }
}
}
diff --git a/src/com/android/settings/biometrics2/ui/viewmodel/FingerprintEnrollProgressViewModel.java b/src/com/android/settings/biometrics2/ui/viewmodel/FingerprintEnrollProgressViewModel.java
index 532e2cc..2cb94ec 100644
--- a/src/com/android/settings/biometrics2/ui/viewmodel/FingerprintEnrollProgressViewModel.java
+++ b/src/com/android/settings/biometrics2/ui/viewmodel/FingerprintEnrollProgressViewModel.java
@@ -55,17 +55,17 @@
new MutableLiveData<>();
private final MutableLiveData<EnrollmentStatusMessage> mErrorMessageLiveData =
new MutableLiveData<>();
-
private final MutableLiveData<Boolean> mAcquireLiveData = new MutableLiveData<>();
private final MutableLiveData<Integer> mPointerDownLiveData = new MutableLiveData<>();
private final MutableLiveData<Integer> mPointerUpLiveData = new MutableLiveData<>();
+ private final MutableLiveData<Boolean> mDoneLiveData = new MutableLiveData<>(false);
private byte[] mToken = null;
private final int mUserId;
private final FingerprintUpdater mFingerprintUpdater;
private final MessageDisplayController mMessageDisplayController;
- private EnrollmentHelper mEnrollmentHelper;
+ @Nullable private CancellationSignal mCancellationSignal = null;
private final EnrollmentCallback mEnrollmentCallback = new EnrollmentCallback() {
@Override
@@ -78,7 +78,11 @@
+ ", post progress as " + progress);
}
mProgressLiveData.postValue(progress);
- // TODO set enrolling to false when remaining is 0 during implementing b/260957933
+
+ final Boolean done = remaining == 0;
+ if (!done.equals(mDoneLiveData.getValue())) {
+ mDoneLiveData.postValue(done);
+ }
}
@Override
@@ -136,7 +140,10 @@
* clear progress
*/
public void clearProgressLiveData() {
+ mDoneLiveData.setValue(false);
mProgressLiveData.setValue(new EnrollmentProgress(INITIAL_STEPS, INITIAL_REMAINING));
+ mHelpMessageLiveData.setValue(null);
+ mErrorMessageLiveData.setValue(null);
}
public LiveData<EnrollmentProgress> getProgressLiveData() {
@@ -151,18 +158,21 @@
return mErrorMessageLiveData;
}
- public MutableLiveData<Boolean> getAcquireLiveData() {
+ public LiveData<Boolean> getAcquireLiveData() {
return mAcquireLiveData;
}
- public MutableLiveData<Integer> getPointerDownLiveData() {
+ public LiveData<Integer> getPointerDownLiveData() {
return mPointerDownLiveData;
}
- public MutableLiveData<Integer> getPointerUpLiveData() {
+ public LiveData<Integer> getPointerUpLiveData() {
return mPointerUpLiveData;
}
+ public LiveData<Boolean> getDoneLiveData() {
+ return mDoneLiveData;
+ }
/**
* Starts enrollment and return latest isEnrolling() result
@@ -172,16 +182,18 @@
Log.e(TAG, "Null hardware auth token for enroll");
return false;
}
- if (isEnrolling()) {
+ if (mCancellationSignal != null) {
Log.w(TAG, "Enrolling has started, shall not start again");
return true;
}
+ if (DEBUG) {
+ Log.e(TAG, "startEnrollment(" + reason + ")");
+ }
- mEnrollmentHelper = new EnrollmentHelper(
- mMessageDisplayController != null
- ? mMessageDisplayController
- : mEnrollmentCallback);
- mEnrollmentHelper.startEnrollment(mFingerprintUpdater, mToken, mUserId, reason);
+ mCancellationSignal = new CancellationSignal();
+ mFingerprintUpdater.enroll(mToken, mCancellationSignal, mUserId,
+ mMessageDisplayController != null ? mMessageDisplayController : mEnrollmentCallback,
+ reason);
return true;
}
@@ -189,86 +201,22 @@
* 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));
+ final CancellationSignal cancellationSignal = mCancellationSignal;
+ if (cancellationSignal == null) {
+ Log.e(TAG, "Fail to cancel enrollment, has cancelled or not start");
return false;
}
- mEnrollmentHelper.cancelEnrollment();
- mEnrollmentHelper = null;
+ mCancellationSignal = null;
+ cancellationSignal.cancel();
return true;
}
public boolean isEnrolling() {
- return (mEnrollmentHelper != null);
+ return (mCancellationSignal != 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();
- }
- }
- }
}