blob: 2bff15bd72fca6c52947fd650d074c50ecdf2124 [file] [log] [blame]
/*
* Copyright (C) 2006-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;
import android.app.Activity;
import android.app.ActivityManagerNative;
import android.app.IActivityManager;
import android.app.IStatusBar;
import android.content.BroadcastReceiver;
import android.content.ContentQueryMap;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.database.Cursor;
import android.graphics.Rect;
import android.os.Handler;
import android.os.IBinder;
import android.os.LocalPowerManager;
import android.os.PowerManager;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemClock;
import android.provider.Settings;
import static android.provider.Settings.System.END_BUTTON_BEHAVIOR;
import com.android.internal.policy.PolicyManager;
import com.android.internal.telephony.ITelephony;
import android.util.Config;
import android.util.EventLog;
import android.util.Log;
import android.view.HapticFeedbackConstants;
import android.view.IWindowManager;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.OrientationListener;
import android.view.RawInputEvent;
import android.view.Surface;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.Window;
import android.view.WindowManager;
import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW;
import static android.view.WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN;
import static android.view.WindowManager.LayoutParams.FLAG_FULLSCREEN;
import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR;
import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS;
import static android.view.WindowManager.LayoutParams.LAST_APPLICATION_WINDOW;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA_OVERLAY;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_PANEL;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_SUB_PANEL;
import static android.view.WindowManager.LayoutParams.TYPE_KEYGUARD;
import static android.view.WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG;
import static android.view.WindowManager.LayoutParams.TYPE_PHONE;
import static android.view.WindowManager.LayoutParams.TYPE_PRIORITY_PHONE;
import static android.view.WindowManager.LayoutParams.TYPE_SEARCH_BAR;
import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR;
import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL;
import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_ALERT;
import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_ERROR;
import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY;
import static android.view.WindowManager.LayoutParams.TYPE_TOAST;
import android.view.WindowManagerImpl;
import android.view.WindowManagerPolicy;
import android.view.WindowManagerPolicy.WindowState;
import android.media.IAudioService;
import android.media.AudioManager;
import java.util.Observable;
import java.util.Observer;
/**
* WindowManagerPolicy implementation for the Android MID UI.
*/
public class MidWindowManager implements WindowManagerPolicy {
private static final String TAG = "MidWindowManager";
private static final boolean DEBUG = false;
private static final boolean localLOGV = DEBUG ? Config.LOGD : Config.LOGV;
private static final boolean SHOW_STARTING_ANIMATIONS = true;
private static final int APPLICATION_LAYER = 1;
private static final int PHONE_LAYER = 2;
private static final int SEARCH_BAR_LAYER = 3;
private static final int STATUS_BAR_PANEL_LAYER = 4;
// toasts and the plugged-in battery thing
private static final int TOAST_LAYER = 5;
private static final int STATUS_BAR_LAYER = 6;
// SIM errors and unlock. Not sure if this really should be in a high layer.
private static final int PRIORITY_PHONE_LAYER = 7;
// like the ANR / app crashed dialogs
private static final int SYSTEM_ALERT_LAYER = 8;
// system-level error dialogs
private static final int SYSTEM_ERROR_LAYER = 9;
// the keyguard; nothing on top of these can take focus, since they are
// responsible for power management when displayed.
private static final int KEYGUARD_LAYER = 10;
private static final int KEYGUARD_DIALOG_LAYER = 11;
// things in here CAN NOT take focus, but are shown on top of everything else.
private static final int SYSTEM_OVERLAY_LAYER = 12;
static final int APPLICATION_MEDIA_SUBLAYER = -2;
static final int APPLICATION_MEDIA_OVERLAY_SUBLAYER = -1;
static final int APPLICATION_PANEL_SUBLAYER = 1;
static final int APPLICATION_SUB_PANEL_SUBLAYER = 2;
private static final boolean SINGLE_PRESS_OFF = false;
private static final float SLIDE_TOUCH_EVENT_SIZE_LIMIT = 0.6f;
static public final String SYSTEM_DIALOG_REASON_KEY = "reason";
static public final String SYSTEM_DIALOG_REASON_GLOBAL_ACTIONS = "globalactions";
static public final String SYSTEM_DIALOG_REASON_RECENT_APPS = "recentapps";
private Context mContext;
private IWindowManager mWindowManager;
private LocalPowerManager mPowerManager;
boolean mSafeMode;
private WindowState mStatusBar = null;
private WindowState mSearchBar = null;
private WindowState mKeyguard = null;
private boolean mFirstConnect = true;
private GlobalActions mGlobalActions;
private boolean mShouldTurnOffOnKeyUp;
private RecentApplicationsDialog mRecentAppsDialog;
private Handler mHandler;
private boolean mLidOpen;
private boolean mScreenOn = false;
private int mCurrentAppOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
private int mW, mH;
private int mCurLeft, mCurTop, mCurRight, mCurBottom;
static final Rect mTmpParentFrame = new Rect();
static final Rect mTmpDisplayFrame = new Rect();
static final Rect mTmpVisibleFrame = new Rect();
private WindowState mTopFullscreenOpaqueWindowState;
private boolean mForceStatusBar;
private boolean mHomePressed;
private Intent mHomeIntent;
private boolean mSearchKeyPressed;
private boolean mConsumeSearchKeyUp;
private static final int ENDCALL_HOME = 0x1;
private static final int ENDCALL_SLEEPS = 0x2;
private static final int DEFAULT_ENDCALL_BEHAVIOR = ENDCALL_SLEEPS;
private int mEndcallBehavior;
private ShortcutManager mShortcutManager;
private PowerManager.WakeLock mBroadcastWakeLock;
/*
* Various use cases for invoking this function
* screen turning off, should always disable listeners if already enabled
* screen turned on and current app has sensor based orientation, enable listeners
* if not already enabled
* screen turned on and current app does not have sensor orientation, disable listeners if
* already enabled
* screen turning on and current app has sensor based orientation, enable listeners if needed
* screen turning on and current app has nosensor based orientation, do nothing
*/
private Runnable mEndCallLongPress = new Runnable() {
public void run() {
mShouldTurnOffOnKeyUp = false;
sendCloseSystemWindows(SYSTEM_DIALOG_REASON_GLOBAL_ACTIONS);
showGlobalActionsDialog();
}
};
private void showGlobalActionsDialog() {
if (mGlobalActions == null) {
mGlobalActions = new GlobalActions(mContext, mPowerManager);
}
mGlobalActions.showDialog(false, isDeviceProvisioned());
}
private boolean isDeviceProvisioned() {
return Settings.System.getInt(
mContext.getContentResolver(), Settings.System.DEVICE_PROVISIONED, 0) != 0;
}
/**
* When a home-key longpress expires, close other system windows and launch the recent apps
*/
private Runnable mHomeLongPress = new Runnable() {
public void run() {
/*
* Eat the longpress so it won't dismiss the recent apps dialog when
* the user lets go of the home key
*/
mHomePressed = false;
sendCloseSystemWindows(SYSTEM_DIALOG_REASON_RECENT_APPS);
showRecentAppsDialog();
}
};
/**
* Create (if necessary) and launch the recent apps dialog
*/
private void showRecentAppsDialog() {
if (mRecentAppsDialog == null) {
mRecentAppsDialog = new RecentApplicationsDialog(mContext);
}
mRecentAppsDialog.show();
}
/** {@inheritDoc} */
public void init(Context context, IWindowManager windowManager,
LocalPowerManager powerManager) {
mContext = context;
mWindowManager = windowManager;
mPowerManager = powerManager;
mHandler = new Handler();
mShortcutManager = new ShortcutManager(context, mHandler);
mShortcutManager.observe();
mHomeIntent = new Intent(Intent.ACTION_MAIN, null);
mHomeIntent.addCategory(Intent.CATEGORY_HOME);
mHomeIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK
| Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
mBroadcastWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
"MidWindowManager.mBroadcastWakeLock");
}
/** {@inheritDoc} */
public int checkAddPermission(WindowManager.LayoutParams attrs) {
int type = attrs.type;
if (type < WindowManager.LayoutParams.FIRST_SYSTEM_WINDOW
|| type > WindowManager.LayoutParams.LAST_SYSTEM_WINDOW) {
return WindowManagerImpl.ADD_OKAY;
}
String permission = null;
switch (type) {
case TYPE_TOAST:
// XXX right now the app process has complete control over
// this... should introduce a token to let the system
// monitor/control what they are doing.
break;
case TYPE_PHONE:
case TYPE_PRIORITY_PHONE:
case TYPE_SYSTEM_ALERT:
case TYPE_SYSTEM_ERROR:
case TYPE_SYSTEM_OVERLAY:
permission = android.Manifest.permission.SYSTEM_ALERT_WINDOW;
break;
default:
permission = android.Manifest.permission.INTERNAL_SYSTEM_WINDOW;
}
if (permission != null) {
if (mContext.checkCallingOrSelfPermission(permission)
!= PackageManager.PERMISSION_GRANTED) {
return WindowManagerImpl.ADD_PERMISSION_DENIED;
}
}
return WindowManagerImpl.ADD_OKAY;
}
public void adjustWindowParamsLw(WindowManager.LayoutParams attrs) {
switch (attrs.type) {
case TYPE_SYSTEM_OVERLAY:
case TYPE_TOAST:
// These types of windows can't receive input events.
attrs.flags |= WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
| WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
break;
}
}
/** {@inheritDoc} */
public void adjustConfigurationLw(Configuration config) {
mPowerManager.setKeyboardVisibility(true);
config.keyboardHidden = Configuration.KEYBOARDHIDDEN_NO;
mPowerManager.userActivity(SystemClock.uptimeMillis(), false,
LocalPowerManager.OTHER_EVENT);
}
public boolean isCheekPressedAgainstScreen(MotionEvent ev) {
return false;
}
/** {@inheritDoc} */
public int windowTypeToLayerLw(int type) {
if (type >= FIRST_APPLICATION_WINDOW && type <= LAST_APPLICATION_WINDOW) {
return APPLICATION_LAYER;
}
switch (type) {
case TYPE_APPLICATION_PANEL:
return APPLICATION_LAYER;
case TYPE_APPLICATION_SUB_PANEL:
return APPLICATION_LAYER;
case TYPE_STATUS_BAR:
return STATUS_BAR_LAYER;
case TYPE_STATUS_BAR_PANEL:
return STATUS_BAR_PANEL_LAYER;
case TYPE_SEARCH_BAR:
return SEARCH_BAR_LAYER;
case TYPE_PHONE:
return PHONE_LAYER;
case TYPE_KEYGUARD:
return KEYGUARD_LAYER;
case TYPE_KEYGUARD_DIALOG:
return KEYGUARD_DIALOG_LAYER;
case TYPE_SYSTEM_ALERT:
return SYSTEM_ALERT_LAYER;
case TYPE_SYSTEM_ERROR:
return SYSTEM_ERROR_LAYER;
case TYPE_SYSTEM_OVERLAY:
return SYSTEM_OVERLAY_LAYER;
case TYPE_PRIORITY_PHONE:
return PRIORITY_PHONE_LAYER;
case TYPE_TOAST:
return TOAST_LAYER;
}
Log.e(TAG, "Unknown window type: " + type);
return APPLICATION_LAYER;
}
/** {@inheritDoc} */
public int subWindowTypeToLayerLw(int type) {
switch (type) {
case TYPE_APPLICATION_PANEL:
return APPLICATION_PANEL_SUBLAYER;
case TYPE_APPLICATION_MEDIA:
return APPLICATION_MEDIA_SUBLAYER;
case TYPE_APPLICATION_MEDIA_OVERLAY:
return APPLICATION_MEDIA_OVERLAY_SUBLAYER;
case TYPE_APPLICATION_SUB_PANEL:
return APPLICATION_SUB_PANEL_SUBLAYER;
}
Log.e(TAG, "Unknown sub-window type: " + type);
return 0;
}
/** {@inheritDoc} */
public View addStartingWindow(IBinder appToken, String packageName,
int theme, CharSequence nonLocalizedLabel,
int labelRes, int icon) {
if (!SHOW_STARTING_ANIMATIONS) {
return null;
}
if (packageName == null) {
return null;
}
Context context = mContext;
boolean setTheme = false;
//Log.i(TAG, "addStartingWindow " + packageName + ": nonLocalizedLabel="
// + nonLocalizedLabel + " theme=" + Integer.toHexString(theme));
if (theme != 0 || labelRes != 0) {
try {
context = context.createPackageContext(packageName, 0);
if (theme != 0) {
context.setTheme(theme);
setTheme = true;
}
} catch (PackageManager.NameNotFoundException e) {
}
}
if (!setTheme) {
context.setTheme(com.android.internal.R.style.Theme);
}
Window win = PolicyManager.makeNewWindow(context);
Resources r = context.getResources();
win.setTitle(r.getText(labelRes, nonLocalizedLabel));
win.setType(
WindowManager.LayoutParams.TYPE_APPLICATION_STARTING);
win.setFlags(
WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE|
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE|
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE);
win.setLayout(WindowManager.LayoutParams.FILL_PARENT,
WindowManager.LayoutParams.FILL_PARENT);
final WindowManager.LayoutParams params = win.getAttributes();
params.token = appToken;
params.packageName = packageName;
params.windowAnimations = win.getWindowStyle().getResourceId(
com.android.internal.R.styleable.Window_windowAnimationStyle, 0);
params.setTitle("Starting " + packageName);
try {
WindowManagerImpl wm = (WindowManagerImpl)
context.getSystemService(Context.WINDOW_SERVICE);
View view = win.getDecorView();
if (win.isFloating()) {
// Whoops, there is no way to display an animation/preview
// of such a thing! After all that work... let's skip it.
// (Note that we must do this here because it is in
// getDecorView() where the theme is evaluated... maybe
// we should peek the floating attribute from the theme
// earlier.)
return null;
}
if (localLOGV) Log.v(
TAG, "Adding starting window for " + packageName
+ " / " + appToken + ": "
+ (view.getParent() != null ? view : null));
wm.addView(view, params);
// Only return the view if it was successfully added to the
// window manager... which we can tell by it having a parent.
return view.getParent() != null ? view : null;
} catch (WindowManagerImpl.BadTokenException e) {
// ignore
Log.w(TAG, appToken + " already running, starting window not displayed");
}
return null;
}
/** {@inheritDoc} */
public void removeStartingWindow(IBinder appToken, View window) {
// RuntimeException e = new RuntimeException();
// Log.i(TAG, "remove " + appToken + " " + window, e);
if (localLOGV) Log.v(
TAG, "Removing starting window for " + appToken + ": " + window);
if (window != null) {
WindowManagerImpl wm = (WindowManagerImpl) mContext.getSystemService(Context.WINDOW_SERVICE);
wm.removeView(window);
}
}
/**
* Preflight adding a window to the system.
*
* Currently enforces that three window types are singletons:
* <ul>
* <li>STATUS_BAR_TYPE</li>
* <li>SEARCH_BAR_TYPE</li>
* <li>KEYGUARD_TYPE</li>
* </ul>
*
* @param win The window to be added
* @param attrs Information about the window to be added
*
* @return If ok, WindowManagerImpl.ADD_OKAY. If too many singletons, WindowManagerImpl.ADD_MULTIPLE_SINGLETON
*/
public int prepareAddWindowLw(WindowState win, WindowManager.LayoutParams attrs) {
switch (attrs.type) {
case TYPE_STATUS_BAR:
if (mStatusBar != null) {
return WindowManagerImpl.ADD_MULTIPLE_SINGLETON;
}
mStatusBar = win;
break;
case TYPE_SEARCH_BAR:
if (mSearchBar != null) {
return WindowManagerImpl.ADD_MULTIPLE_SINGLETON;
}
mSearchBar = win;
break;
case TYPE_KEYGUARD:
if (mKeyguard != null) {
return WindowManagerImpl.ADD_MULTIPLE_SINGLETON;
}
mKeyguard = win;
break;
}
return WindowManagerImpl.ADD_OKAY;
}
/** {@inheritDoc} */
public void removeWindowLw(WindowState win) {
if (mStatusBar == win) {
mStatusBar = null;
}
else if (mSearchBar == win) {
mSearchBar = null;
}
else if (mKeyguard == win) {
mKeyguard = null;
}
}
private static final boolean PRINT_ANIM = false;
/** {@inheritDoc} */
public int selectAnimationLw(WindowState win, int transit) {
if (PRINT_ANIM) Log.i(TAG, "selectAnimation in " + win
+ ": transit=" + transit);
if (transit == TRANSIT_PREVIEW_DONE) {
if (win.hasAppShownWindows()) {
if (PRINT_ANIM) Log.i(TAG, "**** STARTING EXIT");
return com.android.internal.R.anim.app_starting_exit;
}
}
return 0;
}
private static IAudioService getAudioInterface() {
return IAudioService.Stub.asInterface(ServiceManager.checkService(Context.AUDIO_SERVICE));
}
/** {@inheritDoc} */
public boolean interceptKeyTi(WindowState win, int code, int metaKeys, boolean down,
int repeatCount) {
if (false) {
Log.d(TAG, "interceptKeyTi code=" + code + " down=" + down + " repeatCount="
+ repeatCount);
}
// Clear a pending HOME longpress if the user releases Home
// TODO: This could probably be inside the next bit of logic, but that code
// turned out to be a bit fragile so I'm doing it here explicitly, for now.
if ((code == KeyEvent.KEYCODE_HOME) && !down) {
mHandler.removeCallbacks(mHomeLongPress);
}
// If the HOME button is currently being held, then we do special
// chording with it.
if (mHomePressed) {
// If we have released the home key, and didn't do anything else
// while it was pressed, then it is time to go home!
if (code == KeyEvent.KEYCODE_HOME) {
if (!down) {
mHomePressed = false;
launchHomeFromHotKey();
}
}
return true;
}
// First we always handle the home key here, so applications
// can never break it, although if keyguard is on, we do let
// it handle it, because that gives us the correct 5 second
// timeout.
if (code == KeyEvent.KEYCODE_HOME) {
// If a system window has focus, then it doesn't make sense
// right now to interact with applications.
WindowManager.LayoutParams attrs = win != null ? win.getAttrs() : null;
if (attrs != null) {
int type = attrs.type;
if (type >= WindowManager.LayoutParams.FIRST_SYSTEM_WINDOW
&& type <= WindowManager.LayoutParams.LAST_SYSTEM_WINDOW) {
// Only do this once, so home-key-longpress doesn't close itself
if (repeatCount == 0 && down) {
sendCloseSystemWindows();
}
return false;
}
}
if (down && repeatCount == 0) {
mHandler.postDelayed(mHomeLongPress, ViewConfiguration.getGlobalActionKeyTimeout());
mHomePressed = true;
}
return true;
} else if (code == KeyEvent.KEYCODE_MENU) {
// Hijack modified menu keys for debugging features
final int chordBug = KeyEvent.META_SHIFT_ON;
final int chordProcess = KeyEvent.META_ALT_ON;
if (down && repeatCount == 0) {
if ((metaKeys & chordBug) == chordBug) {
Intent intent = new Intent(Intent.ACTION_BUG_REPORT);
mContext.sendOrderedBroadcast(intent, null);
return true;
} else if ((metaKeys & chordProcess) == chordProcess) {
Intent service = new Intent();
service.setClassName(mContext, "com.android.server.LoadAverageService");
ContentResolver res = mContext.getContentResolver();
boolean shown = Settings.System.getInt(
res, Settings.System.SHOW_PROCESSES, 0) != 0;
if (!shown) {
mContext.startService(service);
} else {
mContext.stopService(service);
}
Settings.System.putInt(
res, Settings.System.SHOW_PROCESSES, shown ? 0 : 1);
return true;
}
}
} else if (code == KeyEvent.KEYCODE_NOTIFICATION) {
if (down) {
// this key doesn't exist on current hardware, but if a device
// didn't have a touchscreen, it would want one of these to open
// the status bar.
IStatusBar sbs = IStatusBar.Stub.asInterface(ServiceManager.getService("statusbar"));
if (sbs != null) {
try {
sbs.toggle();
} catch (RemoteException e) {
// we're screwed anyway, since it's in this process
throw new RuntimeException(e);
}
}
}
return true;
} else if (code == KeyEvent.KEYCODE_SEARCH) {
if (down) {
if (repeatCount == 0) {
mSearchKeyPressed = true;
}
} else {
mSearchKeyPressed = false;
if (mConsumeSearchKeyUp) {
// Consume the up-event
mConsumeSearchKeyUp = false;
return true;
}
}
}
// Shortcuts are invoked through Search+key, so intercept those here
if (mSearchKeyPressed) {
if (down && repeatCount == 0) {
Intent shortcutIntent = mShortcutManager.getIntent(code, metaKeys);
if (shortcutIntent != null) {
shortcutIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
mContext.startActivity(shortcutIntent);
/*
* We launched an app, so the up-event of the search key
* should be consumed
*/
mConsumeSearchKeyUp = true;
return true;
}
}
}
return false;
}
/**
* A home key -> launch home action was detected. Take the appropriate action
* given the situation with the keyguard.
*/
private void launchHomeFromHotKey() {
// no keyguard stuff to worry about, just launch home!
try {
ActivityManagerNative.getDefault().stopAppSwitches();
} catch (RemoteException e) {
}
mContext.startActivity(mHomeIntent);
sendCloseSystemWindows();
}
public void getContentInsetHintLw(WindowManager.LayoutParams attrs, Rect coveredInset) {
final int fl = attrs.flags;
if ((fl &
(FLAG_LAYOUT_IN_SCREEN | FLAG_FULLSCREEN | FLAG_LAYOUT_INSET_DECOR))
== (FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR)) {
coveredInset.set(mCurLeft, mCurTop, mW - mCurRight, mH - mCurBottom);
} else {
coveredInset.setEmpty();
}
}
/** {@inheritDoc} */
public void beginLayoutLw(int displayWidth, int displayHeight) {
mW = displayWidth;
mH = displayHeight;
mCurLeft = 0;
mCurTop = 0;
mCurRight = displayWidth;
mCurBottom = displayHeight;
// decide where the status bar goes ahead of time
if (mStatusBar != null) {
final Rect pf = mTmpParentFrame;
final Rect df = mTmpDisplayFrame;
final Rect vf = mTmpVisibleFrame;
pf.left = df.left = vf.left = 0;
pf.top = df.top = vf.top = 0;
pf.right = df.right = vf.right = displayWidth;
pf.bottom = df.bottom = vf.bottom = displayHeight;
mStatusBar.computeFrameLw(pf, df, vf, vf);
mCurTop = mStatusBar.getFrameLw().bottom;
}
}
/** {@inheritDoc} */
public void layoutWindowLw(WindowState win, WindowManager.LayoutParams attrs, WindowState attached) {
// we've already done the status bar
if (win == mStatusBar) {
return;
}
final int fl = attrs.flags;
final Rect pf = mTmpParentFrame;
final Rect df = mTmpDisplayFrame;
final Rect vf = mTmpVisibleFrame;
if ((fl &
(FLAG_LAYOUT_IN_SCREEN | FLAG_FULLSCREEN | FLAG_LAYOUT_INSET_DECOR))
== (FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR)) {
df.left = 0;
df.top = 0;
df.right = mW;
df.bottom = mH;
vf.left = mCurLeft;
vf.top = mCurTop;
vf.right = mCurRight;
vf.bottom = mCurBottom;
} else if ((fl & FLAG_LAYOUT_IN_SCREEN) == 0) {
// Make sure this window doesn't intrude into the status bar.
df.left = vf.left = mCurLeft;
df.top = vf.top = mCurTop;
df.right = vf.right = mCurRight;
df.bottom = vf.bottom = mCurBottom;
} else {
df.left = vf.left = 0;
df.top = vf.top = 0;
df.right = vf.right = mW;
df.bottom = vf.bottom = mH;
}
if (attached != null && (fl & (FLAG_LAYOUT_IN_SCREEN)) == 0) {
pf.set(attached.getFrameLw());
} else {
pf.set(df);
}
if ((fl & FLAG_LAYOUT_NO_LIMITS) != 0) {
df.left = df.top = vf.left = vf.top = -10000;
df.right = df.bottom = vf.right = vf.bottom = 10000;
}
win.computeFrameLw(pf, df, vf, vf);
}
/** {@inheritDoc} */
public boolean finishLayoutLw() {
return false;
}
/** {@inheritDoc} */
public void beginAnimationLw(int displayWidth, int displayHeight) {
mTopFullscreenOpaqueWindowState = null;
mForceStatusBar = false;
}
/** {@inheritDoc} */
public void animatingWindowLw(WindowState win,
WindowManager.LayoutParams attrs) {
if (mTopFullscreenOpaqueWindowState == null
&& attrs.type >= FIRST_APPLICATION_WINDOW
&& attrs.type <= LAST_APPLICATION_WINDOW
&& win.fillsScreenLw(mW, mH, true, false)
&& win.isDisplayedLw()) {
mTopFullscreenOpaqueWindowState = win;
} else if ((attrs.flags & FLAG_FORCE_NOT_FULLSCREEN) != 0) {
mForceStatusBar = true;
}
}
/** {@inheritDoc} */
public boolean finishAnimationLw() {
if (mStatusBar != null) {
if (mForceStatusBar) {
mStatusBar.showLw(true);
} else if (mTopFullscreenOpaqueWindowState != null) {
WindowManager.LayoutParams lp =
mTopFullscreenOpaqueWindowState.getAttrs();
boolean hideStatusBar =
(lp.flags & WindowManager.LayoutParams.FLAG_FULLSCREEN) != 0;
if (hideStatusBar) {
mStatusBar.hideLw(true);
} else {
mStatusBar.showLw(true);
}
}
}
return false;
}
/** {@inheritDoc} */
public boolean preprocessInputEventTq(RawInputEvent event) {
return false;
}
/** {@inheritDoc} */
public boolean isAppSwitchKeyTqTiLwLi(int keycode) {
return keycode == KeyEvent.KEYCODE_HOME
|| keycode == KeyEvent.KEYCODE_ENDCALL;
}
/** {@inheritDoc} */
public boolean isMovementKeyTi(int keycode) {
switch (keycode) {
case KeyEvent.KEYCODE_DPAD_UP:
case KeyEvent.KEYCODE_DPAD_DOWN:
case KeyEvent.KEYCODE_DPAD_LEFT:
case KeyEvent.KEYCODE_DPAD_RIGHT:
return true;
}
return false;
}
/**
* @return Whether music is being played right now.
*/
private boolean isMusicActive() {
final IAudioService audio = getAudioInterface();
if (audio == null) {
Log.w(TAG, "isMusicActive: couldn't get IAudioService reference");
return false;
}
try {
return audio.isMusicActive();
} catch (RemoteException e) {
Log.w(TAG, "IAudioService.isMusicActive() threw RemoteException " + e);
return false;
}
}
/**
* Tell the audio service to adjust the volume appropriate to the event.
* @param keycode
*/
private void sendVolToMusic(int keycode) {
final IAudioService audio = getAudioInterface();
if (audio == null) {
Log.w(TAG, "sendVolToMusic: couldn't get IAudioService reference");
return;
}
try {
// since audio is playing, we shouldn't have to hold a wake lock
// during the call, but we do it as a precaution for the rare possibility
// that the music stops right before we call this
mBroadcastWakeLock.acquire();
audio.adjustStreamVolume(
AudioManager.STREAM_MUSIC,
keycode == KeyEvent.KEYCODE_VOLUME_UP
? AudioManager.ADJUST_RAISE
: AudioManager.ADJUST_LOWER,
0);
} catch (RemoteException e) {
Log.w(TAG, "IAudioService.adjustStreamVolume() threw RemoteException " + e);
} finally {
mBroadcastWakeLock.release();
}
}
/** {@inheritDoc} */
public int interceptKeyTq(RawInputEvent event, boolean screenIsOn) {
int result = ACTION_PASS_TO_USER | ACTION_POKE_USER_ACTIVITY;
return result;
}
class PassHeadsetKey implements Runnable {
KeyEvent mKeyEvent;
PassHeadsetKey(KeyEvent keyEvent) {
mKeyEvent = keyEvent;
}
public void run() {
Intent intent = new Intent(Intent.ACTION_MEDIA_BUTTON, null);
intent.putExtra(Intent.EXTRA_KEY_EVENT, mKeyEvent);
mContext.sendOrderedBroadcast(intent, null, mBroadcastDone,
mHandler, Activity.RESULT_OK, null, null);
}
}
private BroadcastReceiver mBroadcastDone = new BroadcastReceiver() {
public void onReceive(Context context, Intent intent) {
mBroadcastWakeLock.release();
}
};
/** {@inheritDoc} */
public boolean isWakeRelMovementTq(int device, int classes,
RawInputEvent event) {
// if it's tagged with one of the wake bits, it wakes up the device
return ((event.flags & (FLAG_WAKE | FLAG_WAKE_DROPPED)) != 0);
}
/** {@inheritDoc} */
public boolean isWakeAbsMovementTq(int device, int classes,
RawInputEvent event) {
// if it's tagged with one of the wake bits, it wakes up the device
return ((event.flags & (FLAG_WAKE | FLAG_WAKE_DROPPED)) != 0);
}
/**
* Given the current state of the world, should this key wake up the device?
*/
protected boolean isWakeKeyTq(RawInputEvent event) {
// There are not key maps for trackball devices, but we'd still
// like to have pressing it wake the device up, so force it here.
int keycode = event.keycode;
int flags = event.flags;
if (keycode == RawInputEvent.BTN_MOUSE) {
flags |= WindowManagerPolicy.FLAG_WAKE;
}
return (flags
& (WindowManagerPolicy.FLAG_WAKE | WindowManagerPolicy.FLAG_WAKE_DROPPED)) != 0;
}
/** {@inheritDoc} */
public void screenTurnedOff(int why) {
EventLog.writeEvent(70000, 0);
mScreenOn = false;
}
/** {@inheritDoc} */
public void screenTurnedOn() {
EventLog.writeEvent(70000, 1);
mScreenOn = true;
}
/** {@inheritDoc} */
public void enableKeyguard(boolean enabled) {
}
/** {@inheritDoc} */
public void exitKeyguardSecurely(OnKeyguardExitResult callback) {
}
/** {@inheritDoc} */
public boolean keyguardIsShowingTq() {
return false;
}
/** {@inheritDoc} */
public boolean inKeyguardRestrictedKeyInputMode() {
return false;
}
/**
* Callback from {@link KeyguardViewMediator}
*/
public void onKeyguardShow() {
sendCloseSystemWindows();
}
private void sendCloseSystemWindows() {
sendCloseSystemWindows(null);
}
private void sendCloseSystemWindows(String reason) {
try {
ActivityManagerNative.getDefault().closeSystemDialogs(reason);
} catch (RemoteException e) {
}
}
public int rotationForOrientationLw(int orientation, int lastRotation,
boolean displayEnabled) {
// get rid of rotation for now. Always rotation of 0
return Surface.ROTATION_0;
}
public boolean detectSafeMode() {
try {
int menuState = mWindowManager.getKeycodeState(KeyEvent.KEYCODE_MENU);
mSafeMode = menuState > 0;
Log.i(TAG, "Menu key state: " + menuState + " safeMode=" + mSafeMode);
return mSafeMode;
} catch (RemoteException e) {
// Doom! (it's also local)
throw new RuntimeException("window manager dead");
}
}
/** {@inheritDoc} */
public void systemReady() {
try {
if (mSafeMode) {
// If the user is holding the menu key code, then we are
// going to boot into safe mode.
ActivityManagerNative.getDefault().enterSafeMode();
} else {
// tell the keyguard
android.os.SystemProperties.set("dev.bootcomplete", "1");
}
} catch (RemoteException e) {
}
}
/** {@inheritDoc} */
public void enableScreenAfterBoot() {
updateRotation();
}
private void updateRotation() {
mPowerManager.setKeyboardVisibility(true);
int rotation = Surface.ROTATION_0;
try {
//set orientation on WindowManager
mWindowManager.setRotation(rotation, true, 0);
} catch (RemoteException e) {
}
mPowerManager.userActivity(SystemClock.uptimeMillis(), false,
LocalPowerManager.OTHER_EVENT);
}
/**
* goes to the home screen
* @return whether it did anything
*/
boolean goHome() {
if (false) {
// This code always brings home to the front.
try {
ActivityManagerNative.getDefault().stopAppSwitches();
} catch (RemoteException e) {
}
mContext.startActivity(mHomeIntent);
} else {
// This code brings home to the front or, if it is already
// at the front, puts the device to sleep.
try {
ActivityManagerNative.getDefault().stopAppSwitches();
int result = ActivityManagerNative.getDefault()
.startActivity(null, mHomeIntent,
mHomeIntent.resolveTypeIfNeeded(mContext.getContentResolver()),
null, 0, null, null, 0, true /* onlyIfNeeded*/, false);
if (result == IActivityManager.START_RETURN_INTENT_TO_CALLER) {
return false;
}
} catch (RemoteException ex) {
// bummer, the activity manager, which is in this process, is dead
}
}
sendCloseSystemWindows();
return true;
}
public void setCurrentOrientationLw(int newOrientation) {
if(newOrientation != mCurrentAppOrientation) {
mCurrentAppOrientation = newOrientation;
}
}
public boolean performHapticFeedbackLw(WindowState win, int effectId, boolean always) {
return false;
}
public void screenOnStoppedLw() {
}
}