Skip to content

Commit 68b909d

Browse files
author
Jeff Brown
committed
Fix system hotkey handling.
Fixed a problem where the key up for the ALT or META key was not delivered to the task switcher dialog because it was deemed to be inconsistent with the window's observed state. Consequently the dialog would not be dismissed when the key was released. Moved global hotkey handling for META+* shortcuts and ALT/META-TAB into the window manager policy's interceptKeyBeforeDispatching method. This change prevents applications from hijacking these keys. The original idea was that these shortcuts would be handled only if the application did not handle them itself. That way certain applications, such as remote desktop tools, could deliberately override some of these less important system shortcuts. Unfortunately, that does make the behavior inconsistent across applications. What's more, bugs in the onKeyDown handler of applications can cause the shortcuts to not work at all, for no good reason. Perhaps we can add an opt-in feature later to enable specific applications to repurpose these keys when it makes sense. Bug: 5720358 Change-Id: I22bf17606d12dbea6549c60d20763e6608576cf7
1 parent eb3e4b9 commit 68b909d

File tree

5 files changed

+114
-79
lines changed

5 files changed

+114
-79
lines changed

core/res/res/values/themes.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -810,7 +810,7 @@ please see themes_device_defaults.xml.
810810

811811
<!-- Special theme for the recent apps dialog, to allow customization
812812
with overlays. -->
813-
<style name="Theme.Dialog.RecentApplications">
813+
<style name="Theme.Dialog.RecentApplications" parent="Theme.DeviceDefault.Dialog">
814814
<item name="windowFrame">@null</item>
815815
<item name="windowBackground">@android:color/transparent</item>
816816
<item name="android:windowAnimationStyle">@android:style/Animation.RecentApplications</item>

policy/src/com/android/internal/policy/impl/IconUtilities.java

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@
3838
import android.text.TextPaint;
3939
import android.util.DisplayMetrics;
4040
import android.util.Log;
41+
import android.util.TypedValue;
42+
import android.view.ContextThemeWrapper;
4143
import android.content.res.Resources;
4244
import android.content.Context;
4345

