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