1 /* 2 * Copyright (C) 2023 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.biometrics2.ui.viewmodel; 18 19 import android.annotation.IntDef; 20 import android.app.Application; 21 import android.hardware.fingerprint.FingerprintSensorPropertiesInternal; 22 import android.os.VibrationAttributes; 23 import android.os.VibrationEffect; 24 import android.os.Vibrator; 25 import android.util.Log; 26 import android.view.accessibility.AccessibilityEvent; 27 import android.view.accessibility.AccessibilityManager; 28 29 import androidx.annotation.NonNull; 30 import androidx.annotation.Nullable; 31 import androidx.lifecycle.AndroidViewModel; 32 import androidx.lifecycle.LiveData; 33 import androidx.lifecycle.MutableLiveData; 34 35 import com.android.settings.biometrics2.data.repository.FingerprintRepository; 36 37 import java.lang.annotation.Retention; 38 import java.lang.annotation.RetentionPolicy; 39 40 /** 41 * ViewModel explaining the fingerprint enrolling page 42 */ 43 public class FingerprintEnrollEnrollingViewModel extends AndroidViewModel { 44 45 private static final String TAG = FingerprintEnrollEnrollingViewModel.class.getSimpleName(); 46 private static final boolean DEBUG = false; 47 48 private static final VibrationEffect VIBRATE_EFFECT_ERROR = 49 VibrationEffect.createWaveform(new long[]{0, 5, 55, 60}, -1); 50 private static final VibrationAttributes FINGERPRINT_ENROLLING_SONFICATION_ATTRIBUTES = 51 VibrationAttributes.createForUsage(VibrationAttributes.USAGE_ACCESSIBILITY); 52 53 /** 54 * Enrolling finished 55 */ 56 public static final int FINGERPRINT_ENROLL_ENROLLING_ACTION_DONE = 0; 57 58 /** 59 * Icon touch dialog show 60 */ 61 public static final int FINGERPRINT_ENROLL_ENROLLING_ACTION_SHOW_ICON_TOUCH_DIALOG = 1; 62 63 /** 64 * Has got latest cancelled event due to user skip 65 */ 66 public static final int FINGERPRINT_ENROLL_ENROLLING_CANCELED_BECAUSE_USER_SKIP = 2; 67 68 /** 69 * Has got latest cancelled event due to back key 70 */ 71 public static final int FINGERPRINT_ENROLL_ENROLLING_CANCELED_BECAUSE_BACK_PRESSED = 3; 72 73 @IntDef(prefix = { "FINGERPRINT_ENROLL_ENROLLING_ACTION_" }, value = { 74 FINGERPRINT_ENROLL_ENROLLING_ACTION_DONE, 75 FINGERPRINT_ENROLL_ENROLLING_ACTION_SHOW_ICON_TOUCH_DIALOG, 76 FINGERPRINT_ENROLL_ENROLLING_CANCELED_BECAUSE_USER_SKIP, 77 FINGERPRINT_ENROLL_ENROLLING_CANCELED_BECAUSE_BACK_PRESSED 78 }) 79 @Retention(RetentionPolicy.SOURCE) 80 public @interface FingerprintEnrollEnrollingAction {} 81 82 /** 83 * Enrolling skipped 84 */ 85 public static final int FINGERPRINT_ERROR_DIALOG_ACTION_SET_RESULT_FINISH = 0; 86 87 /** 88 * Enrolling finished 89 */ 90 public static final int FINGERPRINT_ERROR_DIALOG_ACTION_SET_RESULT_TIMEOUT = 1; 91 92 /** 93 * Icon touch dialog show 94 */ 95 public static final int FINGERPRINT_ERROR_DIALOG_ACTION_RESTART = 2; 96 97 @IntDef(prefix = { "FINGERPRINT_ERROR_DIALOG_ACTION_" }, value = { 98 FINGERPRINT_ERROR_DIALOG_ACTION_SET_RESULT_FINISH, 99 FINGERPRINT_ERROR_DIALOG_ACTION_SET_RESULT_TIMEOUT, 100 FINGERPRINT_ERROR_DIALOG_ACTION_RESTART 101 }) 102 @Retention(RetentionPolicy.SOURCE) 103 public @interface FingerprintErrorDialogAction {} 104 105 private final int mUserId; 106 private boolean mOnBackPressed; 107 private boolean mOnSkipPressed; 108 @NonNull private final FingerprintRepository mFingerprintRepository; 109 private final AccessibilityManager mAccessibilityManager; 110 private final Vibrator mVibrator; 111 112 private final MutableLiveData<Integer> mActionLiveData = new MutableLiveData<>(); 113 private final MutableLiveData<ErrorDialogData> mErrorDialogLiveData = new MutableLiveData<>(); 114 private final MutableLiveData<Integer> mErrorDialogActionLiveData = new MutableLiveData<>(); 115 FingerprintEnrollEnrollingViewModel(@onNull Application application, int userId, @NonNull FingerprintRepository fingerprintRepository)116 public FingerprintEnrollEnrollingViewModel(@NonNull Application application, 117 int userId, @NonNull FingerprintRepository fingerprintRepository) { 118 super(application); 119 mUserId = userId; 120 mFingerprintRepository = fingerprintRepository; 121 mAccessibilityManager = application.getSystemService(AccessibilityManager.class); 122 mVibrator = application.getSystemService(Vibrator.class); 123 } 124 125 /** 126 * Notifies activity to show error dialog 127 */ showErrorDialog(@onNull ErrorDialogData errorDialogData)128 public void showErrorDialog(@NonNull ErrorDialogData errorDialogData) { 129 mErrorDialogLiveData.postValue(errorDialogData); 130 } 131 getErrorDialogLiveData()132 public LiveData<ErrorDialogData> getErrorDialogLiveData() { 133 return mErrorDialogLiveData; 134 } 135 getErrorDialogActionLiveData()136 public LiveData<Integer> getErrorDialogActionLiveData() { 137 return mErrorDialogActionLiveData; 138 } 139 getActionLiveData()140 public LiveData<Integer> getActionLiveData() { 141 return mActionLiveData; 142 } 143 144 /** 145 * Clears action live data 146 */ clearActionLiveData()147 public void clearActionLiveData() { 148 mActionLiveData.setValue(null); 149 } 150 151 /** 152 * Saves new user dialog action to mErrorDialogActionLiveData 153 */ onErrorDialogAction(@ingerprintErrorDialogAction int action)154 public void onErrorDialogAction(@FingerprintErrorDialogAction int action) { 155 if (DEBUG) { 156 Log.d(TAG, "onErrorDialogAction(" + action + ")"); 157 } 158 mErrorDialogActionLiveData.postValue(action); 159 } 160 getOnSkipPressed()161 public boolean getOnSkipPressed() { 162 return mOnSkipPressed; 163 } 164 165 /** 166 * User clicks skip button 167 */ setOnSkipPressed()168 public void setOnSkipPressed() { 169 mOnSkipPressed = true; 170 } 171 172 /** 173 * Enrolling is cacelled because user clicks skip 174 */ onCancelledDueToOnSkipPressed()175 public void onCancelledDueToOnSkipPressed() { 176 final int action = FINGERPRINT_ENROLL_ENROLLING_CANCELED_BECAUSE_USER_SKIP; 177 if (DEBUG) { 178 Log.d(TAG, "onSkipButtonClick, post action " + action); 179 } 180 mOnSkipPressed = false; 181 mActionLiveData.postValue(action); 182 } 183 184 /** 185 * Is enrolling finished 186 */ onEnrollingDone()187 public void onEnrollingDone() { 188 final int action = FINGERPRINT_ENROLL_ENROLLING_ACTION_DONE; 189 if (DEBUG) { 190 Log.d(TAG, "onEnrollingDone, post action " + action); 191 } 192 mActionLiveData.postValue(action); 193 } 194 getOnBackPressed()195 public boolean getOnBackPressed() { 196 return mOnBackPressed; 197 } 198 199 /** 200 * Back key is pressed. 201 */ setOnBackPressed()202 public void setOnBackPressed() { 203 mOnBackPressed = true; 204 } 205 206 /** 207 * Enrollment is cancelled because back key is pressed. 208 */ onCancelledDueToOnBackPressed()209 public void onCancelledDueToOnBackPressed() { 210 final int action = FINGERPRINT_ENROLL_ENROLLING_CANCELED_BECAUSE_BACK_PRESSED; 211 if (DEBUG) { 212 Log.d(TAG, "onCancelledEventReceivedAfterOnBackPressed, post action " + action); 213 } 214 mOnBackPressed = false; 215 mActionLiveData.postValue(action); 216 } 217 218 /** 219 * Icon touch dialog show 220 */ showIconTouchDialog()221 public void showIconTouchDialog() { 222 final int action = FINGERPRINT_ENROLL_ENROLLING_ACTION_SHOW_ICON_TOUCH_DIALOG; 223 if (DEBUG) { 224 Log.d(TAG, "onIconTouchDialogShow, post action " + action); 225 } 226 mActionLiveData.postValue(action); 227 } 228 229 /** 230 * get enroll stage threshold 231 */ getEnrollStageThreshold(int index)232 public float getEnrollStageThreshold(int index) { 233 return mFingerprintRepository.getEnrollStageThreshold(index); 234 } 235 236 /** 237 * Get enroll stage count 238 */ getEnrollStageCount()239 public int getEnrollStageCount() { 240 return mFingerprintRepository.getEnrollStageCount(); 241 } 242 243 /** 244 * Requests interruption of the accessibility feedback from all accessibility services. 245 */ clearTalkback()246 public void clearTalkback() { 247 mAccessibilityManager.interrupt(); 248 } 249 250 /** 251 * Returns if the {@link AccessibilityManager} is enabled. 252 * 253 * @return True if this {@link AccessibilityManager} is enabled, false otherwise. 254 */ isAccessibilityEnabled()255 public boolean isAccessibilityEnabled() { 256 return mAccessibilityManager.isEnabled(); 257 } 258 259 /** 260 * Sends an {@link AccessibilityEvent}. 261 */ sendAccessibilityEvent(CharSequence announcement)262 public void sendAccessibilityEvent(CharSequence announcement) { 263 AccessibilityEvent e = AccessibilityEvent.obtain(); 264 e.setEventType(AccessibilityEvent.TYPE_ANNOUNCEMENT); 265 e.setClassName(getClass().getName()); 266 e.setPackageName(getApplication().getPackageName()); 267 e.getText().add(announcement); 268 mAccessibilityManager.sendAccessibilityEvent(e); 269 } 270 271 /** 272 * Returns if the touch exploration in the system is enabled. 273 * 274 * @return True if touch exploration is enabled, false otherwise. 275 */ isTouchExplorationEnabled()276 public boolean isTouchExplorationEnabled() { 277 return mAccessibilityManager.isTouchExplorationEnabled(); 278 } 279 280 /** 281 * Like {@link #vibrate(VibrationEffect, VibrationAttributes)}, but allows the 282 * caller to specify the vibration is owned by someone else and set a reason for vibration. 283 */ vibrateError(String reason)284 public void vibrateError(String reason) { 285 mVibrator.vibrate(mUserId, getApplication().getOpPackageName(), 286 VIBRATE_EFFECT_ERROR, reason, FINGERPRINT_ENROLLING_SONFICATION_ATTRIBUTES); 287 } 288 289 /** 290 * Gets the first FingerprintSensorPropertiesInternal from FingerprintManager 291 */ 292 @Nullable getFirstFingerprintSensorPropertiesInternal()293 public FingerprintSensorPropertiesInternal getFirstFingerprintSensorPropertiesInternal() { 294 return mFingerprintRepository.getFirstFingerprintSensorPropertiesInternal(); 295 } 296 297 /** 298 * The first sensor type is UDFPS sensor or not 299 */ canAssumeUdfps()300 public boolean canAssumeUdfps() { 301 return mFingerprintRepository.canAssumeUdfps(); 302 } 303 304 /** 305 * Data for passing to FingerprintEnrollEnrollingErrorDialog 306 */ 307 public static class ErrorDialogData { 308 @NonNull private final CharSequence mErrMsg; 309 @NonNull private final CharSequence mErrTitle; 310 @NonNull private final int mErrMsgId; 311 ErrorDialogData(@onNull CharSequence errMsg, @NonNull CharSequence errTitle, int errMsgId)312 public ErrorDialogData(@NonNull CharSequence errMsg, @NonNull CharSequence errTitle, 313 int errMsgId) { 314 mErrMsg = errMsg; 315 mErrTitle = errTitle; 316 mErrMsgId = errMsgId; 317 } 318 getErrMsg()319 public CharSequence getErrMsg() { 320 return mErrMsg; 321 } 322 getErrTitle()323 public CharSequence getErrTitle() { 324 return mErrTitle; 325 } 326 getErrMsgId()327 public int getErrMsgId() { 328 return mErrMsgId; 329 } 330 331 @Override toString()332 public String toString() { 333 return getClass().getSimpleName() + "@" + Integer.toHexString(hashCode()) 334 + "{id:" + mErrMsgId + "}"; 335 } 336 } 337 } 338