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.face; 18 19 import android.app.settings.SettingsEnums; 20 import android.content.ComponentName; 21 import android.content.Intent; 22 import android.hardware.face.FaceManager; 23 import android.os.Bundle; 24 import android.os.Handler; 25 import android.os.UserHandle; 26 import android.text.TextUtils; 27 import android.view.View; 28 import android.view.accessibility.AccessibilityManager; 29 import android.widget.Button; 30 import android.widget.CompoundButton; 31 import android.widget.TextView; 32 33 import com.android.settings.R; 34 import com.android.settings.Utils; 35 import com.android.settings.biometrics.BiometricEnrollBase; 36 import com.android.settings.password.ChooseLockSettingsHelper; 37 38 import com.google.android.setupcompat.template.FooterBarMixin; 39 import com.google.android.setupcompat.template.FooterButton; 40 import com.google.android.setupcompat.util.WizardManagerHelper; 41 import com.google.android.setupdesign.view.IllustrationVideoView; 42 43 public class FaceEnrollEducation extends BiometricEnrollBase { 44 45 private static final String TAG = "FaceEducation"; 46 private static final int ON = 1; 47 private static final int OFF = 0; 48 49 private FaceManager mFaceManager; 50 private FaceEnrollAccessibilityToggle mSwitchDiversity; 51 52 private IllustrationVideoView mIllustrationNormal; 53 private View mIllustrationAccessibility; 54 private Handler mHandler; 55 private Intent mResultIntent; 56 private TextView mDescriptionText; 57 private boolean mNextClicked; 58 private boolean mAccessibilityEnabled; 59 60 private CompoundButton.OnCheckedChangeListener mSwitchDiversityListener = 61 new CompoundButton.OnCheckedChangeListener() { 62 @Override 63 public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { 64 int titleRes = isChecked ? 65 R.string.security_settings_face_enroll_education_title_accessibility 66 : R.string.security_settings_face_enroll_education_title; 67 getLayout().setHeaderText(titleRes); 68 setTitle(titleRes); 69 70 if (isChecked) { 71 mIllustrationNormal.stop(); 72 mIllustrationNormal.setVisibility(View.INVISIBLE); 73 mIllustrationAccessibility.setVisibility(View.VISIBLE); 74 mDescriptionText.setVisibility(View.INVISIBLE); 75 } else { 76 mIllustrationNormal.setVisibility(View.VISIBLE); 77 mIllustrationNormal.start(); 78 mIllustrationAccessibility.setVisibility(View.INVISIBLE); 79 mDescriptionText.setVisibility(View.VISIBLE); 80 } 81 } 82 }; 83 84 @Override onCreate(Bundle savedInstanceState)85 protected void onCreate(Bundle savedInstanceState) { 86 super.onCreate(savedInstanceState); 87 setContentView(R.layout.face_enroll_education); 88 getLayout().setHeaderText(R.string.security_settings_face_enroll_education_title); 89 setTitle(R.string.security_settings_face_enroll_education_title); 90 mHandler = new Handler(); 91 92 mFaceManager = Utils.getFaceManagerOrNull(this); 93 94 mIllustrationNormal = findViewById(R.id.illustration_normal); 95 mIllustrationAccessibility = findViewById(R.id.illustration_accessibility); 96 mDescriptionText = findViewById(R.id.sud_layout_description); 97 98 mFooterBarMixin = getLayout().getMixin(FooterBarMixin.class); 99 100 if (WizardManagerHelper.isAnySetupWizard(getIntent())) { 101 mFooterBarMixin.setSecondaryButton( 102 new FooterButton.Builder(this) 103 .setText(R.string.skip_label) 104 .setListener(this::onSkipButtonClick) 105 .setButtonType(FooterButton.ButtonType.SKIP) 106 .setTheme(R.style.SudGlifButton_Secondary) 107 .build() 108 ); 109 } else { 110 mFooterBarMixin.setSecondaryButton( 111 new FooterButton.Builder(this) 112 .setText(R.string.security_settings_face_enroll_introduction_cancel) 113 .setListener(this::onSkipButtonClick) 114 .setButtonType(FooterButton.ButtonType.CANCEL) 115 .setTheme(R.style.SudGlifButton_Secondary) 116 .build() 117 ); 118 } 119 120 final FooterButton footerButton = new FooterButton.Builder(this) 121 .setText(R.string.security_settings_face_enroll_education_start) 122 .setListener(this::onNextButtonClick) 123 .setButtonType(FooterButton.ButtonType.NEXT) 124 .setTheme(R.style.SudGlifButton_Primary) 125 .build(); 126 127 final AccessibilityManager accessibilityManager = getApplicationContext().getSystemService( 128 AccessibilityManager.class); 129 if (accessibilityManager != null) { 130 // Add additional check for touch exploration. This prevents other accessibility 131 // features such as Live Transcribe from defaulting to the accessibility setup. 132 mAccessibilityEnabled = accessibilityManager.isEnabled() 133 && accessibilityManager.isTouchExplorationEnabled(); 134 } 135 mFooterBarMixin.setPrimaryButton(footerButton); 136 137 final Button accessibilityButton = findViewById(R.id.accessibility_button); 138 accessibilityButton.setOnClickListener(view -> { 139 mSwitchDiversity.setChecked(true); 140 accessibilityButton.setVisibility(View.GONE); 141 mSwitchDiversity.setVisibility(View.VISIBLE); 142 }); 143 144 mSwitchDiversity = findViewById(R.id.toggle_diversity); 145 mSwitchDiversity.setListener(mSwitchDiversityListener); 146 mSwitchDiversity.setOnClickListener(v -> { 147 mSwitchDiversity.getSwitch().toggle(); 148 }); 149 150 if (mAccessibilityEnabled) { 151 accessibilityButton.callOnClick(); 152 } 153 } 154 155 @Override onResume()156 protected void onResume() { 157 super.onResume(); 158 mSwitchDiversityListener.onCheckedChanged(mSwitchDiversity.getSwitch(), 159 mSwitchDiversity.isChecked()); 160 161 // If the user goes back after enrollment, we should send them back to the intro page 162 // if they've met the max limit. 163 final int max = getResources().getInteger( 164 com.android.internal.R.integer.config_faceMaxTemplatesPerUser); 165 final int numEnrolledFaces = mFaceManager.getEnrolledFaces(mUserId).size(); 166 if (numEnrolledFaces >= max) { 167 finish(); 168 } 169 } 170 171 @Override shouldFinishWhenBackgrounded()172 protected boolean shouldFinishWhenBackgrounded() { 173 return super.shouldFinishWhenBackgrounded() && !mNextClicked; 174 } 175 176 @Override onNextButtonClick(View view)177 protected void onNextButtonClick(View view) { 178 final Intent intent = new Intent(); 179 if (mToken != null) { 180 intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN, mToken); 181 } 182 if (mUserId != UserHandle.USER_NULL) { 183 intent.putExtra(Intent.EXTRA_USER_ID, mUserId); 184 } 185 intent.putExtra(EXTRA_FROM_SETTINGS_SUMMARY, mFromSettingsSummary); 186 final String flattenedString = getString(R.string.config_face_enroll); 187 if (!TextUtils.isEmpty(flattenedString)) { 188 ComponentName componentName = ComponentName.unflattenFromString(flattenedString); 189 intent.setComponent(componentName); 190 } else { 191 intent.setClass(this, FaceEnrollEnrolling.class); 192 } 193 WizardManagerHelper.copyWizardManagerExtras(getIntent(), intent); 194 if (mResultIntent != null) { 195 intent.putExtras(mResultIntent); 196 } 197 198 intent.putExtra(EXTRA_KEY_REQUIRE_DIVERSITY, !mSwitchDiversity.isChecked()); 199 200 if (!mSwitchDiversity.isChecked() && mAccessibilityEnabled) { 201 FaceEnrollAccessibilityDialog dialog = FaceEnrollAccessibilityDialog.newInstance(); 202 dialog.setPositiveButtonListener((dialog1, which) -> { 203 startActivityForResult(intent, BIOMETRIC_FIND_SENSOR_REQUEST); 204 mNextClicked = true; 205 }); 206 dialog.show(getSupportFragmentManager(), FaceEnrollAccessibilityDialog.class.getName()); 207 } else { 208 startActivityForResult(intent, BIOMETRIC_FIND_SENSOR_REQUEST); 209 mNextClicked = true; 210 } 211 } 212 onSkipButtonClick(View view)213 protected void onSkipButtonClick(View view) { 214 setResult(RESULT_SKIP); 215 finish(); 216 } 217 218 @Override onActivityResult(int requestCode, int resultCode, Intent data)219 protected void onActivityResult(int requestCode, int resultCode, Intent data) { 220 super.onActivityResult(requestCode, resultCode, data); 221 mResultIntent = data; 222 if (requestCode == BIOMETRIC_FIND_SENSOR_REQUEST) { 223 // If the user finished or skipped enrollment, finish this activity 224 if (resultCode == RESULT_FINISHED || resultCode == RESULT_SKIP 225 || resultCode == RESULT_TIMEOUT) { 226 setResult(resultCode, data); 227 finish(); 228 } 229 } 230 } 231 232 @Override getMetricsCategory()233 public int getMetricsCategory() { 234 return SettingsEnums.FACE_ENROLL_INTRO; 235 } 236 } 237