| /* |
| * 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 com.android.internal.R; |
| import com.android.internal.telephony.IccCard; |
| import com.android.internal.widget.LockPatternUtils; |
| |
| 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.AlertDialog; |
| import android.app.admin.DevicePolicyManager; |
| import android.content.Context; |
| import android.content.Intent; |
| import android.content.res.Configuration; |
| import android.graphics.Bitmap; |
| import android.graphics.Canvas; |
| import android.graphics.ColorFilter; |
| import android.graphics.PixelFormat; |
| import android.graphics.drawable.Drawable; |
| import android.os.Bundle; |
| import android.os.SystemClock; |
| import android.os.SystemProperties; |
| import android.telephony.TelephonyManager; |
| import android.text.TextUtils; |
| import android.util.Log; |
| import android.view.KeyEvent; |
| import android.view.View; |
| import android.view.WindowManager; |
| |
| import java.io.IOException; |
| |
| /** |
| * 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 { |
| |
| static final boolean DEBUG_CONFIGURATION = false; |
| |
| // time after launching EmergencyDialer before the screen goes blank. |
| private static final int EMERGENCY_CALL_TIMEOUT = 10000; |
| |
| // 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 mEnableFallback = false; // assume no fallback UI 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 a sim puk. |
| */ |
| SimPuk, |
| |
| /** |
| * Unlock by entering an account's login and password. |
| */ |
| Account, |
| |
| /** |
| * Unlock by entering a password or PIN |
| */ |
| Password, |
| |
| /** |
| * Unknown (uninitialized) value |
| */ |
| Unknown |
| } |
| |
| /** |
| * The current mode. |
| */ |
| private Mode mMode = Mode.LockScreen; |
| |
| /** |
| * Keeps track of what mode the current unlock screen is (cached from most recent computation in |
| * {@link #getUnlockMode}). |
| */ |
| private UnlockMode mUnlockScreenMode; |
| |
| private boolean mForgotPattern; |
| |
| /** |
| * 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; |
| |
| private UnlockMode mCurrentUnlockMode = UnlockMode.Unknown; |
| |
| /** |
| * The current configuration. |
| */ |
| private Configuration mConfiguration; |
| |
| private Runnable mRecreateRunnable = new Runnable() { |
| public void run() { |
| recreateScreens(); |
| } |
| }; |
| |
| /** |
| * @return Whether we are stuck on the lock screen because the sim is |
| * missing. |
| */ |
| private boolean stuckOnLockScreenBecauseSimMissing() { |
| return mRequiresSim |
| && (!mUpdateMonitor.isDeviceProvisioned()) |
| && (mUpdateMonitor.getSimState() == IccCard.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); |
| |
| mConfiguration = context.getResources().getConfiguration(); |
| mEnableFallback = false; |
| |
| mRequiresSim = |
| TextUtils.isEmpty(SystemProperties.get("keyguard.no_require_sim")); |
| |
| mUpdateMonitor = updateMonitor; |
| mLockPatternUtils = lockPatternUtils; |
| mWindowController = controller; |
| |
| mMode = getInitialMode(); |
| |
| mKeyguardScreenCallback = new KeyguardScreenCallback() { |
| |
| public void goToLockScreen() { |
| mForgotPattern = false; |
| 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 IccCard.State simState = mUpdateMonitor.getSimState(); |
| if (stuckOnLockScreenBecauseSimMissing() |
| || (simState == IccCard.State.PUK_REQUIRED |
| && !mLockPatternUtils.isPukUnlockScreenEnable())){ |
| // stuck on lock screen when sim missing or |
| // puk'd but puk unlock screen is disabled |
| return; |
| } |
| if (!isSecure()) { |
| getCallback().keyguardDone(true); |
| } else { |
| updateScreen(Mode.UnlockScreen); |
| } |
| } |
| |
| public void forgotPattern(boolean isForgotten) { |
| if (mEnableFallback) { |
| mForgotPattern = isForgotten; |
| updateScreen(Mode.UnlockScreen); |
| } |
| } |
| |
| public boolean isSecure() { |
| return LockPatternKeyguardView.this.isSecure(); |
| } |
| |
| public boolean isVerifyUnlockOnly() { |
| return mIsVerifyUnlockOnly; |
| } |
| |
| public void recreateMe(Configuration config) { |
| mConfiguration = config; |
| removeCallbacks(mRecreateRunnable); |
| post(mRecreateRunnable); |
| } |
| |
| public void takeEmergencyCallAction() { |
| pokeWakelock(EMERGENCY_CALL_TIMEOUT); |
| if (TelephonyManager.getDefault().getCallState() |
| == TelephonyManager.CALL_STATE_OFFHOOK) { |
| mLockPatternUtils.resumeCall(); |
| } else { |
| 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 reportFailedUnlockAttempt() { |
| mUpdateMonitor.reportFailedAttempt(); |
| final int failedAttempts = mUpdateMonitor.getFailedAttempts(); |
| if (DEBUG) Log.d(TAG, |
| "reportFailedPatternAttempt: #" + failedAttempts + |
| " (enableFallback=" + mEnableFallback + ")"); |
| final boolean usingLockPattern = mLockPatternUtils.getKeyguardStoredPasswordQuality() |
| == DevicePolicyManager.PASSWORD_QUALITY_SOMETHING; |
| if (usingLockPattern && mEnableFallback && failedAttempts == |
| (LockPatternUtils.FAILED_ATTEMPTS_BEFORE_RESET |
| - LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT)) { |
| showAlmostAtAccountLoginDialog(); |
| } else if (usingLockPattern && mEnableFallback |
| && failedAttempts >= LockPatternUtils.FAILED_ATTEMPTS_BEFORE_RESET) { |
| mLockPatternUtils.setPermanentlyLocked(true); |
| updateScreen(mMode); |
| } else if ((failedAttempts % LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT) |
| == 0) { |
| showTimeoutDialog(); |
| } |
| mLockPatternUtils.reportFailedPasswordAttempt(); |
| } |
| |
| public boolean doesFallbackUnlockScreenExist() { |
| return mEnableFallback; |
| } |
| |
| public void reportSuccessfulUnlockAttempt() { |
| mLockPatternUtils.reportSuccessfulPasswordAttempt(); |
| } |
| }; |
| |
| /** |
| * 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); |
| |
| // 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(); |
| if (DEBUG) Log.d(TAG, |
| "LockPatternKeyguardView ctor: about to createUnlockScreenFor; mEnableFallback=" |
| + mEnableFallback); |
| mUnlockScreen = createUnlockScreenFor(unlockMode); |
| mUnlockScreenMode = unlockMode; |
| |
| maybeEnableFallback(context); |
| |
| addView(mUnlockScreen); |
| updateScreen(mMode); |
| } |
| |
| private class AccountAnalyzer implements AccountManagerCallback<Bundle> { |
| private final AccountManager mAccountManager; |
| private final Account[] mAccounts; |
| private int mAccountIndex; |
| |
| private AccountAnalyzer(AccountManager accountManager) { |
| mAccountManager = accountManager; |
| mAccounts = accountManager.getAccountsByType("com.google"); |
| } |
| |
| private void next() { |
| // if we are ready to enable the fallback or if we depleted the list of accounts |
| // then finish and get out |
| if (mEnableFallback || mAccountIndex >= mAccounts.length) { |
| if (mUnlockScreen == null) { |
| Log.w(TAG, "no unlock screen when trying to enable fallback"); |
| } else if (mUnlockScreen instanceof PatternUnlockScreen) { |
| ((PatternUnlockScreen)mUnlockScreen).setEnableFallback(mEnableFallback); |
| } |
| return; |
| } |
| |
| // lookup the confirmCredentials intent for the current account |
| mAccountManager.confirmCredentials(mAccounts[mAccountIndex], null, null, this, null); |
| } |
| |
| public void start() { |
| mEnableFallback = false; |
| mAccountIndex = 0; |
| next(); |
| } |
| |
| public void run(AccountManagerFuture<Bundle> future) { |
| try { |
| Bundle result = future.getResult(); |
| if (result.getParcelable(AccountManager.KEY_INTENT) != null) { |
| mEnableFallback = true; |
| } |
| } catch (OperationCanceledException e) { |
| // just skip the account if we are unable to query it |
| } catch (IOException e) { |
| // just skip the account if we are unable to query it |
| } catch (AuthenticatorException e) { |
| // just skip the account if we are unable to query it |
| } finally { |
| mAccountIndex++; |
| next(); |
| } |
| } |
| } |
| |
| private void maybeEnableFallback(Context context) { |
| // Ask the account manager if we have an account that can be used as a |
| // fallback in case the user forgets his pattern. |
| AccountAnalyzer accountAnalyzer = new AccountAnalyzer(AccountManager.get(context)); |
| accountAnalyzer.start(); |
| } |
| |
| |
| // TODO: |
| // This overloaded method was added to workaround a race condition in the framework between |
| // notification for orientation changed, layout() and switching resources. This code attempts |
| // to avoid drawing the incorrect layout while things are in transition. The method can just |
| // be removed once the race condition is fixed. See bugs 2262578 and 2292713. |
| @Override |
| protected void dispatchDraw(Canvas canvas) { |
| if (DEBUG) Log.v(TAG, "*** dispatchDraw() time: " + SystemClock.elapsedRealtime()); |
| super.dispatchDraw(canvas); |
| } |
| |
| @Override |
| public void reset() { |
| mIsVerifyUnlockOnly = false; |
| mForgotPattern = false; |
| updateScreen(getInitialMode()); |
| } |
| |
| @Override |
| public void onScreenTurnedOff() { |
| mScreenOn = false; |
| mForgotPattern = 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 recreateLockScreen() { |
| if (mLockScreen.getVisibility() == View.VISIBLE) { |
| ((KeyguardScreen) mLockScreen).onPause(); |
| } |
| ((KeyguardScreen) mLockScreen).cleanUp(); |
| removeView(mLockScreen); |
| |
| mLockScreen = createLockScreen(); |
| mLockScreen.setVisibility(View.INVISIBLE); |
| addView(mLockScreen); |
| } |
| |
| private void recreateUnlockScreen() { |
| if (mUnlockScreen.getVisibility() == View.VISIBLE) { |
| ((KeyguardScreen) mUnlockScreen).onPause(); |
| } |
| ((KeyguardScreen) mUnlockScreen).cleanUp(); |
| removeView(mUnlockScreen); |
| |
| final UnlockMode unlockMode = getUnlockMode(); |
| mUnlockScreen = createUnlockScreenFor(unlockMode); |
| mUnlockScreen.setVisibility(View.INVISIBLE); |
| mUnlockScreenMode = unlockMode; |
| addView(mUnlockScreen); |
| } |
| |
| private void recreateScreens() { |
| recreateLockScreen(); |
| recreateUnlockScreen(); |
| updateScreen(mMode); |
| } |
| |
| @Override |
| protected void onDetachedFromWindow() { |
| removeCallbacks(mRecreateRunnable); |
| super.onDetachedFromWindow(); |
| } |
| |
| @Override |
| public void wakeWhenReadyTq(int keyCode) { |
| if (DEBUG) Log.d(TAG, "onWakeKey"); |
| if (keyCode == KeyEvent.KEYCODE_MENU && isSecure() && (mMode == Mode.LockScreen) |
| && (mUpdateMonitor.getSimState() != IccCard.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(); |
| this.removeView(mLockScreen); |
| ((KeyguardScreen) mUnlockScreen).onPause(); |
| ((KeyguardScreen) mUnlockScreen).cleanUp(); |
| this.removeView(mUnlockScreen); |
| } |
| |
| private boolean isSecure() { |
| UnlockMode unlockMode = getUnlockMode(); |
| boolean secure = false; |
| switch (unlockMode) { |
| case Pattern: |
| secure = mLockPatternUtils.isLockPatternEnabled(); |
| break; |
| case SimPin: |
| secure = mUpdateMonitor.getSimState() == IccCard.State.PIN_REQUIRED; |
| break; |
| case SimPuk: |
| secure = mUpdateMonitor.getSimState() == IccCard.State.PUK_REQUIRED; |
| break; |
| case Account: |
| secure = true; |
| break; |
| case Password: |
| secure = mLockPatternUtils.isLockPasswordEnabled(); |
| break; |
| default: |
| throw new IllegalStateException("unknown unlock mode " + unlockMode); |
| } |
| return secure; |
| } |
| |
| private void updateScreen(final Mode mode) { |
| |
| if (DEBUG_CONFIGURATION) Log.v(TAG, "**** UPDATE SCREEN: mode=" + mode |
| + " last mode=" + mMode, new RuntimeException()); |
| |
| mMode = mode; |
| |
| // Re-create the unlock screen if necessary. This is primarily required to properly handle |
| // SIM state changes. This typically happens when this method is called by reset() |
| if (mode == Mode.UnlockScreen && mCurrentUnlockMode != getUnlockMode()) { |
| recreateUnlockScreen(); |
| } |
| |
| final View goneScreen = (mode == Mode.LockScreen) ? mUnlockScreen : mLockScreen; |
| final View visibleScreen = (mode == Mode.LockScreen) ? mLockScreen : mUnlockScreen; |
| |
| // do this before changing visibility so focus isn't requested before the input |
| // flag is set |
| mWindowController.setNeedsInput(((KeyguardScreen)visibleScreen).needsInput()); |
| |
| if (DEBUG_CONFIGURATION) { |
| Log.v(TAG, "Gone=" + goneScreen); |
| Log.v(TAG, "Visible=" + visibleScreen); |
| } |
| |
| 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); |
| requestLayout(); |
| |
| 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, |
| mConfiguration, |
| mLockPatternUtils, |
| mUpdateMonitor, |
| mKeyguardScreenCallback); |
| } |
| |
| View createUnlockScreenFor(UnlockMode unlockMode) { |
| View unlockView = null; |
| |
| if (DEBUG) Log.d(TAG, |
| "createUnlockScreenFor(" + unlockMode + "): mEnableFallback=" + mEnableFallback); |
| |
| if (unlockMode == UnlockMode.Pattern) { |
| PatternUnlockScreen view = new PatternUnlockScreen( |
| mContext, |
| mConfiguration, |
| mLockPatternUtils, |
| mUpdateMonitor, |
| mKeyguardScreenCallback, |
| mUpdateMonitor.getFailedAttempts()); |
| view.setEnableFallback(mEnableFallback); |
| unlockView = view; |
| } else if (unlockMode == UnlockMode.SimPuk) { |
| unlockView = new SimPukUnlockScreen( |
| mContext, |
| mConfiguration, |
| mUpdateMonitor, |
| mKeyguardScreenCallback, |
| mLockPatternUtils); |
| } else if (unlockMode == UnlockMode.SimPin) { |
| unlockView = new SimUnlockScreen( |
| mContext, |
| mConfiguration, |
| mUpdateMonitor, |
| mKeyguardScreenCallback, |
| mLockPatternUtils); |
| } else if (unlockMode == UnlockMode.Account) { |
| try { |
| unlockView = new AccountUnlockScreen( |
| mContext, |
| mConfiguration, |
| mUpdateMonitor, |
| 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.) |
| unlockView = createUnlockScreenFor(UnlockMode.Pattern); |
| } |
| } else if (unlockMode == UnlockMode.Password) { |
| unlockView = new PasswordUnlockScreen( |
| mContext, |
| mConfiguration, |
| mLockPatternUtils, |
| mUpdateMonitor, |
| mKeyguardScreenCallback); |
| } else { |
| throw new IllegalArgumentException("unknown unlock mode " + unlockMode); |
| } |
| mCurrentUnlockMode = unlockMode; |
| return unlockView; |
| } |
| |
| /** |
| * Given the current state of things, what should be the initial mode of |
| * the lock screen (lock or unlock). |
| */ |
| private Mode getInitialMode() { |
| final IccCard.State simState = mUpdateMonitor.getSimState(); |
| if (stuckOnLockScreenBecauseSimMissing() || |
| (simState == IccCard.State.PUK_REQUIRED && |
| !mLockPatternUtils.isPukUnlockScreenEnable())) { |
| return Mode.LockScreen; |
| } else { |
| // Show LockScreen first for any screen other than Pattern unlock. |
| final boolean usingLockPattern = mLockPatternUtils.getKeyguardStoredPasswordQuality() |
| == DevicePolicyManager.PASSWORD_QUALITY_SOMETHING; |
| |
| boolean showSlidingTab = getResources().getBoolean(R.bool.config_enableSlidingTabFirst); |
| if (isSecure() && (usingLockPattern || !showSlidingTab)) { |
| return Mode.UnlockScreen; |
| } else { |
| return Mode.LockScreen; |
| } |
| } |
| } |
| |
| /** |
| * Given the current state of things, what should the unlock screen be? |
| */ |
| private UnlockMode getUnlockMode() { |
| final IccCard.State simState = mUpdateMonitor.getSimState(); |
| UnlockMode currentMode; |
| if (simState == IccCard.State.PIN_REQUIRED) { |
| currentMode = UnlockMode.SimPin; |
| } else if (simState == IccCard.State.PUK_REQUIRED) { |
| currentMode = UnlockMode.SimPuk; |
| } else { |
| final int mode = mLockPatternUtils.getKeyguardStoredPasswordQuality(); |
| switch (mode) { |
| case DevicePolicyManager.PASSWORD_QUALITY_NUMERIC: |
| case DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC: |
| case DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC: |
| case DevicePolicyManager.PASSWORD_QUALITY_COMPLEX: |
| currentMode = UnlockMode.Password; |
| break; |
| case DevicePolicyManager.PASSWORD_QUALITY_SOMETHING: |
| case DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED: |
| // "forgot pattern" button is only available in the pattern mode... |
| if (mForgotPattern || mLockPatternUtils.isPermanentlyLocked()) { |
| currentMode = UnlockMode.Account; |
| } else { |
| currentMode = UnlockMode.Pattern; |
| } |
| break; |
| default: |
| throw new IllegalStateException("Unknown unlock mode:" + mode); |
| } |
| } |
| return currentMode; |
| } |
| |
| private void showTimeoutDialog() { |
| int timeoutInSeconds = (int) LockPatternUtils.FAILED_ATTEMPT_TIMEOUT_MS / 1000; |
| int messageId = R.string.lockscreen_too_many_failed_attempts_dialog_message; |
| if(getUnlockMode() == UnlockMode.Password) { |
| if(mLockPatternUtils.getKeyguardStoredPasswordQuality() == |
| DevicePolicyManager.PASSWORD_QUALITY_NUMERIC) { |
| messageId = R.string.lockscreen_too_many_failed_pin_attempts_dialog_message; |
| } else { |
| messageId = R.string.lockscreen_too_many_failed_password_attempts_dialog_message; |
| } |
| } |
| String message = mContext.getString( |
| messageId, |
| 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); |
| if (!mContext.getResources().getBoolean( |
| com.android.internal.R.bool.config_sf_slowBlur)) { |
| 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); |
| if (!mContext.getResources().getBoolean( |
| com.android.internal.R.bool.config_sf_slowBlur)) { |
| 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 pins the bottom (assuming that the lock screen is aligned |
| * with the bottom, so the wallpaper should extend above the top into the |
| * status bar). |
| */ |
| static private class FastBitmapDrawable extends Drawable { |
| private Bitmap mBitmap; |
| private int mOpacity; |
| |
| private FastBitmapDrawable(Bitmap bitmap) { |
| mBitmap = bitmap; |
| mOpacity = mBitmap.hasAlpha() ? PixelFormat.TRANSLUCENT : PixelFormat.OPAQUE; |
| } |
| |
| @Override |
| public void draw(Canvas canvas) { |
| canvas.drawBitmap( |
| mBitmap, |
| (getBounds().width() - mBitmap.getWidth()) / 2, |
| (getBounds().height() - mBitmap.getHeight()), |
| null); |
| } |
| |
| @Override |
| public int getOpacity() { |
| return mOpacity; |
| } |
| |
| @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(); |
| } |
| } |
| } |
| |