diff options
Diffstat (limited to 'packages/Keyguard/src/com/android/keyguard/KeyguardAccountView.java')
-rw-r--r-- | packages/Keyguard/src/com/android/keyguard/KeyguardAccountView.java | 333 |
1 files changed, 333 insertions, 0 deletions
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardAccountView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardAccountView.java new file mode 100644 index 0000000..e0e7128 --- /dev/null +++ b/packages/Keyguard/src/com/android/keyguard/KeyguardAccountView.java @@ -0,0 +1,333 @@ +/* + * Copyright (C) 2012 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.internal.policy.impl.keyguard; + +import android.accounts.Account; +import android.accounts.AccountManager; +import android.accounts.AccountManagerCallback; +import android.accounts.AccountManagerFuture; +import android.accounts.AuthenticatorException; +import android.accounts.OperationCanceledException; +import android.app.Dialog; +import android.app.ProgressDialog; +import android.content.Context; +import android.content.Intent; +import android.graphics.Rect; +import android.os.Bundle; +import android.os.UserHandle; +import android.text.Editable; +import android.text.InputFilter; +import android.text.LoginFilter; +import android.text.TextWatcher; +import android.util.AttributeSet; +import android.view.KeyEvent; +import android.view.View; +import android.view.WindowManager; +import android.widget.Button; +import android.widget.EditText; +import android.widget.LinearLayout; + +import com.android.internal.widget.LockPatternUtils; +import com.android.internal.R; + +import java.io.IOException; + +/** + * When the user forgets their password a bunch of times, we fall back on their + * account's login/password to unlock the phone (and reset their lock pattern). + */ +public class KeyguardAccountView extends LinearLayout implements KeyguardSecurityView, + View.OnClickListener, TextWatcher { + private static final int AWAKE_POKE_MILLIS = 30000; + private static final String LOCK_PATTERN_PACKAGE = "com.android.settings"; + private static final String LOCK_PATTERN_CLASS = LOCK_PATTERN_PACKAGE + ".ChooseLockGeneric"; + + private KeyguardSecurityCallback mCallback; + private LockPatternUtils mLockPatternUtils; + private EditText mLogin; + private EditText mPassword; + private Button mOk; + public boolean mEnableFallback; + private SecurityMessageDisplay mSecurityMessageDisplay; + + /** + * Shown while making asynchronous check of password. + */ + private ProgressDialog mCheckingDialog; + + public KeyguardAccountView(Context context) { + this(context, null, 0); + } + + public KeyguardAccountView(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public KeyguardAccountView(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + mLockPatternUtils = new LockPatternUtils(getContext()); + } + + @Override + protected void onFinishInflate() { + super.onFinishInflate(); + + mLogin = (EditText) findViewById(R.id.login); + mLogin.setFilters(new InputFilter[] { new LoginFilter.UsernameFilterGeneric() } ); + mLogin.addTextChangedListener(this); + + mPassword = (EditText) findViewById(R.id.password); + mPassword.addTextChangedListener(this); + + mOk = (Button) findViewById(R.id.ok); + mOk.setOnClickListener(this); + + mSecurityMessageDisplay = new KeyguardMessageArea.Helper(this); + reset(); + } + + public void setKeyguardCallback(KeyguardSecurityCallback callback) { + mCallback = callback; + } + + public void setLockPatternUtils(LockPatternUtils utils) { + mLockPatternUtils = utils; + } + + public KeyguardSecurityCallback getCallback() { + return mCallback; + } + + + public void afterTextChanged(Editable s) { + } + + public void beforeTextChanged(CharSequence s, int start, int count, int after) { + } + + public void onTextChanged(CharSequence s, int start, int before, int count) { + if (mCallback != null) { + mCallback.userActivity(AWAKE_POKE_MILLIS); + } + } + + @Override + protected boolean onRequestFocusInDescendants(int direction, + Rect previouslyFocusedRect) { + // send focus to the login field + return mLogin.requestFocus(direction, previouslyFocusedRect); + } + + public boolean needsInput() { + return true; + } + + public void reset() { + // start fresh + mLogin.setText(""); + mPassword.setText(""); + mLogin.requestFocus(); + boolean permLocked = mLockPatternUtils.isPermanentlyLocked(); + mSecurityMessageDisplay.setMessage(permLocked ? R.string.kg_login_too_many_attempts : + R.string.kg_login_instructions, permLocked ? true : false); + } + + /** {@inheritDoc} */ + public void cleanUp() { + if (mCheckingDialog != null) { + mCheckingDialog.hide(); + } + mCallback = null; + mLockPatternUtils = null; + } + + public void onClick(View v) { + mCallback.userActivity(0); + if (v == mOk) { + asyncCheckPassword(); + } + } + + private void postOnCheckPasswordResult(final boolean success) { + // ensure this runs on UI thread + mLogin.post(new Runnable() { + public void run() { + if (success) { + // clear out forgotten password + mLockPatternUtils.setPermanentlyLocked(false); + mLockPatternUtils.setLockPatternEnabled(false); + mLockPatternUtils.saveLockPattern(null); + + // launch the 'choose lock pattern' activity so + // the user can pick a new one if they want to + Intent intent = new Intent(); + intent.setClassName(LOCK_PATTERN_PACKAGE, LOCK_PATTERN_CLASS); + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + mContext.startActivityAsUser(intent, + new UserHandle(mLockPatternUtils.getCurrentUser())); + mCallback.reportSuccessfulUnlockAttempt(); + + // dismiss keyguard + mCallback.dismiss(true); + } else { + mSecurityMessageDisplay.setMessage(R.string.kg_login_invalid_input, true); + mPassword.setText(""); + mCallback.reportFailedUnlockAttempt(); + } + } + }); + } + + @Override + public boolean dispatchKeyEvent(KeyEvent event) { + if (event.getAction() == KeyEvent.ACTION_DOWN + && event.getKeyCode() == KeyEvent.KEYCODE_BACK) { + if (mLockPatternUtils.isPermanentlyLocked()) { + mCallback.dismiss(false); + } else { + // TODO: mCallback.forgotPattern(false); + } + return true; + } + return super.dispatchKeyEvent(event); + } + + /** + * Given the string the user entered in the 'username' field, find + * the stored account that they probably intended. Prefer, in order: + * + * - an exact match for what was typed, or + * - a case-insensitive match for what was typed, or + * - if they didn't include a domain, an exact match of the username, or + * - if they didn't include a domain, a case-insensitive + * match of the username. + * + * If there is a tie for the best match, choose neither -- + * the user needs to be more specific. + * + * @return an account name from the database, or null if we can't + * find a single best match. + */ + private Account findIntendedAccount(String username) { + Account[] accounts = AccountManager.get(mContext).getAccountsByTypeAsUser("com.google", + new UserHandle(mLockPatternUtils.getCurrentUser())); + + // Try to figure out which account they meant if they + // typed only the username (and not the domain), or got + // the case wrong. + + Account bestAccount = null; + int bestScore = 0; + for (Account a: accounts) { + int score = 0; + if (username.equals(a.name)) { + score = 4; + } else if (username.equalsIgnoreCase(a.name)) { + score = 3; + } else if (username.indexOf('@') < 0) { + int i = a.name.indexOf('@'); + if (i >= 0) { + String aUsername = a.name.substring(0, i); + if (username.equals(aUsername)) { + score = 2; + } else if (username.equalsIgnoreCase(aUsername)) { + score = 1; + } + } + } + if (score > bestScore) { + bestAccount = a; + bestScore = score; + } else if (score == bestScore) { + bestAccount = null; + } + } + return bestAccount; + } + + private void asyncCheckPassword() { + mCallback.userActivity(AWAKE_POKE_MILLIS); + final String login = mLogin.getText().toString(); + final String password = mPassword.getText().toString(); + Account account = findIntendedAccount(login); + if (account == null) { + postOnCheckPasswordResult(false); + return; + } + getProgressDialog().show(); + Bundle options = new Bundle(); + options.putString(AccountManager.KEY_PASSWORD, password); + AccountManager.get(mContext).confirmCredentialsAsUser(account, options, null /* activity */, + new AccountManagerCallback<Bundle>() { + public void run(AccountManagerFuture<Bundle> future) { + try { + mCallback.userActivity(AWAKE_POKE_MILLIS); + final Bundle result = future.getResult(); + final boolean verified = result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT); + postOnCheckPasswordResult(verified); + } catch (OperationCanceledException e) { + postOnCheckPasswordResult(false); + } catch (IOException e) { + postOnCheckPasswordResult(false); + } catch (AuthenticatorException e) { + postOnCheckPasswordResult(false); + } finally { + mLogin.post(new Runnable() { + public void run() { + getProgressDialog().hide(); + } + }); + } + } + }, null /* handler */, new UserHandle(mLockPatternUtils.getCurrentUser())); + } + + private Dialog getProgressDialog() { + if (mCheckingDialog == null) { + mCheckingDialog = new ProgressDialog(mContext); + mCheckingDialog.setMessage( + mContext.getString(R.string.kg_login_checking_password)); + mCheckingDialog.setIndeterminate(true); + mCheckingDialog.setCancelable(false); + mCheckingDialog.getWindow().setType( + WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG); + } + return mCheckingDialog; + } + + @Override + public void onPause() { + + } + + @Override + public void onResume(int reason) { + reset(); + } + + @Override + public void showUsabilityHint() { + } + + @Override + public void showBouncer(int duration) { + } + + @Override + public void hideBouncer(int duration) { + } +} + |