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