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