Add fingerprint settings.

- Improved layout.
- Enrollment is now working.
- Added vibration and progress feedback.
- Better fingerprint animation logic.
- Poke userActivity() when sensor is touched.
- Added progress animation.
- Only show fingerprint menu item on devices that have fingerprint hw
- Set View state to GONE for views that aren't shown & fix resulting layout issue
- Fix bug where stage wasn't advancing when returning from ChooseLockGeneric.
- Renamed FingerprintSettings to FingerprintEnroll
- Fixed bug with storing fingerprint ids that prevented the last one from being removed.
- Added better progress indication.  When remaining is at max, count that as the first step.
- Fix whitespace formatting in CL

Fixes bug 1953439

Change-Id: I721bf440c63640203af94ce21340d8281076c249
This commit is contained in:
Jim Miller
2015-02-19 21:04:57 -08:00
parent 78fcb280b9
commit 94d4bd9390
26 changed files with 924 additions and 23 deletions

View File

@@ -72,6 +72,8 @@
<uses-permission android:name="android.permission.READ_SEARCH_INDEXABLES" />
<uses-permission android:name="android.permission.OEM_UNLOCK_STATE" />
<uses-permission android:name="android.permission.OVERRIDE_WIFI_CONFIG" />
<uses-permission android:name="android.permission.USE_FINGERPRINT" />
<uses-permission android:name="android.permission.MANAGE_FINGERPRINT" />
<application android:label="@string/settings_label"
android:icon="@mipmap/ic_launcher_settings"
@@ -1219,6 +1221,9 @@
<activity android:name="ConfirmLockPassword"
android:windowSoftInputMode="stateVisible|adjustResize"/>
<activity android:name="FingerprintSettings" android:exported="false"/>
<activity android:name="FingerprintEnroll" android:exported="false"/>
<!-- Note this must not be exported since it returns the password in the intent -->
<activity android:name="ConfirmLockPattern$InternalActivity"
android:exported="false"/>

Binary file not shown.

After

Width:  |  Height:  |  Size: 928 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 928 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

View File

@@ -0,0 +1,35 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2015 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<animation-list xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/fingerprint_animation"
android:oneshot="false"
android:duration="1000">
<item android:drawable="@drawable/fingerprint_anim00" android:duration="77" />
<item android:drawable="@drawable/fingerprint_anim01" android:duration="77" />
<item android:drawable="@drawable/fingerprint_anim02" android:duration="77" />
<item android:drawable="@drawable/fingerprint_anim03" android:duration="77" />
<item android:drawable="@drawable/fingerprint_anim04" android:duration="77" />
<item android:drawable="@drawable/fingerprint_anim05" android:duration="77" />
<item android:drawable="@drawable/fingerprint_anim06" android:duration="77" />
<item android:drawable="@drawable/fingerprint_anim07" android:duration="77" />
<item android:drawable="@drawable/fingerprint_anim08" android:duration="77" />
<item android:drawable="@drawable/fingerprint_anim09" android:duration="77" />
<item android:drawable="@drawable/fingerprint_anim10" android:duration="77" />
<item android:drawable="@drawable/fingerprint_anim11" android:duration="77" />
<item android:drawable="@drawable/fingerprint_anim12" android:duration="77" />
</animation-list>

View File

@@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2015 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<shape
android:innerRadius="96dip"
android:shape="ring"
android:thickness="4dip">
<gradient
android:startColor="@color/fingerprint_progress_ring_color"
android:endColor="@color/fingerprint_progress_ring_color"
android:angle="180"
/>
</shape>
</item>
</layer-list>

View File

@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2015 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:innerRadius="96dip"
android:shape="ring"
android:thickness="4dip"
android:color="@color/fingerprint_progress_ring_bg">
</shape>

View File

