| /* |
| * Copyright (C) 2008 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; |
| |
| import com.android.internal.R; |
| import com.android.internal.widget.LockPatternUtils; |
| |
| import android.accounts.Account; |
| import android.accounts.AccountManager; |
| import android.accounts.OperationCanceledException; |
| import android.accounts.AccountManagerFuture; |
| import android.accounts.AuthenticatorException; |
| import android.accounts.AccountManagerCallback; |
| import android.content.Context; |
| import android.content.Intent; |
| import android.content.res.Configuration; |
| import android.graphics.Rect; |
| import android.text.Editable; |
| import android.text.InputFilter; |
| import android.text.LoginFilter; |
| import android.text.TextWatcher; |
| import android.view.KeyEvent; |
| import android.view.LayoutInflater; |
| import android.view.View; |
| import android.view.WindowManager; |
| import android.widget.Button; |
| import android.widget.EditText; |
| import android.widget.RelativeLayout; |
| import android.widget.TextView; |
| import android.app.Dialog; |
| import android.app.ProgressDialog; |
| import android.os.Bundle; |
| |
| 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 AccountUnlockScreen extends RelativeLayout implements KeyguardScreen, |
| KeyguardUpdateMonitor.InfoCallback,View.OnClickListener, TextWatcher { |
| private static final String LOCK_PATTERN_PACKAGE = "com.android.settings"; |
| private static final String LOCK_PATTERN_CLASS = |
| "com.android.settings.ChooseLockPattern"; |
| |
| /** |
| * The amount of millis to stay awake once this screen detects activity |
| */ |
| private static final int AWAKE_POKE_MILLIS = 30000; |
| |
| private KeyguardScreenCallback mCallback; |
| private LockPatternUtils mLockPatternUtils; |
| private KeyguardUpdateMonitor mUpdateMonitor; |
| |
| private TextView mTopHeader; |
| private TextView mInstructions; |
| private EditText mLogin; |
| private EditText mPassword; |
| private Button mOk; |
| private Button mEmergencyCall; |
| |
| /** |
| * Shown while making asynchronous check of password. |
| */ |
| private ProgressDialog mCheckingDialog; |
| |
| /** |
| * AccountUnlockScreen constructor. |
| * @param configuration |
| * @param updateMonitor |
| */ |
| public AccountUnlockScreen(Context context,Configuration configuration, |
| KeyguardUpdateMonitor updateMonitor, KeyguardScreenCallback callback, |
| LockPatternUtils lockPatternUtils) { |
| super(context); |
| mCallback = callback; |
| mLockPatternUtils = lockPatternUtils; |
| |
| LayoutInflater.from(context).inflate( |
| R.layout.keyguard_screen_glogin_unlock, this, true); |
| |
| mTopHeader = (TextView) findViewById(R.id.topHeader); |
| mTopHeader.setText(mLockPatternUtils.isPermanentlyLocked() ? |
| R.string.lockscreen_glogin_too_many_attempts : |
| R.string.lockscreen_glogin_forgot_pattern); |
| |
| mInstructions = (TextView) findViewById(R.id.instructions); |
| |
| 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); |
| |
| mEmergencyCall = (Button) findViewById(R.id.emergencyCall); |
| mEmergencyCall.setOnClickListener(this); |
| mLockPatternUtils.updateEmergencyCallButtonState(mEmergencyCall); |
| |
| mUpdateMonitor = updateMonitor; |
| mUpdateMonitor.registerInfoCallback(this); |
| } |
| |
| 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) { |
| mCallback.pokeWakelock(AWAKE_POKE_MILLIS); |
| } |
| |
| @Override |
| protected boolean onRequestFocusInDescendants(int direction, |
| Rect previouslyFocusedRect) { |
| // send focus to the login field |
| return mLogin.requestFocus(direction, previouslyFocusedRect); |
| } |
| |
| /** {@inheritDoc} */ |
| public boolean needsInput() { |
| return true; |
| } |
| |
| /** {@inheritDoc} */ |
| public void onPause() { |
| |
| } |
| |
| /** {@inheritDoc} */ |
| public void onResume() { |
| // start fresh |
| mLogin.setText(""); |
| mPassword.setText(""); |
| mLogin.requestFocus(); |
| mLockPatternUtils.updateEmergencyCallButtonState(mEmergencyCall); |
| } |
| |
| /** {@inheritDoc} */ |
| public void cleanUp() { |
| if (mCheckingDialog != null) { |
| mCheckingDialog.hide(); |
| } |
| mUpdateMonitor.removeCallback(this); // this must be first |
| mCallback = null; |
| mLockPatternUtils = null; |
| mUpdateMonitor = null; |
| } |
| |
| /** {@inheritDoc} */ |
| public void onClick(View v) { |
| mCallback.pokeWakelock(); |
| if (v == mOk) { |
| asyncCheckPassword(); |
| } |
| |
| if (v == mEmergencyCall) { |
| mCallback.takeEmergencyCallAction(); |
| } |
| } |
| |
| 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.startActivity(intent); |
| mCallback.reportSuccessfulUnlockAttempt(); |
| |
| // close the keyguard |
| mCallback.keyguardDone(true); |
| } else { |
| mInstructions.setText(R.string.lockscreen_glogin_invalid_input); |
| 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.goToLockScreen(); |
| } else { |
| 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).getAccountsByType("com.google"); |
| |
| // 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.pokeWakelock(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).confirmCredentials(account, options, null /* activity */, |
| new AccountManagerCallback<Bundle>() { |
| public void run(AccountManagerFuture<Bundle> future) { |
| try { |
| mCallback.pokeWakelock(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 */); |
| } |
| |
| private Dialog getProgressDialog() { |
| if (mCheckingDialog == null) { |
| mCheckingDialog = new ProgressDialog(mContext); |
| mCheckingDialog.setMessage( |
| mContext.getString(R.string.lockscreen_glogin_checking_password)); |
| mCheckingDialog.setIndeterminate(true); |
| mCheckingDialog.setCancelable(false); |
| mCheckingDialog.getWindow().setType( |
| WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG); |
| if (!mContext.getResources().getBoolean( |
| com.android.internal.R.bool.config_sf_slowBlur)) { |
| mCheckingDialog.getWindow().setFlags( |
| WindowManager.LayoutParams.FLAG_BLUR_BEHIND, |
| WindowManager.LayoutParams.FLAG_BLUR_BEHIND); |
| } |
| } |
| return mCheckingDialog; |
| } |
| |
| public void onPhoneStateChanged(String newState) { |
| mLockPatternUtils.updateEmergencyCallButtonState(mEmergencyCall); |
| } |
| |
| public void onRefreshBatteryInfo(boolean showBatteryInfo, boolean pluggedIn, int batteryLevel) { |
| |
| } |
| |
| public void onRefreshCarrierInfo(CharSequence plmn, CharSequence spn) { |
| |
| } |
| |
| public void onRingerModeChanged(int state) { |
| |
| } |
| |
| public void onTimeChanged() { |
| |
| } |
| } |