OSDN Git Service

Merge "Fix SystemUI crash on devices with WiFi only" into klp-dev
[android-x86/frameworks-base.git] / packages / Keyguard / src / com / android / keyguard / KeyguardViewManager.java
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
17 package com.android.keyguard;
18
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;
24
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;
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  */
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";
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 (shouldEnableTranslucentDecor()) {
128             mWindowLayoutParams.flags |= WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS
129                                        | WindowManager.LayoutParams.FLAG_TRANSLUCENT_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 shouldEnableTranslucentDecor() {
147         Resources res = mContext.getResources();
148         return res.getBoolean(R.bool.config_enableLockScreenTranslucentDecor);
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         final IBinder token = mKeyguardHost == null ? null : mKeyguardHost.getWindowToken();
424         if (mKeyguardView != null) {
425             mKeyguardView.onScreenTurnedOn();
426
427             // Caller should wait for this window to be shown before turning
428             // on the screen.
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() {
434                         @Override
435                         public void run() {
436                             try {
437                                 callback.onShown(token);
438                             } catch (RemoteException e) {
439                                 Slog.w(TAG, "Exception calling onShown():", e);
440                             }
441                         }
442                     });
443                 } else {
444                     try {
445                         callback.onShown(token);
446                     } catch (RemoteException e) {
447                         Slog.w(TAG, "Exception calling onShown():", e);
448                     }
449                 }
450             }
451         } else if (callback != null) {
452             try {
453                 callback.onShown(token);
454             } catch (RemoteException e) {
455                 Slog.w(TAG, "Exception calling onShown():", e);
456             }
457         }
458     }
459
460     public synchronized void verifyUnlock() {
461         if (DEBUG) Log.d(TAG, "verifyUnlock()");
462         show(null);
463         mKeyguardView.verifyUnlock();
464     }
465
466     /**
467      * Hides the keyguard view
468      */
469     public synchronized void hide() {
470         if (DEBUG) Log.d(TAG, "hide()");
471
472         if (mKeyguardHost != null) {
473             mKeyguardHost.setVisibility(View.GONE);
474
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();
479
480             // Don't do this right away, so we can let the view continue to animate
481             // as it goes away.
482             if (mKeyguardView != null) {
483                 final KeyguardViewBase lastView = mKeyguardView;
484                 mKeyguardView = null;
485                 mKeyguardHost.postDelayed(new Runnable() {
486                     @Override
487                     public void run() {
488                         synchronized (KeyguardViewManager.this) {
489                             lastView.cleanUp();
490                             // Let go of any large bitmaps.
491                             mKeyguardHost.setCustomBackground(null);
492                             mKeyguardHost.removeView(lastView);
493                         }
494                     }
495                 }, 500);
496             }
497         }
498     }
499
500     /**
501      * Dismisses the keyguard by going to the next screen or making it gone.
502      */
503     public synchronized void dismiss() {
504         if (mScreenOn) {
505             mKeyguardView.dismiss();
506         }
507     }
508
509     /**
510      * @return Whether the keyguard is showing
511      */
512     public synchronized boolean isShowing() {
513         return (mKeyguardHost != null && mKeyguardHost.getVisibility() == View.VISIBLE);
514     }
515
516     public void showAssistant() {
517         if (mKeyguardView != null) {
518             mKeyguardView.showAssistant();
519         }
520     }
521
522     public void dispatch(MotionEvent event) {
523         if (mKeyguardView != null) {
524             mKeyguardView.dispatch(event);
525         }
526     }
527
528     public void launchCamera() {
529         if (mKeyguardView != null) {
530             mKeyguardView.launchCamera();
531         }
532     }
533 }