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