KeyguardViewManager.java revision 43a372f38ad642f86047e8112e3d43edb7300439
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.keyguard; 18 19import android.app.PendingIntent; 20import android.graphics.Bitmap; 21import android.graphics.drawable.BitmapDrawable; 22import com.android.internal.policy.IKeyguardShowCallback; 23import com.android.internal.widget.LockPatternUtils; 24 25import android.app.Activity; 26import android.app.ActivityManager; 27import android.appwidget.AppWidgetManager; 28import android.content.Context; 29import android.content.pm.ActivityInfo; 30import android.content.res.Configuration; 31import android.content.res.Resources; 32import android.graphics.Canvas; 33import android.graphics.ColorFilter; 34import android.graphics.PixelFormat; 35import android.graphics.PorterDuff; 36import android.graphics.Rect; 37import android.graphics.drawable.Drawable; 38import android.os.Bundle; 39import android.os.IBinder; 40import android.os.Parcelable; 41import android.os.RemoteException; 42import android.os.SystemProperties; 43import android.util.Log; 44import android.util.Slog; 45import android.util.SparseArray; 46import android.view.KeyEvent; 47import android.view.LayoutInflater; 48import android.view.MotionEvent; 49import android.view.View; 50import android.view.ViewGroup; 51import android.view.ViewManager; 52import android.view.WindowManager; 53import android.widget.FrameLayout; 54 55/** 56 * Manages creating, showing, hiding and resetting the keyguard. Calls back 57 * via {@link KeyguardViewMediator.ViewMediatorCallback} to poke 58 * the wake lock and report that the keyguard is done, which is in turn, 59 * reported to this class by the current {@link KeyguardViewBase}. 60 */ 61public class KeyguardViewManager { 62 private final static boolean DEBUG = KeyguardViewMediator.DEBUG; 63 private static String TAG = "KeyguardViewManager"; 64 public static boolean USE_UPPER_CASE = true; 65 public final static String IS_SWITCHING_USER = "is_switching_user"; 66 67 // Timeout used for keypresses 68 static final int DIGIT_PRESS_WAKE_MILLIS = 5000; 69 70 private final Context mContext; 71 private final ViewManager mViewManager; 72 private final KeyguardViewMediator.ViewMediatorCallback mViewMediatorCallback; 73 74 private WindowManager.LayoutParams mWindowLayoutParams; 75 private boolean mNeedsInput = false; 76 77 private ViewManagerHost mKeyguardHost; 78 private KeyguardHostView mKeyguardView; 79 80 private boolean mScreenOn = false; 81 private LockPatternUtils mLockPatternUtils; 82 83 private KeyguardUpdateMonitorCallback mBackgroundChanger = new KeyguardUpdateMonitorCallback() { 84 @Override 85 public void onSetBackground(Bitmap bmp) { 86 mKeyguardHost.setCustomBackground(bmp != null ? 87 new BitmapDrawable(mContext.getResources(), bmp) : null); 88 } 89 }; 90 91 public interface ShowListener { 92 void onShown(IBinder windowToken); 93 }; 94 95 /** 96 * @param context Used to create views. 97 * @param viewManager Keyguard will be attached to this. 98 * @param callback Used to notify of changes. 99 * @param lockPatternUtils 100 */ 101 public KeyguardViewManager(Context context, ViewManager viewManager, 102 KeyguardViewMediator.ViewMediatorCallback callback, 103 LockPatternUtils lockPatternUtils) { 104 mContext = context; 105 mViewManager = viewManager; 106 mViewMediatorCallback = callback; 107 mLockPatternUtils = lockPatternUtils; 108 } 109 110 /** 111 * Show the keyguard. Will handle creating and attaching to the view manager 112 * lazily. 113 */ 114 public synchronized void show(Bundle options) { 115 if (DEBUG) Log.d(TAG, "show(); mKeyguardView==" + mKeyguardView); 116 117 boolean enableScreenRotation = shouldEnableScreenRotation(); 118 119 maybeCreateKeyguardLocked(enableScreenRotation, false, options); 120 maybeEnableScreenRotation(enableScreenRotation); 121 122 // Disable common aspects of the system/status/navigation bars that are not appropriate or 123 // useful on any keyguard screen but can be re-shown by dialogs or SHOW_WHEN_LOCKED 124 // activities. Other disabled bits are handled by the KeyguardViewMediator talking 125 // directly to the status bar service. 126 int visFlags = View.STATUS_BAR_DISABLE_HOME; 127 if (shouldEnableTransparentBars()) { 128 visFlags |= View.SYSTEM_UI_FLAG_TRANSPARENT_STATUS 129 | View.SYSTEM_UI_FLAG_TRANSPARENT_NAVIGATION; 130 } 131 if (DEBUG) Log.v(TAG, "show:setSystemUiVisibility(" + Integer.toHexString(visFlags)+")"); 132 mKeyguardHost.setSystemUiVisibility(visFlags); 133 134 mViewManager.updateViewLayout(mKeyguardHost, mWindowLayoutParams); 135 mKeyguardHost.setVisibility(View.VISIBLE); 136 mKeyguardView.show(); 137 mKeyguardView.requestFocus(); 138 } 139 140 private boolean shouldEnableScreenRotation() { 141 Resources res = mContext.getResources(); 142 return SystemProperties.getBoolean("lockscreen.rot_override",false) 143 || res.getBoolean(R.bool.config_enableLockScreenRotation); 144 } 145 146 private boolean shouldEnableTransparentBars() { 147 Resources res = mContext.getResources(); 148 return res.getBoolean(R.bool.config_enableLockScreenTransparentBars); 149 } 150 151 class ViewManagerHost extends FrameLayout { 152 private static final int BACKGROUND_COLOR = 0x70000000; 153 154 private Drawable mCustomBackground; 155 156 // This is a faster way to draw the background on devices without hardware acceleration 157 private final Drawable mBackgroundDrawable = new Drawable() { 158 @Override 159 public void draw(Canvas canvas) { 160 if (mCustomBackground != null) { 161 final Rect bounds = mCustomBackground.getBounds(); 162 final int vWidth = getWidth(); 163 final int vHeight = getHeight(); 164 165 final int restore = canvas.save(); 166 canvas.translate(-(bounds.width() - vWidth) / 2, 167 -(bounds.height() - vHeight) / 2); 168 mCustomBackground.draw(canvas); 169 canvas.restoreToCount(restore); 170 } else { 171 canvas.drawColor(BACKGROUND_COLOR, PorterDuff.Mode.SRC); 172 } 173 } 174 175 @Override 176 public void setAlpha(int alpha) { 177 } 178 179 @Override 180 public void setColorFilter(ColorFilter cf) { 181 } 182 183 @Override 184 public int getOpacity() { 185 return PixelFormat.TRANSLUCENT; 186 } 187 }; 188 189 public ViewManagerHost(Context context) { 190 super(context); 191 setBackground(mBackgroundDrawable); 192 } 193 194 public void setCustomBackground(Drawable d) { 195 mCustomBackground = d; 196 if (d != null) { 197 d.setColorFilter(BACKGROUND_COLOR, PorterDuff.Mode.SRC_OVER); 198 } 199 computeCustomBackgroundBounds(); 200 invalidate(); 201 } 202 203 private void computeCustomBackgroundBounds() { 204 if (mCustomBackground == null) return; // Nothing to do 205 if (!isLaidOut()) return; // We'll do this later 206 207 final int bgWidth = mCustomBackground.getIntrinsicWidth(); 208 final int bgHeight = mCustomBackground.getIntrinsicHeight(); 209 final int vWidth = getWidth(); 210 final int vHeight = getHeight(); 211 212 final float bgAspect = (float) bgWidth / bgHeight; 213 final float vAspect = (float) vWidth / vHeight; 214 215 if (bgAspect > vAspect) { 216 mCustomBackground.setBounds(0, 0, (int) (vHeight * bgAspect), vHeight); 217 } else { 218 mCustomBackground.setBounds(0, 0, vWidth, (int) (vWidth / bgAspect)); 219 } 220 } 221 222 @Override 223 protected void onSizeChanged(int w, int h, int oldw, int oldh) { 224 super.onSizeChanged(w, h, oldw, oldh); 225 computeCustomBackgroundBounds(); 226 } 227 228 @Override 229 protected void onConfigurationChanged(Configuration newConfig) { 230 super.onConfigurationChanged(newConfig); 231 if (mKeyguardHost.getVisibility() == View.VISIBLE) { 232 // only propagate configuration messages if we're currently showing 233 maybeCreateKeyguardLocked(shouldEnableScreenRotation(), true, null); 234 } else { 235 if (DEBUG) Log.v(TAG, "onConfigurationChanged: view not visible"); 236 } 237 } 238 239 @Override 240 public boolean dispatchKeyEvent(KeyEvent event) { 241 if (mKeyguardView != null) { 242 // Always process back and menu keys, regardless of focus 243 if (event.getAction() == KeyEvent.ACTION_DOWN) { 244 int keyCode = event.getKeyCode(); 245 if (keyCode == KeyEvent.KEYCODE_BACK && mKeyguardView.handleBackKey()) { 246 return true; 247 } else if (keyCode == KeyEvent.KEYCODE_MENU && mKeyguardView.handleMenuKey()) { 248 return true; 249 } 250 } 251 // Always process media keys, regardless of focus 252 if (mKeyguardView.dispatchKeyEvent(event)) { 253 return true; 254 } 255 } 256 return super.dispatchKeyEvent(event); 257 } 258 } 259 260 SparseArray<Parcelable> mStateContainer = new SparseArray<Parcelable>(); 261 262 private void maybeCreateKeyguardLocked(boolean enableScreenRotation, boolean force, 263 Bundle options) { 264 if (mKeyguardHost != null) { 265 mKeyguardHost.saveHierarchyState(mStateContainer); 266 } 267 268 if (mKeyguardHost == null) { 269 if (DEBUG) Log.d(TAG, "keyguard host is null, creating it..."); 270 271 mKeyguardHost = new ViewManagerHost(mContext); 272 273 int flags = WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN 274 | WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR 275 | WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN 276 | WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER; 277 278 if (!mNeedsInput) { 279 flags |= WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM; 280 } 281 282 final int stretch = ViewGroup.LayoutParams.MATCH_PARENT; 283 final int type = WindowManager.LayoutParams.TYPE_KEYGUARD; 284 WindowManager.LayoutParams lp = new WindowManager.LayoutParams( 285 stretch, stretch, type, flags, PixelFormat.TRANSLUCENT); 286 lp.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE; 287 lp.windowAnimations = R.style.Animation_LockScreen; 288 lp.screenOrientation = enableScreenRotation ? 289 ActivityInfo.SCREEN_ORIENTATION_USER : ActivityInfo.SCREEN_ORIENTATION_NOSENSOR; 290 291 if (ActivityManager.isHighEndGfx()) { 292 lp.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED; 293 lp.privateFlags |= 294 WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_HARDWARE_ACCELERATED; 295 } 296 lp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_SET_NEEDS_MENU_KEY; 297 lp.inputFeatures |= WindowManager.LayoutParams.INPUT_FEATURE_DISABLE_USER_ACTIVITY; 298 lp.setTitle("Keyguard"); 299 mWindowLayoutParams = lp; 300 mViewManager.addView(mKeyguardHost, lp); 301 302 KeyguardUpdateMonitor.getInstance(mContext).registerCallback(mBackgroundChanger); 303 } 304 305 if (force || mKeyguardView == null) { 306 mKeyguardHost.setCustomBackground(null); 307 mKeyguardHost.removeAllViews(); 308 inflateKeyguardView(options); 309 mKeyguardView.requestFocus(); 310 } 311 updateUserActivityTimeoutInWindowLayoutParams(); 312 mViewManager.updateViewLayout(mKeyguardHost, mWindowLayoutParams); 313 314 mKeyguardHost.restoreHierarchyState(mStateContainer); 315 } 316 317 private void inflateKeyguardView(Bundle options) { 318 View v = mKeyguardHost.findViewById(R.id.keyguard_host_view); 319 if (v != null) { 320 mKeyguardHost.removeView(v); 321 } 322 final LayoutInflater inflater = LayoutInflater.from(mContext); 323 View view = inflater.inflate(R.layout.keyguard_host_view, mKeyguardHost, true); 324 mKeyguardView = (KeyguardHostView) view.findViewById(R.id.keyguard_host_view); 325 mKeyguardView.setLockPatternUtils(mLockPatternUtils); 326 mKeyguardView.setViewMediatorCallback(mViewMediatorCallback); 327 mKeyguardView.initializeSwitchingUserState(options != null && 328 options.getBoolean(IS_SWITCHING_USER)); 329 330 // HACK 331 // The keyguard view will have set up window flags in onFinishInflate before we set 332 // the view mediator callback. Make sure it knows the correct IME state. 333 if (mViewMediatorCallback != null) { 334 KeyguardPasswordView kpv = (KeyguardPasswordView) mKeyguardView.findViewById( 335 R.id.keyguard_password_view); 336 337 if (kpv != null) { 338 mViewMediatorCallback.setNeedsInput(kpv.needsInput()); 339 } 340 } 341 342 if (options != null) { 343 int widgetToShow = options.getInt(LockPatternUtils.KEYGUARD_SHOW_APPWIDGET, 344 AppWidgetManager.INVALID_APPWIDGET_ID); 345 if (widgetToShow != AppWidgetManager.INVALID_APPWIDGET_ID) { 346 mKeyguardView.goToWidget(widgetToShow); 347 } 348 } 349 } 350 351 public void updateUserActivityTimeout() { 352 updateUserActivityTimeoutInWindowLayoutParams(); 353 mViewManager.updateViewLayout(mKeyguardHost, mWindowLayoutParams); 354 } 355 356 private void updateUserActivityTimeoutInWindowLayoutParams() { 357 // Use the user activity timeout requested by the keyguard view, if any. 358 if (mKeyguardView != null) { 359 long timeout = mKeyguardView.getUserActivityTimeout(); 360 if (timeout >= 0) { 361 mWindowLayoutParams.userActivityTimeout = timeout; 362 return; 363 } 364 } 365 366 // Otherwise, use the default timeout. 367 mWindowLayoutParams.userActivityTimeout = KeyguardViewMediator.AWAKE_INTERVAL_DEFAULT_MS; 368 } 369 370 private void maybeEnableScreenRotation(boolean enableScreenRotation) { 371 // TODO: move this outside 372 if (enableScreenRotation) { 373 if (DEBUG) Log.d(TAG, "Rotation sensor for lock screen On!"); 374 mWindowLayoutParams.screenOrientation = ActivityInfo.SCREEN_ORIENTATION_USER; 375 } else { 376 if (DEBUG) Log.d(TAG, "Rotation sensor for lock screen Off!"); 377 mWindowLayoutParams.screenOrientation = ActivityInfo.SCREEN_ORIENTATION_NOSENSOR; 378 } 379 mViewManager.updateViewLayout(mKeyguardHost, mWindowLayoutParams); 380 } 381 382 public void setNeedsInput(boolean needsInput) { 383 mNeedsInput = needsInput; 384 if (mWindowLayoutParams != null) { 385 if (needsInput) { 386 mWindowLayoutParams.flags &= 387 ~WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM; 388 } else { 389 mWindowLayoutParams.flags |= 390 WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM; 391 } 392 393 try { 394 mViewManager.updateViewLayout(mKeyguardHost, mWindowLayoutParams); 395 } catch (java.lang.IllegalArgumentException e) { 396 // TODO: Ensure this method isn't called on views that are changing... 397 Log.w(TAG,"Can't update input method on " + mKeyguardHost + " window not attached"); 398 } 399 } 400 } 401 402 /** 403 * Reset the state of the view. 404 */ 405 public synchronized void reset(Bundle options) { 406 if (DEBUG) Log.d(TAG, "reset()"); 407 // User might have switched, check if we need to go back to keyguard 408 // TODO: It's preferable to stay and show the correct lockscreen or unlock if none 409 maybeCreateKeyguardLocked(shouldEnableScreenRotation(), true, options); 410 } 411 412 public synchronized void onScreenTurnedOff() { 413 if (DEBUG) Log.d(TAG, "onScreenTurnedOff()"); 414 mScreenOn = false; 415 if (mKeyguardView != null) { 416 mKeyguardView.onScreenTurnedOff(); 417 } 418 } 419 420 public synchronized void onScreenTurnedOn(final IKeyguardShowCallback callback) { 421 if (DEBUG) Log.d(TAG, "onScreenTurnedOn()"); 422 mScreenOn = true; 423 if (mKeyguardView != null) { 424 mKeyguardView.onScreenTurnedOn(); 425 426 // Caller should wait for this window to be shown before turning 427 // on the screen. 428 if (callback != null) { 429 if (mKeyguardHost.getVisibility() == View.VISIBLE) { 430 // Keyguard may be in the process of being shown, but not yet 431 // updated with the window manager... give it a chance to do so. 432 mKeyguardHost.post(new Runnable() { 433 @Override 434 public void run() { 435 IBinder token = null; 436 if (mKeyguardHost.getVisibility() == View.VISIBLE) { 437 token = mKeyguardHost.getWindowToken(); 438 } 439 try { 440 callback.onShown(token); 441 } catch (RemoteException e) { 442 Slog.w(TAG, "Exception calling onShown():", e); 443 } 444 } 445 }); 446 } else { 447 try { 448 callback.onShown(null); 449 } catch (RemoteException e) { 450 Slog.w(TAG, "Exception calling onShown():", e); 451 } 452 } 453 } 454 } else if (callback != null) { 455 try { 456 callback.onShown(null); 457 } catch (RemoteException e) { 458 Slog.w(TAG, "Exception calling onShown():", e); 459 } 460 } 461 } 462 463 public synchronized void verifyUnlock() { 464 if (DEBUG) Log.d(TAG, "verifyUnlock()"); 465 show(null); 466 mKeyguardView.verifyUnlock(); 467 } 468 469 /** 470 * Hides the keyguard view 471 */ 472 public synchronized void hide() { 473 if (DEBUG) Log.d(TAG, "hide()"); 474 475 if (mKeyguardHost != null) { 476 mKeyguardHost.setVisibility(View.GONE); 477 478 // We really only want to preserve keyguard state for configuration changes. Hence 479 // we should clear state of widgets (e.g. Music) when we hide keyguard so it can 480 // start with a fresh state when we return. 481 mStateContainer.clear(); 482 483 // Don't do this right away, so we can let the view continue to animate 484 // as it goes away. 485 if (mKeyguardView != null) { 486 final KeyguardViewBase lastView = mKeyguardView; 487 mKeyguardView = null; 488 mKeyguardHost.postDelayed(new Runnable() { 489 @Override 490 public void run() { 491 synchronized (KeyguardViewManager.this) { 492 lastView.cleanUp(); 493 // Let go of any large bitmaps. 494 mKeyguardHost.setCustomBackground(null); 495 mKeyguardHost.removeView(lastView); 496 } 497 } 498 }, 500); 499 } 500 } 501 } 502 503 /** 504 * Dismisses the keyguard by going to the next screen or making it gone. 505 */ 506 public synchronized void dismiss() { 507 if (mScreenOn) { 508 mKeyguardView.dismiss(); 509 } 510 } 511 512 /** 513 * @return Whether the keyguard is showing 514 */ 515 public synchronized boolean isShowing() { 516 return (mKeyguardHost != null && mKeyguardHost.getVisibility() == View.VISIBLE); 517 } 518 519 public void showAssistant() { 520 if (mKeyguardView != null) { 521 mKeyguardView.showAssistant(); 522 } 523 } 524 525 public void dispatch(MotionEvent event) { 526 if (mKeyguardView != null) { 527 mKeyguardView.dispatch(event); 528 } 529 } 530 531 public void launchCamera() { 532 if (mKeyguardView != null) { 533 mKeyguardView.launchCamera(); 534 } 535 } 536} 537