• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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