@@ -0,0 +1,143 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2015 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center_vertical">
<!-- Left area -->
<LinearLayout
android:layout_width="0dip"
android:layout_height="match_parent"
android:layout_weight="1"
android:orientation="vertical">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="0dip"
android:layout_weight="0.4"
android:background="@color/fingerprint_title_area_bg"
android:orientation="vertical">
<TextView
android:id="@+id/fingerprint_enroll_title"
android:background="@color/fingerprint_title_area_bg"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="20dip"
android:layout_marginStart="40dip"
android:layout_marginEnd="40dip"
style="@style/TextAppearance.FingerprintTitle"
android:layout_alignParentBottom="true"/>
</LinearLayout>
<TextView
android:id="@+id/fingerprint_enroll_message"
android:layout_width="match_parent"
android:layout_height="0dip"
android:layout_weight="0.6"
android:layout_marginTop="24dip"
android:layout_marginStart="40dip"
android:layout_marginEnd="40dip"
android:layout_marginBottom="36dip"
style="@style/TextAppearance.FingerprintMessage"/>
</LinearLayout>
<!-- Right area -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="0dip"
android:layout_weight="1"
android:orientation="vertical">
<LinearLayout
android:id="@+id/fingerprint_view_selector"
android:layout_width="wrap_content"
android:layout_height="0dip"
android:orientation="vertical"
android:layout_weight="1"
android:layout_gravity="center">
<FrameLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:background="@drawable/fingerprint_progress_ring_bg">
<!-- One of the following views will show for any given mode -->
<ProgressBar
android:id="@+id/fingerprint_progress_bar"
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="200dip"
android:layout_height="200dip"
android:max="100"
android:progress="0"
android:indeterminate="false"
android:progressDrawable="@drawable/fingerprint_progress_ring" />
<ImageView
android:id="@+id/fingerprint_sensor_location"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:src="@drawable/fingerprint_sensor_location"
android:visibility="gone"/>
<ImageView
android:id="@+id/fingerprint_animator"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"
android:src="@drawable/fingerprint_animation"
android:visibility="visible" />
<ImageView
android:id="@+id/fingerprint_in_app_indicator"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:src="@drawable/fingerprint_in_app_indicator"
android:visibility="visible" />
</FrameLayout>
</LinearLayout>
<!-- Button area -->
<LinearLayout
android:id="@+id/fingerprint_enroll_button_area"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<Button
android:id="@+id/fingerprint_enroll_button_add"
style="@style/SecurityPreferenceButton"
android:text="@string/fingerprint_enroll_button_add" />
<Button
android:id="@+id/fingerprint_enroll_button_next"
style="@style/SecurityPreferenceButton"
android:text="@string/fingerprint_enroll_button_next" />
</LinearLayout>
</LinearLayout>
</LinearLayout>

View File

@@ -0,0 +1,128 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2015 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center_horizontal">
<!-- Upper title area -->
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="0dip"
android:layout_weight="0.3"
android:background="@color/fingerprint_title_area_bg">
<TextView
android:id="@+id/fingerprint_enroll_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="20dip"
android:layout_marginStart="40dip"
android:layout_marginEnd="40dip"
style="@style/TextAppearance.FingerprintTitle"
android:layout_alignParentBottom="true"/>
</RelativeLayout>
<!-- Lower message area -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="0dip"
android:layout_weight="0.6"
android:orientation="vertical">
<TextView
android:id="@+id/fingerprint_enroll_message"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="24dip"
android:layout_marginStart="40dip"
android:layout_marginEnd="40dip"
android:layout_marginBottom="36dip"
style="@style/TextAppearance.FingerprintMessage"/>
<FrameLayout
android:id="@+id/fingerprint_view_selector"
android:layout_width="match_parent"
android:layout_height="0dip"
android:layout_weight="1"
android:layout_gravity="center">
<FrameLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:background="@drawable/fingerprint_progress_ring_bg">
<!-- One of the following views will show for any given mode -->
<ProgressBar
android:id="@+id/fingerprint_progress_bar"
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="200dip"
android:layout_height="200dip"
android:max="100"
android:progress="0"
android:indeterminate="false"
android:progressDrawable="@drawable/fingerprint_progress_ring" />
<ImageView
android:id="@+id/fingerprint_sensor_location"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:src="@drawable/fingerprint_sensor_location" />
<ImageView
android:id="@+id/fingerprint_animator"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"
android:src="@drawable/fingerprint_animation" />
<ImageView
android:id="@+id/fingerprint_in_app_indicator"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:src="@drawable/fingerprint_in_app_indicator" />
</FrameLayout>
</FrameLayout>
</LinearLayout>
<!-- Button area -->
<LinearLayout
android:id="@+id/fingerprint_enroll_button_area"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<Button
android:id="@+id/fingerprint_enroll_button_add"
style="@style/SecurityPreferenceButton"
android:text="@string/fingerprint_enroll_button_add" />
<Button
android:id="@+id/fingerprint_enroll_button_next"
style="@style/SecurityPreferenceButton"
android:text="@string/fingerprint_enroll_button_next" />
</LinearLayout>
</LinearLayout>

