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