blob: efd09e0d0ade61ceca593e41e5a563d2b13e08e9 [file] [log] [blame]
/*
* Copyright (C) 2007 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.internal.policy.impl.keyguard;
import android.app.Activity;
import android.app.ActivityManager;
import android.appwidget.AppWidgetManager;
import android.content.Context;
import android.content.pm.ActivityInfo;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.os.Bundle;
import android.os.IBinder;
import android.os.Parcelable;
import android.os.SystemProperties;
import android.util.Log;
import android.util.Slog;
import android.util.SparseArray;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewManager;
import android.view.WindowManager;
import android.widget.FrameLayout;
import com.android.internal.R;
import com.android.internal.widget.LockPatternUtils;
/**
* Manages creating, showing, hiding and resetting the keyguard. Calls back
* via {@link KeyguardViewMediator.ViewMediatorCallback} to poke
* the wake lock and report that the keyguard is done, which is in turn,
* reported to this class by the current {@link KeyguardViewBase}.
*/
public class KeyguardViewManager {
private final static boolean DEBUG = KeyguardViewMediator.DEBUG;
private static String TAG = "KeyguardViewManager";
public static boolean USE_UPPER_CASE = true;
// Timeout used for keypresses
static final int DIGIT_PRESS_WAKE_MILLIS = 5000;
private final Context mContext;
private final ViewManager mViewManager;
private final KeyguardViewMediator.ViewMediatorCallback mViewMediatorCallback;
private WindowManager.LayoutParams mWindowLayoutParams;
private boolean mNeedsInput = false;
private FrameLayout mKeyguardHost;
private KeyguardHostView mKeyguardView;
private boolean mScreenOn = false;
private LockPatternUtils mLockPatternUtils;
public interface ShowListener {
void onShown(IBinder windowToken);
};
/**
* @param context Used to create views.
* @param viewManager Keyguard will be attached to this.
* @param callback Used to notify of changes.
* @param lockPatternUtils
*/
public KeyguardViewManager(Context context, ViewManager viewManager,
KeyguardViewMediator.ViewMediatorCallback callback,
LockPatternUtils lockPatternUtils) {
mContext = context;
mViewManager = viewManager;
mViewMediatorCallback = callback;
mLockPatternUtils = lockPatternUtils;
}
/**
* Show the keyguard. Will handle creating and attaching to the view manager
* lazily.
*/
public synchronized void show(Bundle options) {
if (DEBUG) Log.d(TAG, "show(); mKeyguardView==" + mKeyguardView);
boolean enableScreenRotation = shouldEnableScreenRotation();
maybeCreateKeyguardLocked(enableScreenRotation, false, options);
maybeEnableScreenRotation(enableScreenRotation);
// Disable common aspects of the system/status/navigation bars that are not appropriate or
// useful on any keyguard screen but can be re-shown by dialogs or SHOW_WHEN_LOCKED
// activities. Other disabled bits are handled by the KeyguardViewMediator talking
// directly to the status bar service.
final int visFlags = View.STATUS_BAR_DISABLE_HOME;
if (DEBUG) Log.v(TAG, "show:setSystemUiVisibility(" + Integer.toHexString(visFlags)+")");
mKeyguardHost.setSystemUiVisibility(visFlags);
mViewManager.updateViewLayout(mKeyguardHost, mWindowLayoutParams);
mKeyguardHost.setVisibility(View.VISIBLE);
mKeyguardView.show();
mKeyguardView.requestFocus();
}
private boolean shouldEnableScreenRotation() {
Resources res = mContext.getResources();
return SystemProperties.getBoolean("lockscreen.rot_override",false)
|| res.getBoolean(com.android.internal.R.bool.config_enableLockScreenRotation);
}
class ViewManagerHost extends FrameLayout {
public ViewManagerHost(Context context) {
super(context);
setFitsSystemWindows(true);
}
@Override
protected boolean fitSystemWindows(Rect insets) {
Log.v("TAG", "bug 7643792: fitSystemWindows(" + insets.toShortString() + ")");
return super.fitSystemWindows(insets);
}
@Override
protected void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
post(new Runnable() {
@Override
public void run() {
synchronized (KeyguardViewManager.this) {
if (mKeyguardHost.getVisibility() == View.VISIBLE) {
// only propagate configuration messages if we're currently showing
maybeCreateKeyguardLocked(shouldEnableScreenRotation(), true, null);
} else {
if (DEBUG) Log.v(TAG, "onConfigurationChanged: view not visible");
}
}
}
});
}
@Override
public boolean dispatchKeyEvent(KeyEvent event) {
if (event.getAction() == KeyEvent.ACTION_DOWN && mKeyguardView != null) {
int keyCode = event.getKeyCode();
if (keyCode == KeyEvent.KEYCODE_BACK && mKeyguardView.handleBackKey()) {
return true;
} else if (keyCode == KeyEvent.KEYCODE_MENU && mKeyguardView.handleMenuKey()) {
return true;
}
}
return super.dispatchKeyEvent(event);
}
}
SparseArray<Parcelable> mStateContainer = new SparseArray<Parcelable>();
private void maybeCreateKeyguardLocked(boolean enableScreenRotation, boolean force,
Bundle options) {
final boolean isActivity = (mContext instanceof Activity); // for test activity
if (mKeyguardHost != null) {
mKeyguardHost.saveHierarchyState(mStateContainer);
}
if (mKeyguardHost == null) {
if (DEBUG) Log.d(TAG, "keyguard host is null, creating it...");
mKeyguardHost = new ViewManagerHost(mContext);
int flags = WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
| WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR
| WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN
| WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
if (!mNeedsInput) {
flags |= WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
}
if (ActivityManager.isHighEndGfx()) {
flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
}
final int stretch = ViewGroup.LayoutParams.MATCH_PARENT;
final int type = isActivity ? WindowManager.LayoutParams.TYPE_APPLICATION
: WindowManager.LayoutParams.TYPE_KEYGUARD;
WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
stretch, stretch, type, flags, PixelFormat.TRANSLUCENT);
lp.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
lp.windowAnimations = com.android.internal.R.style.Animation_LockScreen;
if (ActivityManager.isHighEndGfx()) {
lp.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
lp.privateFlags |=
WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_HARDWARE_ACCELERATED;
}
lp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_SET_NEEDS_MENU_KEY;
if (isActivity) {
lp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
}
lp.inputFeatures |= WindowManager.LayoutParams.INPUT_FEATURE_DISABLE_USER_ACTIVITY;
lp.setTitle(isActivity ? "KeyguardMock" : "Keyguard");
mWindowLayoutParams = lp;
mViewManager.addView(mKeyguardHost, lp);
}
if (force || mKeyguardView == null) {
inflateKeyguardView(options);
}
updateUserActivityTimeoutInWindowLayoutParams();
mViewManager.updateViewLayout(mKeyguardHost, mWindowLayoutParams);
mKeyguardHost.restoreHierarchyState(mStateContainer);
}
private void inflateKeyguardView(Bundle options) {
View v = mKeyguardHost.findViewById(R.id.keyguard_host_view);
if (v != null) {
mKeyguardHost.removeView(v);
}
// TODO: Remove once b/7094175 is fixed
if (false) Slog.d(TAG, "inflateKeyguardView: b/7094175 mContext.config="
+ mContext.getResources().getConfiguration());
final LayoutInflater inflater = LayoutInflater.from(mContext);
View view = inflater.inflate(R.layout.keyguard_host_view, mKeyguardHost, true);
mKeyguardView = (KeyguardHostView) view.findViewById(R.id.keyguard_host_view);
mKeyguardView.setLockPatternUtils(mLockPatternUtils);
mKeyguardView.setViewMediatorCallback(mViewMediatorCallback);
// HACK
// The keyguard view will have set up window flags in onFinishInflate before we set
// the view mediator callback. Make sure it knows the correct IME state.
if (mViewMediatorCallback != null) {
KeyguardPasswordView kpv = (KeyguardPasswordView) mKeyguardView.findViewById(
R.id.keyguard_password_view);
if (kpv != null) {
mViewMediatorCallback.setNeedsInput(kpv.needsInput());
}
}
if (options != null) {
int widgetToShow = options.getInt(LockPatternUtils.KEYGUARD_SHOW_APPWIDGET,
AppWidgetManager.INVALID_APPWIDGET_ID);
if (widgetToShow != AppWidgetManager.INVALID_APPWIDGET_ID) {
mKeyguardView.goToWidget(widgetToShow);
}
}
}
public void updateUserActivityTimeout() {
updateUserActivityTimeoutInWindowLayoutParams();
mViewManager.updateViewLayout(mKeyguardHost, mWindowLayoutParams);
}
private void updateUserActivityTimeoutInWindowLayoutParams() {
// Use the user activity timeout requested by the keyguard view, if any.
if (mKeyguardView != null) {
long timeout = mKeyguardView.getUserActivityTimeout();
if (timeout >= 0) {
mWindowLayoutParams.userActivityTimeout = timeout;
return;
}
}
// Otherwise, use the default timeout.
mWindowLayoutParams.userActivityTimeout = KeyguardViewMediator.AWAKE_INTERVAL_DEFAULT_MS;
}
private void maybeEnableScreenRotation(boolean enableScreenRotation) {
// TODO: move this outside
if (enableScreenRotation) {
if (DEBUG) Log.d(TAG, "Rotation sensor for lock screen On!");
mWindowLayoutParams.screenOrientation = ActivityInfo.SCREEN_ORIENTATION_USER;
} else {
if (DEBUG) Log.d(TAG, "Rotation sensor for lock screen Off!");
mWindowLayoutParams.screenOrientation = ActivityInfo.SCREEN_ORIENTATION_NOSENSOR;
}
mViewManager.updateViewLayout(mKeyguardHost, mWindowLayoutParams);
}
public void setNeedsInput(boolean needsInput) {
mNeedsInput = needsInput;
if (mWindowLayoutParams != null) {
if (needsInput) {
mWindowLayoutParams.flags &=
~WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
} else {
mWindowLayoutParams.flags |=
WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
}
try {
mViewManager.updateViewLayout(mKeyguardHost, mWindowLayoutParams);
} catch (java.lang.IllegalArgumentException e) {
// TODO: Ensure this method isn't called on views that are changing...
Log.w(TAG,"Can't update input method on " + mKeyguardHost + " window not attached");
}
}
}
/**
* Reset the state of the view.
*/
public synchronized void reset(Bundle options) {
if (DEBUG) Log.d(TAG, "reset()");
// User might have switched, check if we need to go back to keyguard
// TODO: It's preferable to stay and show the correct lockscreen or unlock if none
maybeCreateKeyguardLocked(shouldEnableScreenRotation(), true, options);
}
public synchronized void onScreenTurnedOff() {
if (DEBUG) Log.d(TAG, "onScreenTurnedOff()");
mScreenOn = false;
if (mKeyguardView != null) {
mKeyguardView.onScreenTurnedOff();
}
}
public synchronized void onScreenTurnedOn(
final KeyguardViewManager.ShowListener showListener) {
if (DEBUG) Log.d(TAG, "onScreenTurnedOn()");
mScreenOn = true;
if (mKeyguardView != null) {
mKeyguardView.onScreenTurnedOn();
// Caller should wait for this window to be shown before turning
// on the screen.
if (showListener != null) {
if (mKeyguardHost.getVisibility() == View.VISIBLE) {
// Keyguard may be in the process of being shown, but not yet
// updated with the window manager... give it a chance to do so.
mKeyguardHost.post(new Runnable() {
@Override
public void run() {
if (mKeyguardHost.getVisibility() == View.VISIBLE) {
showListener.onShown(mKeyguardHost.getWindowToken());
} else {
showListener.onShown(null);
}
}
});
} else {
showListener.onShown(null);
}
}
} else if (showListener != null) {
showListener.onShown(null);
}
}
public synchronized void verifyUnlock() {
if (DEBUG) Log.d(TAG, "verifyUnlock()");
show(null);
mKeyguardView.verifyUnlock();
}
/**
* A key has woken the device. We use this to potentially adjust the state
* of the lock screen based on the key.
*
* The 'Tq' suffix is per the documentation in {@link android.view.WindowManagerPolicy}.
* Be sure not to take any action that takes a long time; any significant
* action should be posted to a handler.
*
* @param keyCode The wake key. May be {@link KeyEvent#KEYCODE_UNKNOWN} if waking
* for a reason other than a key press.
*/
public boolean wakeWhenReadyTq(int keyCode) {
if (DEBUG) Log.d(TAG, "wakeWhenReady(" + keyCode + ")");
if (mKeyguardView != null) {
mKeyguardView.wakeWhenReadyTq(keyCode);
return true;
}
Log.w(TAG, "mKeyguardView is null in wakeWhenReadyTq");
return false;
}
/**
* Hides the keyguard view
*/
public synchronized void hide() {
if (DEBUG) Log.d(TAG, "hide()");
if (mKeyguardHost != null) {
mKeyguardHost.setVisibility(View.GONE);
// We really only want to preserve keyguard state for configuration changes. Hence
// we should clear state of widgets (e.g. Music) when we hide keyguard so it can
// start with a fresh state when we return.
mStateContainer.clear();
// Don't do this right away, so we can let the view continue to animate
// as it goes away.
if (mKeyguardView != null) {
final KeyguardViewBase lastView = mKeyguardView;
mKeyguardView = null;
mKeyguardHost.postDelayed(new Runnable() {
@Override
public void run() {
synchronized (KeyguardViewManager.this) {
lastView.cleanUp();
mKeyguardHost.removeView(lastView);
}
}
}, 500);
}
}
}
/**
* Dismisses the keyguard by going to the next screen or making it gone.
*/
public synchronized void dismiss() {
if (mScreenOn) {
mKeyguardView.dismiss();
}
}
/**
* @return Whether the keyguard is showing
*/
public synchronized boolean isShowing() {
return (mKeyguardHost != null && mKeyguardHost.getVisibility() == View.VISIBLE);
}
public void showAssistant() {
if (mKeyguardView != null) {
mKeyguardView.showAssistant();
}
}
}