@Override public boolean onKey(View v, int keyCode, KeyEvent event) { if (event != null && v.getId() == R.id.edittext_note_editor) { // if shift key is down, then we want to insert the '\n' char in the // TextView; // otherwise, the default action is to send the message. if (event.getAction() == KeyEvent.ACTION_DOWN) { Log.d(TAG, "keyCode is " + keyCode); if (keyCode == KeyEvent.KEYCODE_ENTER) { if (!event.isShiftPressed()) { performSubmitClick(); return true; } } if (keyCode == KeyEvent.KEYCODE_DPAD_RIGHT) { if (event.isShiftPressed() && event.isCtrlPressed()) { mCallbacks.onKeyNextTalk(); return true; } } if (keyCode == KeyEvent.KEYCODE_DPAD_LEFT) { if (event.isShiftPressed() && event.isCtrlPressed()) { mCallbacks.onKeyPreviousTalk(); return true; } } } } return false; }
/** * Convert a KeyEvent into a long which can be kept in settings and compared to key presses when * the service is in use. * * @param keyEvent The key event to convert. The (non-extended) keycode must not be a modifier. * @return An extended key code that includes modifier information */ public static long keyEventToExtendedKeyCode(KeyEvent keyEvent) { long returnValue = keyEvent.getKeyCode(); returnValue |= (keyEvent.isShiftPressed()) ? (((long) KeyEvent.META_SHIFT_ON) << 32) : 0; returnValue |= (keyEvent.isCtrlPressed()) ? (((long) KeyEvent.META_CTRL_ON) << 32) : 0; returnValue |= (keyEvent.isAltPressed()) ? (((long) KeyEvent.META_ALT_ON) << 32) : 0; return returnValue; }
@Override public boolean onKeyUp(int keyCode, KeyEvent event) { if (keyCode == KeyEvent.KEYCODE_BACK) { // We don't want to swallow the back button press return false; } // NOTE: Most keyboards, and specifically the Android default keyboard when // entering non-ascii characters, will not trigger KeyEvent events as documented // here: http://developer.android.com/reference/android/view/KeyEvent.html // Log.e("KeyDown", "------------"); // Log.e("KeyDown", "keyChar:" + (int) event.getDisplayLabel()); // Log.e("KeyDown", "utfChar:" + (char)event.getUnicodeChar()); // Log.e("KeyDown", "intUtfChar:" + event.getUnicodeChar()); final NetworkPackage np = new NetworkPackage(MousePadPlugin.PACKAGE_TYPE_MOUSEPAD_REQUEST); boolean modifier = false; if (event.isAltPressed()) { np.set("alt", true); modifier = true; } if (Build.VERSION.SDK_INT >= 11) { if (event.isCtrlPressed()) { np.set("ctrl", true); modifier = true; } } if (event.isShiftPressed()) { np.set("shift", true); } int specialKey = SpecialKeysMap.get(keyCode, -1); if (specialKey != -1) { np.set("specialKey", specialKey); } else if (event.getDisplayLabel() != 0 && modifier) { // Alt will change the utf symbol to non-ascii characters, we want the plain original letter // Since getDisplayLabel will always have a value, we have to check for special keys before char keyCharacter = event.getDisplayLabel(); np.set("key", new String(new char[] {keyCharacter}).toLowerCase()); } else { // A normal key, but still not handled by the KeyInputConnection (happens with numbers) np.set("key", new String(new char[] {(char) event.getUnicodeChar()})); } sendKeyPressPackage(np); return true; }
/** * Converts the {@link KeyEvent} into low-level events and sends them to the host as either * key-events or text-events. This contains some logic for handling some special keys, and avoids * sending a key-up event for a key that was previously injected as a text-event. */ public boolean sendKeyEvent(KeyEvent event) { int keyCode = event.getKeyCode(); boolean pressed = event.getAction() == KeyEvent.ACTION_DOWN; // Events received from software keyboards generate TextEvent in two // cases: // 1. This is an ACTION_MULTIPLE event. // 2. Ctrl, Alt and Meta are not pressed. // This ensures that on-screen keyboard always injects input that // correspond to what user sees on the screen, while physical keyboard // acts as if it is connected to the remote host. if (event.getAction() == KeyEvent.ACTION_MULTIPLE) { mInjector.sendTextEvent(event.getCharacters()); return true; } // For Enter getUnicodeChar() returns 10 (line feed), but we still // want to send it as KeyEvent. int unicode = keyCode != KeyEvent.KEYCODE_ENTER ? event.getUnicodeChar() : 0; boolean no_modifiers = !event.isAltPressed() && !event.isCtrlPressed() && !event.isMetaPressed(); if (pressed && unicode != 0 && no_modifiers) { mPressedTextKeys.add(keyCode); int[] codePoints = {unicode}; mInjector.sendTextEvent(new String(codePoints, 0, 1)); return true; } if (!pressed && mPressedTextKeys.contains(keyCode)) { mPressedTextKeys.remove(keyCode); return true; } switch (keyCode) { // KEYCODE_AT, KEYCODE_POUND, KEYCODE_STAR and KEYCODE_PLUS are // deprecated, but they still need to be here for older devices and // third-party keyboards that may still generate these events. See // https://source.android.com/devices/input/keyboard-devices.html#legacy-unsupported-keys case KeyEvent.KEYCODE_AT: mInjector.sendKeyEvent(0, KeyEvent.KEYCODE_SHIFT_LEFT, pressed); mInjector.sendKeyEvent(0, KeyEvent.KEYCODE_2, pressed); return true; case KeyEvent.KEYCODE_POUND: mInjector.sendKeyEvent(0, KeyEvent.KEYCODE_SHIFT_LEFT, pressed); mInjector.sendKeyEvent(0, KeyEvent.KEYCODE_3, pressed); return true; case KeyEvent.KEYCODE_STAR: mInjector.sendKeyEvent(0, KeyEvent.KEYCODE_SHIFT_LEFT, pressed); mInjector.sendKeyEvent(0, KeyEvent.KEYCODE_8, pressed); return true; case KeyEvent.KEYCODE_PLUS: mInjector.sendKeyEvent(0, KeyEvent.KEYCODE_SHIFT_LEFT, pressed); mInjector.sendKeyEvent(0, KeyEvent.KEYCODE_EQUALS, pressed); return true; default: // We try to send all other key codes to the host directly. return mInjector.sendKeyEvent(0, keyCode, pressed); } }
private static int getMetaState(KeyEvent event) { return (event.isCtrlPressed() ? CTRL : 0) | (event.isAltPressed() ? ALT : 0) | (event.isShiftPressed() ? SHIFT : 0); }
/** * This should be called from the Activity's onKeyDown() to handle keyboard shortcuts. * * <p>Note: onKeyDown() is called after the active view or web page has had a chance to handle the * key event. So the keys handled here *can* be overridden by any view or web page. * * @param event The KeyEvent to handle. * @param activity The ChromeActivity in which the key was pressed. * @param isCurrentTabVisible Whether page-related actions are valid, e.g. reload, zoom in. This * should be false when in the tab switcher. * @param tabSwitchingEnabled Whether shortcuts that switch between tabs are enabled (e.g. * Ctrl+Tab, Ctrl+3). * @return Whether the key event was handled. */ public static boolean onKeyDown( KeyEvent event, ChromeActivity activity, boolean isCurrentTabVisible, boolean tabSwitchingEnabled) { int keyCode = event.getKeyCode(); if (event.getRepeatCount() != 0 || KeyEvent.isModifierKey(keyCode)) return false; if (KeyEvent.isGamepadButton(keyCode)) { if (isGamepadAPIActive(activity)) return false; } else if (!event.isCtrlPressed() && !event.isAltPressed() && keyCode != KeyEvent.KEYCODE_F3 && keyCode != KeyEvent.KEYCODE_F5 && keyCode != KeyEvent.KEYCODE_F10 && keyCode != KeyEvent.KEYCODE_FORWARD) { return false; } TabModel curModel = activity.getCurrentTabModel(); int count = curModel.getCount(); int metaState = getMetaState(event); int keyCodeAndMeta = keyCode | metaState; switch (keyCodeAndMeta) { case CTRL | SHIFT | KeyEvent.KEYCODE_T: activity.onMenuOrKeyboardAction(R.id.open_recently_closed_tab, false); return true; case CTRL | KeyEvent.KEYCODE_T: activity.onMenuOrKeyboardAction( curModel.isIncognito() ? R.id.new_incognito_tab_menu_id : R.id.new_tab_menu_id, false); return true; case CTRL | KeyEvent.KEYCODE_N: activity.onMenuOrKeyboardAction(R.id.new_tab_menu_id, false); return true; case CTRL | SHIFT | KeyEvent.KEYCODE_N: activity.onMenuOrKeyboardAction(R.id.new_incognito_tab_menu_id, false); return true; // Alt+E represents a special character ´ (latin code: ´) in Android. // If an EditText or ContentView has focus, Alt+E will be swallowed by // the default dispatchKeyEvent and cannot open the menu. case ALT | KeyEvent.KEYCODE_E: case ALT | KeyEvent.KEYCODE_F: case KeyEvent.KEYCODE_F10: case KeyEvent.KEYCODE_BUTTON_Y: activity.onMenuOrKeyboardAction(R.id.show_menu, false); return true; } if (isCurrentTabVisible) { if (tabSwitchingEnabled && (metaState == CTRL || metaState == ALT)) { int numCode = keyCode - KeyEvent.KEYCODE_0; if (numCode > 0 && numCode <= Math.min(count, 8)) { // Ctrl+1 to Ctrl+8: select tab by index TabModelUtils.setIndex(curModel, numCode - 1); return true; } else if (numCode == 9 && count != 0) { // Ctrl+9: select last tab TabModelUtils.setIndex(curModel, count - 1); return true; } } switch (keyCodeAndMeta) { case CTRL | KeyEvent.KEYCODE_TAB: case CTRL | KeyEvent.KEYCODE_PAGE_DOWN: case KeyEvent.KEYCODE_BUTTON_R1: if (tabSwitchingEnabled && count > 1) { TabModelUtils.setIndex(curModel, (curModel.index() + 1) % count); } return true; case CTRL | SHIFT | KeyEvent.KEYCODE_TAB: case CTRL | KeyEvent.KEYCODE_PAGE_UP: case KeyEvent.KEYCODE_BUTTON_L1: if (tabSwitchingEnabled && count > 1) { TabModelUtils.setIndex(curModel, (curModel.index() + count - 1) % count); } return true; case CTRL | KeyEvent.KEYCODE_W: case CTRL | KeyEvent.KEYCODE_F4: case KeyEvent.KEYCODE_BUTTON_B: TabModelUtils.closeCurrentTab(curModel); return true; case CTRL | KeyEvent.KEYCODE_F: case CTRL | KeyEvent.KEYCODE_G: case CTRL | SHIFT | KeyEvent.KEYCODE_G: case KeyEvent.KEYCODE_F3: case SHIFT | KeyEvent.KEYCODE_F3: activity.onMenuOrKeyboardAction(R.id.find_in_page_id, false); return true; case CTRL | KeyEvent.KEYCODE_L: case ALT | KeyEvent.KEYCODE_D: case KeyEvent.KEYCODE_BUTTON_X: activity.onMenuOrKeyboardAction(R.id.focus_url_bar, false); return true; case CTRL | SHIFT | KeyEvent.KEYCODE_B: activity.onMenuOrKeyboardAction(R.id.all_bookmarks_menu_id, false); return true; case KeyEvent.KEYCODE_BOOKMARK: case CTRL | KeyEvent.KEYCODE_D: activity.onMenuOrKeyboardAction(R.id.bookmark_this_page_id, false); return true; case CTRL | KeyEvent.KEYCODE_H: activity.onMenuOrKeyboardAction(R.id.open_history_menu_id, false); return true; case CTRL | KeyEvent.KEYCODE_P: activity.onMenuOrKeyboardAction(R.id.print_id, false); return true; case CTRL | KeyEvent.KEYCODE_PLUS: case CTRL | KeyEvent.KEYCODE_EQUALS: case CTRL | SHIFT | KeyEvent.KEYCODE_PLUS: case CTRL | SHIFT | KeyEvent.KEYCODE_EQUALS: case KeyEvent.KEYCODE_ZOOM_IN: ContentViewCore cvc = activity.getCurrentContentViewCore(); if (cvc != null) cvc.zoomIn(); return true; case CTRL | KeyEvent.KEYCODE_MINUS: case KeyEvent.KEYCODE_ZOOM_OUT: cvc = activity.getCurrentContentViewCore(); if (cvc != null) cvc.zoomOut(); return true; case CTRL | KeyEvent.KEYCODE_0: cvc = activity.getCurrentContentViewCore(); if (cvc != null) cvc.zoomReset(); return true; case SHIFT | CTRL | KeyEvent.KEYCODE_R: case CTRL | KeyEvent.KEYCODE_R: case SHIFT | KeyEvent.KEYCODE_F5: case KeyEvent.KEYCODE_F5: Tab tab = activity.getActivityTab(); if (tab != null) { if ((keyCodeAndMeta & SHIFT) == SHIFT) { tab.reloadIgnoringCache(); } else { tab.reload(); } if (activity.getToolbarManager() != null && tab.getWebContents() != null && tab.getWebContents().focusLocationBarByDefault()) { activity.getToolbarManager().revertLocationBarChanges(); } else { tab.requestFocus(); } } return true; case ALT | KeyEvent.KEYCODE_DPAD_LEFT: tab = activity.getActivityTab(); if (tab != null && tab.canGoBack()) tab.goBack(); return true; case ALT | KeyEvent.KEYCODE_DPAD_RIGHT: case KeyEvent.KEYCODE_FORWARD: case KeyEvent.KEYCODE_BUTTON_START: tab = activity.getActivityTab(); if (tab != null && tab.canGoForward()) tab.goForward(); return true; case CTRL | SHIFT | KeyEvent.KEYCODE_SLASH: // i.e. Ctrl+? activity.onMenuOrKeyboardAction(R.id.help_id, false); return true; } } return false; }
/** * Called once when a keyboard key is pressed, then again when that same key is released. This is * not guaranteed to be notified of all soft keyboard events: certian keyboards might not call it * at all, while others might skip it in certain situations (e.g. swipe input). */ @Override public boolean dispatchKeyEvent(KeyEvent event) { int keyCode = event.getKeyCode(); // Dispatch the back button to the system to handle navigation if (keyCode == KeyEvent.KEYCODE_BACK) { JniInterface.disconnectFromHost(); return super.dispatchKeyEvent(event); } boolean pressed = event.getAction() == KeyEvent.ACTION_DOWN; // Physical keyboard must work as if it is connected to the remote host // and so events coming from physical keyboard never generate text // events. Also scan codes must be used instead of key code, so that // the keyboard layout selected on the client doesn't affect the key // codes sent to the host. if (event.getDeviceId() != KeyCharacterMap.VIRTUAL_KEYBOARD) { return JniInterface.sendKeyEvent(event.getScanCode(), 0, pressed); } // Events received from software keyboards generate TextEvent in two // cases: // 1. This is an ACTION_MULTIPLE event. // 2. Ctrl, Alt and Meta are not pressed. // This ensures that on-screen keyboard always injects input that // correspond to what user sees on the screen, while physical keyboard // acts as if it is connected to the remote host. if (event.getAction() == KeyEvent.ACTION_MULTIPLE) { JniInterface.sendTextEvent(event.getCharacters()); return true; } // For Enter getUnicodeChar() returns 10 (line feed), but we still // want to send it as KeyEvent. int unicode = keyCode != KeyEvent.KEYCODE_ENTER ? event.getUnicodeChar() : 0; boolean no_modifiers = !event.isAltPressed() && !event.isCtrlPressed() && !event.isMetaPressed(); if (pressed && unicode != 0 && no_modifiers) { mPressedTextKeys.add(keyCode); int[] codePoints = {unicode}; JniInterface.sendTextEvent(new String(codePoints, 0, 1)); return true; } if (!pressed && mPressedTextKeys.contains(keyCode)) { mPressedTextKeys.remove(keyCode); return true; } switch (keyCode) { // KEYCODE_AT, KEYCODE_POUND, KEYCODE_STAR and KEYCODE_PLUS are // deprecated, but they still need to be here for older devices and // third-party keyboards that may still generate these events. See // https://source.android.com/devices/input/keyboard-devices.html#legacy-unsupported-keys case KeyEvent.KEYCODE_AT: JniInterface.sendKeyEvent(0, KeyEvent.KEYCODE_SHIFT_LEFT, pressed); JniInterface.sendKeyEvent(0, KeyEvent.KEYCODE_2, pressed); return true; case KeyEvent.KEYCODE_POUND: JniInterface.sendKeyEvent(0, KeyEvent.KEYCODE_SHIFT_LEFT, pressed); JniInterface.sendKeyEvent(0, KeyEvent.KEYCODE_3, pressed); return true; case KeyEvent.KEYCODE_STAR: JniInterface.sendKeyEvent(0, KeyEvent.KEYCODE_SHIFT_LEFT, pressed); JniInterface.sendKeyEvent(0, KeyEvent.KEYCODE_8, pressed); return true; case KeyEvent.KEYCODE_PLUS: JniInterface.sendKeyEvent(0, KeyEvent.KEYCODE_SHIFT_LEFT, pressed); JniInterface.sendKeyEvent(0, KeyEvent.KEYCODE_EQUALS, pressed); return true; default: // We try to send all other key codes to the host directly. return JniInterface.sendKeyEvent(0, keyCode, pressed); } }