@@ -74,9 +76,13 @@ public IconUtilities(Context context) {
7476
mIconTextureWidth = mIconTextureHeight = mIconWidth + (int)(blurPx*2);
7577

7678
mBlurPaint.setMaskFilter(new BlurMaskFilter(blurPx, BlurMaskFilter.Blur.NORMAL));
77-
mGlowColorPressedPaint.setColor(0xffffc300);
79+
80+
TypedValue value = new TypedValue();
81+
mGlowColorPressedPaint.setColor(context.getTheme().resolveAttribute(
82+
android.R.attr.colorPressedHighlight, value, true) ? value.data : 0xffffc300);
7883
mGlowColorPressedPaint.setMaskFilter(TableMaskFilter.CreateClipTable(0, 30));
79-
mGlowColorFocusedPaint.setColor(0xffff8e00);
84+
mGlowColorFocusedPaint.setColor(context.getTheme().resolveAttribute(
85+
android.R.attr.colorFocusedHighlight, value, true) ? value.data : 0xffff8e00);
8086
mGlowColorFocusedPaint.setMaskFilter(TableMaskFilter.CreateClipTable(0, 30));
8187

8288
ColorMatrix cm = new ColorMatrix();

policy/src/com/android/internal/policy/impl/PhoneWindowManager.java

Lines changed: 77 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -298,9 +298,15 @@ public class PhoneWindowManager implements WindowManagerPolicy {
298298
GlobalActions mGlobalActions;
299299
volatile boolean mPowerKeyHandled; // accessed from input reader and handler thread
300300
boolean mPendingPowerKeyUpCanceled;
301-
RecentApplicationsDialog mRecentAppsDialog;
302301
Handler mHandler;
303302

303+
static final int RECENT_APPS_BEHAVIOR_SHOW_OR_DISMISS = 0;
304+
static final int RECENT_APPS_BEHAVIOR_EXIT_TOUCH_MODE_AND_SHOW = 1;
305+
static final int RECENT_APPS_BEHAVIOR_DISMISS_AND_SWITCH = 2;
306+
307+
RecentApplicationsDialog mRecentAppsDialog;
308+
int mRecentAppsDialogHeldModifiers;
309+
304310
private static final int LID_ABSENT = -1;
305311
private static final int LID_CLOSED = 0;
306312
private static final int LID_OPEN = 1;
@@ -693,7 +699,7 @@ private void handleLongPressOnHome() {
693699
}
694700

695701
if (mLongPressOnHomeBehavior == LONG_PRESS_HOME_RECENT_DIALOG) {
696-
showOrHideRecentAppsDialog(0, true /*dismissIfShown*/);
702+
showOrHideRecentAppsDialog(RECENT_APPS_BEHAVIOR_SHOW_OR_DISMISS);
697703
} else if (mLongPressOnHomeBehavior == LONG_PRESS_HOME_RECENT_SYSTEM_UI) {
698704
try {
699705
mStatusBarService.toggleRecentApps();
@@ -704,23 +710,44 @@ private void handleLongPressOnHome() {
704710
}
705711

706712
/**
707-
* Create (if necessary) and launch the recent apps dialog, or hide it if it is
708-
* already shown.
713+
* Create (if necessary) and show or dismiss the recent apps dialog according
714+
* according to the requested behavior.
709715
*/
710-
void showOrHideRecentAppsDialog(final int heldModifiers, final boolean dismissIfShown) {
716+
void showOrHideRecentAppsDialog(final int behavior) {
711717
mHandler.post(new Runnable() {
712718
@Override
713719
public void run() {
714720
if (mRecentAppsDialog == null) {
715721
mRecentAppsDialog = new RecentApplicationsDialog(mContext);
716722
}
717723
if (mRecentAppsDialog.isShowing()) {
718-
if (dismissIfShown) {
719-
mRecentAppsDialog.dismiss();
724+
switch (behavior) {
725+
case RECENT_APPS_BEHAVIOR_SHOW_OR_DISMISS:
726+
mRecentAppsDialog.dismiss();
727+
break;
728+
case RECENT_APPS_BEHAVIOR_DISMISS_AND_SWITCH:
729+
mRecentAppsDialog.dismissAndSwitch();
730+
break;
731+
case RECENT_APPS_BEHAVIOR_EXIT_TOUCH_MODE_AND_SHOW:
732+
default:
733+
break;
720734
}
721735
} else {
722-
mRecentAppsDialog.setHeldModifiers(heldModifiers);
723-
mRecentAppsDialog.show();
736+
switch (behavior) {
737+
case RECENT_APPS_BEHAVIOR_SHOW_OR_DISMISS:
738+
mRecentAppsDialog.show();
739+
break;
740+
case RECENT_APPS_BEHAVIOR_EXIT_TOUCH_MODE_AND_SHOW:
741+
try {
742+
mWindowManager.setInTouchMode(false);
743+
} catch (RemoteException e) {
744+
}
745+
mRecentAppsDialog.show();
746+
break;
747+
case RECENT_APPS_BEHAVIOR_DISMISS_AND_SWITCH:
748+
default:
749+
break;
750+
}
724751
}
725752
}
726753
});
@@ -1598,7 +1625,7 @@ public long interceptKeyBeforeDispatching(WindowState win, KeyEvent event, int p
15981625
return 0;
15991626
} else if (keyCode == KeyEvent.KEYCODE_APP_SWITCH) {
16001627
if (down && repeatCount == 0) {
1601-
showOrHideRecentAppsDialog(0, true /*dismissIfShown*/);
1628+
showOrHideRecentAppsDialog(RECENT_APPS_BEHAVIOR_SHOW_OR_DISMISS);
16021629
}
16031630
return -1;
16041631
}
@@ -1634,6 +1661,26 @@ public long interceptKeyBeforeDispatching(WindowState win, KeyEvent event, int p
16341661
}
16351662
}
16361663

1664+
// Invoke shortcuts using Meta.
1665+
if (down && repeatCount == 0
1666+
&& (metaState & KeyEvent.META_META_ON) != 0) {
1667+
final KeyCharacterMap kcm = event.getKeyCharacterMap();
1668+
Intent shortcutIntent = mShortcutManager.getIntent(kcm, keyCode,
1669+
metaState & ~(KeyEvent.META_META_ON
1670+
| KeyEvent.META_META_LEFT_ON | KeyEvent.META_META_RIGHT_ON));
1671+
if (shortcutIntent != null) {
1672+
shortcutIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
1673+
try {
1674+
mContext.startActivity(shortcutIntent);
1675+
} catch (ActivityNotFoundException ex) {
1676+
Slog.w(TAG, "Dropping shortcut key combination because "
1677+
+ "the activity to which it is registered was not found: "
1678+
+ "META+" + KeyEvent.keyCodeToString(keyCode), ex);
1679+
}
1680+
return -1;
1681+
}
1682+
}
1683+
16371684
// Handle application launch keys.
16381685
if (down && repeatCount == 0) {
16391686
String category = sApplicationLaunchKeyCategories.get(keyCode);
@@ -1647,9 +1694,29 @@ public long interceptKeyBeforeDispatching(WindowState win, KeyEvent event, int p
16471694
+ "the activity to which it is registered was not found: "
16481695
+ "keyCode=" + keyCode + ", category=" + category, ex);
16491696
}
1697+
return -1;
16501698
}
16511699
}
16521700

1701+
// Display task switcher for ALT-TAB or Meta-TAB.
1702+
if (down && repeatCount == 0 && keyCode == KeyEvent.KEYCODE_TAB) {
1703+
if (mRecentAppsDialogHeldModifiers == 0) {
1704+
final int shiftlessModifiers = event.getModifiers() & ~KeyEvent.META_SHIFT_MASK;
1705+
if (KeyEvent.metaStateHasModifiers(shiftlessModifiers, KeyEvent.META_ALT_ON)
1706+
|| KeyEvent.metaStateHasModifiers(
1707+
shiftlessModifiers, KeyEvent.META_META_ON)) {
1708+
mRecentAppsDialogHeldModifiers = shiftlessModifiers;
1709+
showOrHideRecentAppsDialog(RECENT_APPS_BEHAVIOR_EXIT_TOUCH_MODE_AND_SHOW);
1710+
return -1;
1711+
}
1712+
}
1713+
} else if (!down && mRecentAppsDialogHeldModifiers != 0
1714+
&& (metaState & mRecentAppsDialogHeldModifiers) == 0) {
1715+
mRecentAppsDialogHeldModifiers = 0;
1716+
showOrHideRecentAppsDialog(RECENT_APPS_BEHAVIOR_DISMISS_AND_SWITCH);
1717+
}
1718+
1719+
// Let the application handle the key.
16531720
return 0;
16541721
}
16551722

@@ -1671,39 +1738,6 @@ public KeyEvent dispatchUnhandledKey(WindowState win, KeyEvent event, int policy
16711738
final KeyCharacterMap kcm = event.getKeyCharacterMap();
16721739
final int keyCode = event.getKeyCode();
16731740
final int metaState = event.getMetaState();
1674-
final boolean initialDown = event.getAction() == KeyEvent.ACTION_DOWN
1675-
&& event.getRepeatCount() == 0;
1676-
1677-
if (initialDown) {
1678-
// Invoke shortcuts using Meta as a fallback.
1679-
if ((metaState & KeyEvent.META_META_ON) != 0) {
1680-
Intent shortcutIntent = mShortcutManager.getIntent(kcm, keyCode,
1681-
metaState & ~(KeyEvent.META_META_ON
1682-
| KeyEvent.META_META_LEFT_ON | KeyEvent.META_META_RIGHT_ON));
1683-
if (shortcutIntent != null) {
1684-
shortcutIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
1685-
try {
1686-
mContext.startActivity(shortcutIntent);
1687-
} catch (ActivityNotFoundException ex) {
1688-
Slog.w(TAG, "Dropping shortcut key combination because "
1689-
+ "the activity to which it is registered was not found: "
1690-
+ "META+" + KeyEvent.keyCodeToString(keyCode), ex);
1691-
}
1692-
return null;
1693-
}
1694-
}
1695-
1696-
// Display task switcher for ALT-TAB or Meta-TAB.
1697-
if (keyCode == KeyEvent.KEYCODE_TAB) {
1698-
final int shiftlessModifiers = event.getModifiers() & ~KeyEvent.META_SHIFT_MASK;
1699-
if (KeyEvent.metaStateHasModifiers(shiftlessModifiers, KeyEvent.META_ALT_ON)
1700-
|| KeyEvent.metaStateHasModifiers(
1701-
shiftlessModifiers, KeyEvent.META_META_ON)) {
1702-
showOrHideRecentAppsDialog(shiftlessModifiers, false /*dismissIfShown*/);
1703-
return null;
1704-
}
1705-
}
1706-
}
17071741

17081742
// Check for fallback actions specified by the key character map.
17091743
if (getFallbackAction(kcm, keyCode, metaState, mFallbackAction)) {

policy/src/com/android/internal/policy/impl/RecentApplicationsDialog.java

Lines changed: 17 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -71,8 +71,6 @@ public void run() {
7171
}
7272
};
7373

74-
private int mHeldModifiers;
75-
7674
public RecentApplicationsDialog(Context context) {
7775
super(context, com.android.internal.R.style.Theme_Dialog_RecentApplications);
7876

@@ -124,17 +122,6 @@ protected void onCreate(Bundle savedInstanceState) {
124122
}
125123
}
126124

127-
/**
128-
* Sets the modifier keys that are being held to keep the dialog open, or 0 if none.
129-
* Used to make the recent apps dialog automatically dismiss itself when the modifiers
130-
* all go up.
131-
* @param heldModifiers The held key modifiers, such as {@link KeyEvent#META_ALT_ON}.
132-
* Should exclude shift.
133-
*/
134-
public void setHeldModifiers(int heldModifiers) {
135-
mHeldModifiers = heldModifiers;
136-
}
137-
138125
@Override
139126
public boolean onKeyDown(int keyCode, KeyEvent event) {
140127
if (keyCode == KeyEvent.KEYCODE_TAB) {
@@ -174,30 +161,27 @@ public boolean onKeyDown(int keyCode, KeyEvent event) {
174161
return super.onKeyDown(keyCode, event);
175162
}
176163

177-
@Override
178-
public boolean onKeyUp(int keyCode, KeyEvent event) {
179-
if (mHeldModifiers != 0 && (event.getModifiers() & mHeldModifiers) == 0) {
180-
final int numIcons = mIcons.length;
181-
RecentTag tag = null;
182-
for (int i = 0; i < numIcons; i++) {
183-
if (mIcons[i].getVisibility() != View.VISIBLE) {
164+
/**
165+
* Dismiss the dialog and switch to the selected application.
166+
*/
167+
public void dismissAndSwitch() {
168+
final int numIcons = mIcons.length;
169+
RecentTag tag = null;
170+
for (int i = 0; i < numIcons; i++) {
171+
if (mIcons[i].getVisibility() != View.VISIBLE) {
172+
break;
173+
}
174+
if (i == 0 || mIcons[i].hasFocus()) {
175+
tag = (RecentTag) mIcons[i].getTag();
176+
if (mIcons[i].hasFocus()) {
184177
break;
185178
}
186-
if (i == 0 || mIcons[i].hasFocus()) {
187-
tag = (RecentTag) mIcons[i].getTag();
188-
if (mIcons[i].hasFocus()) {
189-
break;
190-
}
191-
}
192-
}
193-
if (tag != null) {
194-
switchTo(tag);
195179
}
196-
dismiss();
197-
return true;
198180
}
199-
200-
return super.onKeyUp(keyCode, event);
181+
if (tag != null) {
182+
switchTo(tag);
183+
}
184+
dismiss();
201185
}
202186

203187
/**

services/input/InputDispatcher.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4320,12 +4320,23 @@ bool InputDispatcher::InputState::trackKey(const KeyEntry* entry,
43204320
mKeyMementos.removeAt(index);
43214321
return true;
43224322
}
4323+
/* FIXME: We can't just drop the key up event because that prevents creating
4324+
* popup windows that are automatically shown when a key is held and then
4325+
* dismissed when the key is released. The problem is that the popup will
4326+
* not have received the original key down, so the key up will be considered
4327+
* to be inconsistent with its observed state. We could perhaps handle this
4328+
* by synthesizing a key down but that will cause other problems.
4329+
*
4330+
* So for now, allow inconsistent key up events to be dispatched.
4331+
*
43234332
#if DEBUG_OUTBOUND_EVENT_DETAILS
43244333
LOGD("Dropping inconsistent key up event: deviceId=%d, source=%08x, "
43254334
"keyCode=%d, scanCode=%d",
43264335
entry->deviceId, entry->source, entry->keyCode, entry->scanCode);
43274336
#endif
43284337
return false;
4338+
*/
4339+
return true;
43294340
}
43304341

43314342
case AKEY_EVENT_ACTION_DOWN: {

0 commit comments

Comments
 (0)