diff options
Diffstat (limited to 'src/com/android/settings/cyanogenmod/ProtectedAccountView.java')
-rw-r--r-- | src/com/android/settings/cyanogenmod/ProtectedAccountView.java | 281 |
1 files changed, 281 insertions, 0 deletions
diff --git a/src/com/android/settings/cyanogenmod/ProtectedAccountView.java b/src/com/android/settings/cyanogenmod/ProtectedAccountView.java new file mode 100644 index 0000000..4b9e14e --- /dev/null +++ b/src/com/android/settings/cyanogenmod/ProtectedAccountView.java @@ -0,0 +1,281 @@ +/* + * Copyright (C) 2014 The CyanogenMod 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.cyanogenmod; + +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.Activity; +import android.app.ActivityManager; +import android.app.Dialog; +import android.app.ProgressDialog; +import android.content.Context; +import android.content.SharedPreferences; +import android.graphics.Rect; +import android.os.Bundle; +import android.os.UserHandle; +import android.preference.PreferenceManager; +import android.text.InputFilter; +import android.text.LoginFilter; +import android.util.AttributeSet; +import android.view.View; +import android.view.WindowManager; +import android.view.inputmethod.InputMethodManager; +import android.widget.Button; +import android.widget.EditText; +import android.widget.LinearLayout; +import android.widget.Toast; +import com.android.internal.widget.LockPatternUtils; +import com.android.settings.R; +import com.android.settings.applications.LockPatternActivity; + +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 protected apps (and reset their lock pattern). + */ +public class ProtectedAccountView extends LinearLayout implements View.OnClickListener { + + public static interface OnNotifyAccountReset { + void onNotifyAccountReset(); + } + + private EditText mLogin; + private EditText mPassword; + private Button mOk; + private Context mContext; + private LockPatternUtils mLockPatternUtils; + private OnNotifyAccountReset mNotifyAccountResetCb; + + /** + * Shown while making asynchronous check of password. + */ + private ProgressDialog mCheckingDialog; + + public ProtectedAccountView(Context context) { + this(context, null); + } + + public ProtectedAccountView(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public ProtectedAccountView(Context context, AttributeSet st, int ds) { + super(context, st, ds); + mContext = context; + mLockPatternUtils = new LockPatternUtils(mContext); + } + + @Override + protected void onFinishInflate() { + super.onFinishInflate(); + + mLogin = (EditText) findViewById(R.id.login); + mLogin.setFilters(new InputFilter[] { new LoginFilter.UsernameFilterGeneric() } ); + mPassword = (EditText) findViewById(R.id.password); + + mOk = (Button) findViewById(R.id.ok); + mOk.setOnClickListener(this); + + reset(); + } + + @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 setOnNotifyAccountResetCb(OnNotifyAccountReset callback) { + this.mNotifyAccountResetCb = callback; + } + + public void clearFocusOnInput() { + mLogin.clearFocus(); + mPassword.clearFocus(); + + // hide keyboard + final InputMethodManager imm = (InputMethodManager) + mContext.getSystemService(Context.INPUT_METHOD_SERVICE); + imm.hideSoftInputFromWindow(mLogin.getWindowToken(), 0); + imm.hideSoftInputFromWindow(mPassword.getWindowToken(), 0); + } + + public void reset() { + mLogin.setText(""); + mPassword.setText(""); + mLogin.requestFocus(); + } + + /** {@inheritDoc} */ + public void cleanUp() { + if (mCheckingDialog != null) { + mCheckingDialog.hide(); + } + } + + public void onClick(View v) { + 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) { + + Activity baseActivity = (Activity) mContext; + + if (!baseActivity.isFinishing()) { + // Remove pattern + SharedPreferences prefs = PreferenceManager + .getDefaultSharedPreferences(mContext); + SharedPreferences.Editor editor = prefs.edit(); + editor.remove(LockPatternActivity.PATTERN_LOCK_PROTECTED_APPS); + editor.commit(); + + if (mNotifyAccountResetCb != null) { + mNotifyAccountResetCb.onNotifyAccountReset(); + } else { + baseActivity.setResult(Activity.RESULT_OK); + baseActivity.finish(); + } + } + } else { + Toast.makeText(mContext, + getResources().getString( + R.string.pa_login_incorrect_login), + Toast.LENGTH_SHORT).show(); + mPassword.setText(""); + } + } + }); + } + + /** + * 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(ActivityManager.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() { + 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 { + 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(ActivityManager.getCurrentUser())); + } + + private Dialog getProgressDialog() { + if (mCheckingDialog == null) { + mCheckingDialog = new ProgressDialog(mContext); + mCheckingDialog.setMessage( + mContext.getString(R.string.pa_login_checking_password)); + mCheckingDialog.setIndeterminate(true); + mCheckingDialog.setCancelable(false); + mCheckingDialog.getWindow().setType( + WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG); + } + return mCheckingDialog; + } +} + |