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