View File

@@ -60,6 +60,12 @@
<color name="unlock_pattern_view_success_color">@color/theme_accent</color>
<color name="unlock_pattern_view_error_color">#fff4511e</color>
<color name="fingerprint_title_area_bg">#ff009688</color>
<color name="fingerprint_title_color">#ffffffff</color>
<color name="fingerprint_message_color">#de000000</color>
<color name="fingerprint_progress_ring_color">#ff009688</color>
<color name="fingerprint_progress_ring_bg">#20000000</color>
<color name="running_processes_system_ram">#ff384248</color>
<color name="running_processes_apps_ram">#ff009587</color>
<color name="running_processes_free_ram">#ffced7db</color>

View File

@@ -756,6 +756,54 @@
<!-- In the security screen, the header title for settings related to Passwords-->
<string name="security_passwords_title">Passwords</string>
<!-- Fingerprint enrollment and settings --><skip />
<!-- Title shown for menu item that launches fingerprint settings or enrollment [CHAR LIMIT=22] -->
<string name="security_settings_fingerprint_preference_title">Fingerprint</string>
<!-- Message shown for menu item that launches fingerprint settings or enrollment -->
<plurals name="security_settings_fingerprint_preference_summary">
<item quantity="one"><xliff:g id="count">%1$d</xliff:g> fingerprint enrolled</item>
<item quantity="other"><xliff:g id="count">%1$d</xliff:g> fingerprints enrolled</item>
</plurals>
<!-- Introduction title shown in fingerprint enrollment dialog [CHAR LIMIT=22] -->
<string name="security_settings_fingerprint_enroll_onboard_title">Fingerprint setup</string>
<!-- Introduction detail message shown in fingerprint enrollment dialog -->
<string name="security_settings_fingerprint_enroll_onboard_message">
To use your fingerprint to unlock your screen or confirm purchases, we\'ll need to:
\n\u2713 Set up your background screen lock method
\n\u2713 Add your fingerprint</string>
<!-- Title shown in fingerprint enrollment dialog to begin enrollment [CHAR LIMIT=22]-->
<string name="security_settings_fingerprint_enroll_start_title">Let\'s start!</string>
<!-- Message shown in fingerprint enrollment dialog to begin enrollment -->
<string name="security_settings_fingerprint_enroll_start_message">Put your finger on the fingerprint sensor. Lift after you feel a vibration.</string>
<!-- Title shown in fingerprint enrollment dialog to repeat touching the fingerprint sensor [CHAR LIMIT=22] -->
<string name="security_settings_fingerprint_enroll_repeat_title">Great! Now repeat.</string>
<!-- Message shown in fingerprint enrollment dialog to repeat touching the fingerprint sensor -->
<string name="security_settings_fingerprint_enroll_repeat_message">Put the same finger on the fingerprint sensor and lift after you feel a vibration.</string>
<!-- Title shown in fingerprint enrollment dialog once enrollment is completed [CHAR LIMIT=22] -->
<string name="security_settings_fingerprint_enroll_finish_title">Fingerprint added!</string>
<!-- Message shown in fingerprint enrollment dialog once enrollment is completed -->
<string name="security_settings_fingerprint_enroll_finish_message">Whenever you see this icon, you can use your fingerprint for identification or to authorize a purchase.</string>
<!-- Button text shown at the end of enrollment that allows the user to add another fingerprint -->
<string name="fingerprint_enroll_button_add">Add</string>
<!-- Button text shown at the end of enrollment that allows the user to move to the next step -->
<string name="fingerprint_enroll_button_next">Next</string>
<!-- Error message shown when the fingerprint cannot be recognized -->
<string name="fingerprint_acquired_try_again">Partial fingerprint detected. Please try again.</string>
<!-- Error message shown when the fingerprint sensor needs cleaning -->
<string name="fingerprint_acquired_imager_dirty">Fingerprint sensor is dirty. Please clean and try again.</string>
<!-- Error message shown when the user removes their finger from the sensor too quickly -->
<string name="fingerprint_acquired_too_fast">Finger moved to fast. Please try again.</string>
<!-- Error message shown when the user moves their finger too slowly -->
<string name="fingerprint_acquired_too_slow">Finger moved to slow. Please try again.</string>
<!-- Generic error message shown when the fingerprint hardware can't recognize the fingerprint -->
<string name="fingerprint_error_unable_to_process">Unable to process. Try again.</string>
<!-- Error message shown when the fingerprint hardware can't be accessed -->
<string name="fingerprint_error_hw_not_available">Hardware not available.</string>
<!-- Error message shown when the fingerprint hardware has run out of room for storing fingerprints -->
<string name="fingerprint_error_no_space">Fingerprint can\'t be stored. Please remove an existing fingerprint.</string>
<!-- Error message shown when the fingerprint hardware timer has expired and the user needs to restart the operation. -->
<string name="fingerprint_error_timeout">Fingerprint time out reached. Try again.</string>
<!-- Title of the preferences category for preference items to control encryption -->
<string name="crypt_keeper_settings_title">Encryption</string>

