KeyguardViewManager.java revision 75931e05470992d51d135d09031902fd1b3fdbc7
1/* 2 * Copyright (C) 2007 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package com.android.internal.policy.impl.keyguard; 18 19import android.app.Activity; 20import android.app.ActivityManager; 21import android.content.Context; 22import android.content.pm.ActivityInfo; 23import android.content.res.Configuration; 24import android.content.res.Resources; 25import android.graphics.PixelFormat; 26import android.os.Bundle; 27import android.os.IBinder; 28import android.os.Parcelable; 29import android.os.SystemProperties; 30import android.util.Log; 31import android.util.Slog; 32import android.util.SparseArray; 33import android.view.KeyEvent; 34import android.view.LayoutInflater; 35import android.view.View; 36import android.view.ViewGroup; 37import android.view.ViewManager; 38import android.view.WindowManager; 39import android.widget.FrameLayout; 40 41import com.android.internal.R; 42import com.android.internal.widget.LockPatternUtils; 43 44/** 45 * Manages creating, showing, hiding and resetting the keyguard. Calls back 46 * via {@link KeyguardViewMediator.ViewMediatorCallback} to poke 47 * the wake lock and report that the keyguard is done, which is in turn, 48 * reported to this class by the current {@link KeyguardViewBase}. 49 */ 50public class KeyguardViewManager { 51 private final static boolean DEBUG = KeyguardViewMediator.DEBUG; 52 private static String TAG = "KeyguardViewManager"; 53 public static boolean USE_UPPER_CASE = true; 54 55 // Timeout used for keypresses 56 static final int DIGIT_PRESS_WAKE_MILLIS = 5000; 57 58 private final Context mContext; 59 private final ViewManager mViewManager; 60 private final KeyguardViewMediator.ViewMediatorCallback mViewMediatorCallback; 61 62 private WindowManager.LayoutParams mWindowLayoutParams; 63 private boolean mNeedsInput = false; 64 65 private FrameLayout mKeyguardHost; 66 private KeyguardHostView mKeyguardView; 67 68 private boolean mScreenOn = false; 69 private LockPatternUtils mLockPatternUtils; 70 71 public interface ShowListener { 72 void onShown(IBinder windowToken); 73 }; 74 75 /** 76 * @param context Used to create views. 77 * @param viewManager Keyguard will be attached to this. 78 * @param callback Used to notify of changes. 79 * @param lockPatternUtils 80 */ 81 public KeyguardViewManager(Context context, ViewManager viewManager, 82 KeyguardViewMediator.ViewMediatorCallback callback, 83 LockPatternUtils lockPatternUtils) { 84 mContext = context; 85 mViewManager = viewManager; 86 mViewMediatorCallback = callback; 87 mLockPatternUtils = lockPatternUtils; 88 } 89 90 /** 91 * Show the keyguard. Will handle creating and attaching to the view manager 92 * lazily. 93 */ 94 public synchronized void show(Bundle options) { 95 if (DEBUG) Log.d(TAG, "show(); mKeyguardView==" + mKeyguardView); 96 97 boolean enableScreenRotation = shouldEnableScreenRotation(); 98 99 maybeCreateKeyguardLocked(enableScreenRotation, false, options); 100 maybeEnableScreenRotation(enableScreenRotation); 101 102 // Disable common aspects of the system/status/navigation bars that are not appropriate or 103 // useful on any keyguard screen but can be re-shown by dialogs or SHOW_WHEN_LOCKED 104 // activities. Other disabled bits are handled by the KeyguardViewMediator talking 105 // directly to the status bar service. 106 final int visFlags = View.STATUS_BAR_DISABLE_HOME; 107 if (DEBUG) Log.v(TAG, "show:setSystemUiVisibility(" + Integer.toHexString(visFlags)+")"); 108 mKeyguardHost.setSystemUiVisibility(visFlags); 109 110 mViewManager.updateViewLayout(mKeyguardHost, mWindowLayoutParams); 111 mKeyguardHost.setVisibility(View.VISIBLE); 112 mKeyguardView.show(); 113 mKeyguardView.requestFocus(); 114 } 115 116 private boolean shouldEnableScreenRotation() { 117 Resources res = mContext.getResources(); 118 return SystemProperties.getBoolean("lockscreen.rot_override",false) 119 || res.getBoolean(com.android.internal.R.bool.config_enableLockScreenRotation); 120 } 121 122 class ViewManagerHost extends FrameLayout { 123 public ViewManagerHost(Context context) { 124 super(context); 125 } 126 127 @Override 128 protected void onConfigurationChanged(Configuration newConfig) { 129 super.onConfigurationChanged(newConfig); 130 if (mKeyguardHost.getVisibility() == View.VISIBLE) { 131 // only propagate configuration messages if we're currently showing 132 maybeCreateKeyguardLocked(shouldEnableScreenRotation(), true, null); 133 } else { 134 if (DEBUG) Log.v(TAG, "onConfigurationChanged: view not visible"); 135 } 136 } 137 138 @Override 139 public boolean dispatchKeyEvent(KeyEvent event) { 140 if (event.getAction() == KeyEvent.ACTION_DOWN && mKeyguardView != null) { 141 int keyCode = event.getKeyCode(); 142 if (keyCode == KeyEvent.KEYCODE_BACK && mKeyguardView.handleBackKey()) { 143 return true; 144 } else if (keyCode == KeyEvent.KEYCODE_MENU && mKeyguardView.handleMenuKey()) { 145 return true; 146 } 147 } 148 return super.dispatchKeyEvent(event); 149 } 150 } 151 152 SparseArray<Parcelable> mStateContainer = new SparseArray<Parcelable>(); 153 154 private void maybeCreateKeyguardLocked(boolean enableScreenRotation, boolean force, 155 Bundle options) { 156 final boolean isActivity = (mContext instanceof Activity); // for test activity 157 158 if (mKeyguardHost != null) { 159 mKeyguardHost.saveHierarchyState(mStateContainer); 160 } 161 162 if (mKeyguardHost == null) { 163 if (DEBUG) Log.d(TAG, "keyguard host is null, creating it..."); 164 165 mKeyguardHost = new ViewManagerHost(mContext); 166 167 int flags = WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN 168 | WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER; 169 170 if (!mNeedsInput) { 171 flags |= WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM; 172 } 173 if (ActivityManager.isHighEndGfx()) { 174 flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED; 175 } 176 177 final int stretch = ViewGroup.LayoutParams.MATCH_PARENT; 178 final int type = isActivity ? WindowManager.LayoutParams.TYPE_APPLICATION 179 : WindowManager.LayoutParams.TYPE_KEYGUARD; 180 WindowManager.LayoutParams lp = new WindowManager.LayoutParams( 181 stretch, stretch, type, flags, PixelFormat.TRANSLUCENT); 182 lp.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE; 183 lp.windowAnimations = com.android.internal.R.style.Animation_LockScreen; 184 if (ActivityManager.isHighEndGfx()) { 185 lp.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED; 186 lp.privateFlags |= 187 WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_HARDWARE_ACCELERATED; 188 } 189 lp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_SET_NEEDS_MENU_KEY; 190 if (isActivity) { 191 lp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS; 192 } 193 lp.inputFeatures |= WindowManager.LayoutParams.INPUT_FEATURE_DISABLE_USER_ACTIVITY; 194 lp.setTitle(isActivity ? "KeyguardMock" : "Keyguard"); 195 mWindowLayoutParams = lp; 196 mViewManager.addView(mKeyguardHost, lp); 197 } 198 199 if (force || mKeyguardView == null) { 200 inflateKeyguardView(options); 201 } 202 updateUserActivityTimeoutInWindowLayoutParams(); 203 mViewManager.updateViewLayout(mKeyguardHost, mWindowLayoutParams); 204 205 mKeyguardHost.restoreHierarchyState(mStateContainer); 206 } 207 208 private void inflateKeyguardView(Bundle options) { 209 View v = mKeyguardHost.findViewById(R.id.keyguard_host_view); 210 if (v != null) { 211 mKeyguardHost.removeView(v); 212 } 213 // TODO: Remove once b/7094175 is fixed 214 Slog.d(TAG, "inflateKeyguardView: b/7094175 mContext.config=" 215 + mContext.getResources().getConfiguration()); 216 final LayoutInflater inflater = LayoutInflater.from(mContext); 217 View view = inflater.inflate(R.layout.keyguard_host_view, mKeyguardHost, true); 218 mKeyguardView = (KeyguardHostView) view.findViewById(R.id.keyguard_host_view); 219 mKeyguardView.setLockPatternUtils(mLockPatternUtils); 220 mKeyguardView.setViewMediatorCallback(mViewMediatorCallback); 221 222 // HACK 223 // The keyguard view will have set up window flags in onFinishInflate before we set 224 // the view mediator callback. Make sure it knows the correct IME state. 225 if (mViewMediatorCallback != null) { 226 KeyguardPasswordView kpv = (KeyguardPasswordView) mKeyguardView.findViewById( 227 R.id.keyguard_password_view); 228 229 if (kpv != null) { 230 mViewMediatorCallback.setNeedsInput(kpv.needsInput()); 231 } 232 } 233 234 if (options != null) { 235 if (options.getBoolean(LockPatternUtils.KEYGUARD_SHOW_USER_SWITCHER)) { 236 mKeyguardView.goToUserSwitcher(); 237 } 238 if (options.getBoolean(LockPatternUtils.KEYGUARD_SHOW_SECURITY_CHALLENGE)) { 239 mKeyguardView.showNextSecurityScreenIfPresent(); 240 } 241 } 242 } 243 244 public void updateUserActivityTimeout() { 245 updateUserActivityTimeoutInWindowLayoutParams(); 246 mViewManager.updateViewLayout(mKeyguardHost, mWindowLayoutParams); 247 } 248 249 private void updateUserActivityTimeoutInWindowLayoutParams() { 250 // Use the user activity timeout requested by the keyguard view, if any. 251 if (mKeyguardView != null) { 252 long timeout = mKeyguardView.getUserActivityTimeout(); 253 if (timeout >= 0) { 254 mWindowLayoutParams.userActivityTimeout = timeout; 255 return; 256 } 257 } 258 259 // Otherwise, use the default timeout. 260 mWindowLayoutParams.userActivityTimeout = KeyguardViewMediator.AWAKE_INTERVAL_DEFAULT_MS; 261 } 262 263 private void maybeEnableScreenRotation(boolean enableScreenRotation) { 264 // TODO: move this outside 265 if (enableScreenRotation) { 266 if (DEBUG) Log.d(TAG, "Rotation sensor for lock screen On!"); 267 mWindowLayoutParams.screenOrientation = ActivityInfo.SCREEN_ORIENTATION_USER; 268 } else { 269 if (DEBUG) Log.d(TAG, "Rotation sensor for lock screen Off!"); 270 mWindowLayoutParams.screenOrientation = ActivityInfo.SCREEN_ORIENTATION_NOSENSOR; 271 } 272 mViewManager.updateViewLayout(mKeyguardHost, mWindowLayoutParams); 273 } 274 275 public void setNeedsInput(boolean needsInput) { 276 mNeedsInput = needsInput; 277 if (mWindowLayoutParams != null) { 278 if (needsInput) { 279 mWindowLayoutParams.flags &= 280 ~WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM; 281 } else { 282 mWindowLayoutParams.flags |= 283 WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM; 284 } 285 286 try { 287 mViewManager.updateViewLayout(mKeyguardHost, mWindowLayoutParams); 288 } catch (java.lang.IllegalArgumentException e) { 289 // TODO: Ensure this method isn't called on views that are changing... 290 Log.w(TAG,"Can't update input method on " + mKeyguardHost + " window not attached"); 291 } 292 } 293 } 294 295 /** 296 * Reset the state of the view. 297 */ 298 public synchronized void reset(Bundle options) { 299 if (DEBUG) Log.d(TAG, "reset()"); 300 // User might have switched, check if we need to go back to keyguard 301 // TODO: It's preferable to stay and show the correct lockscreen or unlock if none 302 maybeCreateKeyguardLocked(shouldEnableScreenRotation(), true, options); 303 } 304 305 public synchronized void onScreenTurnedOff() { 306 if (DEBUG) Log.d(TAG, "onScreenTurnedOff()"); 307 mScreenOn = false; 308 if (mKeyguardView != null) { 309 mKeyguardView.onScreenTurnedOff(); 310 } 311 } 312 313 public synchronized void onScreenTurnedOn( 314 final KeyguardViewManager.ShowListener showListener) { 315 if (DEBUG) Log.d(TAG, "onScreenTurnedOn()"); 316 mScreenOn = true; 317 if (mKeyguardView != null) { 318 mKeyguardView.onScreenTurnedOn(); 319 320 // Caller should wait for this window to be shown before turning 321 // on the screen. 322 if (showListener != null) { 323 if (mKeyguardHost.getVisibility() == View.VISIBLE) { 324 // Keyguard may be in the process of being shown, but not yet 325 // updated with the window manager... give it a chance to do so. 326 mKeyguardHost.post(new Runnable() { 327 @Override 328 public void run() { 329 if (mKeyguardHost.getVisibility() == View.VISIBLE) { 330 showListener.onShown(mKeyguardHost.getWindowToken()); 331 } else { 332 showListener.onShown(null); 333 } 334 } 335 }); 336 } else { 337 showListener.onShown(null); 338 } 339 } 340 } else if (showListener != null) { 341 showListener.onShown(null); 342 } 343 } 344 345 public synchronized void verifyUnlock() { 346 if (DEBUG) Log.d(TAG, "verifyUnlock()"); 347 show(null); 348 mKeyguardView.verifyUnlock(); 349 } 350 351 /** 352 * A key has woken the device. We use this to potentially adjust the state 353 * of the lock screen based on the key. 354 * 355 * The 'Tq' suffix is per the documentation in {@link android.view.WindowManagerPolicy}. 356 * Be sure not to take any action that takes a long time; any significant 357 * action should be posted to a handler. 358 * 359 * @param keyCode The wake key. May be {@link KeyEvent#KEYCODE_UNKNOWN} if waking 360 * for a reason other than a key press. 361 */ 362 public boolean wakeWhenReadyTq(int keyCode) { 363 if (DEBUG) Log.d(TAG, "wakeWhenReady(" + keyCode + ")"); 364 if (mKeyguardView != null) { 365 mKeyguardView.wakeWhenReadyTq(keyCode); 366 return true; 367 } 368 Log.w(TAG, "mKeyguardView is null in wakeWhenReadyTq"); 369 return false; 370 } 371 372 /** 373 * Hides the keyguard view 374 */ 375 public synchronized void hide() { 376 if (DEBUG) Log.d(TAG, "hide()"); 377 378 if (mKeyguardHost != null) { 379 mKeyguardHost.setVisibility(View.GONE); 380 381 // We really only want to preserve keyguard state for configuration changes. Hence 382 // we should clear state of widgets (e.g. Music) when we hide keyguard so it can 383 // start with a fresh state when we return. 384 mStateContainer.clear(); 385 386 // Don't do this right away, so we can let the view continue to animate 387 // as it goes away. 388 if (mKeyguardView != null) { 389 final KeyguardViewBase lastView = mKeyguardView; 390 mKeyguardView = null; 391 mKeyguardHost.postDelayed(new Runnable() { 392 @Override 393 public void run() { 394 synchronized (KeyguardViewManager.this) { 395 lastView.cleanUp(); 396 mKeyguardHost.removeView(lastView); 397 } 398 } 399 }, 500); 400 } 401 } 402 } 403 404 /** 405 * Dismisses the keyguard by going to the next screen or making it gone. 406 */ 407 public synchronized void dismiss() { 408 if (mScreenOn) { 409 mKeyguardView.dismiss(); 410 } 411 } 412 413 /** 414 * @return Whether the keyguard is showing 415 */ 416 public synchronized boolean isShowing() { 417 return (mKeyguardHost != null && mKeyguardHost.getVisibility() == View.VISIBLE); 418 } 419} 420