1 /* 2 * Copyright (C) 2022 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.settings.biometrics.fingerprint; 18 19 import android.content.Context; 20 import android.graphics.Rect; 21 import android.graphics.RectF; 22 import android.hardware.fingerprint.FingerprintSensorProperties; 23 import android.os.Handler; 24 import android.os.Looper; 25 import android.util.AttributeSet; 26 import android.util.RotationUtils; 27 import android.view.Gravity; 28 import android.view.Surface; 29 import android.view.ViewGroup; 30 import android.widget.FrameLayout; 31 import android.widget.ImageView; 32 33 import androidx.annotation.NonNull; 34 import androidx.annotation.Nullable; 35 36 import com.android.settings.R; 37 import com.android.systemui.biometrics.shared.model.UdfpsOverlayParams; 38 39 /** 40 * View corresponding with udfps_enroll_view.xml 41 */ 42 public class UdfpsEnrollView extends FrameLayout implements UdfpsEnrollHelper.Listener { 43 private static final String TAG = "UdfpsEnrollView"; 44 @NonNull 45 private final UdfpsEnrollDrawable mFingerprintDrawable; 46 @NonNull 47 private final UdfpsEnrollProgressBarDrawable mFingerprintProgressDrawable; 48 @NonNull 49 private final Handler mHandler; 50 51 @NonNull 52 private ImageView mFingerprintProgressView; 53 54 private int mProgressBarRadius; 55 56 // sensorRect may be bigger than the sensor. True sensor dimensions are defined in 57 // overlayParams.sensorBounds 58 private Rect mSensorRect; 59 private UdfpsOverlayParams mOverlayParams; 60 UdfpsEnrollView(Context context, @Nullable AttributeSet attrs)61 public UdfpsEnrollView(Context context, @Nullable AttributeSet attrs) { 62 super(context, attrs); 63 mFingerprintDrawable = new UdfpsEnrollDrawable(mContext, attrs); 64 mFingerprintProgressDrawable = new UdfpsEnrollProgressBarDrawable(context, attrs); 65 mHandler = new Handler(Looper.getMainLooper()); 66 } 67 68 @Override onFinishInflate()69 protected void onFinishInflate() { 70 ImageView fingerprintView = findViewById(R.id.udfps_enroll_animation_fp_view); 71 fingerprintView.setImageDrawable(mFingerprintDrawable); 72 mFingerprintProgressView = findViewById(R.id.udfps_enroll_animation_fp_progress_view); 73 mFingerprintProgressView.setImageDrawable(mFingerprintProgressDrawable); 74 } 75 76 // Implements UdfpsEnrollHelper.Listener 77 @Override onEnrollmentProgress(int remaining, int totalSteps)78 public void onEnrollmentProgress(int remaining, int totalSteps) { 79 mHandler.post(() -> { 80 mFingerprintProgressDrawable.onEnrollmentProgress(remaining, totalSteps); 81 mFingerprintDrawable.onEnrollmentProgress(remaining, totalSteps); 82 }); 83 } 84 85 @Override onEnrollmentHelp(int remaining, int totalSteps)86 public void onEnrollmentHelp(int remaining, int totalSteps) { 87 mHandler.post(() -> mFingerprintProgressDrawable.onEnrollmentHelp(remaining, totalSteps)); 88 } 89 90 @Override onAcquired(boolean animateIfLastStepGood)91 public void onAcquired(boolean animateIfLastStepGood) { 92 mHandler.post(() -> { 93 onFingerUp(); 94 if (animateIfLastStepGood) mFingerprintProgressDrawable.onLastStepAcquired(); 95 }); 96 } 97 98 @Override onPointerDown(int sensorId)99 public void onPointerDown(int sensorId) { 100 onFingerDown(); 101 } 102 103 @Override onPointerUp(int sensorId)104 public void onPointerUp(int sensorId) { 105 onFingerUp(); 106 } 107 getOverlayParams()108 public UdfpsOverlayParams getOverlayParams() { 109 return mOverlayParams; 110 } 111 112 /** 113 * Set UdfpsOverlayParams 114 */ setOverlayParams(UdfpsOverlayParams params)115 public void setOverlayParams(UdfpsOverlayParams params) { 116 mOverlayParams = params; 117 118 post(() -> { 119 mProgressBarRadius = 120 (int) (mOverlayParams.getScaleFactor() * getContext().getResources().getInteger( 121 R.integer.config_udfpsEnrollProgressBar)); 122 mSensorRect = new Rect(mOverlayParams.getSensorBounds()); 123 124 onSensorRectUpdated(); 125 }); 126 } 127 128 /** 129 * Set UdfpsEnrollHelper 130 */ setEnrollHelper(UdfpsEnrollHelper enrollHelper)131 public void setEnrollHelper(UdfpsEnrollHelper enrollHelper) { 132 mFingerprintDrawable.setEnrollHelper(enrollHelper); 133 enrollHelper.setListener(this); 134 } 135 136 /** 137 * Adjust progress bar radius only for decreasing. 138 * @param decreasePadding the decrease padding 139 */ setDecreasePadding(int decreasePadding)140 void setDecreasePadding(int decreasePadding) { 141 mProgressBarRadius -= decreasePadding; 142 onSensorRectUpdated(); 143 } 144 onSensorRectUpdated()145 private void onSensorRectUpdated() { 146 updateDimensions(); 147 148 // Updates sensor rect in relation to the overlay view 149 mSensorRect.set(getPaddingX(), getPaddingY(), 150 (mOverlayParams.getSensorBounds().width() + getPaddingX()), 151 (mOverlayParams.getSensorBounds().height() + getPaddingY())); 152 mFingerprintDrawable.onSensorRectUpdated(new RectF(mSensorRect)); 153 } 154 updateDimensions()155 private void updateDimensions() { 156 // Original sensorBounds assume portrait mode. 157 final Rect rotatedBounds = new Rect(mOverlayParams.getSensorBounds()); 158 int rotation = mOverlayParams.getRotation(); 159 if (rotation == Surface.ROTATION_90 || rotation == Surface.ROTATION_270) { 160 RotationUtils.rotateBounds( 161 rotatedBounds, 162 mOverlayParams.getNaturalDisplayWidth(), 163 mOverlayParams.getNaturalDisplayHeight(), 164 rotation 165 ); 166 } 167 168 // Use parent view's and rotatedBound's absolute coordinates to decide the margins of 169 // UdfpsEnrollView, so that its center keeps consistent with sensor rect's. 170 ViewGroup parentView = (ViewGroup) getParent(); 171 MarginLayoutParams marginLayoutParams = (MarginLayoutParams) getLayoutParams(); 172 FrameLayout.LayoutParams params = (LayoutParams) getLayoutParams(); 173 if (rotation == Surface.ROTATION_0 || rotation == Surface.ROTATION_180) { 174 final int[] coords = parentView.getLocationOnScreen(); 175 final int parentLeft = coords[0]; 176 final int parentTop = coords[1]; 177 final int parentRight = parentLeft + parentView.getWidth(); 178 params.gravity = Gravity.RIGHT | Gravity.TOP; 179 final int rightMargin = parentRight - rotatedBounds.right - getPaddingX(); 180 final int topMargin = rotatedBounds.top - parentTop - getPaddingY(); 181 if (marginLayoutParams.rightMargin == rightMargin 182 && marginLayoutParams.topMargin == topMargin) { 183 return; 184 } 185 marginLayoutParams.rightMargin = rightMargin; 186 marginLayoutParams.topMargin = topMargin; 187 setLayoutParams(params); 188 } else { 189 final int[] coords = parentView.getLocationOnScreen(); 190 final int parentLeft = coords[0]; 191 final int parentTop = coords[1]; 192 final int parentRight = parentLeft + parentView.getWidth(); 193 final int parentBottom = parentTop + parentView.getHeight(); 194 if (rotation == Surface.ROTATION_90) { 195 params.gravity = Gravity.RIGHT | Gravity.BOTTOM; 196 marginLayoutParams.rightMargin = parentRight - rotatedBounds.right - getPaddingX(); 197 marginLayoutParams.bottomMargin = 198 parentBottom - rotatedBounds.bottom - getPaddingY(); 199 } else if (rotation == Surface.ROTATION_270) { 200 params.gravity = Gravity.LEFT | Gravity.BOTTOM; 201 marginLayoutParams.leftMargin = rotatedBounds.left - parentLeft - getPaddingX(); 202 marginLayoutParams.bottomMargin = 203 parentBottom - rotatedBounds.bottom - getPaddingY(); 204 } 205 } 206 207 params.height = rotatedBounds.height() + 2 * getPaddingX(); 208 params.width = rotatedBounds.width() + 2 * getPaddingY(); 209 setLayoutParams(params); 210 211 212 } 213 onFingerDown()214 private void onFingerDown() { 215 if (mOverlayParams.getSensorType() == FingerprintSensorProperties.TYPE_UDFPS_OPTICAL) { 216 mFingerprintDrawable.setShouldSkipDraw(true); 217 } 218 mFingerprintDrawable.invalidateSelf(); 219 } 220 onFingerUp()221 private void onFingerUp() { 222 mFingerprintDrawable.setShouldSkipDraw(false); 223 mFingerprintDrawable.invalidateSelf(); 224 } 225 getPaddingX()226 private int getPaddingX() { 227 return mProgressBarRadius; 228 } 229 getPaddingY()230 private int getPaddingY() { 231 return mProgressBarRadius; 232 } 233 } 234