From 7ba30ffcf84b03a94995bbabab2b0b5f5ee2bec2 Mon Sep 17 00:00:00 2001 From: firewall Date: Mon, 20 Feb 2023 06:03:20 +0000 Subject: [PATCH] Refactor FingerprintEnrollEnrolling to fragment Bug: b/260957933 Test: NA Change-Id: I5b65f5957569418e85c39f05bc2fece856ef4a2b --- .../fingerprint/UdfpsEnrollHelper.java | 25 ++- .../fingerprint/UdfpsEnrollView.java | 12 +- .../repository/AccessibilityRepository.java | 9 + ...ingerprintEnrollEnrollingSfpsFragment.java | 8 +- ...ngerprintEnrollEnrollingUdfpsFragment.java | 212 +++++++++++++++--- .../FingerprintEnrollEnrollingViewModel.java | 29 +-- 6 files changed, 243 insertions(+), 52 deletions(-) diff --git a/src/com/android/settings/biometrics/fingerprint/UdfpsEnrollHelper.java b/src/com/android/settings/biometrics/fingerprint/UdfpsEnrollHelper.java index f7f138cbabd..70fdbf08bc2 100644 --- a/src/com/android/settings/biometrics/fingerprint/UdfpsEnrollHelper.java +++ b/src/com/android/settings/biometrics/fingerprint/UdfpsEnrollHelper.java @@ -142,7 +142,10 @@ public class UdfpsEnrollHelper extends InstrumentedFragment { setRetainInstance(true); } - void onEnrollmentProgress(int totalSteps, int remaining) { + /** + * Called when a enroll progress update + */ + public void onEnrollmentProgress(int totalSteps, int remaining) { if (mTotalSteps == -1) { mTotalSteps = totalSteps; } @@ -161,25 +164,37 @@ public class UdfpsEnrollHelper extends InstrumentedFragment { } } - void onEnrollmentHelp() { + /** + * Called when a receive error has been encountered during enrollment. + */ + public void onEnrollmentHelp() { if (mListener != null) { mListener.onEnrollmentHelp(mRemainingSteps, mTotalSteps); } } - void onAcquired(boolean isAcquiredGood) { + /** + * Called when a fingerprint image has been acquired, but wasn't processed yet. + */ + public void onAcquired(boolean isAcquiredGood) { if (mListener != null && mTotalSteps != -1) { mListener.onAcquired(isAcquiredGood && animateIfLastStep()); } } - void onPointerDown(int sensorId) { + /** + * Called when pointer down + */ + public void onPointerDown(int sensorId) { if (mListener != null) { mListener.onPointerDown(sensorId); } } - void onPointerUp(int sensorId) { + /** + * Called when pointer up + */ + public void onPointerUp(int sensorId) { if (mListener != null) { mListener.onPointerUp(sensorId); } diff --git a/src/com/android/settings/biometrics/fingerprint/UdfpsEnrollView.java b/src/com/android/settings/biometrics/fingerprint/UdfpsEnrollView.java index 96b49aa0edf..6e42059395f 100644 --- a/src/com/android/settings/biometrics/fingerprint/UdfpsEnrollView.java +++ b/src/com/android/settings/biometrics/fingerprint/UdfpsEnrollView.java @@ -108,7 +108,10 @@ public class UdfpsEnrollView extends FrameLayout implements UdfpsEnrollHelper.Li return mOverlayParams; } - void setOverlayParams(UdfpsOverlayParams params) { + /** + * Set UdfpsOverlayParams + */ + public void setOverlayParams(UdfpsOverlayParams params) { mOverlayParams = params; post(() -> { @@ -121,7 +124,10 @@ public class UdfpsEnrollView extends FrameLayout implements UdfpsEnrollHelper.Li }); } - void setEnrollHelper(UdfpsEnrollHelper enrollHelper) { + /** + * Set UdfpsEnrollHelper + */ + public void setEnrollHelper(UdfpsEnrollHelper enrollHelper) { mFingerprintDrawable.setEnrollHelper(enrollHelper); enrollHelper.setListener(this); } @@ -193,6 +199,8 @@ public class UdfpsEnrollView extends FrameLayout implements UdfpsEnrollHelper.Li params.height = rotatedBounds.height() + 2 * getPaddingX(); params.width = rotatedBounds.width() + 2 * getPaddingY(); setLayoutParams(params); + + } private void onFingerDown() { diff --git a/src/com/android/settings/biometrics2/data/repository/AccessibilityRepository.java b/src/com/android/settings/biometrics2/data/repository/AccessibilityRepository.java index 8777b4fd4ac..20d7f1fc70f 100644 --- a/src/com/android/settings/biometrics2/data/repository/AccessibilityRepository.java +++ b/src/com/android/settings/biometrics2/data/repository/AccessibilityRepository.java @@ -65,4 +65,13 @@ public class AccessibilityRepository { public void sendAccessibilityEvent(@NonNull AccessibilityEvent event) { mAccessibilityManager.sendAccessibilityEvent(event); } + + /** + * Returns if the touch exploration in the system is enabled. + * + * @return True if touch exploration is enabled, false otherwise. + */ + public boolean isTouchExplorationEnabled() { + return mAccessibilityManager.isTouchExplorationEnabled(); + } } diff --git a/src/com/android/settings/biometrics2/ui/view/FingerprintEnrollEnrollingSfpsFragment.java b/src/com/android/settings/biometrics2/ui/view/FingerprintEnrollEnrollingSfpsFragment.java index a91e20632e7..be18b9c3e3f 100644 --- a/src/com/android/settings/biometrics2/ui/view/FingerprintEnrollEnrollingSfpsFragment.java +++ b/src/com/android/settings/biometrics2/ui/view/FingerprintEnrollEnrollingSfpsFragment.java @@ -35,7 +35,6 @@ import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; -import android.view.accessibility.AccessibilityEvent; import android.view.animation.AccelerateDecelerateInterpolator; import android.view.animation.AnimationUtils; import android.view.animation.Interpolator; @@ -329,12 +328,7 @@ public class FingerprintEnrollEnrollingSfpsFragment extends Fragment { } private void announceEnrollmentProgress(CharSequence announcement) { - AccessibilityEvent event = new AccessibilityEvent(); - event.setEventType(AccessibilityEvent.TYPE_ANNOUNCEMENT); - event.setClassName(getClass().getName()); - event.setPackageName(getClass().getPackageName()); - event.getText().add(announcement); - mEnrollingViewModel.sendAccessibilityEvent(event); + mEnrollingViewModel.sendAccessibilityEvent(announcement); } private void onEnrollmentProgressChange(@NonNull EnrollmentProgress progress) { diff --git a/src/com/android/settings/biometrics2/ui/view/FingerprintEnrollEnrollingUdfpsFragment.java b/src/com/android/settings/biometrics2/ui/view/FingerprintEnrollEnrollingUdfpsFragment.java index ad6abf1b47f..4cf35736274 100644 --- a/src/com/android/settings/biometrics2/ui/view/FingerprintEnrollEnrollingUdfpsFragment.java +++ b/src/com/android/settings/biometrics2/ui/view/FingerprintEnrollEnrollingUdfpsFragment.java @@ -22,18 +22,23 @@ import android.annotation.RawRes; import android.app.Activity; import android.content.Context; import android.content.res.Configuration; +import android.graphics.Point; +import android.graphics.Rect; import android.graphics.drawable.Animatable2; -import android.graphics.drawable.AnimatedVectorDrawable; import android.graphics.drawable.Drawable; +import android.hardware.fingerprint.FingerprintManager; +import android.hardware.fingerprint.FingerprintSensorPropertiesInternal; import android.os.Bundle; import android.text.TextUtils; import android.util.Log; +import android.view.DisplayInfo; import android.view.LayoutInflater; 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.FrameLayout; import android.widget.LinearLayout; import android.widget.TextView; @@ -47,15 +52,20 @@ import androidx.transition.TransitionSet; import com.android.settings.R; import com.android.settings.biometrics.BiometricUtils; +import com.android.settings.biometrics.fingerprint.UdfpsEnrollHelper; +import com.android.settings.biometrics.fingerprint.UdfpsEnrollView; 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.android.settingslib.udfps.UdfpsOverlayParams; +import com.android.settingslib.udfps.UdfpsUtils; import com.airbnb.lottie.LottieAnimationView; import com.airbnb.lottie.LottieCompositionFactory; +import com.google.android.setupcompat.template.FooterActionButton; import com.google.android.setupcompat.template.FooterBarMixin; import com.google.android.setupcompat.template.FooterButton; import com.google.android.setupdesign.GlifLayout; @@ -70,9 +80,6 @@ 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; private static final int STAGE_UNKNOWN = -1; private static final int STAGE_CENTER = 0; @@ -100,35 +107,52 @@ public class FingerprintEnrollEnrollingUdfpsFragment extends Fragment { private GlifLayout mView; private TextView mErrorText; private FooterBarMixin mFooterBarMixin; - private AnimatedVectorDrawable mIconAnimationDrawable; - private AnimatedVectorDrawable mIconBackgroundBlinksDrawable; private boolean mShouldShowLottie; private boolean mIsAccessibilityEnabled; private final View.OnClickListener mOnSkipClickListener = (v) -> mEnrollingViewModel.onCancelledDueToOnSkipPressed(); - private final Observer mProgressObserver = progress -> { + + private Observer mProgressObserver = progress -> { + if (progress != null) { + onEnrollmentProgressChange(progress); + } + }; + private Observer mHelpMessageObserver = helpMessage -> { + if (helpMessage != null) { + onEnrollmentHelp(helpMessage.getMsgId(), helpMessage.getStr()); + } + }; + private Observer mErrorMessageObserver = errorMessage -> { // TODO }; - private final Observer mHelpMessageObserver = helpMessage -> { - // TODO - }; - private final Observer mErrorMessageObserver = errorMessage -> { - // TODO - }; - private final Observer mAcquireObserver = isAcquiredGood -> { - // TODO + private Observer mAcquireObserver = isAcquiredGood -> { + if (isAcquiredGood != null) { + onAcquired(isAcquiredGood); + } }; private final Observer mPointerDownObserver = sensorId -> { - // TODO + if (sensorId != null) { + onPointerDown(sensorId); + } }; private final Observer mPointerUpObserver = sensorId -> { - // TODO + if (sensorId != null) { + onPointerUp(sensorId); + } }; private int mIconTouchCount; + private UdfpsUtils mUdfpsUtils; + private float mScaleFactor = 1.0f; + //TODO UdfpsEnrollHelper should not be a Fragment, we should tell enrollview & progress + // drawable enough information EnrollView & ProgressDrawable should draw themselves without + // UdfpsEnrollHelper + private UdfpsEnrollHelper mUdfpsEnrollHelper; + + @Override public void onAttach(@NonNull Context context) { final FragmentActivity activity = getActivity(); @@ -176,6 +200,10 @@ public class FingerprintEnrollEnrollingUdfpsFragment extends Fragment { super.onCreate(savedInstanceState); mEnrollingViewModel.restoreSavedState(savedInstanceState); mIsAccessibilityEnabled = mEnrollingViewModel.isAccessibilityEnabled(); + mUdfpsUtils = new UdfpsUtils(); + mUdfpsEnrollHelper = new UdfpsEnrollHelper(getActivity(), getActivity().getSystemService( + FingerprintManager.class + )); } @Override @@ -188,13 +216,15 @@ public class FingerprintEnrollEnrollingUdfpsFragment extends Fragment { public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { mView = initUdfpsLayout(inflater, container); + return mView; } private GlifLayout initUdfpsLayout(LayoutInflater inflater, ViewGroup container) { final GlifLayout containView = (GlifLayout) inflater.inflate( R.layout.udfps_enroll_enrolling, container, false); - + final UdfpsEnrollView udfpsEnrollView = addUdfpsEnrollView(inflater, + mEnrollingViewModel.getFirstFingerprintSensorPropertiesInternal()); final int rotation = mRotationViewModel.getLiveData().getValue(); if (rotation == Surface.ROTATION_90) { final boolean isLayoutRtl = (TextUtils.getLayoutDirectionFromLocale( @@ -207,19 +237,36 @@ public class FingerprintEnrollEnrollingUdfpsFragment extends Fragment { 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.setLayoutParams(lp); + containView.addView(udfpsEnrollView); + containView.setClipChildren(false); + containView.setClipToPadding(false); containView.setLayoutParams(lp); + setOnHoverListener(true, containView, udfpsEnrollView); + } else if (rotation == Surface.ROTATION_270) { + containView.addView(udfpsEnrollView); + containView.setClipChildren(false); + containView.setClipToPadding(false); + setOnHoverListener(true, containView, udfpsEnrollView); + } else { + final FrameLayout portraitLayoutContainer = containView.findViewById( + R.id.layout_container); + portraitLayoutContainer.addView(udfpsEnrollView); + ViewGroup parent = ((ViewGroup) portraitLayoutContainer.getParent()); + parent.setClipChildren(false); + parent.setClipToPadding(false); + setOnHoverListener(false, containView, udfpsEnrollView); } 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) @@ -245,6 +292,19 @@ public class FingerprintEnrollEnrollingUdfpsFragment extends Fragment { mFastOutLinearInInterpolator = AnimationUtils.loadInterpolator( activity, android.R.interpolator.fast_out_linear_in); + final LinearLayout buttonContainer = mFooterBarMixin.getButtonContainer(); + View spaceView = null; + for (int i = 0; i < buttonContainer.getChildCount(); i++) { + if (!(buttonContainer.getChildAt(i) instanceof FooterActionButton)) { + spaceView = buttonContainer.getChildAt(i); + break; + } + } + if (spaceView != null) { + spaceView.setVisibility(View.GONE); + buttonContainer.getLayoutParams().width = ViewGroup.LayoutParams.WRAP_CONTENT; + } + return containView; } @@ -298,6 +358,9 @@ public class FingerprintEnrollEnrollingUdfpsFragment extends Fragment { final int progress = getProgress(enrollmentProgress); + mUdfpsEnrollHelper.onEnrollmentProgress(enrollmentProgress.getSteps(), + enrollmentProgress.getRemaining()); + if (animate) { animateProgress(progress); } else if (progress >= PROGRESS_BAR_MAX) { @@ -319,6 +382,38 @@ public class FingerprintEnrollEnrollingUdfpsFragment extends Fragment { super.onDestroy(); } + private UdfpsEnrollView addUdfpsEnrollView(LayoutInflater inflater, + FingerprintSensorPropertiesInternal udfpsProps) { + + UdfpsEnrollView enrollView = (UdfpsEnrollView) inflater.inflate(R.layout.udfps_enroll_view, + null, false); + DisplayInfo displayInfo = new DisplayInfo(); + getActivity().getDisplay().getDisplayInfo(displayInfo); + mScaleFactor = mUdfpsUtils.getScaleFactor(displayInfo); + Rect udfpsBounds = udfpsProps.getLocation().getRect(); + udfpsBounds.scale(mScaleFactor); + + final Rect overlayBounds = new Rect( + 0, /* left */ + displayInfo.getNaturalHeight() / 2, /* top */ + displayInfo.getNaturalWidth(), /* right */ + displayInfo.getNaturalHeight() /* botom */); + + UdfpsOverlayParams params = new UdfpsOverlayParams( + udfpsBounds, + overlayBounds, + displayInfo.getNaturalWidth(), + displayInfo.getNaturalHeight(), + mScaleFactor, + displayInfo.rotation); + + enrollView.setOverlayParams(params); + + enrollView.setEnrollHelper(mUdfpsEnrollHelper); + + return enrollView; + } + private void animateProgress(int progress) { // UDFPS animations are owned by SystemUI if (progress >= PROGRESS_BAR_MAX) { @@ -462,16 +557,10 @@ public class FingerprintEnrollEnrollingUdfpsFragment extends Fragment { } private void startIconAnimation() { - if (mIconAnimationDrawable != null) { - mIconAnimationDrawable.start(); - } } private void stopIconAnimation() { mAnimationCancelled = true; - if (mIconAnimationDrawable != null) { - mIconAnimationDrawable.stop(); - } } private int getCurrentStage() { @@ -545,6 +634,79 @@ public class FingerprintEnrollEnrollingUdfpsFragment extends Fragment { }); } + private void setOnHoverListener(boolean isLandscape, GlifLayout enrollLayout, + UdfpsEnrollView udfpsEnrollView) { + if (!mIsAccessibilityEnabled) return; + + final Context context = getActivity(); + final View.OnHoverListener onHoverListener = (v, event) -> { + // Map the touch to portrait mode if the device is in + // landscape mode. + final Point scaledTouch = + mUdfpsUtils.getTouchInNativeCoordinates(event.getPointerId(0), + event, udfpsEnrollView.getOverlayParams()); + + final String theStr = mUdfpsUtils.onTouchOutsideOfSensorArea( + mEnrollingViewModel.isTouchExplorationEnabled(), context, + scaledTouch.x, scaledTouch.y, udfpsEnrollView.getOverlayParams()); + if (theStr != null) { + v.announceForAccessibility(theStr); + } + return false; + }; + + enrollLayout.findManagedViewById(isLandscape ? R.id.sud_landscape_content_area + : R.id.sud_layout_content).setOnHoverListener(onHoverListener); + } + + private void onEnrollmentProgressChange(@NonNull EnrollmentProgress progress) { + updateProgress(true /* animate */, progress); + + updateTitleAndDescription(); + + if (mIsAccessibilityEnabled) { + final int steps = progress.getSteps(); + final int remaining = progress.getRemaining(); + final int percent = (int) (((float) (steps - remaining) / (float) steps) * 100); + CharSequence announcement = getActivity().getString( + R.string.security_settings_udfps_enroll_progress_a11y_message, percent); + mEnrollingViewModel.sendAccessibilityEvent(announcement); + } + + } + + private void onEnrollmentHelp(int helpMsgId, CharSequence helpString) { + if (!TextUtils.isEmpty(helpString)) { + showError(helpString); + mUdfpsEnrollHelper.onEnrollmentHelp(); + } + } + + private void onAcquired(boolean isAcquiredGood) { + if (mUdfpsEnrollHelper != null) { + mUdfpsEnrollHelper.onAcquired(isAcquiredGood); + } + } + + private void onPointerDown(int sensorId) { + if (mUdfpsEnrollHelper != null) { + mUdfpsEnrollHelper.onPointerDown(sensorId); + } + } + + private void onPointerUp(int sensorId) { + if (mUdfpsEnrollHelper != null) { + mUdfpsEnrollHelper.onPointerUp(sensorId); + } + } + + private void showError(CharSequence error) { + mView.setHeaderText(error); + mView.getHeaderTextView().setContentDescription(error); + new GlifLayoutHelper(getActivity(), mView).setDescriptionText(""); + } + + private final Runnable mShowDialogRunnable = new Runnable() { @Override public void run() { diff --git a/src/com/android/settings/biometrics2/ui/viewmodel/FingerprintEnrollEnrollingViewModel.java b/src/com/android/settings/biometrics2/ui/viewmodel/FingerprintEnrollEnrollingViewModel.java index 6a9adb74d3f..6fde44cb054 100644 --- a/src/com/android/settings/biometrics2/ui/viewmodel/FingerprintEnrollEnrollingViewModel.java +++ b/src/com/android/settings/biometrics2/ui/viewmodel/FingerprintEnrollEnrollingViewModel.java @@ -23,7 +23,6 @@ import android.os.Bundle; import android.os.VibrationAttributes; import android.os.VibrationEffect; import android.util.Log; -import android.view.View; import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityManager; @@ -282,19 +281,23 @@ public class FingerprintEnrollEnrollingViewModel extends AndroidViewModel { /** * Sends an {@link AccessibilityEvent}. - * - * @param event The event to send. - * - * @throws IllegalStateException if accessibility is not enabled. - * - * Note: The preferred mechanism for sending custom accessibility - * events is through calling - * {@link android.view.ViewParent#requestSendAccessibilityEvent(View, AccessibilityEvent)} - * instead of this method to allow predecessors to augment/filter events sent by - * their descendants. */ - public void sendAccessibilityEvent(@NonNull AccessibilityEvent event) { - mAccessibilityRepository.sendAccessibilityEvent(event); + public void sendAccessibilityEvent(CharSequence announcement) { + AccessibilityEvent e = AccessibilityEvent.obtain(); + e.setEventType(AccessibilityEvent.TYPE_ANNOUNCEMENT); + e.setClassName(getClass().getName()); + e.setPackageName(getApplication().getPackageName()); + e.getText().add(announcement); + mAccessibilityRepository.sendAccessibilityEvent(e); + } + + /** + * Returns if the touch exploration in the system is enabled. + * + * @return True if touch exploration is enabled, false otherwise. + */ + public boolean isTouchExplorationEnabled() { + return mAccessibilityRepository.isTouchExplorationEnabled(); } /**