import android.hardware.hdmi.HdmiPlaybackClient;
import android.hardware.hdmi.HdmiPlaybackClient.OneTouchPlayCallback;
import android.hardware.input.InputManagerInternal;
+import android.hardware.input.InputManager;
import android.media.AudioAttributes;
import android.media.AudioManager;
import android.media.AudioSystem;
static public final String SYSTEM_DIALOG_REASON_HOME_KEY = "homekey";
static public final String SYSTEM_DIALOG_REASON_ASSIST = "assist";
+ // Available custom actions to perform on a key press.
+ // Must match values for KEY_HOME_LONG_PRESS_ACTION in:
+ // core/java/android/provider/Settings.java
+ private static final int KEY_ACTION_NOTHING = 0;
+ private static final int KEY_ACTION_MENU = 1;
+ private static final int KEY_ACTION_APP_SWITCH = 2;
+ private static final int KEY_ACTION_SEARCH = 3;
+ private static final int KEY_ACTION_VOICE_SEARCH = 4;
+ private static final int KEY_ACTION_IN_APP_SEARCH = 5;
+ private static final int KEY_ACTION_LAUNCH_CAMERA = 6;
+ private static final int KEY_ACTION_SLEEP = 7;
+
+ // Masks for checking presence of hardware keys.
+ // Must match values in core/res/res/values/config.xml
+ private static final int KEY_MASK_HOME = 0x01;
+ private static final int KEY_MASK_BACK = 0x02;
+ private static final int KEY_MASK_MENU = 0x04;
+ private static final int KEY_MASK_ASSIST = 0x08;
+ private static final int KEY_MASK_APP_SWITCH = 0x10;
+ private static final int KEY_MASK_CAMERA = 0x20;
+
+
/**
* These are the system UI flags that, when changing, can cause the layout
* of the screen to change.
boolean mTranslucentDecorEnabled = true;
boolean mUseTvRouting;
+ int mDeviceHardwareKeys;
+
int mPointerLocationMode = 0; // guarded by mLock
// The last window we were told about in focusChanged.
boolean mHomePressed;
boolean mHomeConsumed;
boolean mHomeDoubleTapPending;
+ boolean mMenuPressed;
+ boolean mAppSwitchLongPressed;
Intent mHomeIntent;
Intent mCarDockIntent;
Intent mDeskDockIntent;
int mInitialMetaState;
boolean mForceShowSystemBars;
+ // Tracks user-customisable behavior for certain key events
+ private int mLongPressOnHomeBehavior = -1;
+ private int mPressOnMenuBehavior = -1;
+ private int mLongPressOnMenuBehavior = -1;
+ private int mPressOnAssistBehavior = -1;
+ private int mLongPressOnAssistBehavior = -1;
+ private int mPressOnAppSwitchBehavior = -1;
+ private int mLongPressOnAppSwitchBehavior = -1;
+
// support for activating the lock screen while the screen is on
boolean mAllowLockscreenWhenOn;
int mLockScreenTimeout;
int mOverscanRight = 0;
int mOverscanBottom = 0;
- // What we do when the user long presses on home
- private int mLongPressOnHomeBehavior;
-
// What we do when the user double-taps on home
private int mDoubleTapOnHomeBehavior;
resolver.registerContentObserver(Settings.System.getUriFor(
Settings.System.POINTER_LOCATION), false, this,
UserHandle.USER_ALL);
+ resolver.registerContentObserver(Settings.System.getUriFor(
+ Settings.System.KEY_HOME_LONG_PRESS_ACTION), false, this,
+ UserHandle.USER_ALL);
+ resolver.registerContentObserver(Settings.System.getUriFor(
+ Settings.System.KEY_HOME_DOUBLE_TAP_ACTION), false, this,
+ UserHandle.USER_ALL);
+ resolver.registerContentObserver(Settings.System.getUriFor(
+ Settings.System.KEY_MENU_ACTION), false, this,
+ UserHandle.USER_ALL);
+ resolver.registerContentObserver(Settings.System.getUriFor(
+ Settings.System.KEY_MENU_LONG_PRESS_ACTION), false, this,
+ UserHandle.USER_ALL);
+ resolver.registerContentObserver(Settings.System.getUriFor(
+ Settings.System.KEY_ASSIST_ACTION), false, this,
+ UserHandle.USER_ALL);
+ resolver.registerContentObserver(Settings.System.getUriFor(
+ Settings.System.KEY_ASSIST_LONG_PRESS_ACTION), false, this,
+ UserHandle.USER_ALL);
+ resolver.registerContentObserver(Settings.System.getUriFor(
+ Settings.System.KEY_APP_SWITCH_ACTION), false, this,
+ UserHandle.USER_ALL);
+ resolver.registerContentObserver(Settings.System.getUriFor(
+ Settings.System.KEY_APP_SWITCH_LONG_PRESS_ACTION), false, this,
+ UserHandle.USER_ALL);
resolver.registerContentObserver(Settings.Secure.getUriFor(
Settings.Secure.DEFAULT_INPUT_METHOD), false, this,
UserHandle.USER_ALL);
}
}
- private void handleLongPressOnHome(int deviceId) {
- if (mLongPressOnHomeBehavior == LONG_PRESS_HOME_NOTHING) {
- return;
- }
- mHomeConsumed = true;
- performHapticFeedbackLw(null, HapticFeedbackConstants.LONG_PRESS, false);
+ private void triggerVirtualKeypress(final int keyCode) {
+ InputManager im = InputManager.getInstance();
+ long now = SystemClock.uptimeMillis();
+ final KeyEvent downEvent = new KeyEvent(now, now, KeyEvent.ACTION_DOWN,
+ keyCode, 0, 0, KeyCharacterMap.VIRTUAL_KEYBOARD, 0,
+ KeyEvent.FLAG_FROM_SYSTEM, InputDevice.SOURCE_KEYBOARD);
+ final KeyEvent upEvent = KeyEvent.changeAction(downEvent, KeyEvent.ACTION_UP);
- switch (mLongPressOnHomeBehavior) {
- case LONG_PRESS_HOME_RECENT_SYSTEM_UI:
- toggleRecentApps();
- break;
- case LONG_PRESS_HOME_ASSIST:
- launchAssistAction(null, deviceId);
- break;
- default:
- Log.w(TAG, "Undefined home long press behavior: " + mLongPressOnHomeBehavior);
- break;
- }
+ im.injectInputEvent(downEvent, InputManager.INJECT_INPUT_EVENT_MODE_ASYNC);
+ im.injectInputEvent(upEvent, InputManager.INJECT_INPUT_EVENT_MODE_ASYNC);
}
- private void handleDoubleTapOnHome() {
- if (mDoubleTapOnHomeBehavior == DOUBLE_TAP_HOME_RECENT_SYSTEM_UI) {
- mHomeConsumed = true;
- toggleRecentApps();
- }
+ private void launchCameraAction() {
+ sendCloseSystemWindows();
+ Intent intent = new Intent(Intent.ACTION_CAMERA_BUTTON, null);
+ mContext.sendOrderedBroadcastAsUser(intent, UserHandle.CURRENT_OR_SELF,
+ null, null, null, 0, null, null);
}
private void showTvPictureInPictureMenu(KeyEvent event) {
}
}
+ private void performKeyAction(int behavior, KeyEvent event) {
+ switch (behavior) {
+ case KEY_ACTION_NOTHING:
+ break;
+ case KEY_ACTION_MENU:
+ triggerVirtualKeypress(KeyEvent.KEYCODE_MENU);
+ break;
+ case KEY_ACTION_APP_SWITCH:
+ toggleRecentApps();
+ break;
+ case KEY_ACTION_SEARCH:
+ launchAssistAction(null, event.getDeviceId());
+ break;
+ case KEY_ACTION_VOICE_SEARCH:
+ launchAssistLongPressAction();
+ break;
+ case KEY_ACTION_IN_APP_SEARCH:
+ triggerVirtualKeypress(KeyEvent.KEYCODE_SEARCH);
+ break;
+ case KEY_ACTION_LAUNCH_CAMERA:
+ launchCameraAction();
+ break;
+ case KEY_ACTION_SLEEP:
+ mPowerManager.goToSleep(SystemClock.uptimeMillis());
+ break;
+ default:
+ break;
+ }
+ }
+
private final Runnable mHomeDoubleTapTimeoutRunnable = new Runnable() {
@Override
public void run() {
mUseTvRouting = AudioSystem.getPlatformType(mContext) == AudioSystem.PLATFORM_TELEVISION;
- readConfigurationDependentBehaviors();
+ mDeviceHardwareKeys = mContext.getResources().getInteger(
+ com.android.internal.R.integer.config_deviceHardwareKeys);
+
+ updateKeyAssignments();
mAccessibilityManager = (AccessibilityManager) context.getSystemService(
Context.ACCESSIBILITY_SERVICE);
mStatusBarController.getAppTransitionListener());
}
- /**
- * Read values from config.xml that may be overridden depending on
- * the configuration of the device.
- * eg. Disable long press on home goes to recents on sw600dp.
- */
- private void readConfigurationDependentBehaviors() {
- final Resources res = mContext.getResources();
+ private void updateKeyAssignments() {
+ final boolean hasMenu = (mDeviceHardwareKeys & KEY_MASK_MENU) != 0;
+ final boolean hasHome = (mDeviceHardwareKeys & KEY_MASK_HOME) != 0;
+ final boolean hasAssist = (mDeviceHardwareKeys & KEY_MASK_ASSIST) != 0;
+ final boolean hasAppSwitch = (mDeviceHardwareKeys & KEY_MASK_APP_SWITCH) != 0;
+ final ContentResolver resolver = mContext.getContentResolver();
+
+ // Initialize all assignments to sane defaults.
+ mPressOnMenuBehavior = KEY_ACTION_MENU;
+
+ mLongPressOnMenuBehavior = mContext.getResources().getInteger(
+ com.android.internal.R.integer.config_longPressOnMenuBehavior);
+
+ if (mLongPressOnMenuBehavior == KEY_ACTION_NOTHING &&
+ (hasMenu && !hasAssist)) {
+ mLongPressOnMenuBehavior = KEY_ACTION_SEARCH;
+ }
+ mPressOnAssistBehavior = KEY_ACTION_SEARCH;
+ mLongPressOnAssistBehavior = KEY_ACTION_VOICE_SEARCH;
+ mPressOnAppSwitchBehavior = KEY_ACTION_APP_SWITCH;
+ mLongPressOnAppSwitchBehavior = KEY_ACTION_NOTHING;
- mLongPressOnHomeBehavior = res.getInteger(
+ mLongPressOnHomeBehavior = mContext.getResources().getInteger(
com.android.internal.R.integer.config_longPressOnHomeBehavior);
- if (mLongPressOnHomeBehavior < LONG_PRESS_HOME_NOTHING ||
- mLongPressOnHomeBehavior > LAST_LONG_PRESS_HOME_BEHAVIOR) {
- mLongPressOnHomeBehavior = LONG_PRESS_HOME_NOTHING;
+ if (mLongPressOnHomeBehavior < KEY_ACTION_NOTHING ||
+ mLongPressOnHomeBehavior > KEY_ACTION_SLEEP) {
+ mLongPressOnHomeBehavior = KEY_ACTION_NOTHING;
}
mDoubleTapOnHomeBehavior = res.getInteger(
com.android.internal.R.integer.config_doubleTapOnHomeBehavior);
- if (mDoubleTapOnHomeBehavior < DOUBLE_TAP_HOME_NOTHING ||
- mDoubleTapOnHomeBehavior > DOUBLE_TAP_HOME_RECENT_SYSTEM_UI) {
- mDoubleTapOnHomeBehavior = LONG_PRESS_HOME_NOTHING;
+ if (mDoubleTapOnHomeBehavior < KEY_ACTION_NOTHING ||
+ mDoubleTapOnHomeBehavior > KEY_ACTION_SLEEP) {
+ mDoubleTapOnHomeBehavior = KEY_ACTION_NOTHING;
+ }
+
+ // Check for custom assignments and whether KEY_ACTION_MENU is assigned.
+ if (hasHome) {
+ mLongPressOnHomeBehavior = Settings.System.getIntForUser(resolver,
+ Settings.System.KEY_HOME_LONG_PRESS_ACTION,
+ mLongPressOnHomeBehavior, UserHandle.USER_CURRENT);
+ mDoubleTapOnHomeBehavior = Settings.System.getIntForUser(resolver,
+ Settings.System.KEY_HOME_DOUBLE_TAP_ACTION,
+ mDoubleTapOnHomeBehavior, UserHandle.USER_CURRENT);
+ }
+ if (hasMenu) {
+ mPressOnMenuBehavior = Settings.System.getIntForUser(resolver,
+ Settings.System.KEY_MENU_ACTION,
+ mPressOnMenuBehavior, UserHandle.USER_CURRENT);
+ mLongPressOnMenuBehavior = Settings.System.getIntForUser(resolver,
+ Settings.System.KEY_MENU_LONG_PRESS_ACTION,
+ mLongPressOnMenuBehavior, UserHandle.USER_CURRENT);
+ }
+ if (hasAssist) {
+ mPressOnAssistBehavior = Settings.System.getIntForUser(resolver,
+ Settings.System.KEY_ASSIST_ACTION,
+ mPressOnAssistBehavior, UserHandle.USER_CURRENT);
+ mLongPressOnAssistBehavior = Settings.System.getIntForUser(resolver,
+ Settings.System.KEY_ASSIST_LONG_PRESS_ACTION,
+ mLongPressOnAssistBehavior, UserHandle.USER_CURRENT);
+ }
+ if (hasAppSwitch) {
+ mPressOnAppSwitchBehavior = Settings.System.getIntForUser(resolver,
+ Settings.System.KEY_APP_SWITCH_ACTION,
+ mPressOnAppSwitchBehavior, UserHandle.USER_CURRENT);
+ mLongPressOnAppSwitchBehavior = Settings.System.getIntForUser(resolver,
+ Settings.System.KEY_APP_SWITCH_LONG_PRESS_ACTION,
+ mLongPressOnAppSwitchBehavior, UserHandle.USER_CURRENT);
}
mShortPressWindowBehavior = SHORT_PRESS_WINDOW_NOTHING;
updateWakeGestureListenerLp();
}
+ updateKeyAssignments();
+
// Configure rotation lock.
int userRotation = Settings.System.getIntForUser(resolver,
Settings.System.USER_ROTATION, Surface.ROTATION_0,
int navigationPresence) {
mHaveBuiltInKeyboard = (keyboardPresence & PRESENCE_INTERNAL) != 0;
- readConfigurationDependentBehaviors();
readLidState();
if (config.keyboard == Configuration.KEYBOARD_NOKEYS
final int flags = event.getFlags();
final boolean down = event.getAction() == KeyEvent.ACTION_DOWN;
final boolean canceled = event.isCanceled();
+ final boolean longPress = (flags & KeyEvent.FLAG_LONG_PRESS) != 0;
+ final boolean virtualKey = event.getDeviceId() == KeyCharacterMap.VIRTUAL_KEYBOARD;
if (DEBUG_INPUT) {
Log.d(TAG, "interceptKeyTi keyCode=" + keyCode + " down=" + down + " repeatCount="
// 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 (!down) {
- cancelPreloadRecentApps();
+ if (!down && mHomePressed) {
+ if (mDoubleTapOnHomeBehavior != KEY_ACTION_APP_SWITCH) {
+ cancelPreloadRecentApps();
+ }
mHomePressed = false;
if (mHomeConsumed) {
}
// Delay handling home if a double-tap is possible.
- if (mDoubleTapOnHomeBehavior != DOUBLE_TAP_HOME_NOTHING) {
+ if (mDoubleTapOnHomeBehavior != KEY_ACTION_NOTHING) {
mHandler.removeCallbacks(mHomeDoubleTapTimeoutRunnable); // just in case
mHomeDoubleTapPending = true;
mHandler.postDelayed(mHomeDoubleTapTimeoutRunnable,
if (mHomeDoubleTapPending) {
mHomeDoubleTapPending = false;
mHandler.removeCallbacks(mHomeDoubleTapTimeoutRunnable);
- handleDoubleTapOnHome();
- } else if (mLongPressOnHomeBehavior == LONG_PRESS_HOME_RECENT_SYSTEM_UI
- || mDoubleTapOnHomeBehavior == DOUBLE_TAP_HOME_RECENT_SYSTEM_UI) {
+ performKeyAction(mDoubleTapOnHomeBehavior, event);
+ mHomeConsumed = true;
+ } else if (mLongPressOnHomeBehavior == KEY_ACTION_APP_SWITCH
+ || mDoubleTapOnHomeBehavior == KEY_ACTION_APP_SWITCH) {
preloadRecentApps();
}
- } else if ((event.getFlags() & KeyEvent.FLAG_LONG_PRESS) != 0) {
- if (!keyguardOn) {
- handleLongPressOnHome(event.getDeviceId());
+ } else if (longPress) {
+ if (!keyguardOn && !mHomeConsumed &&
+ mLongPressOnHomeBehavior != KEY_ACTION_NOTHING) {
+ if (mLongPressOnHomeBehavior != KEY_ACTION_APP_SWITCH) {
+ cancelPreloadRecentApps();
+ }
+ mHomePressed = true;
+ performHapticFeedbackLw(null, HapticFeedbackConstants.LONG_PRESS, false);
+ performKeyAction(mLongPressOnHomeBehavior, event);
+ mHomeConsumed = true;
}
}
return -1;
} else if (keyCode == KeyEvent.KEYCODE_MENU) {
// Hijack modified menu keys for debugging features
final int chordBug = KeyEvent.META_SHIFT_ON;
+ if (virtualKey || keyguardOn) {
+ // Let the app handle the key
+ return 0;
+ }
- if (down && repeatCount == 0) {
- if (mEnableShiftMenuBugReports && (metaState & chordBug) == chordBug) {
- Intent intent = new Intent(Intent.ACTION_BUG_REPORT);
- mContext.sendOrderedBroadcastAsUser(intent, UserHandle.CURRENT,
- null, null, null, 0, null, null);
- return -1;
- } else if (SHOW_PROCESSES_ON_ALT_MENU &&
- (metaState & KeyEvent.META_ALT_ON) == KeyEvent.META_ALT_ON) {
- Intent service = new Intent();
- service.setClassName(mContext, "com.android.server.LoadAverageService");
- ContentResolver res = mContext.getContentResolver();
- boolean shown = Settings.Global.getInt(
- res, Settings.Global.SHOW_PROCESSES, 0) != 0;
- if (!shown) {
- mContext.startService(service);
- } else {
- mContext.stopService(service);
+ if (down) {
+ if (mPressOnMenuBehavior == KEY_ACTION_APP_SWITCH
+ || mLongPressOnMenuBehavior == KEY_ACTION_APP_SWITCH) {
+ preloadRecentApps();
+ }
+ if (repeatCount == 0) {
+ mMenuPressed = true;
+ if (mEnableShiftMenuBugReports && (metaState & chordBug) == chordBug) {
+ Intent intent = new Intent(Intent.ACTION_BUG_REPORT);
+ mContext.sendOrderedBroadcastAsUser(intent, UserHandle.CURRENT,
+ null, null, null, 0, null, null);
+ return -1;
+ } else if (SHOW_PROCESSES_ON_ALT_MENU &&
+ (metaState & KeyEvent.META_ALT_ON) == KeyEvent.META_ALT_ON) {
+ Intent service = new Intent();
+ service.setClassName(mContext, "com.android.server.LoadAverageService");
+ ContentResolver res = mContext.getContentResolver();
+ boolean shown = Settings.Global.getInt(
+ res, Settings.Global.SHOW_PROCESSES, 0) != 0;
+ if (!shown) {
+ mContext.startService(service);
+ } else {
+ mContext.stopService(service);
+ }
+ Settings.Global.putInt(
+ res, Settings.Global.SHOW_PROCESSES, shown ? 0 : 1);
+ return -1;
+ }
+ } else if (longPress) {
+ if (!keyguardOn && mLongPressOnMenuBehavior != KEY_ACTION_NOTHING) {
+ if (mLongPressOnMenuBehavior != KEY_ACTION_APP_SWITCH) {
+ cancelPreloadRecentApps();
+ }
+ performHapticFeedbackLw(null, HapticFeedbackConstants.LONG_PRESS, false);
+ performKeyAction(mLongPressOnMenuBehavior, event);
+ mMenuPressed = false;
+ return -1;
}
- Settings.Global.putInt(
- res, Settings.Global.SHOW_PROCESSES, shown ? 0 : 1);
- return -1;
}
}
+ if (!down && mMenuPressed) {
+ if (mPressOnMenuBehavior != KEY_ACTION_APP_SWITCH) {
+ cancelPreloadRecentApps();
+ }
+ mMenuPressed = false;
+ if (!canceled) {
+ performKeyAction(mPressOnMenuBehavior, event);
+ }
+ }
+ return -1;
} else if (keyCode == KeyEvent.KEYCODE_SEARCH) {
if (down) {
if (repeatCount == 0) {
}
return 0;
} else if (keyCode == KeyEvent.KEYCODE_APP_SWITCH) {
- if (!keyguardOn) {
- if (down && repeatCount == 0) {
+ if (down) {
+ if (mPressOnAppSwitchBehavior == KEY_ACTION_APP_SWITCH
+ || mLongPressOnAppSwitchBehavior == KEY_ACTION_APP_SWITCH) {
preloadRecentApps();
- } else if (!down) {
- toggleRecentApps();
+ }
+ if (repeatCount == 0) {
+ mAppSwitchLongPressed = false;
+ } else if (longPress) {
+ if (!keyguardOn && mLongPressOnAppSwitchBehavior != KEY_ACTION_NOTHING) {
+ if (mLongPressOnAppSwitchBehavior != KEY_ACTION_APP_SWITCH) {
+ cancelPreloadRecentApps();
+ }
+ performHapticFeedbackLw(null, HapticFeedbackConstants.LONG_PRESS, false);
+ performKeyAction(mLongPressOnAppSwitchBehavior, event);
+ mAppSwitchLongPressed = true;
+ }
+ }
+ } else {
+ if (mAppSwitchLongPressed) {
+ mAppSwitchLongPressed = false;
+ } else {
+ if (mPressOnAppSwitchBehavior != KEY_ACTION_APP_SWITCH) {
+ cancelPreloadRecentApps();
+ }
+ if (!canceled) {
+ performKeyAction(mPressOnAppSwitchBehavior, event);
+ }
}
}
return -1;
}
} else if (keyCode == KeyEvent.KEYCODE_ASSIST) {
if (down) {
+ if (mPressOnAssistBehavior == KEY_ACTION_APP_SWITCH
+ || mLongPressOnAssistBehavior == KEY_ACTION_APP_SWITCH) {
+ preloadRecentApps();
+ }
if (repeatCount == 0) {
mAssistKeyLongPressed = false;
- } else if (repeatCount == 1) {
- mAssistKeyLongPressed = true;
- if (!keyguardOn) {
- launchAssistLongPressAction();
+ } else if (longPress) {
+ if (!keyguardOn && mLongPressOnAssistBehavior != KEY_ACTION_NOTHING) {
+ if (mLongPressOnAssistBehavior != KEY_ACTION_APP_SWITCH) {
+ cancelPreloadRecentApps();
+ }
+ performHapticFeedbackLw(null, HapticFeedbackConstants.LONG_PRESS, false);
+ performKeyAction(mLongPressOnAssistBehavior, event);
+ mAssistKeyLongPressed = true;
}
}
} else {
if (mAssistKeyLongPressed) {
mAssistKeyLongPressed = false;
} else {
- if (!keyguardOn) {
- launchAssistAction(null, event.getDeviceId());
+ if (mPressOnAssistBehavior != KEY_ACTION_APP_SWITCH) {
+ cancelPreloadRecentApps();
+ }
+ if (!canceled) {
+ performKeyAction(mPressOnAssistBehavior, event);
}
}
}