Files
frameworks_base/policy/com/android/internal/policy/impl/AccountUnlockScreen.java
Jim Miller ecaced9a78 Manual merge of Idf6bb25438336616e6453e7afe0e79f227af98d3
Fix 2575838: Update emergency button in AccountUnlockScreen DO NOT MERGE

AccountUnlockScreen wasn't registered to receive updates from
UpdateMonitor. It now correctly updates the Emergency call button
to allow the user to return to the call in progress (InCallScreen).

Tested on WVGA device with keyboard:
- hitting Home during call, observing state, and pressing button to return to call.
- hitting Home while not in call to ensure it still invokes EmergencyDialer.
- above in various keyboard states.

Change-Id: Ic4480ff69ff4479557ddb7f76c751b005b1c60bf
2010-04-15 17:39:17 -07:00

348 lines
12 KiB
Java

/*
* 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 final KeyguardScreenCallback mCallback;
private final 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);
}
/** {@inheritDoc} */
public void onClick(View v) {
mCallback.pokeWakelock();
if (v == mOk) {
asyncCheckPassword();
}
if (v == mEmergencyCall) {
mCallback.takeEmergencyCallAction();
}
}
private void onCheckPasswordResult(boolean success) {
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) {
onCheckPasswordResult(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);
// ensure on UI thread
mLogin.post(new Runnable() {
public void run() {
onCheckPasswordResult(verified);
}
});
} catch (OperationCanceledException e) {
onCheckPasswordResult(false);
} catch (IOException e) {
onCheckPasswordResult(false);
} catch (AuthenticatorException e) {
onCheckPasswordResult(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() {
}
}