1 /* 2 * Copyright (C) 2021 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.systemui.biometrics; 18 19 import android.annotation.NonNull; 20 import android.annotation.Nullable; 21 import android.content.Context; 22 import android.graphics.PointF; 23 import android.hardware.fingerprint.IUdfpsOverlayController; 24 import android.os.Build; 25 import android.os.UserHandle; 26 import android.provider.Settings; 27 import android.util.Log; 28 import android.util.TypedValue; 29 import android.view.accessibility.AccessibilityManager; 30 31 import java.util.ArrayList; 32 import java.util.List; 33 34 /** 35 * Helps keep track of enrollment state and animates the progress bar accordingly. 36 */ 37 public class UdfpsEnrollHelper { 38 private static final String TAG = "UdfpsEnrollHelper"; 39 40 private static final String SCALE_OVERRIDE = 41 "com.android.systemui.biometrics.UdfpsEnrollHelper.scale"; 42 private static final float SCALE = 0.5f; 43 44 private static final String NEW_COORDS_OVERRIDE = 45 "com.android.systemui.biometrics.UdfpsNewCoords"; 46 47 // Enroll with two center touches before going to guided enrollment 48 private static final int NUM_CENTER_TOUCHES = 2; 49 50 interface Listener { onEnrollmentProgress(int remaining, int totalSteps)51 void onEnrollmentProgress(int remaining, int totalSteps); onLastStepAcquired()52 void onLastStepAcquired(); 53 } 54 55 @NonNull private final Context mContext; 56 // IUdfpsOverlayController reason 57 private final int mEnrollReason; 58 private final boolean mAccessibilityEnabled; 59 @NonNull private final List<PointF> mGuidedEnrollmentPoints; 60 61 private int mTotalSteps = -1; 62 private int mRemainingSteps = -1; 63 64 // Note that this is actually not equal to "mTotalSteps - mRemainingSteps", because the 65 // interface makes no promises about monotonically increasing by one each time. 66 private int mLocationsEnrolled = 0; 67 68 @Nullable Listener mListener; 69 UdfpsEnrollHelper(@onNull Context context, int reason)70 public UdfpsEnrollHelper(@NonNull Context context, int reason) { 71 mContext = context; 72 mEnrollReason = reason; 73 74 final AccessibilityManager am = context.getSystemService(AccessibilityManager.class); 75 mAccessibilityEnabled = am.isEnabled(); 76 77 mGuidedEnrollmentPoints = new ArrayList<>(); 78 79 // Number of pixels per mm 80 float px = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_MM, 1, 81 context.getResources().getDisplayMetrics()); 82 boolean useNewCoords = Settings.Secure.getIntForUser(mContext.getContentResolver(), 83 NEW_COORDS_OVERRIDE, 0, 84 UserHandle.USER_CURRENT) != 0; 85 if (useNewCoords && (Build.IS_ENG || Build.IS_USERDEBUG)) { 86 Log.v(TAG, "Using new coordinates"); 87 mGuidedEnrollmentPoints.add(new PointF(-0.15f * px, -1.02f * px)); 88 mGuidedEnrollmentPoints.add(new PointF(-0.15f * px, 1.02f * px)); 89 mGuidedEnrollmentPoints.add(new PointF( 0.29f * px, 0.00f * px)); 90 mGuidedEnrollmentPoints.add(new PointF( 2.17f * px, -2.35f * px)); 91 mGuidedEnrollmentPoints.add(new PointF( 1.07f * px, -3.96f * px)); 92 mGuidedEnrollmentPoints.add(new PointF(-0.37f * px, -4.31f * px)); 93 mGuidedEnrollmentPoints.add(new PointF(-1.69f * px, -3.29f * px)); 94 mGuidedEnrollmentPoints.add(new PointF(-2.48f * px, -1.23f * px)); 95 mGuidedEnrollmentPoints.add(new PointF(-2.48f * px, 1.23f * px)); 96 mGuidedEnrollmentPoints.add(new PointF(-1.69f * px, 3.29f * px)); 97 mGuidedEnrollmentPoints.add(new PointF(-0.37f * px, 4.31f * px)); 98 mGuidedEnrollmentPoints.add(new PointF( 1.07f * px, 3.96f * px)); 99 mGuidedEnrollmentPoints.add(new PointF( 2.17f * px, 2.35f * px)); 100 mGuidedEnrollmentPoints.add(new PointF( 2.58f * px, 0.00f * px)); 101 } else { 102 Log.v(TAG, "Using old coordinates"); 103 mGuidedEnrollmentPoints.add(new PointF( 2.00f * px, 0.00f * px)); 104 mGuidedEnrollmentPoints.add(new PointF( 0.87f * px, -2.70f * px)); 105 mGuidedEnrollmentPoints.add(new PointF(-1.80f * px, -1.31f * px)); 106 mGuidedEnrollmentPoints.add(new PointF(-1.80f * px, 1.31f * px)); 107 mGuidedEnrollmentPoints.add(new PointF( 0.88f * px, 2.70f * px)); 108 mGuidedEnrollmentPoints.add(new PointF( 3.94f * px, -1.06f * px)); 109 mGuidedEnrollmentPoints.add(new PointF( 2.90f * px, -4.14f * px)); 110 mGuidedEnrollmentPoints.add(new PointF(-0.52f * px, -5.95f * px)); 111 mGuidedEnrollmentPoints.add(new PointF(-3.33f * px, -3.33f * px)); 112 mGuidedEnrollmentPoints.add(new PointF(-3.99f * px, -0.35f * px)); 113 mGuidedEnrollmentPoints.add(new PointF(-3.62f * px, 2.54f * px)); 114 mGuidedEnrollmentPoints.add(new PointF(-1.49f * px, 5.57f * px)); 115 mGuidedEnrollmentPoints.add(new PointF( 2.29f * px, 4.92f * px)); 116 mGuidedEnrollmentPoints.add(new PointF( 3.82f * px, 1.78f * px)); 117 } 118 } 119 shouldShowProgressBar()120 boolean shouldShowProgressBar() { 121 return mEnrollReason == IUdfpsOverlayController.REASON_ENROLL_ENROLLING; 122 } 123 onEnrollmentProgress(int remaining)124 void onEnrollmentProgress(int remaining) { 125 if (mTotalSteps == -1) { 126 mTotalSteps = remaining; 127 } 128 129 if (remaining != mRemainingSteps) { 130 mLocationsEnrolled++; 131 } 132 133 mRemainingSteps = remaining; 134 135 if (mListener != null) { 136 mListener.onEnrollmentProgress(remaining, mTotalSteps); 137 } 138 } 139 onEnrollmentHelp()140 void onEnrollmentHelp() { 141 142 } 143 setListener(Listener listener)144 void setListener(Listener listener) { 145 mListener = listener; 146 147 // Only notify during setListener if enrollment is already in progress, so the progress 148 // bar can be updated. If enrollment has not started yet, the progress bar will be empty 149 // anyway. 150 if (mListener != null && mTotalSteps != -1) { 151 mListener.onEnrollmentProgress(mRemainingSteps, mTotalSteps); 152 } 153 } 154 isCenterEnrollmentComplete()155 boolean isCenterEnrollmentComplete() { 156 if (mTotalSteps == -1 || mRemainingSteps == -1) { 157 return false; 158 } else if (mAccessibilityEnabled) { 159 return false; 160 } 161 final int stepsEnrolled = mTotalSteps - mRemainingSteps; 162 return stepsEnrolled >= NUM_CENTER_TOUCHES; 163 } 164 165 @NonNull getNextGuidedEnrollmentPoint()166 PointF getNextGuidedEnrollmentPoint() { 167 if (mAccessibilityEnabled) { 168 return new PointF(0f, 0f); 169 } 170 171 float scale = SCALE; 172 if (Build.IS_ENG || Build.IS_USERDEBUG) { 173 scale = Settings.Secure.getFloatForUser(mContext.getContentResolver(), 174 SCALE_OVERRIDE, SCALE, 175 UserHandle.USER_CURRENT); 176 } 177 final int index = mLocationsEnrolled - NUM_CENTER_TOUCHES; 178 final PointF originalPoint = mGuidedEnrollmentPoints 179 .get(index % mGuidedEnrollmentPoints.size()); 180 return new PointF(originalPoint.x * scale, originalPoint.y * scale); 181 } 182 animateIfLastStep()183 void animateIfLastStep() { 184 if (mListener == null) { 185 Log.e(TAG, "animateIfLastStep, null listener"); 186 return; 187 } 188 189 if (mRemainingSteps <= 2 && mRemainingSteps >= 0) { 190 mListener.onLastStepAcquired(); 191 } 192 } 193 } 194