1 /* 2 * Copyright (C) 2015 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.fingerprint; 18 19 import android.app.admin.DevicePolicyManager; 20 import android.content.ActivityNotFoundException; 21 import android.content.Context; 22 import android.content.Intent; 23 import android.graphics.Typeface; 24 import android.hardware.fingerprint.FingerprintManager; 25 import android.os.Bundle; 26 import android.os.UserHandle; 27 import android.os.UserManager; 28 import android.text.Annotation; 29 import android.text.SpannableString; 30 import android.text.SpannableStringBuilder; 31 import android.text.TextPaint; 32 import android.text.TextUtils; 33 import android.text.style.URLSpan; 34 import android.util.Log; 35 import android.view.View; 36 37 import com.android.internal.logging.MetricsProto.MetricsEvent; 38 import com.android.internal.util.CharSequences; 39 import com.android.settings.ChooseLockGeneric; 40 import com.android.settings.ChooseLockSettingsHelper; 41 import com.android.settingslib.HelpUtils; 42 import com.android.settings.R; 43 import com.android.setupwizardlib.SetupWizardRecyclerLayout; 44 import com.android.setupwizardlib.items.IItem; 45 import com.android.setupwizardlib.items.Item; 46 import com.android.setupwizardlib.items.RecyclerItemAdapter; 47 48 /** 49 * Onboarding activity for fingerprint enrollment. 50 */ 51 public class FingerprintEnrollIntroduction extends FingerprintEnrollBase 52 implements RecyclerItemAdapter.OnItemSelectedListener { 53 54 protected static final int CHOOSE_LOCK_GENERIC_REQUEST = 1; 55 protected static final int FINGERPRINT_FIND_SENSOR_REQUEST = 2; 56 protected static final int LEARN_MORE_REQUEST = 3; 57 58 private UserManager mUserManager; 59 private boolean mHasPassword; 60 61 @Override onCreate(Bundle savedInstanceState)62 protected void onCreate(Bundle savedInstanceState) { 63 super.onCreate(savedInstanceState); 64 setContentView(R.layout.fingerprint_enroll_introduction); 65 setHeaderText(R.string.security_settings_fingerprint_enroll_introduction_title); 66 final SetupWizardRecyclerLayout layout = 67 (SetupWizardRecyclerLayout) findViewById(R.id.setup_wizard_layout); 68 mUserManager = UserManager.get(this); 69 final RecyclerItemAdapter adapter = (RecyclerItemAdapter) layout.getAdapter(); 70 adapter.setOnItemSelectedListener(this); 71 Item item = (Item) adapter.findItemById(R.id.fingerprint_introduction_message); 72 item.setTitle(LearnMoreSpan.linkify( 73 getText(R.string.security_settings_fingerprint_enroll_introduction_message), 74 getString(R.string.help_url_fingerprint))); 75 // setupwizard library automatically sets the divider inset to 76 // R.dimen.suw_items_icon_divider_inset. We adjust this back to 0 as we do not want 77 // an inset within settings. 78 layout.setDividerInset(0); 79 updatePasswordQuality(); 80 } 81 updatePasswordQuality()82 private void updatePasswordQuality() { 83 final int passwordQuality = new ChooseLockSettingsHelper(this).utils() 84 .getActivePasswordQuality(mUserManager.getCredentialOwnerProfile(mUserId)); 85 mHasPassword = passwordQuality != DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED; 86 } 87 88 @Override onNextButtonClick()89 protected void onNextButtonClick() { 90 if (!mHasPassword) { 91 // No fingerprints registered, launch into enrollment wizard. 92 launchChooseLock(); 93 } else { 94 // Lock thingy is already set up, launch directly into find sensor step from wizard. 95 launchFindSensor(null); 96 } 97 } 98 launchChooseLock()99 private void launchChooseLock() { 100 Intent intent = getChooseLockIntent(); 101 long challenge = getSystemService(FingerprintManager.class).preEnroll(); 102 intent.putExtra(ChooseLockGeneric.ChooseLockGenericFragment.MINIMUM_QUALITY_KEY, 103 DevicePolicyManager.PASSWORD_QUALITY_SOMETHING); 104 intent.putExtra(ChooseLockGeneric.ChooseLockGenericFragment.HIDE_DISABLED_PREFS, true); 105 intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_HAS_CHALLENGE, true); 106 intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE, challenge); 107 intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_FOR_FINGERPRINT, true); 108 if (mUserId != UserHandle.USER_NULL) { 109 intent.putExtra(Intent.EXTRA_USER_ID, mUserId); 110 } 111 startActivityForResult(intent, CHOOSE_LOCK_GENERIC_REQUEST); 112 } 113 launchFindSensor(byte[] token)114 private void launchFindSensor(byte[] token) { 115 Intent intent = getFindSensorIntent(); 116 if (token != null) { 117 intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN, token); 118 } 119 if (mUserId != UserHandle.USER_NULL) { 120 intent.putExtra(Intent.EXTRA_USER_ID, mUserId); 121 } 122 startActivityForResult(intent, FINGERPRINT_FIND_SENSOR_REQUEST); 123 } 124 getChooseLockIntent()125 protected Intent getChooseLockIntent() { 126 return new Intent(this, ChooseLockGeneric.class); 127 } 128 getFindSensorIntent()129 protected Intent getFindSensorIntent() { 130 Intent intent = new Intent(this, FingerprintEnrollFindSensor.class); 131 return intent; 132 } 133 134 @Override onActivityResult(int requestCode, int resultCode, Intent data)135 protected void onActivityResult(int requestCode, int resultCode, Intent data) { 136 final boolean isResultFinished = resultCode == RESULT_FINISHED; 137 if (requestCode == FINGERPRINT_FIND_SENSOR_REQUEST) { 138 if (isResultFinished || resultCode == RESULT_SKIP) { 139 final int result = isResultFinished ? RESULT_OK : RESULT_SKIP; 140 setResult(result, data); 141 finish(); 142 return; 143 } 144 } else if (requestCode == CHOOSE_LOCK_GENERIC_REQUEST) { 145 if (isResultFinished) { 146 updatePasswordQuality(); 147 byte[] token = data.getByteArrayExtra( 148 ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN); 149 launchFindSensor(token); 150 return; 151 } 152 } 153 super.onActivityResult(requestCode, resultCode, data); 154 } 155 156 @Override onItemSelected(IItem item)157 public void onItemSelected(IItem item) { 158 switch (((Item) item).getId()) { 159 case R.id.next_button: 160 onNextButtonClick(); 161 break; 162 case R.id.cancel_button: 163 onCancelButtonClick(); 164 break; 165 } 166 } 167 168 @Override getMetricsCategory()169 protected int getMetricsCategory() { 170 return MetricsEvent.FINGERPRINT_ENROLL_INTRO; 171 } 172 onCancelButtonClick()173 protected void onCancelButtonClick() { 174 finish(); 175 } 176 177 private static class LearnMoreSpan extends URLSpan { 178 private static final String TAG = "LearnMoreSpan"; 179 private static final Typeface TYPEFACE_MEDIUM = 180 Typeface.create("sans-serif-medium", Typeface.NORMAL); 181 LearnMoreSpan(String url)182 private LearnMoreSpan(String url) { 183 super(url); 184 } 185 186 @Override onClick(View widget)187 public void onClick(View widget) { 188 Context ctx = widget.getContext(); 189 Intent intent = HelpUtils.getHelpIntent(ctx, getURL(), ctx.getClass().getName()); 190 try { 191 // This needs to be startActivityForResult even though we do not care about the 192 // actual result because the help app needs to know about who invoked it. 193 widget.startActivityForResult(intent, LEARN_MORE_REQUEST); 194 } catch (ActivityNotFoundException e) { 195 Log.w(LearnMoreSpan.TAG, 196 "Actvity was not found for intent, " + intent.toString()); 197 } 198 } 199 200 @Override updateDrawState(TextPaint ds)201 public void updateDrawState(TextPaint ds) { 202 super.updateDrawState(ds); 203 ds.setUnderlineText(false); 204 ds.setTypeface(TYPEFACE_MEDIUM); 205 } 206 linkify(CharSequence rawText, String uri)207 public static CharSequence linkify(CharSequence rawText, String uri) { 208 SpannableString msg = new SpannableString(rawText); 209 Annotation[] spans = msg.getSpans(0, msg.length(), Annotation.class); 210 if (TextUtils.isEmpty(uri)) { 211 CharSequence ret = rawText; 212 for (Annotation annotation : spans) { 213 int start = msg.getSpanStart(annotation); 214 int end = msg.getSpanEnd(annotation); 215 ret = TextUtils.concat(ret.subSequence(0, start), 216 msg.subSequence(end, msg.length())); 217 } 218 return ret; 219 } else { 220 SpannableStringBuilder builder = new SpannableStringBuilder(msg); 221 for (Annotation annotation : spans) { 222 int start = msg.getSpanStart(annotation); 223 int end = msg.getSpanEnd(annotation); 224 LearnMoreSpan link = new LearnMoreSpan(uri); 225 builder.setSpan(link, start, end, msg.getSpanFlags(link)); 226 } 227 return builder; 228 } 229 } 230 } 231 } 232