diff options
author | Jorim Jaggi <jjaggi@google.com> | 2015-04-06 17:47:18 -0700 |
---|---|---|
committer | Jorim Jaggi <jjaggi@google.com> | 2015-04-13 14:59:26 -0700 |
commit | 8a09b619aeb233e2aab1919301f162d8acf1f0f0 (patch) | |
tree | aeec574a47f0f71ab9177b2e4ef7f912e87f61b6 /src/com | |
parent | b5aa73f46f812ba03518a6d1ac218e3af5975236 (diff) | |
download | packages_apps_Settings-8a09b619aeb233e2aab1919301f162d8acf1f0f0.zip packages_apps_Settings-8a09b619aeb233e2aab1919301f162d8acf1f0f0.tar.gz packages_apps_Settings-8a09b619aeb233e2aab1919301f162d8acf1f0f0.tar.bz2 |
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
Diffstat (limited to 'src/com')
17 files changed, 453 insertions, 235 deletions
diff --git a/src/com/android/settings/ChooseLockGeneric.java b/src/com/android/settings/ChooseLockGeneric.java index 479baf7..f4e3c4e 100644 --- a/src/com/android/settings/ChooseLockGeneric.java +++ b/src/com/android/settings/ChooseLockGeneric.java @@ -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 { diff --git a/src/com/android/settings/ChooseLockPassword.java b/src/com/android/settings/ChooseLockPassword.java index 6d7b71d..8838286 100644 --- a/src/com/android/settings/ChooseLockPassword.java +++ b/src/com/android/settings/ChooseLockPassword.java @@ -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); diff --git a/src/com/android/settings/ChooseLockPattern.java b/src/com/android/settings/ChooseLockPattern.java index 1865a6b..8892346 100644 --- a/src/com/android/settings/ChooseLockPattern.java +++ b/src/com/android/settings/ChooseLockPattern.java @@ -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); } diff --git a/src/com/android/settings/ChooseLockSettingsHelper.java b/src/com/android/settings/ChooseLockSettingsHelper.java index bba45ce..65697f6 100644 --- a/src/com/android/settings/ChooseLockSettingsHelper.java +++ b/src/com/android/settings/ChooseLockSettingsHelper.java @@ -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 Activity#onActivityResult(int, int, android.content.Intent) + */ + 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 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. * @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, 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 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 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. + * 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 #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, @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; - } - - } diff --git a/src/com/android/settings/ConfirmDeviceCredentialActivity.java b/src/com/android/settings/ConfirmDeviceCredentialActivity.java index beb2d97..9a7f843 100644 --- a/src/com/android/settings/ConfirmDeviceCredentialActivity.java +++ b/src/com/android/settings/ConfirmDeviceCredentialActivity.java @@ -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(); diff --git a/src/com/android/settings/ConfirmDeviceCredentialBaseActivity.java b/src/com/android/settings/ConfirmDeviceCredentialBaseActivity.java new file mode 100644 index 0000000..fe3de97 --- /dev/null +++ b/src/com/android/settings/ConfirmDeviceCredentialBaseActivity.java @@ -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); + } +} diff --git a/src/com/android/settings/ConfirmDeviceCredentialBaseFragment.java b/src/com/android/settings/ConfirmDeviceCredentialBaseFragment.java new file mode 100644 index 0000000..f9908b0 --- /dev/null +++ b/src/com/android/settings/ConfirmDeviceCredentialBaseFragment.java @@ -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) { + } +} diff --git a/src/com/android/settings/ConfirmLockPassword.java b/src/com/android/settings/ConfirmLockPassword.java index b49dc6e..44c599b 100644 --- a/src/com/android/settings/ConfirmLockPassword.java +++ b/src/com/android/settings/ConfirmLockPassword.java @@ -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); - } } } diff --git a/src/com/android/settings/ConfirmLockPattern.java b/src/com/android/settings/ConfirmLockPattern.java index b5d444b..8c4fabc 100644 --- a/src/com/android/settings/ConfirmLockPattern.java +++ b/src/com/android/settings/ConfirmLockPattern.java @@ -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,23 +43,11 @@ 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, @@ -66,13 +55,6 @@ public class ConfirmLockPattern extends SettingsActivity { } @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()); modIntent.putExtra(EXTRA_SHOW_FRAGMENT, ConfirmLockPatternFragment.class.getName()); @@ -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)); } diff --git a/src/com/android/settings/CredentialStorage.java b/src/com/android/settings/CredentialStorage.java index 45d3d11..8506964 100644 --- a/src/com/android/settings/CredentialStorage.java +++ b/src/com/android/settings/CredentialStorage.java @@ -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; } diff --git a/src/com/android/settings/CryptKeeperSettings.java b/src/com/android/settings/CryptKeeperSettings.java index 3fb60e3..2e4aeb8 100644 --- a/src/com/android/settings/CryptKeeperSettings.java +++ b/src/com/android/settings/CryptKeeperSettings.java @@ -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 diff --git a/src/com/android/settings/DevelopmentSettings.java b/src/com/android/settings/DevelopmentSettings.java index 5fafc92..5c7e0e9 100644 --- a/src/com/android/settings/DevelopmentSettings.java +++ b/src/com/android/settings/DevelopmentSettings.java @@ -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 diff --git a/src/com/android/settings/FingerprintUiHelper.java b/src/com/android/settings/FingerprintUiHelper.java new file mode 100644 index 0000000..20ad7fc --- /dev/null +++ b/src/com/android/settings/FingerprintUiHelper.java @@ -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); + } +} diff --git a/src/com/android/settings/MasterClear.java b/src/com/android/settings/MasterClear.java index 1b6468f..23f6812 100644 --- a/src/com/android/settings/MasterClear.java +++ b/src/com/android/settings/MasterClear.java @@ -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 diff --git a/src/com/android/settings/MediaFormat.java b/src/com/android/settings/MediaFormat.java index 20dac8e..517ec2d 100644 --- a/src/com/android/settings/MediaFormat.java +++ b/src/com/android/settings/MediaFormat.java @@ -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 diff --git a/src/com/android/settings/ResetNetwork.java b/src/com/android/settings/ResetNetwork.java index a3376d3..587b8e8 100644 --- a/src/com/android/settings/ResetNetwork.java +++ b/src/com/android/settings/ResetNetwork.java @@ -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 diff --git a/src/com/android/settings/SecuritySettings.java b/src/com/android/settings/SecuritySettings.java index c74c90a..fd99536 100644 --- a/src/com/android/settings/SecuritySettings.java +++ b/src/com/android/settings/SecuritySettings.java @@ -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; |