View File

@@ -351,4 +351,19 @@
<style name="TextAppearance.ResultTitle" parent="TextAppearance.CategoryTitle">
</style>
<style name="TextAppearance.FingerprintTitle"
parent="android:TextAppearance.Material.Large.Inverse">
<item name="android:textStyle">bold</item>
<item name="android:textSize">24sp</item>
<item name="android:textColor">@color/fingerprint_title_color</item>
</style>
<style name="TextAppearance.FingerprintMessage"
parent="android:TextAppearance.Material.Medium.Inverse">
<item name="android:textStyle">bold</item>
<item name="android:textSize">16sp</item>
<item name="android:textColor">@color/fingerprint_message_color</item>
</style>
</resources>

View File

@@ -0,0 +1,435 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings;
import android.animation.Animator;
import android.animation.Animator.AnimatorListener;
import android.animation.ObjectAnimator;
import android.app.Activity;
import android.app.Fragment;
import android.content.Context;
import android.content.Intent;
import android.content.res.Resources;
import android.graphics.drawable.AnimationDrawable;
import android.media.AudioAttributes;
import android.os.Bundle;
import android.os.PowerManager;
import android.os.SystemClock;
import android.os.Vibrator;
import android.service.fingerprint.FingerprintManager;
import android.service.fingerprint.FingerprintManagerReceiver;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.widget.ImageView;
import android.widget.ProgressBar;
import android.widget.TextView;
import com.android.internal.widget.LockPatternUtils;
/**
* Wizard to enroll a fingerprint
*/
public class FingerprintEnroll extends SettingsActivity {
/**
* Used by the choose fingerprint wizard to indicate the wizard is
* finished, and each activity in the wizard should finish.
* <p>
* Previously, each activity in the wizard would finish itself after
* starting the next activity. However, this leads to broken 'Back'
* behavior. So, now an activity does not finish itself until it gets this
* result.
*/
static final int RESULT_FINISHED = RESULT_FIRST_USER;
@Override
public Intent getIntent() {
Intent modIntent = new Intent(super.getIntent());
modIntent.putExtra(EXTRA_SHOW_FRAGMENT, FingerprintEnrollFragment.class.getName());
return modIntent;
}
@Override
protected boolean isValidFragment(String fragmentName) {
if (FingerprintEnrollFragment.class.getName().equals(fragmentName)) return true;
return false;
}
@Override
public void onCreate(Bundle savedInstanceState) {
requestWindowFeature(Window.FEATURE_NO_TITLE);
super.onCreate(savedInstanceState);
CharSequence msg = getText(R.string.security_settings_fingerprint_preference_title);
setTitle(msg);
}
public static class FingerprintEnrollFragment extends Fragment implements View.OnClickListener {
private static final String TAG = "FingerprintEnroll";
private static final boolean DEBUG = true;
private static final int CONFIRM_REQUEST = 101;
private static final int CHOOSE_LOCK_GENERIC_REQUEST = 102;
private static final long ENROLL_TIMEOUT = 300*1000;
private PowerManager mPowerManager;
private FingerprintManager mFingerprintManager;
private View mContentView;
private TextView mTitleText;
private TextView mMessageText;
private Stage mStage;
private int mEnrollmentSteps;
private boolean mEnrolling;
private Vibrator mVibrator;
private ProgressBar mProgressBar;
private ImageView mFingerprintAnimator;
private ObjectAnimator mProgressAnim;
private final AnimatorListener mProgressAnimationListener = new AnimatorListener() {
@Override
public void onAnimationStart(Animator animation) { }
@Override
public void onAnimationRepeat(Animator animation) { }
@Override
public void onAnimationEnd(Animator animation) {
if (mProgressBar.getProgress() >= 100) {
updateStage(Stage.EnrollingFinish);
}
}
@Override
public void onAnimationCancel(Animator animation) { }
};
// This contains a list of all views managed by the UI. Used to determine which views
// need to be shown/hidden at each stage. It should be the union of the lists that follow
private static final int MANAGED_VIEWS[] = {
R.id.fingerprint_sensor_location,
R.id.fingerprint_animator,
R.id.fingerprint_enroll_button_area,
R.id.fingerprint_in_app_indicator,
R.id.fingerprint_enroll_button_add,
R.id.fingerprint_enroll_button_next,
R.id.fingerprint_progress_bar
};
private static final int VIEWS_ENROLL_ONBOARD[] = {
R.id.fingerprint_enroll_button_area,
R.id.fingerprint_enroll_button_next
};
private static final int VIEWS_ENROLL_START[] = {
R.id.fingerprint_sensor_location,
R.id.fingerprint_progress_bar
};
private static final int VIEWS_ENROLL_PROGRESS[] = {
R.id.fingerprint_animator,
R.id.fingerprint_progress_bar
};
private static final int VIEWS_ENROLL_FINISH[] = {
R.id.fingerprint_enroll_button_area,
R.id.fingerprint_in_app_indicator,
R.id.fingerprint_enroll_button_add,
R.id.fingerprint_enroll_button_next
};
enum Stage {
EnrollingOnboarding(R.string.security_settings_fingerprint_enroll_onboard_title,
R.string.security_settings_fingerprint_enroll_onboard_message,
VIEWS_ENROLL_ONBOARD),
EnrollingStart(R.string.security_settings_fingerprint_enroll_start_title,
R.string.security_settings_fingerprint_enroll_start_message,
VIEWS_ENROLL_START),
EnrollingRepeat(R.string.security_settings_fingerprint_enroll_repeat_title,
R.string.security_settings_fingerprint_enroll_repeat_message,
VIEWS_ENROLL_PROGRESS),
EnrollingFinish(R.string.security_settings_fingerprint_enroll_finish_title,
R.string.security_settings_fingerprint_enroll_finish_message,
VIEWS_ENROLL_FINISH);
Stage(int title, int message, int[] enabledViewIds) {
this.title = title;
this.message = message;
this.enabledViewIds = enabledViewIds;
}
public int title;
public int message;
public int[] enabledViewIds;
};
void updateStage(Stage stage) {
if (DEBUG) Log.v(TAG, "updateStage(" + stage.toString() + ")");
// Show/hide views
for (int i = 0; i < MANAGED_VIEWS.length; i++) {
mContentView.findViewById(MANAGED_VIEWS[i]).setVisibility(View.INVISIBLE);
}
for (int i = 0; i < stage.enabledViewIds.length; i++) {
mContentView.findViewById(stage.enabledViewIds[i]).setVisibility(View.VISIBLE);
}
setTitleMessage(stage.title);
setMessage(stage.message);
if (mStage != stage) {
onStageChanged(stage);
mStage = stage;
}
}
private void startFingerprintAnimator() {
AnimationDrawable drawable = (AnimationDrawable) mFingerprintAnimator.getDrawable();
drawable.start();
}
private void stopFingerprintAnimator() {
AnimationDrawable drawable = (AnimationDrawable) mFingerprintAnimator.getDrawable();
drawable.stop();
drawable.setLevel(0);
}
private void onStageChanged(Stage stage) {
// Update state
switch (stage) {
case EnrollingOnboarding:
mEnrollmentSteps = -1;
mEnrolling = false;
break;
case EnrollingStart:
mEnrollmentSteps = -1;
mFingerprintManager.startListening(mReceiver);
mFingerprintManager.enroll(ENROLL_TIMEOUT);
mProgressBar.setProgress(0);
mEnrolling = true;
startFingerprintAnimator(); // XXX hack - this should follow fingerprint detection
break;
case EnrollingRepeat:
break;
case EnrollingFinish:
stopFingerprintAnimator(); // XXX hack - this should follow fingerprint detection
mFingerprintManager.stopListening();
mEnrolling = false;
break;
default:
mFingerprintManager.stopListening();
break;
}
}
private void cancelEnrollment() {
if (mEnrolling) {
if (DEBUG) Log.v(TAG, "Cancel enrollment\n");
mFingerprintManager.enrollCancel();
mEnrolling = false;
}
}
@Override
public void onDetach() {
super.onDetach();
// Do a little cleanup
cancelEnrollment();
mFingerprintManager.stopListening();
}
private void updateProgress(int progress) {
if (DEBUG) Log.v(TAG, "Progress: " + progress);
if (mVibrator != null) {
mVibrator.vibrate(100, new AudioAttributes.Builder()
.setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
.setUsage(AudioAttributes.USAGE_ASSISTANCE_SONIFICATION).build());
}
if (mProgressAnim != null) {
mProgressAnim.cancel();
}
ObjectAnimator anim = ObjectAnimator.ofInt(mProgressBar, "progress",
mProgressBar.getProgress(), progress);
anim.addListener(mProgressAnimationListener);
anim.start();
mProgressAnim = anim;
}
private void setMessage(int id) {
if (id != 0) mMessageText.setText(id);
}
private void setTitleMessage(int title) {
if (title != 0) mTitleText.setText(title);
}
private FingerprintManagerReceiver mReceiver = new FingerprintManagerReceiver() {
public void onEnrollResult(int fingerprintId, int remaining) {
if (DEBUG) Log.v(TAG, "onEnrollResult(id=" + fingerprintId + ", rem=" + remaining);
if (mEnrollmentSteps == -1) {
mEnrollmentSteps = remaining;
updateStage(Stage.EnrollingRepeat);
}
if (remaining >= 0) {
int progress = Math.max(0, mEnrollmentSteps + 1 - remaining);
updateProgress(100*progress / (mEnrollmentSteps + 1));
// Treat fingerprint like a touch event
mPowerManager.userActivity(SystemClock.uptimeMillis(),
PowerManager.USER_ACTIVITY_EVENT_OTHER,
PowerManager.USER_ACTIVITY_FLAG_NO_CHANGE_LIGHTS);
}
}
public void onError(int error) {
switch(error) {
case FingerprintManager.FINGERPRINT_ERROR_UNABLE_TO_PROCESS:
setMessage(R.string.fingerprint_error_unable_to_process);
break;
case FingerprintManager.FINGERPRINT_ERROR_HW_UNAVAILABLE:
setMessage(R.string.fingerprint_error_hw_not_available);
break;
case FingerprintManager.FINGERPRINT_ERROR_NO_SPACE:
setMessage(R.string.fingerprint_error_no_space);
break;
case FingerprintManager.FINGERPRINT_ERROR_TIMEOUT:
setMessage(R.string.fingerprint_error_timeout);
break;
case FingerprintManager.FINGERPRINT_ERROR_NO_RECEIVER:
Log.w(TAG, "Receiver not registered");
break;
}
}
public void onRemoved(int fingerprintId) {
if (DEBUG) Log.v(TAG, "onRemoved(id=" + fingerprintId + ")");
}
@Override
public void onProcessed(int fingerprintId) {
if (DEBUG) Log.v(TAG, "onProcessed(id=" + fingerprintId + ")");
}
public void onAcquired(int scanInfo) {
int msgId = 0;
startFingerprintAnimator();
switch(scanInfo) {
case FingerprintManager.FINGERPRINT_ACQUIRED_GOOD:
break;
case FingerprintManager.FINGERPRINT_ACQUIRED_IMAGER_DIRTY:
msgId = R.string.fingerprint_acquired_imager_dirty;
break;
case FingerprintManager.FINGERPRINT_ACQUIRED_TOO_SLOW:
msgId = R.string.fingerprint_acquired_too_fast;
break;
case FingerprintManager.FINGERPRINT_ACQUIRED_TOO_FAST:
msgId = R.string.fingerprint_acquired_too_slow;
break;
case FingerprintManager.FINGERPRINT_ACQUIRED_PARTIAL:
case FingerprintManager.FINGERPRINT_ACQUIRED_INSUFFICIENT:
msgId = R.string.fingerprint_acquired_try_again;
break;
default:
// Try not to be too verbose in the UI. The user just needs to try again.
// Log the message so we can dig into the issue if necessary.
Log.w(TAG, "Try again because scanInfo was " + scanInfo);
msgId = R.string.fingerprint_acquired_try_again;
break;
}
setMessage(msgId);
}
};
private boolean runConfirmDeviceCredentials(int request) {
if (DEBUG) Log.v(TAG, "runKeyguardConfirmation(" + request + ")");
Resources res = getResources();
return new ChooseLockSettingsHelper(getActivity(), this)
.launchConfirmationActivity(request,
res.getText(R.string.master_clear_gesture_prompt),
res.getText(R.string.master_clear_gesture_explanation));
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == CHOOSE_LOCK_GENERIC_REQUEST) {
if (resultCode == RESULT_FINISHED) {
// The lock pin/pattern/password was set. Start enrolling!
updateStage(Stage.EnrollingStart);
}
}
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
final Activity activity = getActivity();
mFingerprintManager = (FingerprintManager)activity
.getSystemService(Context.FINGERPRINT_SERVICE);
mVibrator = (Vibrator) activity.getSystemService(Context.VIBRATOR_SERVICE);
mPowerManager = (PowerManager) activity.getSystemService(Context.POWER_SERVICE);
mContentView = inflater.inflate(R.layout.fingerprint_enroll, null);
mTitleText = (TextView) mContentView.findViewById(R.id.fingerprint_enroll_title);
mMessageText = (TextView) mContentView.findViewById(R.id.fingerprint_enroll_message);
mProgressBar = (ProgressBar) mContentView.findViewById(R.id.fingerprint_progress_bar);
mFingerprintAnimator = (ImageView) mContentView.findViewById(R.id.fingerprint_animator);
final int buttons[] = {
R.id.fingerprint_enroll_button_add,
R.id.fingerprint_enroll_button_next };
for (int i = 0; i < buttons.length; i++) {
mContentView.findViewById(buttons[i]).setOnClickListener(this);
}
LockPatternUtils utils = new LockPatternUtils(activity);
if (!utils.isSecure()) {
// Device doesn't have any security. Set that up first.
updateStage(Stage.EnrollingOnboarding);
} else {
updateStage(Stage.EnrollingStart);
}
return mContentView;
}
@Override
public void onClick(View v) {
switch(v.getId()) {
case R.id.fingerprint_enroll_button_add:
updateStage(Stage.EnrollingStart);
break;
case R.id.fingerprint_enroll_button_next:
if (mStage == Stage.EnrollingOnboarding) {
launchChooseLock();
} else if (mStage == Stage.EnrollingFinish) {
getActivity().finish();
} else {
Log.v(TAG, "No idea what to do next!");
}
break;
}
}
private void launchChooseLock() {
Intent intent = new Intent();
intent.setClassName("com.android.settings", ChooseLockGeneric.class.getName());
startActivityForResult(intent, CHOOSE_LOCK_GENERIC_REQUEST);
}
}
}

