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