Update confirm device credentials to spec, add fingerprint

- New strings in the screen.
- New layout/style.
- Clean up internal API's around it.
- Add fingerprint support if launched from externally
- Separate theme if launched from externally
- If launched from above Keyguard, use SHOW_WHEN_LOCKED flag

Change-Id: Icdf9bf9e0506841f24e8aab5f0f1d1f4b688951f
This commit is contained in:
Jorim Jaggi
2015-04-06 17:47:18 -07:00
parent b5aa73f46f
commit 8a09b619ae
41 changed files with 1005 additions and 824 deletions

View File

@@ -145,7 +145,8 @@ public class ChooseLockGeneric extends SettingsActivity {
} else if (!mWaitingForConfirmation) {
ChooseLockSettingsHelper helper =
new ChooseLockSettingsHelper(this.getActivity(), this);
if (!helper.launchConfirmationActivity(CONFIRM_EXISTING_REQUEST, null, null)) {
if (!helper.launchConfirmationActivity(CONFIRM_EXISTING_REQUEST,
getString(R.string.unlock_set_unlock_launch_picker_title))) {
mPasswordConfirmed = true; // no password set, so no need to confirm
updatePreferencesOrFinish();
} else {

View File

@@ -243,7 +243,7 @@ public class ChooseLockPassword extends SettingsActivity {
updateStage(Stage.Introduction);
if (confirmCredentials) {
mChooseLockSettingsHelper.launchConfirmationActivity(CONFIRM_EXISTING_REQUEST,
null, null);
getString(R.string.unlock_set_unlock_launch_picker_title));
}
} else {
mFirstPin = savedInstanceState.getString(KEY_FIRST_PIN);

View File

@@ -379,7 +379,8 @@ public class ChooseLockPattern extends SettingsActivity {
updateStage(Stage.NeedToConfirm);
boolean launchedConfirmationActivity =
mChooseLockSettingsHelper.launchConfirmationActivity(
CONFIRM_EXISTING_REQUEST, null, null);
CONFIRM_EXISTING_REQUEST,
getString(R.string.unlock_set_unlock_launch_picker_title));
if (!launchedConfirmationActivity) {
updateStage(Stage.Introduction);
}

View File

@@ -16,13 +16,14 @@
package com.android.settings;
import com.android.internal.widget.LockPatternUtils;
import android.annotation.Nullable;
import android.app.Activity;
import android.app.Fragment;
import android.app.admin.DevicePolicyManager;
import android.content.Intent;
import com.android.internal.widget.LockPatternUtils;
public final class ChooseLockSettingsHelper {
static final String EXTRA_KEY_TYPE = "type";
@@ -48,64 +49,77 @@ public final class ChooseLockSettingsHelper {
/**
* If a pattern, password or PIN exists, prompt the user before allowing them to change it.
* @param message optional message to display about the action about to be done
* @param details optional detail message to display
*
* @param title title of the confirmation screen; shown in the action bar
* @return true if one exists and we launched an activity to confirm it
* @see #onActivityResult(int, int, android.content.Intent)
* @see Activity#onActivityResult(int, int, android.content.Intent)
*/
boolean launchConfirmationActivity(int request, CharSequence message, CharSequence details) {
return launchConfirmationActivity(request, message, details, false);
boolean launchConfirmationActivity(int request, CharSequence title) {
return launchConfirmationActivity(request, title, null, null, false, false);
}
/**
* If a pattern, password or PIN exists, prompt the user before allowing them to change it.
* @param message optional message to display about the action about to be done
* @param details optional detail message to display
*
* @param title title of the confirmation screen; shown in the action bar
* @param returnCredentials if true, put credentials into intent. Note that if this is true,
this can only be called internally.
* this can only be called internally.
* @return true if one exists and we launched an activity to confirm it
* @see #onActivityResult(int, int, android.content.Intent)
* @see Activity#onActivityResult(int, int, android.content.Intent)
*/
boolean launchConfirmationActivity(int request, CharSequence message, CharSequence details,
boolean returnCredentials) {
boolean launchConfirmationActivity(int request, CharSequence title, boolean returnCredentials) {
return launchConfirmationActivity(request, title, null, null, returnCredentials, false);
}
/**
* If a pattern, password or PIN exists, prompt the user before allowing them to change it.
*
* @param title title of the confirmation screen; shown in the action bar
* @param header header of the confirmation screen; shown as large text
* @param description description of the confirmation screen
* @param returnCredentials if true, put credentials into intent. Note that if this is true,
* this can only be called internally.
* @param external specifies whether this activity is launched externally, meaning that it will
* get a dark theme and allow fingerprint authentication
* @return true if one exists and we launched an activity to confirm it
* @see Activity#onActivityResult(int, int, android.content.Intent)
*/
boolean launchConfirmationActivity(int request, @Nullable CharSequence title,
@Nullable CharSequence header, @Nullable CharSequence description,
boolean returnCredentials, boolean external) {
boolean launched = false;
switch (mLockPatternUtils.getKeyguardStoredPasswordQuality()) {
case DevicePolicyManager.PASSWORD_QUALITY_SOMETHING:
launched = confirmPattern(request, message, details, returnCredentials);
launched = launchConfirmationActivity(request, title, header, description,
returnCredentials
? ConfirmLockPattern.InternalActivity.class
: ConfirmLockPattern.class, external);
break;
case DevicePolicyManager.PASSWORD_QUALITY_NUMERIC:
case DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX:
case DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC:
case DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC:
case DevicePolicyManager.PASSWORD_QUALITY_COMPLEX:
// TODO: update UI layout for ConfirmPassword to show message and details
launched = confirmPassword(request, message, returnCredentials);
launched = launchConfirmationActivity(request, title, header, description,
returnCredentials
? ConfirmLockPassword.InternalActivity.class
: ConfirmLockPassword.class, external);
break;
}
return launched;
}
/**
* Launch screen to confirm the existing lock pattern.
* @param message shown in header of ConfirmLockPattern if not null
* @param details shown in footer of ConfirmLockPattern if not null
* @param returnCredentials if true, put credentials into intent.
* @see #onActivityResult(int, int, android.content.Intent)
* @return true if we launched an activity to confirm pattern
*/
private boolean confirmPattern(int request, CharSequence message,
CharSequence details, boolean returnCredentials) {
if (!mLockPatternUtils.isLockPatternEnabled()) {
return false;
}
private boolean launchConfirmationActivity(int request, CharSequence title, CharSequence header,
CharSequence message, Class<?> activityClass, boolean external) {
final Intent intent = new Intent();
// supply header and footer text in the intent
intent.putExtra(ConfirmLockPattern.HEADER_TEXT, message);
intent.putExtra(ConfirmLockPattern.FOOTER_TEXT, details);
intent.setClassName("com.android.settings",
returnCredentials
? ConfirmLockPattern.InternalActivity.class.getName()
: ConfirmLockPattern.class.getName());
intent.putExtra(ConfirmDeviceCredentialBaseFragment.TITLE_TEXT, title);
intent.putExtra(ConfirmDeviceCredentialBaseFragment.HEADER_TEXT, header);
intent.putExtra(ConfirmDeviceCredentialBaseFragment.DETAILS_TEXT, message);
intent.putExtra(ConfirmDeviceCredentialBaseFragment.ALLOW_FP_AUTHENTICATION, external);
intent.putExtra(ConfirmDeviceCredentialBaseFragment.DARK_THEME, external);
intent.putExtra(ConfirmDeviceCredentialBaseFragment.SHOW_CANCEL_BUTTON, external);
intent.putExtra(ConfirmDeviceCredentialBaseFragment.SHOW_WHEN_LOCKED, external);
intent.setClassName(ConfirmDeviceCredentialBaseFragment.PACKAGE, activityClass.getName());
if (mFragment != null) {
mFragment.startActivityForResult(intent, request);
} else {
@@ -113,31 +127,4 @@ public final class ChooseLockSettingsHelper {
}
return true;
}
/**
* Launch screen to confirm the existing lock password.
* @param message shown in header of ConfirmLockPassword if not null
* @param returnCredentials if true, put credentials into intent.
* @see #onActivityResult(int, int, android.content.Intent)
* @return true if we launched an activity to confirm password
*/
private boolean confirmPassword(int request, CharSequence message,
boolean returnCredentials) {
if (!mLockPatternUtils.isLockPasswordEnabled()) return false;
final Intent intent = new Intent();
// supply header text in the intent
intent.putExtra(ConfirmLockPattern.HEADER_TEXT, message);
intent.setClassName("com.android.settings",
returnCredentials
? ConfirmLockPassword.InternalActivity.class.getName()
: ConfirmLockPassword.class.getName());
if (mFragment != null) {
mFragment.startActivityForResult(intent, request);
} else {
mActivity.startActivityForResult(intent, request);
}
return true;
}
}

View File

@@ -48,7 +48,8 @@ public class ConfirmDeviceCredentialActivity extends Activity {
String details = intent.getStringExtra(KeyguardManager.EXTRA_DESCRIPTION);
ChooseLockSettingsHelper helper = new ChooseLockSettingsHelper(this);
if (!helper.launchConfirmationActivity(0 /* request code */, title, details)) {
if (!helper.launchConfirmationActivity(0 /* request code */, null /* title */, title,
details, false /* returnCredentials */, true /* isExternal */)) {
Log.d(TAG, "No pattern, password or PIN set.");
setResult(Activity.RESULT_OK);
finish();

View File

@@ -0,0 +1,54 @@
/*
* 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.app.KeyguardManager;
import android.os.Bundle;
import android.view.MenuItem;
import android.view.WindowManager;
public class ConfirmDeviceCredentialBaseActivity extends SettingsActivity {
@Override
protected void onCreate(Bundle savedState) {
if (getIntent().getBooleanExtra(ConfirmDeviceCredentialBaseFragment.DARK_THEME, false)) {
setTheme(R.style.Theme_ConfirmDeviceCredentialsDark);
}
super.onCreate(savedState);
boolean deviceLocked = getSystemService(KeyguardManager.class).isKeyguardLocked();
if (deviceLocked && getIntent().getBooleanExtra(
ConfirmDeviceCredentialBaseFragment.SHOW_WHEN_LOCKED, false)) {
getWindow().addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED);
}
CharSequence msg = getIntent().getStringExtra(
ConfirmDeviceCredentialBaseFragment.TITLE_TEXT);
setTitle(msg);
if (getActionBar() != null) {
getActionBar().setDisplayHomeAsUpEnabled(true);
getActionBar().setHomeButtonEnabled(true);
}
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
if (item.getItemId() == android.R.id.home) {
finish();
return true;
}
return super.onOptionsItemSelected(item);
}
}

View File

@@ -0,0 +1,102 @@
/*
* 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.annotation.Nullable;
import android.app.Fragment;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView;
/**
* Base fragment to be shared for PIN/Pattern/Password confirmation fragments.
*/
public abstract class ConfirmDeviceCredentialBaseFragment extends InstrumentedFragment
implements FingerprintUiHelper.Callback {
public static final String PACKAGE = "com.android.settings";
public static final String TITLE_TEXT = PACKAGE + ".ConfirmCredentials.title";
public static final String HEADER_TEXT = PACKAGE + ".ConfirmCredentials.header";
public static final String DETAILS_TEXT = PACKAGE + ".ConfirmCredentials.details";
public static final String ALLOW_FP_AUTHENTICATION =
PACKAGE + ".ConfirmCredentials.allowFpAuthentication";
public static final String DARK_THEME = PACKAGE + ".ConfirmCredentials.darkTheme";
public static final String SHOW_CANCEL_BUTTON =
PACKAGE + ".ConfirmCredentials.showCancelButton";
public static final String SHOW_WHEN_LOCKED =
PACKAGE + ".ConfirmCredentials.showWhenLocked";
private FingerprintUiHelper mFingerprintHelper;
private boolean mAllowFpAuthentication;
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mAllowFpAuthentication = getActivity().getIntent().getBooleanExtra(
ALLOW_FP_AUTHENTICATION, false);
}
@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
mFingerprintHelper = new FingerprintUiHelper(
(ImageView) view.findViewById(R.id.fingerprintIcon),
(TextView) view.findViewById(R.id.errorText), this);
boolean showCancelButton = getActivity().getIntent().getBooleanExtra(
SHOW_CANCEL_BUTTON, false);
Button cancelButton = (Button) view.findViewById(R.id.cancelButton);
cancelButton.setVisibility(showCancelButton ? View.VISIBLE : View.GONE);
cancelButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
getActivity().finish();
}
});
}
@Override
public void onResume() {
super.onResume();
if (mAllowFpAuthentication) {
mFingerprintHelper.startListening();
}
}
@Override
public void onPause() {
super.onPause();
if (mAllowFpAuthentication) {
mFingerprintHelper.stopListening();
}
}
@Override
public void onAuthenticated() {
// Check whether we are still active.
if (getActivity() != null && getActivity().isResumed()) {
authenticationSucceeded(null /* password */);
}
}
protected abstract void authenticationSucceeded(@Nullable String password);
@Override
public void onFingerprintIconVisibilityChanged(boolean visible) {
}
}

View File

@@ -16,14 +16,11 @@
package com.android.settings;
import android.annotation.Nullable;
import android.text.TextUtils;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.widget.LockPatternUtils;
import com.android.internal.widget.PasswordEntryKeyboardHelper;
import com.android.internal.widget.PasswordEntryKeyboardView;
import android.app.Activity;
import android.app.Fragment;
import android.app.admin.DevicePolicyManager;
import android.content.Intent;
import android.os.Bundle;
@@ -31,23 +28,18 @@ import android.os.CountDownTimer;
import android.os.Handler;
import android.os.SystemClock;
import android.os.storage.StorageManager;
import android.text.Editable;
import android.text.InputType;
import android.text.TextWatcher;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.view.inputmethod.EditorInfo;
import android.widget.Button;
import android.widget.TextView;
import android.widget.TextView.OnEditorActionListener;
public class ConfirmLockPassword extends SettingsActivity {
public static final String PACKAGE = "com.android.settings";
public static final String HEADER_TEXT = PACKAGE + ".ConfirmLockPattern.header";
public class ConfirmLockPassword extends ConfirmDeviceCredentialBaseActivity {
public static class InternalActivity extends ConfirmLockPassword {
}
@@ -65,28 +57,17 @@ public class ConfirmLockPassword extends SettingsActivity {
return false;
}
@Override
public void onCreate(Bundle savedInstanceState) {
// Disable IME on our window since we provide our own keyboard
//getWindow().setFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM,
//WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM);
super.onCreate(savedInstanceState);
CharSequence msg = getText(R.string.lockpassword_confirm_your_password_header);
setTitle(msg);
}
public static class ConfirmLockPasswordFragment extends InstrumentedFragment
implements OnClickListener, OnEditorActionListener, TextWatcher {
public static class ConfirmLockPasswordFragment extends ConfirmDeviceCredentialBaseFragment
implements OnClickListener, OnEditorActionListener {
private static final String KEY_NUM_WRONG_CONFIRM_ATTEMPTS
= "confirm_lock_password_fragment.key_num_wrong_confirm_attempts";
private static final long ERROR_MESSAGE_TIMEOUT = 3000;
private TextView mPasswordEntry;
private LockPatternUtils mLockPatternUtils;
private TextView mHeaderText;
private TextView mHeaderTextView;
private TextView mDetailsTextView;
private TextView mErrorTextView;
private Handler mHandler = new Handler();
private PasswordEntryKeyboardHelper mKeyboardHelper;
private PasswordEntryKeyboardView mKeyboardView;
private Button mContinueButton;
private int mNumWrongConfirmAttempts;
private CountDownTimer mCountdownTimer;
private boolean mIsAlpha;
@@ -111,51 +92,35 @@ public class ConfirmLockPassword extends SettingsActivity {
Bundle savedInstanceState) {
final int storedQuality = mLockPatternUtils.getKeyguardStoredPasswordQuality();
View view = inflater.inflate(R.layout.confirm_lock_password, null);
// Disable IME on our window since we provide our own keyboard
view.findViewById(R.id.cancel_button).setOnClickListener(this);
mContinueButton = (Button) view.findViewById(R.id.next_button);
mContinueButton.setOnClickListener(this);
mContinueButton.setEnabled(false); // disable until the user enters at least one char
mPasswordEntry = (TextView) view.findViewById(R.id.password_entry);
mPasswordEntry.setOnEditorActionListener(this);
mPasswordEntry.addTextChangedListener(this);
mKeyboardView = (PasswordEntryKeyboardView) view.findViewById(R.id.keyboard);
mHeaderText = (TextView) view.findViewById(R.id.headerText);
mHeaderTextView = (TextView) view.findViewById(R.id.headerText);
mDetailsTextView = (TextView) view.findViewById(R.id.detailsText);
mErrorTextView = (TextView) view.findViewById(R.id.errorText);
mIsAlpha = DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC == storedQuality
|| DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC == storedQuality
|| DevicePolicyManager.PASSWORD_QUALITY_COMPLEX == storedQuality;
Intent intent = getActivity().getIntent();
if (intent != null) {
CharSequence headerMessage = intent.getCharSequenceExtra(HEADER_TEXT);
CharSequence headerMessage = intent.getCharSequenceExtra(
ConfirmDeviceCredentialBaseFragment.HEADER_TEXT);
CharSequence detailsMessage = intent.getCharSequenceExtra(
ConfirmDeviceCredentialBaseFragment.DETAILS_TEXT);
if (TextUtils.isEmpty(headerMessage)) {
headerMessage = getString(getDefaultHeader());
}
mHeaderText.setText(headerMessage);
if (TextUtils.isEmpty(detailsMessage)) {
detailsMessage = getString(getDefaultDetails());
}
mHeaderTextView.setText(headerMessage);
mDetailsTextView.setText(detailsMessage);
}
final Activity activity = getActivity();
mKeyboardHelper = new PasswordEntryKeyboardHelper(activity,
mKeyboardView, mPasswordEntry);
mKeyboardHelper.setKeyboardMode(mIsAlpha ?
PasswordEntryKeyboardHelper.KEYBOARD_MODE_ALPHA
: PasswordEntryKeyboardHelper.KEYBOARD_MODE_NUMERIC);
mKeyboardView.requestFocus();
int currentType = mPasswordEntry.getInputType();
mPasswordEntry.setInputType(mIsAlpha ? currentType
: (InputType.TYPE_CLASS_NUMBER | InputType.TYPE_NUMBER_VARIATION_PASSWORD));
if (activity instanceof SettingsActivity) {
final SettingsActivity sa = (SettingsActivity) activity;
int id = getDefaultHeader();
CharSequence title = getText(id);
sa.setTitle(title);
}
return view;
}
@@ -164,10 +129,19 @@ public class ConfirmLockPassword extends SettingsActivity {
: R.string.lockpassword_confirm_your_pin_header;
}
private int getDefaultDetails() {
return mIsAlpha ? R.string.lockpassword_confirm_your_password_generic
: R.string.lockpassword_confirm_your_pin_generic;
}
private int getErrorMessage() {
return mIsAlpha ? R.string.lockpassword_invalid_password
: R.string.lockpassword_invalid_pin;
}
@Override
public void onPause() {
super.onPause();
mKeyboardView.requestFocus();
if (mCountdownTimer != null) {
mCountdownTimer.cancel();
mCountdownTimer = null;
@@ -181,9 +155,7 @@ public class ConfirmLockPassword extends SettingsActivity {
@Override
public void onResume() {
// TODO Auto-generated method stub
super.onResume();
mKeyboardView.requestFocus();
long deadline = mLockPatternUtils.getLockoutAttemptDeadline();
if (deadline != 0) {
handleAttemptLockout(deadline);
@@ -196,33 +168,35 @@ public class ConfirmLockPassword extends SettingsActivity {
outState.putInt(KEY_NUM_WRONG_CONFIRM_ATTEMPTS, mNumWrongConfirmAttempts);
}
@Override
protected void authenticationSucceeded(@Nullable String password) {
Intent intent = new Intent();
if (getActivity() instanceof ConfirmLockPassword.InternalActivity) {
intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_TYPE,
mIsAlpha ? StorageManager.CRYPT_TYPE_PASSWORD
: StorageManager.CRYPT_TYPE_PIN);
intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD, password);
}
getActivity().setResult(RESULT_OK, intent);
getActivity().finish();
}
private void handleNext() {
final String pin = mPasswordEntry.getText().toString();
if (mLockPatternUtils.checkPassword(pin)) {
Intent intent = new Intent();
if (getActivity() instanceof ConfirmLockPassword.InternalActivity) {
intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_TYPE,
mIsAlpha ? StorageManager.CRYPT_TYPE_PASSWORD
: StorageManager.CRYPT_TYPE_PIN);
intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD, pin);
}
getActivity().setResult(RESULT_OK, intent);
getActivity().finish();
authenticationSucceeded(pin);
} else {
if (++mNumWrongConfirmAttempts >= LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT) {
long deadline = mLockPatternUtils.setLockoutAttemptDeadline();
handleAttemptLockout(deadline);
} else {
showError(R.string.lockpattern_need_to_unlock_wrong);
showError(getErrorMessage());
}
}
}
private void handleAttemptLockout(long elapsedRealtimeDeadline) {
long elapsedRealtime = SystemClock.elapsedRealtime();
showError(R.string.lockpattern_too_many_failed_confirmation_attempts_header, 0);
mPasswordEntry.setEnabled(false);
mCountdownTimer = new CountDownTimer(
elapsedRealtimeDeadline - elapsedRealtime,
@@ -231,15 +205,15 @@ public class ConfirmLockPassword extends SettingsActivity {
@Override
public void onTick(long millisUntilFinished) {
final int secondsCountdown = (int) (millisUntilFinished / 1000);
mHeaderText.setText(getString(
R.string.lockpattern_too_many_failed_confirmation_attempts_footer,
secondsCountdown));
showError(getString(
R.string.lockpattern_too_many_failed_confirmation_attempts,
secondsCountdown), 0);
}
@Override
public void onFinish() {
mPasswordEntry.setEnabled(true);
mHeaderText.setText(getDefaultHeader());
mErrorTextView.setText("");
mNumWrongConfirmAttempts = 0;
}
}.start();
@@ -264,13 +238,13 @@ public class ConfirmLockPassword extends SettingsActivity {
private final Runnable mResetErrorRunnable = new Runnable() {
public void run() {
mHeaderText.setText(getDefaultHeader());
mErrorTextView.setText("");
}
};
private void showError(int msg, long timeout) {
mHeaderText.setText(msg);
mHeaderText.announceForAccessibility(mHeaderText.getText());
private void showError(CharSequence msg, long timeout) {
mErrorTextView.setText(msg);
mErrorTextView.announceForAccessibility(mErrorTextView.getText());
mPasswordEntry.setText(null);
mHandler.removeCallbacks(mResetErrorRunnable);
if (timeout != 0) {
@@ -278,6 +252,10 @@ public class ConfirmLockPassword extends SettingsActivity {
}
}
private void showError(int msg, long timeout) {
showError(getText(msg), timeout);
}
// {@link OnEditorActionListener} methods.
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
// Check if this was the result of hitting the enter or "done" key
@@ -289,16 +267,5 @@ public class ConfirmLockPassword extends SettingsActivity {
}
return false;
}
// {@link TextWatcher} methods.
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
public void afterTextChanged(Editable s) {
mContinueButton.setEnabled(mPasswordEntry.getText().length() > 0);
}
}
}

View File

@@ -22,13 +22,14 @@ import com.android.internal.widget.LockPatternView;
import com.android.internal.widget.LinearLayoutWithDefaultTouchRecepient;
import com.android.internal.widget.LockPatternView.Cell;
import android.annotation.Nullable;
import android.app.Activity;
import android.app.Fragment;
import android.content.Intent;
import android.os.CountDownTimer;
import android.os.SystemClock;
import android.os.Bundle;
import android.os.storage.StorageManager;
import android.view.MenuItem;
import android.widget.TextView;
import android.view.LayoutInflater;
import android.view.View;
@@ -42,36 +43,17 @@ import java.util.List;
* Sets an activity result of {@link Activity#RESULT_OK} when the user
* successfully confirmed their pattern.
*/
public class ConfirmLockPattern extends SettingsActivity {
public class ConfirmLockPattern extends ConfirmDeviceCredentialBaseActivity {
public static class InternalActivity extends ConfirmLockPattern {
}
/**
* Names of {@link CharSequence} fields within the originating {@link Intent}
* that are used to configure the keyguard confirmation view's labeling.
* The view will use the system-defined resource strings for any labels that
* the caller does not supply.
*/
public static final String PACKAGE = "com.android.settings";
public static final String HEADER_TEXT = PACKAGE + ".ConfirmLockPattern.header";
public static final String FOOTER_TEXT = PACKAGE + ".ConfirmLockPattern.footer";
public static final String HEADER_WRONG_TEXT = PACKAGE + ".ConfirmLockPattern.header_wrong";
public static final String FOOTER_WRONG_TEXT = PACKAGE + ".ConfirmLockPattern.footer_wrong";
private enum Stage {
NeedToUnlock,
NeedToUnlockWrong,
LockedOut
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
CharSequence msg = getText(R.string.lockpassword_confirm_your_pattern_header);
setTitle(msg);
}
@Override
public Intent getIntent() {
Intent modIntent = new Intent(super.getIntent());
@@ -85,7 +67,7 @@ public class ConfirmLockPattern extends SettingsActivity {
return false;
}
public static class ConfirmLockPatternFragment extends InstrumentedFragment {
public static class ConfirmLockPatternFragment extends ConfirmDeviceCredentialBaseFragment {
// how long we wait to clear a wrong pattern
private static final int WRONG_PATTERN_CLEAR_TIMEOUT_MS = 2000;
@@ -98,13 +80,14 @@ public class ConfirmLockPattern extends SettingsActivity {
private CountDownTimer mCountdownTimer;
private TextView mHeaderTextView;
private TextView mFooterTextView;
private TextView mDetailsTextView;
private TextView mErrorTextView;
private View mLeftSpacerLandscape;
private View mRightSpacerLandscape;
// caller-supplied text for various prompts
private CharSequence mHeaderText;
private CharSequence mFooterText;
private CharSequence mHeaderWrongText;
private CharSequence mFooterWrongText;
private CharSequence mDetailsText;
// required constructor for fragments
public ConfirmLockPatternFragment() {
@@ -123,7 +106,10 @@ public class ConfirmLockPattern extends SettingsActivity {
View view = inflater.inflate(R.layout.confirm_lock_pattern, null);
mHeaderTextView = (TextView) view.findViewById(R.id.headerText);
mLockPatternView = (LockPatternView) view.findViewById(R.id.lockPattern);
mFooterTextView = (TextView) view.findViewById(R.id.footerText);
mDetailsTextView = (TextView) view.findViewById(R.id.detailsText);
mErrorTextView = (TextView) view.findViewById(R.id.errorText);
mLeftSpacerLandscape = view.findViewById(R.id.leftSpacer);
mRightSpacerLandscape = view.findViewById(R.id.rightSpacer);
// make it so unhandled touch events within the unlock screen go to the
// lock pattern view.
@@ -133,10 +119,10 @@ public class ConfirmLockPattern extends SettingsActivity {
Intent intent = getActivity().getIntent();
if (intent != null) {
mHeaderText = intent.getCharSequenceExtra(HEADER_TEXT);
mFooterText = intent.getCharSequenceExtra(FOOTER_TEXT);
mHeaderWrongText = intent.getCharSequenceExtra(HEADER_WRONG_TEXT);
mFooterWrongText = intent.getCharSequenceExtra(FOOTER_WRONG_TEXT);
mHeaderText = intent.getCharSequenceExtra(
ConfirmDeviceCredentialBaseFragment.HEADER_TEXT);
mDetailsText = intent.getCharSequenceExtra(
ConfirmDeviceCredentialBaseFragment.DETAILS_TEXT);
}
mLockPatternView.setTactileFeedbackEnabled(mLockPatternUtils.isTactileFeedbackEnabled());
@@ -199,28 +185,21 @@ public class ConfirmLockPattern extends SettingsActivity {
if (mHeaderText != null) {
mHeaderTextView.setText(mHeaderText);
} else {
mHeaderTextView.setText(R.string.lockpattern_need_to_unlock);
mHeaderTextView.setText(R.string.lockpassword_confirm_your_pattern_header);
}
if (mFooterText != null) {
mFooterTextView.setText(mFooterText);
if (mDetailsText != null) {
mDetailsTextView.setText(mDetailsText);
} else {
mFooterTextView.setText(R.string.lockpattern_need_to_unlock_footer);
mDetailsTextView.setText(
R.string.lockpassword_confirm_your_pattern_generic);
}
mErrorTextView.setText("");
mLockPatternView.setEnabled(true);
mLockPatternView.enableInput();
break;
case NeedToUnlockWrong:
if (mHeaderWrongText != null) {
mHeaderTextView.setText(mHeaderWrongText);
} else {
mHeaderTextView.setText(R.string.lockpattern_need_to_unlock_wrong);
}
if (mFooterWrongText != null) {
mFooterTextView.setText(mFooterWrongText);
} else {
mFooterTextView.setText(R.string.lockpattern_need_to_unlock_wrong_footer);
}
mErrorTextView.setText(R.string.lockpattern_need_to_unlock_wrong);
mLockPatternView.setDisplayMode(LockPatternView.DisplayMode.Wrong);
mLockPatternView.setEnabled(true);
@@ -252,6 +231,28 @@ public class ConfirmLockPattern extends SettingsActivity {
mLockPatternView.postDelayed(mClearPatternRunnable, WRONG_PATTERN_CLEAR_TIMEOUT_MS);
}
@Override
protected void authenticationSucceeded(@Nullable String password) {
Intent intent = new Intent();
if (getActivity() instanceof ConfirmLockPattern.InternalActivity) {
intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_TYPE,
StorageManager.CRYPT_TYPE_PATTERN);
intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD, password);
}
getActivity().setResult(Activity.RESULT_OK, intent);
getActivity().finish();
}
@Override
public void onFingerprintIconVisibilityChanged(boolean visible) {
if (mLeftSpacerLandscape != null && mRightSpacerLandscape != null) {
// In landscape, adjust spacing depending on fingerprint icon visibility.
mLeftSpacerLandscape.setVisibility(visible ? View.GONE : View.VISIBLE);
mRightSpacerLandscape.setVisibility(visible ? View.GONE : View.VISIBLE);
}
}
/**
* The pattern listener that responds according to a user confirming
* an existing lock pattern.
@@ -273,17 +274,7 @@ public class ConfirmLockPattern extends SettingsActivity {
public void onPatternDetected(List<LockPatternView.Cell> pattern) {
if (mLockPatternUtils.checkPattern(pattern)) {
Intent intent = new Intent();
if (getActivity() instanceof ConfirmLockPattern.InternalActivity) {
intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_TYPE,
StorageManager.CRYPT_TYPE_PATTERN);
intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD,
LockPatternUtils.patternToString(pattern));
}
getActivity().setResult(Activity.RESULT_OK, intent);
getActivity().finish();
authenticationSucceeded(LockPatternUtils.patternToString(pattern));
} else {
if (pattern.size() >= LockPatternUtils.MIN_PATTERN_REGISTER_FAIL &&
++mNumWrongConfirmAttempts
@@ -308,10 +299,9 @@ public class ConfirmLockPattern extends SettingsActivity {
@Override
public void onTick(long millisUntilFinished) {
mHeaderTextView.setText(R.string.lockpattern_too_many_failed_confirmation_attempts_header);
final int secondsCountdown = (int) (millisUntilFinished / 1000);
mFooterTextView.setText(getString(
R.string.lockpattern_too_many_failed_confirmation_attempts_footer,
mErrorTextView.setText(getString(
R.string.lockpattern_too_many_failed_confirmation_attempts,
secondsCountdown));
}

View File

@@ -439,9 +439,8 @@ public final class CredentialStorage extends Activity {
private boolean confirmKeyGuard() {
Resources res = getResources();
boolean launched = new ChooseLockSettingsHelper(this)
.launchConfirmationActivity(CONFIRM_KEY_GUARD_REQUEST, null,
res.getText(R.string.credentials_install_gesture_explanation),
true);
.launchConfirmationActivity(CONFIRM_KEY_GUARD_REQUEST,
res.getText(R.string.credentials_title), true);
return launched;
}

View File

@@ -166,9 +166,8 @@ public class CryptKeeperSettings extends InstrumentedFragment {
return true;
}
return helper.launchConfirmationActivity(request, null,
res.getText(R.string.crypt_keeper_confirm_encrypt),
true);
return helper.launchConfirmationActivity(request,
res.getText(R.string.crypt_keeper_encrypt_title), true);
}
@Override

View File

@@ -1610,10 +1610,8 @@ public class DevelopmentSettings extends SettingsPreferenceFragment
}
private boolean showKeyguardConfirmation(Resources resources, int requestCode) {
return new ChooseLockSettingsHelper(getActivity(), this)
.launchConfirmationActivity(requestCode,
resources.getString(R.string.oem_unlock_enable_pin_prompt),
resources.getString(R.string.oem_unlock_enable_pin_description));
return new ChooseLockSettingsHelper(getActivity(), this).launchConfirmationActivity(
requestCode, resources.getString(R.string.oem_unlock_enable));
}
@Override

View File

@@ -0,0 +1,119 @@
/*
* 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.hardware.fingerprint.FingerprintManager;
import android.os.CancellationSignal;
import android.os.Vibrator;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;
/**
* Small helper class to manage text/icon around fingerprint authentication UI.
*/
public class FingerprintUiHelper extends FingerprintManager.AuthenticationCallback {
private static final long ERROR_TIMEOUT = 1300;
private static final long[] FP_ERROR_VIBRATE_PATTERN = new long[] {0, 30, 100, 30};
private static final long[] FP_SUCCESS_VIBRATE_PATTERN = new long[] {0, 30};
private ImageView mIcon;
private TextView mErrorTextView;
private CancellationSignal mCancellationSignal;
private Callback mCallback;
private FingerprintManager mFingerprintManager;
public FingerprintUiHelper(ImageView icon, TextView errorTextView, Callback callback) {
mFingerprintManager = icon.getContext().getSystemService(FingerprintManager.class);
mIcon = icon;
mErrorTextView = errorTextView;
mCallback = callback;
}
public void startListening() {
if (mFingerprintManager.getEnrolledFingerprints().size() > 0) {
mCancellationSignal = new CancellationSignal();
mFingerprintManager.authenticate(null, mCancellationSignal, this, 0 /* flags */);
setFingerprintIconVisibility(true);
mIcon.setImageResource(R.drawable.ic_fingerprint);
}
}
public void stopListening() {
mCancellationSignal.cancel();
mCancellationSignal = null;
}
private void setFingerprintIconVisibility(boolean visible) {
mIcon.setVisibility(visible ? View.VISIBLE : View.GONE);
mCallback.onFingerprintIconVisibilityChanged(visible);
}
@Override
public void onAuthenticationError(int errMsgId, CharSequence errString) {
showError(errString);
setFingerprintIconVisibility(false);
}
@Override
public void onAuthenticationHelp(int helpMsgId, CharSequence helpString) {
showError(helpString);
}
@Override
public void onAuthenticationFailed() {
showError(mIcon.getResources().getString(
R.string.fingerprint_not_recognized));
}
@Override
public void onAuthenticationSucceeded(FingerprintManager.AuthenticationResult result) {
vibrateFingerprintSuccess();
mCallback.onAuthenticated();
}
private void showError(CharSequence error) {
vibrateFingerprintError();
mIcon.setImageResource(R.drawable.ic_fingerprint_error);
mErrorTextView.setText(error);
mErrorTextView.removeCallbacks(mResetErrorTextRunnable);
mErrorTextView.postDelayed(mResetErrorTextRunnable, ERROR_TIMEOUT);
}
private void vibrateFingerprintError() {
mIcon.getContext().getSystemService(Vibrator.class).vibrate(FP_ERROR_VIBRATE_PATTERN, -1);
}
private void vibrateFingerprintSuccess() {
mIcon.getContext().getSystemService(Vibrator.class).vibrate(FP_SUCCESS_VIBRATE_PATTERN, -1);
}
private Runnable mResetErrorTextRunnable = new Runnable() {
@Override
public void run() {
mErrorTextView.setText("");
mIcon.setImageResource(R.drawable.ic_fingerprint);
}
};
public interface Callback {
void onAuthenticated();
void onFingerprintIconVisibilityChanged(boolean visible);
}
}

View File

@@ -75,8 +75,8 @@ public class MasterClear extends InstrumentedFragment {
*/
private boolean runKeyguardConfirmation(int request) {
Resources res = getActivity().getResources();
return new ChooseLockSettingsHelper(getActivity(), this).launchConfirmationActivity(request,
null, res.getText(R.string.master_clear_gesture_explanation));
return new ChooseLockSettingsHelper(getActivity(), this).launchConfirmationActivity(
request, res.getText(R.string.master_clear_title));
}
@Override

View File

@@ -72,9 +72,8 @@ public class MediaFormat extends Activity {
* component as a subactivity
*/
private boolean runKeyguardConfirmation(int request) {
return new ChooseLockSettingsHelper(this)
.launchConfirmationActivity(request, null,
getText(R.string.media_format_gesture_explanation));
return new ChooseLockSettingsHelper(this).launchConfirmationActivity(request,
getText(R.string.media_format_title));
}
@Override

View File

@@ -78,8 +78,8 @@ public class ResetNetwork extends InstrumentedFragment {
*/
private boolean runKeyguardConfirmation(int request) {
Resources res = getActivity().getResources();
return new ChooseLockSettingsHelper(getActivity(), this).launchConfirmationActivity(request,
null, res.getText(R.string.reset_network_gesture_explanation));
return new ChooseLockSettingsHelper(getActivity(), this).launchConfirmationActivity(
request, res.getText(R.string.reset_network_title));
}
@Override

View File

@@ -603,8 +603,9 @@ public class SecuritySettings extends SettingsPreferenceFragment
ChooseLockSettingsHelper helper =
new ChooseLockSettingsHelper(this.getActivity(), this);
mTrustAgentClickIntent = preference.getIntent();
if (!helper.launchConfirmationActivity(CHANGE_TRUST_AGENT_SETTINGS, null, null) &&
mTrustAgentClickIntent != null) {
boolean confirmationLaunched = helper.launchConfirmationActivity(
CHANGE_TRUST_AGENT_SETTINGS, preference.getTitle());
if (!confirmationLaunched&& mTrustAgentClickIntent != null) {
// If this returns false, it means no password confirmation is required.
startActivity(mTrustAgentClickIntent);
mTrustAgentClickIntent = null;