summaryrefslogtreecommitdiff
path: root/phone/com/android/internal/policy/impl/LockPatternKeyguardView.java
diff options
context:
space:
mode:
authorThe Android Open Source Project <initial-contribution@android.com>2009-03-03 19:32:13 -0800
committerThe Android Open Source Project <initial-contribution@android.com>2009-03-03 19:32:13 -0800
commitc5d93b3b6a0ba4831903f8e8d1664c4470cf15d7 (patch)
tree0a927a9daf4d72d6f048d2d20d769cb290091544 /phone/com/android/internal/policy/impl/LockPatternKeyguardView.java
parent461429674b80b8c0452df6f69376c6394014e6a8 (diff)
downloadbase-c5d93b3b6a0ba4831903f8e8d1664c4470cf15d7.tar.gz
auto import from //depot/cupcake/@135843
Diffstat (limited to 'phone/com/android/internal/policy/impl/LockPatternKeyguardView.java')
-rw-r--r--phone/com/android/internal/policy/impl/LockPatternKeyguardView.java639
1 files changed, 639 insertions, 0 deletions
diff --git a/phone/com/android/internal/policy/impl/LockPatternKeyguardView.java b/phone/com/android/internal/policy/impl/LockPatternKeyguardView.java
new file mode 100644
index 0000000..cb3131d
--- /dev/null
+++ b/phone/com/android/internal/policy/impl/LockPatternKeyguardView.java
@@ -0,0 +1,639 @@
+/*
+ * Copyright (C) 2007 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 android.accounts.AccountsServiceConstants;
+import android.accounts.IAccountsService;
+import android.app.AlertDialog;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.SystemProperties;
+import com.android.internal.telephony.SimCard;
+import android.text.TextUtils;
+import android.util.Log;
+import android.view.KeyEvent;
+import android.view.View;
+import android.view.WindowManager;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.PixelFormat;
+import android.graphics.ColorFilter;
+import com.android.internal.R;
+import com.android.internal.widget.LockPatternUtils;
+
+/**
+ * The host view for all of the screens of the pattern unlock screen. There are
+ * two {@link Mode}s of operation, lock and unlock. This will show the appropriate
+ * screen, and listen for callbacks via {@link com.android.internal.policy.impl.KeyguardScreenCallback
+ * from the current screen.
+ *
+ * This view, in turn, communicates back to {@link com.android.internal.policy.impl.KeyguardViewManager}
+ * via its {@link com.android.internal.policy.impl.KeyguardViewCallback}, as appropriate.
+ */
+public class LockPatternKeyguardView extends KeyguardViewBase {
+
+ // intent action for launching emergency dialer activity.
+ static final String ACTION_EMERGENCY_DIAL = "com.android.phone.EmergencyDialer.DIAL";
+
+ private static final boolean DEBUG = false;
+ private static final String TAG = "LockPatternKeyguardView";
+
+ private final KeyguardUpdateMonitor mUpdateMonitor;
+ private final KeyguardWindowController mWindowController;
+
+ private View mLockScreen;
+ private View mUnlockScreen;
+
+ private boolean mScreenOn = false;
+ private boolean mHasAccount = false; // assume they don't have an account until we know better
+
+
+ /**
+ * The current {@link KeyguardScreen} will use this to communicate back to us.
+ */
+ KeyguardScreenCallback mKeyguardScreenCallback;
+
+
+ private boolean mRequiresSim;
+
+
+ /**
+ * Either a lock screen (an informational keyguard screen), or an unlock
+ * screen (a means for unlocking the device) is shown at any given time.
+ */
+ enum Mode {
+ LockScreen,
+ UnlockScreen
+ }
+
+ /**
+ * The different types screens available for {@link Mode#UnlockScreen}.
+ * @see com.android.internal.policy.impl.LockPatternKeyguardView#getUnlockMode()
+ */
+ enum UnlockMode {
+
+ /**
+ * Unlock by drawing a pattern.
+ */
+ Pattern,
+
+ /**
+ * Unlock by entering a sim pin.
+ */
+ SimPin,
+
+ /**
+ * Unlock by entering an account's login and password.
+ */
+ Account
+ }
+
+ /**
+ * The current mode.
+ */
+ private Mode mMode = Mode.LockScreen;
+
+ /**
+ * Keeps track of what mode the current unlock screen is
+ */
+ private UnlockMode mUnlockScreenMode;
+
+ /**
+ * If true, it means we are in the process of verifying that the user
+ * can get past the lock screen per {@link #verifyUnlock()}
+ */
+ private boolean mIsVerifyUnlockOnly = false;
+
+
+ /**
+ * Used to lookup the state of the lock pattern
+ */
+ private final LockPatternUtils mLockPatternUtils;
+
+ /**
+ * Used to fetch accounts from GLS.
+ */
+ private ServiceConnection mServiceConnection;
+
+ /**
+ * @return Whether we are stuck on the lock screen because the sim is
+ * missing.
+ */
+ private boolean stuckOnLockScreenBecauseSimMissing() {
+ return mRequiresSim
+ && (!mUpdateMonitor.isDeviceProvisioned())
+ && (mUpdateMonitor.getSimState() == SimCard.State.ABSENT);
+ }
+
+ /**
+ * @param context Used to inflate, and create views.
+ * @param updateMonitor Knows the state of the world, and passed along to each
+ * screen so they can use the knowledge, and also register for callbacks
+ * on dynamic information.
+ * @param lockPatternUtils Used to look up state of lock pattern.
+ */
+ public LockPatternKeyguardView(
+ Context context,
+ KeyguardUpdateMonitor updateMonitor,
+ LockPatternUtils lockPatternUtils,
+ KeyguardWindowController controller) {
+ super(context);
+
+ asyncCheckForAccount();
+
+ mRequiresSim =
+ TextUtils.isEmpty(SystemProperties.get("keyguard.no_require_sim"));
+
+ mUpdateMonitor = updateMonitor;
+ mLockPatternUtils = lockPatternUtils;
+ mWindowController = controller;
+
+ mMode = getInitialMode();
+
+ mKeyguardScreenCallback = new KeyguardScreenCallback() {
+
+ public void goToLockScreen() {
+ if (mIsVerifyUnlockOnly) {
+ // navigating away from unlock screen during verify mode means
+ // we are done and the user failed to authenticate.
+ mIsVerifyUnlockOnly = false;
+ getCallback().keyguardDone(false);
+ } else {
+ updateScreen(Mode.LockScreen);
+ }
+ }
+
+ public void goToUnlockScreen() {
+ final SimCard.State simState = mUpdateMonitor.getSimState();
+ if (stuckOnLockScreenBecauseSimMissing()
+ || (simState == SimCard.State.PUK_REQUIRED)){
+ // stuck on lock screen when sim missing or puk'd
+ return;
+ }
+ if (!isSecure()) {
+ getCallback().keyguardDone(true);
+ } else {
+ updateScreen(Mode.UnlockScreen);
+ }
+ }
+
+ public boolean isSecure() {
+ return LockPatternKeyguardView.this.isSecure();
+ }
+
+ public boolean isVerifyUnlockOnly() {
+ return mIsVerifyUnlockOnly;
+ }
+
+ public void recreateMe() {
+ recreateScreens();
+ }
+
+ public void takeEmergencyCallAction() {
+ Intent intent = new Intent(ACTION_EMERGENCY_DIAL);
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
+ | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
+ getContext().startActivity(intent);
+ }
+
+ public void pokeWakelock() {
+ getCallback().pokeWakelock();
+ }
+
+ public void pokeWakelock(int millis) {
+ getCallback().pokeWakelock(millis);
+ }
+
+ public void keyguardDone(boolean authenticated) {
+ getCallback().keyguardDone(authenticated);
+ }
+
+ public void keyguardDoneDrawing() {
+ // irrelevant to keyguard screen, they shouldn't be calling this
+ }
+
+ public void reportFailedPatternAttempt() {
+ mUpdateMonitor.reportFailedAttempt();
+ final int failedAttempts = mUpdateMonitor.getFailedAttempts();
+ if (mHasAccount && failedAttempts ==
+ (LockPatternUtils.FAILED_ATTEMPTS_BEFORE_RESET
+ - LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT)) {
+ showAlmostAtAccountLoginDialog();
+ } else if (mHasAccount
+ && failedAttempts >= LockPatternUtils.FAILED_ATTEMPTS_BEFORE_RESET) {
+ mLockPatternUtils.setPermanentlyLocked(true);
+ updateScreen(mMode);
+ } else if ((failedAttempts % LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT)
+ == 0) {
+ showTimeoutDialog();
+ }
+ }
+
+ public boolean doesFallbackUnlockScreenExist() {
+ return mHasAccount;
+ }
+ };
+
+ /**
+ * We'll get key events the current screen doesn't use. see
+ * {@link KeyguardViewBase#onKeyDown(int, android.view.KeyEvent)}
+ */
+ setFocusableInTouchMode(true);
+ setDescendantFocusability(FOCUS_AFTER_DESCENDANTS);
+
+ // wall paper background
+ final BitmapDrawable drawable = (BitmapDrawable) context.getWallpaper();
+ setBackgroundDrawable(
+ new FastBitmapDrawable(drawable.getBitmap()));
+
+ // create both the lock and unlock screen so they are quickly available
+ // when the screen turns on
+ mLockScreen = createLockScreen();
+ addView(mLockScreen);
+ final UnlockMode unlockMode = getUnlockMode();
+ mUnlockScreen = createUnlockScreenFor(unlockMode);
+ mUnlockScreenMode = unlockMode;
+ addView(mUnlockScreen);
+ updateScreen(mMode);
+ }
+
+ /**
+ * Asynchronously checks for at least one account. This will set mHasAccount
+ * to true if an account is found.
+ */
+ private void asyncCheckForAccount() {
+
+ mServiceConnection = new ServiceConnection() {
+ public void onServiceConnected(ComponentName className, IBinder service) {
+ try {
+ IAccountsService accountsService = IAccountsService.Stub.asInterface(service);
+ String accounts[] = accountsService.getAccounts();
+ mHasAccount = (accounts.length > 0);
+ } catch (RemoteException e) {
+ // Not much we can do here...
+ Log.e(TAG, "Gls died while attempting to get accounts: " + e);
+ } finally {
+ getContext().unbindService(mServiceConnection);
+ mServiceConnection = null;
+ }
+ }
+
+ public void onServiceDisconnected(ComponentName className) {
+ // nothing to do here
+ }
+ };
+ boolean status = getContext().bindService(AccountsServiceConstants.SERVICE_INTENT,
+ mServiceConnection, Context.BIND_AUTO_CREATE);
+ if (!status) Log.e(TAG, "Failed to bind to GLS while checking for account");
+ }
+
+ @Override
+ public void reset() {
+ mIsVerifyUnlockOnly = false;
+ updateScreen(getInitialMode());
+ }
+
+ @Override
+ public void onScreenTurnedOff() {
+ mScreenOn = false;
+ if (mMode == Mode.LockScreen) {
+ ((KeyguardScreen) mLockScreen).onPause();
+ } else {
+ ((KeyguardScreen) mUnlockScreen).onPause();
+ }
+ }
+
+ @Override
+ public void onScreenTurnedOn() {
+ mScreenOn = true;
+ if (mMode == Mode.LockScreen) {
+ ((KeyguardScreen) mLockScreen).onResume();
+ } else {
+ ((KeyguardScreen) mUnlockScreen).onResume();
+ }
+ }
+
+
+ private void recreateScreens() {
+ if (mLockScreen.getVisibility() == View.VISIBLE) {
+ ((KeyguardScreen) mLockScreen).onPause();
+ }
+ ((KeyguardScreen) mLockScreen).cleanUp();
+ removeViewInLayout(mLockScreen);
+
+ mLockScreen = createLockScreen();
+ mLockScreen.setVisibility(View.INVISIBLE);
+ addView(mLockScreen);
+
+ if (mUnlockScreen.getVisibility() == View.VISIBLE) {
+ ((KeyguardScreen) mUnlockScreen).onPause();
+ }
+ ((KeyguardScreen) mUnlockScreen).cleanUp();
+ removeViewInLayout(mUnlockScreen);
+
+ final UnlockMode unlockMode = getUnlockMode();
+ mUnlockScreen = createUnlockScreenFor(unlockMode);
+ mUnlockScreen.setVisibility(View.INVISIBLE);
+ mUnlockScreenMode = unlockMode;
+ addView(mUnlockScreen);
+
+ updateScreen(mMode);
+ }
+
+
+ @Override
+ public void wakeWhenReadyTq(int keyCode) {
+ if (DEBUG) Log.d(TAG, "onWakeKey");
+ if (keyCode == KeyEvent.KEYCODE_MENU && isSecure() && (mMode == Mode.LockScreen)
+ && (mUpdateMonitor.getSimState() != SimCard.State.PUK_REQUIRED)) {
+ if (DEBUG) Log.d(TAG, "switching screens to unlock screen because wake key was MENU");
+ updateScreen(Mode.UnlockScreen);
+ getCallback().pokeWakelock();
+ } else {
+ if (DEBUG) Log.d(TAG, "poking wake lock immediately");
+ getCallback().pokeWakelock();
+ }
+ }
+
+ @Override
+ public void verifyUnlock() {
+ if (!isSecure()) {
+ // non-secure keyguard screens are successfull by default
+ getCallback().keyguardDone(true);
+ } else if (mUnlockScreenMode != UnlockMode.Pattern) {
+ // can only verify unlock when in pattern mode
+ getCallback().keyguardDone(false);
+ } else {
+ // otherwise, go to the unlock screen, see if they can verify it
+ mIsVerifyUnlockOnly = true;
+ updateScreen(Mode.UnlockScreen);
+ }
+ }
+
+ @Override
+ public void cleanUp() {
+ ((KeyguardScreen) mLockScreen).onPause();
+ ((KeyguardScreen) mLockScreen).cleanUp();
+ ((KeyguardScreen) mUnlockScreen).onPause();
+ ((KeyguardScreen) mUnlockScreen).cleanUp();
+ }
+
+ private boolean isSecure() {
+ UnlockMode unlockMode = getUnlockMode();
+ if (unlockMode == UnlockMode.Pattern) {
+ return mLockPatternUtils.isLockPatternEnabled();
+ } else if (unlockMode == UnlockMode.SimPin) {
+ return mUpdateMonitor.getSimState() == SimCard.State.PIN_REQUIRED
+ || mUpdateMonitor.getSimState() == SimCard.State.PUK_REQUIRED;
+ } else if (unlockMode == UnlockMode.Account) {
+ return true;
+ } else {
+ throw new IllegalStateException("unknown unlock mode " + unlockMode);
+ }
+ }
+
+ private void updateScreen(final Mode mode) {
+
+ mMode = mode;
+
+ final View goneScreen = (mode == Mode.LockScreen) ? mUnlockScreen : mLockScreen;
+ final View visibleScreen = (mode == Mode.LockScreen)
+ ? mLockScreen : getUnlockScreenForCurrentUnlockMode();
+
+ // do this before changing visibility so focus isn't requested before the input
+ // flag is set
+ mWindowController.setNeedsInput(((KeyguardScreen)visibleScreen).needsInput());
+
+
+ if (mScreenOn) {
+ if (goneScreen.getVisibility() == View.VISIBLE) {
+ ((KeyguardScreen) goneScreen).onPause();
+ }
+ if (visibleScreen.getVisibility() != View.VISIBLE) {
+ ((KeyguardScreen) visibleScreen).onResume();
+ }
+ }
+
+ goneScreen.setVisibility(View.GONE);
+ visibleScreen.setVisibility(View.VISIBLE);
+
+
+ if (!visibleScreen.requestFocus()) {
+ throw new IllegalStateException("keyguard screen must be able to take "
+ + "focus when shown " + visibleScreen.getClass().getCanonicalName());
+ }
+ }
+
+ View createLockScreen() {
+ return new LockScreen(
+ mContext,
+ mLockPatternUtils,
+ mUpdateMonitor,
+ mKeyguardScreenCallback);
+ }
+
+ View createUnlockScreenFor(UnlockMode unlockMode) {
+ if (unlockMode == UnlockMode.Pattern) {
+ return new UnlockScreen(
+ mContext,
+ mLockPatternUtils,
+ mUpdateMonitor,
+ mKeyguardScreenCallback,
+ mUpdateMonitor.getFailedAttempts());
+ } else if (unlockMode == UnlockMode.SimPin) {
+ return new SimUnlockScreen(
+ mContext,
+ mUpdateMonitor,
+ mKeyguardScreenCallback);
+ } else if (unlockMode == UnlockMode.Account) {
+ try {
+ return new AccountUnlockScreen(
+ mContext,
+ mKeyguardScreenCallback,
+ mLockPatternUtils);
+ } catch (IllegalStateException e) {
+ Log.i(TAG, "Couldn't instantiate AccountUnlockScreen"
+ + " (IAccountsService isn't available)");
+ // TODO: Need a more general way to provide a
+ // platform-specific fallback UI here.
+ // For now, if we can't display the account login
+ // unlock UI, just bring back the regular "Pattern" unlock mode.
+
+ // (We do this by simply returning a regular UnlockScreen
+ // here. This means that the user will still see the
+ // regular pattern unlock UI, regardless of the value of
+ // mUnlockScreenMode or whether or not we're in the
+ // "permanently locked" state.)
+ return createUnlockScreenFor(UnlockMode.Pattern);
+ }
+ } else {
+ throw new IllegalArgumentException("unknown unlock mode " + unlockMode);
+ }
+ }
+
+ private View getUnlockScreenForCurrentUnlockMode() {
+ final UnlockMode unlockMode = getUnlockMode();
+
+ // if a screen exists for the correct mode, we're done
+ if (unlockMode == mUnlockScreenMode) {
+ return mUnlockScreen;
+ }
+
+ // remember the mode
+ mUnlockScreenMode = unlockMode;
+
+ // unlock mode has changed and we have an existing old unlock screen
+ // to clean up
+ if (mScreenOn && (mUnlockScreen.getVisibility() == View.VISIBLE)) {
+ ((KeyguardScreen) mUnlockScreen).onPause();
+ }
+ ((KeyguardScreen) mUnlockScreen).cleanUp();
+ removeViewInLayout(mUnlockScreen);
+
+ // create the new one
+ mUnlockScreen = createUnlockScreenFor(unlockMode);
+ mUnlockScreen.setVisibility(View.INVISIBLE);
+ addView(mUnlockScreen);
+ return mUnlockScreen;
+ }
+
+ /**
+ * Given the current state of things, what should be the initial mode of
+ * the lock screen (lock or unlock).
+ */
+ private Mode getInitialMode() {
+ final SimCard.State simState = mUpdateMonitor.getSimState();
+ if (stuckOnLockScreenBecauseSimMissing() || (simState == SimCard.State.PUK_REQUIRED)) {
+ return Mode.LockScreen;
+ } else if (mUpdateMonitor.isKeyboardOpen() && isSecure()) {
+ return Mode.UnlockScreen;
+ } else {
+ return Mode.LockScreen;
+ }
+ }
+
+ /**
+ * Given the current state of things, what should the unlock screen be?
+ */
+ private UnlockMode getUnlockMode() {
+ final SimCard.State simState = mUpdateMonitor.getSimState();
+ if (simState == SimCard.State.PIN_REQUIRED || simState == SimCard.State.PUK_REQUIRED) {
+ return UnlockMode.SimPin;
+ } else {
+ return mLockPatternUtils.isPermanentlyLocked() ?
+ UnlockMode.Account:
+ UnlockMode.Pattern;
+ }
+ }
+
+ private void showTimeoutDialog() {
+ int timeoutInSeconds = (int) LockPatternUtils.FAILED_ATTEMPT_TIMEOUT_MS / 1000;
+ String message = mContext.getString(
+ R.string.lockscreen_too_many_failed_attempts_dialog_message,
+ mUpdateMonitor.getFailedAttempts(),
+ timeoutInSeconds);
+ final AlertDialog dialog = new AlertDialog.Builder(mContext)
+ .setTitle(null)
+ .setMessage(message)
+ .setNeutralButton(R.string.ok, null)
+ .create();
+ dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
+ dialog.getWindow().setFlags(
+ WindowManager.LayoutParams.FLAG_BLUR_BEHIND,
+ WindowManager.LayoutParams.FLAG_BLUR_BEHIND);
+ dialog.show();
+ }
+
+ private void showAlmostAtAccountLoginDialog() {
+ int timeoutInSeconds = (int) LockPatternUtils.FAILED_ATTEMPT_TIMEOUT_MS / 1000;
+ String message = mContext.getString(
+ R.string.lockscreen_failed_attempts_almost_glogin,
+ LockPatternUtils.FAILED_ATTEMPTS_BEFORE_RESET - LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT,
+ LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT,
+ timeoutInSeconds);
+ final AlertDialog dialog = new AlertDialog.Builder(mContext)
+ .setTitle(null)
+ .setMessage(message)
+ .setNeutralButton(R.string.ok, null)
+ .create();
+ dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
+ dialog.getWindow().setFlags(
+ WindowManager.LayoutParams.FLAG_BLUR_BEHIND,
+ WindowManager.LayoutParams.FLAG_BLUR_BEHIND);
+ dialog.show();
+ }
+
+ /**
+ * Used to put wallpaper on the background of the lock screen. Centers it Horizontally and
+ * vertically.
+ */
+ static private class FastBitmapDrawable extends Drawable {
+ private Bitmap mBitmap;
+
+ private FastBitmapDrawable(Bitmap bitmap) {
+ mBitmap = bitmap;
+ }
+
+ @Override
+ public void draw(Canvas canvas) {
+ canvas.drawBitmap(
+ mBitmap,
+ (getBounds().width() - mBitmap.getWidth()) / 2,
+ (getBounds().height() - mBitmap.getHeight()) / 2,
+ null);
+ }
+
+ @Override
+ public int getOpacity() {
+ return PixelFormat.TRANSLUCENT;
+ }
+
+ @Override
+ public void setAlpha(int alpha) {
+ }
+
+ @Override
+ public void setColorFilter(ColorFilter cf) {
+ }
+
+ @Override
+ public int getIntrinsicWidth() {
+ return mBitmap.getWidth();
+ }
+
+ @Override
+ public int getIntrinsicHeight() {
+ return mBitmap.getHeight();
+ }
+
+ @Override
+ public int getMinimumWidth() {
+ return mBitmap.getWidth();
+ }
+
+ @Override
+ public int getMinimumHeight() {
+ return mBitmap.getHeight();
+ }
+ }
+}