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