Merge "Remove UdfpsEnroll* in system ui."
This commit is contained in:
committed by
Android (Google) Code Review
commit
9b4cf2f908
@@ -1,35 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (C) 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.
|
||||
-->
|
||||
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="54dp"
|
||||
android:height="54dp"
|
||||
android:viewportWidth="54"
|
||||
android:viewportHeight="54">
|
||||
<path
|
||||
android:pathData="M26.9999,3.9619C39.7029,3.9619 50.0369,14.2969 50.0369,26.9999C50.0369,39.7029 39.7029,50.0379 26.9999,50.0379C14.2969,50.0379 3.9629,39.7029 3.9629,26.9999C3.9629,14.2969 14.2969,3.9619 26.9999,3.9619Z"
|
||||
android:fillColor="?android:colorBackground"
|
||||
android:fillType="evenOdd"/>
|
||||
<path
|
||||
android:pathData="M27,0C12.088,0 0,12.088 0,27C0,41.912 12.088,54 27,54C41.912,54 54,41.912 54,27C54,12.088 41.912,0 27,0ZM27,3.962C39.703,3.962 50.037,14.297 50.037,27C50.037,39.703 39.703,50.038 27,50.038C14.297,50.038 3.963,39.703 3.963,27C3.963,14.297 14.297,3.962 27,3.962Z"
|
||||
android:fillColor="@color/udfps_enroll_progress"
|
||||
android:fillType="evenOdd"/>
|
||||
<path
|
||||
android:pathData="M23.0899,38.8534L10.4199,26.1824L13.2479,23.3544L23.0899,33.1974L41.2389,15.0474L44.0679,17.8754L23.0899,38.8534Z"
|
||||
android:fillColor="@color/udfps_enroll_progress"
|
||||
android:fillType="evenOdd"/>
|
||||
</vector>
|
||||
@@ -100,13 +100,4 @@
|
||||
<color name="accessibility_floating_menu_message_text">@*android:color/primary_text_default_material_dark</color>
|
||||
|
||||
<color name="people_tile_background">@color/material_dynamic_secondary20</color>
|
||||
|
||||
<!-- UDFPS colors -->
|
||||
<color name="udfps_enroll_icon">#7DA7F1</color>
|
||||
<color name="udfps_moving_target_fill">#475670</color>
|
||||
<!-- 50% of udfps_moving_target_fill-->
|
||||
<color name="udfps_moving_target_fill_error">#80475670</color>
|
||||
<color name="udfps_enroll_progress">#7DA7F1</color>
|
||||
<color name="udfps_enroll_progress_help">#607DA7F1</color>
|
||||
<color name="udfps_enroll_progress_help_with_talkback">#FFEE675C</color>
|
||||
</resources>
|
||||
|
||||
@@ -210,16 +210,6 @@
|
||||
<attr name="permissionGrantButtonBottomStyle" format="reference"/>
|
||||
</declare-styleable>
|
||||
|
||||
<declare-styleable name="BiometricsEnrollView">
|
||||
<attr name="biometricsEnrollStyle" format="reference" />
|
||||
<attr name="biometricsEnrollIcon" format="reference|color" />
|
||||
<attr name="biometricsMovingTargetFill" format="reference|color" />
|
||||
<attr name="biometricsMovingTargetFillError" format="reference|color" />
|
||||
<attr name="biometricsEnrollProgress" format="reference|color" />
|
||||
<attr name="biometricsEnrollProgressHelp" format="reference|color" />
|
||||
<attr name="biometricsEnrollProgressHelpWithTalkback" format="reference|color" />
|
||||
</declare-styleable>
|
||||
|
||||
<declare-styleable name="SeekBarWithIconButtonsView_Layout">
|
||||
<attr name="max" format="integer" />
|
||||
<attr name="progress" format="integer" />
|
||||
|
||||
@@ -136,15 +136,6 @@
|
||||
<!-- SFPS colors -->
|
||||
<color name="sfps_chevron_fill">@color/material_dynamic_primary90</color>
|
||||
|
||||
<!-- UDFPS colors -->
|
||||
<color name="udfps_enroll_icon">#699FF3</color>
|
||||
<color name="udfps_moving_target_fill">#C2D7F7</color>
|
||||
<!-- 50% of udfps_moving_target_fill-->
|
||||
<color name="udfps_moving_target_fill_error">#80C2D7F7</color>
|
||||
<color name="udfps_enroll_progress">#699FF3</color>
|
||||
<color name="udfps_enroll_progress_help">#70699FF3</color>
|
||||
<color name="udfps_enroll_progress_help_with_talkback">#FFEE675C</color>
|
||||
|
||||
<!-- Floating overlay actions -->
|
||||
<color name="overlay_button_ripple">#1f000000</color>
|
||||
<color name="overlay_background_protection_start">#40000000</color> <!-- 25% black -->
|
||||
|
||||
@@ -593,11 +593,6 @@
|
||||
58.0001 29.2229,56.9551 26.8945,55.195
|
||||
</string>
|
||||
|
||||
<!-- The radius of the enrollment progress bar, in dp -->
|
||||
<integer name="config_udfpsEnrollProgressBar" translatable="false">
|
||||
280
|
||||
</integer>
|
||||
|
||||
<!-- The time (in ms) needed to trigger the lock icon view's long-press affordance -->
|
||||
<integer name="config_lockIconLongPress" translatable="false">200</integer>
|
||||
|
||||
|
||||
@@ -315,10 +315,6 @@
|
||||
|
||||
<!-- Needed for MediaRoute chooser dialog -->
|
||||
<item name="*android:isLightTheme">false</item>
|
||||
|
||||
<!-- Biometrics enroll color style -->
|
||||
<item name="biometricsEnrollStyle">@style/BiometricsEnrollStyle</item>
|
||||
|
||||
</style>
|
||||
|
||||
<style name="Theme.SystemUI.LightWallpaper">
|
||||
@@ -1355,15 +1351,6 @@
|
||||
<item name="android:background">@drawable/grant_permissions_buttons_bottom</item>
|
||||
</style>
|
||||
|
||||
<style name="BiometricsEnrollStyle">
|
||||
<item name="biometricsEnrollIcon">@color/udfps_enroll_icon</item>
|
||||
<item name="biometricsMovingTargetFill">@color/udfps_moving_target_fill</item>
|
||||
<item name="biometricsMovingTargetFillError">@color/udfps_moving_target_fill_error</item>
|
||||
<item name="biometricsEnrollProgress">@color/udfps_enroll_progress</item>
|
||||
<item name="biometricsEnrollProgressHelp">@color/udfps_enroll_progress_help</item>
|
||||
<item name="biometricsEnrollProgressHelpWithTalkback">@color/udfps_enroll_progress_help_with_talkback</item>
|
||||
</style>
|
||||
|
||||
<!-- Magnification styles -->
|
||||
<style name="TextAppearance.MagnificationSetting" />
|
||||
|
||||
|
||||
@@ -307,34 +307,15 @@ public class UdfpsController implements DozeReceiver, Dumpable {
|
||||
unconfigureDisplay(view);
|
||||
}
|
||||
tryAodSendFingerUp();
|
||||
if (acquiredGood) {
|
||||
mOverlay.onAcquiredGood();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEnrollmentProgress(int sensorId, int remaining) {
|
||||
mFgExecutor.execute(() -> {
|
||||
if (mOverlay == null) {
|
||||
Log.e(TAG, "onEnrollProgress received but serverRequest is null");
|
||||
return;
|
||||
}
|
||||
mOverlay.onEnrollmentProgress(remaining);
|
||||
});
|
||||
}
|
||||
public void onEnrollmentProgress(int sensorId, int remaining) { }
|
||||
|
||||
@Override
|
||||
public void onEnrollmentHelp(int sensorId) {
|
||||
mFgExecutor.execute(() -> {
|
||||
if (mOverlay == null) {
|
||||
Log.e(TAG, "onEnrollmentHelp received but serverRequest is null");
|
||||
return;
|
||||
}
|
||||
mOverlay.onEnrollmentHelp();
|
||||
});
|
||||
}
|
||||
public void onEnrollmentHelp(int sensorId) { }
|
||||
|
||||
@Override
|
||||
public void setDebugMessage(int sensorId, String message) {
|
||||
|
||||
@@ -34,7 +34,6 @@ import android.hardware.fingerprint.IUdfpsOverlayControllerCallback
|
||||
import android.os.Build
|
||||
import android.os.RemoteException
|
||||
import android.provider.Settings
|
||||
import android.util.FeatureFlagUtils
|
||||
import android.util.Log
|
||||
import android.util.RotationUtils
|
||||
import android.view.LayoutInflater
|
||||
@@ -135,14 +134,6 @@ class UdfpsControllerOverlay @JvmOverloads constructor(
|
||||
}
|
||||
}
|
||||
|
||||
/** A helper if the [requestReason] was due to enrollment. */
|
||||
val enrollHelper: UdfpsEnrollHelper? =
|
||||
if (requestReason.isEnrollmentReason() && !shouldRemoveEnrollmentUi()) {
|
||||
UdfpsEnrollHelper(context, fingerprintManager, secureSettings, requestReason)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
|
||||
/** If the overlay is currently showing. */
|
||||
val isShowing: Boolean
|
||||
get() = overlayView != null
|
||||
@@ -239,32 +230,16 @@ class UdfpsControllerOverlay @JvmOverloads constructor(
|
||||
return when (filteredRequestReason) {
|
||||
REASON_ENROLL_FIND_SENSOR,
|
||||
REASON_ENROLL_ENROLLING -> {
|
||||
if (FeatureFlagUtils.isEnabled(context,
|
||||
FeatureFlagUtils.SETTINGS_SHOW_UDFPS_ENROLL_IN_SETTINGS)) {
|
||||
// Enroll udfps UI is handled by settings, so use empty view here
|
||||
UdfpsFpmEmptyViewController(
|
||||
view.addUdfpsView(R.layout.udfps_fpm_empty_view){
|
||||
updateAccessibilityViewLocation(sensorBounds)
|
||||
},
|
||||
statusBarStateController,
|
||||
shadeExpansionStateManager,
|
||||
dialogManager,
|
||||
dumpManager
|
||||
)
|
||||
} else {
|
||||
UdfpsEnrollViewController(
|
||||
view.addUdfpsView(R.layout.udfps_enroll_view) {
|
||||
updateSensorLocation(sensorBounds)
|
||||
},
|
||||
enrollHelper ?: throw IllegalStateException("no enrollment helper"),
|
||||
statusBarStateController,
|
||||
shadeExpansionStateManager,
|
||||
dialogManager,
|
||||
dumpManager,
|
||||
featureFlags,
|
||||
overlayParams.scaleFactor
|
||||
)
|
||||
}
|
||||
// Enroll udfps UI is handled by settings, so use empty view here
|
||||
UdfpsFpmEmptyViewController(
|
||||
view.addUdfpsView(R.layout.udfps_fpm_empty_view){
|
||||
updateAccessibilityViewLocation(sensorBounds)
|
||||
},
|
||||
statusBarStateController,
|
||||
shadeExpansionStateManager,
|
||||
dialogManager,
|
||||
dumpManager
|
||||
)
|
||||
}
|
||||
REASON_AUTH_KEYGUARD -> {
|
||||
UdfpsKeyguardViewController(
|
||||
@@ -337,18 +312,6 @@ class UdfpsControllerOverlay @JvmOverloads constructor(
|
||||
return wasShowing
|
||||
}
|
||||
|
||||
fun onEnrollmentProgress(remaining: Int) {
|
||||
enrollHelper?.onEnrollmentProgress(remaining)
|
||||
}
|
||||
|
||||
fun onAcquiredGood() {
|
||||
enrollHelper?.animateIfLastStep()
|
||||
}
|
||||
|
||||
fun onEnrollmentHelp() {
|
||||
enrollHelper?.onEnrollmentHelp()
|
||||
}
|
||||
|
||||
/**
|
||||
* This function computes the angle of touch relative to the sensor and maps
|
||||
* the angle to a list of help messages which are announced if accessibility is enabled.
|
||||
@@ -456,10 +419,6 @@ class UdfpsControllerOverlay @JvmOverloads constructor(
|
||||
}
|
||||
}
|
||||
|
||||
@ShowReason
|
||||
private fun Int.isEnrollmentReason() =
|
||||
this == REASON_ENROLL_FIND_SENSOR || this == REASON_ENROLL_ENROLLING
|
||||
|
||||
@ShowReason
|
||||
private fun Int.isImportantForAccessibility() =
|
||||
this == REASON_ENROLL_FIND_SENSOR ||
|
||||
|
||||
@@ -1,252 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 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.systemui.biometrics;
|
||||
|
||||
import android.animation.Animator;
|
||||
import android.animation.AnimatorSet;
|
||||
import android.animation.ValueAnimator;
|
||||
import android.content.Context;
|
||||
import android.content.res.TypedArray;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.PointF;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.RectF;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.animation.AccelerateDecelerateInterpolator;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.android.systemui.R;
|
||||
|
||||
/**
|
||||
* UDFPS fingerprint drawable that is shown when enrolling
|
||||
*/
|
||||
public class UdfpsEnrollDrawable extends UdfpsDrawable {
|
||||
private static final String TAG = "UdfpsAnimationEnroll";
|
||||
|
||||
private static final long TARGET_ANIM_DURATION_LONG = 800L;
|
||||
private static final long TARGET_ANIM_DURATION_SHORT = 600L;
|
||||
// 1 + SCALE_MAX is the maximum that the moving target will animate to
|
||||
private static final float SCALE_MAX = 0.25f;
|
||||
|
||||
private final Handler mHandler = new Handler(Looper.getMainLooper());
|
||||
|
||||
@NonNull private final Drawable mMovingTargetFpIcon;
|
||||
@NonNull private final Paint mSensorOutlinePaint;
|
||||
@NonNull private final Paint mBlueFill;
|
||||
|
||||
@Nullable private RectF mSensorRect;
|
||||
@Nullable private UdfpsEnrollHelper mEnrollHelper;
|
||||
|
||||
// Moving target animator set
|
||||
@Nullable AnimatorSet mTargetAnimatorSet;
|
||||
// Moving target location
|
||||
float mCurrentX;
|
||||
float mCurrentY;
|
||||
// Moving target size
|
||||
float mCurrentScale = 1.f;
|
||||
|
||||
@NonNull private final Animator.AnimatorListener mTargetAnimListener;
|
||||
|
||||
private boolean mShouldShowTipHint = false;
|
||||
private boolean mShouldShowEdgeHint = false;
|
||||
|
||||
private int mEnrollIcon;
|
||||
private int mMovingTargetFill;
|
||||
|
||||
UdfpsEnrollDrawable(@NonNull Context context, @Nullable AttributeSet attrs) {
|
||||
super(context);
|
||||
|
||||
loadResources(context, attrs);
|
||||
mSensorOutlinePaint = new Paint(0 /* flags */);
|
||||
mSensorOutlinePaint.setAntiAlias(true);
|
||||
mSensorOutlinePaint.setColor(mMovingTargetFill);
|
||||
mSensorOutlinePaint.setStyle(Paint.Style.FILL);
|
||||
|
||||
mBlueFill = new Paint(0 /* flags */);
|
||||
mBlueFill.setAntiAlias(true);
|
||||
mBlueFill.setColor(mMovingTargetFill);
|
||||
mBlueFill.setStyle(Paint.Style.FILL);
|
||||
|
||||
mMovingTargetFpIcon = context.getResources()
|
||||
.getDrawable(R.drawable.ic_kg_fingerprint, null);
|
||||
mMovingTargetFpIcon.setTint(mEnrollIcon);
|
||||
mMovingTargetFpIcon.mutate();
|
||||
|
||||
getFingerprintDrawable().setTint(mEnrollIcon);
|
||||
|
||||
mTargetAnimListener = new Animator.AnimatorListener() {
|
||||
@Override
|
||||
public void onAnimationStart(Animator animation) {}
|
||||
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
updateTipHintVisibility();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationCancel(Animator animation) {}
|
||||
|
||||
@Override
|
||||
public void onAnimationRepeat(Animator animation) {}
|
||||
};
|
||||
}
|
||||
|
||||
void loadResources(Context context, @Nullable AttributeSet attrs) {
|
||||
final TypedArray ta = context.obtainStyledAttributes(attrs,
|
||||
R.styleable.BiometricsEnrollView, R.attr.biometricsEnrollStyle,
|
||||
R.style.BiometricsEnrollStyle);
|
||||
mEnrollIcon = ta.getColor(R.styleable.BiometricsEnrollView_biometricsEnrollIcon, 0);
|
||||
mMovingTargetFill = ta.getColor(
|
||||
R.styleable.BiometricsEnrollView_biometricsMovingTargetFill, 0);
|
||||
ta.recycle();
|
||||
}
|
||||
|
||||
void setEnrollHelper(@NonNull UdfpsEnrollHelper helper) {
|
||||
mEnrollHelper = helper;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSensorRectUpdated(@NonNull RectF sensorRect) {
|
||||
super.onSensorRectUpdated(sensorRect);
|
||||
mSensorRect = sensorRect;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void updateFingerprintIconBounds(@NonNull Rect bounds) {
|
||||
super.updateFingerprintIconBounds(bounds);
|
||||
mMovingTargetFpIcon.setBounds(bounds);
|
||||
invalidateSelf();
|
||||
}
|
||||
|
||||
void onEnrollmentProgress(int remaining, int totalSteps) {
|
||||
if (mEnrollHelper == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!mEnrollHelper.isCenterEnrollmentStage()) {
|
||||
if (mTargetAnimatorSet != null && mTargetAnimatorSet.isRunning()) {
|
||||
mTargetAnimatorSet.end();
|
||||
}
|
||||
|
||||
final PointF point = mEnrollHelper.getNextGuidedEnrollmentPoint();
|
||||
if (mCurrentX != point.x || mCurrentY != point.y) {
|
||||
final ValueAnimator x = ValueAnimator.ofFloat(mCurrentX, point.x);
|
||||
x.addUpdateListener(animation -> {
|
||||
mCurrentX = (float) animation.getAnimatedValue();
|
||||
invalidateSelf();
|
||||
});
|
||||
|
||||
final ValueAnimator y = ValueAnimator.ofFloat(mCurrentY, point.y);
|
||||
y.addUpdateListener(animation -> {
|
||||
mCurrentY = (float) animation.getAnimatedValue();
|
||||
invalidateSelf();
|
||||
});
|
||||
|
||||
final boolean isMovingToCenter = point.x == 0f && point.y == 0f;
|
||||
final long duration = isMovingToCenter
|
||||
? TARGET_ANIM_DURATION_SHORT
|
||||
: TARGET_ANIM_DURATION_LONG;
|
||||
|
||||
final ValueAnimator scale = ValueAnimator.ofFloat(0, (float) Math.PI);
|
||||
scale.setDuration(duration);
|
||||
scale.addUpdateListener(animation -> {
|
||||
// Grow then shrink
|
||||
mCurrentScale = 1
|
||||
+ SCALE_MAX * (float) Math.sin((float) animation.getAnimatedValue());
|
||||
invalidateSelf();
|
||||
});
|
||||
|
||||
mTargetAnimatorSet = new AnimatorSet();
|
||||
|
||||
mTargetAnimatorSet.setInterpolator(new AccelerateDecelerateInterpolator());
|
||||
mTargetAnimatorSet.setDuration(duration);
|
||||
mTargetAnimatorSet.addListener(mTargetAnimListener);
|
||||
mTargetAnimatorSet.playTogether(x, y, scale);
|
||||
mTargetAnimatorSet.start();
|
||||
} else {
|
||||
updateTipHintVisibility();
|
||||
}
|
||||
} else {
|
||||
updateTipHintVisibility();
|
||||
}
|
||||
|
||||
updateEdgeHintVisibility();
|
||||
}
|
||||
|
||||
private void updateTipHintVisibility() {
|
||||
final boolean shouldShow = mEnrollHelper != null && mEnrollHelper.isTipEnrollmentStage();
|
||||
// With the new update, we will git rid of most of this code, and instead
|
||||
// we will change the fingerprint icon.
|
||||
if (mShouldShowTipHint == shouldShow) {
|
||||
return;
|
||||
}
|
||||
mShouldShowTipHint = shouldShow;
|
||||
}
|
||||
|
||||
private void updateEdgeHintVisibility() {
|
||||
final boolean shouldShow = mEnrollHelper != null && mEnrollHelper.isEdgeEnrollmentStage();
|
||||
if (mShouldShowEdgeHint == shouldShow) {
|
||||
return;
|
||||
}
|
||||
mShouldShowEdgeHint = shouldShow;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(@NonNull Canvas canvas) {
|
||||
if (isDisplayConfigured()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Draw moving target
|
||||
if (mEnrollHelper != null && !mEnrollHelper.isCenterEnrollmentStage()) {
|
||||
canvas.save();
|
||||
canvas.translate(mCurrentX, mCurrentY);
|
||||
|
||||
if (mSensorRect != null) {
|
||||
canvas.scale(mCurrentScale, mCurrentScale,
|
||||
mSensorRect.centerX(), mSensorRect.centerY());
|
||||
canvas.drawOval(mSensorRect, mBlueFill);
|
||||
}
|
||||
|
||||
mMovingTargetFpIcon.draw(canvas);
|
||||
canvas.restore();
|
||||
} else {
|
||||
if (mSensorRect != null) {
|
||||
canvas.drawOval(mSensorRect, mSensorOutlinePaint);
|
||||
}
|
||||
getFingerprintDrawable().draw(canvas);
|
||||
getFingerprintDrawable().setAlpha(getAlpha());
|
||||
mSensorOutlinePaint.setAlpha(getAlpha());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAlpha(int alpha) {
|
||||
super.setAlpha(alpha);
|
||||
mSensorOutlinePaint.setAlpha(alpha);
|
||||
mBlueFill.setAlpha(alpha);
|
||||
mMovingTargetFpIcon.setAlpha(alpha);
|
||||
invalidateSelf();
|
||||
}
|
||||
}
|
||||
@@ -1,233 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 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.systemui.biometrics;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.content.Context;
|
||||
import android.graphics.PointF;
|
||||
import android.hardware.biometrics.BiometricOverlayConstants;
|
||||
import android.hardware.fingerprint.FingerprintManager;
|
||||
import android.os.Build;
|
||||
import android.os.UserHandle;
|
||||
import android.util.Log;
|
||||
import android.util.TypedValue;
|
||||
import android.view.accessibility.AccessibilityManager;
|
||||
|
||||
import com.android.systemui.util.settings.SecureSettings;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Helps keep track of enrollment state and animates the progress bar accordingly.
|
||||
*/
|
||||
public class UdfpsEnrollHelper {
|
||||
private static final String TAG = "UdfpsEnrollHelper";
|
||||
|
||||
private static final String SCALE_OVERRIDE =
|
||||
"com.android.systemui.biometrics.UdfpsEnrollHelper.scale";
|
||||
private static final float SCALE = 0.5f;
|
||||
|
||||
private static final String NEW_COORDS_OVERRIDE =
|
||||
"com.android.systemui.biometrics.UdfpsNewCoords";
|
||||
|
||||
interface Listener {
|
||||
void onEnrollmentProgress(int remaining, int totalSteps);
|
||||
void onEnrollmentHelp(int remaining, int totalSteps);
|
||||
void onLastStepAcquired();
|
||||
}
|
||||
|
||||
@NonNull private final FingerprintManager mFingerprintManager;
|
||||
@NonNull private final SecureSettings mSecureSettings;
|
||||
// IUdfpsOverlayController reason
|
||||
private final int mEnrollReason;
|
||||
private final boolean mAccessibilityEnabled;
|
||||
@NonNull private final List<PointF> mGuidedEnrollmentPoints;
|
||||
|
||||
private int mTotalSteps = -1;
|
||||
private int mRemainingSteps = -1;
|
||||
|
||||
// Note that this is actually not equal to "mTotalSteps - mRemainingSteps", because the
|
||||
// interface makes no promises about monotonically increasing by one each time.
|
||||
private int mLocationsEnrolled = 0;
|
||||
|
||||
private int mCenterTouchCount = 0;
|
||||
|
||||
@Nullable Listener mListener;
|
||||
|
||||
public UdfpsEnrollHelper(@NonNull Context context,
|
||||
@NonNull FingerprintManager fingerprintManager, SecureSettings secureSettings,
|
||||
int reason) {
|
||||
|
||||
mFingerprintManager = fingerprintManager;
|
||||
mSecureSettings = secureSettings;
|
||||
mEnrollReason = reason;
|
||||
|
||||
final AccessibilityManager am = context.getSystemService(AccessibilityManager.class);
|
||||
mAccessibilityEnabled = am.isEnabled();
|
||||
|
||||
mGuidedEnrollmentPoints = new ArrayList<>();
|
||||
|
||||
// Number of pixels per mm
|
||||
float px = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_MM, 1,
|
||||
context.getResources().getDisplayMetrics());
|
||||
boolean useNewCoords = mSecureSettings.getIntForUser(NEW_COORDS_OVERRIDE, 0,
|
||||
UserHandle.USER_CURRENT) != 0;
|
||||
if (useNewCoords && (Build.IS_ENG || Build.IS_USERDEBUG)) {
|
||||
Log.v(TAG, "Using new coordinates");
|
||||
mGuidedEnrollmentPoints.add(new PointF(-0.15f * px, -1.02f * px));
|
||||
mGuidedEnrollmentPoints.add(new PointF(-0.15f * px, 1.02f * px));
|
||||
mGuidedEnrollmentPoints.add(new PointF( 0.29f * px, 0.00f * px));
|
||||
mGuidedEnrollmentPoints.add(new PointF( 2.17f * px, -2.35f * px));
|
||||
mGuidedEnrollmentPoints.add(new PointF( 1.07f * px, -3.96f * px));
|
||||
mGuidedEnrollmentPoints.add(new PointF(-0.37f * px, -4.31f * px));
|
||||
mGuidedEnrollmentPoints.add(new PointF(-1.69f * px, -3.29f * px));
|
||||
mGuidedEnrollmentPoints.add(new PointF(-2.48f * px, -1.23f * px));
|
||||
mGuidedEnrollmentPoints.add(new PointF(-2.48f * px, 1.23f * px));
|
||||
mGuidedEnrollmentPoints.add(new PointF(-1.69f * px, 3.29f * px));
|
||||
mGuidedEnrollmentPoints.add(new PointF(-0.37f * px, 4.31f * px));
|
||||
mGuidedEnrollmentPoints.add(new PointF( 1.07f * px, 3.96f * px));
|
||||
mGuidedEnrollmentPoints.add(new PointF( 2.17f * px, 2.35f * px));
|
||||
mGuidedEnrollmentPoints.add(new PointF( 2.58f * px, 0.00f * px));
|
||||
} else {
|
||||
Log.v(TAG, "Using old coordinates");
|
||||
mGuidedEnrollmentPoints.add(new PointF( 2.00f * px, 0.00f * px));
|
||||
mGuidedEnrollmentPoints.add(new PointF( 0.87f * px, -2.70f * px));
|
||||
mGuidedEnrollmentPoints.add(new PointF(-1.80f * px, -1.31f * px));
|
||||
mGuidedEnrollmentPoints.add(new PointF(-1.80f * px, 1.31f * px));
|
||||
mGuidedEnrollmentPoints.add(new PointF( 0.88f * px, 2.70f * px));
|
||||
mGuidedEnrollmentPoints.add(new PointF( 3.94f * px, -1.06f * px));
|
||||
mGuidedEnrollmentPoints.add(new PointF( 2.90f * px, -4.14f * px));
|
||||
mGuidedEnrollmentPoints.add(new PointF(-0.52f * px, -5.95f * px));
|
||||
mGuidedEnrollmentPoints.add(new PointF(-3.33f * px, -3.33f * px));
|
||||
mGuidedEnrollmentPoints.add(new PointF(-3.99f * px, -0.35f * px));
|
||||
mGuidedEnrollmentPoints.add(new PointF(-3.62f * px, 2.54f * px));
|
||||
mGuidedEnrollmentPoints.add(new PointF(-1.49f * px, 5.57f * px));
|
||||
mGuidedEnrollmentPoints.add(new PointF( 2.29f * px, 4.92f * px));
|
||||
mGuidedEnrollmentPoints.add(new PointF( 3.82f * px, 1.78f * px));
|
||||
}
|
||||
}
|
||||
|
||||
int getStageCount() {
|
||||
return mFingerprintManager.getEnrollStageCount();
|
||||
}
|
||||
|
||||
int getStageThresholdSteps(int totalSteps, int stageIndex) {
|
||||
return Math.round(totalSteps * mFingerprintManager.getEnrollStageThreshold(stageIndex));
|
||||
}
|
||||
|
||||
boolean shouldShowProgressBar() {
|
||||
return mEnrollReason == BiometricOverlayConstants.REASON_ENROLL_ENROLLING;
|
||||
}
|
||||
|
||||
void onEnrollmentProgress(int remaining) {
|
||||
if (mTotalSteps == -1) {
|
||||
mTotalSteps = remaining;
|
||||
}
|
||||
|
||||
if (remaining != mRemainingSteps) {
|
||||
mLocationsEnrolled++;
|
||||
if (isCenterEnrollmentStage()) {
|
||||
mCenterTouchCount++;
|
||||
}
|
||||
}
|
||||
|
||||
mRemainingSteps = remaining;
|
||||
|
||||
if (mListener != null) {
|
||||
mListener.onEnrollmentProgress(remaining, mTotalSteps);
|
||||
}
|
||||
}
|
||||
|
||||
void onEnrollmentHelp() {
|
||||
if (mListener != null) {
|
||||
mListener.onEnrollmentHelp(mRemainingSteps, mTotalSteps);
|
||||
}
|
||||
}
|
||||
|
||||
void setListener(Listener listener) {
|
||||
mListener = listener;
|
||||
|
||||
// Only notify during setListener if enrollment is already in progress, so the progress
|
||||
// bar can be updated. If enrollment has not started yet, the progress bar will be empty
|
||||
// anyway.
|
||||
if (mListener != null && mTotalSteps != -1) {
|
||||
mListener.onEnrollmentProgress(mRemainingSteps, mTotalSteps);
|
||||
}
|
||||
}
|
||||
|
||||
boolean isCenterEnrollmentStage() {
|
||||
if (mTotalSteps == -1 || mRemainingSteps == -1) {
|
||||
return true;
|
||||
}
|
||||
return mTotalSteps - mRemainingSteps < getStageThresholdSteps(mTotalSteps, 0);
|
||||
}
|
||||
|
||||
boolean isGuidedEnrollmentStage() {
|
||||
if (mAccessibilityEnabled || mTotalSteps == -1 || mRemainingSteps == -1) {
|
||||
return false;
|
||||
}
|
||||
final int progressSteps = mTotalSteps - mRemainingSteps;
|
||||
return progressSteps >= getStageThresholdSteps(mTotalSteps, 0)
|
||||
&& progressSteps < getStageThresholdSteps(mTotalSteps, 1);
|
||||
}
|
||||
|
||||
boolean isTipEnrollmentStage() {
|
||||
if (mTotalSteps == -1 || mRemainingSteps == -1) {
|
||||
return false;
|
||||
}
|
||||
final int progressSteps = mTotalSteps - mRemainingSteps;
|
||||
return progressSteps >= getStageThresholdSteps(mTotalSteps, 1)
|
||||
&& progressSteps < getStageThresholdSteps(mTotalSteps, 2);
|
||||
}
|
||||
|
||||
boolean isEdgeEnrollmentStage() {
|
||||
if (mTotalSteps == -1 || mRemainingSteps == -1) {
|
||||
return false;
|
||||
}
|
||||
return mTotalSteps - mRemainingSteps >= getStageThresholdSteps(mTotalSteps, 2);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
PointF getNextGuidedEnrollmentPoint() {
|
||||
if (mAccessibilityEnabled || !isGuidedEnrollmentStage()) {
|
||||
return new PointF(0f, 0f);
|
||||
}
|
||||
|
||||
float scale = SCALE;
|
||||
if (Build.IS_ENG || Build.IS_USERDEBUG) {
|
||||
scale = mSecureSettings.getFloatForUser(SCALE_OVERRIDE, SCALE,
|
||||
UserHandle.USER_CURRENT);
|
||||
}
|
||||
final int index = mLocationsEnrolled - mCenterTouchCount;
|
||||
final PointF originalPoint = mGuidedEnrollmentPoints
|
||||
.get(index % mGuidedEnrollmentPoints.size());
|
||||
return new PointF(originalPoint.x * scale, originalPoint.y * scale);
|
||||
}
|
||||
|
||||
void animateIfLastStep() {
|
||||
if (mListener == null) {
|
||||
Log.e(TAG, "animateIfLastStep, null listener");
|
||||
return;
|
||||
}
|
||||
|
||||
if (mRemainingSteps <= 2 && mRemainingSteps >= 0) {
|
||||
mListener.onLastStepAcquired();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,400 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 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.systemui.biometrics;
|
||||
|
||||
import android.animation.ValueAnimator;
|
||||
import android.content.Context;
|
||||
import android.content.res.TypedArray;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.ColorFilter;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Process;
|
||||
import android.os.VibrationAttributes;
|
||||
import android.os.VibrationEffect;
|
||||
import android.os.Vibrator;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.accessibility.AccessibilityManager;
|
||||
import android.view.animation.DecelerateInterpolator;
|
||||
import android.view.animation.Interpolator;
|
||||
import android.view.animation.OvershootInterpolator;
|
||||
|
||||
import androidx.annotation.ColorInt;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.android.systemui.R;
|
||||
|
||||
/**
|
||||
* UDFPS enrollment progress bar.
|
||||
*/
|
||||
public class UdfpsEnrollProgressBarDrawable extends Drawable {
|
||||
private static final String TAG = "UdfpsProgressBar";
|
||||
|
||||
private static final long CHECKMARK_ANIMATION_DELAY_MS = 200L;
|
||||
private static final long CHECKMARK_ANIMATION_DURATION_MS = 300L;
|
||||
private static final long FILL_COLOR_ANIMATION_DURATION_MS = 350L;
|
||||
private static final long PROGRESS_ANIMATION_DURATION_MS = 400L;
|
||||
private static final float STROKE_WIDTH_DP = 12f;
|
||||
private static final Interpolator DEACCEL = new DecelerateInterpolator();
|
||||
|
||||
private static final VibrationEffect VIBRATE_EFFECT_ERROR =
|
||||
VibrationEffect.createWaveform(new long[] {0, 5, 55, 60}, -1);
|
||||
private static final VibrationAttributes FINGERPRINT_ENROLLING_SONFICATION_ATTRIBUTES =
|
||||
VibrationAttributes.createForUsage(VibrationAttributes.USAGE_ACCESSIBILITY);
|
||||
|
||||
private static final VibrationAttributes HARDWARE_FEEDBACK_VIBRATION_ATTRIBUTES =
|
||||
VibrationAttributes.createForUsage(VibrationAttributes.USAGE_HARDWARE_FEEDBACK);
|
||||
|
||||
private static final VibrationEffect SUCCESS_VIBRATION_EFFECT =
|
||||
VibrationEffect.get(VibrationEffect.EFFECT_CLICK);
|
||||
|
||||
private final float mStrokeWidthPx;
|
||||
@ColorInt private final int mProgressColor;
|
||||
@ColorInt private final int mHelpColor;
|
||||
@ColorInt private final int mOnFirstBucketFailedColor;
|
||||
@NonNull private final Drawable mCheckmarkDrawable;
|
||||
@NonNull private final Interpolator mCheckmarkInterpolator;
|
||||
@NonNull private final Paint mBackgroundPaint;
|
||||
@NonNull private final Paint mFillPaint;
|
||||
@NonNull private final Vibrator mVibrator;
|
||||
@NonNull private final boolean mIsAccessibilityEnabled;
|
||||
@NonNull private final Context mContext;
|
||||
|
||||
private boolean mAfterFirstTouch;
|
||||
|
||||
private int mRemainingSteps = 0;
|
||||
private int mTotalSteps = 0;
|
||||
private float mProgress = 0f;
|
||||
@Nullable private ValueAnimator mProgressAnimator;
|
||||
@NonNull private final ValueAnimator.AnimatorUpdateListener mProgressUpdateListener;
|
||||
|
||||
private boolean mShowingHelp = false;
|
||||
@Nullable private ValueAnimator mFillColorAnimator;
|
||||
@NonNull private final ValueAnimator.AnimatorUpdateListener mFillColorUpdateListener;
|
||||
|
||||
@Nullable private ValueAnimator mBackgroundColorAnimator;
|
||||
@NonNull private final ValueAnimator.AnimatorUpdateListener mBackgroundColorUpdateListener;
|
||||
|
||||
private boolean mComplete = false;
|
||||
private float mCheckmarkScale = 0f;
|
||||
@Nullable private ValueAnimator mCheckmarkAnimator;
|
||||
@NonNull private final ValueAnimator.AnimatorUpdateListener mCheckmarkUpdateListener;
|
||||
|
||||
private int mMovingTargetFill;
|
||||
private int mMovingTargetFillError;
|
||||
private int mEnrollProgress;
|
||||
private int mEnrollProgressHelp;
|
||||
private int mEnrollProgressHelpWithTalkback;
|
||||
|
||||
public UdfpsEnrollProgressBarDrawable(@NonNull Context context, @Nullable AttributeSet attrs) {
|
||||
mContext = context;
|
||||
|
||||
loadResources(context, attrs);
|
||||
mStrokeWidthPx = Utils.dpToPixels(context, STROKE_WIDTH_DP);
|
||||
mProgressColor = mEnrollProgress;
|
||||
final AccessibilityManager am = context.getSystemService(AccessibilityManager.class);
|
||||
mIsAccessibilityEnabled = am.isTouchExplorationEnabled();
|
||||
mOnFirstBucketFailedColor = mMovingTargetFillError;
|
||||
if (!mIsAccessibilityEnabled) {
|
||||
mHelpColor = mEnrollProgressHelp;
|
||||
} else {
|
||||
mHelpColor = mEnrollProgressHelpWithTalkback;
|
||||
}
|
||||
mCheckmarkDrawable = context.getDrawable(R.drawable.udfps_enroll_checkmark);
|
||||
mCheckmarkDrawable.mutate();
|
||||
mCheckmarkInterpolator = new OvershootInterpolator();
|
||||
|
||||
mBackgroundPaint = new Paint();
|
||||
mBackgroundPaint.setStrokeWidth(mStrokeWidthPx);
|
||||
mBackgroundPaint.setColor(mMovingTargetFill);
|
||||
mBackgroundPaint.setAntiAlias(true);
|
||||
mBackgroundPaint.setStyle(Paint.Style.STROKE);
|
||||
mBackgroundPaint.setStrokeCap(Paint.Cap.ROUND);
|
||||
|
||||
// Progress fill should *not* use the extracted system color.
|
||||
mFillPaint = new Paint();
|
||||
mFillPaint.setStrokeWidth(mStrokeWidthPx);
|
||||
mFillPaint.setColor(mProgressColor);
|
||||
mFillPaint.setAntiAlias(true);
|
||||
mFillPaint.setStyle(Paint.Style.STROKE);
|
||||
mFillPaint.setStrokeCap(Paint.Cap.ROUND);
|
||||
|
||||
mVibrator = mContext.getSystemService(Vibrator.class);
|
||||
|
||||
mProgressUpdateListener = animation -> {
|
||||
mProgress = (float) animation.getAnimatedValue();
|
||||
invalidateSelf();
|
||||
};
|
||||
|
||||
mFillColorUpdateListener = animation -> {
|
||||
mFillPaint.setColor((int) animation.getAnimatedValue());
|
||||
invalidateSelf();
|
||||
};
|
||||
|
||||
mCheckmarkUpdateListener = animation -> {
|
||||
mCheckmarkScale = (float) animation.getAnimatedValue();
|
||||
invalidateSelf();
|
||||
};
|
||||
|
||||
mBackgroundColorUpdateListener = animation -> {
|
||||
mBackgroundPaint.setColor((int) animation.getAnimatedValue());
|
||||
invalidateSelf();
|
||||
};
|
||||
}
|
||||
|
||||
void loadResources(Context context, @Nullable AttributeSet attrs) {
|
||||
final TypedArray ta = context.obtainStyledAttributes(attrs,
|
||||
R.styleable.BiometricsEnrollView, R.attr.biometricsEnrollStyle,
|
||||
R.style.BiometricsEnrollStyle);
|
||||
mMovingTargetFill = ta.getColor(
|
||||
R.styleable.BiometricsEnrollView_biometricsMovingTargetFill, 0);
|
||||
mMovingTargetFillError = ta.getColor(
|
||||
R.styleable.BiometricsEnrollView_biometricsMovingTargetFillError, 0);
|
||||
mEnrollProgress = ta.getColor(
|
||||
R.styleable.BiometricsEnrollView_biometricsEnrollProgress, 0);
|
||||
mEnrollProgressHelp = ta.getColor(
|
||||
R.styleable.BiometricsEnrollView_biometricsEnrollProgressHelp, 0);
|
||||
mEnrollProgressHelpWithTalkback = ta.getColor(
|
||||
R.styleable.BiometricsEnrollView_biometricsEnrollProgressHelpWithTalkback, 0);
|
||||
ta.recycle();
|
||||
}
|
||||
|
||||
void onEnrollmentProgress(int remaining, int totalSteps) {
|
||||
mAfterFirstTouch = true;
|
||||
updateState(remaining, totalSteps, false /* showingHelp */);
|
||||
}
|
||||
|
||||
void onEnrollmentHelp(int remaining, int totalSteps) {
|
||||
updateState(remaining, totalSteps, true /* showingHelp */);
|
||||
}
|
||||
|
||||
void onLastStepAcquired() {
|
||||
updateState(0, mTotalSteps, false /* showingHelp */);
|
||||
}
|
||||
|
||||
private void updateState(int remainingSteps, int totalSteps, boolean showingHelp) {
|
||||
updateProgress(remainingSteps, totalSteps, showingHelp);
|
||||
updateFillColor(showingHelp);
|
||||
}
|
||||
|
||||
private void updateProgress(int remainingSteps, int totalSteps, boolean showingHelp) {
|
||||
if (mRemainingSteps == remainingSteps && mTotalSteps == totalSteps) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (mShowingHelp) {
|
||||
if (mVibrator != null && mIsAccessibilityEnabled) {
|
||||
mVibrator.vibrate(Process.myUid(), mContext.getOpPackageName(),
|
||||
VIBRATE_EFFECT_ERROR, getClass().getSimpleName() + "::onEnrollmentHelp",
|
||||
FINGERPRINT_ENROLLING_SONFICATION_ATTRIBUTES);
|
||||
}
|
||||
} else {
|
||||
// If the first touch is an error, remainingSteps will be -1 and the callback
|
||||
// doesn't come from onEnrollmentHelp. If we are in the accessibility flow,
|
||||
// we still would like to vibrate.
|
||||
if (mVibrator != null) {
|
||||
if (remainingSteps == -1 && mIsAccessibilityEnabled) {
|
||||
mVibrator.vibrate(Process.myUid(), mContext.getOpPackageName(),
|
||||
VIBRATE_EFFECT_ERROR,
|
||||
getClass().getSimpleName() + "::onFirstTouchError",
|
||||
FINGERPRINT_ENROLLING_SONFICATION_ATTRIBUTES);
|
||||
} else if (remainingSteps != -1 && !mIsAccessibilityEnabled) {
|
||||
mVibrator.vibrate(Process.myUid(),
|
||||
mContext.getOpPackageName(),
|
||||
SUCCESS_VIBRATION_EFFECT,
|
||||
getClass().getSimpleName() + "::OnEnrollmentProgress",
|
||||
HARDWARE_FEEDBACK_VIBRATION_ATTRIBUTES);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mShowingHelp = showingHelp;
|
||||
mRemainingSteps = remainingSteps;
|
||||
mTotalSteps = totalSteps;
|
||||
|
||||
final int progressSteps = Math.max(0, totalSteps - remainingSteps);
|
||||
|
||||
// If needed, add 1 to progress and total steps to account for initial touch.
|
||||
final int adjustedSteps = mAfterFirstTouch ? progressSteps + 1 : progressSteps;
|
||||
final int adjustedTotal = mAfterFirstTouch ? mTotalSteps + 1 : mTotalSteps;
|
||||
|
||||
final float targetProgress = Math.min(1f, (float) adjustedSteps / (float) adjustedTotal);
|
||||
|
||||
if (mProgressAnimator != null && mProgressAnimator.isRunning()) {
|
||||
mProgressAnimator.cancel();
|
||||
}
|
||||
|
||||
mProgressAnimator = ValueAnimator.ofFloat(mProgress, targetProgress);
|
||||
mProgressAnimator.setDuration(PROGRESS_ANIMATION_DURATION_MS);
|
||||
mProgressAnimator.addUpdateListener(mProgressUpdateListener);
|
||||
mProgressAnimator.start();
|
||||
|
||||
if (remainingSteps == 0) {
|
||||
startCompletionAnimation();
|
||||
} else if (remainingSteps > 0) {
|
||||
rollBackCompletionAnimation();
|
||||
}
|
||||
}
|
||||
|
||||
private void animateBackgroundColor() {
|
||||
if (mBackgroundColorAnimator != null && mBackgroundColorAnimator.isRunning()) {
|
||||
mBackgroundColorAnimator.end();
|
||||
}
|
||||
mBackgroundColorAnimator = ValueAnimator.ofArgb(mBackgroundPaint.getColor(),
|
||||
mOnFirstBucketFailedColor);
|
||||
mBackgroundColorAnimator.setDuration(FILL_COLOR_ANIMATION_DURATION_MS);
|
||||
mBackgroundColorAnimator.setRepeatCount(1);
|
||||
mBackgroundColorAnimator.setRepeatMode(ValueAnimator.REVERSE);
|
||||
mBackgroundColorAnimator.setInterpolator(DEACCEL);
|
||||
mBackgroundColorAnimator.addUpdateListener(mBackgroundColorUpdateListener);
|
||||
mBackgroundColorAnimator.start();
|
||||
}
|
||||
|
||||
private void updateFillColor(boolean showingHelp) {
|
||||
if (!mAfterFirstTouch && showingHelp) {
|
||||
// If we are on the first touch, animate the background color
|
||||
// instead of the progress color.
|
||||
animateBackgroundColor();
|
||||
return;
|
||||
}
|
||||
|
||||
if (mFillColorAnimator != null && mFillColorAnimator.isRunning()) {
|
||||
mFillColorAnimator.end();
|
||||
}
|
||||
|
||||
@ColorInt final int targetColor = showingHelp ? mHelpColor : mProgressColor;
|
||||
mFillColorAnimator = ValueAnimator.ofArgb(mFillPaint.getColor(), targetColor);
|
||||
mFillColorAnimator.setDuration(FILL_COLOR_ANIMATION_DURATION_MS);
|
||||
mFillColorAnimator.setRepeatCount(1);
|
||||
mFillColorAnimator.setRepeatMode(ValueAnimator.REVERSE);
|
||||
mFillColorAnimator.setInterpolator(DEACCEL);
|
||||
mFillColorAnimator.addUpdateListener(mFillColorUpdateListener);
|
||||
mFillColorAnimator.start();
|
||||
}
|
||||
|
||||
private void startCompletionAnimation() {
|
||||
if (mComplete) {
|
||||
return;
|
||||
}
|
||||
mComplete = true;
|
||||
|
||||
if (mCheckmarkAnimator != null && mCheckmarkAnimator.isRunning()) {
|
||||
mCheckmarkAnimator.cancel();
|
||||
}
|
||||
|
||||
mCheckmarkAnimator = ValueAnimator.ofFloat(mCheckmarkScale, 1f);
|
||||
mCheckmarkAnimator.setStartDelay(CHECKMARK_ANIMATION_DELAY_MS);
|
||||
mCheckmarkAnimator.setDuration(CHECKMARK_ANIMATION_DURATION_MS);
|
||||
mCheckmarkAnimator.setInterpolator(mCheckmarkInterpolator);
|
||||
mCheckmarkAnimator.addUpdateListener(mCheckmarkUpdateListener);
|
||||
mCheckmarkAnimator.start();
|
||||
}
|
||||
|
||||
private void rollBackCompletionAnimation() {
|
||||
if (!mComplete) {
|
||||
return;
|
||||
}
|
||||
mComplete = false;
|
||||
|
||||
// Adjust duration based on how much of the completion animation has played.
|
||||
final float animatedFraction = mCheckmarkAnimator != null
|
||||
? mCheckmarkAnimator.getAnimatedFraction()
|
||||
: 0f;
|
||||
final long durationMs = Math.round(CHECKMARK_ANIMATION_DELAY_MS * animatedFraction);
|
||||
|
||||
if (mCheckmarkAnimator != null && mCheckmarkAnimator.isRunning()) {
|
||||
mCheckmarkAnimator.cancel();
|
||||
}
|
||||
|
||||
mCheckmarkAnimator = ValueAnimator.ofFloat(mCheckmarkScale, 0f);
|
||||
mCheckmarkAnimator.setDuration(durationMs);
|
||||
mCheckmarkAnimator.addUpdateListener(mCheckmarkUpdateListener);
|
||||
mCheckmarkAnimator.start();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(@NonNull Canvas canvas) {
|
||||
canvas.save();
|
||||
|
||||
// Progress starts from the top, instead of the right
|
||||
canvas.rotate(-90f, getBounds().centerX(), getBounds().centerY());
|
||||
|
||||
final float halfPaddingPx = mStrokeWidthPx / 2f;
|
||||
|
||||
if (mProgress < 1f) {
|
||||
// Draw the background color of the progress circle.
|
||||
canvas.drawArc(
|
||||
halfPaddingPx,
|
||||
halfPaddingPx,
|
||||
getBounds().right - halfPaddingPx,
|
||||
getBounds().bottom - halfPaddingPx,
|
||||
0f /* startAngle */,
|
||||
360f /* sweepAngle */,
|
||||
false /* useCenter */,
|
||||
mBackgroundPaint);
|
||||
}
|
||||
|
||||
if (mProgress > 0f) {
|
||||
// Draw the filled portion of the progress circle.
|
||||
canvas.drawArc(
|
||||
halfPaddingPx,
|
||||
halfPaddingPx,
|
||||
getBounds().right - halfPaddingPx,
|
||||
getBounds().bottom - halfPaddingPx,
|
||||
0f /* startAngle */,
|
||||
360f * mProgress /* sweepAngle */,
|
||||
false /* useCenter */,
|
||||
mFillPaint);
|
||||
}
|
||||
|
||||
canvas.restore();
|
||||
|
||||
if (mCheckmarkScale > 0f) {
|
||||
final float offsetScale = (float) Math.sqrt(2) / 2f;
|
||||
final float centerXOffset = (getBounds().width() - mStrokeWidthPx) / 2f * offsetScale;
|
||||
final float centerYOffset = (getBounds().height() - mStrokeWidthPx) / 2f * offsetScale;
|
||||
final float centerX = getBounds().centerX() + centerXOffset;
|
||||
final float centerY = getBounds().centerY() + centerYOffset;
|
||||
|
||||
final float boundsXOffset =
|
||||
mCheckmarkDrawable.getIntrinsicWidth() / 2f * mCheckmarkScale;
|
||||
final float boundsYOffset =
|
||||
mCheckmarkDrawable.getIntrinsicHeight() / 2f * mCheckmarkScale;
|
||||
|
||||
final int left = Math.round(centerX - boundsXOffset);
|
||||
final int top = Math.round(centerY - boundsYOffset);
|
||||
final int right = Math.round(centerX + boundsXOffset);
|
||||
final int bottom = Math.round(centerY + boundsYOffset);
|
||||
mCheckmarkDrawable.setBounds(left, top, right, bottom);
|
||||
mCheckmarkDrawable.draw(canvas);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAlpha(int alpha) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setColorFilter(@Nullable ColorFilter colorFilter) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOpacity() {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -1,121 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 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.systemui.biometrics;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.RectF;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ImageView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.android.systemui.R;
|
||||
|
||||
/**
|
||||
* View corresponding with udfps_enroll_view.xml
|
||||
*/
|
||||
public class UdfpsEnrollView extends UdfpsAnimationView {
|
||||
@NonNull private final UdfpsEnrollDrawable mFingerprintDrawable;
|
||||
@NonNull private final UdfpsEnrollProgressBarDrawable mFingerprintProgressDrawable;
|
||||
@NonNull private final Handler mHandler;
|
||||
|
||||
@NonNull private ImageView mFingerprintView;
|
||||
@NonNull private ImageView mFingerprintProgressView;
|
||||
|
||||
private LayoutParams mProgressParams;
|
||||
private float mProgressBarRadius;
|
||||
|
||||
public UdfpsEnrollView(Context context, @Nullable AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
mFingerprintDrawable = new UdfpsEnrollDrawable(mContext, attrs);
|
||||
mFingerprintProgressDrawable = new UdfpsEnrollProgressBarDrawable(context, attrs);
|
||||
mHandler = new Handler(Looper.getMainLooper());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onFinishInflate() {
|
||||
mFingerprintView = findViewById(R.id.udfps_enroll_animation_fp_view);
|
||||
mFingerprintProgressView = findViewById(R.id.udfps_enroll_animation_fp_progress_view);
|
||||
mFingerprintView.setImageDrawable(mFingerprintDrawable);
|
||||
mFingerprintProgressView.setImageDrawable(mFingerprintProgressDrawable);
|
||||
}
|
||||
|
||||
@Override
|
||||
void onSensorRectUpdated(RectF bounds) {
|
||||
if (mUseExpandedOverlay) {
|
||||
RectF converted = getBoundsRelativeToView(bounds);
|
||||
|
||||
mProgressParams = new LayoutParams(
|
||||
(int) (converted.width() + mProgressBarRadius * 2),
|
||||
(int) (converted.height() + mProgressBarRadius * 2));
|
||||
mProgressParams.setMargins(
|
||||
(int) (converted.left - mProgressBarRadius),
|
||||
(int) (converted.top - mProgressBarRadius),
|
||||
(int) (converted.right + mProgressBarRadius),
|
||||
(int) (converted.bottom + mProgressBarRadius)
|
||||
);
|
||||
|
||||
mFingerprintProgressView.setLayoutParams(mProgressParams);
|
||||
super.onSensorRectUpdated(converted);
|
||||
} else {
|
||||
super.onSensorRectUpdated(bounds);
|
||||
}
|
||||
}
|
||||
|
||||
void setProgressBarRadius(float radius) {
|
||||
mProgressBarRadius = radius;
|
||||
}
|
||||
|
||||
@Override
|
||||
public UdfpsDrawable getDrawable() {
|
||||
return mFingerprintDrawable;
|
||||
}
|
||||
|
||||
void updateSensorLocation(@NonNull Rect sensorBounds) {
|
||||
View fingerprintAccessibilityView = findViewById(R.id.udfps_enroll_accessibility_view);
|
||||
ViewGroup.LayoutParams params = fingerprintAccessibilityView.getLayoutParams();
|
||||
params.width = sensorBounds.width();
|
||||
params.height = sensorBounds.height();
|
||||
fingerprintAccessibilityView.setLayoutParams(params);
|
||||
fingerprintAccessibilityView.requestLayout();
|
||||
}
|
||||
|
||||
void setEnrollHelper(UdfpsEnrollHelper enrollHelper) {
|
||||
mFingerprintDrawable.setEnrollHelper(enrollHelper);
|
||||
}
|
||||
|
||||
void onEnrollmentProgress(int remaining, int totalSteps) {
|
||||
mHandler.post(() -> {
|
||||
mFingerprintProgressDrawable.onEnrollmentProgress(remaining, totalSteps);
|
||||
mFingerprintDrawable.onEnrollmentProgress(remaining, totalSteps);
|
||||
});
|
||||
}
|
||||
|
||||
void onEnrollmentHelp(int remaining, int totalSteps) {
|
||||
mHandler.post(() -> mFingerprintProgressDrawable.onEnrollmentHelp(remaining, totalSteps));
|
||||
}
|
||||
|
||||
void onLastStepAcquired() {
|
||||
mHandler.post(mFingerprintProgressDrawable::onLastStepAcquired);
|
||||
}
|
||||
}
|
||||
@@ -1,113 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 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.systemui.biometrics;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.graphics.PointF;
|
||||
|
||||
import com.android.systemui.R;
|
||||
import com.android.systemui.dump.DumpManager;
|
||||
import com.android.systemui.flags.FeatureFlags;
|
||||
import com.android.systemui.flags.Flags;
|
||||
import com.android.systemui.plugins.statusbar.StatusBarStateController;
|
||||
import com.android.systemui.shade.ShadeExpansionStateManager;
|
||||
import com.android.systemui.statusbar.phone.SystemUIDialogManager;
|
||||
|
||||
/**
|
||||
* Class that coordinates non-HBM animations during enrollment.
|
||||
*/
|
||||
public class UdfpsEnrollViewController extends UdfpsAnimationViewController<UdfpsEnrollView> {
|
||||
|
||||
private final int mEnrollProgressBarRadius;
|
||||
@NonNull private final UdfpsEnrollHelper mEnrollHelper;
|
||||
@NonNull private final UdfpsEnrollHelper.Listener mEnrollHelperListener =
|
||||
new UdfpsEnrollHelper.Listener() {
|
||||
@Override
|
||||
public void onEnrollmentProgress(int remaining, int totalSteps) {
|
||||
mView.onEnrollmentProgress(remaining, totalSteps);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEnrollmentHelp(int remaining, int totalSteps) {
|
||||
mView.onEnrollmentHelp(remaining, totalSteps);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLastStepAcquired() {
|
||||
mView.onLastStepAcquired();
|
||||
}
|
||||
};
|
||||
|
||||
protected UdfpsEnrollViewController(
|
||||
@NonNull UdfpsEnrollView view,
|
||||
@NonNull UdfpsEnrollHelper enrollHelper,
|
||||
@NonNull StatusBarStateController statusBarStateController,
|
||||
@NonNull ShadeExpansionStateManager shadeExpansionStateManager,
|
||||
@NonNull SystemUIDialogManager systemUIDialogManager,
|
||||
@NonNull DumpManager dumpManager,
|
||||
@NonNull FeatureFlags featureFlags,
|
||||
float scaleFactor) {
|
||||
super(view, statusBarStateController, shadeExpansionStateManager, systemUIDialogManager,
|
||||
dumpManager);
|
||||
mEnrollProgressBarRadius = (int) (scaleFactor * getContext().getResources().getInteger(
|
||||
R.integer.config_udfpsEnrollProgressBar));
|
||||
mEnrollHelper = enrollHelper;
|
||||
mView.setEnrollHelper(mEnrollHelper);
|
||||
mView.setProgressBarRadius(mEnrollProgressBarRadius);
|
||||
mView.mUseExpandedOverlay = featureFlags.isEnabled(Flags.UDFPS_NEW_TOUCH_DETECTION);
|
||||
}
|
||||
|
||||
@Override
|
||||
@NonNull protected String getTag() {
|
||||
return "UdfpsEnrollViewController";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onViewAttached() {
|
||||
super.onViewAttached();
|
||||
if (mEnrollHelper.shouldShowProgressBar()) {
|
||||
// Only need enrollment updates if the progress bar is showing :)
|
||||
mEnrollHelper.setListener(mEnrollHelperListener);
|
||||
}
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public PointF getTouchTranslation() {
|
||||
if (!mEnrollHelper.isGuidedEnrollmentStage()) {
|
||||
return new PointF(0, 0);
|
||||
} else {
|
||||
return mEnrollHelper.getNextGuidedEnrollmentPoint();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPaddingX() {
|
||||
return mEnrollProgressBarRadius;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPaddingY() {
|
||||
return mEnrollProgressBarRadius;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doAnnounceForAccessibility(String str) {
|
||||
mView.announceForAccessibility(str);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -189,8 +189,6 @@ public class UdfpsControllerTest extends SysuiTestCase {
|
||||
@Mock
|
||||
private UdfpsView mUdfpsView;
|
||||
@Mock
|
||||
private UdfpsEnrollView mEnrollView;
|
||||
@Mock
|
||||
private UdfpsBpView mBpView;
|
||||
@Mock
|
||||
private UdfpsFpmEmptyView mFpmEmptyView;
|
||||
@@ -241,15 +239,12 @@ public class UdfpsControllerTest extends SysuiTestCase {
|
||||
|
||||
when(mLayoutInflater.inflate(R.layout.udfps_view, null, false))
|
||||
.thenReturn(mUdfpsView);
|
||||
when(mLayoutInflater.inflate(R.layout.udfps_enroll_view, null))
|
||||
.thenReturn(mEnrollView); // for showOverlay REASON_ENROLL_ENROLLING
|
||||
when(mLayoutInflater.inflate(R.layout.udfps_keyguard_view, null))
|
||||
.thenReturn(mKeyguardView); // for showOverlay REASON_AUTH_FPM_KEYGUARD
|
||||
when(mLayoutInflater.inflate(R.layout.udfps_bp_view, null))
|
||||
.thenReturn(mBpView);
|
||||
when(mLayoutInflater.inflate(R.layout.udfps_fpm_empty_view, null))
|
||||
.thenReturn(mFpmEmptyView);
|
||||
when(mEnrollView.getContext()).thenReturn(mContext);
|
||||
when(mKeyguardUpdateMonitor.isFingerprintDetectionRunning()).thenReturn(true);
|
||||
when(mSessionTracker.getSessionId(anyInt())).thenReturn(
|
||||
(new InstanceIdSequence(1 << 20)).newInstanceId());
|
||||
|
||||
Reference in New Issue
Block a user