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