KeyguardViewManager.java revision f1f3b49b949af72692f7f85a1c1ef220e8630e30
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        final LayoutInflater inflater = LayoutInflater.from(mContext);
205        View view = inflater.inflate(R.layout.keyguard_host_view, mKeyguardHost, true);
206        mKeyguardView = (KeyguardHostView) view.findViewById(R.id.keyguard_host_view);
207        mKeyguardView.setLockPatternUtils(mLockPatternUtils);
208        mKeyguardView.setViewMediatorCallback(mViewMediatorCallback);
209
210        if (options != null && options.getBoolean(LockPatternUtils.KEYGUARD_SHOW_USER_SWITCHER)) {
211            mKeyguardView.goToUserSwitcher();
212            mKeyguardView.showNextSecurityScreenIfPresent();
213        }
214
215        if (options != null &&
216                options.getBoolean(LockPatternUtils.KEYGUARD_SHOW_SECURITY_CHALLENGE)) {
217            mKeyguardView.showNextSecurityScreenIfPresent();
218        }
219
220        if (mScreenOn) {
221            mKeyguardView.show();
222        }
223    }
224
225    public void updateUserActivityTimeout() {
226        updateUserActivityTimeoutInWindowLayoutParams();
227        mViewManager.updateViewLayout(mKeyguardHost, mWindowLayoutParams);
228    }
229
230    private void updateUserActivityTimeoutInWindowLayoutParams() {
231        // Use the user activity timeout requested by the keyguard view, if any.
232        if (mKeyguardView != null) {
233            long timeout = mKeyguardView.getUserActivityTimeout();
234            if (timeout >= 0) {
235                mWindowLayoutParams.userActivityTimeout = timeout;
236                return;
237            }
238        }
239
240        // Otherwise, use the default timeout.
241        mWindowLayoutParams.userActivityTimeout = KeyguardViewMediator.AWAKE_INTERVAL_DEFAULT_MS;
242    }
243
244    private void maybeEnableScreenRotation(boolean enableScreenRotation) {
245        // TODO: move this outside
246        if (enableScreenRotation) {
247            if (DEBUG) Log.d(TAG, "Rotation sensor for lock screen On!");
248            mWindowLayoutParams.screenOrientation = ActivityInfo.SCREEN_ORIENTATION_USER;
249        } else {
250            if (DEBUG) Log.d(TAG, "Rotation sensor for lock screen Off!");
251            mWindowLayoutParams.screenOrientation = ActivityInfo.SCREEN_ORIENTATION_NOSENSOR;
252        }
253        mViewManager.updateViewLayout(mKeyguardHost, mWindowLayoutParams);
254    }
255
256    public void setNeedsInput(boolean needsInput) {
257        mNeedsInput = needsInput;
258        if (mWindowLayoutParams != null) {
259            if (needsInput) {
260                mWindowLayoutParams.flags &=
261                    ~WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
262            } else {
263                mWindowLayoutParams.flags |=
264                    WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
265            }
266
267            try {
268                mViewManager.updateViewLayout(mKeyguardHost, mWindowLayoutParams);
269            } catch (java.lang.IllegalArgumentException e) {
270                // TODO: Ensure this method isn't called on views that are changing...
271                Log.w(TAG,"Can't update input method on " + mKeyguardHost + " window not attached");
272            }
273        }
274    }
275
276    /**
277     * Reset the state of the view.
278     */
279    public synchronized void reset(Bundle options) {
280        if (DEBUG) Log.d(TAG, "reset()");
281        // User might have switched, check if we need to go back to keyguard
282        // TODO: It's preferable to stay and show the correct lockscreen or unlock if none
283        maybeCreateKeyguardLocked(shouldEnableScreenRotation(), options);
284    }
285
286    public synchronized void onScreenTurnedOff() {
287        if (DEBUG) Log.d(TAG, "onScreenTurnedOff()");
288        mScreenOn = false;
289        if (mKeyguardView != null) {
290            mKeyguardView.onScreenTurnedOff();
291        }
292    }
293
294    public synchronized void onScreenTurnedOn(
295            final KeyguardViewManager.ShowListener showListener) {
296        if (DEBUG) Log.d(TAG, "onScreenTurnedOn()");
297        mScreenOn = true;
298        if (mKeyguardView != null) {
299            mKeyguardView.onScreenTurnedOn();
300
301            // Caller should wait for this window to be shown before turning
302            // on the screen.
303            if (mKeyguardHost.getVisibility() == View.VISIBLE) {
304                // Keyguard may be in the process of being shown, but not yet
305                // updated with the window manager...  give it a chance to do so.
306                mKeyguardHost.post(new Runnable() {
307                    public void run() {
308                        if (mKeyguardHost.getVisibility() == View.VISIBLE) {
309                            showListener.onShown(mKeyguardHost.getWindowToken());
310                        } else {
311                            showListener.onShown(null);
312                        }
313                    }
314                });
315            } else {
316                showListener.onShown(null);
317            }
318        } else {
319            showListener.onShown(null);
320        }
321    }
322
323    public synchronized void verifyUnlock() {
324        if (DEBUG) Log.d(TAG, "verifyUnlock()");
325        show(null);
326        mKeyguardView.verifyUnlock();
327    }
328
329    /**
330     * A key has woken the device.  We use this to potentially adjust the state
331     * of the lock screen based on the key.
332     *
333     * The 'Tq' suffix is per the documentation in {@link android.view.WindowManagerPolicy}.
334     * Be sure not to take any action that takes a long time; any significant
335     * action should be posted to a handler.
336     *
337     * @param keyCode The wake key.  May be {@link KeyEvent#KEYCODE_UNKNOWN} if waking
338     * for a reason other than a key press.
339     */
340    public boolean wakeWhenReadyTq(int keyCode) {
341        if (DEBUG) Log.d(TAG, "wakeWhenReady(" + keyCode + ")");
342        if (mKeyguardView != null) {
343            mKeyguardView.wakeWhenReadyTq(keyCode);
344            return true;
345        } else {
346            Log.w(TAG, "mKeyguardView is null in wakeWhenReadyTq");
347            return false;
348        }
349    }
350
351    /**
352     * Hides the keyguard view
353     */
354    public synchronized void hide() {
355        if (DEBUG) Log.d(TAG, "hide()");
356
357        if (mKeyguardHost != null) {
358            mKeyguardHost.setVisibility(View.GONE);
359            // Don't do this right away, so we can let the view continue to animate
360            // as it goes away.
361            if (mKeyguardView != null) {
362                final KeyguardViewBase lastView = mKeyguardView;
363                mKeyguardView = null;
364                mKeyguardHost.postDelayed(new Runnable() {
365                    public void run() {
366                        synchronized (KeyguardViewManager.this) {
367                            lastView.cleanUp();
368                            mKeyguardHost.removeView(lastView);
369                        }
370                    }
371                }, 500);
372            }
373        }
374    }
375
376    /**
377     * @return Whether the keyguard is showing
378     */
379    public synchronized boolean isShowing() {
380        return (mKeyguardHost != null && mKeyguardHost.getVisibility() == View.VISIBLE);
381    }
382}
383