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;
18
19import com.android.internal.R;
20
21import android.app.ActivityManager;
22import android.content.Context;
23import android.content.pm.ActivityInfo;
24import android.content.res.Resources;
25import android.graphics.PixelFormat;
26import android.graphics.Canvas;
27import android.os.IBinder;
28import android.os.SystemProperties;
29import android.util.Log;
30import android.view.View;
31import android.view.ViewGroup;
32import android.view.ViewManager;
33import android.view.WindowManager;
34import android.widget.FrameLayout;
35
36import android.graphics.Color;
37
38/**
39 * Manages creating, showing, hiding and resetting the keyguard.  Calls back
40 * via {@link com.android.internal.policy.impl.KeyguardViewCallback} to poke
41 * the wake lock and report that the keyguard is done, which is in turn,
42 * reported to this class by the current {@link KeyguardViewBase}.
43 */
44public class KeyguardViewManager implements KeyguardWindowController {
45    private final static boolean DEBUG = false;
46    private static String TAG = "KeyguardViewManager";
47
48    private final Context mContext;
49    private final ViewManager mViewManager;
50    private final KeyguardViewCallback mCallback;
51    private final KeyguardViewProperties mKeyguardViewProperties;
52
53    private final KeyguardUpdateMonitor mUpdateMonitor;
54
55    private WindowManager.LayoutParams mWindowLayoutParams;
56    private boolean mNeedsInput = false;
57
58    private FrameLayout mKeyguardHost;
59    private KeyguardViewBase mKeyguardView;
60
61    private boolean mScreenOn = false;
62
63    public interface ShowListener {
64        void onShown(IBinder windowToken);
65    };
66
67    /**
68     * @param context Used to create views.
69     * @param viewManager Keyguard will be attached to this.
70     * @param callback Used to notify of changes.
71     */
72    public KeyguardViewManager(Context context, ViewManager viewManager,
73            KeyguardViewCallback callback, KeyguardViewProperties keyguardViewProperties,
74            KeyguardUpdateMonitor updateMonitor) {
75        mContext = context;
76        mViewManager = viewManager;
77        mCallback = callback;
78        mKeyguardViewProperties = keyguardViewProperties;
79
80        mUpdateMonitor = updateMonitor;
81    }
82
83    /**
84     * Helper class to host the keyguard view.
85     */
86    private static class KeyguardViewHost extends FrameLayout {
87        private final KeyguardViewCallback mCallback;
88
89        private KeyguardViewHost(Context context, KeyguardViewCallback callback) {
90            super(context);
91            mCallback = callback;
92        }
93
94        @Override
95        protected void dispatchDraw(Canvas canvas) {
96            super.dispatchDraw(canvas);
97            mCallback.keyguardDoneDrawing();
98        }
99    }
100
101    /**
102     * Show the keyguard.  Will handle creating and attaching to the view manager
103     * lazily.
104     */
105    public synchronized void show() {
106        if (DEBUG) Log.d(TAG, "show(); mKeyguardView==" + mKeyguardView);
107
108        Resources res = mContext.getResources();
109        boolean enableScreenRotation =
110                SystemProperties.getBoolean("lockscreen.rot_override",false)
111                || res.getBoolean(R.bool.config_enableLockScreenRotation);
112        if (mKeyguardHost == null) {
113            if (DEBUG) Log.d(TAG, "keyguard host is null, creating it...");
114
115            mKeyguardHost = new KeyguardViewHost(mContext, mCallback);
116
117            final int stretch = ViewGroup.LayoutParams.MATCH_PARENT;
118            int flags = WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN
119                    | WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER
120                    | WindowManager.LayoutParams.FLAG_KEEP_SURFACE_WHILE_ANIMATING
121                    /*| WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
122                    | WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR*/ ;
123            if (!mNeedsInput) {
124                flags |= WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
125            }
126            if (ActivityManager.isHighEndGfx(((WindowManager)mContext.getSystemService(
127                    Context.WINDOW_SERVICE)).getDefaultDisplay())) {
128                flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
129            }
130            WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
131                    stretch, stretch, WindowManager.LayoutParams.TYPE_KEYGUARD,
132                    flags, PixelFormat.TRANSLUCENT);
133            lp.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
134            lp.windowAnimations = com.android.internal.R.style.Animation_LockScreen;
135            if (ActivityManager.isHighEndGfx(((WindowManager)mContext.getSystemService(
136                    Context.WINDOW_SERVICE)).getDefaultDisplay())) {
137                lp.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
138                lp.privateFlags |=
139                        WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_HARDWARE_ACCELERATED;
140            }
141            lp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_SET_NEEDS_MENU_KEY;
142            lp.setTitle("Keyguard");
143            mWindowLayoutParams = lp;
144
145            mViewManager.addView(mKeyguardHost, lp);
146        }
147
148        if (enableScreenRotation) {
149            if (DEBUG) Log.d(TAG, "Rotation sensor for lock screen On!");
150            mWindowLayoutParams.screenOrientation = ActivityInfo.SCREEN_ORIENTATION_SENSOR;
151        } else {
152            if (DEBUG) Log.d(TAG, "Rotation sensor for lock screen Off!");
153            mWindowLayoutParams.screenOrientation = ActivityInfo.SCREEN_ORIENTATION_NOSENSOR;
154        }
155
156        mViewManager.updateViewLayout(mKeyguardHost, mWindowLayoutParams);
157
158        if (mKeyguardView == null) {
159            if (DEBUG) Log.d(TAG, "keyguard view is null, creating it...");
160            mKeyguardView = mKeyguardViewProperties.createKeyguardView(mContext, mUpdateMonitor, this);
161            mKeyguardView.setId(R.id.lock_screen);
162            mKeyguardView.setCallback(mCallback);
163
164            final ViewGroup.LayoutParams lp = new FrameLayout.LayoutParams(
165                    ViewGroup.LayoutParams.MATCH_PARENT,
166                    ViewGroup.LayoutParams.MATCH_PARENT);
167
168            mKeyguardHost.addView(mKeyguardView, lp);
169
170            if (mScreenOn) {
171                mKeyguardView.show();
172            }
173        }
174
175        // Disable aspects of the system/status/navigation bars that are not appropriate or
176        // useful for the lockscreen but can be re-shown by dialogs or SHOW_WHEN_LOCKED activities.
177        // Other disabled bits are handled by the KeyguardViewMediator talking directly to the
178        // status bar service.
179        int visFlags =
180                ( View.STATUS_BAR_DISABLE_BACK
181                | View.STATUS_BAR_DISABLE_HOME
182                );
183        mKeyguardHost.setSystemUiVisibility(visFlags);
184
185        mViewManager.updateViewLayout(mKeyguardHost, mWindowLayoutParams);
186        mKeyguardHost.setVisibility(View.VISIBLE);
187        mKeyguardView.requestFocus();
188    }
189
190    public void setNeedsInput(boolean needsInput) {
191        mNeedsInput = needsInput;
192        if (mWindowLayoutParams != null) {
193            if (needsInput) {
194                mWindowLayoutParams.flags &=
195                    ~WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
196            } else {
197                mWindowLayoutParams.flags |=
198                    WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
199            }
200            mViewManager.updateViewLayout(mKeyguardHost, mWindowLayoutParams);
201        }
202    }
203
204    /**
205     * Reset the state of the view.
206     */
207    public synchronized void reset() {
208        if (DEBUG) Log.d(TAG, "reset()");
209        if (mKeyguardView != null) {
210            mKeyguardView.reset();
211        }
212    }
213
214    public synchronized void onScreenTurnedOff() {
215        if (DEBUG) Log.d(TAG, "onScreenTurnedOff()");
216        mScreenOn = false;
217        if (mKeyguardView != null) {
218            mKeyguardView.onScreenTurnedOff();
219        }
220    }
221
222    public synchronized void onScreenTurnedOn(
223            final KeyguardViewManager.ShowListener showListener) {
224        if (DEBUG) Log.d(TAG, "onScreenTurnedOn()");
225        mScreenOn = true;
226        if (mKeyguardView != null) {
227            mKeyguardView.onScreenTurnedOn();
228
229            // Caller should wait for this window to be shown before turning
230            // on the screen.
231            if (mKeyguardHost.getVisibility() == View.VISIBLE) {
232                // Keyguard may be in the process of being shown, but not yet
233                // updated with the window manager...  give it a chance to do so.
234                mKeyguardHost.post(new Runnable() {
235                    @Override public void run() {
236                        if (mKeyguardHost.getVisibility() == View.VISIBLE) {
237                            showListener.onShown(mKeyguardHost.getWindowToken());
238                        } else {
239                            showListener.onShown(null);
240                        }
241                    }
242                });
243            } else {
244                showListener.onShown(null);
245            }
246        } else {
247            showListener.onShown(null);
248        }
249    }
250
251    public synchronized void verifyUnlock() {
252        if (DEBUG) Log.d(TAG, "verifyUnlock()");
253        show();
254        mKeyguardView.verifyUnlock();
255    }
256
257    /**
258     * A key has woken the device.  We use this to potentially adjust the state
259     * of the lock screen based on the key.
260     *
261     * The 'Tq' suffix is per the documentation in {@link android.view.WindowManagerPolicy}.
262     * Be sure not to take any action that takes a long time; any significant
263     * action should be posted to a handler.
264     *
265     * @param keyCode The wake key.  May be {@link KeyEvent#KEYCODE_UNKNOWN} if waking
266     * for a reason other than a key press.
267     */
268    public boolean wakeWhenReadyTq(int keyCode) {
269        if (DEBUG) Log.d(TAG, "wakeWhenReady(" + keyCode + ")");
270        if (mKeyguardView != null) {
271            mKeyguardView.wakeWhenReadyTq(keyCode);
272            return true;
273        } else {
274            Log.w(TAG, "mKeyguardView is null in wakeWhenReadyTq");
275            return false;
276        }
277    }
278
279    /**
280     * Hides the keyguard view
281     */
282    public synchronized void hide() {
283        if (DEBUG) Log.d(TAG, "hide()");
284
285        if (mKeyguardHost != null) {
286            mKeyguardHost.setVisibility(View.GONE);
287            // Don't do this right away, so we can let the view continue to animate
288            // as it goes away.
289            if (mKeyguardView != null) {
290                final KeyguardViewBase lastView = mKeyguardView;
291                mKeyguardView = null;
292                mKeyguardHost.postDelayed(new Runnable() {
293                    public void run() {
294                        synchronized (KeyguardViewManager.this) {
295                            lastView.cleanUp();
296                            mKeyguardHost.removeView(lastView);
297                        }
298                    }
299                }, 500);
300            }
301        }
302    }
303
304    /**
305     * @return Whether the keyguard is showing
306     */
307    public synchronized boolean isShowing() {
308        return (mKeyguardHost != null && mKeyguardHost.getVisibility() == View.VISIBLE);
309    }
310}
311