Attach FingerprintEnrollEnrollingRfps fragment

Support enrolling RFPS on biomerics v2.

Bug: 260957939
Test: atest FingerprintEnrollFindSensorViewModelTest
      FingerprintEnrollProgressViewModelTest
      FingerprintEnrollmentViewModelTest
      FingerprintEnrollmentActivityTest
Change-Id: Ic04b934592415d03f1b119383bffd40bd5eef2bd
This commit is contained in:
Milton Wu
2023-02-07 17:59:06 +08:00
parent b6cc92a9b8
commit 81530e3f27
14 changed files with 1333 additions and 403 deletions

View File

@@ -123,7 +123,10 @@ public class FingerprintErrorDialog extends InstrumentedDialogFragment {
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 @@ public class FingerprintErrorDialog extends InstrumentedDialogFragment {
}
}
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

View File

@@ -98,8 +98,11 @@ public class FingerprintRepository {
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

View File

@@ -113,13 +113,14 @@ public class BiometricsViewModelFactory implements ViewModelProvider.Factory {
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);
}
}

View File

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

View File

@@ -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.view.animation.Interpolator;
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 @@ import com.google.android.setupdesign.GlifLayout;
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 @@ public class FingerprintEnrollEnrollingRfpsFragment extends Fragment {
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 @@ public class FingerprintEnrollEnrollingRfpsFragment extends Fragment {
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,14 +156,48 @@ public class FingerprintEnrollEnrollingRfpsFragment extends Fragment {
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) {
@@ -118,24 +205,25 @@ public class FingerprintEnrollEnrollingRfpsFragment extends Fragment {
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 @@ public class FingerprintEnrollEnrollingRfpsFragment extends Fragment {
.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 @@ public class FingerprintEnrollEnrollingRfpsFragment extends Fragment {
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 @@ public class FingerprintEnrollEnrollingRfpsFragment extends Fragment {
}
}
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(); */
}
};

View File

@@ -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 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;
@@ -51,6 +59,8 @@ import com.android.settingslib.display.DisplayDensityUtils;
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 @@ public class FingerprintEnrollEnrollingSfpsFragment extends Fragment {
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 @@ public class FingerprintEnrollEnrollingSfpsFragment extends Fragment {
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 @@ public class FingerprintEnrollEnrollingSfpsFragment extends Fragment {
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 @@ public class FingerprintEnrollEnrollingSfpsFragment extends Fragment {
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 @@ public class FingerprintEnrollEnrollingSfpsFragment extends Fragment {
.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 @@ public class FingerprintEnrollEnrollingSfpsFragment extends Fragment {
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 @@ public class FingerprintEnrollEnrollingSfpsFragment extends Fragment {
// 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 @@ public class FingerprintEnrollEnrollingSfpsFragment extends Fragment {
}
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 @@ public class FingerprintEnrollEnrollingSfpsFragment extends Fragment {
* mEnrollingViewModel.getEnrollStageThreshold(index));
}
private void updateOrientation(int orientation) {
private void updateOrientation() {
mIllustrationLottie = mView.findViewById(R.id.illustration_lottie);
}
@@ -372,9 +548,7 @@ public class FingerprintEnrollEnrollingSfpsFragment extends Fragment {
}
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 @@ public class FingerprintEnrollEnrollingSfpsFragment extends Fragment {
}
};
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

View File

@@ -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 @@ public class FingerprintEnrollEnrollingUdfpsFragment extends Fragment {
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 @@ public class FingerprintEnrollEnrollingUdfpsFragment extends Fragment {
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 @@ public class FingerprintEnrollEnrollingUdfpsFragment extends Fragment {
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,14 +137,53 @@ public class FingerprintEnrollEnrollingUdfpsFragment extends Fragment {
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) {
@@ -132,46 +191,44 @@ public class FingerprintEnrollEnrollingUdfpsFragment extends Fragment {
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 @@ public class FingerprintEnrollEnrollingUdfpsFragment extends Fragment {
.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 @@ public class FingerprintEnrollEnrollingUdfpsFragment extends Fragment {
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 @@ public class FingerprintEnrollEnrollingUdfpsFragment extends Fragment {
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 @@ public class FingerprintEnrollEnrollingUdfpsFragment extends Fragment {
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 @@ public class FingerprintEnrollEnrollingUdfpsFragment extends Fragment {
} 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 @@ public class FingerprintEnrollEnrollingUdfpsFragment extends Fragment {
// 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 @@ public class FingerprintEnrollEnrollingUdfpsFragment extends Fragment {
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 @@ public class FingerprintEnrollEnrollingUdfpsFragment extends Fragment {
}
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 @@ public class FingerprintEnrollEnrollingUdfpsFragment extends Fragment {
}
private boolean isStageHalfCompleted() {
EnrollmentProgress progressLiveData = mProgressViewModel.getProgressLiveData().getValue();
if (progressLiveData == null || progressLiveData.getSteps() == -1) {
return false;
@@ -431,9 +536,7 @@ public class FingerprintEnrollEnrollingUdfpsFragment extends Fragment {
}
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 @@ public class FingerprintEnrollEnrollingUdfpsFragment extends Fragment {
}
};
// 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 @@ public class FingerprintEnrollEnrollingUdfpsFragment extends Fragment {
}
// Start animation after it has ended.
/* TODO check mProgressBar?
mProgressBar.post(new Runnable() {
@Override
public void run() {
startIconAnimation();
}
});
*/
}
};
}

View File

@@ -21,6 +21,7 @@ import static android.view.View.OnClickListener;
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 androidx.lifecycle.ViewModelProvider;
import com.android.settings.R;
import com.android.settings.biometrics.fingerprint.FingerprintFindSensorAnimation;
import com.android.settings.biometrics2.ui.model.EnrollmentProgress;
import com.android.settings.biometrics2.ui.model.EnrollmentStatusMessage;
import com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollFindSensorViewModel;
import com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollProgressViewModel;
@@ -64,7 +66,7 @@ public class FingerprintEnrollFindRfpsFragment extends Fragment {
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 @@ public class FingerprintEnrollFindRfpsFragment extends Fragment {
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 @@ public class FingerprintEnrollFindRfpsFragment extends Fragment {
@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 @@ public class FingerprintEnrollFindRfpsFragment extends Fragment {
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);
}
}

View File

@@ -98,6 +98,7 @@ public class FingerprintEnrollFindSfpsFragment extends Fragment {
Log.d(TAG, "mProgressObserver(" + progress + ")");
}
if (progress != null && !progress.isInitialStep()) {
mProgressViewModel.clearProgressLiveData();
mViewModel.onStartButtonClick();
}
};

View File

@@ -85,15 +85,15 @@ public class FingerprintEnrollFinishFragment extends Fragment {
(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));
}
}

View File

@@ -26,6 +26,7 @@ import static com.android.settings.biometrics2.ui.viewmodel.AutoCredentialViewMo
import static com.android.settings.biometrics2.ui.viewmodel.AutoCredentialViewModel.CREDENTIAL_FAIL_NEED_TO_CONFIRM_LOCK;
import static com.android.settings.biometrics2.ui.viewmodel.AutoCredentialViewModel.CREDENTIAL_IS_GENERATING_CHALLENGE;
import static com.android.settings.biometrics2.ui.viewmodel.AutoCredentialViewModel.CREDENTIAL_VALID;
import static com.android.settings.biometrics2.ui.viewmodel.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 androidx.lifecycle.viewmodel.MutableCreationExtras;
import com.android.settings.R;
import com.android.settings.Utils;
import com.android.settings.biometrics.BiometricEnrollBase;
import com.android.settings.biometrics.fingerprint.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.model.EnrollmentRequest;
import com.android.settings.biometrics2.ui.viewmodel.AutoCredentialViewModel;
import com.android.settings.biometrics2.ui.viewmodel.AutoCredentialViewModel.FingerprintChallengeGenerator;
import com.android.settings.biometrics2.ui.viewmodel.DeviceFoldedViewModel;
import com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollEnrollingViewModel;
import com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollFindSensorViewModel;
import com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollIntroViewModel;
import com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollProgressViewModel;
@@ -88,11 +90,16 @@ public class FingerprintEnrollmentActivity extends FragmentActivity {
private static final boolean DEBUG = false;
private static final String TAG = "FingerprintEnrollmentActivity";
private static final String INTRO_TAG = "enroll-intro";
private static final String FIND_UDFPS_TAG = "enroll-find-udfps";
private static final String FIND_SFPS_TAG = "enroll-find-sfps";
private static final String FIND_RFPS_TAG = "enroll-find-rfps";
private static final String 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 @@ public class FingerprintEnrollmentActivity extends FragmentActivity {
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 @@ public class FingerprintEnrollmentActivity extends FragmentActivity {
} 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 @@ public class FingerprintEnrollmentActivity extends FragmentActivity {
}
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 @@ public class FingerprintEnrollmentActivity extends FragmentActivity {
// 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 @@ public class FingerprintEnrollmentActivity extends FragmentActivity {
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,26 +447,43 @@ public class FingerprintEnrollmentActivity extends FragmentActivity {
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();
mViewModel.checkFinishActivityDuringOnPause(isFinishing(), isChangingConfigurations());
}
@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);

View File

@@ -68,15 +68,13 @@ public class GlifLayoutHelper {
}
}
/**
* 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;
}
}

View File

@@ -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,34 +51,117 @@ public class FingerprintEnrollEnrollingViewModel extends AndroidViewModel
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
*/
@@ -84,6 +173,17 @@ public class FingerprintEnrollEnrollingViewModel extends AndroidViewModel
mEnrollingLiveData.postValue(action);
}
/**
* 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
*/
@@ -120,20 +220,6 @@ public class FingerprintEnrollEnrollingViewModel extends AndroidViewModel
return mFingerprintRepository.getEnrollStageCount();
}
/**
* 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.
*/
@@ -154,8 +240,80 @@ public class FingerprintEnrollEnrollingViewModel extends AndroidViewModel
* 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 + "}";
}
}
}

View File

@@ -55,17 +55,17 @@ public class FingerprintEnrollProgressViewModel extends AndroidViewModel {
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 @@ public class FingerprintEnrollProgressViewModel extends AndroidViewModel {
+ ", 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 @@ public class FingerprintEnrollProgressViewModel extends AndroidViewModel {
* 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 @@ public class FingerprintEnrollProgressViewModel extends AndroidViewModel {
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 @@ public class FingerprintEnrollProgressViewModel extends AndroidViewModel {
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 @@ public class FingerprintEnrollProgressViewModel extends AndroidViewModel {
* 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();
}
}
}
}