Support invoking key shortcuts using Control.
This enables Select All, Cut, Copy and Paste behavior in TextViews
and provides a general pattern for implementing additional keyboard
accelerators based on Control key shortcuts. The same shortcuts
also apply to menu accelerators.
Bug: 3286262
Change-Id: I7d458ee26abf51e0de1735ce490ce3baf504b471
diff --git a/core/java/android/view/KeyEvent.java b/core/java/android/view/KeyEvent.java
index 97d7ad5..3a3d1d8 100755
--- a/core/java/android/view/KeyEvent.java
+++ b/core/java/android/view/KeyEvent.java
@@ -990,6 +990,22 @@
*/
public static final int META_SCROLL_LOCK_ON = 0x400000;
+ /** {@hide} */
+ public static final int META_SHIFT_MASK = META_SHIFT_ON
+ | META_SHIFT_LEFT_ON | META_SHIFT_RIGHT_ON;
+
+ /** {@hide} */
+ public static final int META_ALT_MASK = META_ALT_ON
+ | META_ALT_LEFT_ON | META_ALT_RIGHT_ON;
+
+ /** {@hide} */
+ public static final int META_CTRL_MASK = META_CTRL_ON
+ | META_CTRL_LEFT_ON | META_CTRL_RIGHT_ON;
+
+ /** {@hide} */
+ public static final int META_META_MASK = META_ALT_ON
+ | META_META_LEFT_ON | META_META_RIGHT_ON;
+
/**
* This mask is set if the device woke because of this key event.
*/
diff --git a/core/java/android/view/ViewRoot.java b/core/java/android/view/ViewRoot.java
index 281dd27..5d81702 100644
--- a/core/java/android/view/ViewRoot.java
+++ b/core/java/android/view/ViewRoot.java
@@ -2626,6 +2626,16 @@
return;
}
+ // If the Control modifier is held, try to interpret the key as a shortcut.
+ if (event.getAction() == KeyEvent.ACTION_UP
+ && event.isCtrlPressed()
+ && !KeyEvent.isModifierKey(event.getKeyCode())) {
+ if (mView.dispatchKeyShortcutEvent(event)) {
+ finishKeyEvent(event, sendDone, true);
+ return;
+ }
+ }
+
// Apply the fallback event policy.
if (mFallbackEventHandler.dispatchKeyEvent(event)) {
finishKeyEvent(event, sendDone, true);
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 0699ac2..aa2e68f 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -7530,36 +7530,31 @@
@Override
public boolean onKeyShortcut(int keyCode, KeyEvent event) {
- switch (keyCode) {
- case KeyEvent.KEYCODE_A:
- if (canSelectText()) {
- return onTextContextMenuItem(ID_SELECT_ALL);
+ final int filteredMetaState = event.getMetaState() & ~KeyEvent.META_CTRL_MASK;
+ if (KeyEvent.metaStateHasNoModifiers(filteredMetaState)) {
+ switch (keyCode) {
+ case KeyEvent.KEYCODE_A:
+ if (canSelectText()) {
+ return onTextContextMenuItem(ID_SELECT_ALL);
+ }
+ break;
+ case KeyEvent.KEYCODE_X:
+ if (canCut()) {
+ return onTextContextMenuItem(ID_CUT);
+ }
+ break;
+ case KeyEvent.KEYCODE_C:
+ if (canCopy()) {
+ return onTextContextMenuItem(ID_COPY);
+ }
+ break;
+ case KeyEvent.KEYCODE_V:
+ if (canPaste()) {
+ return onTextContextMenuItem(ID_PASTE);
+ }
+ break;
}
-
- break;
-
- case KeyEvent.KEYCODE_X:
- if (canCut()) {
- return onTextContextMenuItem(ID_CUT);
- }
-
- break;
-
- case KeyEvent.KEYCODE_C:
- if (canCopy()) {
- return onTextContextMenuItem(ID_COPY);
- }
-
- break;
-
- case KeyEvent.KEYCODE_V:
- if (canPaste()) {
- return onTextContextMenuItem(ID_PASTE);
- }
-
- break;
}
-
return super.onKeyShortcut(keyCode, event);
}
@@ -7889,7 +7884,9 @@
/**
* Called when a context menu option for the text view is selected. Currently
- * this will be {@link android.R.id#copyUrl} or {@link android.R.id#selectTextMode}.
+ * this will be {@link android.R.id#copyUrl}, {@link android.R.id#selectTextMode},
+ * {@link android.R.id#selectAll}, {@link android.R.id#paste}, {@link android.R.id#cut}
+ * or {@link android.R.id#copy}.
*/
public boolean onTextContextMenuItem(int id) {
int min = 0;
@@ -7934,8 +7931,32 @@
startSelectionActionMode();
}
return true;
- }
+ case ID_SELECT_ALL:
+ selectAll();
+ // Update controller positions after selection change.
+ if (hasSelectionController()) {
+ getSelectionController().show();
+ }
+ return true;
+
+ case ID_PASTE:
+ paste(min, max);
+ return true;
+
+ case ID_CUT:
+ setPrimaryClip(ClipData.newPlainText(null, null,
+ mTransformed.subSequence(min, max)));
+ ((Editable) mText).delete(min, max);
+ stopSelectionActionMode();
+ return true;
+
+ case ID_COPY:
+ setPrimaryClip(ClipData.newPlainText(null, null,
+ mTransformed.subSequence(min, max)));
+ stopSelectionActionMode();
+ return true;
+ }
return false;
}
@@ -8292,49 +8313,7 @@
mCustomSelectionActionModeCallback.onActionItemClicked(mode, item)) {
return true;
}
-
- final int itemId = item.getItemId();
-
- if (itemId == ID_SELECT_ALL) {
- selectAll();
- // Update controller positions after selection change.
- if (hasSelectionController()) {
- getSelectionController().show();
- }
- return true;
- }
-
- int min = 0;
- int max = mText.length();
-
- if (isFocused()) {
- final int selStart = getSelectionStart();
- final int selEnd = getSelectionEnd();
-
- min = Math.max(0, Math.min(selStart, selEnd));
- max = Math.max(0, Math.max(selStart, selEnd));
- }
-
- switch (item.getItemId()) {
- case ID_PASTE:
- paste(min, max);
- return true;
-
- case ID_CUT:
- setPrimaryClip(ClipData.newPlainText(null, null,
- mTransformed.subSequence(min, max)));
- ((Editable) mText).delete(min, max);
- stopSelectionActionMode();
- return true;
-
- case ID_COPY:
- setPrimaryClip(ClipData.newPlainText(null, null,
- mTransformed.subSequence(min, max)));
- stopSelectionActionMode();
- return true;
- }
-
- return false;
+ return onTextContextMenuItem(item.getItemId());
}
@Override