2 * Copyright (C) 2007 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 package com.android.keyguard;
19 import android.app.PendingIntent;
20 import android.graphics.Bitmap;
21 import android.graphics.drawable.BitmapDrawable;
22 import com.android.internal.policy.IKeyguardShowCallback;
23 import com.android.internal.widget.LockPatternUtils;
25 import android.app.Activity;
26 import android.app.ActivityManager;
27 import android.appwidget.AppWidgetManager;
28 import android.content.Context;
29 import android.content.pm.ActivityInfo;
30 import android.content.res.Configuration;
31 import android.content.res.Resources;
32 import android.graphics.Canvas;
33 import android.graphics.ColorFilter;
34 import android.graphics.PixelFormat;
35 import android.graphics.PorterDuff;
36 import android.graphics.Rect;
37 import android.graphics.drawable.Drawable;
38 import android.os.Bundle;
39 import android.os.IBinder;
40 import android.os.Parcelable;
41 import android.os.RemoteException;
42 import android.os.SystemProperties;
43 import android.util.Log;
44 import android.util.Slog;
45 import android.util.SparseArray;
46 import android.view.KeyEvent;
47 import android.view.LayoutInflater;
48 import android.view.MotionEvent;
49 import android.view.View;
50 import android.view.ViewGroup;
51 import android.view.ViewManager;
52 import android.view.WindowManager;
53 import android.widget.FrameLayout;
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}.
61 public 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";
67 // Timeout used for keypresses
68 static final int DIGIT_PRESS_WAKE_MILLIS = 5000;
70 private final Context mContext;
71 private final ViewManager mViewManager;
72 private final KeyguardViewMediator.ViewMediatorCallback mViewMediatorCallback;
74 private WindowManager.LayoutParams mWindowLayoutParams;
75 private boolean mNeedsInput = false;
77 private ViewManagerHost mKeyguardHost;
78 private KeyguardHostView mKeyguardView;
80 private boolean mScreenOn = false;
81 private LockPatternUtils mLockPatternUtils;
83 private KeyguardUpdateMonitorCallback mBackgroundChanger = new KeyguardUpdateMonitorCallback() {
85 public void onSetBackground(Bitmap bmp) {
86 mKeyguardHost.setCustomBackground(bmp != null ?
87 new BitmapDrawable(mContext.getResources(), bmp) : null);
91 public interface ShowListener {
92 void onShown(IBinder windowToken);
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
101 public KeyguardViewManager(Context context, ViewManager viewManager,
102 KeyguardViewMediator.ViewMediatorCallback callback,
103 LockPatternUtils lockPatternUtils) {
105 mViewManager = viewManager;
106 mViewMediatorCallback = callback;
107 mLockPatternUtils = lockPatternUtils;
111 * Show the keyguard. Will handle creating and attaching to the view manager
114 public synchronized void show(Bundle options) {
115 if (DEBUG) Log.d(TAG, "show(); mKeyguardView==" + mKeyguardView);
117 boolean enableScreenRotation = shouldEnableScreenRotation();
119 maybeCreateKeyguardLocked(enableScreenRotation, false, options);
120 maybeEnableScreenRotation(enableScreenRotation);
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 (shouldEnableTranslucentDecor()) {
128 mWindowLayoutParams.flags |= WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS
129 | WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION;
131 if (DEBUG) Log.v(TAG, "show:setSystemUiVisibility(" + Integer.toHexString(visFlags)+")");
132 mKeyguardHost.setSystemUiVisibility(visFlags);
134 mViewManager.updateViewLayout(mKeyguardHost, mWindowLayoutParams);
135 mKeyguardHost.setVisibility(View.VISIBLE);
136 mKeyguardView.show();
137 mKeyguardView.requestFocus();
140 private boolean shouldEnableScreenRotation() {
141 Resources res = mContext.getResources();
142 return SystemProperties.getBoolean("lockscreen.rot_override",false)
143 || res.getBoolean(R.bool.config_enableLockScreenRotation);
146 private boolean shouldEnableTranslucentDecor() {
147 Resources res = mContext.getResources();
148 return res.getBoolean(R.bool.config_enableLockScreenTranslucentDecor);
151 class ViewManagerHost extends FrameLayout {
152 private static final int BACKGROUND_COLOR = 0x70000000;
154 private Drawable mCustomBackground;
156 // This is a faster way to draw the background on devices without hardware acceleration
157 private final Drawable mBackgroundDrawable = new Drawable() {
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();
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);
171 canvas.drawColor(BACKGROUND_COLOR, PorterDuff.Mode.SRC);
176 public void setAlpha(int alpha) {
180 public void setColorFilter(ColorFilter cf) {
184 public int getOpacity() {
185 return PixelFormat.TRANSLUCENT;
189 public ViewManagerHost(Context context) {
191 setBackground(mBackgroundDrawable);
194 public void setCustomBackground(Drawable d) {
195 mCustomBackground = d;
197 d.setColorFilter(BACKGROUND_COLOR, PorterDuff.Mode.SRC_OVER);
199 computeCustomBackgroundBounds();
203 private void computeCustomBackgroundBounds() {
204 if (mCustomBackground == null) return; // Nothing to do
205 if (!isLaidOut()) return; // We'll do this later
207 final int bgWidth = mCustomBackground.getIntrinsicWidth();
208 final int bgHeight = mCustomBackground.getIntrinsicHeight();
209 final int vWidth = getWidth();
210 final int vHeight = getHeight();
212 final float bgAspect = (float) bgWidth / bgHeight;
213 final float vAspect = (float) vWidth / vHeight;
215 if (bgAspect > vAspect) {
216 mCustomBackground.setBounds(0, 0, (int) (vHeight * bgAspect), vHeight);
218 mCustomBackground.setBounds(0, 0, vWidth, (int) (vWidth / bgAspect));
223 protected void onSizeChanged(int w, int h, int oldw, int oldh) {
224 super.onSizeChanged(w, h, oldw, oldh);
225 computeCustomBackgroundBounds();
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);
235 if (DEBUG) Log.v(TAG, "onConfigurationChanged: view not visible");
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()) {
247 } else if (keyCode == KeyEvent.KEYCODE_MENU && mKeyguardView.handleMenuKey()) {
251 // Always process media keys, regardless of focus
252 if (mKeyguardView.dispatchKeyEvent(event)) {
256 return super.dispatchKeyEvent(event);
260 SparseArray<Parcelable> mStateContainer = new SparseArray<Parcelable>();
262 private void maybeCreateKeyguardLocked(boolean enableScreenRotation, boolean force,
264 if (mKeyguardHost != null) {
265 mKeyguardHost.saveHierarchyState(mStateContainer);
268 if (mKeyguardHost == null) {
269 if (DEBUG) Log.d(TAG, "keyguard host is null, creating it...");
271 mKeyguardHost = new ViewManagerHost(mContext);
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;
279 flags |= WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
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;
291 if (ActivityManager.isHighEndGfx()) {
292 lp.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
294 WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_HARDWARE_ACCELERATED;
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);
302 KeyguardUpdateMonitor.getInstance(mContext).registerCallback(mBackgroundChanger);
305 if (force || mKeyguardView == null) {
306 mKeyguardHost.setCustomBackground(null);
307 mKeyguardHost.removeAllViews();
308 inflateKeyguardView(options);
309 mKeyguardView.requestFocus();
311 updateUserActivityTimeoutInWindowLayoutParams();
312 mViewManager.updateViewLayout(mKeyguardHost, mWindowLayoutParams);
314 mKeyguardHost.restoreHierarchyState(mStateContainer);
317 private void inflateKeyguardView(Bundle options) {
318 View v = mKeyguardHost.findViewById(R.id.keyguard_host_view);
320 mKeyguardHost.removeView(v);
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));
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);
338 mViewMediatorCallback.setNeedsInput(kpv.needsInput());
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);
351 public void updateUserActivityTimeout() {
352 updateUserActivityTimeoutInWindowLayoutParams();
353 mViewManager.updateViewLayout(mKeyguardHost, mWindowLayoutParams);
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();
361 mWindowLayoutParams.userActivityTimeout = timeout;
366 // Otherwise, use the default timeout.
367 mWindowLayoutParams.userActivityTimeout = KeyguardViewMediator.AWAKE_INTERVAL_DEFAULT_MS;
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;
376 if (DEBUG) Log.d(TAG, "Rotation sensor for lock screen Off!");
377 mWindowLayoutParams.screenOrientation = ActivityInfo.SCREEN_ORIENTATION_NOSENSOR;
379 mViewManager.updateViewLayout(mKeyguardHost, mWindowLayoutParams);
382 public void setNeedsInput(boolean needsInput) {
383 mNeedsInput = needsInput;
384 if (mWindowLayoutParams != null) {
386 mWindowLayoutParams.flags &=
387 ~WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
389 mWindowLayoutParams.flags |=
390 WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
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");
403 * Reset the state of the view.
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);
412 public synchronized void onScreenTurnedOff() {
413 if (DEBUG) Log.d(TAG, "onScreenTurnedOff()");
415 if (mKeyguardView != null) {
416 mKeyguardView.onScreenTurnedOff();
420 public synchronized void onScreenTurnedOn(final IKeyguardShowCallback callback) {
421 if (DEBUG) Log.d(TAG, "onScreenTurnedOn()");
423 final IBinder token = mKeyguardHost == null ? null : mKeyguardHost.getWindowToken();
424 if (mKeyguardView != null) {
425 mKeyguardView.onScreenTurnedOn();
427 // Caller should wait for this window to be shown before turning
429 if (callback != null) {
430 if (mKeyguardHost.getVisibility() == View.VISIBLE) {
431 // Keyguard may be in the process of being shown, but not yet
432 // updated with the window manager... give it a chance to do so.
433 mKeyguardHost.post(new Runnable() {
437 callback.onShown(token);
438 } catch (RemoteException e) {
439 Slog.w(TAG, "Exception calling onShown():", e);
445 callback.onShown(token);
446 } catch (RemoteException e) {
447 Slog.w(TAG, "Exception calling onShown():", e);
451 } else if (callback != null) {
453 callback.onShown(token);
454 } catch (RemoteException e) {
455 Slog.w(TAG, "Exception calling onShown():", e);
460 public synchronized void verifyUnlock() {
461 if (DEBUG) Log.d(TAG, "verifyUnlock()");
463 mKeyguardView.verifyUnlock();
467 * Hides the keyguard view
469 public synchronized void hide() {
470 if (DEBUG) Log.d(TAG, "hide()");
472 if (mKeyguardHost != null) {
473 mKeyguardHost.setVisibility(View.GONE);
475 // We really only want to preserve keyguard state for configuration changes. Hence
476 // we should clear state of widgets (e.g. Music) when we hide keyguard so it can
477 // start with a fresh state when we return.
478 mStateContainer.clear();
480 // Don't do this right away, so we can let the view continue to animate
482 if (mKeyguardView != null) {
483 final KeyguardViewBase lastView = mKeyguardView;
484 mKeyguardView = null;
485 mKeyguardHost.postDelayed(new Runnable() {
488 synchronized (KeyguardViewManager.this) {
490 // Let go of any large bitmaps.
491 mKeyguardHost.setCustomBackground(null);
492 mKeyguardHost.removeView(lastView);
501 * Dismisses the keyguard by going to the next screen or making it gone.
503 public synchronized void dismiss() {
505 mKeyguardView.dismiss();
510 * @return Whether the keyguard is showing
512 public synchronized boolean isShowing() {
513 return (mKeyguardHost != null && mKeyguardHost.getVisibility() == View.VISIBLE);
516 public void showAssistant() {
517 if (mKeyguardView != null) {
518 mKeyguardView.showAssistant();
522 public void dispatch(MotionEvent event) {
523 if (mKeyguardView != null) {
524 mKeyguardView.dispatch(event);
528 public void launchCamera() {
529 if (mKeyguardView != null) {
530 mKeyguardView.launchCamera();