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_SLIPPERY
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, mCallback,
161                    mUpdateMonitor, this);
162            mKeyguardView.setId(R.id.lock_screen);
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        Log.v(TAG, "KGVM: Set visibility on " + mKeyguardHost + " to " + visFlags);
184        mKeyguardHost.setSystemUiVisibility(visFlags);
185
186        mViewManager.updateViewLayout(mKeyguardHost, mWindowLayoutParams);
187        mKeyguardHost.setVisibility(View.VISIBLE);
188        mKeyguardView.requestFocus();
189    }
190
191    public void setNeedsInput(boolean needsInput) {
192        mNeedsInput = needsInput;
193        if (mWindowLayoutParams != null) {
194            if (needsInput) {
195                mWindowLayoutParams.flags &=
196                    ~WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
197            } else {
198                mWindowLayoutParams.flags |=
199                    WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
200            }
201            mViewManager.updateViewLayout(mKeyguardHost, mWindowLayoutParams);
202        }
203    }
204
205    /**
206     * Reset the state of the view.
207     */
208    public synchronized void reset() {
209        if (DEBUG) Log.d(TAG, "reset()");
210        if (mKeyguardView != null) {
211            mKeyguardView.reset();
212        }
213    }
214
215    public synchronized void onScreenTurnedOff() {
216        if (DEBUG) Log.d(TAG, "onScreenTurnedOff()");
217        mScreenOn = false;
218        if (mKeyguardView != null) {
219            mKeyguardView.onScreenTurnedOff();
220        }
221    }
222
223    public synchronized void onScreenTurnedOn(
224            final KeyguardViewManager.ShowListener showListener) {
225        if (DEBUG) Log.d(TAG, "onScreenTurnedOn()");
226        mScreenOn = true;
227        if (mKeyguardView != null) {
228            mKeyguardView.onScreenTurnedOn();
229
230            // Caller should wait for this window to be shown before turning
231            // on the screen.
232            if (mKeyguardHost.getVisibility() == View.VISIBLE) {
233                // Keyguard may be in the process of being shown, but not yet
234                // updated with the window manager...  give it a chance to do so.
235                mKeyguardHost.post(new Runnable() {
236                    @Override public void run() {
237                        if (mKeyguardHost.getVisibility() == View.VISIBLE) {
238                            showListener.onShown(mKeyguardHost.getWindowToken());
239                        } else {
240                            showListener.onShown(null);
241                        }
242                    }
243                });
244            } else {
245                showListener.onShown(null);
246            }
247        } else {
248            showListener.onShown(null);
249        }
250    }
251
252    public synchronized void verifyUnlock() {
253        if (DEBUG) Log.d(TAG, "verifyUnlock()");
254        show();
255        mKeyguardView.verifyUnlock();
256    }
257
258    /**
259     * A key has woken the device.  We use this to potentially adjust the state
260     * of the lock screen based on the key.
261     *
262     * The 'Tq' suffix is per the documentation in {@link android.view.WindowManagerPolicy}.
263     * Be sure not to take any action that takes a long time; any significant
264     * action should be posted to a handler.
265     *
266     * @param keyCode The wake key.  May be {@link KeyEvent#KEYCODE_UNKNOWN} if waking
267     * for a reason other than a key press.
268     */
269    public boolean wakeWhenReadyTq(int keyCode) {
270        if (DEBUG) Log.d(TAG, "wakeWhenReady(" + keyCode + ")");
271        if (mKeyguardView != null) {
272            mKeyguardView.wakeWhenReadyTq(keyCode);
273            return true;
274        } else {
275            Log.w(TAG, "mKeyguardView is null in wakeWhenReadyTq");
276            return false;
277        }
278    }
279
280    /**
281     * Hides the keyguard view
282     */
283    public synchronized void hide() {
284        if (DEBUG) Log.d(TAG, "hide()");
285
286        if (mKeyguardHost != null) {
287            mKeyguardHost.setVisibility(View.GONE);
288            // Don't do this right away, so we can let the view continue to animate
289            // as it goes away.
290            if (mKeyguardView != null) {
291                final KeyguardViewBase lastView = mKeyguardView;
292                mKeyguardView = null;
293                mKeyguardHost.postDelayed(new Runnable() {
294                    public void run() {
295                        synchronized (KeyguardViewManager.this) {
296                            lastView.cleanUp();
297                            mKeyguardHost.removeView(lastView);
298                        }
299                    }
300                }, 500);
301            }
302        }
303    }
304
305    /**
306     * @return Whether the keyguard is showing
307     */
308    public synchronized boolean isShowing() {
309        return (mKeyguardHost != null && mKeyguardHost.getVisibility() == View.VISIBLE);
310    }
311}
312