KeyguardViewManager.java revision caf24fc2c4bb9747eb80138bf3ce0be067851749
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.keyguard; 18 19import com.android.internal.policy.IKeyguardShowCallback; 20import com.android.internal.widget.LockPatternUtils; 21 22import android.app.Activity; 23import android.app.ActivityManager; 24import android.appwidget.AppWidgetManager; 25import android.content.Context; 26import android.content.pm.ActivityInfo; 27import android.content.res.Configuration; 28import android.content.res.Resources; 29import android.graphics.PixelFormat; 30import android.graphics.Rect; 31import android.os.Bundle; 32import android.os.IBinder; 33import android.os.Parcelable; 34import android.os.RemoteException; 35import android.os.SystemProperties; 36import android.util.Log; 37import android.util.Slog; 38import android.util.SparseArray; 39import android.view.KeyEvent; 40import android.view.LayoutInflater; 41import android.view.MotionEvent; 42import android.view.View; 43import android.view.ViewGroup; 44import android.view.ViewManager; 45import android.view.WindowManager; 46import android.widget.FrameLayout; 47 48/** 49 * Manages creating, showing, hiding and resetting the keyguard. Calls back 50 * via {@link KeyguardViewMediator.ViewMediatorCallback} to poke 51 * the wake lock and report that the keyguard is done, which is in turn, 52 * reported to this class by the current {@link KeyguardViewBase}. 53 */ 54public class KeyguardViewManager { 55 private final static boolean DEBUG = KeyguardViewMediator.DEBUG; 56 private static String TAG = "KeyguardViewManager"; 57 public static boolean USE_UPPER_CASE = true; 58 public final static String IS_SWITCHING_USER = "is_switching_user"; 59 60 // Timeout used for keypresses 61 static final int DIGIT_PRESS_WAKE_MILLIS = 5000; 62 63 private final Context mContext; 64 private final ViewManager mViewManager; 65 private final KeyguardViewMediator.ViewMediatorCallback mViewMediatorCallback; 66 67 private WindowManager.LayoutParams mWindowLayoutParams; 68 private boolean mNeedsInput = false; 69 70 private FrameLayout mKeyguardHost; 71 private KeyguardHostView mKeyguardView; 72 73 private boolean mScreenOn = false; 74 private LockPatternUtils mLockPatternUtils; 75 76 public interface ShowListener { 77 void onShown(IBinder windowToken); 78 }; 79 80 /** 81 * @param context Used to create views. 82 * @param viewManager Keyguard will be attached to this. 83 * @param callback Used to notify of changes. 84 * @param lockPatternUtils 85 */ 86 public KeyguardViewManager(Context context, ViewManager viewManager, 87 KeyguardViewMediator.ViewMediatorCallback callback, 88 LockPatternUtils lockPatternUtils) { 89 mContext = context; 90 mViewManager = viewManager; 91 mViewMediatorCallback = callback; 92 mLockPatternUtils = lockPatternUtils; 93 } 94 95 /** 96 * Show the keyguard. Will handle creating and attaching to the view manager 97 * lazily. 98 */ 99 public synchronized void show(Bundle options) { 100 if (DEBUG) Log.d(TAG, "show(); mKeyguardView==" + mKeyguardView); 101 102 boolean enableScreenRotation = shouldEnableScreenRotation(); 103 104 maybeCreateKeyguardLocked(enableScreenRotation, false, options); 105 maybeEnableScreenRotation(enableScreenRotation); 106 107 // Disable common aspects of the system/status/navigation bars that are not appropriate or 108 // useful on any keyguard screen but can be re-shown by dialogs or SHOW_WHEN_LOCKED 109 // activities. Other disabled bits are handled by the KeyguardViewMediator talking 110 // directly to the status bar service. 111 final int visFlags = View.STATUS_BAR_DISABLE_HOME; 112 if (DEBUG) Log.v(TAG, "show:setSystemUiVisibility(" + Integer.toHexString(visFlags)+")"); 113 mKeyguardHost.setSystemUiVisibility(visFlags); 114 115 mViewManager.updateViewLayout(mKeyguardHost, mWindowLayoutParams); 116 mKeyguardHost.setVisibility(View.VISIBLE); 117 mKeyguardView.show(); 118 mKeyguardView.requestFocus(); 119 } 120 121 private boolean shouldEnableScreenRotation() { 122 Resources res = mContext.getResources(); 123 return SystemProperties.getBoolean("lockscreen.rot_override",false) 124 || res.getBoolean(R.bool.config_enableLockScreenRotation); 125 } 126 127 class ViewManagerHost extends FrameLayout { 128 public ViewManagerHost(Context context) { 129 super(context); 130 setFitsSystemWindows(true); 131 } 132 133 @Override 134 protected boolean fitSystemWindows(Rect insets) { 135 Log.v("TAG", "bug 7643792: fitSystemWindows(" + insets.toShortString() + ")"); 136 return super.fitSystemWindows(insets); 137 } 138 139 @Override 140 protected void onConfigurationChanged(Configuration newConfig) { 141 super.onConfigurationChanged(newConfig); 142 if (mKeyguardHost.getVisibility() == View.VISIBLE) { 143 // only propagate configuration messages if we're currently showing 144 maybeCreateKeyguardLocked(shouldEnableScreenRotation(), true, null); 145 } else { 146 if (DEBUG) Log.v(TAG, "onConfigurationChanged: view not visible"); 147 } 148 } 149 150 @Override 151 public boolean dispatchKeyEvent(KeyEvent event) { 152 if (mKeyguardView != null) { 153 // Always process back and menu keys, regardless of focus 154 if (event.getAction() == KeyEvent.ACTION_DOWN) { 155 int keyCode = event.getKeyCode(); 156 if (keyCode == KeyEvent.KEYCODE_BACK && mKeyguardView.handleBackKey()) { 157 return true; 158 } else if (keyCode == KeyEvent.KEYCODE_MENU && mKeyguardView.handleMenuKey()) { 159 return true; 160 } 161 } 162 // Always process media keys, regardless of focus 163 if (mKeyguardView.dispatchKeyEvent(event)) { 164 return true; 165 } 166 } 167 return super.dispatchKeyEvent(event); 168 } 169 } 170 171 SparseArray<Parcelable> mStateContainer = new SparseArray<Parcelable>(); 172 173 private void maybeCreateKeyguardLocked(boolean enableScreenRotation, boolean force, 174 Bundle options) { 175 if (mKeyguardHost != null) { 176 mKeyguardHost.saveHierarchyState(mStateContainer); 177 } 178 179 if (mKeyguardHost == null) { 180 if (DEBUG) Log.d(TAG, "keyguard host is null, creating it..."); 181 182 mKeyguardHost = new ViewManagerHost(mContext); 183 184 int flags = WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN 185 | WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR 186 | WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN 187 | WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER; 188 189 if (!mNeedsInput) { 190 flags |= WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM; 191 } 192 193 final int stretch = ViewGroup.LayoutParams.MATCH_PARENT; 194 final int type = WindowManager.LayoutParams.TYPE_KEYGUARD; 195 WindowManager.LayoutParams lp = new WindowManager.LayoutParams( 196 stretch, stretch, type, flags, PixelFormat.TRANSLUCENT); 197 lp.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE; 198 lp.windowAnimations = R.style.Animation_LockScreen; 199 lp.screenOrientation = enableScreenRotation ? 200 ActivityInfo.SCREEN_ORIENTATION_USER : ActivityInfo.SCREEN_ORIENTATION_NOSENSOR; 201 202 if (ActivityManager.isHighEndGfx()) { 203 lp.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED; 204 lp.privateFlags |= 205 WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_HARDWARE_ACCELERATED; 206 } 207 lp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_SET_NEEDS_MENU_KEY; 208 lp.inputFeatures |= WindowManager.LayoutParams.INPUT_FEATURE_DISABLE_USER_ACTIVITY; 209 lp.setTitle("Keyguard"); 210 mWindowLayoutParams = lp; 211 mViewManager.addView(mKeyguardHost, lp); 212 } 213 214 if (force || mKeyguardView == null) { 215 inflateKeyguardView(options); 216 mKeyguardView.requestFocus(); 217 } 218 updateUserActivityTimeoutInWindowLayoutParams(); 219 mViewManager.updateViewLayout(mKeyguardHost, mWindowLayoutParams); 220 221 mKeyguardHost.restoreHierarchyState(mStateContainer); 222 } 223 224 private void inflateKeyguardView(Bundle options) { 225 View v = mKeyguardHost.findViewById(R.id.keyguard_host_view); 226 if (v != null) { 227 mKeyguardHost.removeView(v); 228 } 229 final LayoutInflater inflater = LayoutInflater.from(mContext); 230 View view = inflater.inflate(R.layout.keyguard_host_view, mKeyguardHost, true); 231 mKeyguardView = (KeyguardHostView) view.findViewById(R.id.keyguard_host_view); 232 mKeyguardView.setLockPatternUtils(mLockPatternUtils); 233 mKeyguardView.setViewMediatorCallback(mViewMediatorCallback); 234 mKeyguardView.initializeSwitchingUserState(options != null && 235 options.getBoolean(IS_SWITCHING_USER)); 236 237 // HACK 238 // The keyguard view will have set up window flags in onFinishInflate before we set 239 // the view mediator callback. Make sure it knows the correct IME state. 240 if (mViewMediatorCallback != null) { 241 KeyguardPasswordView kpv = (KeyguardPasswordView) mKeyguardView.findViewById( 242 R.id.keyguard_password_view); 243 244 if (kpv != null) { 245 mViewMediatorCallback.setNeedsInput(kpv.needsInput()); 246 } 247 } 248 249 if (options != null) { 250 int widgetToShow = options.getInt(LockPatternUtils.KEYGUARD_SHOW_APPWIDGET, 251 AppWidgetManager.INVALID_APPWIDGET_ID); 252 if (widgetToShow != AppWidgetManager.INVALID_APPWIDGET_ID) { 253 mKeyguardView.goToWidget(widgetToShow); 254 } 255 } 256 } 257 258 public void updateUserActivityTimeout() { 259 updateUserActivityTimeoutInWindowLayoutParams(); 260 mViewManager.updateViewLayout(mKeyguardHost, mWindowLayoutParams); 261 } 262 263 private void updateUserActivityTimeoutInWindowLayoutParams() { 264 // Use the user activity timeout requested by the keyguard view, if any. 265 if (mKeyguardView != null) { 266 long timeout = mKeyguardView.getUserActivityTimeout(); 267 if (timeout >= 0) { 268 mWindowLayoutParams.userActivityTimeout = timeout; 269 return; 270 } 271 } 272 273 // Otherwise, use the default timeout. 274 mWindowLayoutParams.userActivityTimeout = KeyguardViewMediator.AWAKE_INTERVAL_DEFAULT_MS; 275 } 276 277 private void maybeEnableScreenRotation(boolean enableScreenRotation) { 278 // TODO: move this outside 279 if (enableScreenRotation) { 280 if (DEBUG) Log.d(TAG, "Rotation sensor for lock screen On!"); 281 mWindowLayoutParams.screenOrientation = ActivityInfo.SCREEN_ORIENTATION_USER; 282 } else { 283 if (DEBUG) Log.d(TAG, "Rotation sensor for lock screen Off!"); 284 mWindowLayoutParams.screenOrientation = ActivityInfo.SCREEN_ORIENTATION_NOSENSOR; 285 } 286 mViewManager.updateViewLayout(mKeyguardHost, mWindowLayoutParams); 287 } 288 289 public void setNeedsInput(boolean needsInput) { 290 mNeedsInput = needsInput; 291 if (mWindowLayoutParams != null) { 292 if (needsInput) { 293 mWindowLayoutParams.flags &= 294 ~WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM; 295 } else { 296 mWindowLayoutParams.flags |= 297 WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM; 298 } 299 300 try { 301 mViewManager.updateViewLayout(mKeyguardHost, mWindowLayoutParams); 302 } catch (java.lang.IllegalArgumentException e) { 303 // TODO: Ensure this method isn't called on views that are changing... 304 Log.w(TAG,"Can't update input method on " + mKeyguardHost + " window not attached"); 305 } 306 } 307 } 308 309 /** 310 * Reset the state of the view. 311 */ 312 public synchronized void reset(Bundle options) { 313 if (DEBUG) Log.d(TAG, "reset()"); 314 // User might have switched, check if we need to go back to keyguard 315 // TODO: It's preferable to stay and show the correct lockscreen or unlock if none 316 maybeCreateKeyguardLocked(shouldEnableScreenRotation(), true, options); 317 } 318 319 public synchronized void onScreenTurnedOff() { 320 if (DEBUG) Log.d(TAG, "onScreenTurnedOff()"); 321 mScreenOn = false; 322 if (mKeyguardView != null) { 323 mKeyguardView.onScreenTurnedOff(); 324 } 325 } 326 327 public synchronized void onScreenTurnedOn(final IKeyguardShowCallback callback) { 328 if (DEBUG) Log.d(TAG, "onScreenTurnedOn()"); 329 mScreenOn = true; 330 if (mKeyguardView != null) { 331 mKeyguardView.onScreenTurnedOn(); 332 333 // Caller should wait for this window to be shown before turning 334 // on the screen. 335 if (callback != null) { 336 if (mKeyguardHost.getVisibility() == View.VISIBLE) { 337 // Keyguard may be in the process of being shown, but not yet 338 // updated with the window manager... give it a chance to do so. 339 mKeyguardHost.post(new Runnable() { 340 @Override 341 public void run() { 342 IBinder token = null; 343 if (mKeyguardHost.getVisibility() == View.VISIBLE) { 344 token = mKeyguardHost.getWindowToken(); 345 } 346 try { 347 callback.onShown(token); 348 } catch (RemoteException e) { 349 Slog.w(TAG, "Exception calling onShown():", e); 350 } 351 } 352 }); 353 } else { 354 try { 355 callback.onShown(null); 356 } catch (RemoteException e) { 357 Slog.w(TAG, "Exception calling onShown():", e); 358 } 359 } 360 } 361 } else if (callback != null) { 362 try { 363 callback.onShown(null); 364 } catch (RemoteException e) { 365 Slog.w(TAG, "Exception calling onShown():", e); 366 } 367 } 368 } 369 370 public synchronized void verifyUnlock() { 371 if (DEBUG) Log.d(TAG, "verifyUnlock()"); 372 show(null); 373 mKeyguardView.verifyUnlock(); 374 } 375 376 /** 377 * Hides the keyguard view 378 */ 379 public synchronized void hide() { 380 if (DEBUG) Log.d(TAG, "hide()"); 381 382 if (mKeyguardHost != null) { 383 mKeyguardHost.setVisibility(View.GONE); 384 385 // We really only want to preserve keyguard state for configuration changes. Hence 386 // we should clear state of widgets (e.g. Music) when we hide keyguard so it can 387 // start with a fresh state when we return. 388 mStateContainer.clear(); 389 390 // Don't do this right away, so we can let the view continue to animate 391 // as it goes away. 392 if (mKeyguardView != null) { 393 final KeyguardViewBase lastView = mKeyguardView; 394 mKeyguardView = null; 395 mKeyguardHost.postDelayed(new Runnable() { 396 @Override 397 public void run() { 398 synchronized (KeyguardViewManager.this) { 399 lastView.cleanUp(); 400 mKeyguardHost.removeView(lastView); 401 } 402 } 403 }, 500); 404 } 405 } 406 } 407 408 /** 409 * Dismisses the keyguard by going to the next screen or making it gone. 410 */ 411 public synchronized void dismiss() { 412 if (mScreenOn) { 413 mKeyguardView.dismiss(); 414 } 415 } 416 417 /** 418 * @return Whether the keyguard is showing 419 */ 420 public synchronized boolean isShowing() { 421 return (mKeyguardHost != null && mKeyguardHost.getVisibility() == View.VISIBLE); 422 } 423 424 public void showAssistant() { 425 if (mKeyguardView != null) { 426 mKeyguardView.showAssistant(); 427 } 428 } 429 430 public void dispatch(MotionEvent event) { 431 if (mKeyguardView != null) { 432 mKeyguardView.dispatch(event); 433 } 434 } 435} 436