View File

@@ -41,6 +41,8 @@ import android.provider.SearchIndexableResource;
import android.provider.Settings;
import android.provider.Settings.SettingNotFoundException;
import android.security.KeyStore;
import android.service.fingerprint.FingerprintManager;
import android.service.fingerprint.FingerprintManager.FingerprintItem;
import android.service.trust.TrustAgentService;
import android.telephony.TelephonyManager;
import android.telephony.SubscriptionManager;
@@ -220,32 +222,12 @@ public class SecuritySettings extends SettingsPreferenceFragment
}
}
// Trust Agent preferences
// Fingerprint and trust agents
PreferenceGroup securityCategory = (PreferenceGroup)
root.findPreference(KEY_SECURITY_CATEGORY);
if (securityCategory != null) {
final boolean hasSecurity = mLockPatternUtils.isSecure();
ArrayList<TrustAgentComponentInfo> agents =
getActiveTrustAgents(getPackageManager(), mLockPatternUtils);
for (int i = 0; i < agents.size(); i++) {
final TrustAgentComponentInfo agent = agents.get(i);
Preference trustAgentPreference =
new Preference(securityCategory.getContext());
trustAgentPreference.setKey(KEY_TRUST_AGENT);
trustAgentPreference.setTitle(agent.title);
trustAgentPreference.setSummary(agent.summary);
// Create intent for this preference.
Intent intent = new Intent();
intent.setComponent(agent.componentName);
intent.setAction(Intent.ACTION_MAIN);
trustAgentPreference.setIntent(intent);
// Add preference to the settings menu.
securityCategory.addPreference(trustAgentPreference);
if (!hasSecurity) {
trustAgentPreference.setEnabled(false);
trustAgentPreference.setSummary(R.string.disabled_because_no_backup_security);
}
}
maybeAddFingerprintPreference(securityCategory);
addTrustAgentSettings(securityCategory);
}
// lock after preference
@@ -347,6 +329,58 @@ public class SecuritySettings extends SettingsPreferenceFragment
return root;
}
private void maybeAddFingerprintPreference(PreferenceGroup securityCategory) {
FingerprintManager fpm = (FingerprintManager) getActivity().getSystemService(
Context.FINGERPRINT_SERVICE);
if (!fpm.isHardwareDetected()) {
Log.v(TAG, "No fingerprint hardware detected!!");
return;
}
Preference fingerprintPreference = new Preference(securityCategory.getContext());
fingerprintPreference.setKey(KEY_TRUST_AGENT);
fingerprintPreference.setTitle(R.string.security_settings_fingerprint_preference_title);
Intent intent = new Intent();
List<FingerprintItem> items = fpm.getEnrolledFingerprints();
int fingerprintCount = items.size();
if (fingerprintCount > 0) {
fingerprintPreference.setSummary(getResources().getQuantityString(
R.plurals.security_settings_fingerprint_preference_summary,
fingerprintCount, fingerprintCount));
// TODO: Launch fingerprintSettings instead...
intent.setClassName("com.android.settings", FingerprintEnroll.class.getName());
} else {
// No fingerprints registered, launch directly into fingerprint enrollment wizard
intent.setClassName("com.android.settings", FingerprintEnroll.class.getName());
}
fingerprintPreference.setIntent(intent);
securityCategory.addPreference(fingerprintPreference);
}
private void addTrustAgentSettings(PreferenceGroup securityCategory) {
final boolean hasSecurity = mLockPatternUtils.isSecure();
ArrayList<TrustAgentComponentInfo> agents =
getActiveTrustAgents(getPackageManager(), mLockPatternUtils);
for (int i = 0; i < agents.size(); i++) {
final TrustAgentComponentInfo agent = agents.get(i);
Preference trustAgentPreference =
new Preference(securityCategory.getContext());
trustAgentPreference.setKey(KEY_TRUST_AGENT);
trustAgentPreference.setTitle(agent.title);
trustAgentPreference.setSummary(agent.summary);
// Create intent for this preference.
Intent intent = new Intent();
intent.setComponent(agent.componentName);
intent.setAction(Intent.ACTION_MAIN);
trustAgentPreference.setIntent(intent);
// Add preference to the settings menu.
securityCategory.addPreference(trustAgentPreference);
if (!hasSecurity) {
trustAgentPreference.setEnabled(false);
trustAgentPreference.setSummary(R.string.disabled_because_no_backup_security);
}
}
}
/* Return true if a there is a Slot that has Icc.
*/
private boolean isSimIccReady() {