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