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     })
78     @Retention(RetentionPolicy.SOURCE)
79     public @interface FingerprintEnrollEnrollingAction {}
80 
81     private final int mUserId;
82     private boolean mOnBackPressed;
83     private boolean mOnSkipPressed;
84     @NonNull private final FingerprintRepository mFingerprintRepository;
85     private final AccessibilityManager mAccessibilityManager;
86     private final Vibrator mVibrator;
87 
88     private final MutableLiveData<Integer> mActionLiveData = new MutableLiveData<>();
89 
FingerprintEnrollEnrollingViewModel( @onNull Application application, int userId, @NonNull FingerprintRepository fingerprintRepository )90     public FingerprintEnrollEnrollingViewModel(
91             @NonNull Application application,
92             int userId,
93             @NonNull FingerprintRepository fingerprintRepository
94     ) {
95         super(application);
96         mUserId = userId;
97         mFingerprintRepository = fingerprintRepository;
98         mAccessibilityManager = application.getSystemService(AccessibilityManager.class);
99         mVibrator = application.getSystemService(Vibrator.class);
100     }
101 
getActionLiveData()102     public LiveData<Integer> getActionLiveData() {
103         return mActionLiveData;
104     }
105 
106     /**
107      * Clears action live data
108      */
clearActionLiveData()109     public void clearActionLiveData() {
110         mActionLiveData.setValue(null);
111     }
112 
getOnSkipPressed()113     public boolean getOnSkipPressed() {
114         return mOnSkipPressed;
115     }
116 
117     /**
118      * User clicks skip button
119      */
setOnSkipPressed()120     public void setOnSkipPressed() {
121         mOnSkipPressed = true;
122     }
123 
124     /**
125      * Enrolling is cancelled because user clicks skip
126      */
onCancelledDueToOnSkipPressed()127     public void onCancelledDueToOnSkipPressed() {
128         final int action = FINGERPRINT_ENROLL_ENROLLING_CANCELED_BECAUSE_USER_SKIP;
129         if (DEBUG) {
130             Log.d(TAG, "onSkipButtonClick, post action " + action);
131         }
132         mOnSkipPressed = false;
133         mActionLiveData.postValue(action);
134     }
135 
136     /**
137      * Is enrolling finished
138      */
onEnrollingDone()139     public void onEnrollingDone() {
140         final int action = FINGERPRINT_ENROLL_ENROLLING_ACTION_DONE;
141         if (DEBUG) {
142             Log.d(TAG, "onEnrollingDone, post action " + action);
143         }
144         mActionLiveData.postValue(action);
145     }
146 
getOnBackPressed()147     public boolean getOnBackPressed() {
148         return mOnBackPressed;
149     }
150 
151     /**
152      * Back key is pressed.
153      */
setOnBackPressed()154     public void setOnBackPressed() {
155         mOnBackPressed = true;
156     }
157 
158     /**
159      * Enrollment is cancelled because back key is pressed.
160      */
onCancelledDueToOnBackPressed()161     public void onCancelledDueToOnBackPressed() {
162         final int action = FINGERPRINT_ENROLL_ENROLLING_CANCELED_BECAUSE_BACK_PRESSED;
163         if (DEBUG) {
164             Log.d(TAG, "onCancelledEventReceivedAfterOnBackPressed, post action " + action);
165         }
166         mOnBackPressed = false;
167         mActionLiveData.postValue(action);
168     }
169 
170     /**
171      * Icon touch dialog show
172      */
showIconTouchDialog()173     public void showIconTouchDialog() {
174         final int action = FINGERPRINT_ENROLL_ENROLLING_ACTION_SHOW_ICON_TOUCH_DIALOG;
175         if (DEBUG) {
176             Log.d(TAG, "onIconTouchDialogShow, post action " + action);
177         }
178         mActionLiveData.postValue(action);
179     }
180 
181     /**
182      * get enroll stage threshold
183      */
getEnrollStageThreshold(int index)184     public float getEnrollStageThreshold(int index) {
185         return mFingerprintRepository.getEnrollStageThreshold(index);
186     }
187 
188     /**
189      * Get enroll stage count
190      */
getEnrollStageCount()191     public int getEnrollStageCount() {
192         return mFingerprintRepository.getEnrollStageCount();
193     }
194 
195     /**
196      * Requests interruption of the accessibility feedback from all accessibility services.
197      */
clearTalkback()198     public void clearTalkback() {
199         mAccessibilityManager.interrupt();
200     }
201 
202     /**
203      * Returns if the {@link AccessibilityManager} is enabled.
204      *
205      * @return True if this {@link AccessibilityManager} is enabled, false otherwise.
206      */
isAccessibilityEnabled()207     public boolean isAccessibilityEnabled() {
208         return mAccessibilityManager.isEnabled();
209     }
210 
211     /**
212      * Sends an {@link AccessibilityEvent}.
213      */
sendAccessibilityEvent(CharSequence announcement)214     public void sendAccessibilityEvent(CharSequence announcement) {
215         AccessibilityEvent e = AccessibilityEvent.obtain();
216         e.setEventType(AccessibilityEvent.TYPE_ANNOUNCEMENT);
217         e.setClassName(getClass().getName());
218         e.setPackageName(getApplication().getPackageName());
219         e.getText().add(announcement);
220         mAccessibilityManager.sendAccessibilityEvent(e);
221     }
222 
223      /**
224      * Returns if the touch exploration in the system is enabled.
225      *
226      * @return True if touch exploration is enabled, false otherwise.
227      */
isTouchExplorationEnabled()228     public boolean isTouchExplorationEnabled() {
229         return mAccessibilityManager.isTouchExplorationEnabled();
230     }
231 
232     /**
233      * Like {@link #vibrate(VibrationEffect, VibrationAttributes)}, but allows the
234      * caller to specify the vibration is owned by someone else and set a reason for vibration.
235      */
vibrateError(String reason)236     public void vibrateError(String reason) {
237         mVibrator.vibrate(mUserId, getApplication().getOpPackageName(),
238                 VIBRATE_EFFECT_ERROR, reason, FINGERPRINT_ENROLLING_SONFICATION_ATTRIBUTES);
239     }
240 
241     /**
242      * Gets the first FingerprintSensorPropertiesInternal from FingerprintManager
243      */
244     @Nullable
getFirstFingerprintSensorPropertiesInternal()245     public FingerprintSensorPropertiesInternal getFirstFingerprintSensorPropertiesInternal() {
246         return mFingerprintRepository.getFirstFingerprintSensorPropertiesInternal();
247     }
248 }
249