1 /*
2  * Copyright (C) 2018 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.app.admin.DevicePolicyManager;
20 import android.app.settings.SettingsEnums;
21 import android.content.ActivityNotFoundException;
22 import android.content.Intent;
23 import android.hardware.biometrics.BiometricAuthenticator;
24 import android.hardware.fingerprint.FingerprintManager;
25 import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
26 import android.os.Bundle;
27 import android.util.Log;
28 import android.view.View;
29 import android.widget.ImageView;
30 import android.widget.TextView;
31 
32 import androidx.annotation.NonNull;
33 import androidx.annotation.Nullable;
34 import androidx.annotation.StringRes;
35 
36 import com.android.settings.R;
37 import com.android.settings.Utils;
38 import com.android.settings.biometrics.BiometricEnrollIntroduction;
39 import com.android.settings.biometrics.BiometricUtils;
40 import com.android.settings.biometrics.MultiBiometricEnrollHelper;
41 import com.android.settings.password.ChooseLockSettingsHelper;
42 import com.android.settings.password.SetupSkipDialog;
43 import com.android.settingslib.HelpUtils;
44 import com.android.settingslib.RestrictedLockUtilsInternal;
45 
46 import com.google.android.setupcompat.template.FooterButton;
47 import com.google.android.setupcompat.util.WizardManagerHelper;
48 import com.google.android.setupdesign.span.LinkSpan;
49 
50 import java.util.List;
51 
52 public class FingerprintEnrollIntroduction extends BiometricEnrollIntroduction {
53 
54     private static final String TAG = "FingerprintIntro";
55 
56     private FingerprintManager mFingerprintManager;
57     @Nullable private FooterButton mPrimaryFooterButton;
58     @Nullable private FooterButton mSecondaryFooterButton;
59 
60     @Override
onCreate(Bundle savedInstanceState)61     protected void onCreate(Bundle savedInstanceState) {
62         mFingerprintManager = Utils.getFingerprintManagerOrNull(this);
63         if (mFingerprintManager == null) {
64             Log.e(TAG, "Null FingerprintManager");
65             finish();
66             return;
67         }
68 
69         super.onCreate(savedInstanceState);
70 
71         final ImageView iconFingerprint = findViewById(R.id.icon_fingerprint);
72         final ImageView iconDeviceLocked = findViewById(R.id.icon_device_locked);
73         final ImageView iconTrashCan = findViewById(R.id.icon_trash_can);
74         final ImageView iconInfo = findViewById(R.id.icon_info);
75         final ImageView iconLink = findViewById(R.id.icon_link);
76         iconFingerprint.getDrawable().setColorFilter(getIconColorFilter());
77         iconDeviceLocked.getDrawable().setColorFilter(getIconColorFilter());
78         iconTrashCan.getDrawable().setColorFilter(getIconColorFilter());
79         iconInfo.getDrawable().setColorFilter(getIconColorFilter());
80         iconLink.getDrawable().setColorFilter(getIconColorFilter());
81 
82         final TextView footerMessage2 = findViewById(R.id.footer_message_2);
83         final TextView footerMessage3 = findViewById(R.id.footer_message_3);
84         final TextView footerMessage4 = findViewById(R.id.footer_message_4);
85         final TextView footerMessage5 = findViewById(R.id.footer_message_5);
86         footerMessage2.setText(getFooterMessage2());
87         footerMessage3.setText(getFooterMessage3());
88         footerMessage4.setText(getFooterMessage4());
89         footerMessage5.setText(getFooterMessage5());
90 
91         final TextView footerTitle1 = findViewById(R.id.footer_title_1);
92         final TextView footerTitle2 = findViewById(R.id.footer_title_2);
93         footerTitle1.setText(getFooterTitle1());
94         footerTitle2.setText(getFooterTitle2());
95     }
96 
97     @Override
onActivityResult(int requestCode, int resultCode, Intent data)98     protected void onActivityResult(int requestCode, int resultCode, Intent data) {
99         // If user has skipped or finished enrolling, don't restart enrollment.
100         final boolean isEnrollRequest = requestCode == BIOMETRIC_FIND_SENSOR_REQUEST
101                 || requestCode == ENROLL_NEXT_BIOMETRIC_REQUEST;
102         final boolean isResultSkipOrFinished = resultCode == RESULT_SKIP
103                 || resultCode == SetupSkipDialog.RESULT_SKIP || resultCode == RESULT_FINISHED;
104         if (isEnrollRequest && isResultSkipOrFinished) {
105             data = setSkipPendingEnroll(data);
106         }
107         super.onActivityResult(requestCode, resultCode, data);
108     }
109 
110     @Override
onCancelButtonClick(View view)111     protected void onCancelButtonClick(View view) {
112         // User has explicitly canceled enroll. Don't restart it automatically.
113         Intent data = setSkipPendingEnroll(new Intent());
114         setResult(RESULT_SKIP, data);
115         finish();
116     }
117 
118     @Override
onSkipButtonClick(View view)119     protected void onSkipButtonClick(View view) {
120         onCancelButtonClick(view);
121     }
122 
123     @StringRes
getNegativeButtonTextId()124     int getNegativeButtonTextId() {
125         return R.string.security_settings_fingerprint_enroll_introduction_no_thanks;
126     }
127 
128     @StringRes
getFooterTitle1()129     protected int getFooterTitle1() {
130         return R.string.security_settings_fingerprint_enroll_introduction_footer_title_1;
131     }
132 
133     @StringRes
getFooterTitle2()134     protected int getFooterTitle2() {
135         return R.string.security_settings_fingerprint_enroll_introduction_footer_title_2;
136     }
137 
138     @StringRes
getFooterMessage2()139     protected int getFooterMessage2() {
140         return R.string.security_settings_fingerprint_v2_enroll_introduction_footer_message_2;
141     }
142 
143     @StringRes
getFooterMessage3()144     protected int getFooterMessage3() {
145         return R.string.security_settings_fingerprint_v2_enroll_introduction_footer_message_3;
146     }
147 
148     @StringRes
getFooterMessage4()149     protected int getFooterMessage4() {
150         return R.string.security_settings_fingerprint_v2_enroll_introduction_footer_message_4;
151     }
152 
153     @StringRes
getFooterMessage5()154     protected int getFooterMessage5() {
155         return R.string.security_settings_fingerprint_v2_enroll_introduction_footer_message_5;
156     }
157 
158     @Override
isDisabledByAdmin()159     protected boolean isDisabledByAdmin() {
160         return RestrictedLockUtilsInternal.checkIfKeyguardFeaturesDisabled(
161                 this, DevicePolicyManager.KEYGUARD_DISABLE_FINGERPRINT, mUserId) != null;
162     }
163 
164     @Override
getLayoutResource()165     protected int getLayoutResource() {
166         return R.layout.fingerprint_enroll_introduction;
167     }
168 
169     @Override
getHeaderResDisabledByAdmin()170     protected int getHeaderResDisabledByAdmin() {
171         return R.string.security_settings_fingerprint_enroll_introduction_title_unlock_disabled;
172     }
173 
174     @Override
getHeaderResDefault()175     protected int getHeaderResDefault() {
176         return R.string.security_settings_fingerprint_enroll_introduction_title;
177     }
178 
179     @Override
getDescriptionResDisabledByAdmin()180     protected int getDescriptionResDisabledByAdmin() {
181         return R.string.security_settings_fingerprint_enroll_introduction_message_unlock_disabled;
182     }
183 
184     @Override
getCancelButton()185     protected FooterButton getCancelButton() {
186         if (mFooterBarMixin != null) {
187             return mFooterBarMixin.getSecondaryButton();
188         }
189         return null;
190     }
191 
192     @Override
getNextButton()193     protected FooterButton getNextButton() {
194         if (mFooterBarMixin != null) {
195             return mFooterBarMixin.getPrimaryButton();
196         }
197         return null;
198     }
199 
200     @Override
getErrorTextView()201     protected TextView getErrorTextView() {
202         return findViewById(R.id.error_text);
203     }
204 
205     @Override
checkMaxEnrolled()206     protected int checkMaxEnrolled() {
207         final boolean isSetupWizard = WizardManagerHelper.isAnySetupWizard(getIntent());
208         if (mFingerprintManager != null) {
209             final List<FingerprintSensorPropertiesInternal> props =
210                     mFingerprintManager.getSensorPropertiesInternal();
211             // This will need to be updated for devices with multiple fingerprint sensors
212             final int max = props.get(0).maxEnrollmentsPerUser;
213             final int numEnrolledFingerprints =
214                     mFingerprintManager.getEnrolledFingerprints(mUserId).size();
215             final int maxFingerprintsEnrollableIfSUW = getApplicationContext().getResources()
216                     .getInteger(R.integer.suw_max_fingerprints_enrollable);
217             if (isSetupWizard) {
218                 if (numEnrolledFingerprints >= maxFingerprintsEnrollableIfSUW) {
219                     return R.string.fingerprint_intro_error_max;
220                 } else {
221                     return 0;
222                 }
223             } else if (numEnrolledFingerprints >= max) {
224                 return R.string.fingerprint_intro_error_max;
225             } else {
226                 return 0;
227             }
228         } else {
229             return R.string.fingerprint_intro_error_unknown;
230         }
231     }
232 
233     @Override
getChallenge(GenerateChallengeCallback callback)234     protected void getChallenge(GenerateChallengeCallback callback) {
235         mFingerprintManager = Utils.getFingerprintManagerOrNull(this);
236         if (mFingerprintManager == null) {
237             callback.onChallengeGenerated(0, 0, 0L);
238             return;
239         }
240         mFingerprintManager.generateChallenge(mUserId, callback::onChallengeGenerated);
241     }
242 
243     @Override
getExtraKeyForBiometric()244     protected String getExtraKeyForBiometric() {
245         return ChooseLockSettingsHelper.EXTRA_KEY_FOR_FINGERPRINT;
246     }
247 
248     @Override
getEnrollingIntent()249     protected Intent getEnrollingIntent() {
250         final Intent intent = new Intent(this, FingerprintEnrollFindSensor.class);
251         if (BiometricUtils.containsGatekeeperPasswordHandle(getIntent())) {
252             intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_GK_PW_HANDLE,
253                     BiometricUtils.getGatekeeperPasswordHandle(getIntent()));
254         }
255         return intent;
256     }
257 
258     @Override
getConfirmLockTitleResId()259     protected int getConfirmLockTitleResId() {
260         return R.string.security_settings_fingerprint_preference_title;
261     }
262 
263     @Override
getMetricsCategory()264     public int getMetricsCategory() {
265         return SettingsEnums.FINGERPRINT_ENROLL_INTRO;
266     }
267 
268     @Override
onClick(LinkSpan span)269     public void onClick(LinkSpan span) {
270         if ("url".equals(span.getId())) {
271             String url = getString(R.string.help_url_fingerprint);
272             Intent intent = HelpUtils.getHelpIntent(this, url, getClass().getName());
273             if (intent == null) {
274                 Log.w(TAG, "Null help intent.");
275                 return;
276             }
277             try {
278                 // This needs to be startActivityForResult even though we do not care about the
279                 // actual result because the help app needs to know about who invoked it.
280                 startActivityForResult(intent, LEARN_MORE_REQUEST);
281             } catch (ActivityNotFoundException e) {
282                 Log.w(TAG, "Activity was not found for intent, " + e);
283             }
284         }
285     }
286 
287     @Override
getModality()288     public @BiometricAuthenticator.Modality int getModality() {
289         return BiometricAuthenticator.TYPE_FINGERPRINT;
290     }
291 
292     @Override
293     @NonNull
getPrimaryFooterButton()294     protected FooterButton getPrimaryFooterButton() {
295         if (mPrimaryFooterButton == null) {
296             mPrimaryFooterButton = new FooterButton.Builder(this)
297                     .setText(R.string.security_settings_fingerprint_enroll_introduction_agree)
298                     .setListener(this::onNextButtonClick)
299                     .setButtonType(FooterButton.ButtonType.OPT_IN)
300                     .setTheme(R.style.SudGlifButton_Primary)
301                     .build();
302         }
303         return mPrimaryFooterButton;
304     }
305 
306     @Override
307     @NonNull
getSecondaryFooterButton()308     protected FooterButton getSecondaryFooterButton() {
309         if (mSecondaryFooterButton == null) {
310             mSecondaryFooterButton = new FooterButton.Builder(this)
311                     .setText(getNegativeButtonTextId())
312                     .setListener(this::onSkipButtonClick)
313                     .setButtonType(FooterButton.ButtonType.NEXT)
314                     .setTheme(R.style.SudGlifButton_Primary)
315                     .build();
316         }
317         return mSecondaryFooterButton;
318     }
319 
320     @Override
321     @StringRes
getAgreeButtonTextRes()322     protected int getAgreeButtonTextRes() {
323         return R.string.security_settings_fingerprint_enroll_introduction_agree;
324     }
325 
326     @Override
327     @StringRes
getMoreButtonTextRes()328     protected int getMoreButtonTextRes() {
329         return R.string.security_settings_face_enroll_introduction_more;
330     }
331 
332     @NonNull
setSkipPendingEnroll(@ullable Intent data)333     protected static Intent setSkipPendingEnroll(@Nullable Intent data) {
334         if (data == null) {
335             data = new Intent();
336         }
337         data.putExtra(MultiBiometricEnrollHelper.EXTRA_SKIP_PENDING_ENROLL, true);
338         return data;
339     }
340 }
341