diff options
25 files changed, 853 insertions, 509 deletions
diff --git a/packages/SystemUI/res/layout/udfps_animation_view_bp.xml b/packages/SystemUI/res/layout/udfps_bp_view.xml index 0cfbf2e61dd1..f1c55ef16cdc 100644 --- a/packages/SystemUI/res/layout/udfps_animation_view_bp.xml +++ b/packages/SystemUI/res/layout/udfps_bp_view.xml @@ -14,9 +14,9 @@ ~ See the License for the specific language governing permissions and ~ limitations under the License. --> -<com.android.systemui.biometrics.UdfpsAnimationViewBp +<com.android.systemui.biometrics.UdfpsBpView xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/udfps_animation_view" android:layout_width="match_parent" android:layout_height="match_parent"> -</com.android.systemui.biometrics.UdfpsAnimationViewBp> +</com.android.systemui.biometrics.UdfpsBpView> diff --git a/packages/SystemUI/res/layout/udfps_animation_view_enroll.xml b/packages/SystemUI/res/layout/udfps_enroll_view.xml index 9b5752d2de59..40353052ca85 100644 --- a/packages/SystemUI/res/layout/udfps_animation_view_enroll.xml +++ b/packages/SystemUI/res/layout/udfps_enroll_view.xml @@ -14,13 +14,13 @@ ~ See the License for the specific language governing permissions and ~ limitations under the License. --> -<com.android.systemui.biometrics.UdfpsAnimationViewEnroll +<com.android.systemui.biometrics.UdfpsEnrollView xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/udfps_animation_view" android:layout_width="match_parent" android:layout_height="match_parent"> - <!-- Enrollment progress bar--> + <!-- Enrollment progress bar --> <com.android.systemui.biometrics.UdfpsProgressBar android:id="@+id/progress_bar" android:layout_width="match_parent" @@ -31,4 +31,9 @@ android:layout_gravity="center" android:visibility="gone"/> -</com.android.systemui.biometrics.UdfpsAnimationViewEnroll> + <!-- Fingerprint --> + <ImageView + android:id="@+id/udfps_enroll_animation_fp_view" + android:layout_width="match_parent" + android:layout_height="match_parent"/> +</com.android.systemui.biometrics.UdfpsEnrollView> diff --git a/packages/SystemUI/res/layout/udfps_animation_view_fpm_other.xml b/packages/SystemUI/res/layout/udfps_fpm_other_view.xml index f32faa0df867..6ecbb473d720 100644 --- a/packages/SystemUI/res/layout/udfps_animation_view_fpm_other.xml +++ b/packages/SystemUI/res/layout/udfps_fpm_other_view.xml @@ -14,9 +14,15 @@ ~ See the License for the specific language governing permissions and ~ limitations under the License. --> -<com.android.systemui.biometrics.UdfpsAnimationViewFpmOther +<com.android.systemui.biometrics.UdfpsFpmOtherView xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/udfps_animation_view" android:layout_width="match_parent" android:layout_height="match_parent"> -</com.android.systemui.biometrics.UdfpsAnimationViewFpmOther> + + <!-- Fingerprint --> + <ImageView + android:id="@+id/udfps_fpm_other_fp_view" + android:layout_width="match_parent" + android:layout_height="match_parent"/> +</com.android.systemui.biometrics.UdfpsFpmOtherView> diff --git a/packages/SystemUI/res/layout/udfps_animation_view_keyguard.xml b/packages/SystemUI/res/layout/udfps_keyguard_view.xml index 644d1adac46b..0199ccb04be6 100644 --- a/packages/SystemUI/res/layout/udfps_animation_view_keyguard.xml +++ b/packages/SystemUI/res/layout/udfps_keyguard_view.xml @@ -14,9 +14,17 @@ ~ See the License for the specific language governing permissions and ~ limitations under the License. --> -<com.android.systemui.biometrics.UdfpsAnimationViewKeyguard +<com.android.systemui.biometrics.UdfpsKeyguardView xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/udfps_animation_view" android:layout_width="match_parent" android:layout_height="match_parent"> -</com.android.systemui.biometrics.UdfpsAnimationViewKeyguard> + + <!-- TODO: add background protection --> + + <!-- Fingerprint --> + <ImageView + android:id="@+id/udfps_keyguard_animation_fp_view" + android:layout_width="match_parent" + android:layout_height="match_parent"/> +</com.android.systemui.biometrics.UdfpsKeyguardView> diff --git a/packages/SystemUI/res/layout/udfps_view.xml b/packages/SystemUI/res/layout/udfps_view.xml index e24c9e99a405..50b2f209d871 100644 --- a/packages/SystemUI/res/layout/udfps_view.xml +++ b/packages/SystemUI/res/layout/udfps_view.xml @@ -22,6 +22,11 @@ android:layout_height="match_parent" systemui:sensorTouchAreaCoefficient="0.5"> + <ViewStub + android:id="@+id/animation_view" + android:layout_width="match_parent" + android:layout_height="match_parent"/> + <com.android.systemui.biometrics.UdfpsSurfaceView android:id="@+id/hbm_view" android:layout_width="match_parent" diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationView.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationView.java index 43ecf6778022..2036150d3679 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationView.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationView.java @@ -16,57 +16,63 @@ package com.android.systemui.biometrics; -import android.annotation.NonNull; import android.annotation.Nullable; import android.content.Context; -import android.graphics.Canvas; -import android.graphics.PointF; import android.graphics.RectF; import android.util.AttributeSet; import android.widget.FrameLayout; -import com.android.systemui.doze.DozeReceiver; -import com.android.systemui.statusbar.phone.StatusBar; - /** * Base class for views containing UDFPS animations. Note that this is a FrameLayout so that we - * can support multiple child views drawing on the same region around the sensor location. + * can support multiple child views drawing in the same region around the sensor location. + * + * - hides animation view when pausing auth + * - sends illumination events to fingerprint drawable + * - sends sensor rect updates to fingerprint drawable + * - optionally can override dozeTimeTick to adjust views for burn-in mitigation */ -public abstract class UdfpsAnimationView extends FrameLayout implements DozeReceiver, - StatusBar.ExpansionChangedListener { - - private static final String TAG = "UdfpsAnimationView"; +abstract class UdfpsAnimationView extends FrameLayout { - @Nullable protected abstract UdfpsAnimation getUdfpsAnimation(); - - @NonNull private UdfpsView mParent; - @NonNull private RectF mSensorRect; private int mAlpha; + private boolean mPauseAuth; public UdfpsAnimationView(Context context, @Nullable AttributeSet attrs) { super(context, attrs); - mSensorRect = new RectF(); - setWillNotDraw(false); } - @Override - protected void onDraw(Canvas canvas) { - super.onDraw(canvas); + /** + * Fingerprint drawable + */ + abstract UdfpsDrawable getDrawable(); - if (getUdfpsAnimation() != null) { - final int alpha = mParent.shouldPauseAuth() ? mAlpha : 255; - getUdfpsAnimation().setAlpha(alpha); - getUdfpsAnimation().draw(canvas); - } + void onSensorRectUpdated(RectF bounds) { + getDrawable().onSensorRectUpdated(bounds); } - @Override - protected void onDetachedFromWindow() { - super.onDetachedFromWindow(); + void onIlluminationStarting() { + getDrawable().setIlluminationShowing(true); + getDrawable().invalidateSelf(); + } + + void onIlluminationStopped() { + getDrawable().setIlluminationShowing(false); + getDrawable().invalidateSelf(); + } - if (getUdfpsAnimation() != null) { - getUdfpsAnimation().onDestroy(); + /** + * @return true if changed + */ + boolean setPauseAuth(boolean pauseAuth) { + if (pauseAuth != mPauseAuth) { + mPauseAuth = pauseAuth; + updateAlpha(); + return true; } + return false; + } + + private void updateAlpha() { + getDrawable().setAlpha(mPauseAuth ? mAlpha : 255); } private int expansionToAlpha(float expansion) { @@ -81,76 +87,15 @@ public abstract class UdfpsAnimationView extends FrameLayout implements DozeRece return (int) ((1 - percent) * 255); } - void onIlluminationStarting() { - if (getUdfpsAnimation() == null) { - return; - } - - getUdfpsAnimation().setIlluminationShowing(true); - postInvalidate(); - } - - void onIlluminationStopped() { - if (getUdfpsAnimation() == null) { - return; - } - - getUdfpsAnimation().setIlluminationShowing(false); - postInvalidate(); - } - - void setParent(@NonNull UdfpsView parent) { - mParent = parent; - } - - void onSensorRectUpdated(@NonNull RectF sensorRect) { - mSensorRect = sensorRect; - if (getUdfpsAnimation() != null) { - getUdfpsAnimation().onSensorRectUpdated(mSensorRect); - } - } - - void updateColor() { - if (getUdfpsAnimation() != null) { - getUdfpsAnimation().updateColor(); - } - postInvalidate(); - } - - @Override - public void dozeTimeTick() { - if (getUdfpsAnimation() instanceof DozeReceiver) { - ((DozeReceiver) getUdfpsAnimation()).dozeTimeTick(); - } - } - - @Override public void onExpansionChanged(float expansion, boolean expanded) { mAlpha = expansionToAlpha(expansion); - postInvalidate(); - } - - public int getPaddingX() { - if (getUdfpsAnimation() == null) { - return 0; - } - return getUdfpsAnimation().getPaddingX(); - } - - public int getPaddingY() { - if (getUdfpsAnimation() == null) { - return 0; - } - return getUdfpsAnimation().getPaddingY(); + updateAlpha(); } /** - * @return the amount of translation needed if the view currently requires the user to touch - * somewhere other than the exact center of the sensor. For example, this can happen - * during guided enrollment. + * @return true if handled */ - @NonNull - PointF getTouchTranslation() { - return new PointF(0, 0); + boolean dozeTimeTick() { + return false; } } diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewController.java new file mode 100644 index 000000000000..b6d80ba14dc0 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewController.java @@ -0,0 +1,167 @@ +/* + * 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 static com.android.systemui.statusbar.StatusBarState.FULLSCREEN_USER_SWITCHER; +import static com.android.systemui.statusbar.StatusBarState.KEYGUARD; +import static com.android.systemui.statusbar.StatusBarState.SHADE_LOCKED; + +import android.annotation.NonNull; +import android.graphics.PointF; +import android.graphics.RectF; + +import com.android.systemui.plugins.statusbar.StatusBarStateController; +import com.android.systemui.statusbar.phone.StatusBar; +import com.android.systemui.util.ViewController; + +/** + * Handles: + * 1. registering for listeners when its view is attached and unregistering on view detached + * 2. pausing udfps when fingerprintManager may still be running but we temporarily want to hide + * the affordance. this allows us to fade the view in and out nicely (see shouldPauseAuth) + * 3. sending events to its view including: + * - illumination events + * - sensor position changes + * - doze time event + */ +abstract class UdfpsAnimationViewController<T extends UdfpsAnimationView> + extends ViewController<T> { + @NonNull final StatusBarStateController mStatusBarStateController; + @NonNull final StatusBar mStatusBar; + + private boolean mNotificationShadeExpanded; + private int mStatusBarState; + + protected UdfpsAnimationViewController( + T view, + StatusBarStateController statusBarStateController, + StatusBar statusBar) { + super(view); + mStatusBarStateController = statusBarStateController; + mStatusBar = statusBar; + } + + @Override + protected void onViewAttached() { + mStatusBarStateController.addCallback(mStateListener); + mStateListener.onStateChanged(mStatusBarStateController.getState()); + mStatusBar.addExpansionChangedListener(mStatusBarExpansionChangedListener); + } + + @Override + protected void onViewDetached() { + mStatusBarStateController.removeCallback(mStateListener); + mStatusBar.removeExpansionChangedListener(mStatusBarExpansionChangedListener); + } + + /** + * Returns true if the fingerprint manager is running but we want to temporarily pause + * authentication. + */ + boolean shouldPauseAuth() { + return (mNotificationShadeExpanded && mStatusBarState != KEYGUARD) + || mStatusBarState == SHADE_LOCKED + || mStatusBarState == FULLSCREEN_USER_SWITCHER; + } + + /** + * Send pause auth update to our view. + */ + void updatePauseAuth() { + if (mView.setPauseAuth(shouldPauseAuth())) { + mView.postInvalidate(); + } + } + + /** + * Send sensor position change to our view. This rect contains paddingX and paddingY. + */ + void onSensorRectUpdated(RectF sensorRect) { + mView.onSensorRectUpdated(sensorRect); + } + + /** + * Send dozeTimeTick to view in case it wants to handle its burn-in offset. + */ + void dozeTimeTick() { + if (mView.dozeTimeTick()) { + mView.postInvalidate(); + } + } + + /** + * @return the amount of translation needed if the view currently requires the user to touch + * somewhere other than the exact center of the sensor. For example, this can happen + * during guided enrollment. + */ + PointF getTouchTranslation() { + return new PointF(0, 0); + } + + /** + * X-Padding to add to left and right of the sensor rectangle area to increase the size of our + * window to draw within. + * @return + */ + int getPaddingX() { + return 0; + } + + /** + * Y-Padding to add to top and bottom of the sensor rectangle area to increase the size of our + * window to draw within. + */ + int getPaddingY() { + return 0; + } + + /** + * Udfps has started illuminating and the fingerprint manager is working on authenticating. + */ + void onIlluminationStarting() { + mView.onIlluminationStarting(); + mView.postInvalidate(); + } + + /** + * Udfps has stopped illuminating and the fingerprint manager is no longer attempting to + * authenticate. + */ + void onIlluminationStopped() { + mView.onIlluminationStopped(); + mView.postInvalidate(); + } + + private final StatusBar.ExpansionChangedListener mStatusBarExpansionChangedListener = + new StatusBar.ExpansionChangedListener() { + @Override + public void onExpansionChanged(float expansion, boolean expanded) { + mNotificationShadeExpanded = expanded; + mView.onExpansionChanged(expansion, expanded); + updatePauseAuth(); + } + }; + + private final StatusBarStateController.StateListener mStateListener = + new StatusBarStateController.StateListener() { + @Override + public void onStateChanged(int newState) { + mStatusBarState = newState; + updatePauseAuth(); + } + }; +} diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewEnroll.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewEnroll.java deleted file mode 100644 index 543df33dd5d7..000000000000 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewEnroll.java +++ /dev/null @@ -1,96 +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.util.AttributeSet; -import android.util.Log; -import android.view.View; - -import com.android.systemui.R; - -/** - * Class that coordinates non-HBM animations during enrollment. - */ -public class UdfpsAnimationViewEnroll extends UdfpsAnimationView - implements UdfpsEnrollHelper.Listener { - - private static final String TAG = "UdfpsAnimationViewEnroll"; - - @NonNull private UdfpsAnimationEnroll mUdfpsAnimation; - @NonNull private UdfpsProgressBar mProgressBar; - @Nullable private UdfpsEnrollHelper mEnrollHelper; - - @NonNull - @Override - protected UdfpsAnimation getUdfpsAnimation() { - return mUdfpsAnimation; - } - - public UdfpsAnimationViewEnroll(Context context, @Nullable AttributeSet attrs) { - super(context, attrs); - mUdfpsAnimation = new UdfpsAnimationEnroll(context); - } - - public void setEnrollHelper(@NonNull UdfpsEnrollHelper helper) { - mEnrollHelper = helper; - mUdfpsAnimation.setEnrollHelper(helper); - } - - @Override - protected void onFinishInflate() { - mProgressBar = findViewById(R.id.progress_bar); - } - - @Override - protected void onAttachedToWindow() { - super.onAttachedToWindow(); - - if (mEnrollHelper == null) { - Log.e(TAG, "Enroll helper is null"); - return; - } - - if (mEnrollHelper.shouldShowProgressBar()) { - mProgressBar.setVisibility(View.VISIBLE); - - // Only need enrollment updates if the progress bar is showing :) - mEnrollHelper.setListener(this); - } - } - - @Override - public void onEnrollmentProgress(int remaining, int totalSteps) { - final int interpolatedProgress = mProgressBar.getMax() - * Math.max(0, totalSteps + 1 - remaining) / (totalSteps + 1); - - mProgressBar.setProgress(interpolatedProgress, true); - } - - @NonNull - @Override - PointF getTouchTranslation() { - if (!mEnrollHelper.isCenterEnrollmentComplete()) { - return new PointF(0, 0); - } else { - return mEnrollHelper.getNextGuidedEnrollmentPoint(); - } - } -} diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewBp.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsBpView.java index 515b442b61f6..70be907228c8 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewBp.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsBpView.java @@ -24,19 +24,22 @@ import androidx.annotation.Nullable; /** * Class that coordinates non-HBM animations during BiometricPrompt. * + * Currently doesn't draw anything. + * * Note that {@link AuthBiometricUdfpsView} also shows UDFPS animations. At some point we should - * de-dupe this if necessary. This will probably happen once the top-level TODO in UdfpsController - * is completed (inflate operation-specific views, instead of inflating generic udfps_view and - * adding operation-specific animations to it). + * de-dupe this if necessary. */ -public class UdfpsAnimationViewBp extends UdfpsAnimationView { - public UdfpsAnimationViewBp(Context context, @Nullable AttributeSet attrs) { +public class UdfpsBpView extends UdfpsAnimationView { + private UdfpsFpDrawable mFingerprintDrawable; + + public UdfpsBpView(Context context, @Nullable AttributeSet attrs) { super(context, attrs); + // Drawable isn't ever added to the view, so we don't currently show anything + mFingerprintDrawable = new UdfpsFpDrawable(mContext); } - @Nullable @Override - protected UdfpsAnimation getUdfpsAnimation() { - return null; + UdfpsDrawable getDrawable() { + return mFingerprintDrawable; } } diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsBpViewController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsBpViewController.java new file mode 100644 index 000000000000..b712c655a6e7 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsBpViewController.java @@ -0,0 +1,32 @@ +/* + * 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 com.android.systemui.plugins.statusbar.StatusBarStateController; +import com.android.systemui.statusbar.phone.StatusBar; + +/** + * Class that coordinates non-HBM animations for biometric prompt. + */ +class UdfpsBpViewController extends UdfpsAnimationViewController<UdfpsBpView> { + protected UdfpsBpViewController( + UdfpsBpView view, + StatusBarStateController statusBarStateController, + StatusBar statusBar) { + super(view, statusBarStateController, statusBar); + } +} diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java index 4b6a8f639cc4..94aeb73c4b42 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java @@ -31,9 +31,9 @@ import android.graphics.RectF; import android.hardware.fingerprint.FingerprintManager; import android.hardware.fingerprint.FingerprintSensorPropertiesInternal; import android.hardware.fingerprint.IUdfpsOverlayController; -import android.os.SystemClock; import android.hardware.fingerprint.IUdfpsOverlayControllerCallback; import android.os.RemoteException; +import android.os.SystemClock; import android.util.Log; import android.view.Gravity; import android.view.LayoutInflater; @@ -195,17 +195,6 @@ public class UdfpsController implements DozeReceiver, HbmCallback { } } - @VisibleForTesting final StatusBar.ExpansionChangedListener mStatusBarExpansionListener = - (expansion, expanded) -> mView.onExpansionChanged(expansion, expanded); - - @VisibleForTesting final StatusBarStateController.StateListener mStatusBarStateListener = - new StatusBarStateController.StateListener() { - @Override - public void onStateChanged(int newState) { - mView.onStateChanged(newState); - } - }; - private static float computePointerSpeed(@NonNull VelocityTracker tracker, int pointerId) { final float vx = tracker.getXVelocity(pointerId); final float vy = tracker.getYVelocity(pointerId); @@ -360,10 +349,9 @@ public class UdfpsController implements DozeReceiver, HbmCallback { @Override public void dozeTimeTick() { - if (mView == null) { - return; + if (mView != null) { + mView.dozeTimeTick(); } - mView.dozeTimeTick(); } /** @@ -387,7 +375,8 @@ public class UdfpsController implements DozeReceiver, HbmCallback { } } - private WindowManager.LayoutParams computeLayoutParams(@Nullable UdfpsAnimationView animation) { + private WindowManager.LayoutParams computeLayoutParams( + @Nullable UdfpsAnimationViewController animation) { final int paddingX = animation != null ? animation.getPaddingX() : 0; final int paddingY = animation != null ? animation.getPaddingY() : 0; @@ -438,20 +427,13 @@ public class UdfpsController implements DozeReceiver, HbmCallback { mFgExecutor.execute(() -> { if (mView == null) { try { - Log.v(TAG, "showUdfpsOverlay | adding window"); - // TODO: Eventually we should refactor the code to inflate an - // operation-specific view here, instead of inflating a generic udfps_view - // and adding operation-specific animations to it. + Log.v(TAG, "showUdfpsOverlay | adding window reason=" + reason); mView = (UdfpsView) mInflater.inflate(R.layout.udfps_view, null, false); mView.setSensorProperties(mSensorProps); mView.setHbmCallback(this); - - final UdfpsAnimationView animation = getUdfpsAnimationViewForReason(reason); - mView.setAnimationView(animation); - - mStatusBar.addExpansionChangedListener(mStatusBarExpansionListener); - mStatusBarStateController.addCallback(mStatusBarStateListener); - mStatusBarStateListener.onStateChanged(mStatusBarStateController.getState()); + UdfpsAnimationViewController animation = inflateUdfpsAnimation(reason); + animation.init(); + mView.setAnimationViewController(animation); mWindowManager.addView(mView, computeLayoutParams(animation)); mView.setOnTouchListener(mOnTouchListener); @@ -464,40 +446,46 @@ public class UdfpsController implements DozeReceiver, HbmCallback { }); } - @NonNull - private UdfpsAnimationView getUdfpsAnimationViewForReason(int reason) { - Log.d(TAG, "getUdfpsAnimationForReason: " + reason); - - final LayoutInflater inflater = LayoutInflater.from(mContext); - + private UdfpsAnimationViewController inflateUdfpsAnimation(int reason) { switch (reason) { case IUdfpsOverlayController.REASON_ENROLL_FIND_SENSOR: - case IUdfpsOverlayController.REASON_ENROLL_ENROLLING: { - final UdfpsAnimationViewEnroll view = (UdfpsAnimationViewEnroll) - inflater.inflate(R.layout.udfps_animation_view_enroll, null, false); - view.setEnrollHelper(mServerRequest.mEnrollHelper); - return view; - } - - case IUdfpsOverlayController.REASON_AUTH_BP: { - final UdfpsAnimationViewBp view = (UdfpsAnimationViewBp) - inflater.inflate(R.layout.udfps_animation_view_bp, null, false); - return view; - } - - case IUdfpsOverlayController.REASON_AUTH_FPM_KEYGUARD: { - final UdfpsAnimationViewKeyguard view = (UdfpsAnimationViewKeyguard) - inflater.inflate(R.layout.udfps_animation_view_keyguard, null, false); - view.setStatusBarStateController(mStatusBarStateController); - return view; - } - - case IUdfpsOverlayController.REASON_AUTH_FPM_OTHER: { - final UdfpsAnimationViewFpmOther view = (UdfpsAnimationViewFpmOther) - inflater.inflate(R.layout.udfps_animation_view_fpm_other, null, false); - return view; - } - + case IUdfpsOverlayController.REASON_ENROLL_ENROLLING: + UdfpsEnrollView enrollView = (UdfpsEnrollView) mInflater.inflate( + R.layout.udfps_enroll_view, null); + mView.addView(enrollView); + return new UdfpsEnrollViewController( + enrollView, + mServerRequest.mEnrollHelper, + mStatusBarStateController, + mStatusBar + ); + case IUdfpsOverlayController.REASON_AUTH_FPM_KEYGUARD: + UdfpsKeyguardView keyguardView = (UdfpsKeyguardView) + mInflater.inflate(R.layout.udfps_keyguard_view, null); + mView.addView(keyguardView); + return new UdfpsKeyguardViewController( + keyguardView, + mStatusBarStateController, + mStatusBar + ); + case IUdfpsOverlayController.REASON_AUTH_BP: + // note: empty controller, currently shows no visual affordance + UdfpsBpView bpView = (UdfpsBpView) mInflater.inflate(R.layout.udfps_bp_view, null); + mView.addView(bpView); + return new UdfpsBpViewController( + bpView, + mStatusBarStateController, + mStatusBar + ); + case IUdfpsOverlayController.REASON_AUTH_FPM_OTHER: + UdfpsFpmOtherView authOtherView = (UdfpsFpmOtherView) + mInflater.inflate(R.layout.udfps_fpm_other_view, null); + mView.addView(authOtherView); + return new UdfpsFpmOtherViewController( + authOtherView, + mStatusBarStateController, + mStatusBar + ); default: Log.d(TAG, "Animation for reason " + reason + " not supported yet"); return null; @@ -510,11 +498,9 @@ public class UdfpsController implements DozeReceiver, HbmCallback { Log.v(TAG, "hideUdfpsOverlay | removing window"); // Reset the controller back to its starting state. onFingerUp(); - - mStatusBar.removeExpansionChangedListener(mStatusBarExpansionListener); - mStatusBarStateController.removeCallback(mStatusBarStateListener); - mWindowManager.removeView(mView); + mView.setOnTouchListener(null); + mView.setAnimationViewController(null); mView = null; } else { Log.v(TAG, "hideUdfpsOverlay | the overlay is already hidden"); diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimation.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsDrawable.java index a51b6fd16445..13d31cb87fdc 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimation.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsDrawable.java @@ -17,10 +17,10 @@ package com.android.systemui.biometrics; import android.content.Context; +import android.graphics.ColorFilter; import android.graphics.Rect; import android.graphics.RectF; import android.graphics.drawable.Drawable; -import android.view.View; import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -28,24 +28,24 @@ import androidx.annotation.Nullable; import com.android.systemui.R; /** - * Abstract base class for animations that should be drawn when the finger is not touching the + * Abstract base class for drawable displayed when the finger is not touching the * sensor area. */ -public abstract class UdfpsAnimation extends Drawable { - protected abstract void updateColor(); - protected abstract void onDestroy(); - +public abstract class UdfpsDrawable extends Drawable { @NonNull protected final Context mContext; @NonNull protected final Drawable mFingerprintDrawable; - @Nullable private View mView; private boolean mIlluminationShowing; - public UdfpsAnimation(@NonNull Context context) { + int mAlpha = 255; // 0 - 255 + public UdfpsDrawable(@NonNull Context context) { mContext = context; mFingerprintDrawable = context.getResources().getDrawable(R.drawable.ic_fingerprint, null); mFingerprintDrawable.mutate(); } + /** + * @param sensorRect the rect coordinates for the sensor area + */ public void onSensorRectUpdated(@NonNull RectF sensorRect) { final int margin = (int) sensorRect.height() / 8; @@ -56,17 +56,17 @@ public abstract class UdfpsAnimation extends Drawable { updateFingerprintIconBounds(bounds); } + /** + * Bounds for the fingerprint icon + */ protected void updateFingerprintIconBounds(@NonNull Rect bounds) { mFingerprintDrawable.setBounds(bounds); } @Override public void setAlpha(int alpha) { - mFingerprintDrawable.setAlpha(alpha); - } - - public void setAnimationView(UdfpsAnimationView view) { - mView = view; + mAlpha = alpha; + mFingerprintDrawable.setAlpha(mAlpha); } boolean isIlluminationShowing() { @@ -77,23 +77,12 @@ public abstract class UdfpsAnimation extends Drawable { mIlluminationShowing = showing; } - /** - * @return The amount of padding that's needed on each side of the sensor, in pixels. - */ - public int getPaddingX() { - return 0; + @Override + public void setColorFilter(@Nullable ColorFilter colorFilter) { } - /** - * @return The amount of padding that's needed on each side of the sensor, in pixels. - */ - public int getPaddingY() { + @Override + public int getOpacity() { return 0; } - - protected void postInvalidateView() { - if (mView != null) { - mView.postInvalidate(); - } - } } diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationEnroll.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollDrawable.java index 015a598e972b..d80e085bdc70 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationEnroll.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollDrawable.java @@ -20,7 +20,6 @@ import android.content.Context; import android.content.res.Configuration; import android.graphics.Canvas; import android.graphics.Color; -import android.graphics.ColorFilter; import android.graphics.Paint; import android.graphics.PointF; import android.graphics.Rect; @@ -33,23 +32,23 @@ import androidx.annotation.Nullable; import com.android.systemui.R; /** - * UDFPS animations that should be shown when enrolling. + * UDFPS fingerprint drawable that is shown when enrolling */ -public class UdfpsAnimationEnroll extends UdfpsAnimation { +public class UdfpsEnrollDrawable extends UdfpsDrawable { private static final String TAG = "UdfpsAnimationEnroll"; private static final float SHADOW_RADIUS = 5.f; - private static final float PROGRESS_BAR_RADIUS = 140.f; + static final float PROGRESS_BAR_RADIUS = 140.f; @NonNull private final Drawable mMovingTargetFpIcon; @NonNull private final Paint mSensorPaint; @NonNull private final Paint mBlueFill; - @NonNull private final Paint mBlueStroke;; + @NonNull private final Paint mBlueStroke; @Nullable private RectF mSensorRect; @Nullable private UdfpsEnrollHelper mEnrollHelper; - UdfpsAnimationEnroll(@NonNull Context context) { + UdfpsEnrollDrawable(@NonNull Context context) { super(context); mSensorPaint = new Paint(0 /* flags */); @@ -72,20 +71,12 @@ public class UdfpsAnimationEnroll extends UdfpsAnimation { mMovingTargetFpIcon = context.getResources().getDrawable(R.drawable.ic_fingerprint, null); mMovingTargetFpIcon.setTint(Color.WHITE); mMovingTargetFpIcon.mutate(); - } - - void setEnrollHelper(@NonNull UdfpsEnrollHelper helper) { - mEnrollHelper = helper; - } - @Override - protected void updateColor() { mFingerprintDrawable.setTint(mContext.getColor(R.color.udfps_enroll_icon)); } - @Override - protected void onDestroy() { - + void setEnrollHelper(@NonNull UdfpsEnrollHelper helper) { + mEnrollHelper = helper; } @Override @@ -98,6 +89,7 @@ public class UdfpsAnimationEnroll extends UdfpsAnimation { protected void updateFingerprintIconBounds(@NonNull Rect bounds) { super.updateFingerprintIconBounds(bounds); mMovingTargetFpIcon.setBounds(bounds); + invalidateSelf(); } @Override @@ -117,7 +109,7 @@ public class UdfpsAnimationEnroll extends UdfpsAnimation { // Draw moving target if (mEnrollHelper.isCenterEnrollmentComplete()) { - mFingerprintDrawable.setAlpha(64); + mFingerprintDrawable.setAlpha(mAlpha == 255 ? 64 : mAlpha); canvas.save(); final PointF point = mEnrollHelper.getNextGuidedEnrollmentPoint(); @@ -130,33 +122,16 @@ public class UdfpsAnimationEnroll extends UdfpsAnimation { mMovingTargetFpIcon.draw(canvas); canvas.restore(); } else { - mFingerprintDrawable.setAlpha(255); + mFingerprintDrawable.setAlpha(mAlpha); } } @Override - public int getPaddingX() { - return (int) Math.ceil(PROGRESS_BAR_RADIUS); - } - - @Override - public int getPaddingY() { - return (int) Math.ceil(PROGRESS_BAR_RADIUS); - } - - @Override public void setAlpha(int alpha) { super.setAlpha(alpha); mSensorPaint.setAlpha(alpha); - } - - @Override - public void setColorFilter(@Nullable ColorFilter colorFilter) { - - } - - @Override - public int getOpacity() { - return 0; + mBlueFill.setAlpha(alpha); + mBlueStroke.setAlpha(alpha); + invalidateSelf(); } } diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollHelper.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollHelper.java index 667b7a7cf0a3..62058a965c09 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollHelper.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollHelper.java @@ -100,13 +100,13 @@ public class UdfpsEnrollHelper { } - void setListener(@NonNull Listener listener) { + 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 (mTotalSteps != -1) { + if (mListener != null && mTotalSteps != -1) { mListener.onEnrollmentProgress(mRemainingSteps, mTotalSteps); } } diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewKeyguard.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollView.java index 7d0b3e59feb1..7985d95c7c61 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewKeyguard.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollView.java @@ -18,32 +18,36 @@ package com.android.systemui.biometrics; import android.content.Context; import android.util.AttributeSet; +import android.widget.ImageView; -import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import com.android.systemui.plugins.statusbar.StatusBarStateController; +import com.android.systemui.R; /** - * Class that coordinates non-HBM animations during keyguard authentication. + * View corresponding with udfps_enroll_view.xml */ -public class UdfpsAnimationViewKeyguard extends UdfpsAnimationView { - @Nullable private UdfpsAnimationKeyguard mAnimation; +public class UdfpsEnrollView extends UdfpsAnimationView { + private final UdfpsEnrollDrawable mFingerprintDrawable; + private ImageView mFingerprintView; - public UdfpsAnimationViewKeyguard(Context context, @Nullable AttributeSet attrs) { + public UdfpsEnrollView(Context context, @Nullable AttributeSet attrs) { super(context, attrs); + mFingerprintDrawable = new UdfpsEnrollDrawable(mContext); } - void setStatusBarStateController(@NonNull StatusBarStateController statusBarStateController) { - if (mAnimation == null) { - mAnimation = new UdfpsAnimationKeyguard(getContext(), statusBarStateController); - mAnimation.setAnimationView(this); - } + @Override + protected void onFinishInflate() { + mFingerprintView = findViewById(R.id.udfps_enroll_animation_fp_view); + mFingerprintView.setImageDrawable(mFingerprintDrawable); } - @Nullable @Override - protected UdfpsAnimation getUdfpsAnimation() { - return mAnimation; + public UdfpsDrawable getDrawable() { + return mFingerprintDrawable; + } + + void setEnrollHelper(UdfpsEnrollHelper enrollHelper) { + mFingerprintDrawable.setEnrollHelper(enrollHelper); } } diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollViewController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollViewController.java new file mode 100644 index 000000000000..da8d712ebbdc --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollViewController.java @@ -0,0 +1,92 @@ +/* + * 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 android.view.View; + +import com.android.systemui.R; +import com.android.systemui.plugins.statusbar.StatusBarStateController; +import com.android.systemui.statusbar.phone.StatusBar; + +/** + * Class that coordinates non-HBM animations during enrollment. + */ +public class UdfpsEnrollViewController extends UdfpsAnimationViewController<UdfpsEnrollView> { + @NonNull private final UdfpsProgressBar mProgressBar; + @NonNull private final UdfpsEnrollHelper mEnrollHelper; + + protected UdfpsEnrollViewController( + UdfpsEnrollView view, + @NonNull UdfpsEnrollHelper enrollHelper, + StatusBarStateController statusBarStateController, + StatusBar statusBar) { + super(view, statusBarStateController, statusBar); + mEnrollHelper = enrollHelper; + mProgressBar = mView.findViewById(R.id.progress_bar); + mView.setEnrollHelper(mEnrollHelper); + } + + @Override + protected void onViewAttached() { + super.onViewAttached(); + if (mEnrollHelper.shouldShowProgressBar()) { + mProgressBar.setVisibility(View.VISIBLE); + + // Only need enrollment updates if the progress bar is showing :) + mEnrollHelper.setListener(mEnrollHelperListener); + } + } + + @Override + protected void onViewDetached() { + super.onViewDetached(); + mEnrollHelper.setListener(null); + } + + @NonNull + @Override + public PointF getTouchTranslation() { + if (!mEnrollHelper.isCenterEnrollmentComplete()) { + return new PointF(0, 0); + } else { + return mEnrollHelper.getNextGuidedEnrollmentPoint(); + } + } + + @Override + public int getPaddingX() { + return (int) Math.ceil(UdfpsEnrollDrawable.PROGRESS_BAR_RADIUS); + } + + @Override + public int getPaddingY() { + return (int) Math.ceil(UdfpsEnrollDrawable.PROGRESS_BAR_RADIUS); + } + + private final UdfpsEnrollHelper.Listener mEnrollHelperListener = + new UdfpsEnrollHelper.Listener() { + @Override + public void onEnrollmentProgress(int remaining, int totalSteps) { + final int interpolatedProgress = mProgressBar.getMax() + * Math.max(0, totalSteps + 1 - remaining) / (totalSteps + 1); + + mProgressBar.setProgress(interpolatedProgress, true); + } + }; +} diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationFpmOther.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsFpDrawable.java index ef7a34000841..09b6fabbdd15 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationFpmOther.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsFpDrawable.java @@ -18,32 +18,19 @@ package com.android.systemui.biometrics; import android.content.Context; import android.graphics.Canvas; -import android.graphics.ColorFilter; import androidx.annotation.NonNull; -import androidx.annotation.Nullable; /** - * UDFPS animations that should be shown when authenticating via FingerprintManager, excluding - * keyguard. + * Draws udfps fingerprint if sensor isn't illuminating. */ -public class UdfpsAnimationFpmOther extends UdfpsAnimation { +public class UdfpsFpDrawable extends UdfpsDrawable { - UdfpsAnimationFpmOther(@NonNull Context context) { + UdfpsFpDrawable(@NonNull Context context) { super(context); } @Override - protected void updateColor() { - - } - - @Override - protected void onDestroy() { - - } - - @Override public void draw(@NonNull Canvas canvas) { if (isIlluminationShowing()) { return; @@ -51,14 +38,4 @@ public class UdfpsAnimationFpmOther extends UdfpsAnimation { mFingerprintDrawable.draw(canvas); } - - @Override - public void setColorFilter(@Nullable ColorFilter colorFilter) { - - } - - @Override - public int getOpacity() { - return 0; - } } diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewFpmOther.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsFpmOtherView.java index 3d2f5a0fe5cf..85f16068188e 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewFpmOther.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsFpmOtherView.java @@ -18,25 +18,32 @@ package com.android.systemui.biometrics; import android.content.Context; import android.util.AttributeSet; +import android.widget.ImageView; import androidx.annotation.Nullable; +import com.android.systemui.R; + /** - * Class that coordinates non-HBM animations during other usage of FingerprintManager (not - * including Keyguard). + * View corresponding with udfps_fpm_other_view.xml */ -public class UdfpsAnimationViewFpmOther extends UdfpsAnimationView { - - private final UdfpsAnimationFpmOther mAnimation; +public class UdfpsFpmOtherView extends UdfpsAnimationView { + private final UdfpsFpDrawable mFingerprintDrawable; + private ImageView mFingerprintView; - public UdfpsAnimationViewFpmOther(Context context, @Nullable AttributeSet attrs) { + public UdfpsFpmOtherView(Context context, @Nullable AttributeSet attrs) { super(context, attrs); - mAnimation = new UdfpsAnimationFpmOther(context); + mFingerprintDrawable = new UdfpsFpDrawable(context); + } + + @Override + protected void onFinishInflate() { + mFingerprintView = findViewById(R.id.udfps_fpm_other_fp_view); + mFingerprintView.setImageDrawable(mFingerprintDrawable); } - @Nullable @Override - protected UdfpsAnimation getUdfpsAnimation() { - return mAnimation; + UdfpsDrawable getDrawable() { + return mFingerprintDrawable; } } diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsFpmOtherViewController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsFpmOtherViewController.java new file mode 100644 index 000000000000..587501bd1aa5 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsFpmOtherViewController.java @@ -0,0 +1,35 @@ +/* + * 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 com.android.systemui.plugins.statusbar.StatusBarStateController; +import com.android.systemui.statusbar.phone.StatusBar; + +/** + * Class that coordinates non-HBM animations for non keyguard, enrollment or biometric prompt + * states. + * + * Currently only shows the fp drawable. + */ +class UdfpsFpmOtherViewController extends UdfpsAnimationViewController<UdfpsFpmOtherView> { + protected UdfpsFpmOtherViewController( + UdfpsFpmOtherView view, + StatusBarStateController statusBarStateController, + StatusBar statusBar) { + super(view, statusBarStateController, statusBar); + } +} diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationKeyguard.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardDrawable.java index 5f268cfa8fa5..b0c5da09d916 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationKeyguard.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardDrawable.java @@ -21,28 +21,25 @@ import static com.android.systemui.doze.util.BurnInHelperKt.getBurnInOffset; import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; -import android.graphics.ColorFilter; import android.util.MathUtils; import androidx.annotation.NonNull; -import androidx.annotation.Nullable; import com.android.internal.graphics.ColorUtils; import com.android.settingslib.Utils; import com.android.systemui.R; import com.android.systemui.doze.DozeReceiver; -import com.android.systemui.plugins.statusbar.StatusBarStateController; /** * UDFPS animations that should be shown when authenticating on keyguard. */ -public class UdfpsAnimationKeyguard extends UdfpsAnimation implements DozeReceiver, - StatusBarStateController.StateListener { +public class UdfpsKeyguardDrawable extends UdfpsDrawable implements DozeReceiver { private static final String TAG = "UdfpsAnimationKeyguard"; + private final int mLockScreenColor; + private final int mAmbientDisplayColor; @NonNull private final Context mContext; - @NonNull private final StatusBarStateController mStatusBarStateController; private final int mMaxBurnInOffsetX; private final int mMaxBurnInOffsetY; @@ -51,18 +48,19 @@ public class UdfpsAnimationKeyguard extends UdfpsAnimation implements DozeReceiv private float mBurnInOffsetX; private float mBurnInOffsetY; - UdfpsAnimationKeyguard(@NonNull Context context, - @NonNull StatusBarStateController statusBarStateController) { + UdfpsKeyguardDrawable(@NonNull Context context) { super(context); mContext = context; - mStatusBarStateController = statusBarStateController; + // TODO: move burn-in to view mMaxBurnInOffsetX = context.getResources() .getDimensionPixelSize(R.dimen.udfps_burn_in_offset_x); mMaxBurnInOffsetY = context.getResources() .getDimensionPixelSize(R.dimen.udfps_burn_in_offset_y); - statusBarStateController.addCallback(this); + mLockScreenColor = Utils.getColorAttrDefaultColor(mContext, R.attr.wallpaperTextColor); + mAmbientDisplayColor = Color.WHITE; + updateAodPositionAndColor(); } private void updateAodPositionAndColor() { @@ -74,18 +72,14 @@ public class UdfpsAnimationKeyguard extends UdfpsAnimation implements DozeReceiv getBurnInOffset(mMaxBurnInOffsetY * 2, false /* xAxis */) - mMaxBurnInOffsetY, mInterpolatedDarkAmount); - updateColor(); - postInvalidateView(); - } - @Override - public void dozeTimeTick() { - updateAodPositionAndColor(); + mFingerprintDrawable.setTint(ColorUtils.blendARGB(mLockScreenColor, + mAmbientDisplayColor, mInterpolatedDarkAmount)); + invalidateSelf(); } @Override - public void onDozeAmountChanged(float linear, float eased) { - mInterpolatedDarkAmount = eased; + public void dozeTimeTick() { updateAodPositionAndColor(); } @@ -94,34 +88,11 @@ public class UdfpsAnimationKeyguard extends UdfpsAnimation implements DozeReceiv if (isIlluminationShowing()) { return; } - - canvas.save(); - canvas.translate(mBurnInOffsetX, mBurnInOffsetY); mFingerprintDrawable.draw(canvas); - canvas.restore(); } - @Override - public void setColorFilter(@Nullable ColorFilter colorFilter) { - - } - - @Override - public int getOpacity() { - return 0; - } - - @Override - protected void updateColor() { - final int lockScreenIconColor = Utils.getColorAttrDefaultColor(mContext, - R.attr.wallpaperTextColor); - final int ambientDisplayIconColor = Color.WHITE; - mFingerprintDrawable.setTint(ColorUtils.blendARGB(lockScreenIconColor, - ambientDisplayIconColor, mInterpolatedDarkAmount)); - } - - @Override - protected void onDestroy() { - mStatusBarStateController.removeCallback(this); + void onDozeAmountChanged(float linear, float eased) { + mInterpolatedDarkAmount = eased; + updateAodPositionAndColor(); } } diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardView.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardView.java new file mode 100644 index 000000000000..6a9356034d22 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardView.java @@ -0,0 +1,60 @@ +/* + * 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.util.AttributeSet; +import android.widget.ImageView; + +import androidx.annotation.Nullable; + +import com.android.systemui.R; + +/** + * View corresponding with udfps_keyguard_view.xml + */ +public class UdfpsKeyguardView extends UdfpsAnimationView { + private final UdfpsKeyguardDrawable mFingerprintDrawable; + private ImageView mFingerprintView; + + public UdfpsKeyguardView(Context context, @Nullable AttributeSet attrs) { + super(context, attrs); + mFingerprintDrawable = new UdfpsKeyguardDrawable(mContext); + } + + @Override + protected void onFinishInflate() { + mFingerprintView = findViewById(R.id.udfps_keyguard_animation_fp_view); + mFingerprintView.setImageDrawable(mFingerprintDrawable); + } + + @Override + public UdfpsDrawable getDrawable() { + return mFingerprintDrawable; + } + + @Override + public boolean dozeTimeTick() { + // TODO: burnin + mFingerprintDrawable.dozeTimeTick(); + return true; + } + + void onDozeAmountChanged(float linear, float eased) { + mFingerprintDrawable.onDozeAmountChanged(linear, eased); + } +} diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java new file mode 100644 index 000000000000..14bb3fee1174 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java @@ -0,0 +1,81 @@ +/* + * 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 com.android.systemui.plugins.statusbar.StatusBarStateController; +import com.android.systemui.statusbar.phone.StatusBar; + +/** + * Class that coordinates non-HBM animations during keyguard authentication. + */ +public class UdfpsKeyguardViewController extends UdfpsAnimationViewController<UdfpsKeyguardView> { + private boolean mForceShow; + + protected UdfpsKeyguardViewController( + UdfpsKeyguardView view, + StatusBarStateController statusBarStateController, + StatusBar statusBar) { + super(view, statusBarStateController, statusBar); + } + + @Override + protected void onViewAttached() { + super.onViewAttached(); + mStatusBarStateController.addCallback(mStateListener); + final float dozeAmount = mStatusBarStateController.getDozeAmount(); + mStateListener.onDozeAmountChanged(dozeAmount, dozeAmount); + } + + @Override + protected void onViewDetached() { + super.onViewDetached(); + mStatusBarStateController.removeCallback(mStateListener); + } + + /** + * Overrides non-force show logic in shouldPauseAuth to still auth. + */ + private void forceShow(boolean forceShow) { + if (mForceShow == forceShow) { + return; + } + + mForceShow = forceShow; + updatePauseAuth(); + // TODO: animate show/hide background protection + } + + /** + * Returns true if the fingerprint manager is running but we want to temporarily pause + * authentication. On the keyguard, we may want to show udfps when the shade + * is expanded, so this can be overridden with the forceShow method. + */ + public boolean shouldPauseAuth() { + if (mForceShow) { + return false; + } + return super.shouldPauseAuth(); + } + + private final StatusBarStateController.StateListener mStateListener = + new StatusBarStateController.StateListener() { + @Override + public void onDozeAmountChanged(float linear, float eased) { + mView.onDozeAmountChanged(linear, eased); + } + }; +} diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java index a52bddc1dcd5..42d0d8438e15 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java @@ -16,10 +16,6 @@ package com.android.systemui.biometrics; -import static com.android.systemui.statusbar.StatusBarState.FULLSCREEN_USER_SWITCHER; -import static com.android.systemui.statusbar.StatusBarState.KEYGUARD; -import static com.android.systemui.statusbar.StatusBarState.SHADE_LOCKED; - import android.annotation.NonNull; import android.annotation.Nullable; import android.content.Context; @@ -39,15 +35,12 @@ import android.widget.FrameLayout; import com.android.systemui.R; import com.android.systemui.doze.DozeReceiver; -import com.android.systemui.plugins.statusbar.StatusBarStateController; -import com.android.systemui.statusbar.phone.StatusBar; /** * A view containing 1) A SurfaceView for HBM, and 2) A normal drawable view for all other * animations. */ -public class UdfpsView extends FrameLayout implements DozeReceiver, UdfpsIlluminator, - StatusBarStateController.StateListener, StatusBar.ExpansionChangedListener { +public class UdfpsView extends FrameLayout implements DozeReceiver, UdfpsIlluminator { private static final String TAG = "UdfpsView"; private static final int DEBUG_TEXT_SIZE_PX = 32; @@ -56,7 +49,7 @@ public class UdfpsView extends FrameLayout implements DozeReceiver, UdfpsIllumin @NonNull private final Paint mDebugTextPaint; @NonNull private UdfpsSurfaceView mHbmSurfaceView; - @Nullable private UdfpsAnimationView mAnimationView; + @Nullable private UdfpsAnimationViewController mAnimationViewController; // Used to obtain the sensor location. @NonNull private FingerprintSensorPropertiesInternal mSensorProps; @@ -64,8 +57,6 @@ public class UdfpsView extends FrameLayout implements DozeReceiver, UdfpsIllumin private final float mSensorTouchAreaCoefficient; @Nullable private String mDebugMessage; private boolean mIlluminationRequested; - private int mStatusBarState; - private boolean mNotificationShadeExpanded; public UdfpsView(Context context, AttributeSet attrs) { super(context, attrs); @@ -108,15 +99,6 @@ public class UdfpsView extends FrameLayout implements DozeReceiver, UdfpsIllumin mSensorProps = properties; } - void setAnimationView(@NonNull UdfpsAnimationView animation) { - mAnimationView = animation; - animation.setParent(this); - - // TODO: Consider using a ViewStub placeholder to maintain positioning and inflating it - // after the animation type has been decided. - addView(animation, 0); - } - @Override public void setHbmCallback(@Nullable HbmCallback callback) { mHbmSurfaceView.setHbmCallback(callback); @@ -124,45 +106,38 @@ public class UdfpsView extends FrameLayout implements DozeReceiver, UdfpsIllumin @Override public void dozeTimeTick() { - if (mAnimationView == null) { - return; - } - mAnimationView.dozeTimeTick(); - } - - @Override - public void onStateChanged(int newState) { - mStatusBarState = newState; - } - - @Override - public void onExpansionChanged(float expansion, boolean expanded) { - mNotificationShadeExpanded = expanded; - - if (mAnimationView != null) { - mAnimationView.onExpansionChanged(expansion, expanded); + if (mAnimationViewController != null) { + mAnimationViewController.dozeTimeTick(); } } @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { super.onLayout(changed, left, top, right, bottom); - mSensorRect.set(0 + mAnimationView.getPaddingX(), - 0 + mAnimationView.getPaddingY(), - 2 * mSensorProps.sensorRadius + mAnimationView.getPaddingX(), - 2 * mSensorProps.sensorRadius + mAnimationView.getPaddingY()); + int paddingX = mAnimationViewController == null ? 0 + : mAnimationViewController.getPaddingX(); + int paddingY = mAnimationViewController == null ? 0 + : mAnimationViewController.getPaddingY(); + mSensorRect.set( + paddingX, + paddingY, + 2 * mSensorProps.sensorRadius + paddingX, + 2 * mSensorProps.sensorRadius + paddingY); mHbmSurfaceView.onSensorRectUpdated(new RectF(mSensorRect)); - mAnimationView.onSensorRectUpdated(new RectF(mSensorRect)); + if (mAnimationViewController != null) { + mAnimationViewController.onSensorRectUpdated(new RectF(mSensorRect)); + } + } + + void setAnimationViewController(UdfpsAnimationViewController animationViewController) { + mAnimationViewController = animationViewController; } @Override protected void onAttachedToWindow() { super.onAttachedToWindow(); Log.v(TAG, "onAttachedToWindow"); - - // Retrieve the colors each time, since it depends on day/night mode - mAnimationView.updateColor(); } @Override @@ -188,7 +163,9 @@ public class UdfpsView extends FrameLayout implements DozeReceiver, UdfpsIllumin boolean isWithinSensorArea(float x, float y) { // The X and Y coordinates of the sensor's center. - final PointF translation = mAnimationView.getTouchTranslation(); + final PointF translation = mAnimationViewController == null + ? new PointF(0, 0) + : mAnimationViewController.getTouchTranslation(); final float cx = mSensorRect.centerX() + translation.x; final float cy = mSensorRect.centerY() + translation.y; // Radii along the X and Y axes. @@ -199,18 +176,7 @@ public class UdfpsView extends FrameLayout implements DozeReceiver, UdfpsIllumin && x < (cx + rx * mSensorTouchAreaCoefficient) && y > (cy - ry * mSensorTouchAreaCoefficient) && y < (cy + ry * mSensorTouchAreaCoefficient) - && !shouldPauseAuth(); - } - - /** - * States where UDFPS should temporarily not be authenticating. Instead of completely stopping - * authentication which would cause the UDFPS icons to abruptly disappear, do it here by not - * sending onFingerDown and smoothly animating away. - */ - boolean shouldPauseAuth() { - return (mNotificationShadeExpanded && mStatusBarState != KEYGUARD) - || mStatusBarState == SHADE_LOCKED - || mStatusBarState == FULLSCREEN_USER_SWITCHER; + && !mAnimationViewController.shouldPauseAuth(); } boolean isIlluminationRequested() { @@ -223,7 +189,9 @@ public class UdfpsView extends FrameLayout implements DozeReceiver, UdfpsIllumin @Override public void startIllumination(@Nullable Runnable onIlluminatedRunnable) { mIlluminationRequested = true; - mAnimationView.onIlluminationStarting(); + if (mAnimationViewController != null) { + mAnimationViewController.onIlluminationStarting(); + } mHbmSurfaceView.setVisibility(View.VISIBLE); mHbmSurfaceView.startIllumination(onIlluminatedRunnable); } @@ -231,7 +199,9 @@ public class UdfpsView extends FrameLayout implements DozeReceiver, UdfpsIllumin @Override public void stopIllumination() { mIlluminationRequested = false; - mAnimationView.onIlluminationStopped(); + if (mAnimationViewController != null) { + mAnimationViewController.onIlluminationStopped(); + } mHbmSurfaceView.setVisibility(View.INVISIBLE); mHbmSurfaceView.stopIllumination(); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java index 07686181649d..d6f4958942dd 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java @@ -246,20 +246,4 @@ public class UdfpsControllerTest extends SysuiTestCase { // THEN the illumination is hidden verify(mUdfpsView).stopIllumination(); } - - @Test - public void registersAndUnregistersViewForCallbacks() throws RemoteException { - mOverlayController.showUdfpsOverlay(TEST_UDFPS_SENSOR_ID, - IUdfpsOverlayController.REASON_AUTH_FPM_KEYGUARD, mUdfpsOverlayControllerCallback); - mFgExecutor.runAllReady(); - verify(mStatusBarStateController).addCallback(mUdfpsController.mStatusBarStateListener); - verify(mStatusBar).addExpansionChangedListener( - mUdfpsController.mStatusBarExpansionListener); - - mOverlayController.hideUdfpsOverlay(TEST_UDFPS_SENSOR_ID); - mFgExecutor.runAllReady(); - verify(mStatusBarStateController).removeCallback(mUdfpsController.mStatusBarStateListener); - verify(mStatusBar).removeExpansionChangedListener( - mUdfpsController.mStatusBarExpansionListener); - } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java new file mode 100644 index 000000000000..480b33556b27 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java @@ -0,0 +1,138 @@ +/* + * Copyright (C) 2020 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 static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.testing.AndroidTestingRunner; +import android.testing.TestableLooper.RunWithLooper; + +import androidx.test.filters.SmallTest; + +import com.android.systemui.SysuiTestCase; +import com.android.systemui.plugins.statusbar.StatusBarStateController; +import com.android.systemui.statusbar.StatusBarState; +import com.android.systemui.statusbar.phone.StatusBar; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import java.util.List; + +@SmallTest +@RunWith(AndroidTestingRunner.class) +@RunWithLooper +public class UdfpsKeyguardViewControllerTest extends SysuiTestCase { + // Dependencies + @Mock + private UdfpsKeyguardView mView; + @Mock + private StatusBarStateController mStatusBarStateController; + @Mock + private StatusBar mStatusBar; + + private UdfpsKeyguardViewController mController; + + // Capture listeners so that they can be used to send events + @Captor private ArgumentCaptor<StatusBarStateController.StateListener> mStateListenerCaptor; + private StatusBarStateController.StateListener mParentListener; + private StatusBarStateController.StateListener mDozeListener; + + @Captor private ArgumentCaptor<StatusBar.ExpansionChangedListener> mExpansionListenerCaptor; + private StatusBar.ExpansionChangedListener mExpansionListener; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + mController = new UdfpsKeyguardViewController( + mView, + mStatusBarStateController, + mStatusBar); + } + + @Test + public void testRegistersExpansionChangedListenerOnAttached() { + mController.onViewAttached(); + captureExpansionListener(); + } + + @Test + public void testRegistersStatusBarStateListenersOnAttached() { + mController.onViewAttached(); + captureStatusBarStateListeners(); + } + + @Test + public void testViewControllerQueriesSBStateOnAttached() { + mController.onViewAttached(); + verify(mStatusBarStateController).getState(); + verify(mStatusBarStateController).getDozeAmount(); + + final float dozeAmount = .88f; + when(mStatusBarStateController.getState()).thenReturn(StatusBarState.SHADE_LOCKED); + when(mStatusBarStateController.getDozeAmount()).thenReturn(dozeAmount); + captureStatusBarStateListeners(); + + mController.onViewAttached(); + verify(mView).setPauseAuth(true); + verify(mView).onDozeAmountChanged(dozeAmount, dozeAmount); + } + + @Test + public void testListenersUnregisteredOnDetached() { + mController.onViewAttached(); + captureStatusBarStateListeners(); + captureExpansionListener(); + mController.onViewDetached(); + + verify(mStatusBarStateController).removeCallback(mParentListener); + verify(mStatusBarStateController).removeCallback(mDozeListener); + verify(mStatusBar).removeExpansionChangedListener(mExpansionListener); + } + + @Test + public void testDozeEventsSentToView() { + mController.onViewAttached(); + captureStatusBarStateListeners(); + + final float linear = .55f; + final float eased = .65f; + mDozeListener.onDozeAmountChanged(linear, eased); + + verify(mView).onDozeAmountChanged(linear, eased); + } + + private void captureStatusBarStateListeners() { + verify(mStatusBarStateController, times(2)).addCallback(mStateListenerCaptor.capture()); + List<StatusBarStateController.StateListener> stateListeners = + mStateListenerCaptor.getAllValues(); + mParentListener = stateListeners.get(0); + mDozeListener = stateListeners.get(1); + } + + private void captureExpansionListener() { + verify(mStatusBar).addExpansionChangedListener(mExpansionListenerCaptor.capture()); + mExpansionListener = mExpansionListenerCaptor.getValue(); + } +} |