Migrate small UDFPS classes to kotlin and backfill missing tests.
Bug: 205875955 Test: atest UdfpsViewTest UdfpsControllerTest Test: manual (enroll manually and BP test app) Change-Id: Ia09f18bffe943ecdf7a73c71faae6f2e2d40e359
This commit is contained in:
@@ -33,7 +33,7 @@ import android.widget.FrameLayout;
|
||||
* - sends sensor rect updates to fingerprint drawable
|
||||
* - optionally can override dozeTimeTick to adjust views for burn-in mitigation
|
||||
*/
|
||||
abstract class UdfpsAnimationView extends FrameLayout {
|
||||
public abstract class UdfpsAnimationView extends FrameLayout {
|
||||
// mAlpha takes into consideration the status bar expansion amount to fade out icon when
|
||||
// the status bar is expanded
|
||||
private int mAlpha;
|
||||
|
||||
@@ -1,202 +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 android.graphics.RectF;
|
||||
|
||||
import com.android.systemui.Dumpable;
|
||||
import com.android.systemui.dump.DumpManager;
|
||||
import com.android.systemui.plugins.statusbar.StatusBarStateController;
|
||||
import com.android.systemui.statusbar.phone.SystemUIDialogManager;
|
||||
import com.android.systemui.statusbar.phone.panelstate.PanelExpansionListener;
|
||||
import com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManager;
|
||||
import com.android.systemui.util.ViewController;
|
||||
|
||||
import java.io.FileDescriptor;
|
||||
import java.io.PrintWriter;
|
||||
|
||||
/**
|
||||
* 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> implements Dumpable {
|
||||
@NonNull final StatusBarStateController mStatusBarStateController;
|
||||
@NonNull final PanelExpansionStateManager mPanelExpansionStateManager;
|
||||
@NonNull final SystemUIDialogManager mDialogManager;
|
||||
@NonNull final DumpManager mDumpManger;
|
||||
|
||||
boolean mNotificationShadeVisible;
|
||||
|
||||
protected UdfpsAnimationViewController(
|
||||
T view,
|
||||
@NonNull StatusBarStateController statusBarStateController,
|
||||
@NonNull PanelExpansionStateManager panelExpansionStateManager,
|
||||
@NonNull SystemUIDialogManager dialogManager,
|
||||
@NonNull DumpManager dumpManager) {
|
||||
super(view);
|
||||
mStatusBarStateController = statusBarStateController;
|
||||
mPanelExpansionStateManager = panelExpansionStateManager;
|
||||
mDialogManager = dialogManager;
|
||||
mDumpManger = dumpManager;
|
||||
}
|
||||
|
||||
abstract @NonNull String getTag();
|
||||
|
||||
@Override
|
||||
protected void onViewAttached() {
|
||||
mPanelExpansionStateManager.addExpansionListener(mPanelExpansionListener);
|
||||
mDialogManager.registerListener(mDialogListener);
|
||||
mDumpManger.registerDumpable(getDumpTag(), this);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onViewDetached() {
|
||||
mPanelExpansionStateManager.removeExpansionListener(mPanelExpansionListener);
|
||||
mDialogManager.unregisterListener(mDialogListener);
|
||||
mDumpManger.unregisterDumpable(getDumpTag());
|
||||
}
|
||||
|
||||
/**
|
||||
* in some cases, onViewAttached is called for the newly added view using an instance of
|
||||
* this controller before onViewDetached is called on the previous view, so we must have a
|
||||
* unique dump tag per instance of this class
|
||||
* @return a unique tag for this instance of this class
|
||||
*/
|
||||
private String getDumpTag() {
|
||||
return getTag() + " (" + this + ")";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
|
||||
pw.println("mNotificationShadeVisible=" + mNotificationShadeVisible);
|
||||
pw.println("shouldPauseAuth()=" + shouldPauseAuth());
|
||||
pw.println("isPauseAuth=" + mView.isPauseAuth());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the fingerprint manager is running but we want to temporarily pause
|
||||
* authentication.
|
||||
*/
|
||||
boolean shouldPauseAuth() {
|
||||
return mNotificationShadeVisible
|
||||
|| mDialogManager.shouldHideAffordance();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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();
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether to listen for touches outside of the view.
|
||||
*/
|
||||
boolean listenForTouchesOutsideView() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called on touches outside of the view if listenForTouchesOutsideView returns true
|
||||
*/
|
||||
void onTouchOutsideView() { }
|
||||
|
||||
private final PanelExpansionListener mPanelExpansionListener = new PanelExpansionListener() {
|
||||
@Override
|
||||
public void onPanelExpansionChanged(
|
||||
float fraction, boolean expanded, boolean tracking) {
|
||||
// Notification shade can be expanded but not visible (fraction: 0.0), for example
|
||||
// when a heads-up notification (HUN) is showing.
|
||||
mNotificationShadeVisible = expanded && fraction > 0f;
|
||||
mView.onExpansionChanged(fraction);
|
||||
updatePauseAuth();
|
||||
}
|
||||
};
|
||||
|
||||
private final SystemUIDialogManager.Listener mDialogListener =
|
||||
(shouldHide) -> updatePauseAuth();
|
||||
}
|
||||
@@ -0,0 +1,170 @@
|
||||
/*
|
||||
* 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.graphics.PointF
|
||||
import android.graphics.RectF
|
||||
import com.android.systemui.Dumpable
|
||||
import com.android.systemui.dump.DumpManager
|
||||
import com.android.systemui.plugins.statusbar.StatusBarStateController
|
||||
import com.android.systemui.statusbar.phone.SystemUIDialogManager
|
||||
import com.android.systemui.statusbar.phone.panelstate.PanelExpansionListener
|
||||
import com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManager
|
||||
import com.android.systemui.util.ViewController
|
||||
import java.io.FileDescriptor
|
||||
import java.io.PrintWriter
|
||||
|
||||
/**
|
||||
* 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 : UdfpsAnimationView>(
|
||||
view: T,
|
||||
protected val statusBarStateController: StatusBarStateController,
|
||||
protected val panelExpansionStateManager: PanelExpansionStateManager,
|
||||
protected val dialogManager: SystemUIDialogManager,
|
||||
private val dumpManager: DumpManager
|
||||
) : ViewController<T>(view), Dumpable {
|
||||
|
||||
protected abstract val tag: String
|
||||
|
||||
private val view: T
|
||||
get() = mView!!
|
||||
|
||||
private val dialogListener = SystemUIDialogManager.Listener { updatePauseAuth() }
|
||||
|
||||
private val panelExpansionListener =
|
||||
PanelExpansionListener { fraction, expanded, tracking ->
|
||||
// Notification shade can be expanded but not visible (fraction: 0.0), for example
|
||||
// when a heads-up notification (HUN) is showing.
|
||||
notificationShadeVisible = expanded && fraction > 0f
|
||||
view.onExpansionChanged(fraction)
|
||||
updatePauseAuth()
|
||||
}
|
||||
|
||||
/** If the notification shade is visible. */
|
||||
var notificationShadeVisible: Boolean = false
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
open val touchTranslation: PointF = PointF(0f, 0f)
|
||||
|
||||
/**
|
||||
* X-Padding to add to left and right of the sensor rectangle area to increase the size of our
|
||||
* window to draw within.
|
||||
*/
|
||||
open val paddingX: Int = 0
|
||||
|
||||
/**
|
||||
* Y-Padding to add to top and bottom of the sensor rectangle area to increase the size of our
|
||||
* window to draw within.
|
||||
*/
|
||||
open val paddingY: Int = 0
|
||||
|
||||
override fun onViewAttached() {
|
||||
panelExpansionStateManager.addExpansionListener(panelExpansionListener)
|
||||
dialogManager.registerListener(dialogListener)
|
||||
dumpManager.registerDumpable(dumpTag, this)
|
||||
}
|
||||
|
||||
override fun onViewDetached() {
|
||||
panelExpansionStateManager.removeExpansionListener(panelExpansionListener)
|
||||
dialogManager.unregisterListener(dialogListener)
|
||||
dumpManager.unregisterDumpable(dumpTag)
|
||||
}
|
||||
|
||||
/**
|
||||
* in some cases, onViewAttached is called for the newly added view using an instance of
|
||||
* this controller before onViewDetached is called on the previous view, so we must have a
|
||||
* unique [dumpTag] per instance of this class.
|
||||
*/
|
||||
private val dumpTag = "$tag ($this)"
|
||||
|
||||
override fun dump(fd: FileDescriptor, pw: PrintWriter, args: Array<String>) {
|
||||
pw.println("mNotificationShadeVisible=$notificationShadeVisible")
|
||||
pw.println("shouldPauseAuth()=" + shouldPauseAuth())
|
||||
pw.println("isPauseAuth=" + view.isPauseAuth)
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the fingerprint manager is running, but we want to temporarily pause
|
||||
* authentication.
|
||||
*/
|
||||
open fun shouldPauseAuth(): Boolean {
|
||||
return notificationShadeVisible || dialogManager.shouldHideAffordance()
|
||||
}
|
||||
|
||||
/**
|
||||
* Send pause auth update to our view.
|
||||
*/
|
||||
fun updatePauseAuth() {
|
||||
if (view.setPauseAuth(shouldPauseAuth())) {
|
||||
view.postInvalidate()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Send sensor position change to our view. This rect contains paddingX and paddingY.
|
||||
*/
|
||||
fun onSensorRectUpdated(sensorRect: RectF) {
|
||||
view.onSensorRectUpdated(sensorRect)
|
||||
}
|
||||
|
||||
/**
|
||||
* Send dozeTimeTick to view in case it wants to handle its burn-in offset.
|
||||
*/
|
||||
fun dozeTimeTick() {
|
||||
if (view.dozeTimeTick()) {
|
||||
view.postInvalidate()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Udfps has started illuminating and the fingerprint manager is working on authenticating.
|
||||
*/
|
||||
fun onIlluminationStarting() {
|
||||
view.onIlluminationStarting()
|
||||
view.postInvalidate()
|
||||
}
|
||||
|
||||
/**
|
||||
* Udfps has stopped illuminating and the fingerprint manager is no longer attempting to
|
||||
* authenticate.
|
||||
*/
|
||||
fun onIlluminationStopped() {
|
||||
view.onIlluminationStopped()
|
||||
view.postInvalidate()
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether to listen for touches outside of the view.
|
||||
*/
|
||||
open fun listenForTouchesOutsideView(): Boolean = false
|
||||
|
||||
/**
|
||||
* Called on touches outside of the view if listenForTouchesOutsideView returns true
|
||||
*/
|
||||
open fun onTouchOutsideView() {}
|
||||
}
|
||||
@@ -13,33 +13,23 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.android.systemui.biometrics
|
||||
|
||||
package com.android.systemui.biometrics;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.AttributeSet;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import android.content.Context
|
||||
import android.util.AttributeSet
|
||||
|
||||
/**
|
||||
* 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
|
||||
* Note that [AuthBiometricUdfpsView] also shows UDFPS animations. At some point we should
|
||||
* de-dupe this if necessary.
|
||||
*/
|
||||
public class UdfpsBpView extends UdfpsAnimationView {
|
||||
private UdfpsFpDrawable mFingerprintDrawable;
|
||||
class UdfpsBpView(context: Context, attrs: AttributeSet?) : UdfpsAnimationView(context, attrs) {
|
||||
|
||||
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);
|
||||
}
|
||||
// Drawable isn't ever added to the view, so we don't currently show anything
|
||||
private val fingerprintDrawable: UdfpsFpDrawable = UdfpsFpDrawable(context)
|
||||
|
||||
@Override
|
||||
UdfpsDrawable getDrawable() {
|
||||
return mFingerprintDrawable;
|
||||
}
|
||||
override fun getDrawable(): UdfpsDrawable = fingerprintDrawable
|
||||
}
|
||||
@@ -13,32 +13,28 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.android.systemui.biometrics
|
||||
|
||||
package com.android.systemui.biometrics;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
|
||||
import com.android.systemui.dump.DumpManager;
|
||||
import com.android.systemui.plugins.statusbar.StatusBarStateController;
|
||||
import com.android.systemui.statusbar.phone.SystemUIDialogManager;
|
||||
import com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManager;
|
||||
import com.android.systemui.dump.DumpManager
|
||||
import com.android.systemui.plugins.statusbar.StatusBarStateController
|
||||
import com.android.systemui.statusbar.phone.SystemUIDialogManager
|
||||
import com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManager
|
||||
|
||||
/**
|
||||
* Class that coordinates non-HBM animations for biometric prompt.
|
||||
*/
|
||||
class UdfpsBpViewController extends UdfpsAnimationViewController<UdfpsBpView> {
|
||||
protected UdfpsBpViewController(
|
||||
@NonNull UdfpsBpView view,
|
||||
@NonNull StatusBarStateController statusBarStateController,
|
||||
@NonNull PanelExpansionStateManager panelExpansionStateManager,
|
||||
@NonNull SystemUIDialogManager systemUIDialogManager,
|
||||
@NonNull DumpManager dumpManager) {
|
||||
super(view, statusBarStateController, panelExpansionStateManager,
|
||||
systemUIDialogManager, dumpManager);
|
||||
}
|
||||
|
||||
@Override
|
||||
@NonNull String getTag() {
|
||||
return "UdfpsBpViewController";
|
||||
}
|
||||
class UdfpsBpViewController(
|
||||
view: UdfpsBpView,
|
||||
statusBarStateController: StatusBarStateController,
|
||||
panelExpansionStateManager: PanelExpansionStateManager,
|
||||
systemUIDialogManager: SystemUIDialogManager,
|
||||
dumpManager: DumpManager
|
||||
) : UdfpsAnimationViewController<UdfpsBpView>(
|
||||
view,
|
||||
statusBarStateController,
|
||||
panelExpansionStateManager,
|
||||
systemUIDialogManager,
|
||||
dumpManager
|
||||
) {
|
||||
override val tag = "UdfpsBpViewController"
|
||||
}
|
||||
@@ -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.content.Context;
|
||||
import android.graphics.ColorFilter;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.RectF;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.graphics.drawable.ShapeDrawable;
|
||||
import android.graphics.drawable.shapes.PathShape;
|
||||
import android.util.PathParser;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.android.systemui.R;
|
||||
|
||||
/**
|
||||
* Abstract base class for drawable displayed when the finger is not touching the
|
||||
* sensor area.
|
||||
*/
|
||||
public abstract class UdfpsDrawable extends Drawable {
|
||||
static final float DEFAULT_STROKE_WIDTH = 3f;
|
||||
|
||||
@NonNull final Context mContext;
|
||||
@NonNull final ShapeDrawable mFingerprintDrawable;
|
||||
private final Paint mPaint;
|
||||
private boolean mIlluminationShowing;
|
||||
|
||||
int mAlpha = 255; // 0 - 255
|
||||
public UdfpsDrawable(@NonNull Context context) {
|
||||
mContext = context;
|
||||
final String fpPath = context.getResources().getString(R.string.config_udfpsIcon);
|
||||
mFingerprintDrawable = new ShapeDrawable(
|
||||
new PathShape(PathParser.createPathFromPathData(fpPath), 72, 72));
|
||||
mFingerprintDrawable.mutate();
|
||||
|
||||
mPaint = mFingerprintDrawable.getPaint();
|
||||
mPaint.setStyle(Paint.Style.STROKE);
|
||||
mPaint.setStrokeCap(Paint.Cap.ROUND);
|
||||
setStrokeWidth(DEFAULT_STROKE_WIDTH);
|
||||
}
|
||||
|
||||
void setStrokeWidth(float strokeWidth) {
|
||||
mPaint.setStrokeWidth(strokeWidth);
|
||||
invalidateSelf();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param sensorRect the rect coordinates for the sensor area
|
||||
*/
|
||||
public void onSensorRectUpdated(@NonNull RectF sensorRect) {
|
||||
final int margin = (int) sensorRect.height() / 8;
|
||||
|
||||
final Rect bounds = new Rect((int) sensorRect.left + margin,
|
||||
(int) sensorRect.top + margin,
|
||||
(int) sensorRect.right - margin,
|
||||
(int) sensorRect.bottom - margin);
|
||||
updateFingerprintIconBounds(bounds);
|
||||
}
|
||||
|
||||
/**
|
||||
* Bounds for the fingerprint icon
|
||||
*/
|
||||
protected void updateFingerprintIconBounds(@NonNull Rect bounds) {
|
||||
mFingerprintDrawable.setBounds(bounds);
|
||||
invalidateSelf();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAlpha(int alpha) {
|
||||
mAlpha = alpha;
|
||||
mFingerprintDrawable.setAlpha(mAlpha);
|
||||
invalidateSelf();
|
||||
}
|
||||
|
||||
boolean isIlluminationShowing() {
|
||||
return mIlluminationShowing;
|
||||
}
|
||||
|
||||
void setIlluminationShowing(boolean showing) {
|
||||
if (mIlluminationShowing == showing) {
|
||||
return;
|
||||
}
|
||||
mIlluminationShowing = showing;
|
||||
invalidateSelf();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setColorFilter(@Nullable ColorFilter colorFilter) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOpacity() {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
/*
|
||||
* 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.ColorFilter
|
||||
import android.graphics.Paint
|
||||
import android.graphics.Rect
|
||||
import android.graphics.RectF
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.graphics.drawable.ShapeDrawable
|
||||
import android.graphics.drawable.shapes.PathShape
|
||||
import android.util.PathParser
|
||||
import com.android.systemui.R
|
||||
|
||||
private const val DEFAULT_STROKE_WIDTH = 3f
|
||||
|
||||
/**
|
||||
* Abstract base class for drawable displayed when the finger is not touching the
|
||||
* sensor area.
|
||||
*/
|
||||
abstract class UdfpsDrawable(
|
||||
protected val context: Context,
|
||||
drawableFactory: (Context) -> ShapeDrawable
|
||||
) : Drawable() {
|
||||
|
||||
constructor(context: Context) : this(context, defaultFactory)
|
||||
|
||||
/** Fingerprint affordance. */
|
||||
val fingerprintDrawable: ShapeDrawable = drawableFactory(context)
|
||||
|
||||
private var _alpha: Int = 255 // 0 - 255
|
||||
|
||||
var strokeWidth: Float = fingerprintDrawable.paint.strokeWidth
|
||||
set(value) {
|
||||
field = value
|
||||
fingerprintDrawable.paint.strokeWidth = value
|
||||
invalidateSelf()
|
||||
}
|
||||
|
||||
var isIlluminationShowing: Boolean = false
|
||||
set(showing) {
|
||||
if (field == showing) {
|
||||
return
|
||||
}
|
||||
field = showing
|
||||
invalidateSelf()
|
||||
}
|
||||
|
||||
/** The [sensorRect] coordinates for the sensor area. */
|
||||
open fun onSensorRectUpdated(sensorRect: RectF) {
|
||||
val margin = sensorRect.height().toInt() / 8
|
||||
val bounds = Rect(
|
||||
sensorRect.left.toInt() + margin,
|
||||
sensorRect.top.toInt() + margin,
|
||||
sensorRect.right.toInt() - margin,
|
||||
sensorRect.bottom.toInt() - margin
|
||||
)
|
||||
updateFingerprintIconBounds(bounds)
|
||||
}
|
||||
|
||||
/** Bounds for the fingerprint icon. */
|
||||
protected open fun updateFingerprintIconBounds(bounds: Rect) {
|
||||
fingerprintDrawable.bounds = bounds
|
||||
invalidateSelf()
|
||||
}
|
||||
|
||||
override fun getAlpha(): Int = _alpha
|
||||
|
||||
override fun setAlpha(alpha: Int) {
|
||||
_alpha = alpha
|
||||
fingerprintDrawable.alpha = alpha
|
||||
invalidateSelf()
|
||||
}
|
||||
|
||||
override fun setColorFilter(colorFilter: ColorFilter?) {}
|
||||
|
||||
override fun getOpacity(): Int = 0
|
||||
}
|
||||
|
||||
private val defaultFactory = { context: Context ->
|
||||
val fpPath = context.resources.getString(R.string.config_udfpsIcon)
|
||||
val drawable = ShapeDrawable(
|
||||
PathShape(PathParser.createPathFromPathData(fpPath), 72f, 72f)
|
||||
)
|
||||
drawable.mutate()
|
||||
drawable.paint.style = Paint.Style.STROKE
|
||||
drawable.paint.strokeCap = Paint.Cap.ROUND
|
||||
drawable.paint.strokeWidth = DEFAULT_STROKE_WIDTH
|
||||
drawable
|
||||
}
|
||||
@@ -102,7 +102,7 @@ public class UdfpsEnrollDrawable extends UdfpsDrawable {
|
||||
|
||||
mSensorOutlinePaint = new Paint(0 /* flags */);
|
||||
mSensorOutlinePaint.setAntiAlias(true);
|
||||
mSensorOutlinePaint.setColor(mContext.getColor(R.color.udfps_moving_target_fill));
|
||||
mSensorOutlinePaint.setColor(context.getColor(R.color.udfps_moving_target_fill));
|
||||
mSensorOutlinePaint.setStyle(Paint.Style.FILL);
|
||||
|
||||
mBlueFill = new Paint(0 /* flags */);
|
||||
@@ -112,10 +112,10 @@ public class UdfpsEnrollDrawable extends UdfpsDrawable {
|
||||
|
||||
mMovingTargetFpIcon = context.getResources()
|
||||
.getDrawable(R.drawable.ic_kg_fingerprint, null);
|
||||
mMovingTargetFpIcon.setTint(mContext.getColor(R.color.udfps_enroll_icon));
|
||||
mMovingTargetFpIcon.setTint(context.getColor(R.color.udfps_enroll_icon));
|
||||
mMovingTargetFpIcon.mutate();
|
||||
|
||||
mFingerprintDrawable.setTint(mContext.getColor(R.color.udfps_enroll_icon));
|
||||
getFingerprintDrawable().setTint(context.getColor(R.color.udfps_enroll_icon));
|
||||
|
||||
mHintColorFaded = context.getColor(R.color.udfps_moving_target_fill);
|
||||
mHintColorHighlight = context.getColor(R.color.udfps_enroll_progress);
|
||||
@@ -404,9 +404,9 @@ public class UdfpsEnrollDrawable extends UdfpsDrawable {
|
||||
if (mSensorRect != null) {
|
||||
canvas.drawOval(mSensorRect, mSensorOutlinePaint);
|
||||
}
|
||||
mFingerprintDrawable.draw(canvas);
|
||||
mFingerprintDrawable.setAlpha(mAlpha);
|
||||
mSensorOutlinePaint.setAlpha(mAlpha);
|
||||
getFingerprintDrawable().draw(canvas);
|
||||
getFingerprintDrawable().setAlpha(getAlpha());
|
||||
mSensorOutlinePaint.setAlpha(getAlpha());
|
||||
}
|
||||
|
||||
// Draw the finger tip or edges hint.
|
||||
|
||||
@@ -66,7 +66,7 @@ public class UdfpsEnrollViewController extends UdfpsAnimationViewController<Udfp
|
||||
}
|
||||
|
||||
@Override
|
||||
@NonNull String getTag() {
|
||||
@NonNull protected String getTag() {
|
||||
return "UdfpsEnrollViewController";
|
||||
}
|
||||
|
||||
|
||||
@@ -13,29 +13,19 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.android.systemui.biometrics
|
||||
|
||||
package com.android.systemui.biometrics;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Canvas;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import android.content.Context
|
||||
import android.graphics.Canvas
|
||||
|
||||
/**
|
||||
* Draws udfps fingerprint if sensor isn't illuminating.
|
||||
*/
|
||||
public class UdfpsFpDrawable extends UdfpsDrawable {
|
||||
|
||||
UdfpsFpDrawable(@NonNull Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(@NonNull Canvas canvas) {
|
||||
if (isIlluminationShowing()) {
|
||||
return;
|
||||
class UdfpsFpDrawable(context: Context) : UdfpsDrawable(context) {
|
||||
override fun draw(canvas: Canvas) {
|
||||
if (isIlluminationShowing) {
|
||||
return
|
||||
}
|
||||
|
||||
mFingerprintDrawable.draw(canvas);
|
||||
fingerprintDrawable.draw(canvas)
|
||||
}
|
||||
}
|
||||
@@ -1,49 +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.util.AttributeSet;
|
||||
import android.widget.ImageView;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.android.systemui.R;
|
||||
|
||||
/**
|
||||
* View corresponding with udfps_fpm_other_view.xml
|
||||
*/
|
||||
public class UdfpsFpmOtherView extends UdfpsAnimationView {
|
||||
private final UdfpsFpDrawable mFingerprintDrawable;
|
||||
private ImageView mFingerprintView;
|
||||
|
||||
public UdfpsFpmOtherView(Context context, @Nullable AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
mFingerprintDrawable = new UdfpsFpDrawable(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onFinishInflate() {
|
||||
mFingerprintView = findViewById(R.id.udfps_fpm_other_fp_view);
|
||||
mFingerprintView.setImageDrawable(mFingerprintDrawable);
|
||||
}
|
||||
|
||||
@Override
|
||||
UdfpsDrawable getDrawable() {
|
||||
return mFingerprintDrawable;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* 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 com.android.systemui.R
|
||||
|
||||
/**
|
||||
* View corresponding with udfps_fpm_other_view.xml
|
||||
*/
|
||||
class UdfpsFpmOtherView(
|
||||
context: Context,
|
||||
attrs: AttributeSet?
|
||||
) : UdfpsAnimationView(context, attrs) {
|
||||
|
||||
private val fingerprintDrawable: UdfpsFpDrawable = UdfpsFpDrawable(context)
|
||||
private lateinit var fingerprintView: ImageView
|
||||
|
||||
override fun onFinishInflate() {
|
||||
fingerprintView = findViewById(R.id.udfps_fpm_other_fp_view)!!
|
||||
fingerprintView.setImageDrawable(fingerprintDrawable)
|
||||
}
|
||||
|
||||
override fun getDrawable(): UdfpsDrawable = fingerprintDrawable
|
||||
}
|
||||
@@ -13,15 +13,13 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.android.systemui.biometrics
|
||||
|
||||
package com.android.systemui.biometrics;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
|
||||
import com.android.systemui.dump.DumpManager;
|
||||
import com.android.systemui.plugins.statusbar.StatusBarStateController;
|
||||
import com.android.systemui.statusbar.phone.SystemUIDialogManager;
|
||||
import com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManager;
|
||||
import com.android.systemui.dump.DumpManager
|
||||
import com.android.systemui.plugins.statusbar.StatusBarStateController
|
||||
import com.android.systemui.statusbar.phone.SystemUIDialogManager
|
||||
import com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManager
|
||||
|
||||
/**
|
||||
* Class that coordinates non-HBM animations for non keyguard, enrollment or biometric prompt
|
||||
@@ -29,19 +27,18 @@ import com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManage
|
||||
*
|
||||
* Currently only shows the fp drawable.
|
||||
*/
|
||||
class UdfpsFpmOtherViewController extends UdfpsAnimationViewController<UdfpsFpmOtherView> {
|
||||
protected UdfpsFpmOtherViewController(
|
||||
@NonNull UdfpsFpmOtherView view,
|
||||
@NonNull StatusBarStateController statusBarStateController,
|
||||
@NonNull PanelExpansionStateManager panelExpansionStateManager,
|
||||
@NonNull SystemUIDialogManager systemUIDialogManager,
|
||||
@NonNull DumpManager dumpManager) {
|
||||
super(view, statusBarStateController, panelExpansionStateManager, systemUIDialogManager,
|
||||
dumpManager);
|
||||
}
|
||||
|
||||
@Override
|
||||
@NonNull String getTag() {
|
||||
return "UdfpsFpmOtherViewController";
|
||||
}
|
||||
class UdfpsFpmOtherViewController(
|
||||
view: UdfpsFpmOtherView,
|
||||
statusBarStateController: StatusBarStateController,
|
||||
panelExpansionStateManager: PanelExpansionStateManager,
|
||||
systemUIDialogManager: SystemUIDialogManager,
|
||||
dumpManager: DumpManager
|
||||
) : UdfpsAnimationViewController<UdfpsFpmOtherView>(
|
||||
view,
|
||||
statusBarStateController,
|
||||
panelExpansionStateManager,
|
||||
systemUIDialogManager,
|
||||
dumpManager
|
||||
) {
|
||||
override val tag = "UdfpsFpmOtherViewController"
|
||||
}
|
||||
@@ -102,7 +102,7 @@ public class UdfpsKeyguardViewController extends UdfpsAnimationViewController<Ud
|
||||
}
|
||||
|
||||
@Override
|
||||
@NonNull String getTag() {
|
||||
@NonNull protected String getTag() {
|
||||
return "UdfpsKeyguardViewController";
|
||||
}
|
||||
|
||||
@@ -115,21 +115,21 @@ public class UdfpsKeyguardViewController extends UdfpsAnimationViewController<Ud
|
||||
@Override
|
||||
protected void onViewAttached() {
|
||||
super.onViewAttached();
|
||||
final float dozeAmount = mStatusBarStateController.getDozeAmount();
|
||||
final float dozeAmount = getStatusBarStateController().getDozeAmount();
|
||||
mLastDozeAmount = dozeAmount;
|
||||
mStateListener.onDozeAmountChanged(dozeAmount, dozeAmount);
|
||||
mStatusBarStateController.addCallback(mStateListener);
|
||||
getStatusBarStateController().addCallback(mStateListener);
|
||||
|
||||
mUdfpsRequested = false;
|
||||
|
||||
mLaunchTransitionFadingAway = mKeyguardStateController.isLaunchTransitionFadingAway();
|
||||
mKeyguardStateController.addCallback(mKeyguardStateControllerCallback);
|
||||
mStatusBarState = mStatusBarStateController.getState();
|
||||
mStatusBarState = getStatusBarStateController().getState();
|
||||
mQsExpanded = mKeyguardViewManager.isQsExpanded();
|
||||
mInputBouncerHiddenAmount = KeyguardBouncer.EXPANSION_HIDDEN;
|
||||
mIsBouncerVisible = mKeyguardViewManager.bouncerIsOrWillBeShowing();
|
||||
mConfigurationController.addCallback(mConfigurationListener);
|
||||
mPanelExpansionStateManager.addExpansionListener(mPanelExpansionListener);
|
||||
getPanelExpansionStateManager().addExpansionListener(mPanelExpansionListener);
|
||||
updateAlpha();
|
||||
updatePauseAuth();
|
||||
|
||||
@@ -144,11 +144,11 @@ public class UdfpsKeyguardViewController extends UdfpsAnimationViewController<Ud
|
||||
mFaceDetectRunning = false;
|
||||
|
||||
mKeyguardStateController.removeCallback(mKeyguardStateControllerCallback);
|
||||
mStatusBarStateController.removeCallback(mStateListener);
|
||||
getStatusBarStateController().removeCallback(mStateListener);
|
||||
mKeyguardViewManager.removeAlternateAuthInterceptor(mAlternateAuthInterceptor);
|
||||
mKeyguardUpdateMonitor.requestFaceAuthOnOccludingApp(false);
|
||||
mConfigurationController.removeCallback(mConfigurationListener);
|
||||
mPanelExpansionStateManager.removeExpansionListener(mPanelExpansionListener);
|
||||
getPanelExpansionStateManager().removeExpansionListener(mPanelExpansionListener);
|
||||
if (mLockScreenShadeTransitionController.getUdfpsKeyguardViewController() == this) {
|
||||
mLockScreenShadeTransitionController.setUdfpsKeyguardViewController(null);
|
||||
}
|
||||
@@ -214,13 +214,13 @@ public class UdfpsKeyguardViewController extends UdfpsAnimationViewController<Ud
|
||||
return false;
|
||||
}
|
||||
|
||||
if (mUdfpsRequested && !mNotificationShadeVisible
|
||||
if (mUdfpsRequested && !getNotificationShadeVisible()
|
||||
&& (!mIsBouncerVisible
|
||||
|| mInputBouncerHiddenAmount != KeyguardBouncer.EXPANSION_VISIBLE)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (mDialogManager.shouldHideAffordance()) {
|
||||
if (getDialogManager().shouldHideAffordance()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,274 +0,0 @@
|
||||
/*
|
||||
* 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 android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.content.Context;
|
||||
import android.content.res.TypedArray;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.PointF;
|
||||
import android.graphics.RectF;
|
||||
import android.hardware.biometrics.SensorLocationInternal;
|
||||
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
|
||||
import android.os.Build;
|
||||
import android.os.UserHandle;
|
||||
import android.provider.Settings;
|
||||
import android.text.TextUtils;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.Log;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.Surface;
|
||||
import android.view.View;
|
||||
import android.widget.FrameLayout;
|
||||
|
||||
import com.android.systemui.R;
|
||||
import com.android.systemui.biometrics.UdfpsHbmTypes.HbmType;
|
||||
import com.android.systemui.doze.DozeReceiver;
|
||||
|
||||
/**
|
||||
* 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 {
|
||||
private static final String TAG = "UdfpsView";
|
||||
|
||||
private static final String SETTING_HBM_TYPE =
|
||||
"com.android.systemui.biometrics.UdfpsSurfaceView.hbmType";
|
||||
private static final @HbmType int DEFAULT_HBM_TYPE = UdfpsHbmTypes.LOCAL_HBM;
|
||||
|
||||
private static final int DEBUG_TEXT_SIZE_PX = 32;
|
||||
|
||||
@NonNull private final RectF mSensorRect;
|
||||
@NonNull private final Paint mDebugTextPaint;
|
||||
private final float mSensorTouchAreaCoefficient;
|
||||
private final int mOnIlluminatedDelayMs;
|
||||
private final @HbmType int mHbmType;
|
||||
|
||||
// Only used for UdfpsHbmTypes.GLOBAL_HBM.
|
||||
@Nullable private UdfpsSurfaceView mGhbmView;
|
||||
// Can be different for enrollment, BiometricPrompt, Keyguard, etc.
|
||||
@Nullable private UdfpsAnimationViewController mAnimationViewController;
|
||||
// Used to obtain the sensor location.
|
||||
@NonNull private FingerprintSensorPropertiesInternal mSensorProps;
|
||||
@Nullable private UdfpsHbmProvider mHbmProvider;
|
||||
@Nullable private String mDebugMessage;
|
||||
private boolean mIlluminationRequested;
|
||||
|
||||
public UdfpsView(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
|
||||
TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.UdfpsView, 0,
|
||||
0);
|
||||
try {
|
||||
if (!a.hasValue(R.styleable.UdfpsView_sensorTouchAreaCoefficient)) {
|
||||
throw new IllegalArgumentException(
|
||||
"UdfpsView must contain sensorTouchAreaCoefficient");
|
||||
}
|
||||
mSensorTouchAreaCoefficient = a.getFloat(
|
||||
R.styleable.UdfpsView_sensorTouchAreaCoefficient, 0f);
|
||||
} finally {
|
||||
a.recycle();
|
||||
}
|
||||
|
||||
mSensorRect = new RectF();
|
||||
|
||||
mDebugTextPaint = new Paint();
|
||||
mDebugTextPaint.setAntiAlias(true);
|
||||
mDebugTextPaint.setColor(Color.BLUE);
|
||||
mDebugTextPaint.setTextSize(DEBUG_TEXT_SIZE_PX);
|
||||
|
||||
mOnIlluminatedDelayMs = mContext.getResources().getInteger(
|
||||
com.android.internal.R.integer.config_udfps_illumination_transition_ms);
|
||||
|
||||
if (Build.IS_ENG || Build.IS_USERDEBUG) {
|
||||
mHbmType = Settings.Secure.getIntForUser(mContext.getContentResolver(),
|
||||
SETTING_HBM_TYPE, DEFAULT_HBM_TYPE, UserHandle.USER_CURRENT);
|
||||
} else {
|
||||
mHbmType = DEFAULT_HBM_TYPE;
|
||||
}
|
||||
}
|
||||
|
||||
// Don't propagate any touch events to the child views.
|
||||
@Override
|
||||
public boolean onInterceptTouchEvent(MotionEvent ev) {
|
||||
return mAnimationViewController == null
|
||||
|| !mAnimationViewController.shouldPauseAuth();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onFinishInflate() {
|
||||
if (mHbmType == UdfpsHbmTypes.GLOBAL_HBM) {
|
||||
mGhbmView = findViewById(R.id.hbm_view);
|
||||
}
|
||||
}
|
||||
|
||||
void setSensorProperties(@NonNull FingerprintSensorPropertiesInternal properties) {
|
||||
mSensorProps = properties;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setHbmProvider(@Nullable UdfpsHbmProvider hbmProvider) {
|
||||
mHbmProvider = hbmProvider;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dozeTimeTick() {
|
||||
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);
|
||||
int paddingX = mAnimationViewController == null ? 0
|
||||
: mAnimationViewController.getPaddingX();
|
||||
int paddingY = mAnimationViewController == null ? 0
|
||||
: mAnimationViewController.getPaddingY();
|
||||
final SensorLocationInternal location = mSensorProps.getLocation();
|
||||
mSensorRect.set(
|
||||
paddingX,
|
||||
paddingY,
|
||||
2 * location.sensorRadius + paddingX,
|
||||
2 * location.sensorRadius + paddingY);
|
||||
|
||||
if (mAnimationViewController != null) {
|
||||
mAnimationViewController.onSensorRectUpdated(new RectF(mSensorRect));
|
||||
}
|
||||
}
|
||||
|
||||
void onTouchOutsideView() {
|
||||
if (mAnimationViewController != null) {
|
||||
mAnimationViewController.onTouchOutsideView();
|
||||
}
|
||||
}
|
||||
|
||||
void setAnimationViewController(
|
||||
@Nullable UdfpsAnimationViewController animationViewController) {
|
||||
mAnimationViewController = animationViewController;
|
||||
}
|
||||
|
||||
@Nullable UdfpsAnimationViewController getAnimationViewController() {
|
||||
return mAnimationViewController;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onAttachedToWindow() {
|
||||
super.onAttachedToWindow();
|
||||
Log.v(TAG, "onAttachedToWindow");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDetachedFromWindow() {
|
||||
super.onDetachedFromWindow();
|
||||
Log.v(TAG, "onDetachedFromWindow");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDraw(Canvas canvas) {
|
||||
super.onDraw(canvas);
|
||||
if (!mIlluminationRequested) {
|
||||
if (!TextUtils.isEmpty(mDebugMessage)) {
|
||||
canvas.drawText(mDebugMessage, 0, 160, mDebugTextPaint);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void setDebugMessage(String message) {
|
||||
mDebugMessage = message;
|
||||
postInvalidate();
|
||||
}
|
||||
|
||||
boolean isWithinSensorArea(float x, float y) {
|
||||
// The X and Y coordinates of the sensor's center.
|
||||
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.
|
||||
final float rx = (mSensorRect.right - mSensorRect.left) / 2.0f;
|
||||
final float ry = (mSensorRect.bottom - mSensorRect.top) / 2.0f;
|
||||
|
||||
return x > (cx - rx * mSensorTouchAreaCoefficient)
|
||||
&& x < (cx + rx * mSensorTouchAreaCoefficient)
|
||||
&& y > (cy - ry * mSensorTouchAreaCoefficient)
|
||||
&& y < (cy + ry * mSensorTouchAreaCoefficient)
|
||||
&& !mAnimationViewController.shouldPauseAuth();
|
||||
}
|
||||
|
||||
boolean isIlluminationRequested() {
|
||||
return mIlluminationRequested;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param onIlluminatedRunnable Runs when the first illumination frame reaches the panel.
|
||||
*/
|
||||
@Override
|
||||
public void startIllumination(@Nullable Runnable onIlluminatedRunnable) {
|
||||
mIlluminationRequested = true;
|
||||
if (mAnimationViewController != null) {
|
||||
mAnimationViewController.onIlluminationStarting();
|
||||
}
|
||||
|
||||
if (mGhbmView != null) {
|
||||
mGhbmView.setGhbmIlluminationListener(this::doIlluminate);
|
||||
mGhbmView.setVisibility(View.VISIBLE);
|
||||
mGhbmView.startGhbmIllumination(onIlluminatedRunnable);
|
||||
} else {
|
||||
doIlluminate(null /* surface */, onIlluminatedRunnable);
|
||||
}
|
||||
}
|
||||
|
||||
private void doIlluminate(@Nullable Surface surface, @Nullable Runnable onIlluminatedRunnable) {
|
||||
if (mGhbmView != null && surface == null) {
|
||||
Log.e(TAG, "doIlluminate | surface must be non-null for GHBM");
|
||||
}
|
||||
if (mHbmProvider != null) {
|
||||
mHbmProvider.enableHbm(mHbmType, surface, () -> {
|
||||
if (mGhbmView != null) {
|
||||
mGhbmView.drawIlluminationDot(mSensorRect);
|
||||
}
|
||||
if (onIlluminatedRunnable != null) {
|
||||
// No framework API can reliably tell when a frame reaches the panel. A timeout
|
||||
// is the safest solution.
|
||||
postDelayed(onIlluminatedRunnable, mOnIlluminatedDelayMs);
|
||||
} else {
|
||||
Log.w(TAG, "doIlluminate | onIlluminatedRunnable is null");
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stopIllumination() {
|
||||
mIlluminationRequested = false;
|
||||
if (mAnimationViewController != null) {
|
||||
mAnimationViewController.onIlluminationStopped();
|
||||
}
|
||||
if (mGhbmView != null) {
|
||||
mGhbmView.setGhbmIlluminationListener(null);
|
||||
mGhbmView.setVisibility(View.INVISIBLE);
|
||||
}
|
||||
if (mHbmProvider != null) {
|
||||
mHbmProvider.disableHbm(null /* onHbmDisabled */);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,220 @@
|
||||
/*
|
||||
* 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 android.content.Context
|
||||
import android.graphics.Canvas
|
||||
import android.graphics.Color
|
||||
import android.graphics.Paint
|
||||
import android.graphics.PointF
|
||||
import android.graphics.RectF
|
||||
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal
|
||||
import android.os.Build
|
||||
import android.os.UserHandle
|
||||
import android.provider.Settings
|
||||
import android.util.AttributeSet
|
||||
import android.util.Log
|
||||
import android.view.MotionEvent
|
||||
import android.view.Surface
|
||||
import android.widget.FrameLayout
|
||||
import com.android.systemui.R
|
||||
import com.android.systemui.doze.DozeReceiver
|
||||
import com.android.systemui.biometrics.UdfpsHbmTypes.HbmType
|
||||
|
||||
private const val TAG = "UdfpsView"
|
||||
private const val SETTING_HBM_TYPE = "com.android.systemui.biometrics.UdfpsSurfaceView.hbmType"
|
||||
@HbmType
|
||||
private const val DEFAULT_HBM_TYPE = UdfpsHbmTypes.LOCAL_HBM
|
||||
|
||||
/**
|
||||
* A view containing 1) A SurfaceView for HBM, and 2) A normal drawable view for all other
|
||||
* animations.
|
||||
*/
|
||||
class UdfpsView(
|
||||
context: Context,
|
||||
attrs: AttributeSet?
|
||||
) : FrameLayout(context, attrs), DozeReceiver, UdfpsIlluminator {
|
||||
|
||||
private val sensorRect = RectF()
|
||||
private var hbmProvider: UdfpsHbmProvider? = null
|
||||
private val debugTextPaint = Paint().apply {
|
||||
isAntiAlias = true
|
||||
color = Color.BLUE
|
||||
textSize = 32f
|
||||
}
|
||||
|
||||
private val sensorTouchAreaCoefficient: Float =
|
||||
context.theme.obtainStyledAttributes(attrs, R.styleable.UdfpsView, 0, 0).use { a ->
|
||||
require(a.hasValue(R.styleable.UdfpsView_sensorTouchAreaCoefficient)) {
|
||||
"UdfpsView must contain sensorTouchAreaCoefficient"
|
||||
}
|
||||
a.getFloat(R.styleable.UdfpsView_sensorTouchAreaCoefficient, 0f)
|
||||
}
|
||||
|
||||
private val onIlluminatedDelayMs = context.resources.getInteger(
|
||||
com.android.internal.R.integer.config_udfps_illumination_transition_ms
|
||||
).toLong()
|
||||
|
||||
@HbmType
|
||||
private val hbmType = if (Build.IS_ENG || Build.IS_USERDEBUG) {
|
||||
Settings.Secure.getIntForUser(
|
||||
context.contentResolver,
|
||||
SETTING_HBM_TYPE,
|
||||
DEFAULT_HBM_TYPE,
|
||||
UserHandle.USER_CURRENT
|
||||
)
|
||||
} else {
|
||||
DEFAULT_HBM_TYPE
|
||||
}
|
||||
|
||||
// Only used for UdfpsHbmTypes.GLOBAL_HBM.
|
||||
private var ghbmView: UdfpsSurfaceView? = null
|
||||
|
||||
/** View controller (can be different for enrollment, BiometricPrompt, Keyguard, etc.). */
|
||||
var animationViewController: UdfpsAnimationViewController<*>? = null
|
||||
|
||||
/** Properties used to obtain the sensor location. */
|
||||
var sensorProperties: FingerprintSensorPropertiesInternal? = null
|
||||
|
||||
/** Debug message. */
|
||||
var debugMessage: String? = null
|
||||
set(value) {
|
||||
field = value
|
||||
postInvalidate()
|
||||
}
|
||||
|
||||
/** When [startIllumination] has been called but not stopped via [stopIllumination]. */
|
||||
var isIlluminationRequested: Boolean = false
|
||||
private set
|
||||
|
||||
override fun setHbmProvider(provider: UdfpsHbmProvider?) {
|
||||
hbmProvider = provider
|
||||
}
|
||||
|
||||
// Don't propagate any touch events to the child views.
|
||||
override fun onInterceptTouchEvent(ev: MotionEvent): Boolean {
|
||||
return (animationViewController == null || !animationViewController!!.shouldPauseAuth())
|
||||
}
|
||||
|
||||
override fun onFinishInflate() {
|
||||
if (hbmType == UdfpsHbmTypes.GLOBAL_HBM) {
|
||||
ghbmView = findViewById(R.id.hbm_view)
|
||||
}
|
||||
}
|
||||
|
||||
override fun dozeTimeTick() {
|
||||
animationViewController?.dozeTimeTick()
|
||||
}
|
||||
|
||||
override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
|
||||
super.onLayout(changed, left, top, right, bottom)
|
||||
|
||||
val paddingX = animationViewController?.paddingX ?: 0
|
||||
val paddingY = animationViewController?.paddingY ?: 0
|
||||
val sensorRadius = sensorProperties?.location?.sensorRadius ?: 0
|
||||
|
||||
sensorRect.set(
|
||||
paddingX.toFloat(),
|
||||
paddingY.toFloat(),
|
||||
(2 * sensorRadius + paddingX).toFloat(),
|
||||
(2 * sensorRadius + paddingY).toFloat()
|
||||
)
|
||||
animationViewController?.onSensorRectUpdated(RectF(sensorRect))
|
||||
}
|
||||
|
||||
fun onTouchOutsideView() {
|
||||
animationViewController?.onTouchOutsideView()
|
||||
}
|
||||
|
||||
override fun onAttachedToWindow() {
|
||||
super.onAttachedToWindow()
|
||||
Log.v(TAG, "onAttachedToWindow")
|
||||
}
|
||||
|
||||
override fun onDetachedFromWindow() {
|
||||
super.onDetachedFromWindow()
|
||||
Log.v(TAG, "onDetachedFromWindow")
|
||||
}
|
||||
|
||||
override fun onDraw(canvas: Canvas) {
|
||||
super.onDraw(canvas)
|
||||
if (!isIlluminationRequested) {
|
||||
if (!debugMessage.isNullOrEmpty()) {
|
||||
canvas.drawText(debugMessage!!, 0f, 160f, debugTextPaint)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun isWithinSensorArea(x: Float, y: Float): Boolean {
|
||||
// The X and Y coordinates of the sensor's center.
|
||||
val translation = animationViewController?.touchTranslation ?: PointF(0f, 0f)
|
||||
val cx = sensorRect.centerX() + translation.x
|
||||
val cy = sensorRect.centerY() + translation.y
|
||||
// Radii along the X and Y axes.
|
||||
val rx = (sensorRect.right - sensorRect.left) / 2.0f
|
||||
val ry = (sensorRect.bottom - sensorRect.top) / 2.0f
|
||||
|
||||
return x > cx - rx * sensorTouchAreaCoefficient &&
|
||||
x < cx + rx * sensorTouchAreaCoefficient &&
|
||||
y > cy - ry * sensorTouchAreaCoefficient &&
|
||||
y < cy + ry * sensorTouchAreaCoefficient &&
|
||||
!(animationViewController?.shouldPauseAuth() ?: false)
|
||||
}
|
||||
|
||||
/**
|
||||
* Start and run [onIlluminatedRunnable] when the first illumination frame reaches the panel.
|
||||
*/
|
||||
override fun startIllumination(onIlluminatedRunnable: Runnable?) {
|
||||
isIlluminationRequested = true
|
||||
animationViewController?.onIlluminationStarting()
|
||||
|
||||
val gView = ghbmView
|
||||
if (gView != null) {
|
||||
gView.setGhbmIlluminationListener(this::doIlluminate)
|
||||
gView.visibility = VISIBLE
|
||||
gView.startGhbmIllumination(onIlluminatedRunnable)
|
||||
} else {
|
||||
doIlluminate(null /* surface */, onIlluminatedRunnable)
|
||||
}
|
||||
}
|
||||
|
||||
private fun doIlluminate(surface: Surface?, onIlluminatedRunnable: Runnable?) {
|
||||
if (ghbmView != null && surface == null) {
|
||||
Log.e(TAG, "doIlluminate | surface must be non-null for GHBM")
|
||||
}
|
||||
|
||||
hbmProvider?.enableHbm(hbmType, surface) {
|
||||
ghbmView?.drawIlluminationDot(sensorRect)
|
||||
if (onIlluminatedRunnable != null) {
|
||||
// No framework API can reliably tell when a frame reaches the panel. A timeout
|
||||
// is the safest solution.
|
||||
postDelayed(onIlluminatedRunnable, onIlluminatedDelayMs)
|
||||
} else {
|
||||
Log.w(TAG, "doIlluminate | onIlluminatedRunnable is null")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun stopIllumination() {
|
||||
isIlluminationRequested = false
|
||||
animationViewController?.onIlluminationStopped()
|
||||
ghbmView?.let { view ->
|
||||
view.setGhbmIlluminationListener(null)
|
||||
view.visibility = INVISIBLE
|
||||
}
|
||||
hbmProvider?.disableHbm(null /* onHbmDisabled */)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* 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.hardware.biometrics.ComponentInfoInternal
|
||||
import android.hardware.biometrics.SensorLocationInternal
|
||||
import android.hardware.biometrics.SensorProperties
|
||||
import android.hardware.fingerprint.FingerprintSensorProperties
|
||||
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal
|
||||
|
||||
/** Creates properties from the sensor location with test values. */
|
||||
fun SensorLocationInternal.asFingerprintSensorProperties(
|
||||
sensorId: Int = 22,
|
||||
@SensorProperties.Strength sensorStrength: Int = SensorProperties.STRENGTH_WEAK,
|
||||
@FingerprintSensorProperties.SensorType sensorType: Int =
|
||||
FingerprintSensorProperties.TYPE_UDFPS_OPTICAL,
|
||||
maxEnrollmentsPerUser: Int = 1,
|
||||
info: List<ComponentInfoInternal> = listOf(ComponentInfoInternal("a", "b", "c", "d", "e")),
|
||||
resetLockoutRequiresHardwareAuthToken: Boolean = false
|
||||
) = FingerprintSensorPropertiesInternal(
|
||||
sensorId,
|
||||
sensorStrength,
|
||||
maxEnrollmentsPerUser,
|
||||
info,
|
||||
sensorType,
|
||||
resetLockoutRequiresHardwareAuthToken,
|
||||
listOf(this)
|
||||
)
|
||||
@@ -165,7 +165,11 @@ public class UdfpsControllerTest extends SysuiTestCase {
|
||||
@Mock
|
||||
private UdfpsKeyguardView mKeyguardView;
|
||||
@Mock
|
||||
private UdfpsKeyguardViewController mUdfpsKeyguardViewController;
|
||||
private UdfpsBpView mBpView;
|
||||
@Mock
|
||||
private UdfpsFpmOtherView mFpmOtherView;
|
||||
private UdfpsAnimationViewController mUdfpsKeyguardViewController =
|
||||
mock(UdfpsKeyguardViewController.class);
|
||||
@Mock
|
||||
private TypedArray mBrightnessValues;
|
||||
@Mock
|
||||
@@ -192,6 +196,10 @@ public class UdfpsControllerTest extends SysuiTestCase {
|
||||
.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_other_view, null))
|
||||
.thenReturn(mFpmOtherView);
|
||||
when(mEnrollView.getContext()).thenReturn(mContext);
|
||||
when(mKeyguardStateController.isOccluded()).thenReturn(false);
|
||||
final List<FingerprintSensorPropertiesInternal> props = new ArrayList<>();
|
||||
@@ -340,7 +348,7 @@ public class UdfpsControllerTest extends SysuiTestCase {
|
||||
when(mKeyguardStateController.canDismissLockScreen()).thenReturn(false);
|
||||
when(mUdfpsView.isWithinSensorArea(anyFloat(), anyFloat())).thenReturn(true);
|
||||
when(mUdfpsView.getAnimationViewController()).thenReturn(
|
||||
mock(UdfpsEnrollViewController.class));
|
||||
(UdfpsAnimationViewController) mock(UdfpsEnrollViewController.class));
|
||||
|
||||
// GIVEN that the overlay is showing
|
||||
mOverlayController.showUdfpsOverlay(TEST_UDFPS_SENSOR_ID,
|
||||
|
||||
@@ -0,0 +1,172 @@
|
||||
/*
|
||||
* 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.graphics.PointF
|
||||
import android.graphics.RectF
|
||||
import android.hardware.biometrics.SensorLocationInternal
|
||||
import android.testing.AndroidTestingRunner
|
||||
import android.testing.TestableLooper
|
||||
import android.testing.ViewUtils
|
||||
import android.view.LayoutInflater
|
||||
import android.view.Surface
|
||||
import androidx.test.filters.SmallTest
|
||||
import com.android.systemui.R
|
||||
import com.android.systemui.SysuiTestCase
|
||||
import com.android.systemui.util.mockito.any
|
||||
import com.android.systemui.util.mockito.mock
|
||||
import com.android.systemui.util.mockito.withArgCaptor
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import org.junit.After
|
||||
import org.junit.Before
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.mockito.Mock
|
||||
import org.mockito.Mockito.anyInt
|
||||
import org.mockito.Mockito.nullable
|
||||
import org.mockito.Mockito.never
|
||||
import org.mockito.Mockito.`when` as whenever
|
||||
import org.mockito.Mockito.verify
|
||||
import org.mockito.junit.MockitoJUnit
|
||||
|
||||
private const val DISPLAY_ID = "" // default display id
|
||||
private const val SENSOR_X = 50
|
||||
private const val SENSOR_Y = 250
|
||||
private const val SENSOR_RADIUS = 10
|
||||
|
||||
@SmallTest
|
||||
@RunWith(AndroidTestingRunner::class)
|
||||
@TestableLooper.RunWithLooper
|
||||
class UdfpsViewTest : SysuiTestCase() {
|
||||
|
||||
@JvmField @Rule
|
||||
var rule = MockitoJUnit.rule()
|
||||
|
||||
@Mock
|
||||
lateinit var hbmProvider: UdfpsHbmProvider
|
||||
@Mock
|
||||
lateinit var animationViewController: UdfpsAnimationViewController<UdfpsAnimationView>
|
||||
|
||||
private lateinit var view: UdfpsView
|
||||
|
||||
@Before
|
||||
fun setup() {
|
||||
context.setTheme(R.style.Theme_AppCompat)
|
||||
context.orCreateTestableResources.addOverride(
|
||||
com.android.internal.R.integer.config_udfps_illumination_transition_ms, 0)
|
||||
view = LayoutInflater.from(context).inflate(R.layout.udfps_view, null) as UdfpsView
|
||||
view.animationViewController = animationViewController
|
||||
view.sensorProperties =
|
||||
SensorLocationInternal(DISPLAY_ID, SENSOR_X, SENSOR_Y, SENSOR_RADIUS)
|
||||
.asFingerprintSensorProperties()
|
||||
view.setHbmProvider(hbmProvider)
|
||||
ViewUtils.attachView(view)
|
||||
}
|
||||
|
||||
@After
|
||||
fun cleanup() {
|
||||
ViewUtils.detachView(view)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun forwardsEvents() {
|
||||
view.dozeTimeTick()
|
||||
verify(animationViewController).dozeTimeTick()
|
||||
|
||||
view.onTouchOutsideView()
|
||||
verify(animationViewController).onTouchOutsideView()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun layoutSizeFitsSensor() {
|
||||
val params = withArgCaptor<RectF> {
|
||||
verify(animationViewController).onSensorRectUpdated(capture())
|
||||
}
|
||||
assertThat(params.width()).isAtLeast(2f * SENSOR_RADIUS)
|
||||
assertThat(params.height()).isAtLeast(2f * SENSOR_RADIUS)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun isWithinSensorAreaAndPaused() = isWithinSensorArea(paused = true)
|
||||
|
||||
@Test
|
||||
fun isWithinSensorAreaAndNotPaused() = isWithinSensorArea(paused = false)
|
||||
|
||||
private fun isWithinSensorArea(paused: Boolean) {
|
||||
whenever(animationViewController.shouldPauseAuth()).thenReturn(paused)
|
||||
whenever(animationViewController.touchTranslation).thenReturn(PointF(0f, 0f))
|
||||
val end = (SENSOR_RADIUS * 2) - 1
|
||||
for (x in 1 until end) {
|
||||
for (y in 1 until end) {
|
||||
assertThat(view.isWithinSensorArea(x.toFloat(), y.toFloat())).isEqualTo(!paused)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun isWithinSensorAreaWhenTranslated() {
|
||||
val offset = PointF(100f, 200f)
|
||||
whenever(animationViewController.touchTranslation).thenReturn(offset)
|
||||
val end = (SENSOR_RADIUS * 2) - 1
|
||||
for (x in 0 until offset.x.toInt() step 2) {
|
||||
for (y in 0 until offset.y.toInt() step 2) {
|
||||
assertThat(view.isWithinSensorArea(x.toFloat(), y.toFloat())).isFalse()
|
||||
}
|
||||
}
|
||||
for (x in offset.x.toInt() + 1 until offset.x.toInt() + end) {
|
||||
for (y in offset.y.toInt() + 1 until offset.y.toInt() + end) {
|
||||
assertThat(view.isWithinSensorArea(x.toFloat(), y.toFloat())).isTrue()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun isNotWithinSensorArea() {
|
||||
whenever(animationViewController.touchTranslation).thenReturn(PointF(0f, 0f))
|
||||
assertThat(view.isWithinSensorArea(SENSOR_RADIUS * 2.5f, SENSOR_RADIUS.toFloat())).isFalse()
|
||||
assertThat(view.isWithinSensorArea(SENSOR_RADIUS.toFloat(), SENSOR_RADIUS * 2.5f)).isFalse()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun startAndStopIllumination() {
|
||||
val onDone: Runnable = mock()
|
||||
view.startIllumination(onDone)
|
||||
|
||||
val illuminator = withArgCaptor<Runnable> {
|
||||
verify(hbmProvider).enableHbm(anyInt(), nullable(Surface::class.java), capture())
|
||||
}
|
||||
|
||||
assertThat(view.isIlluminationRequested).isTrue()
|
||||
verify(animationViewController).onIlluminationStarting()
|
||||
verify(animationViewController, never()).onIlluminationStopped()
|
||||
verify(onDone, never()).run()
|
||||
|
||||
// fake illumination event
|
||||
illuminator.run()
|
||||
waitForLooper()
|
||||
verify(onDone).run()
|
||||
verify(hbmProvider, never()).disableHbm(any())
|
||||
|
||||
view.stopIllumination()
|
||||
assertThat(view.isIlluminationRequested).isFalse()
|
||||
verify(animationViewController).onIlluminationStopped()
|
||||
verify(hbmProvider).disableHbm(nullable(Runnable::class.java))
|
||||
}
|
||||
|
||||
private fun waitForLooper() = TestableLooper.get(this).processAllMessages()
|
||||
}
|
||||
Reference in New Issue
Block a user