diff options
author | Jean-Baptiste Queru <jbq@google.com> | 2009-11-12 18:46:07 -0800 |
---|---|---|
committer | Jean-Baptiste Queru <jbq@google.com> | 2009-11-12 18:46:07 -0800 |
commit | 0469e9b13987ffe67894bde0a3af091580a036ff (patch) | |
tree | 074e3a1fc6bc2fb96b34b3e9b3665b40e3550280 /policy | |
parent | 94a679daba6e07a600966b1cb034f04f2ec20003 (diff) | |
download | frameworks_base-0469e9b13987ffe67894bde0a3af091580a036ff.zip frameworks_base-0469e9b13987ffe67894bde0a3af091580a036ff.tar.gz frameworks_base-0469e9b13987ffe67894bde0a3af091580a036ff.tar.bz2 |
eclair snapshot
Diffstat (limited to 'policy')
14 files changed, 1715 insertions, 834 deletions
diff --git a/policy/com/android/internal/policy/impl/AccountUnlockScreen.java b/policy/com/android/internal/policy/impl/AccountUnlockScreen.java index 65102c6..7992dd8 100644 --- a/policy/com/android/internal/policy/impl/AccountUnlockScreen.java +++ b/policy/com/android/internal/policy/impl/AccountUnlockScreen.java @@ -19,38 +19,39 @@ package com.android.internal.policy.impl; import com.android.internal.R; import com.android.internal.widget.LockPatternUtils; -import android.accounts.AccountsServiceConstants; -import android.accounts.IAccountsService; -import android.content.ComponentName; +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.ServiceConnection; import android.graphics.Rect; -import android.os.IBinder; -import android.os.RemoteException; import android.text.Editable; import android.text.InputFilter; import android.text.LoginFilter; import android.text.TextWatcher; -import android.text.TextUtils; -import android.util.Log; 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). - * - * <p>This class is useful only on platforms that support the - * IAccountsService. */ public class AccountUnlockScreen extends RelativeLayout implements KeyguardScreen, - View.OnClickListener, ServiceConnection, TextWatcher { + View.OnClickListener, TextWatcher { private static final String LOCK_PATTERN_PACKAGE = "com.android.settings"; private static final String LOCK_PATTERN_CLASS = "com.android.settings.ChooseLockPattern"; @@ -62,7 +63,6 @@ public class AccountUnlockScreen extends RelativeLayout implements KeyguardScree private final KeyguardScreenCallback mCallback; private final LockPatternUtils mLockPatternUtils; - private IAccountsService mAccountsService; private TextView mTopHeader; private TextView mInstructions; @@ -72,14 +72,16 @@ public class AccountUnlockScreen extends RelativeLayout implements KeyguardScree private Button mEmergencyCall; /** + * Shown while making asynchronous check of password. + */ + private ProgressDialog mCheckingDialog; + + /** * AccountUnlockScreen constructor. - * - * @throws IllegalStateException if the IAccountsService is not - * available on the current platform. */ public AccountUnlockScreen(Context context, - KeyguardScreenCallback callback, - LockPatternUtils lockPatternUtils) { + KeyguardScreenCallback callback, + LockPatternUtils lockPatternUtils) { super(context); mCallback = callback; mLockPatternUtils = lockPatternUtils; @@ -88,6 +90,9 @@ public class AccountUnlockScreen extends RelativeLayout implements KeyguardScree 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); @@ -103,14 +108,6 @@ public class AccountUnlockScreen extends RelativeLayout implements KeyguardScree mEmergencyCall = (Button) findViewById(R.id.emergencyCall); mEmergencyCall.setOnClickListener(this); - - Log.v("AccountUnlockScreen", "debug: Connecting to accounts service"); - final boolean connected = mContext.bindService(AccountsServiceConstants.SERVICE_INTENT, - this, Context.BIND_AUTO_CREATE); - if (!connected) { - Log.v("AccountUnlockScreen", "debug: Couldn't connect to accounts service"); - throw new IllegalStateException("couldn't bind to accounts service"); - } } public void afterTextChanged(Editable s) { @@ -150,30 +147,16 @@ public class AccountUnlockScreen extends RelativeLayout implements KeyguardScree /** {@inheritDoc} */ public void cleanUp() { - mContext.unbindService(this); + if (mCheckingDialog != null) { + mCheckingDialog.hide(); + } } /** {@inheritDoc} */ public void onClick(View v) { mCallback.pokeWakelock(); if (v == mOk) { - if (checkPassword()) { - // clear out forgotten password - mLockPatternUtils.setPermanentlyLocked(false); - - // 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); - - // close the keyguard - mCallback.keyguardDone(true); - } else { - mInstructions.setText(R.string.lockscreen_glogin_invalid_input); - mPassword.setText(""); - } + asyncCheckPassword(); } if (v == mEmergencyCall) { @@ -181,11 +164,37 @@ public class AccountUnlockScreen extends RelativeLayout implements KeyguardScree } } + 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); + + // close the keyguard + mCallback.keyguardDone(true); + } else { + mInstructions.setText(R.string.lockscreen_glogin_invalid_input); + mPassword.setText(""); + } + } + @Override public boolean dispatchKeyEvent(KeyEvent event) { if (event.getAction() == KeyEvent.ACTION_DOWN && event.getKeyCode() == KeyEvent.KEYCODE_BACK) { - mCallback.goToLockScreen(); + if (mLockPatternUtils.isPermanentlyLocked()) { + mCallback.goToLockScreen(); + } else { + mCallback.forgotPattern(false); + } return true; } return super.dispatchKeyEvent(event); @@ -207,33 +216,25 @@ public class AccountUnlockScreen extends RelativeLayout implements KeyguardScree * @return an account name from the database, or null if we can't * find a single best match. */ - private String findIntendedAccount(String username) { - String[] accounts = null; - try { - accounts = mAccountsService.getAccounts(); - } catch (RemoteException e) { - return null; - } - if (accounts == null) { - return null; - } + private Account findIntendedAccount(String username) { + Account[] accounts = AccountManager.get(mContext).getAccounts(); // Try to figure out which account they meant if they // typed only the username (and not the domain), or got // the case wrong. - String bestAccount = null; + Account bestAccount = null; int bestScore = 0; - for (String a: accounts) { + for (Account a: accounts) { int score = 0; - if (username.equals(a)) { + if (username.equals(a.name)) { score = 4; - } else if (username.equalsIgnoreCase(a)) { + } else if (username.equalsIgnoreCase(a.name)) { score = 3; } else if (username.indexOf('@') < 0) { - int i = a.indexOf('@'); + int i = a.name.indexOf('@'); if (i >= 0) { - String aUsername = a.substring(0, i); + String aUsername = a.name.substring(0, i); if (username.equals(aUsername)) { score = 2; } else if (username.equalsIgnoreCase(aUsername)) { @@ -251,28 +252,64 @@ public class AccountUnlockScreen extends RelativeLayout implements KeyguardScree return bestAccount; } - private boolean checkPassword() { + private void asyncCheckPassword() { + mCallback.pokeWakelock(AWAKE_POKE_MILLIS); final String login = mLogin.getText().toString(); final String password = mPassword.getText().toString(); - try { - String account = findIntendedAccount(login); - if (account == null) { - return false; - } - return mAccountsService.shouldUnlock(account, password); - } catch (RemoteException e) { - return false; + 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 */); } - /** {@inheritDoc} */ - public void onServiceConnected(ComponentName name, IBinder service) { - Log.v("AccountUnlockScreen", "debug: About to grab as interface"); - mAccountsService = IAccountsService.Stub.asInterface(service); - } - - /** {@inheritDoc} */ - public void onServiceDisconnected(ComponentName name) { - mAccountsService = null; + 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; } } diff --git a/policy/com/android/internal/policy/impl/GlobalActions.java b/policy/com/android/internal/policy/impl/GlobalActions.java index 377ff78..2f1f024 100644 --- a/policy/com/android/internal/policy/impl/GlobalActions.java +++ b/policy/com/android/internal/policy/impl/GlobalActions.java @@ -16,6 +16,7 @@ package com.android.internal.policy.impl; +import android.app.Activity; import android.app.AlertDialog; import android.app.StatusBarManager; import android.content.BroadcastReceiver; @@ -26,6 +27,7 @@ import android.content.IntentFilter; import android.media.AudioManager; import android.os.Handler; import android.os.Message; +import android.os.SystemProperties; import android.provider.Settings; import android.telephony.PhoneStateListener; import android.telephony.ServiceState; @@ -40,6 +42,8 @@ import android.widget.ImageView; import android.widget.TextView; import com.android.internal.R; import com.android.internal.app.ShutdownThread; +import com.android.internal.telephony.TelephonyIntents; +import com.android.internal.telephony.TelephonyProperties; import com.google.android.collect.Lists; import java.util.ArrayList; @@ -69,6 +73,7 @@ class GlobalActions implements DialogInterface.OnDismissListener, DialogInterfac private boolean mKeyguardShowing = false; private boolean mDeviceProvisioned = false; private ToggleAction.State mAirplaneState = ToggleAction.State.Off; + private boolean mIsWaitingForEcmExit = false; /** * @param context everything needs a context :( @@ -81,6 +86,7 @@ class GlobalActions implements DialogInterface.OnDismissListener, DialogInterfac IntentFilter filter = new IntentFilter(); filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS); filter.addAction(Intent.ACTION_SCREEN_OFF); + filter.addAction(TelephonyIntents.ACTION_EMERGENCY_CALLBACK_MODE_CHANGED); context.registerReceiver(mBroadcastReceiver, filter); // get notified of phone state changes @@ -141,20 +147,27 @@ class GlobalActions implements DialogInterface.OnDismissListener, DialogInterfac R.string.global_actions_airplane_mode_off_status) { void onToggle(boolean on) { - // Change the system setting - Settings.System.putInt( - mContext.getContentResolver(), - Settings.System.AIRPLANE_MODE_ON, - on ? 1 : 0); - Intent intent = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED); - intent.putExtra("state", on); - mContext.sendBroadcast(intent); + if (Boolean.parseBoolean( + SystemProperties.get(TelephonyProperties.PROPERTY_INECM_MODE))) { + mIsWaitingForEcmExit = true; + // Launch ECM exit dialog + Intent ecmDialogIntent = + new Intent(TelephonyIntents.ACTION_SHOW_NOTICE_ECM_BLOCK_OTHERS, null); + ecmDialogIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + mContext.startActivity(ecmDialogIntent); + } else { + changeAirplaneModeSystemSetting(on); + } } @Override protected void changeStateFromPress(boolean buttonOn) { - mState = buttonOn ? State.TurningOn : State.TurningOff; - mAirplaneState = mState; + // In ECM mode airplane state cannot be changed + if (!(Boolean.parseBoolean( + SystemProperties.get(TelephonyProperties.PROPERTY_INECM_MODE)))) { + mState = buttonOn ? State.TurningOn : State.TurningOff; + mAirplaneState = mState; + } } public boolean showDuringKeyguard() { @@ -200,8 +213,11 @@ class GlobalActions implements DialogInterface.OnDismissListener, DialogInterfac final AlertDialog dialog = ab.create(); dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG); - dialog.getWindow().setFlags(WindowManager.LayoutParams.FLAG_BLUR_BEHIND, - WindowManager.LayoutParams.FLAG_BLUR_BEHIND); + 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.setOnDismissListener(this); @@ -218,7 +234,7 @@ class GlobalActions implements DialogInterface.OnDismissListener, DialogInterfac if (mKeyguardShowing) { mDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG); } else { - mDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG); + mDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG); } } @@ -490,6 +506,14 @@ class GlobalActions implements DialogInterface.OnDismissListener, DialogInterfac if (!PhoneWindowManager.SYSTEM_DIALOG_REASON_GLOBAL_ACTIONS.equals(reason)) { mHandler.sendEmptyMessage(MESSAGE_DISMISS); } + } else if (TelephonyIntents.ACTION_EMERGENCY_CALLBACK_MODE_CHANGED.equals(action)) { + // Airplane mode can be changed after ECM exits if airplane toggle button + // is pressed during ECM mode + if (!(intent.getBooleanExtra("PHONE_IN_ECM_STATE", false)) && + mIsWaitingForEcmExit) { + mIsWaitingForEcmExit = false; + changeAirplaneModeSystemSetting(true); + } } } }; @@ -514,4 +538,17 @@ class GlobalActions implements DialogInterface.OnDismissListener, DialogInterfac } } }; + + /** + * Change the airplane mode system setting + */ + private void changeAirplaneModeSystemSetting(boolean on) { + Settings.System.putInt( + mContext.getContentResolver(), + Settings.System.AIRPLANE_MODE_ON, + on ? 1 : 0); + Intent intent = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED); + intent.putExtra("state", on); + mContext.sendBroadcast(intent); + } } diff --git a/policy/com/android/internal/policy/impl/KeyguardScreenCallback.java b/policy/com/android/internal/policy/impl/KeyguardScreenCallback.java index b46b37d..6bb6a45 100644 --- a/policy/com/android/internal/policy/impl/KeyguardScreenCallback.java +++ b/policy/com/android/internal/policy/impl/KeyguardScreenCallback.java @@ -28,11 +28,20 @@ public interface KeyguardScreenCallback extends KeyguardViewCallback { void goToLockScreen(); /** - * Transitino to th unlock screen. + * Transition to the unlock screen. */ void goToUnlockScreen(); /** + * The user reported that they forgot their pattern (or not, when they want to back out of the + * forgot pattern screen). + * + * @param isForgotten True if the user hit the forgot pattern, false if they want to back out + * of the account screen. + */ + void forgotPattern(boolean isForgotten); + + /** * @return Whether the keyguard requires some sort of PIN. */ boolean isSecure(); diff --git a/policy/com/android/internal/policy/impl/KeyguardUpdateMonitor.java b/policy/com/android/internal/policy/impl/KeyguardUpdateMonitor.java index cd21427..58905a1 100644 --- a/policy/com/android/internal/policy/impl/KeyguardUpdateMonitor.java +++ b/policy/com/android/internal/policy/impl/KeyguardUpdateMonitor.java @@ -35,7 +35,6 @@ import static android.provider.Telephony.Intents.EXTRA_SHOW_SPN; import static android.provider.Telephony.Intents.EXTRA_SPN; import static android.provider.Telephony.Intents.SPN_STRINGS_UPDATED_ACTION; -import com.android.internal.app.ShutdownThread; import com.android.internal.telephony.IccCard; import com.android.internal.telephony.TelephonyIntents; import android.util.Log; @@ -67,6 +66,8 @@ public class KeyguardUpdateMonitor { private boolean mInPortrait; private boolean mKeyboardOpen; + private boolean mKeyguardBypassEnabled; + private boolean mDevicePluggedIn; private boolean mDeviceProvisioned; @@ -163,6 +164,9 @@ public class KeyguardUpdateMonitor { } }; + mKeyguardBypassEnabled = context.getResources().getBoolean( + com.android.internal.R.bool.config_bypass_keyguard_if_slider_open); + mDeviceProvisioned = Settings.Secure.getInt( mContext.getContentResolver(), Settings.Secure.DEVICE_PROVISIONED, 0) != 0; @@ -295,13 +299,6 @@ public class KeyguardUpdateMonitor { shouldShowBatteryInfo(), pluggedIn, batteryLevel); } } - - // shut down gracefully if our battery is critically low and we are not powered - if (batteryLevel == 0 && - pluggedInStatus != BATTERY_STATUS_CHARGING && - pluggedInStatus != BATTERY_STATUS_UNKNOWN) { - ShutdownThread.shutdown(mContext, false); - } } /** @@ -513,6 +510,10 @@ public class KeyguardUpdateMonitor { return mKeyboardOpen; } + public boolean isKeyguardBypassEnabled() { + return mKeyguardBypassEnabled; + } + public boolean isDevicePluggedIn() { return mDevicePluggedIn; } diff --git a/policy/com/android/internal/policy/impl/KeyguardViewManager.java b/policy/com/android/internal/policy/impl/KeyguardViewManager.java index 297d62f..bac2fcad 100644 --- a/policy/com/android/internal/policy/impl/KeyguardViewManager.java +++ b/policy/com/android/internal/policy/impl/KeyguardViewManager.java @@ -48,7 +48,7 @@ public class KeyguardViewManager implements KeyguardWindowController { private WindowManager.LayoutParams mWindowLayoutParams; private boolean mNeedsInput = false; - + private FrameLayout mKeyguardHost; private KeyguardViewBase mKeyguardView; @@ -101,6 +101,8 @@ public class KeyguardViewManager implements KeyguardWindowController { final int stretch = ViewGroup.LayoutParams.FILL_PARENT; int flags = WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN + | WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER + | WindowManager.LayoutParams.FLAG_KEEP_SURFACE_WHILE_ANIMATING /*| WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN | WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR*/ ; if (!mNeedsInput) { @@ -108,7 +110,7 @@ public class KeyguardViewManager implements KeyguardWindowController { } WindowManager.LayoutParams lp = new WindowManager.LayoutParams( stretch, stretch, WindowManager.LayoutParams.TYPE_KEYGUARD, - flags, PixelFormat.OPAQUE); + flags, PixelFormat.TRANSLUCENT); lp.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN; lp.windowAnimations = com.android.internal.R.style.Animation_LockScreen; lp.screenOrientation = ActivityInfo.SCREEN_ORIENTATION_NOSENSOR; @@ -152,7 +154,7 @@ public class KeyguardViewManager implements KeyguardWindowController { mViewManager.updateViewLayout(mKeyguardHost, mWindowLayoutParams); } } - + /** * Reset the state of the view. */ @@ -195,10 +197,14 @@ public class KeyguardViewManager implements KeyguardWindowController { * * @param keyCode The wake key. */ - public void wakeWhenReadyTq(int keyCode) { + public boolean wakeWhenReadyTq(int keyCode) { if (DEBUG) Log.d(TAG, "wakeWhenReady(" + keyCode + ")"); if (mKeyguardView != null) { mKeyguardView.wakeWhenReadyTq(keyCode); + return true; + } else { + Log.w(TAG, "mKeyguardView is null in wakeWhenReadyTq"); + return false; } } @@ -209,10 +215,19 @@ public class KeyguardViewManager implements KeyguardWindowController { if (DEBUG) Log.d(TAG, "hide()"); if (mKeyguardHost != null) { mKeyguardHost.setVisibility(View.GONE); + // Don't do this right away, so we can let the view continue to animate + // as it goes away. if (mKeyguardView != null) { - mKeyguardHost.removeView(mKeyguardView); - mKeyguardView.cleanUp(); + final KeyguardViewBase lastView = mKeyguardView; mKeyguardView = null; + mKeyguardHost.postDelayed(new Runnable() { + public void run() { + synchronized (KeyguardViewManager.this) { + mKeyguardHost.removeView(lastView); + lastView.cleanUp(); + } + } + }, 500); } } } diff --git a/policy/com/android/internal/policy/impl/KeyguardViewMediator.java b/policy/com/android/internal/policy/impl/KeyguardViewMediator.java index 043f727..f5591b2 100644 --- a/policy/com/android/internal/policy/impl/KeyguardViewMediator.java +++ b/policy/com/android/internal/policy/impl/KeyguardViewMediator.java @@ -19,11 +19,9 @@ package com.android.internal.policy.impl; import com.android.internal.telephony.IccCard; import com.android.internal.widget.LockPatternUtils; +import android.app.ActivityManagerNative; import android.app.AlarmManager; import android.app.PendingIntent; -import android.app.StatusBarManager; -import static android.app.StatusBarManager.DISABLE_EXPAND; -import static android.app.StatusBarManager.DISABLE_NONE; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; @@ -32,7 +30,9 @@ import android.os.Handler; import android.os.LocalPowerManager; import android.os.Message; import android.os.PowerManager; +import android.os.RemoteException; import android.os.SystemClock; +import android.os.SystemProperties; import android.telephony.TelephonyManager; import android.util.Config; import android.util.EventLog; @@ -84,7 +84,7 @@ import android.view.WindowManagerPolicy; * thread of the keyguard. */ public class KeyguardViewMediator implements KeyguardViewCallback, - KeyguardUpdateMonitor.ConfigurationChangeCallback, KeyguardUpdateMonitor.SimStateCallback { + KeyguardUpdateMonitor.SimStateCallback { private final static boolean DEBUG = false && Config.LOGD; private final static boolean DBG_WAKE = DEBUG || true; @@ -104,6 +104,7 @@ public class KeyguardViewMediator implements KeyguardViewCallback, private static final int WAKE_WHEN_READY = 8; private static final int KEYGUARD_DONE = 9; private static final int KEYGUARD_DONE_DRAWING = 10; + private static final int KEYGUARD_DONE_AUTHENTICATING = 11; /** * The default amount of time we stay awake (used for all key input) @@ -122,7 +123,7 @@ public class KeyguardViewMediator implements KeyguardViewCallback, * turning on the keyguard (i.e, the user has this much time to turn * the screen back on without having to face the keyguard). */ - private static final int KEYGUARD_DELAY_MS = 0; + private static final int KEYGUARD_DELAY_MS = 5000; /** * How long we'll wait for the {@link KeyguardViewCallback#keyguardDoneDrawing()} @@ -150,6 +151,12 @@ public class KeyguardViewMediator implements KeyguardViewCallback, private PowerManager.WakeLock mWakeLock; /** + * Used to keep the device awake while to ensure the keyguard finishes opening before + * we sleep. + */ + private PowerManager.WakeLock mShowKeyguardWakeLock; + + /** * Does not turn on screen, held while a call to {@link KeyguardViewManager#wakeWhenReadyTq(int)} * is called to make sure the device doesn't sleep before it has a chance to poke * the wake lock. @@ -157,11 +164,6 @@ public class KeyguardViewMediator implements KeyguardViewCallback, */ private PowerManager.WakeLock mWakeAndHandOff; - /** - * Used to disable / reenable status bar expansion. - */ - private StatusBarManager mStatusBarManager; - private KeyguardViewManager mKeyguardViewManager; // these are protected by synchronized (this) @@ -182,6 +184,9 @@ public class KeyguardViewMediator implements KeyguardViewCallback, // answer whether the input should be restricted) private boolean mShowing = false; + // true if the keyguard is hidden by another window + private boolean mHidden = false; + /** * Helps remember whether the screen has turned on since the last time * it turned off due to timeout. see {@link #onScreenTurnedOff(int)} @@ -229,6 +234,8 @@ public class KeyguardViewMediator implements KeyguardViewCallback, PowerManager.FULL_WAKE_LOCK | PowerManager.ACQUIRE_CAUSES_WAKEUP, "keyguard"); mWakeLock.setReferenceCounted(false); + mShowKeyguardWakeLock = mPM.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "show keyguard"); + mShowKeyguardWakeLock.setReferenceCounted(false); mWakeAndHandOff = mPM.newWakeLock( PowerManager.PARTIAL_WAKE_LOCK, @@ -245,7 +252,6 @@ public class KeyguardViewMediator implements KeyguardViewCallback, mUpdateMonitor = new KeyguardUpdateMonitor(context); - mUpdateMonitor.registerConfigurationChangeCallback(this); mUpdateMonitor.registerSimStateCallback(this); mKeyguardViewProperties = @@ -347,14 +353,12 @@ public class KeyguardViewMediator implements KeyguardViewCallback, if (DEBUG) Log.d(TAG, "remembering to reshow, hiding keyguard, " + "disabling status bar expansion"); mNeedToReshowWhenReenabled = true; - setStatusBarExpandable(false); hideLocked(); } else if (enabled && mNeedToReshowWhenReenabled) { // reenabled after previously hidden, reshow if (DEBUG) Log.d(TAG, "previously hidden, reshowing, reenabling " + "status bar expansion"); mNeedToReshowWhenReenabled = false; - setStatusBarExpandable(true); if (mExitSecureCallback != null) { if (DEBUG) Log.d(TAG, "onKeyguardExitResult(false), resetting"); @@ -409,15 +413,6 @@ public class KeyguardViewMediator implements KeyguardViewCallback, } } - - private void setStatusBarExpandable(boolean isExpandable) { - if (mStatusBarManager == null) { - mStatusBarManager = - (StatusBarManager) mContext.getSystemService(Context.STATUS_BAR_SERVICE); - } - mStatusBarManager.disable(isExpandable ? DISABLE_NONE : DISABLE_EXPAND); - } - /** * Is the keyguard currently showing? */ @@ -426,6 +421,17 @@ public class KeyguardViewMediator implements KeyguardViewCallback, } /** + * Notify us when the keyguard is hidden by another window + */ + public void setHidden(boolean isHidden) { + if (DEBUG) Log.d(TAG, "setHidden " + isHidden); + synchronized (KeyguardViewMediator.this) { + mHidden = isHidden; + adjustUserActivityLocked(); + } + } + + /** * Given the state of the keyguard, is the input restricted? * Input is restricted when the keyguard is showing, or when the keyguard * was suppressed by an app that disabled the keyguard or we haven't been provisioned yet. @@ -434,6 +440,21 @@ public class KeyguardViewMediator implements KeyguardViewCallback, return mShowing || mNeedToReshowWhenReenabled || !mUpdateMonitor.isDeviceProvisioned(); } + /** + * Returns true if the change is resulting in the keyguard beign dismissed, + * meaning the screen can turn on immediately. Otherwise returns false. + */ + public boolean doLidChangeTq(boolean isLidOpen) { + mKeyboardOpen = isLidOpen; + + if (mUpdateMonitor.isKeyguardBypassEnabled() && mKeyboardOpen + && !mKeyguardViewProperties.isSecure() && mKeyguardViewManager.isShowing()) { + if (DEBUG) Log.d(TAG, "bypassing keyguard on sliding open of keyboard with non-secure keyguard"); + mHandler.sendEmptyMessage(KEYGUARD_DONE_AUTHENTICATING); + return true; + } + return false; + } /** * Enable the keyguard if the settings are appropriate. @@ -463,9 +484,13 @@ public class KeyguardViewMediator implements KeyguardViewCallback, } // if the setup wizard hasn't run yet, don't show + final boolean requireSim = !SystemProperties.getBoolean("keyguard.no_require_sim", + false); final boolean provisioned = mUpdateMonitor.isDeviceProvisioned(); final IccCard.State state = mUpdateMonitor.getSimState(); - final boolean lockedOrMissing = state.isPinLocked() || (state == IccCard.State.ABSENT); + final boolean lockedOrMissing = state.isPinLocked() + || ((state == IccCard.State.ABSENT) && requireSim); + if (!lockedOrMissing && !provisioned) { if (DEBUG) Log.d(TAG, "doKeyguard: not showing because device isn't provisioned" + " and the sim is not locked or missing"); @@ -544,6 +569,8 @@ public class KeyguardViewMediator implements KeyguardViewCallback, */ private void showLocked() { if (DEBUG) Log.d(TAG, "showLocked"); + // ensure we stay awake until we are finished displaying the keyguard + mShowKeyguardWakeLock.acquire(); Message msg = mHandler.obtainMessage(SHOW); mHandler.sendMessage(msg); } @@ -558,26 +585,6 @@ public class KeyguardViewMediator implements KeyguardViewCallback, mHandler.sendMessage(msg); } - /** - * {@link KeyguardUpdateMonitor} callbacks. - */ - - /** {@inheritDoc} */ - public void onOrientationChange(boolean inPortrait) { - - } - - /** {@inheritDoc} */ - public void onKeyboardChange(boolean isKeyboardOpen) { - mKeyboardOpen = isKeyboardOpen; - - if (mKeyboardOpen && !mKeyguardViewProperties.isSecure() - && mKeyguardViewManager.isShowing()) { - if (DEBUG) Log.d(TAG, "bypassing keyguard on sliding open of keyboard with non-secure keyguard"); - keyguardDone(true); - } - } - /** {@inheritDoc} */ public void onSimStateChanged(IccCard.State simState) { if (DEBUG) Log.d(TAG, "onSimStateChanged: " + simState); @@ -615,6 +622,10 @@ public class KeyguardViewMediator implements KeyguardViewCallback, } } + public boolean isSecure() { + return mKeyguardViewProperties.isSecure(); + } + private BroadcastReceiver mBroadCastReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { @@ -719,10 +730,15 @@ public class KeyguardViewMediator implements KeyguardViewCallback, * @see #handleKeyguardDone */ public void keyguardDone(boolean authenticated) { + keyguardDone(authenticated, true); + } + + public void keyguardDone(boolean authenticated, boolean wakeup) { synchronized (this) { EventLog.writeEvent(70000, 2); if (DEBUG) Log.d(TAG, "keyguardDone(" + authenticated + ")"); Message msg = mHandler.obtainMessage(KEYGUARD_DONE); + msg.arg1 = wakeup ? 1 : 0; mHandler.sendMessage(msg); if (authenticated) { @@ -738,7 +754,6 @@ public class KeyguardViewMediator implements KeyguardViewCallback, // the keyguard when they've released the lock mExternallyEnabled = true; mNeedToReshowWhenReenabled = false; - setStatusBarExpandable(true); } } } @@ -789,10 +804,14 @@ public class KeyguardViewMediator implements KeyguardViewCallback, handleWakeWhenReady(msg.arg1); return; case KEYGUARD_DONE: - handleKeyguardDone(); + handleKeyguardDone(msg.arg1 != 0); return; case KEYGUARD_DONE_DRAWING: handleKeyguardDoneDrawing(); + return; + case KEYGUARD_DONE_AUTHENTICATING: + keyguardDone(true); + return; } } }; @@ -801,10 +820,12 @@ public class KeyguardViewMediator implements KeyguardViewCallback, * @see #keyguardDone * @see #KEYGUARD_DONE */ - private void handleKeyguardDone() { + private void handleKeyguardDone(boolean wakeup) { if (DEBUG) Log.d(TAG, "handleKeyguardDone"); handleHide(); - mPM.userActivity(SystemClock.uptimeMillis(), true); + if (wakeup) { + mPM.userActivity(SystemClock.uptimeMillis(), true); + } mWakeLock.release(); mContext.sendBroadcast(mUserPresentIntent); } @@ -853,12 +874,14 @@ public class KeyguardViewMediator implements KeyguardViewCallback, if (DEBUG) Log.d(TAG, "handleShow"); if (!mSystemReady) return; - // while we're showing, we control the wake state, so ask the power - // manager not to honor request for userActivity. - mRealPowerManager.enableUserActivity(false); - mKeyguardViewManager.show(); mShowing = true; + adjustUserActivityLocked(); + try { + ActivityManagerNative.getDefault().closeSystemDialogs("lock"); + } catch (RemoteException e) { + } + mShowKeyguardWakeLock.release(); } } @@ -869,11 +892,25 @@ public class KeyguardViewMediator implements KeyguardViewCallback, private void handleHide() { synchronized (KeyguardViewMediator.this) { if (DEBUG) Log.d(TAG, "handleHide"); - // When we go away, tell the poewr manager to honor requests from userActivity. - mRealPowerManager.enableUserActivity(true); + if (mWakeAndHandOff.isHeld()) { + Log.w(TAG, "attempt to hide the keyguard while waking, ignored"); + return; + } mKeyguardViewManager.hide(); mShowing = false; + adjustUserActivityLocked(); + } + } + + private void adjustUserActivityLocked() { + // disable user activity if we are shown and not hidden + if (DEBUG) Log.d(TAG, "adjustUserActivityLocked mShowing: " + mShowing + " mHidden: " + mHidden); + boolean enabled = !mShowing || mHidden; + mRealPowerManager.enableUserActivity(enabled); + if (!enabled && mScreenOn) { + // reinstate our short screen timeout policy + pokeWakelock(); } } @@ -888,7 +925,11 @@ public class KeyguardViewMediator implements KeyguardViewCallback, // this should result in a call to 'poke wakelock' which will set a timeout // on releasing the wakelock - mKeyguardViewManager.wakeWhenReadyTq(keyCode); + if (!mKeyguardViewManager.wakeWhenReadyTq(keyCode)) { + // poke wakelock ourselves if keyguard is no longer active + Log.w(TAG, "mKeyguardViewManager.wakeWhenReadyTq did not poke wake lock, so poke it ourselves"); + pokeWakelock(); + } /** * Now that the keyguard is ready and has poked the wake lock, we can @@ -897,7 +938,7 @@ public class KeyguardViewMediator implements KeyguardViewCallback, mWakeAndHandOff.release(); if (!mWakeLock.isHeld()) { - Log.w(TAG, "mKeyguardViewManager.wakeWhenReadyTq did not poke wake lock"); + Log.w(TAG, "mWakeLock not held in mKeyguardViewManager.wakeWhenReadyTq"); } } } diff --git a/policy/com/android/internal/policy/impl/LockPatternKeyguardView.java b/policy/com/android/internal/policy/impl/LockPatternKeyguardView.java index 6a1c279..0ebd945 100644 --- a/policy/com/android/internal/policy/impl/LockPatternKeyguardView.java +++ b/policy/com/android/internal/policy/impl/LockPatternKeyguardView.java @@ -16,41 +16,47 @@ package com.android.internal.policy.impl; -import android.accounts.AccountsServiceConstants; -import android.accounts.IAccountsService; +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.content.ComponentName; import android.content.Context; import android.content.Intent; -import android.content.ServiceConnection; -import android.os.IBinder; -import android.os.RemoteException; +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.ColorFilter; +import android.graphics.PixelFormat; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.Drawable; import android.os.SystemProperties; -import com.android.internal.telephony.IccCard; 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; + +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 + * 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} + * 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 { +public class LockPatternKeyguardView extends KeyguardViewBase + implements AccountManagerCallback<Account[]> { // intent action for launching emergency dialer activity. static final String ACTION_EMERGENCY_DIAL = "com.android.phone.EmergencyDialer.DIAL"; @@ -60,12 +66,12 @@ public class LockPatternKeyguardView extends KeyguardViewBase { 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 + private boolean mEnableFallback = false; // assume no fallback UI until we know better /** @@ -114,10 +120,13 @@ public class LockPatternKeyguardView extends KeyguardViewBase { private Mode mMode = Mode.LockScreen; /** - * Keeps track of what mode the current unlock screen is + * 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()} @@ -131,11 +140,6 @@ public class LockPatternKeyguardView extends KeyguardViewBase { 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. */ @@ -145,6 +149,22 @@ public class LockPatternKeyguardView extends KeyguardViewBase { && (mUpdateMonitor.getSimState() == IccCard.State.ABSENT); } + public void run(AccountManagerFuture<Account[]> future) { + // We err on the side of caution. + // In case of error we assume we have a SAML account. + boolean hasSAMLAccount = true; + try { + hasSAMLAccount = future.getResult().length > 0; + } catch (OperationCanceledException e) { + } catch (IOException e) { + } catch (AuthenticatorException e) { + } + mEnableFallback = !hasSAMLAccount; + if (mUnlockScreen instanceof UnlockScreen) { + ((UnlockScreen)mUnlockScreen).setEnableFallback(true); + } + } + /** * @param context Used to inflate, and create views. * @param updateMonitor Knows the state of the world, and passed along to each @@ -158,9 +178,21 @@ public class LockPatternKeyguardView extends KeyguardViewBase { LockPatternUtils lockPatternUtils, KeyguardWindowController controller) { super(context); + + final boolean hasAccount = AccountManager.get(context).getAccounts().length > 0; + if (hasAccount) { + /* If we have a SAML account which requires web login we can not use the + fallback screen UI to ask the user for credentials. + For now we will disable fallback screen in this case. + Ultimately we could consider bringing up a web login from GLS + but need to make sure that it will work in the "locked screen" mode. */ + String[] features = new String[] {"saml"}; + AccountManager.get(context).getAccountsByTypeAndFeatures( + "com.google", features, this, null); + } - asyncCheckForAccount(); - + mEnableFallback = false; + mRequiresSim = TextUtils.isEmpty(SystemProperties.get("keyguard.no_require_sim")); @@ -169,10 +201,11 @@ public class LockPatternKeyguardView extends KeyguardViewBase { 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. @@ -197,6 +230,13 @@ public class LockPatternKeyguardView extends KeyguardViewBase { } } + public void forgotPattern(boolean isForgotten) { + if (mEnableFallback) { + mForgotPattern = isForgotten; + updateScreen(Mode.UnlockScreen); + } + } + public boolean isSecure() { return LockPatternKeyguardView.this.isSecure(); } @@ -235,11 +275,11 @@ public class LockPatternKeyguardView extends KeyguardViewBase { public void reportFailedPatternAttempt() { mUpdateMonitor.reportFailedAttempt(); final int failedAttempts = mUpdateMonitor.getFailedAttempts(); - if (mHasAccount && failedAttempts == - (LockPatternUtils.FAILED_ATTEMPTS_BEFORE_RESET + if (mEnableFallback && failedAttempts == + (LockPatternUtils.FAILED_ATTEMPTS_BEFORE_RESET - LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT)) { showAlmostAtAccountLoginDialog(); - } else if (mHasAccount + } else if (mEnableFallback && failedAttempts >= LockPatternUtils.FAILED_ATTEMPTS_BEFORE_RESET) { mLockPatternUtils.setPermanentlyLocked(true); updateScreen(mMode); @@ -248,9 +288,9 @@ public class LockPatternKeyguardView extends KeyguardViewBase { showTimeoutDialog(); } } - + public boolean doesFallbackUnlockScreenExist() { - return mHasAccount; + return mEnableFallback; } }; @@ -262,9 +302,11 @@ public class LockPatternKeyguardView extends KeyguardViewBase { setDescendantFocusability(FOCUS_AFTER_DESCENDANTS); // wall paper background - final BitmapDrawable drawable = (BitmapDrawable) context.getWallpaper(); - setBackgroundDrawable( - new FastBitmapDrawable(drawable.getBitmap())); + if (false) { + 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 @@ -277,45 +319,18 @@ public class LockPatternKeyguardView extends KeyguardViewBase { 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; + mForgotPattern = false; updateScreen(getInitialMode()); } @Override public void onScreenTurnedOff() { mScreenOn = false; + mForgotPattern = false; if (mMode == Mode.LockScreen) { ((KeyguardScreen) mLockScreen).onPause(); } else { @@ -423,7 +438,7 @@ public class LockPatternKeyguardView extends KeyguardViewBase { // 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) { @@ -454,12 +469,14 @@ public class LockPatternKeyguardView extends KeyguardViewBase { View createUnlockScreenFor(UnlockMode unlockMode) { if (unlockMode == UnlockMode.Pattern) { - return new UnlockScreen( + UnlockScreen view = new UnlockScreen( mContext, mLockPatternUtils, mUpdateMonitor, mKeyguardScreenCallback, mUpdateMonitor.getFailedAttempts()); + view.setEnableFallback(mEnableFallback); + return view; } else if (unlockMode == UnlockMode.SimPin) { return new SimUnlockScreen( mContext, @@ -525,7 +542,7 @@ public class LockPatternKeyguardView extends KeyguardViewBase { final IccCard.State simState = mUpdateMonitor.getSimState(); if (stuckOnLockScreenBecauseSimMissing() || (simState == IccCard.State.PUK_REQUIRED)) { return Mode.LockScreen; - } else if (mUpdateMonitor.isKeyboardOpen() && isSecure()) { + } else if (isSecure()) { return Mode.UnlockScreen; } else { return Mode.LockScreen; @@ -540,7 +557,7 @@ public class LockPatternKeyguardView extends KeyguardViewBase { if (simState == IccCard.State.PIN_REQUIRED || simState == IccCard.State.PUK_REQUIRED) { return UnlockMode.SimPin; } else { - return mLockPatternUtils.isPermanentlyLocked() ? + return (mForgotPattern || mLockPatternUtils.isPermanentlyLocked()) ? UnlockMode.Account: UnlockMode.Pattern; } @@ -558,9 +575,12 @@ public class LockPatternKeyguardView extends KeyguardViewBase { .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); + 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(); } @@ -578,15 +598,20 @@ public class LockPatternKeyguardView extends KeyguardViewBase { .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); + 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 - * vertically. + * 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; @@ -602,7 +627,7 @@ public class LockPatternKeyguardView extends KeyguardViewBase { canvas.drawBitmap( mBitmap, (getBounds().width() - mBitmap.getWidth()) / 2, - (getBounds().height() - mBitmap.getHeight()) / 2, + (getBounds().height() - mBitmap.getHeight()), null); } diff --git a/policy/com/android/internal/policy/impl/LockScreen.java b/policy/com/android/internal/policy/impl/LockScreen.java index 0495a76..dda5097 100644 --- a/policy/com/android/internal/policy/impl/LockScreen.java +++ b/policy/com/android/internal/policy/impl/LockScreen.java @@ -18,21 +18,26 @@ package com.android.internal.policy.impl; import com.android.internal.R; import com.android.internal.widget.LockPatternUtils; +import com.android.internal.widget.RotarySelector; import android.content.Context; +import android.content.res.Resources; import android.text.format.DateFormat; -import android.text.TextUtils; import android.view.KeyEvent; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; -import android.widget.Button; -import android.widget.ImageView; -import android.widget.LinearLayout; -import android.widget.TextView; +import android.widget.*; +import android.graphics.drawable.Drawable; +import android.util.Log; +import android.media.AudioManager; +import android.os.SystemProperties; + import com.android.internal.telephony.IccCard; import java.util.Date; +import java.io.File; +import java.text.SimpleDateFormat; /** * The screen within {@link LockPatternKeyguardView} that shows general @@ -40,40 +45,28 @@ import java.util.Date; * past it, as applicable. */ class LockScreen extends LinearLayout implements KeyguardScreen, KeyguardUpdateMonitor.InfoCallback, - KeyguardUpdateMonitor.SimStateCallback, KeyguardUpdateMonitor.ConfigurationChangeCallback { + KeyguardUpdateMonitor.SimStateCallback, KeyguardUpdateMonitor.ConfigurationChangeCallback, + RotarySelector.OnDialTriggerListener { + + private static final boolean DBG = false; + private static final String TAG = "LockScreen"; + private static final String ENABLE_MENU_KEY_FILE = "/data/local/enable_menu_key"; + + private Status mStatus = Status.Normal; + private final LockPatternUtils mLockPatternUtils; private final KeyguardUpdateMonitor mUpdateMonitor; private final KeyguardScreenCallback mCallback; - private TextView mHeaderSimOk1; - private TextView mHeaderSimOk2; - - private TextView mHeaderSimBad1; - private TextView mHeaderSimBad2; - + private TextView mCarrier; + private RotarySelector mRotary; private TextView mTime; private TextView mDate; - - private ViewGroup mBatteryInfoGroup; - private ImageView mBatteryInfoIcon; - private TextView mBatteryInfoText; - private View mBatteryInfoSpacer; - - private ViewGroup mNextAlarmGroup; - private TextView mAlarmText; - private View mAlarmSpacer; - - private ViewGroup mScreenLockedMessageGroup; - - private TextView mLockInstructions; - + private TextView mStatus1; + private TextView mStatus2; + private TextView mScreenLocked; private Button mEmergencyCallButton; - /** - * false means sim is missing or PUK'd - */ - private boolean mSimOk = true; - // are we showing battery information? private boolean mShowingBatteryInfo = false; @@ -83,10 +76,83 @@ class LockScreen extends LinearLayout implements KeyguardScreen, KeyguardUpdateM // last known battery level private int mBatteryLevel = 100; + private String mNextAlarm = null; + private Drawable mAlarmIcon = null; + private String mCharging = null; + private Drawable mChargingIcon = null; + + private boolean mSilentMode; + private AudioManager mAudioManager; + private java.text.DateFormat mDateFormat; + private java.text.DateFormat mTimeFormat; + private boolean mCreatedInPortrait; + private boolean mEnableMenuKeyInLockScreen; - private View[] mOnlyVisibleWhenSimOk; + /** + * The status of this lock screen. + */ + enum Status { + /** + * Normal case (sim card present, it's not locked) + */ + Normal(true), + + /** + * The sim card is 'network locked'. + */ + NetworkLocked(true), + + /** + * The sim card is missing. + */ + SimMissing(false), + + /** + * The sim card is missing, and this is the device isn't provisioned, so we don't let + * them get past the screen. + */ + SimMissingLocked(false), + + /** + * The sim card is PUK locked, meaning they've entered the wrong sim unlock code too many + * times. + */ + SimPukLocked(false), + + /** + * The sim card is locked. + */ + SimLocked(true); + + private final boolean mShowStatusLines; + + Status(boolean mShowStatusLines) { + this.mShowStatusLines = mShowStatusLines; + } - private View[] mOnlyVisibleWhenSimNotOk; + /** + * @return Whether the status lines (battery level and / or next alarm) are shown while + * in this state. Mostly dictated by whether this is room for them. + */ + public boolean showStatusLines() { + return mShowStatusLines; + } + } + + /** + * In general, we enable unlocking the insecure key guard with the menu key. However, there are + * some cases where we wish to disable it, notably when the menu button placement or technology + * is prone to false positives. + * + * @return true if the menu key should be enabled + */ + private boolean shouldEnableMenuKey() { + final Resources res = getResources(); + final boolean configDisabled = res.getBoolean(R.bool.config_disableMenuKeyInLockScreen); + final boolean isMonkey = SystemProperties.getBoolean("ro.monkey", false); + final boolean fileOverride = (new File(ENABLE_MENU_KEY_FILE)).exists(); + return !configDisabled || isMonkey || fileOverride; + } /** * @param context Used to setup the view. @@ -103,262 +169,391 @@ class LockScreen extends LinearLayout implements KeyguardScreen, KeyguardUpdateM mUpdateMonitor = updateMonitor; mCallback = callback; - final LayoutInflater inflater = LayoutInflater.from(context); - inflater.inflate(R.layout.keyguard_screen_lock, this, true); + mEnableMenuKeyInLockScreen = shouldEnableMenuKey(); - mSimOk = isSimOk(updateMonitor.getSimState()); - mShowingBatteryInfo = updateMonitor.shouldShowBatteryInfo(); - mPluggedIn = updateMonitor.isDevicePluggedIn(); - mBatteryLevel = updateMonitor.getBatteryLevel(); - - mHeaderSimOk1 = (TextView) findViewById(R.id.headerSimOk1); - mHeaderSimOk2 = (TextView) findViewById(R.id.headerSimOk2); + mCreatedInPortrait = updateMonitor.isInPortrait(); - mHeaderSimBad1 = (TextView) findViewById(R.id.headerSimBad1); - mHeaderSimBad2 = (TextView) findViewById(R.id.headerSimBad2); + final LayoutInflater inflater = LayoutInflater.from(context); + if (mCreatedInPortrait) { + inflater.inflate(R.layout.keyguard_screen_rotary_unlock, this, true); + } else { + inflater.inflate(R.layout.keyguard_screen_rotary_unlock_land, this, true); + } + mCarrier = (TextView) findViewById(R.id.carrier); mTime = (TextView) findViewById(R.id.time); mDate = (TextView) findViewById(R.id.date); - - mBatteryInfoGroup = (ViewGroup) findViewById(R.id.batteryInfo); - mBatteryInfoIcon = (ImageView) findViewById(R.id.batteryInfoIcon); - mBatteryInfoText = (TextView) findViewById(R.id.batteryInfoText); - mBatteryInfoSpacer = findViewById(R.id.batteryInfoSpacer); - - mNextAlarmGroup = (ViewGroup) findViewById(R.id.nextAlarmInfo); - mAlarmText = (TextView) findViewById(R.id.nextAlarmText); - mAlarmSpacer = findViewById(R.id.nextAlarmSpacer); - - mScreenLockedMessageGroup = (ViewGroup) findViewById(R.id.screenLockedInfo); - - mLockInstructions = (TextView) findViewById(R.id.lockInstructions); + mStatus1 = (TextView) findViewById(R.id.status1); + mStatus2 = (TextView) findViewById(R.id.status2); mEmergencyCallButton = (Button) findViewById(R.id.emergencyCallButton); - + mEmergencyCallButton.setText(R.string.lockscreen_emergency_call); + mScreenLocked = (TextView) findViewById(R.id.screenLocked); + mRotary = (RotarySelector) findViewById(R.id.rotary); mEmergencyCallButton.setOnClickListener(new View.OnClickListener() { public void onClick(View v) { mCallback.takeEmergencyCallAction(); } }); - mOnlyVisibleWhenSimOk = new View[] { - mHeaderSimOk1, - mHeaderSimOk2, - mBatteryInfoGroup, - mBatteryInfoSpacer, - mNextAlarmGroup, - mAlarmSpacer, - mScreenLockedMessageGroup, - mLockInstructions - }; - - mOnlyVisibleWhenSimNotOk = new View[] { - mHeaderSimBad1, - mHeaderSimBad2, - mEmergencyCallButton - }; - setFocusable(true); setFocusableInTouchMode(true); setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS); - refreshBatteryDisplay(); - refreshAlarmDisplay(); - refreshTimeAndDateDisplay(); - refreshUnlockIntructions(); - refreshViewsWRTSimOk(); - refreshSimOkHeaders(mUpdateMonitor.getTelephonyPlmn(), mUpdateMonitor.getTelephonySpn()); - updateMonitor.registerInfoCallback(this); updateMonitor.registerSimStateCallback(this); updateMonitor.registerConfigurationChangeCallback(this); + + mAudioManager = (AudioManager) getContext().getSystemService(Context.AUDIO_SERVICE); + mSilentMode = mAudioManager.getRingerMode() == AudioManager.RINGER_MODE_SILENT; + + mRotary.setOnDialTriggerListener(this); + mRotary.setLeftHandleResource(R.drawable.ic_jog_dial_unlock); + mRotary.setRightHandleResource(mSilentMode ? + R.drawable.ic_jog_dial_sound_off : + R.drawable.ic_jog_dial_sound_on); + + resetStatusInfo(updateMonitor); } + private void resetStatusInfo(KeyguardUpdateMonitor updateMonitor) { + mShowingBatteryInfo = updateMonitor.shouldShowBatteryInfo(); + mPluggedIn = updateMonitor.isDevicePluggedIn(); + mBatteryLevel = updateMonitor.getBatteryLevel(); + + mStatus = getCurrentStatus(updateMonitor.getSimState()); + updateLayout(mStatus); + + refreshBatteryStringAndIcon(); + refreshAlarmDisplay(); + + mTimeFormat = DateFormat.getTimeFormat(getContext()); + mDateFormat = getLockScreenDateFormat(); + refreshTimeAndDateDisplay(); + updateStatusLines(); + } @Override public boolean onKeyDown(int keyCode, KeyEvent event) { - if (keyCode == KeyEvent.KEYCODE_MENU) { + if (keyCode == KeyEvent.KEYCODE_MENU && mEnableMenuKeyInLockScreen) { mCallback.goToUnlockScreen(); } return false; } - private void refreshViewsWRTSimOk() { - if (mSimOk) { - for (int i = 0; i < mOnlyVisibleWhenSimOk.length; i++) { - final View view = mOnlyVisibleWhenSimOk[i]; - if (view == null) throw new RuntimeException("index " + i + " null"); - view.setVisibility(View.VISIBLE); - } - for (int i = 0; i < mOnlyVisibleWhenSimNotOk.length; i++) { - final View view = mOnlyVisibleWhenSimNotOk[i]; - view.setVisibility(View.GONE); - } - refreshSimOkHeaders(mUpdateMonitor.getTelephonyPlmn(), - mUpdateMonitor.getTelephonySpn()); - refreshAlarmDisplay(); - refreshBatteryDisplay(); - } else { - for (int i = 0; i < mOnlyVisibleWhenSimOk.length; i++) { - final View view = mOnlyVisibleWhenSimOk[i]; - view.setVisibility(View.GONE); - } - for (int i = 0; i < mOnlyVisibleWhenSimNotOk.length; i++) { - final View view = mOnlyVisibleWhenSimNotOk[i]; - view.setVisibility(View.VISIBLE); - } - refreshSimBadInfo(); + /** {@inheritDoc} */ + public void onDialTrigger(View v, int whichHandle) { + if (whichHandle == RotarySelector.OnDialTriggerListener.LEFT_HANDLE) { + mCallback.goToUnlockScreen(); + } else if (whichHandle == RotarySelector.OnDialTriggerListener.RIGHT_HANDLE) { + // toggle silent mode + mSilentMode = !mSilentMode; + mAudioManager.setRingerMode(mSilentMode ? AudioManager.RINGER_MODE_SILENT + : AudioManager.RINGER_MODE_NORMAL); + final int handleIcon = mSilentMode ? + R.drawable.ic_jog_dial_sound_off : + R.drawable.ic_jog_dial_sound_on; + final int toastIcon = mSilentMode ? + R.drawable.ic_lock_ringer_off : + R.drawable.ic_lock_ringer_on; + mRotary.setRightHandleResource(handleIcon); + String message = mSilentMode ? + getContext().getString(R.string.global_action_silent_mode_on_status) : + getContext().getString(R.string.global_action_silent_mode_off_status); + toastMessage(mScreenLocked, message, toastIcon); + mCallback.pokeWakelock(); } } - private void refreshSimBadInfo() { - final IccCard.State simState = mUpdateMonitor.getSimState(); - if (simState == IccCard.State.PUK_REQUIRED) { - mHeaderSimBad1.setText(R.string.lockscreen_sim_puk_locked_message); - mHeaderSimBad2.setText(R.string.lockscreen_sim_puk_locked_instructions); - } else if (simState == IccCard.State.ABSENT) { - mHeaderSimBad1.setText(R.string.lockscreen_missing_sim_message); - mHeaderSimBad2.setVisibility(View.GONE); - //mHeaderSimBad2.setText(R.string.lockscreen_missing_sim_instructions); - } else { - mHeaderSimBad1.setVisibility(View.GONE); - mHeaderSimBad2.setVisibility(View.GONE); - } + /** {@inheritDoc} */ + public void onGrabbedStateChange(View v, int grabbedState) { + // TODO: Update onscreen hint text based on the new state. } - private void refreshUnlockIntructions() { - if (mLockPatternUtils.isLockPatternEnabled() - || mUpdateMonitor.getSimState() == IccCard.State.PIN_REQUIRED - || mUpdateMonitor.getSimState() == IccCard.State.ABSENT) { - mLockInstructions.setText(R.string.lockscreen_instructions_when_pattern_enabled); - } else { - mLockInstructions.setText(R.string.lockscreen_instructions_when_pattern_disabled); + /** + * Displays a message in a text view and then removes it. + * @param textView The text view. + * @param text The text. + * @param iconResourceId The left hand icon. + */ + private void toastMessage(final TextView textView, final String text, final int iconResourceId) { + if (mPendingR1 != null) { + textView.removeCallbacks(mPendingR1); + mPendingR1 = null; } + if (mPendingR2 != null) { + textView.removeCallbacks(mPendingR2); + mPendingR2 = null; + } + + mPendingR1 = new Runnable() { + public void run() { + textView.setText(text); + textView.setCompoundDrawablesWithIntrinsicBounds(iconResourceId, 0, 0, 0); + textView.setCompoundDrawablePadding(4); + } + }; + textView.postDelayed(mPendingR1, 0); + mPendingR2 = new Runnable() { + public void run() { + textView.setText(""); + textView.setCompoundDrawablesWithIntrinsicBounds(0, 0, 0, 0); + } + }; + textView.postDelayed(mPendingR2, 3500); } + private Runnable mPendingR1; + private Runnable mPendingR2; + private void refreshAlarmDisplay() { - final String nextAlarmText = mLockPatternUtils.getNextAlarm(); - - // bug 1685880: if we are in landscape and showing plmn, the information can end up not - // fitting on screen. in this case, the alarm will get cut. - final CharSequence plmn = mUpdateMonitor.getTelephonyPlmn(); - final boolean showingPlmn = plmn != null && !TextUtils.isEmpty(plmn); - final boolean wontFit = !mUpdateMonitor.isInPortrait() && showingPlmn; - if (nextAlarmText != null && mSimOk && !wontFit) { - setAlarmInfoVisible(true); - mAlarmText.setText(nextAlarmText); - } else { - setAlarmInfoVisible(false); + mNextAlarm = mLockPatternUtils.getNextAlarm(); + if (mNextAlarm != null) { + mAlarmIcon = getContext().getResources().getDrawable(R.drawable.ic_lock_idle_alarm); } + updateStatusLines(); } - private void setAlarmInfoVisible(boolean visible) { - final int visibilityFlag = visible ? View.VISIBLE : View.GONE; - mNextAlarmGroup.setVisibility(visibilityFlag); - mAlarmSpacer.setVisibility(visibilityFlag); - } - - + /** {@inheritDoc} */ public void onRefreshBatteryInfo(boolean showBatteryInfo, boolean pluggedIn, int batteryLevel) { + if (DBG) Log.d(TAG, "onRefreshBatteryInfo(" + showBatteryInfo + ", " + pluggedIn + ")"); mShowingBatteryInfo = showBatteryInfo; mPluggedIn = pluggedIn; mBatteryLevel = batteryLevel; - refreshBatteryDisplay(); + refreshBatteryStringAndIcon(); + updateStatusLines(); } - private void refreshBatteryDisplay() { - if (!mShowingBatteryInfo || !mSimOk) { - mBatteryInfoGroup.setVisibility(View.GONE); - mBatteryInfoSpacer.setVisibility(View.GONE); + private void refreshBatteryStringAndIcon() { + if (!mShowingBatteryInfo) { + mCharging = null; return; } - mBatteryInfoGroup.setVisibility(View.VISIBLE); - mBatteryInfoSpacer.setVisibility(View.VISIBLE); + + if (mChargingIcon == null) { + mChargingIcon = + getContext().getResources().getDrawable(R.drawable.ic_lock_idle_charging); + } if (mPluggedIn) { - mBatteryInfoIcon.setImageResource(R.drawable.ic_lock_idle_charging); if (mBatteryLevel >= 100) { - mBatteryInfoText.setText(R.string.lockscreen_charged); + mCharging = getContext().getString(R.string.lockscreen_charged); } else { - mBatteryInfoText.setText( - getContext().getString(R.string.lockscreen_plugged_in, mBatteryLevel)); + mCharging = getContext().getString(R.string.lockscreen_plugged_in, mBatteryLevel); } } else { - mBatteryInfoIcon.setImageResource(R.drawable.ic_lock_idle_low_battery); - mBatteryInfoText.setText(R.string.lockscreen_low_battery); + mCharging = getContext().getString(R.string.lockscreen_low_battery); } } + /** {@inheritDoc} */ public void onTimeChanged() { refreshTimeAndDateDisplay(); } private void refreshTimeAndDateDisplay() { Date now = new Date(); - mTime.setText(DateFormat.getTimeFormat(getContext()).format(now)); - mDate.setText(DateFormat.getDateFormat(getContext()).format(now)); + mTime.setText(mTimeFormat.format(now)); + mDate.setText(mDateFormat.format(now)); } - public void onRefreshCarrierInfo(CharSequence plmn, CharSequence spn) { - refreshSimOkHeaders(plmn, spn); - refreshAlarmDisplay(); // in case alarm won't fit anymore + /** + * @return A localized format like "Fri, Sep 18, 2009" + */ + private java.text.DateFormat getLockScreenDateFormat() { + SimpleDateFormat adjusted = null; + try { + // this call gives us the localized order + final SimpleDateFormat dateFormat = (SimpleDateFormat) + java.text.DateFormat.getDateInstance(java.text.DateFormat.FULL); + adjusted = new SimpleDateFormat(dateFormat.toPattern() + .replace("MMMM", "MMM") // we want "Sep", not "September" + .replace("EEEE", "EEE")); // we want "Fri", no "Friday" + } catch (ClassCastException e) { + // in case the library implementation changes and this throws a class cast exception + // or anything else that is funky + Log.e("LockScreen", "couldn't finnagle our custom date format :(", e); + return java.text.DateFormat.getDateInstance(java.text.DateFormat.MEDIUM); + } + return adjusted; } - private void refreshSimOkHeaders(CharSequence plmn, CharSequence spn) { - final IccCard.State simState = mUpdateMonitor.getSimState(); - if (simState == IccCard.State.READY) { - if (plmn != null && !TextUtils.isEmpty(plmn)) { - mHeaderSimOk1.setVisibility(View.VISIBLE); - mHeaderSimOk1.setText(plmn); - } else { - mHeaderSimOk1.setVisibility(View.GONE); - } - - if (spn != null && !TextUtils.isEmpty(spn)) { - mHeaderSimOk2.setVisibility(View.VISIBLE); - mHeaderSimOk2.setText(spn); - } else { - mHeaderSimOk2.setVisibility(View.GONE); - } - } else if (simState == IccCard.State.PIN_REQUIRED) { - mHeaderSimOk1.setVisibility(View.VISIBLE); - mHeaderSimOk1.setText(R.string.lockscreen_sim_locked_message); - mHeaderSimOk2.setVisibility(View.GONE); - } else if (simState == IccCard.State.ABSENT) { - mHeaderSimOk1.setVisibility(View.VISIBLE); - mHeaderSimOk1.setText(R.string.lockscreen_missing_sim_message_short); - mHeaderSimOk2.setVisibility(View.GONE); - } else if (simState == IccCard.State.NETWORK_LOCKED) { - mHeaderSimOk1.setVisibility(View.VISIBLE); - mHeaderSimOk1.setText(R.string.lockscreen_network_locked_message); - mHeaderSimOk2.setVisibility(View.GONE); + private void updateStatusLines() { + if (!mStatus.showStatusLines() + || (mCharging == null && mNextAlarm == null)) { + mStatus1.setVisibility(View.INVISIBLE); + mStatus2.setVisibility(View.INVISIBLE); + } else if (mCharging != null && mNextAlarm == null) { + // charging only + mStatus1.setVisibility(View.VISIBLE); + mStatus2.setVisibility(View.INVISIBLE); + + mStatus1.setText(mCharging); + mStatus1.setCompoundDrawablesWithIntrinsicBounds(mChargingIcon, null, null, null); + } else if (mNextAlarm != null && mCharging == null) { + // next alarm only + mStatus1.setVisibility(View.VISIBLE); + mStatus2.setVisibility(View.INVISIBLE); + + mStatus1.setText(mNextAlarm); + mStatus1.setCompoundDrawablesWithIntrinsicBounds(mAlarmIcon, null, null, null); + } else if (mCharging != null && mNextAlarm != null) { + // both charging and next alarm + mStatus1.setVisibility(View.VISIBLE); + mStatus2.setVisibility(View.VISIBLE); + + mStatus1.setText(mCharging); + mStatus1.setCompoundDrawablesWithIntrinsicBounds(mChargingIcon, null, null, null); + mStatus2.setText(mNextAlarm); + mStatus2.setCompoundDrawablesWithIntrinsicBounds(mAlarmIcon, null, null, null); } } - public void onSimStateChanged(IccCard.State simState) { - mSimOk = isSimOk(simState); - refreshViewsWRTSimOk(); - refreshUnlockIntructions(); + /** {@inheritDoc} */ + public void onRefreshCarrierInfo(CharSequence plmn, CharSequence spn) { + if (DBG) Log.d(TAG, "onRefreshCarrierInfo(" + plmn + ", " + spn + ")"); + updateLayout(mStatus); + } + + private void putEmergencyBelow(int viewId) { + final RelativeLayout.LayoutParams layoutParams = + (RelativeLayout.LayoutParams) mEmergencyCallButton.getLayoutParams(); + layoutParams.addRule(RelativeLayout.BELOW, viewId); + mEmergencyCallButton.setLayoutParams(layoutParams); } /** - * @return Whether the sim state is ok, meaning we don't need to show - * a special screen with the emergency call button and keep them from - * doing anything else. + * Determine the current status of the lock screen given the sim state and other stuff. */ - private boolean isSimOk(IccCard.State simState) { + private Status getCurrentStatus(IccCard.State simState) { boolean missingAndNotProvisioned = (!mUpdateMonitor.isDeviceProvisioned() && simState == IccCard.State.ABSENT); - return !(missingAndNotProvisioned || simState == IccCard.State.PUK_REQUIRED); + if (missingAndNotProvisioned) { + return Status.SimMissingLocked; + } + + switch (simState) { + case ABSENT: + return Status.SimMissing; + case NETWORK_LOCKED: + return Status.SimMissingLocked; + case NOT_READY: + return Status.SimMissing; + case PIN_REQUIRED: + return Status.SimLocked; + case PUK_REQUIRED: + return Status.SimPukLocked; + case READY: + return Status.Normal; + case UNKNOWN: + return Status.SimMissing; + } + return Status.SimMissing; } + /** + * Update the layout to match the current status. + */ + private void updateLayout(Status status) { + switch (status) { + case Normal: + // text + mCarrier.setText( + getCarrierString( + mUpdateMonitor.getTelephonyPlmn(), + mUpdateMonitor.getTelephonySpn())); +// mScreenLocked.setText(R.string.lockscreen_screen_locked); + + // layout + mScreenLocked.setVisibility(View.VISIBLE); + mRotary.setVisibility(View.VISIBLE); + mEmergencyCallButton.setVisibility(View.GONE); + break; + case NetworkLocked: + // text + mCarrier.setText(R.string.lockscreen_network_locked_message); + mScreenLocked.setText(R.string.lockscreen_instructions_when_pattern_disabled); + + // layout + mScreenLocked.setVisibility(View.VISIBLE); + mRotary.setVisibility(View.VISIBLE); + mEmergencyCallButton.setVisibility(View.GONE); + break; + case SimMissing: + // text + mCarrier.setText(R.string.lockscreen_missing_sim_message_short); + mScreenLocked.setText(R.string.lockscreen_instructions_when_pattern_disabled); + + // layout + mScreenLocked.setVisibility(View.INVISIBLE); + mRotary.setVisibility(View.VISIBLE); + mEmergencyCallButton.setVisibility(View.VISIBLE); + putEmergencyBelow(R.id.divider); + break; + case SimMissingLocked: + // text + mCarrier.setText(R.string.lockscreen_missing_sim_message_short); + mScreenLocked.setText(R.string.lockscreen_missing_sim_instructions); + + // layout + mScreenLocked.setVisibility(View.VISIBLE); + mRotary.setVisibility(View.GONE); + mEmergencyCallButton.setVisibility(View.VISIBLE); + putEmergencyBelow(R.id.screenLocked); + break; + case SimLocked: + // text + mCarrier.setText(R.string.lockscreen_sim_locked_message); + + // layout + mScreenLocked.setVisibility(View.INVISIBLE); + mRotary.setVisibility(View.VISIBLE); + mEmergencyCallButton.setVisibility(View.GONE); + break; + case SimPukLocked: + // text + mCarrier.setText(R.string.lockscreen_sim_puk_locked_message); + mScreenLocked.setText(R.string.lockscreen_sim_puk_locked_instructions); + + // layout + mScreenLocked.setVisibility(View.VISIBLE); + mRotary.setVisibility(View.GONE); + mEmergencyCallButton.setVisibility(View.VISIBLE); + putEmergencyBelow(R.id.screenLocked); + break; + } + } + + static CharSequence getCarrierString(CharSequence telephonyPlmn, CharSequence telephonySpn) { + if (telephonyPlmn != null && telephonySpn == null) { + return telephonyPlmn; + } else if (telephonyPlmn != null && telephonySpn != null) { + return telephonyPlmn + "\n" + telephonySpn; + } else if (telephonyPlmn == null && telephonySpn != null) { + return telephonySpn; + } else { + return ""; + } + } + + public void onSimStateChanged(IccCard.State simState) { + if (DBG) Log.d(TAG, "onSimStateChanged(" + simState + ")"); + mStatus = getCurrentStatus(simState); + updateLayout(mStatus); + updateStatusLines(); + } + + public void onOrientationChange(boolean inPortrait) { + if (inPortrait != mCreatedInPortrait) { + mCallback.recreateMe(); + } } public void onKeyboardChange(boolean isKeyboardOpen) { - if (isKeyboardOpen) { + if (mUpdateMonitor.isKeyguardBypassEnabled() && isKeyboardOpen) { mCallback.goToUnlockScreen(); } } @@ -376,7 +571,7 @@ class LockScreen extends LinearLayout implements KeyguardScreen, KeyguardUpdateM /** {@inheritDoc} */ public void onResume() { - + resetStatusInfo(mUpdateMonitor); } /** {@inheritDoc} */ @@ -384,4 +579,3 @@ class LockScreen extends LinearLayout implements KeyguardScreen, KeyguardUpdateM mUpdateMonitor.removeCallback(this); } } - diff --git a/policy/com/android/internal/policy/impl/PhoneWindow.java b/policy/com/android/internal/policy/impl/PhoneWindow.java index 6341771..6dd5d93 100644 --- a/policy/com/android/internal/policy/impl/PhoneWindow.java +++ b/policy/com/android/internal/policy/impl/PhoneWindow.java @@ -20,6 +20,7 @@ import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT; import static android.view.WindowManager.LayoutParams.FLAG_FULLSCREEN; import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR; import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN; +import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER; import com.android.internal.view.menu.ContextMenuBuilder; import com.android.internal.view.menu.MenuBuilder; @@ -34,7 +35,6 @@ import android.content.Intent; import android.content.res.Configuration; import android.content.res.TypedArray; import android.graphics.Canvas; -import android.graphics.Paint; import android.graphics.PixelFormat; import android.graphics.Rect; import android.graphics.drawable.Drawable; @@ -124,6 +124,7 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { * this is 0, there is no key held down. */ private int mPanelChordingKey; + private boolean mPanelMayLongPress; private ImageView mLeftIconView; @@ -155,120 +156,6 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { private TelephonyManager mTelephonyManager = null; - private boolean mSearchKeyDownReceived; - - private boolean mKeycodeCallTimeoutActive = false; - - private boolean mKeycodeCameraTimeoutActive = false; - - static final int MSG_MENU_LONG_PRESS = 1; - static final int MSG_MENU_LONG_PRESS_COMPLETE = 2; - static final int MSG_CALL_LONG_PRESS = 3; - static final int MSG_CALL_LONG_PRESS_COMPLETE = 4; - static final int MSG_CAMERA_LONG_PRESS = 5; - static final int MSG_CAMERA_LONG_PRESS_COMPLETE = 6; - static final int MSG_SEARCH_LONG_PRESS = 7; - static final int MSG_SEARCH_LONG_PRESS_COMPLETE = 8; - - private final Handler mKeycodeMenuTimeoutHandler = new Handler() { - @Override - public void handleMessage(Message msg) { - switch (msg.what) { - case MSG_MENU_LONG_PRESS: { - if (mPanelChordingKey == 0) return; - // Before actually doing the long press, enqueue another - // message and do the processing there. This helps if - // the app isn't being responsive, and finally woke up -- - // if the window manager wasn't told about it processing - // the down key for too long, it would enqueue the key up - // at a time after the timeout of this message. So we go - // through another message, to make sure we process an up - // before continuing. - mKeycodeMenuTimeoutHandler.sendEmptyMessage( - MSG_MENU_LONG_PRESS_COMPLETE); - break; - } - case MSG_CALL_LONG_PRESS: { - if (!mKeycodeCallTimeoutActive) return; - // See above. - mKeycodeMenuTimeoutHandler.sendEmptyMessage( - MSG_CALL_LONG_PRESS_COMPLETE); - break; - } - case MSG_CAMERA_LONG_PRESS: { - if (!mKeycodeCameraTimeoutActive) return; - // See above. - Message newMessage = Message.obtain(msg); - newMessage.what = MSG_CAMERA_LONG_PRESS_COMPLETE; - mKeycodeMenuTimeoutHandler.sendMessage(newMessage); - break; - } - case MSG_SEARCH_LONG_PRESS: { - if (!mSearchKeyDownReceived) return; - // See above. - Message newMessage = Message.obtain(msg); - newMessage.what = MSG_SEARCH_LONG_PRESS_COMPLETE; - mKeycodeMenuTimeoutHandler.sendMessage(newMessage); - break; - } - case MSG_MENU_LONG_PRESS_COMPLETE: { - if (mPanelChordingKey == 0) return; - mPanelChordingKey = 0; - mDecor.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS); - InputMethodManager imm = (InputMethodManager) - getContext().getSystemService(Context.INPUT_METHOD_SERVICE); - if (imm != null) { - imm.toggleSoftInput(InputMethodManager.SHOW_FORCED, 0); - } - } break; - case MSG_CALL_LONG_PRESS_COMPLETE: { - if (!mKeycodeCallTimeoutActive) return; - mKeycodeCallTimeoutActive = false; - mDecor.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS); - // launch the VoiceDialer - Intent intent = new Intent(Intent.ACTION_VOICE_COMMAND); - intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - try { - sendCloseSystemWindows(); - getContext().startActivity(intent); - } catch (ActivityNotFoundException e) { - startCallActivity(); - } - } break; - case MSG_CAMERA_LONG_PRESS_COMPLETE: { - if (!mKeycodeCameraTimeoutActive) return; - mKeycodeCameraTimeoutActive = false; - mDecor.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS); - sendCloseSystemWindows(); - // Broadcast an intent that the Camera button was longpressed - Intent intent = new Intent(Intent.ACTION_CAMERA_BUTTON, null); - intent.putExtra(Intent.EXTRA_KEY_EVENT, (KeyEvent) msg.obj); - getContext().sendOrderedBroadcast(intent, null); - } break; - case MSG_SEARCH_LONG_PRESS_COMPLETE: { - if (getKeyguardManager().inKeyguardRestrictedInputMode() || - !mSearchKeyDownReceived) { - mSearchKeyDownReceived = false; - return; - } - mDecor.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS); - // launch the search activity - Intent intent = new Intent(Intent.ACTION_SEARCH_LONG_PRESS); - intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - try { - sendCloseSystemWindows(); - getContext().startActivity(intent); - // Only clear this if we successfully start the - // activity; otherwise we will allow the normal short - // press action to be performed. - mSearchKeyDownReceived = false; - } catch (ActivityNotFoundException e) { - } - } break; - } - } - }; - public PhoneWindow(Context context) { super(context); mLayoutInflater = LayoutInflater.from(context); @@ -295,11 +182,9 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { /* Custom title feature is enabled and the user is trying to enable another feature */ throw new AndroidRuntimeException("You cannot combine custom titles with other title features"); } - /* FEATURE_OPENGL disabled for 1.0 if (featureId == FEATURE_OPENGL) { getAttributes().memoryType = WindowManager.LayoutParams.MEMORY_TYPE_GPU; } - */ return super.requestFeature(featureId); } @@ -567,7 +452,7 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { WindowManager.LayoutParams lp = new WindowManager.LayoutParams( WRAP_CONTENT, WRAP_CONTENT, - st.x, st.y, WindowManager.LayoutParams.TYPE_APPLICATION_PANEL, + st.x, st.y, WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG, WindowManager.LayoutParams.FLAG_DITHER | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM, st.decorView.mDefaultOpacity); @@ -649,19 +534,35 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { * @return Whether the key was handled. */ public final boolean onKeyDownPanel(int featureId, KeyEvent event) { - // The panel key was pushed, so set the chording key - mPanelChordingKey = event.getKeyCode(); - - PanelFeatureState st = getPanelState(featureId, true); - if (!st.isOpen) { - if (getContext().getResources().getConfiguration().keyboard - == Configuration.KEYBOARD_NOKEYS) { - mKeycodeMenuTimeoutHandler.removeMessages(MSG_MENU_LONG_PRESS); - mKeycodeMenuTimeoutHandler.sendMessageDelayed( - mKeycodeMenuTimeoutHandler.obtainMessage(MSG_MENU_LONG_PRESS), - ViewConfiguration.getLongPressTimeout()); + final int keyCode = event.getKeyCode(); + + if (event.getRepeatCount() == 0) { + // The panel key was pushed, so set the chording key + mPanelChordingKey = keyCode; + mPanelMayLongPress = false; + + PanelFeatureState st = getPanelState(featureId, true); + if (!st.isOpen) { + if (getContext().getResources().getConfiguration().keyboard + == Configuration.KEYBOARD_NOKEYS) { + mPanelMayLongPress = true; + } + return preparePanel(st, event); + } + + } else if (mPanelMayLongPress && mPanelChordingKey == keyCode + && (event.getFlags()&KeyEvent.FLAG_LONG_PRESS) != 0) { + // We have had a long press while in a state where this + // should be executed... do it! + mPanelChordingKey = 0; + mPanelMayLongPress = false; + InputMethodManager imm = (InputMethodManager) + getContext().getSystemService(Context.INPUT_METHOD_SERVICE); + if (imm != null) { + mDecor.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS); + imm.toggleSoftInput(InputMethodManager.SHOW_FORCED, 0); } - return preparePanel(st, event); + } return false; @@ -676,8 +577,12 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { // The panel key was released, so clear the chording key if (mPanelChordingKey != 0) { mPanelChordingKey = 0; - mKeycodeMenuTimeoutHandler.removeMessages(MSG_MENU_LONG_PRESS); + mPanelMayLongPress = false; + if (event.isCanceled()) { + return; + } + boolean playSoundEffect = false; PanelFeatureState st = getPanelState(featureId, true); if (st.isOpen || st.isHandled) { @@ -1214,6 +1119,11 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { * @see android.view.KeyEvent */ protected boolean onKeyDown(int featureId, int keyCode, KeyEvent event) { + final KeyEvent.DispatcherState dispatcher = + mDecor != null ? mDecor.getKeyDispatcherState() : null; + //Log.i(TAG, "Key down: repeat=" + event.getRepeatCount() + // + " flags=0x" + Integer.toHexString(event.getFlags())); + switch (keyCode) { case KeyEvent.KEYCODE_VOLUME_UP: case KeyEvent.KEYCODE_VOLUME_DOWN: { @@ -1260,21 +1170,25 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { } case KeyEvent.KEYCODE_CAMERA: { - if (getKeyguardManager().inKeyguardRestrictedInputMode()) { + if (getKeyguardManager().inKeyguardRestrictedInputMode() + || dispatcher == null) { break; } - if (event.getRepeatCount() > 0) break; - mKeycodeCameraTimeoutActive = true; - mKeycodeMenuTimeoutHandler.removeMessages(MSG_CAMERA_LONG_PRESS); - Message message = mKeycodeMenuTimeoutHandler.obtainMessage(MSG_CAMERA_LONG_PRESS); - message.obj = event; - mKeycodeMenuTimeoutHandler.sendMessageDelayed(message, - ViewConfiguration.getLongPressTimeout()); + if (event.getRepeatCount() == 0) { + dispatcher.startTracking(event, this); + } else if (event.isLongPress() && dispatcher.isTracking(event)) { + dispatcher.performedLongPress(event); + mDecor.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS); + sendCloseSystemWindows(); + // Broadcast an intent that the Camera button was longpressed + Intent intent = new Intent(Intent.ACTION_CAMERA_BUTTON, null); + intent.putExtra(Intent.EXTRA_KEY_EVENT, event); + getContext().sendOrderedBroadcast(intent, null); + } return true; } case KeyEvent.KEYCODE_MENU: { - if (event.getRepeatCount() > 0) break; onKeyDownPanel((featureId < 0) ? FEATURE_OPTIONS_PANEL : featureId, event); return true; } @@ -1282,48 +1196,61 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { case KeyEvent.KEYCODE_BACK: { if (event.getRepeatCount() > 0) break; if (featureId < 0) break; - if (featureId == FEATURE_OPTIONS_PANEL) { - PanelFeatureState st = getPanelState(featureId, false); - if (st != null && st.isInExpandedMode) { - // If the user is in an expanded menu and hits back, it - // should go back to the icon menu - reopenMenu(true); - return true; - } - } - closePanel(featureId); + // Currently don't do anything with long press. + dispatcher.startTracking(event, this); return true; } case KeyEvent.KEYCODE_CALL: { - if (getKeyguardManager().inKeyguardRestrictedInputMode()) { + if (getKeyguardManager().inKeyguardRestrictedInputMode() + || dispatcher == null) { break; } - if (event.getRepeatCount() > 0) break; - mKeycodeCallTimeoutActive = true; - mKeycodeMenuTimeoutHandler.removeMessages(MSG_CALL_LONG_PRESS); - mKeycodeMenuTimeoutHandler.sendMessageDelayed( - mKeycodeMenuTimeoutHandler.obtainMessage(MSG_CALL_LONG_PRESS), - ViewConfiguration.getLongPressTimeout()); + if (event.getRepeatCount() == 0) { + dispatcher.startTracking(event, this); + } else if (event.isLongPress() && dispatcher.isTracking(event)) { + dispatcher.performedLongPress(event); + mDecor.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS); + // launch the VoiceDialer + Intent intent = new Intent(Intent.ACTION_VOICE_COMMAND); + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + try { + sendCloseSystemWindows(); + getContext().startActivity(intent); + } catch (ActivityNotFoundException e) { + startCallActivity(); + } + } return true; } case KeyEvent.KEYCODE_SEARCH: { + if (getKeyguardManager().inKeyguardRestrictedInputMode() + || dispatcher == null) { + break; + } if (event.getRepeatCount() == 0) { - mSearchKeyDownReceived = true; + dispatcher.startTracking(event, this); + } else if (event.isLongPress() && dispatcher.isTracking(event)) { Configuration config = getContext().getResources().getConfiguration(); if (config.keyboard == Configuration.KEYBOARD_NOKEYS - || config.hardKeyboardHidden == Configuration.HARDKEYBOARDHIDDEN_YES) { - // If this device does not have a hardware keyboard, - // or that keyboard is hidden, then we can't use the - // search key for chording to perform shortcuts; - // instead, we will let the user long press, - mKeycodeMenuTimeoutHandler.removeMessages(MSG_SEARCH_LONG_PRESS); - mKeycodeMenuTimeoutHandler.sendMessageDelayed( - mKeycodeMenuTimeoutHandler.obtainMessage(MSG_SEARCH_LONG_PRESS), - ViewConfiguration.getLongPressTimeout()); + || config.hardKeyboardHidden + == Configuration.HARDKEYBOARDHIDDEN_YES) { + // launch the search activity + Intent intent = new Intent(Intent.ACTION_SEARCH_LONG_PRESS); + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + try { + sendCloseSystemWindows(); + getContext().startActivity(intent); + mDecor.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS); + // Only clear this if we successfully start the + // activity; otherwise we will allow the normal short + // press action to be performed. + dispatcher.performedLongPress(event); + } catch (ActivityNotFoundException e) { + // Ignore + } } - return true; } break; } @@ -1349,6 +1276,14 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { * @see android.view.KeyEvent */ protected boolean onKeyUp(int featureId, int keyCode, KeyEvent event) { + final KeyEvent.DispatcherState dispatcher = + mDecor != null ? mDecor.getKeyDispatcherState() : null; + if (dispatcher != null) { + dispatcher.handleUpEvent(event); + } + //Log.i(TAG, "Key up: repeat=" + event.getRepeatCount() + // + " flags=0x" + Integer.toHexString(event.getFlags())); + switch (keyCode) { case KeyEvent.KEYCODE_VOLUME_UP: case KeyEvent.KEYCODE_VOLUME_DOWN: { @@ -1374,6 +1309,24 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { return true; } + case KeyEvent.KEYCODE_BACK: { + if (featureId < 0) break; + if (event.isTracking() && !event.isCanceled()) { + if (featureId == FEATURE_OPTIONS_PANEL) { + PanelFeatureState st = getPanelState(featureId, false); + if (st != null && st.isInExpandedMode) { + // If the user is in an expanded menu and hits back, it + // should go back to the icon menu + reopenMenu(true); + return true; + } + } + closePanel(featureId); + return true; + } + break; + } + case KeyEvent.KEYCODE_HEADSETHOOK: case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE: case KeyEvent.KEYCODE_MEDIA_STOP: @@ -1391,11 +1344,9 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { if (getKeyguardManager().inKeyguardRestrictedInputMode()) { break; } - if (event.getRepeatCount() > 0) break; // Can a key up event repeat? - mKeycodeMenuTimeoutHandler.removeMessages(MSG_CAMERA_LONG_PRESS); - if (!mKeycodeCameraTimeoutActive) break; - mKeycodeCameraTimeoutActive = false; - // Add short press behavior here if desired + if (event.isTracking() && !event.isCanceled()) { + // Add short press behavior here if desired + } return true; } @@ -1403,11 +1354,9 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { if (getKeyguardManager().inKeyguardRestrictedInputMode()) { break; } - if (event.getRepeatCount() > 0) break; - mKeycodeMenuTimeoutHandler.removeMessages(MSG_CALL_LONG_PRESS); - if (!mKeycodeCallTimeoutActive) break; - mKeycodeCallTimeoutActive = false; - startCallActivity(); + if (event.isTracking() && !event.isCanceled()) { + startCallActivity(); + } return true; } @@ -1416,13 +1365,12 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { * Do this in onKeyUp since the Search key is also used for * chording quick launch shortcuts. */ - if (getKeyguardManager().inKeyguardRestrictedInputMode() || - !mSearchKeyDownReceived) { - mSearchKeyDownReceived = false; + if (getKeyguardManager().inKeyguardRestrictedInputMode()) { break; } - mSearchKeyDownReceived = false; - launchDefaultSearch(); + if (event.isTracking() && !event.isCanceled()) { + launchDefaultSearch(); + } return true; } } @@ -1584,9 +1532,11 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { PanelFeatureState st; for (int i = panels.length - 1; i >= 0; i--) { st = panels[i]; - if ((st != null) && st.isOpen) { - // Clear st.isOpen (openPanel will not open if it's already open) - st.isOpen = false; + // We restore the panel if it was last open; we skip it if it + // now is open, to avoid a race condition if the user immediately + // opens it when we are resuming. + if ((st != null) && !st.isOpen && st.wasLastOpen) { + st.isInExpandedMode = st.wasLastExpanded; openPanel(st, null); } } @@ -1606,8 +1556,6 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { private final Rect mFrameOffsets = new Rect(); - private final Paint mBlackPaint = new Paint(); - private boolean mChanging; private Drawable mMenuBackground; @@ -1617,7 +1565,6 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { public DecorView(Context context, int featureId) { super(context); mFeatureId = featureId; - mBlackPaint.setColor(0xFF000000); } @Override @@ -2009,16 +1956,11 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { public void onWindowFocusChanged(boolean hasWindowFocus) { super.onWindowFocusChanged(hasWindowFocus); - // no KEYCODE_CALL events active across focus changes - mKeycodeMenuTimeoutHandler.removeMessages(MSG_MENU_LONG_PRESS); - mKeycodeMenuTimeoutHandler.removeMessages(MSG_CALL_LONG_PRESS); - mKeycodeMenuTimeoutHandler.removeMessages(MSG_CAMERA_LONG_PRESS); - mKeycodeCallTimeoutActive = false; - mKeycodeCameraTimeoutActive = false; + mPanelMayLongPress = false; // If the user is chording a menu shortcut, release the chord since // this window lost focus - if (!hasWindowFocus && mPanelChordingKey > 0) { + if (!hasWindowFocus && mPanelChordingKey != 0) { closePanel(FEATURE_OPTIONS_PANEL); } @@ -2031,6 +1973,11 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { @Override protected void onAttachedToWindow() { super.onAttachedToWindow(); + + final Callback cb = getCallback(); + if (cb != null && mFeatureId < 0) { + cb.onAttachedToWindow(); + } if (mFeatureId == -1) { /* @@ -2043,6 +1990,23 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { openPanelsAfterRestore(); } } + + @Override + protected void onDetachedFromWindow() { + super.onDetachedFromWindow(); + + final Callback cb = getCallback(); + if (cb != null && mFeatureId < 0) { + cb.onDetachedFromWindow(); + } + } + + @Override + public void onCloseSystemDialogs(String reason) { + if (mFeatureId >= 0) { + closeAllPanels(); + } + } } protected DecorView generateDecor() { @@ -2097,6 +2061,10 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { setFlags(FLAG_FULLSCREEN, FLAG_FULLSCREEN&(~getForcedWindowFlags())); } + if (a.getBoolean(com.android.internal.R.styleable.Window_windowShowWallpaper, false)) { + setFlags(FLAG_SHOW_WALLPAPER, FLAG_SHOW_WALLPAPER&(~getForcedWindowFlags())); + } + WindowManager.LayoutParams params = getAttributes(); if (!hasSoftInputMode()) { @@ -2606,6 +2574,10 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { boolean refreshDecorView; + boolean wasLastOpen; + + boolean wasLastExpanded; + /** * Contains the state of the menu when told to freeze. */ @@ -2654,8 +2626,8 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { void onRestoreInstanceState(Parcelable state) { SavedState savedState = (SavedState) state; featureId = savedState.featureId; - isOpen = savedState.isOpen; - isInExpandedMode = savedState.isInExpandedMode; + wasLastOpen = savedState.isOpen; + wasLastExpanded = savedState.isInExpandedMode; frozenMenuState = savedState.menuState; /* diff --git a/policy/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/com/android/internal/policy/impl/PhoneWindowManager.java index 8140b3d..e57fbe8 100644..100755 --- a/policy/com/android/internal/policy/impl/PhoneWindowManager.java +++ b/policy/com/android/internal/policy/impl/PhoneWindowManager.java @@ -20,17 +20,19 @@ import android.app.Activity; import android.app.ActivityManagerNative; import android.app.IActivityManager; import android.app.IStatusBar; +import android.content.ActivityNotFoundException; import android.content.BroadcastReceiver; -import android.content.ContentQueryMap; import android.content.ContentResolver; import android.content.Context; import android.content.Intent; +import android.content.IntentFilter; import android.content.pm.ActivityInfo; import android.content.pm.PackageManager; import android.content.res.Configuration; import android.content.res.Resources; import android.database.ContentObserver; import android.graphics.Rect; +import android.os.BatteryManager; import android.os.Handler; import android.os.IBinder; import android.os.LocalPowerManager; @@ -67,6 +69,7 @@ import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN; import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR; import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS; import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED; +import static android.view.WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD; import static android.view.WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST; import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE; import static android.view.WindowManager.LayoutParams.LAST_APPLICATION_WINDOW; @@ -88,8 +91,12 @@ import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD; import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG; import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY; import static android.view.WindowManager.LayoutParams.TYPE_TOAST; +import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER; import android.view.WindowManagerImpl; import android.view.WindowManagerPolicy; +import android.view.WindowManagerPolicy.WindowState; +import android.view.animation.Animation; +import android.view.animation.AnimationUtils; import android.media.IAudioService; import android.media.AudioManager; @@ -108,29 +115,31 @@ public class PhoneWindowManager implements WindowManagerPolicy { static final boolean SHOW_STARTING_ANIMATIONS = true; static final boolean SHOW_PROCESSES_ON_ALT_MENU = false; - static final int APPLICATION_LAYER = 1; - static final int PHONE_LAYER = 2; - static final int SEARCH_BAR_LAYER = 3; - static final int STATUS_BAR_PANEL_LAYER = 4; + // wallpaper is at the bottom, though the window manager may move it. + static final int WALLPAPER_LAYER = 2; + static final int APPLICATION_LAYER = 2; + static final int PHONE_LAYER = 3; + static final int SEARCH_BAR_LAYER = 4; + static final int STATUS_BAR_PANEL_LAYER = 5; // toasts and the plugged-in battery thing - static final int TOAST_LAYER = 5; - static final int STATUS_BAR_LAYER = 6; + static final int TOAST_LAYER = 6; + static final int STATUS_BAR_LAYER = 7; // SIM errors and unlock. Not sure if this really should be in a high layer. - static final int PRIORITY_PHONE_LAYER = 7; + static final int PRIORITY_PHONE_LAYER = 8; // like the ANR / app crashed dialogs - static final int SYSTEM_ALERT_LAYER = 8; + static final int SYSTEM_ALERT_LAYER = 9; // system-level error dialogs - static final int SYSTEM_ERROR_LAYER = 9; + static final int SYSTEM_ERROR_LAYER = 10; // on-screen keyboards and other such input method user interfaces go here. - static final int INPUT_METHOD_LAYER = 10; + static final int INPUT_METHOD_LAYER = 11; // on-screen keyboards and other such input method user interfaces go here. - static final int INPUT_METHOD_DIALOG_LAYER = 11; + static final int INPUT_METHOD_DIALOG_LAYER = 12; // the keyguard; nothing on top of these can take focus, since they are // responsible for power management when displayed. - static final int KEYGUARD_LAYER = 12; - static final int KEYGUARD_DIALOG_LAYER = 13; + static final int KEYGUARD_LAYER = 13; + static final int KEYGUARD_DIALOG_LAYER = 14; // things in here CAN NOT take focus, but are shown on top of everything else. - static final int SYSTEM_OVERLAY_LAYER = 14; + static final int SYSTEM_OVERLAY_LAYER = 15; static final int APPLICATION_MEDIA_SUBLAYER = -2; static final int APPLICATION_MEDIA_OVERLAY_SUBLAYER = -1; @@ -146,9 +155,6 @@ public class PhoneWindowManager implements WindowManagerPolicy { static public final String SYSTEM_DIALOG_REASON_GLOBAL_ACTIONS = "globalactions"; static public final String SYSTEM_DIALOG_REASON_RECENT_APPS = "recentapps"; - // Vibrator pattern for haptic feedback of a long press. - private static final long[] LONG_PRESS_VIBE_PATTERN = {0, 1, 20, 21}; - final Object mLock = new Object(); Context mContext; @@ -156,6 +162,18 @@ public class PhoneWindowManager implements WindowManagerPolicy { LocalPowerManager mPowerManager; Vibrator mVibrator; // Vibrator for giving feedback of orientation changes + // Vibrator pattern for haptic feedback of a long press. + long[] mLongPressVibePattern; + + // Vibrator pattern for haptic feedback of virtual key press. + long[] mVirtualKeyVibePattern; + + // Vibrator pattern for haptic feedback during boot when safe mode is disabled. + long[] mSafeModeDisabledVibePattern; + + // Vibrator pattern for haptic feedback during boot when safe mode is enabled. + long[] mSafeModeEnabledVibePattern; + /** If true, hitting shift & menu will broadcast Intent.ACTION_BUG_REPORT */ boolean mEnableShiftMenuBugReports = false; @@ -168,7 +186,21 @@ public class PhoneWindowManager implements WindowManagerPolicy { RecentApplicationsDialog mRecentAppsDialog; Handler mHandler; + final IntentFilter mBatteryStatusFilter = new IntentFilter(); + boolean mLidOpen; + int mPlugged; + boolean mRegisteredBatteryReceiver; + int mDockState = Intent.EXTRA_DOCK_STATE_UNDOCKED; + int mLidOpenRotation; + int mCarDockRotation; + int mDeskDockRotation; + int mCarDockKeepsScreenOn; + int mDeskDockKeepsScreenOn; + boolean mCarDockEnablesAccelerometer; + boolean mDeskDockEnablesAccelerometer; + int mLidKeyboardAccessibility; + int mLidNavigationAccessibility; boolean mScreenOn = false; boolean mOrientationSensorEnabled = false; int mCurrentAppOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; @@ -200,9 +232,12 @@ public class PhoneWindowManager implements WindowManagerPolicy { WindowState mTopFullscreenOpaqueWindowState; boolean mForceStatusBar; - boolean mHideKeyguard; + boolean mHideLockScreen; + boolean mDismissKeyguard; boolean mHomePressed; Intent mHomeIntent; + Intent mCarDockIntent; + Intent mDeskDockIntent; boolean mSearchKeyPressed; boolean mConsumeSearchKeyUp; @@ -219,6 +254,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { ShortcutManager mShortcutManager; PowerManager.WakeLock mBroadcastWakeLock; + PowerManager.WakeLock mDockWakeLock; class SettingsObserver extends ContentObserver { SettingsObserver(Handler handler) { @@ -284,7 +320,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { @Override public void onOrientationChanged(int rotation) { // Send updates based on orientation value - if (true) Log.i(TAG, "onOrientationChanged, rotation changed to " +rotation); + if (localLOGV) Log.v(TAG, "onOrientationChanged, rotation changed to " +rotation); try { mWindowManager.setRotation(rotation, false, mFancyRotationAnimation); @@ -297,14 +333,27 @@ public class PhoneWindowManager implements WindowManagerPolicy { MyOrientationListener mOrientationListener; boolean useSensorForOrientationLp(int appOrientation) { + // The app says use the sensor. if (appOrientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR) { return true; } - if (mAccelerometerDefault != 0 && ( - appOrientation == ActivityInfo.SCREEN_ORIENTATION_USER || - appOrientation == ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED)) { + // The user preference says we can rotate, and the app is willing to rotate. + if (mAccelerometerDefault != 0 && + (appOrientation == ActivityInfo.SCREEN_ORIENTATION_USER + || appOrientation == ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED)) { return true; } + // We're in a dock that has a rotation affinity, an the app is willing to rotate. + if ((mCarDockEnablesAccelerometer && mDockState == Intent.EXTRA_DOCK_STATE_CAR) + || (mDeskDockEnablesAccelerometer && mDockState == Intent.EXTRA_DOCK_STATE_DESK)) { + // Note we override the nosensor flag here. + if (appOrientation == ActivityInfo.SCREEN_ORIENTATION_USER + || appOrientation == ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED + || appOrientation == ActivityInfo.SCREEN_ORIENTATION_NOSENSOR) { + return true; + } + } + // Else, don't use the sensor. return false; } @@ -319,6 +368,12 @@ public class PhoneWindowManager implements WindowManagerPolicy { // orientation, then we need to turn the sensor or. return true; } + if ((mCarDockEnablesAccelerometer && mDockState == Intent.EXTRA_DOCK_STATE_CAR) || + (mDeskDockEnablesAccelerometer && mDockState == Intent.EXTRA_DOCK_STATE_DESK)) { + // enable accelerometer if we are docked in a dock that enables accelerometer + // orientation management, + return true; + } if (mAccelerometerDefault == 0) { // If the setting for using the sensor by default is enabled, then // we will always leave it on. Note that the user could go to @@ -350,7 +405,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { } //Could have been invoked due to screen turning on or off or //change of the currently visible window's orientation - if (localLOGV) Log.i(TAG, "Screen status="+mScreenOn+ + if (localLOGV) Log.v(TAG, "Screen status="+mScreenOn+ ", current orientation="+mCurrentAppOrientation+ ", SensorEnabled="+mOrientationSensorEnabled); boolean disable = true; @@ -360,7 +415,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { //enable listener if not already enabled if (!mOrientationSensorEnabled) { mOrientationListener.enable(); - if(localLOGV) Log.i(TAG, "Enabling listeners"); + if(localLOGV) Log.v(TAG, "Enabling listeners"); mOrientationSensorEnabled = true; } } @@ -368,12 +423,12 @@ public class PhoneWindowManager implements WindowManagerPolicy { //check if sensors need to be disabled if (disable && mOrientationSensorEnabled) { mOrientationListener.disable(); - if(localLOGV) Log.i(TAG, "Disabling listeners"); + if(localLOGV) Log.v(TAG, "Disabling listeners"); mOrientationSensorEnabled = false; } } - Runnable mEndCallLongPress = new Runnable() { + Runnable mPowerLongPress = new Runnable() { public void run() { mShouldTurnOffOnKeyUp = false; performHapticFeedbackLw(null, HapticFeedbackConstants.LONG_PRESS, false); @@ -443,10 +498,81 @@ public class PhoneWindowManager implements WindowManagerPolicy { mHomeIntent.addCategory(Intent.CATEGORY_HOME); mHomeIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED); + mCarDockIntent = new Intent(Intent.ACTION_MAIN, null); + mCarDockIntent.addCategory(Intent.CATEGORY_CAR_DOCK); + mCarDockIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK + | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED); + mDeskDockIntent = new Intent(Intent.ACTION_MAIN, null); + mDeskDockIntent.addCategory(Intent.CATEGORY_DESK_DOCK); + mDeskDockIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK + | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED); PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE); mBroadcastWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "PhoneWindowManager.mBroadcastWakeLock"); + mDockWakeLock = pm.newWakeLock(PowerManager.FULL_WAKE_LOCK, + "PhoneWindowManager.mDockWakeLock"); + mDockWakeLock.setReferenceCounted(false); mEnableShiftMenuBugReports = "1".equals(SystemProperties.get("ro.debuggable")); + mLidOpenRotation = readRotation( + com.android.internal.R.integer.config_lidOpenRotation); + mCarDockRotation = readRotation( + com.android.internal.R.integer.config_carDockRotation); + mDeskDockRotation = readRotation( + com.android.internal.R.integer.config_deskDockRotation); + mCarDockKeepsScreenOn = mContext.getResources().getInteger( + com.android.internal.R.integer.config_carDockKeepsScreenOn); + mDeskDockKeepsScreenOn = mContext.getResources().getInteger( + com.android.internal.R.integer.config_deskDockKeepsScreenOn); + mCarDockEnablesAccelerometer = mContext.getResources().getBoolean( + com.android.internal.R.bool.config_carDockEnablesAccelerometer); + mDeskDockEnablesAccelerometer = mContext.getResources().getBoolean( + com.android.internal.R.bool.config_deskDockEnablesAccelerometer); + mLidKeyboardAccessibility = mContext.getResources().getInteger( + com.android.internal.R.integer.config_lidKeyboardAccessibility); + mLidNavigationAccessibility = mContext.getResources().getInteger( + com.android.internal.R.integer.config_lidNavigationAccessibility); + // register for battery events + mBatteryStatusFilter.addAction(Intent.ACTION_BATTERY_CHANGED); + mPlugged = 0; + updatePlugged(context.registerReceiver(null, mBatteryStatusFilter)); + // register for dock events + context.registerReceiver(mDockReceiver, new IntentFilter(Intent.ACTION_DOCK_EVENT)); + mVibrator = new Vibrator(); + mLongPressVibePattern = getLongIntArray(mContext.getResources(), + com.android.internal.R.array.config_longPressVibePattern); + mVirtualKeyVibePattern = getLongIntArray(mContext.getResources(), + com.android.internal.R.array.config_virtualKeyVibePattern); + mSafeModeDisabledVibePattern = getLongIntArray(mContext.getResources(), + com.android.internal.R.array.config_safeModeDisabledVibePattern); + mSafeModeEnabledVibePattern = getLongIntArray(mContext.getResources(), + com.android.internal.R.array.config_safeModeEnabledVibePattern); + } + + void updatePlugged(Intent powerIntent) { + if (localLOGV) Log.v(TAG, "New battery status: " + powerIntent.getExtras()); + if (powerIntent != null) { + mPlugged = powerIntent.getIntExtra(BatteryManager.EXTRA_PLUGGED, 0); + if (localLOGV) Log.v(TAG, "PLUGGED: " + mPlugged); + } + } + + private int readRotation(int resID) { + try { + int rotation = mContext.getResources().getInteger(resID); + switch (rotation) { + case 0: + return Surface.ROTATION_0; + case 90: + return Surface.ROTATION_90; + case 180: + return Surface.ROTATION_180; + case 270: + return Surface.ROTATION_270; + } + } catch (Resources.NotFoundException e) { + // fall through + } + return -1; } /** {@inheritDoc} */ @@ -465,7 +591,8 @@ public class PhoneWindowManager implements WindowManagerPolicy { // monitor/control what they are doing. break; case TYPE_INPUT_METHOD: - // The window manager will check this. + case TYPE_WALLPAPER: + // The window manager will check these. break; case TYPE_PHONE: case TYPE_PRIORITY_PHONE: @@ -499,7 +626,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { void readLidState() { try { - int sw = mWindowManager.getSwitchState(0); + int sw = mWindowManager.getSwitchState(RawInputEvent.SW_LID); if (sw >= 0) { mLidOpen = sw == 0; } @@ -508,17 +635,32 @@ public class PhoneWindowManager implements WindowManagerPolicy { } } + private int determineHiddenState(boolean lidOpen, + int mode, int hiddenValue, int visibleValue) { + switch (mode) { + case 1: + return lidOpen ? visibleValue : hiddenValue; + case 2: + return lidOpen ? hiddenValue : visibleValue; + } + return visibleValue; + } + /** {@inheritDoc} */ public void adjustConfigurationLw(Configuration config) { readLidState(); final boolean lidOpen = !KEYBOARD_ALWAYS_HIDDEN && mLidOpen; mPowerManager.setKeyboardVisibility(lidOpen); - config.keyboardHidden = (lidOpen || mHasSoftInput) - ? Configuration.KEYBOARDHIDDEN_NO - : Configuration.KEYBOARDHIDDEN_YES; - config.hardKeyboardHidden = lidOpen - ? Configuration.KEYBOARDHIDDEN_NO - : Configuration.KEYBOARDHIDDEN_YES; + config.hardKeyboardHidden = determineHiddenState(lidOpen, + mLidKeyboardAccessibility, Configuration.HARDKEYBOARDHIDDEN_YES, + Configuration.HARDKEYBOARDHIDDEN_NO); + config.navigationHidden = determineHiddenState(lidOpen, + mLidNavigationAccessibility, Configuration.NAVIGATIONHIDDEN_YES, + Configuration.NAVIGATIONHIDDEN_NO); + config.keyboardHidden = (config.hardKeyboardHidden + == Configuration.HARDKEYBOARDHIDDEN_NO || mHasSoftInput) + ? Configuration.KEYBOARDHIDDEN_NO + : Configuration.KEYBOARDHIDDEN_YES; } public boolean isCheekPressedAgainstScreen(MotionEvent ev) { @@ -566,6 +708,8 @@ public class PhoneWindowManager implements WindowManagerPolicy { return PRIORITY_PHONE_LAYER; case TYPE_TOAST: return TOAST_LAYER; + case TYPE_WALLPAPER: + return WALLPAPER_LAYER; } Log.e(TAG, "Unknown window type: " + type); return APPLICATION_LAYER; @@ -588,6 +732,19 @@ public class PhoneWindowManager implements WindowManagerPolicy { return 0; } + public int getMaxWallpaperLayer() { + return STATUS_BAR_LAYER; + } + + public boolean doesForceHide(WindowState win, WindowManager.LayoutParams attrs) { + return attrs.type == WindowManager.LayoutParams.TYPE_KEYGUARD; + } + + public boolean canBeForceHidden(WindowState win, WindowManager.LayoutParams attrs) { + return attrs.type != WindowManager.LayoutParams.TYPE_STATUS_BAR + && attrs.type != WindowManager.LayoutParams.TYPE_WALLPAPER; + } + /** {@inheritDoc} */ public View addStartingWindow(IBinder appToken, String packageName, int theme, CharSequence nonLocalizedLabel, @@ -756,6 +913,11 @@ public class PhoneWindowManager implements WindowManagerPolicy { return 0; } + public Animation createForceHideEnterAnimation() { + return AnimationUtils.loadAnimation(mContext, + com.android.internal.R.anim.lock_screen_behind_enter); + } + static ITelephony getPhoneInterface() { return ITelephony.Stub.asInterface(ServiceManager.checkService(Context.TELEPHONY_SERVICE)); } @@ -775,7 +937,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { /** {@inheritDoc} */ public boolean interceptKeyTi(WindowState win, int code, int metaKeys, boolean down, - int repeatCount) { + int repeatCount, int flags) { boolean keyguardOn = keyguardOn(); if (false) { @@ -800,32 +962,36 @@ public class PhoneWindowManager implements WindowManagerPolicy { if (!down) { mHomePressed = false; - // If an incoming call is ringing, HOME is totally disabled. - // (The user is already on the InCallScreen at this point, - // and his ONLY options are to answer or reject the call.) - boolean incomingRinging = false; - try { - ITelephony phoneServ = getPhoneInterface(); - if (phoneServ != null) { - incomingRinging = phoneServ.isRinging(); + if ((flags&KeyEvent.FLAG_CANCELED) == 0) { + // If an incoming call is ringing, HOME is totally disabled. + // (The user is already on the InCallScreen at this point, + // and his ONLY options are to answer or reject the call.) + boolean incomingRinging = false; + try { + ITelephony phoneServ = getPhoneInterface(); + if (phoneServ != null) { + incomingRinging = phoneServ.isRinging(); + } else { + Log.w(TAG, "Unable to find ITelephony interface"); + } + } catch (RemoteException ex) { + Log.w(TAG, "RemoteException from getPhoneInterface()", ex); + } + + if (incomingRinging) { + Log.i(TAG, "Ignoring HOME; there's a ringing incoming call."); } else { - Log.w(TAG, "Unable to find ITelephony interface"); + launchHomeFromHotKey(); } - } catch (RemoteException ex) { - Log.w(TAG, "RemoteException from getPhoneInterface()", ex); - } - - if (incomingRinging) { - Log.i(TAG, "Ignoring HOME; there's a ringing incoming call."); } else { - launchHomeFromHotKey(); + Log.i(TAG, "Ignoring HOME; event canceled."); } } } return true; } - + // First we always handle the home key here, so applications // can never break it, although if keyguard is on, we do let // it handle it, because that gives us the correct 5 second @@ -942,9 +1108,9 @@ public class PhoneWindowManager implements WindowManagerPolicy { * given the situation with the keyguard. */ void launchHomeFromHotKey() { - if (mKeyguardMediator.isShowing()) { + if (!mHideLockScreen && mKeyguardMediator.isShowing()) { // don't launch home if keyguard showing - } else if (mKeyguardMediator.isInputRestricted()) { + } else if (!mHideLockScreen && mKeyguardMediator.isInputRestricted()) { // when in keyguard restricted mode, must first verify unlock // before launching home mKeyguardMediator.verifyUnlock(new OnKeyguardExitResult() { @@ -955,7 +1121,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { } catch (RemoteException e) { } sendCloseSystemWindows(); - mContext.startActivity(mHomeIntent); + startDockOrHome(); } } }); @@ -966,7 +1132,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { } catch (RemoteException e) { } sendCloseSystemWindows(); - mContext.startActivity(mHomeIntent); + startDockOrHome(); } } @@ -994,7 +1160,8 @@ public class PhoneWindowManager implements WindowManagerPolicy { mTopFullscreenOpaqueWindowState = null; mForceStatusBar = false; - mHideKeyguard = false; + mHideLockScreen = false; + mDismissKeyguard = false; // decide where the status bar goes ahead of time if (mStatusBar != null) { @@ -1192,20 +1359,24 @@ public class PhoneWindowManager implements WindowManagerPolicy { win.computeFrameLw(pf, df, cf, vf); - if (win.isVisibleLw()) { + if (mTopFullscreenOpaqueWindowState == null && + win.isVisibleOrBehindKeyguardLw()) { if ((attrs.flags & FLAG_FORCE_NOT_FULLSCREEN) != 0) { mForceStatusBar = true; - } else if (mTopFullscreenOpaqueWindowState == null - && attrs.type >= FIRST_APPLICATION_WINDOW + } + if (attrs.type >= FIRST_APPLICATION_WINDOW && attrs.type <= LAST_APPLICATION_WINDOW && win.fillsScreenLw(mW, mH, false, false)) { if (DEBUG_LAYOUT) Log.v(TAG, "Fullscreen window: " + win); mTopFullscreenOpaqueWindowState = win; + if ((attrs.flags & FLAG_SHOW_WHEN_LOCKED) != 0) { + if (localLOGV) Log.v(TAG, "Setting mHideLockScreen to true by win " + win); + mHideLockScreen = true; + } } - if ((attrs.flags & FLAG_SHOW_WHEN_LOCKED) != 0) { - // TODO Add a check for the window to be full screen - if (localLOGV) Log.i(TAG, "Setting mHideKeyguard to true by win " + win); - mHideKeyguard = true; + if ((attrs.flags & FLAG_DISMISS_KEYGUARD) != 0) { + if (localLOGV) Log.v(TAG, "Setting mDismissKeyguard to true by win " + win); + mDismissKeyguard = true; } } @@ -1229,15 +1400,15 @@ public class PhoneWindowManager implements WindowManagerPolicy { } /** {@inheritDoc} */ - public boolean finishLayoutLw() { - boolean changed = false; + public int finishLayoutLw() { + int changes = 0; boolean hiding = false; if (mStatusBar != null) { - //Log.i(TAG, "force=" + mForceStatusBar - // + " top=" + mTopFullscreenOpaqueWindowState); + if (localLOGV) Log.i(TAG, "force=" + mForceStatusBar + + " top=" + mTopFullscreenOpaqueWindowState); if (mForceStatusBar) { if (DEBUG_LAYOUT) Log.v(TAG, "Showing status bar"); - changed |= mStatusBar.showLw(true); + if (mStatusBar.showLw(true)) changes |= FINISH_LAYOUT_REDO_LAYOUT; } else if (mTopFullscreenOpaqueWindowState != null) { //Log.i(TAG, "frame: " + mTopFullscreenOpaqueWindowState.getFrameLw() // + " shown frame: " + mTopFullscreenOpaqueWindowState.getShownFrameLw()); @@ -1248,26 +1419,49 @@ public class PhoneWindowManager implements WindowManagerPolicy { (lp.flags & WindowManager.LayoutParams.FLAG_FULLSCREEN) != 0; if (hideStatusBar) { if (DEBUG_LAYOUT) Log.v(TAG, "Hiding status bar"); - changed |= mStatusBar.hideLw(true); + if (mStatusBar.hideLw(true)) changes |= FINISH_LAYOUT_REDO_LAYOUT; hiding = true; } else { if (DEBUG_LAYOUT) Log.v(TAG, "Showing status bar"); - changed |= mStatusBar.showLw(true); + if (mStatusBar.showLw(true)) changes |= FINISH_LAYOUT_REDO_LAYOUT; } } } // Hide the key guard if a visible window explicitly specifies that it wants to be displayed // when the screen is locked if (mKeyguard != null) { - if (localLOGV) Log.i(TAG, "finishLayoutLw::mHideKeyguard="+mHideKeyguard); - if (mHideKeyguard) { - changed |= mKeyguard.hideLw(true); + if (localLOGV) Log.v(TAG, "finishLayoutLw::mHideKeyguard="+mHideLockScreen); + if (mDismissKeyguard && !mKeyguardMediator.isSecure()) { + if (mKeyguard.hideLw(false)) { + changes |= FINISH_LAYOUT_REDO_LAYOUT + | FINISH_LAYOUT_REDO_CONFIG + | FINISH_LAYOUT_REDO_WALLPAPER; + } + if (mKeyguardMediator.isShowing()) { + mHandler.post(new Runnable() { + public void run() { + mKeyguardMediator.keyguardDone(false, false); + } + }); + } + } else if (mHideLockScreen) { + if (mKeyguard.hideLw(false)) { + mKeyguardMediator.setHidden(true); + changes |= FINISH_LAYOUT_REDO_LAYOUT + | FINISH_LAYOUT_REDO_CONFIG + | FINISH_LAYOUT_REDO_WALLPAPER; + } } else { - changed |= mKeyguard.showLw(true); + if (mKeyguard.showLw(false)) { + mKeyguardMediator.setHidden(false); + changes |= FINISH_LAYOUT_REDO_LAYOUT + | FINISH_LAYOUT_REDO_CONFIG + | FINISH_LAYOUT_REDO_WALLPAPER; + } } } - if (changed && hiding) { + if (changes != 0 && hiding) { IStatusBar sbs = IStatusBar.Stub.asInterface(ServiceManager.getService("statusbar")); if (sbs != null) { try { @@ -1278,7 +1472,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { } } - return changed; + return changes; } /** {@inheritDoc} */ @@ -1299,15 +1493,23 @@ public class PhoneWindowManager implements WindowManagerPolicy { public boolean preprocessInputEventTq(RawInputEvent event) { switch (event.type) { case RawInputEvent.EV_SW: - if (event.keycode == 0) { + if (event.keycode == RawInputEvent.SW_LID) { // lid changed state mLidOpen = event.value == 0; + boolean awakeNow = mKeyguardMediator.doLidChangeTq(mLidOpen); updateRotation(Surface.FLAGS_ORIENTATION_ANIMATION_DISABLE); - if (keyguardIsShowingTq()) { + if (awakeNow) { + // If the lid opening and we don't have to keep the + // keyguard up, then we can turn on the screen + // immediately. + mKeyguardMediator.pokeWakelock(); + } else if (keyguardIsShowingTq()) { if (mLidOpen) { - // only do this if it's opening -- closing the device shouldn't turn it - // off, but it also shouldn't turn it on. - mKeyguardMediator.pokeWakelock(); + // If we are opening the lid and not hiding the + // keyguard, then we need to have it turn on the + // screen once it is shown. + mKeyguardMediator.onWakeKeyWhenKeyguardShowingTq( + KeyEvent.KEYCODE_POWER); } } else { // Light up the keyboard if we are sliding up. @@ -1365,27 +1567,22 @@ public class PhoneWindowManager implements WindowManagerPolicy { * @return Whether music is being played right now. */ boolean isMusicActive() { - final IAudioService audio = getAudioInterface(); - if (audio == null) { - Log.w(TAG, "isMusicActive: couldn't get IAudioService reference"); - return false; - } - try { - return audio.isMusicActive(); - } catch (RemoteException e) { - Log.w(TAG, "IAudioService.isMusicActive() threw RemoteException " + e); + final AudioManager am = (AudioManager)mContext.getSystemService(Context.AUDIO_SERVICE); + if (am == null) { + Log.w(TAG, "isMusicActive: couldn't get AudioManager reference"); return false; } + return am.isMusicActive(); } /** * Tell the audio service to adjust the volume appropriate to the event. * @param keycode */ - void sendVolToMusic(int keycode) { + void handleVolumeKey(int stream, int keycode) { final IAudioService audio = getAudioInterface(); if (audio == null) { - Log.w(TAG, "sendVolToMusic: couldn't get IAudioService reference"); + Log.w(TAG, "handleVolumeKey: couldn't get IAudioService reference"); return; } try { @@ -1393,8 +1590,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { // during the call, but we do it as a precaution for the rare possibility // that the music stops right before we call this mBroadcastWakeLock.acquire(); - audio.adjustStreamVolume( - AudioManager.STREAM_MUSIC, + audio.adjustStreamVolume(stream, keycode == KeyEvent.KEYCODE_VOLUME_UP ? AudioManager.ADJUST_RAISE : AudioManager.ADJUST_LOWER, @@ -1456,12 +1652,22 @@ public class PhoneWindowManager implements WindowManagerPolicy { } else if (isMusicActive()) { // when keyguard is showing and screen off, we need // to handle the volume key for music here - sendVolToMusic(event.keycode); + handleVolumeKey(AudioManager.STREAM_MUSIC, event.keycode); } } } } } else if (!screenIsOn) { + // If we are in-call with screen off and keyguard is not showing, + // then handle the volume key ourselves. + // This is necessary because the phone app will disable the keyguard + // when the proximity sensor is in use. + if (isInCall() && event.type == RawInputEvent.EV_KEY && + (event.keycode == KeyEvent.KEYCODE_VOLUME_DOWN + || event.keycode == KeyEvent.KEYCODE_VOLUME_UP)) { + result &= ~ACTION_PASS_TO_USER; + handleVolumeKey(AudioManager.STREAM_VOICE_CALL, event.keycode); + } if (isWakeKey) { // a wake key has a sole purpose of waking the device; don't pass // it to the user @@ -1475,33 +1681,41 @@ public class PhoneWindowManager implements WindowManagerPolicy { boolean down = event.value != 0; if (type == RawInputEvent.EV_KEY) { - if (code == KeyEvent.KEYCODE_ENDCALL) { + if (code == KeyEvent.KEYCODE_ENDCALL + || code == KeyEvent.KEYCODE_POWER) { if (down) { - boolean hungUp = false; + boolean handled = false; // key repeats are generated by the window manager, and we don't see them // here, so unless the driver is doing something it shouldn't be, we know // this is the real press event. - try { - ITelephony phoneServ = getPhoneInterface(); - if (phoneServ != null) { - hungUp = phoneServ.endCall(); - } else { - Log.w(TAG, "!!! Unable to find ITelephony interface !!!"); + ITelephony phoneServ = getPhoneInterface(); + if (phoneServ != null) { + try { + if (code == KeyEvent.KEYCODE_ENDCALL) { + handled = phoneServ.endCall(); + } else if (code == KeyEvent.KEYCODE_POWER && phoneServ.isRinging()) { + // Pressing power during incoming call should silence the ringer + phoneServ.silenceRinger(); + handled = true; + } + } catch (RemoteException ex) { + Log.w(TAG, "ITelephony threw RemoteException" + ex); } - } catch (RemoteException ex) { - Log.w(TAG, "ITelephony.endCall() threw RemoteException" + ex); + } else { + Log.w(TAG, "!!! Unable to find ITelephony interface !!!"); } - if (hungUp || !screenIsOn) { + // power button should turn off screen in addition to hanging up the phone + if ((handled && code != KeyEvent.KEYCODE_POWER) || !screenIsOn) { mShouldTurnOffOnKeyUp = false; } else { // only try to turn off the screen if we didn't already hang up mShouldTurnOffOnKeyUp = true; - mHandler.postDelayed(mEndCallLongPress, + mHandler.postDelayed(mPowerLongPress, ViewConfiguration.getGlobalActionKeyTimeout()); result &= ~ACTION_PASS_TO_USER; } } else { - mHandler.removeCallbacks(mEndCallLongPress); + mHandler.removeCallbacks(mPowerLongPress); if (mShouldTurnOffOnKeyUp) { mShouldTurnOffOnKeyUp = false; boolean gohome = (mEndcallBehavior & ENDCALL_HOME) != 0; @@ -1620,7 +1834,34 @@ public class PhoneWindowManager implements WindowManagerPolicy { mBroadcastWakeLock.release(); } }; - + + BroadcastReceiver mBatteryReceiver = new BroadcastReceiver() { + public void onReceive(Context context, Intent intent) { + updatePlugged(intent); + updateDockKeepingScreenOn(); + } + }; + + BroadcastReceiver mDockReceiver = new BroadcastReceiver() { + public void onReceive(Context context, Intent intent) { + mDockState = intent.getIntExtra(Intent.EXTRA_DOCK_STATE, + Intent.EXTRA_DOCK_STATE_UNDOCKED); + boolean watchBattery = mDockState != Intent.EXTRA_DOCK_STATE_UNDOCKED; + if (watchBattery != mRegisteredBatteryReceiver) { + mRegisteredBatteryReceiver = watchBattery; + if (watchBattery) { + updatePlugged(mContext.registerReceiver(mBatteryReceiver, + mBatteryStatusFilter)); + } else { + mContext.unregisterReceiver(mBatteryReceiver); + } + } + updateRotation(Surface.FLAGS_ORIENTATION_ANIMATION_DISABLE); + updateDockKeepingScreenOn(); + updateOrientationListenerLp(); + } + }; + /** {@inheritDoc} */ public boolean isWakeRelMovementTq(int device, int classes, RawInputEvent event) { @@ -1736,7 +1977,11 @@ public class PhoneWindowManager implements WindowManagerPolicy { // or orientation sensor disabled //or case.unspecified if (mLidOpen) { - return Surface.ROTATION_90; + return mLidOpenRotation; + } else if (mDockState == Intent.EXTRA_DOCK_STATE_CAR && mCarDockRotation >= 0) { + return mCarDockRotation; + } else if (mDockState == Intent.EXTRA_DOCK_STATE_DESK && mDeskDockRotation >= 0) { + return mDeskDockRotation; } else { if (useSensorForOrientationLp(orientation)) { // If the user has enabled auto rotation by default, do it. @@ -1751,8 +1996,19 @@ public class PhoneWindowManager implements WindowManagerPolicy { public boolean detectSafeMode() { try { int menuState = mWindowManager.getKeycodeState(KeyEvent.KEYCODE_MENU); - mSafeMode = menuState > 0; - Log.i(TAG, "Menu key state: " + menuState + " safeMode=" + mSafeMode); + int sState = mWindowManager.getKeycodeState(KeyEvent.KEYCODE_S); + int dpadState = mWindowManager.getKeycodeState(KeyEvent.KEYCODE_DPAD_CENTER); + int trackballState = mWindowManager.getScancodeState(RawInputEvent.BTN_MOUSE); + mSafeMode = menuState > 0 || sState > 0 || dpadState > 0 || trackballState > 0; + performHapticFeedbackLw(null, mSafeMode + ? HapticFeedbackConstants.SAFE_MODE_ENABLED + : HapticFeedbackConstants.SAFE_MODE_DISABLED, true); + if (mSafeMode) { + Log.i(TAG, "SAFE MODE ENABLED (menu=" + menuState + " s=" + sState + + " dpad=" + dpadState + " trackball=" + trackballState + ")"); + } else { + Log.i(TAG, "SAFE MODE not enabled"); + } return mSafeMode; } catch (RemoteException e) { // Doom! (it's also local) @@ -1760,23 +2016,25 @@ public class PhoneWindowManager implements WindowManagerPolicy { } } + static long[] getLongIntArray(Resources r, int resid) { + int[] ar = r.getIntArray(resid); + if (ar == null) { + return null; + } + long[] out = new long[ar.length]; + for (int i=0; i<ar.length; i++) { + out[i] = ar[i]; + } + return out; + } + /** {@inheritDoc} */ public void systemReady() { - try { - if (mSafeMode) { - // If the user is holding the menu key code, then we are - // going to boot into safe mode. - ActivityManagerNative.getDefault().enterSafeMode(); - } - // tell the keyguard - mKeyguardMediator.onSystemReady(); - android.os.SystemProperties.set("dev.bootcomplete", "1"); - synchronized (mLock) { - updateOrientationListenerLp(); - mVibrator = new Vibrator(); - } - } catch (RemoteException e) { - // Ignore + // tell the keyguard + mKeyguardMediator.onSystemReady(); + android.os.SystemProperties.set("dev.bootcomplete", "1"); + synchronized (mLock) { + updateOrientationListenerLp(); } } @@ -1787,12 +2045,41 @@ public class PhoneWindowManager implements WindowManagerPolicy { updateRotation(Surface.FLAGS_ORIENTATION_ANIMATION_DISABLE); } + void updateDockKeepingScreenOn() { + if (mPlugged != 0) { + if (localLOGV) Log.v(TAG, "Update: mDockState=" + mDockState + + " mPlugged=" + mPlugged + + " mCarDockKeepsScreenOn" + mCarDockKeepsScreenOn + + " mDeskDockKeepsScreenOn" + mDeskDockKeepsScreenOn); + if (mDockState == Intent.EXTRA_DOCK_STATE_CAR + && (mPlugged&mCarDockKeepsScreenOn) != 0) { + if (!mDockWakeLock.isHeld()) { + mDockWakeLock.acquire(); + } + return; + } else if (mDockState == Intent.EXTRA_DOCK_STATE_DESK + && (mPlugged&mDeskDockKeepsScreenOn) != 0) { + if (!mDockWakeLock.isHeld()) { + mDockWakeLock.acquire(); + } + return; + } + } + + if (mDockWakeLock.isHeld()) { + mDockWakeLock.release(); + } + } + void updateRotation(int animFlags) { mPowerManager.setKeyboardVisibility(mLidOpen); - int rotation= Surface.ROTATION_0; + int rotation = Surface.ROTATION_0; if (mLidOpen) { - // always use landscape if lid is open - rotation = Surface.ROTATION_90; + rotation = mLidOpenRotation; + } else if (mDockState == Intent.EXTRA_DOCK_STATE_CAR && mCarDockRotation >= 0) { + rotation = mCarDockRotation; + } else if (mDockState == Intent.EXTRA_DOCK_STATE_DESK && mDeskDockRotation >= 0) { + rotation = mDeskDockRotation; } //if lid is closed orientation will be portrait try { @@ -1805,6 +2092,53 @@ public class PhoneWindowManager implements WindowManagerPolicy { } /** + * Return an Intent to launch the currently active dock as home. Returns + * null if the standard home should be launched. + * @return + */ + Intent createHomeDockIntent() { + if (mDockState == Intent.EXTRA_DOCK_STATE_UNDOCKED) { + return null; + } + + Intent intent; + if (mDockState == Intent.EXTRA_DOCK_STATE_CAR) { + intent = mCarDockIntent; + } else if (mDockState == Intent.EXTRA_DOCK_STATE_DESK) { + intent = mDeskDockIntent; + } else { + Log.w(TAG, "Unknown dock state: " + mDockState); + return null; + } + + ActivityInfo ai = intent.resolveActivityInfo( + mContext.getPackageManager(), PackageManager.GET_META_DATA); + if (ai == null) { + return null; + } + + if (ai.metaData != null && ai.metaData.getBoolean(Intent.METADATA_DOCK_HOME)) { + intent = new Intent(intent); + intent.setClassName(ai.packageName, ai.name); + return intent; + } + + return null; + } + + void startDockOrHome() { + Intent dock = createHomeDockIntent(); + if (dock != null) { + try { + mContext.startActivity(dock); + return; + } catch (ActivityNotFoundException e) { + } + } + mContext.startActivity(mHomeIntent); + } + + /** * goes to the home screen * @return whether it did anything */ @@ -1816,13 +2150,23 @@ public class PhoneWindowManager implements WindowManagerPolicy { } catch (RemoteException e) { } sendCloseSystemWindows(); - mContext.startActivity(mHomeIntent); + startDockOrHome(); } else { // This code brings home to the front or, if it is already // at the front, puts the device to sleep. try { ActivityManagerNative.getDefault().stopAppSwitches(); sendCloseSystemWindows(); + Intent dock = createHomeDockIntent(); + if (dock != null) { + int result = ActivityManagerNative.getDefault() + .startActivity(null, dock, + dock.resolveTypeIfNeeded(mContext.getContentResolver()), + null, 0, null, null, 0, true /* onlyIfNeeded*/, false); + if (result == IActivityManager.START_RETURN_INTENT_TO_CALLER) { + return false; + } + } int result = ActivityManagerNative.getDefault() .startActivity(null, mHomeIntent, mHomeIntent.resolveTypeIfNeeded(mContext.getContentResolver()), @@ -1847,23 +2191,45 @@ public class PhoneWindowManager implements WindowManagerPolicy { } public boolean performHapticFeedbackLw(WindowState win, int effectId, boolean always) { - if (!always && Settings.System.getInt(mContext.getContentResolver(), - Settings.System.HAPTIC_FEEDBACK_ENABLED, 0) == 0) { + final boolean hapticsDisabled = Settings.System.getInt(mContext.getContentResolver(), + Settings.System.HAPTIC_FEEDBACK_ENABLED, 0) == 0; + if (!always && (hapticsDisabled || mKeyguardMediator.isShowing())) { return false; } switch (effectId) { case HapticFeedbackConstants.LONG_PRESS: - mVibrator.vibrate(LONG_PRESS_VIBE_PATTERN, -1); + mVibrator.vibrate(mLongPressVibePattern, -1); + return true; + case HapticFeedbackConstants.VIRTUAL_KEY: + mVibrator.vibrate(mVirtualKeyVibePattern, -1); + return true; + case HapticFeedbackConstants.SAFE_MODE_DISABLED: + mVibrator.vibrate(mSafeModeDisabledVibePattern, -1); + return true; + case HapticFeedbackConstants.SAFE_MODE_ENABLED: + mVibrator.vibrate(mSafeModeEnabledVibePattern, -1); return true; } return false; } + public void keyFeedbackFromInput(KeyEvent event) { + if (event.getAction() == KeyEvent.ACTION_DOWN + && (event.getFlags()&KeyEvent.FLAG_VIRTUAL_HARD_KEY) != 0) { + performHapticFeedbackLw(null, HapticFeedbackConstants.VIRTUAL_KEY, false); + } + } + public void screenOnStoppedLw() { if (!mKeyguardMediator.isShowing()) { long curTime = SystemClock.uptimeMillis(); mPowerManager.userActivity(curTime, false, LocalPowerManager.OTHER_EVENT); } } + + public boolean allowKeyRepeat() { + // disable key repeat when screen is off + return mScreenOn; + } } diff --git a/policy/com/android/internal/policy/impl/PowerDialog.java b/policy/com/android/internal/policy/impl/PowerDialog.java index f4d4b04..de35bd7 100644 --- a/policy/com/android/internal/policy/impl/PowerDialog.java +++ b/policy/com/android/internal/policy/impl/PowerDialog.java @@ -70,8 +70,11 @@ public class PowerDialog extends Dialog implements OnClickListener, setContentView(com.android.internal.R.layout.power_dialog); getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG); - getWindow().setFlags(WindowManager.LayoutParams.FLAG_BLUR_BEHIND, - WindowManager.LayoutParams.FLAG_BLUR_BEHIND); + if (!getContext().getResources().getBoolean( + com.android.internal.R.bool.config_sf_slowBlur)) { + getWindow().setFlags(WindowManager.LayoutParams.FLAG_BLUR_BEHIND, + WindowManager.LayoutParams.FLAG_BLUR_BEHIND); + } getWindow().setFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM, WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM); diff --git a/policy/com/android/internal/policy/impl/RecentApplicationsDialog.java b/policy/com/android/internal/policy/impl/RecentApplicationsDialog.java index a680cc8..4c46be5 100644 --- a/policy/com/android/internal/policy/impl/RecentApplicationsDialog.java +++ b/policy/com/android/internal/policy/impl/RecentApplicationsDialog.java @@ -20,7 +20,6 @@ import android.app.ActivityManager; import android.app.Dialog; import android.app.StatusBarManager; import android.content.BroadcastReceiver; -import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; @@ -30,13 +29,10 @@ import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.graphics.drawable.Drawable; import android.os.Bundle; -import android.os.Handler; -import android.os.Message; import android.view.View; import android.view.Window; import android.view.WindowManager; import android.view.View.OnClickListener; -import android.widget.ImageView; import android.widget.TextView; import java.util.List; @@ -59,7 +55,7 @@ public class RecentApplicationsDialog extends Dialog implements OnClickListener private int mIconSize; public RecentApplicationsDialog(Context context) { - super(context); + super(context, com.android.internal.R.style.Theme_Dialog_RecentApplications); final Resources resources = context.getResources(); mIconSize = (int) resources.getDimension(android.R.dimen.app_icon_size); @@ -84,8 +80,8 @@ public class RecentApplicationsDialog extends Dialog implements OnClickListener Window theWindow = getWindow(); theWindow.requestFeature(Window.FEATURE_NO_TITLE); theWindow.setType(WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG); - theWindow.setFlags(WindowManager.LayoutParams.FLAG_BLUR_BEHIND, - WindowManager.LayoutParams.FLAG_BLUR_BEHIND); + theWindow.setFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND, + WindowManager.LayoutParams.FLAG_DIM_BEHIND); theWindow.setFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM, WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM); theWindow.setTitle("Recents"); diff --git a/policy/com/android/internal/policy/impl/SimUnlockScreen.java b/policy/com/android/internal/policy/impl/SimUnlockScreen.java index 0f7fe32..3881d11 100644 --- a/policy/com/android/internal/policy/impl/SimUnlockScreen.java +++ b/policy/com/android/internal/policy/impl/SimUnlockScreen.java @@ -182,9 +182,12 @@ public class SimUnlockScreen extends LinearLayout implements KeyguardScreen, Vie mSimUnlockProgressDialog.setCancelable(false); mSimUnlockProgressDialog.getWindow().setType( WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG); - mSimUnlockProgressDialog.getWindow().setFlags( - WindowManager.LayoutParams.FLAG_BLUR_BEHIND, - WindowManager.LayoutParams.FLAG_BLUR_BEHIND); + if (!mContext.getResources().getBoolean( + com.android.internal.R.bool.config_sf_slowBlur)) { + mSimUnlockProgressDialog.getWindow().setFlags( + WindowManager.LayoutParams.FLAG_BLUR_BEHIND, + WindowManager.LayoutParams.FLAG_BLUR_BEHIND); + } } return mSimUnlockProgressDialog; } diff --git a/policy/com/android/internal/policy/impl/UnlockScreen.java b/policy/com/android/internal/policy/impl/UnlockScreen.java index 9aedf90..a5032b3 100644 --- a/policy/com/android/internal/policy/impl/UnlockScreen.java +++ b/policy/com/android/internal/policy/impl/UnlockScreen.java @@ -17,30 +17,33 @@ package com.android.internal.policy.impl; import android.content.Context; -import android.content.ServiceConnection; import android.os.CountDownTimer; import android.os.SystemClock; -import android.view.KeyEvent; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.view.MotionEvent; import android.widget.Button; -import android.widget.ImageView; import android.widget.TextView; +import android.text.format.DateFormat; +import android.text.TextUtils; import com.android.internal.R; +import com.android.internal.telephony.IccCard; import com.android.internal.widget.LinearLayoutWithDefaultTouchRecepient; import com.android.internal.widget.LockPatternUtils; import com.android.internal.widget.LockPatternView; +import com.android.internal.widget.LockPatternView.Cell; import java.util.List; +import java.util.Date; /** * This is the screen that shows the 9 circle unlock widget and instructs * the user how to unlock their device, or make an emergency call. */ class UnlockScreen extends LinearLayoutWithDefaultTouchRecepient - implements KeyguardScreen, KeyguardUpdateMonitor.ConfigurationChangeCallback { + implements KeyguardScreen, KeyguardUpdateMonitor.ConfigurationChangeCallback, + KeyguardUpdateMonitor.InfoCallback, KeyguardUpdateMonitor.SimStateCallback { private static final String TAG = "UnlockScreen"; @@ -50,6 +53,9 @@ class UnlockScreen extends LinearLayoutWithDefaultTouchRecepient // how long we stay awake once the user is ready to enter a pattern private static final int UNLOCK_PATTERN_WAKE_INTERVAL_MS = 7000; + // how many cells the user has to cross before we poke the wakelock + private static final int MIN_PATTERN_BEFORE_POKE_WAKELOCK = 2; + private int mFailedPatternAttemptsSinceLastTimeout = 0; private int mTotalFailedPatternAttempts = 0; private CountDownTimer mCountdownTimer = null; @@ -58,10 +64,35 @@ class UnlockScreen extends LinearLayoutWithDefaultTouchRecepient private final KeyguardUpdateMonitor mUpdateMonitor; private final KeyguardScreenCallback mCallback; + /** + * whether there is a fallback option available when the pattern is forgotten. + */ + private boolean mEnableFallback; + private boolean mCreatedInPortrait; - private ImageView mUnlockIcon; - private TextView mUnlockHeader; + private TextView mCarrier; + private TextView mCenterDot; + private TextView mDate; + private TextView mTime; + + // are we showing battery information? + private boolean mShowingBatteryInfo = false; + + // last known plugged in state + private boolean mPluggedIn = false; + + // last known battery level + private int mBatteryLevel = 100; + + private String mNextAlarm = null; + + private String mInstructions = null; + private TextView mStatus1; + private TextView mStatusSep; + private TextView mStatus2; + + private LockPatternView mLockPatternView; private ViewGroup mFooterNormal; @@ -86,9 +117,6 @@ class UnlockScreen extends LinearLayoutWithDefaultTouchRecepient private Button mForgotPatternButton; - private ServiceConnection mServiceConnection; - - enum FooterMode { Normal, ForgotLockPattern, @@ -104,6 +132,7 @@ class UnlockScreen extends LinearLayoutWithDefaultTouchRecepient case ForgotLockPattern: mFooterNormal.setVisibility(View.GONE); mFooterForgotPattern.setVisibility(View.VISIBLE); + mForgotPatternButton.setVisibility(View.VISIBLE); break; case VerifyUnlocked: mFooterNormal.setVisibility(View.GONE); @@ -117,12 +146,15 @@ class UnlockScreen extends LinearLayoutWithDefaultTouchRecepient * @param updateMonitor Used to lookup state affecting keyguard. * @param callback Used to notify the manager when we're done, etc. * @param totalFailedAttempts The current number of failed attempts. + * @param enableFallback True if a backup unlock option is available when the user has forgotten + * their pattern (e.g they have a google account so we can show them the account based + * backup option). */ UnlockScreen(Context context, - LockPatternUtils lockPatternUtils, - KeyguardUpdateMonitor updateMonitor, - KeyguardScreenCallback callback, - int totalFailedAttempts) { + LockPatternUtils lockPatternUtils, + KeyguardUpdateMonitor updateMonitor, + KeyguardScreenCallback callback, + int totalFailedAttempts) { super(context); mLockPatternUtils = lockPatternUtils; mUpdateMonitor = updateMonitor; @@ -136,12 +168,22 @@ class UnlockScreen extends LinearLayoutWithDefaultTouchRecepient LayoutInflater.from(context).inflate(R.layout.keyguard_screen_unlock_landscape, this, true); } - mUnlockIcon = (ImageView) findViewById(R.id.unlockLockIcon); + mCarrier = (TextView) findViewById(R.id.carrier); + mCenterDot = (TextView) findViewById(R.id.centerDot); + mDate = (TextView) findViewById(R.id.date); + mTime = (TextView) findViewById(R.id.time); - mLockPatternView = (LockPatternView) findViewById(R.id.lockPattern); - mUnlockHeader = (TextView) findViewById(R.id.headerText); + mCenterDot.setText("|"); + refreshTimeAndDateDisplay(); - mUnlockHeader.setText(R.string.lockscreen_pattern_instructions); + mStatus1 = (TextView) findViewById(R.id.status1); + mStatusSep = (TextView) findViewById(R.id.statusSep); + mStatus2 = (TextView) findViewById(R.id.status2); + + resetStatusInfo(); + + + mLockPatternView = (LockPatternView) findViewById(R.id.lockPattern); mFooterNormal = (ViewGroup) findViewById(R.id.footerNormal); mFooterForgotPattern = (ViewGroup) findViewById(R.id.footerForgotPattern); @@ -164,8 +206,7 @@ class UnlockScreen extends LinearLayoutWithDefaultTouchRecepient mForgotPatternButton.setOnClickListener(new OnClickListener() { public void onClick(View v) { - mLockPatternUtils.setPermanentlyLocked(true); - mCallback.goToUnlockScreen(); + mCallback.forgotPattern(true); } }); @@ -187,19 +228,106 @@ class UnlockScreen extends LinearLayoutWithDefaultTouchRecepient updateFooter(FooterMode.Normal); mCreatedInPortrait = updateMonitor.isInPortrait(); + updateMonitor.registerInfoCallback(this); + updateMonitor.registerSimStateCallback(this); updateMonitor.registerConfigurationChangeCallback(this); setFocusableInTouchMode(true); + + // until we get an update... + mCarrier.setText( + LockScreen.getCarrierString( + mUpdateMonitor.getTelephonyPlmn(), + mUpdateMonitor.getTelephonySpn())); } - @Override - public boolean onKeyDown(int keyCode, KeyEvent event) { - if (keyCode == KeyEvent.KEYCODE_BACK) { - mCallback.goToLockScreen(); - return true; + public void setEnableFallback(boolean state) { + mEnableFallback = state; + } + + private void resetStatusInfo() { + mInstructions = null; + mShowingBatteryInfo = mUpdateMonitor.shouldShowBatteryInfo(); + mPluggedIn = mUpdateMonitor.isDevicePluggedIn(); + mBatteryLevel = mUpdateMonitor.getBatteryLevel(); + mNextAlarm = mLockPatternUtils.getNextAlarm(); + updateStatusLines(); + } + + private void updateStatusLines() { + if (mInstructions != null) { + // instructions only + mStatus1.setText(mInstructions); + if (TextUtils.isEmpty(mInstructions)) { + mStatus1.setCompoundDrawablesWithIntrinsicBounds(0, 0, 0, 0); + } else { + mStatus1.setCompoundDrawablesWithIntrinsicBounds( + R.drawable.ic_lock_idle_lock, 0, 0, 0); + } + + mStatus1.setVisibility(View.VISIBLE); + mStatusSep.setVisibility(View.GONE); + mStatus2.setVisibility(View.GONE); + } else if (mShowingBatteryInfo && mNextAlarm == null) { + // battery only + if (mPluggedIn) { + if (mBatteryLevel >= 100) { + mStatus1.setText(getContext().getString(R.string.lockscreen_charged)); + } else { + mStatus1.setText(getContext().getString(R.string.lockscreen_plugged_in, mBatteryLevel)); + } + } else { + mStatus1.setText(getContext().getString(R.string.lockscreen_low_battery)); + } + mStatus1.setCompoundDrawablesWithIntrinsicBounds(R.drawable.ic_lock_idle_charging, 0, 0, 0); + + mStatus1.setVisibility(View.VISIBLE); + mStatusSep.setVisibility(View.GONE); + mStatus2.setVisibility(View.GONE); + + } else if (mNextAlarm != null && !mShowingBatteryInfo) { + // alarm only + mStatus1.setText(mNextAlarm); + mStatus1.setCompoundDrawablesWithIntrinsicBounds(R.drawable.ic_lock_idle_alarm, 0, 0, 0); + + mStatus1.setVisibility(View.VISIBLE); + mStatusSep.setVisibility(View.GONE); + mStatus2.setVisibility(View.GONE); + } else if (mNextAlarm != null && mShowingBatteryInfo) { + // both battery and next alarm + mStatus1.setText(mNextAlarm); + mStatusSep.setText("|"); + mStatus2.setText(getContext().getString( + R.string.lockscreen_battery_short, + Math.min(100, mBatteryLevel))); + mStatus1.setCompoundDrawablesWithIntrinsicBounds(R.drawable.ic_lock_idle_alarm, 0, 0, 0); + if (mPluggedIn) { + mStatus2.setCompoundDrawablesWithIntrinsicBounds(R.drawable.ic_lock_idle_charging, 0, 0, 0); + } else { + mStatus2.setCompoundDrawablesWithIntrinsicBounds(0, 0, 0, 0); + } + + mStatus1.setVisibility(View.VISIBLE); + mStatusSep.setVisibility(View.VISIBLE); + mStatus2.setVisibility(View.VISIBLE); + } else { + // nothing specific to show; show general instructions + mStatus1.setText(R.string.lockscreen_pattern_instructions); + mStatus1.setCompoundDrawablesWithIntrinsicBounds(R.drawable.ic_lock_idle_lock, 0, 0, 0); + + mStatus1.setVisibility(View.VISIBLE); + mStatusSep.setVisibility(View.GONE); + mStatus2.setVisibility(View.GONE); } - return false; } + + private void refreshTimeAndDateDisplay() { + Date now = new Date(); + mTime.setText(DateFormat.getTimeFormat(getContext()).format(now)); + mDate.setText(DateFormat.getMediumDateFormat(getContext()).format(now)); + } + + @Override public boolean dispatchTouchEvent(MotionEvent ev) { // as long as the user is entering a pattern (i.e sending a touch @@ -210,11 +338,40 @@ class UnlockScreen extends LinearLayoutWithDefaultTouchRecepient ((SystemClock.elapsedRealtime() - mLastPokeTime) > (UNLOCK_PATTERN_WAKE_INTERVAL_MS - 100))) { mLastPokeTime = SystemClock.elapsedRealtime(); - mCallback.pokeWakelock(UNLOCK_PATTERN_WAKE_INTERVAL_MS); } return result; } + + // ---------- InfoCallback + + /** {@inheritDoc} */ + public void onRefreshBatteryInfo(boolean showBatteryInfo, boolean pluggedIn, int batteryLevel) { + mShowingBatteryInfo = showBatteryInfo; + mPluggedIn = pluggedIn; + mBatteryLevel = batteryLevel; + updateStatusLines(); + } + + /** {@inheritDoc} */ + public void onTimeChanged() { + refreshTimeAndDateDisplay(); + } + + /** {@inheritDoc} */ + public void onRefreshCarrierInfo(CharSequence plmn, CharSequence spn) { + mCarrier.setText(LockScreen.getCarrierString(plmn, spn)); + } + + // ---------- SimStateCallback + + /** {@inheritDoc} */ + public void onSimStateChanged(IccCard.State simState) { + } + + + + /** {@inheritDoc} */ public void onOrientationChange(boolean inPortrait) { if (inPortrait != mCreatedInPortrait) { @@ -241,8 +398,7 @@ class UnlockScreen extends LinearLayoutWithDefaultTouchRecepient /** {@inheritDoc} */ public void onResume() { // reset header - mUnlockHeader.setText(R.string.lockscreen_pattern_instructions); - mUnlockIcon.setVisibility(View.VISIBLE); + resetStatusInfo(); // reset lock pattern mLockPatternView.enableInput(); @@ -262,10 +418,11 @@ class UnlockScreen extends LinearLayoutWithDefaultTouchRecepient // the footer depends on how many total attempts the user has failed if (mCallback.isVerifyUnlockOnly()) { updateFooter(FooterMode.VerifyUnlocked); - } else if (mTotalFailedPatternAttempts < LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT) { - updateFooter(FooterMode.Normal); - } else { + } else if (mEnableFallback && + (mTotalFailedPatternAttempts >= LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT)) { updateFooter(FooterMode.ForgotLockPattern); + } else { + updateFooter(FooterMode.Normal); } } @@ -274,6 +431,15 @@ class UnlockScreen extends LinearLayoutWithDefaultTouchRecepient mUpdateMonitor.removeCallback(this); } + @Override + public void onWindowFocusChanged(boolean hasWindowFocus) { + super.onWindowFocusChanged(hasWindowFocus); + if (hasWindowFocus) { + // when timeout dialog closes we want to update our state + onResume(); + } + } + private class UnlockPatternListener implements LockPatternView.OnPatternListener { @@ -284,15 +450,25 @@ class UnlockScreen extends LinearLayoutWithDefaultTouchRecepient public void onPatternCleared() { } + public void onPatternCellAdded(List<Cell> pattern) { + // To guard against accidental poking of the wakelock, look for + // the user actually trying to draw a pattern of some minimal length. + if (pattern.size() > MIN_PATTERN_BEFORE_POKE_WAKELOCK) { + mCallback.pokeWakelock(UNLOCK_PATTERN_WAKE_INTERVAL_MS); + } + } + public void onPatternDetected(List<LockPatternView.Cell> pattern) { if (mLockPatternUtils.checkPattern(pattern)) { mLockPatternView .setDisplayMode(LockPatternView.DisplayMode.Correct); - mUnlockIcon.setVisibility(View.GONE); - mUnlockHeader.setText(""); + mInstructions = ""; + updateStatusLines(); mCallback.keyguardDone(true); } else { - mCallback.pokeWakelock(UNLOCK_PATTERN_WAKE_INTERVAL_MS); + if (pattern.size() > MIN_PATTERN_BEFORE_POKE_WAKELOCK) { + mCallback.pokeWakelock(UNLOCK_PATTERN_WAKE_INTERVAL_MS); + } mLockPatternView.setDisplayMode(LockPatternView.DisplayMode.Wrong); if (pattern.size() >= LockPatternUtils.MIN_PATTERN_REGISTER_FAIL) { mTotalFailedPatternAttempts++; @@ -304,8 +480,9 @@ class UnlockScreen extends LinearLayoutWithDefaultTouchRecepient handleAttemptLockout(deadline); return; } - mUnlockIcon.setVisibility(View.VISIBLE); - mUnlockHeader.setText(R.string.lockscreen_pattern_wrong); + // TODO mUnlockIcon.setVisibility(View.VISIBLE); + mInstructions = getContext().getString(R.string.lockscreen_pattern_wrong); + updateStatusLines(); mLockPatternView.postDelayed( mCancelPatternRunnable, PATTERN_CLEAR_TIMEOUT_MS); @@ -322,20 +499,25 @@ class UnlockScreen extends LinearLayoutWithDefaultTouchRecepient @Override public void onTick(long millisUntilFinished) { int secondsRemaining = (int) (millisUntilFinished / 1000); - mUnlockHeader.setText(getContext().getString( + mInstructions = getContext().getString( R.string.lockscreen_too_many_failed_attempts_countdown, - secondsRemaining)); + secondsRemaining); + updateStatusLines(); } @Override public void onFinish() { mLockPatternView.setEnabled(true); - mUnlockHeader.setText(R.string.lockscreen_pattern_instructions); - mUnlockIcon.setVisibility(View.VISIBLE); + mInstructions = getContext().getString(R.string.lockscreen_pattern_instructions); + updateStatusLines(); + // TODO mUnlockIcon.setVisibility(View.VISIBLE); mFailedPatternAttemptsSinceLastTimeout = 0; - updateFooter(FooterMode.ForgotLockPattern); + if (mEnableFallback) { + updateFooter(FooterMode.ForgotLockPattern); + } else { + updateFooter(FooterMode.Normal); + } } }.start(); } - } |