private void onKeyEvent( KeyEvent event, int action, int savedMetaState, boolean isSynthesizedImeKey) { // Use a separate action argument so we can override the key's original action, // e.g. change ACTION_MULTIPLE to ACTION_DOWN. That way we don't have to allocate // a new key event just to change its action field. // // Normally we expect event.getMetaState() to reflect the current meta-state; however, // some software-generated key events may not have event.getMetaState() set, e.g. key // events from Swype. Therefore, it's necessary to combine the key's meta-states // with the meta-states that we keep separately in KeyListener final int metaState = event.getMetaState() | savedMetaState; final int unmodifiedMetaState = metaState & ~(KeyEvent.META_ALT_MASK | KeyEvent.META_CTRL_MASK | KeyEvent.META_META_MASK); final int unicodeChar = event.getUnicodeChar(metaState); final int domPrintableKeyValue = unicodeChar >= ' ' ? unicodeChar : unmodifiedMetaState != metaState ? event.getUnicodeChar(unmodifiedMetaState) : 0; onKeyEvent( action, event.getKeyCode(), event.getScanCode(), metaState, event.getEventTime(), unicodeChar, // e.g. for Ctrl+A, Android returns 0 for unicodeChar, // but Gecko expects 'a', so we return that in baseUnicodeChar. event.getUnicodeChar(0), domPrintableKeyValue, event.getRepeatCount(), event.getFlags(), isSynthesizedImeKey); }
/** * Handles a key event. * * @param event the key event to handle * @return {@code true} if the event was handled */ public boolean handleKey(KeyEvent event) { if (event.getAction() != KeyEvent.ACTION_DOWN) { return false; } int code = event.getKeyCode(); if (code == KeyEvent.KEYCODE_ENTER) { displaySingleTimeMessage(context.getString(R.string.keyboard_enter)); Action.ENTER.execute(commands); return true; } if (code == KeyEvent.KEYCODE_DEL) { displaySingleTimeMessage(context.getString(R.string.keyboard_del)); Action.BACKSPACE.execute(commands); return true; } if (code == KeyEvent.KEYCODE_SPACE) { appendDisplayedText(" "); commands.keyPress(Code.KEYCODE_SPACE); return true; } int c = event.getUnicodeChar(); return handleChar((char) c); }
/** * This translates incoming hard key events in to edit operations on an InputConnection. It is * only needed when using the PROCESS_HARD_KEYS option. */ private boolean translateKeyDown(int keyCode, KeyEvent event) { mMetaState = MetaKeyKeyListener.handleKeyDown(mMetaState, keyCode, event); int c = event.getUnicodeChar(MetaKeyKeyListener.getMetaState(mMetaState)); mMetaState = MetaKeyKeyListener.adjustMetaAfterKeypress(mMetaState); InputConnection ic = getCurrentInputConnection(); if (c == 0 || ic == null) { return false; } boolean dead = false; if ((c & KeyCharacterMap.COMBINING_ACCENT) != 0) { dead = true; c = c & KeyCharacterMap.COMBINING_ACCENT_MASK; } if (mComposing.length() > 0) { char accent = mComposing.charAt(mComposing.length() - 1); int composed = KeyEvent.getDeadChar(accent, c); if (composed != 0) { c = composed; mComposing.setLength(mComposing.length() - 1); } } onKey(c, null); return true; }
@Override public boolean onKeyUp(int keyCode, KeyEvent event) { if (!m_started) return false; m_metaState = MetaKeyKeyListener.handleKeyUp(m_metaState, keyCode, event); QtApplication.keyUp(keyCode, event.getUnicodeChar(), event.getMetaState()); return true; }
public int translateKey(int inCode, KeyEvent event) { switch (inCode) { case KeyEvent.KEYCODE_DPAD_CENTER: return 13; /* Fake ENTER */ case KeyEvent.KEYCODE_DPAD_LEFT: return 37; case KeyEvent.KEYCODE_DPAD_RIGHT: return 39; case KeyEvent.KEYCODE_DPAD_UP: return 38; case KeyEvent.KEYCODE_DPAD_DOWN: return 40; case KeyEvent.KEYCODE_BACK: return 27; /* Fake Escape */ case KeyEvent.KEYCODE_MENU: return 0x01000012; /* Fake MENU */ case KeyEvent.KEYCODE_DEL: return 8; } int result = event.getUnicodeChar(event.getMetaState()); if (result == android.view.KeyCharacterMap.COMBINING_ACCENT) { // TODO: return 0; } return result; }
boolean sendKeyEvent(KeyEvent event) { if (mNativeImeAdapterAndroid == 0) return false; int action = event.getAction(); if (action != KeyEvent.ACTION_DOWN && action != KeyEvent.ACTION_UP) { // action == KeyEvent.ACTION_MULTIPLE // TODO(bulach): confirm the actual behavior. Apparently: // If event.getKeyCode() == KEYCODE_UNKNOWN, we can send a // composition key down (229) followed by a commit text with the // string from event.getUnicodeChars(). // Otherwise, we'd need to send an event with a // WebInputEvent::IsAutoRepeat modifier. We also need to verify when // we receive ACTION_MULTIPLE: we may receive it after an ACTION_DOWN, // and if that's the case, we'll need to review when to send the Char // event. return false; } mViewEmbedder.onImeEvent(); return nativeSendKeyEvent( mNativeImeAdapterAndroid, event, event.getAction(), getModifiers(event.getMetaState()), event.getEventTime(), event.getKeyCode(), event.getScanCode(), /*isSystemKey=*/ false, event.getUnicodeChar()); }
@Override public boolean onKeyDown(int keyCode, KeyEvent event) { if ((keyCode == KeyEvent.KEYCODE_BACK) && (event.getRepeatCount() == 0)) { NativeKeydown(253); // Trigger KeyBack in ZGE if (NativeCloseQuery()) IsDestroy = true; return true; } else NativeKeydown(event.getUnicodeChar()); return super.onKeyDown(keyCode, event); }
@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; }
private void initKeyEvent(KeyEvent k) { mAction = k.getAction(); mTime = k.getEventTime(); mMetaState = k.getMetaState(); mFlags = k.getFlags(); mKeyCode = k.getKeyCode(); mUnicodeChar = k.getUnicodeChar(); mRepeatCount = k.getRepeatCount(); mCharacters = k.getCharacters(); mDomKeyLocation = isJoystickButton(mKeyCode) ? DOM_KEY_LOCATION_JOYSTICK : DOM_KEY_LOCATION_MOBILE; }
/** * @param keyCode * @param event * @return true to say we handled this, false to tell Android to handle it */ public static boolean keyDown(int keyCode, KeyEvent event) { if ((keyCode == KeyEvent.KEYCODE_BACK && event.getRepeatCount() == 0)) { if (onBackPressed()) { return true; } else { // let the Android system handle the back button return false; } } int unicodeChar = event.getUnicodeChar(); return onKeyDown(keyCode, unicodeChar); }
@Override public boolean onKeyDown(int keyCode, KeyEvent event) { switch (keyCode) { case KeyEvent.KEYCODE_VOLUME_UP: case KeyEvent.KEYCODE_VOLUME_DOWN: return super.onKeyDown(keyCode, event); default: break; } handleKeyDown(host, keyCode, event.getUnicodeChar()); return true; }
/** * @param keyCode * @param event * @return true to say we handled this, false to tell Android to handle it */ public static boolean keyUp(int keyCode, KeyEvent event) { if (KeyEvent.isModifierKey(keyCode)) { /* Android sends a shift keycode (for instance), then the key that goes with the shift. We don't need the first keycode, that info is in event.getMetaState() anyway */ return false; } else { int unicodeChar = event.getUnicodeChar(); onKeyUp(unicodeChar); // return false to let Android handle certain keys // like the back and menu keys return false; } }
@Override public boolean onKeyDown(int keyCode, KeyEvent event) { if (!m_started) return false; m_metaState = MetaKeyKeyListener.handleKeyDown(m_metaState, keyCode, event); int c = event.getUnicodeChar(MetaKeyKeyListener.getMetaState(m_metaState)); int lc = c; m_metaState = MetaKeyKeyListener.adjustMetaAfterKeypress(m_metaState); if ((c & KeyCharacterMap.COMBINING_ACCENT) != 0) { c = c & KeyCharacterMap.COMBINING_ACCENT_MASK; int composed = KeyEvent.getDeadChar(m_lastChar, c); c = composed; } m_lastChar = lc; if (keyCode != KeyEvent.KEYCODE_BACK) QtApplication.keyDown(keyCode, c, event.getMetaState()); return true; }
@Override public boolean onKey(View v, int keyCode, android.view.KeyEvent event) { if (event.getAction() == android.view.KeyEvent.ACTION_MULTIPLE) return false; synchronized (this) { Input.KeyEvent keyEvent = keyEventPool.newObject(); keyEvent.keyCode = keyCode; keyEvent.keyChar = (char) event.getUnicodeChar(); if (event.getAction() == android.view.KeyEvent.ACTION_DOWN) { keyEvent.type = Input.KeyEvent.KEY_DOWN; if (keyCode > 0 && keyCode < 127) pressedKeys[keyCode] = true; } if (event.getAction() == android.view.KeyEvent.ACTION_UP) { keyEvent.type = Input.KeyEvent.KEY_UP; if (keyCode > 0 && keyCode < 127) pressedKeys[keyCode] = false; } keyEventsBuffer.add(keyEvent); } return false; }
boolean translateAndSendNativeEvents(KeyEvent event) { if (mNativeImeAdapterAndroid == 0) return false; int action = event.getAction(); if (action != KeyEvent.ACTION_DOWN && action != KeyEvent.ACTION_UP) { // action == KeyEvent.ACTION_MULTIPLE // TODO(bulach): confirm the actual behavior. Apparently: // If event.getKeyCode() == KEYCODE_UNKNOWN, we can send a // composition key down (229) followed by a commit text with the // string from event.getUnicodeChars(). // Otherwise, we'd need to send an event with a // WebInputEvent::IsAutoRepeat modifier. We also need to verify when // we receive ACTION_MULTIPLE: we may receive it after an ACTION_DOWN, // and if that's the case, we'll need to review when to send the Char // event. return false; } // Begin add by TCL zhanghangzhi, mail: [email protected] if (event.getKeyCode() == KeyEvent.KEYCODE_DPAD_CENTER && (event.getFlags() & KeyEvent.FLAG_FROM_SYSTEM) != 0) { android.os.SystemProperties.set( "webviewchromium.tv_enterKeyFlag", String.valueOf(event.getFlags())); android.util.Log.e("ImeAdapter", "[ESTALENT] sys.tv_enterKeyFlag: " + event.getFlags()); } else { android.os.SystemProperties.set("webviewchromium.tv_enterKeyFlag", ""); android.util.Log.e("ImeAdapter", "[ESTALENT] clean sys.tv_enterKeyFlag"); } // End of TCL mViewEmbedder.onImeEvent(false); return nativeSendKeyEvent( mNativeImeAdapterAndroid, event, event.getAction(), getModifiers(event.getMetaState()), event.getEventTime(), event.getKeyCode(), event.isSystem(), event.getUnicodeChar()); }
@Override public boolean sendKeyEvent(KeyEvent event) { /* * This handles the keycodes from soft keyboard (and IME-translated * input from hardkeyboard) */ int keyCode = event.getKeyCode(); if (event.getAction() == KeyEvent.ACTION_DOWN) { if (event.isPrintingKey()) { commitText(String.valueOf((char) event.getUnicodeChar()), 1); } SDLActivity.onNativeKeyDown(keyCode); return true; } else if (event.getAction() == KeyEvent.ACTION_UP) { SDLActivity.onNativeKeyUp(keyCode); return true; } return super.sendKeyEvent(event); }
@Override public boolean onKey(View v, int keyCode, KeyEvent event) { // This handles the hardware keyboard input if (event.isPrintingKey()) { if (event.getAction() == KeyEvent.ACTION_DOWN) { ic.commitText(String.valueOf((char) event.getUnicodeChar()), 1); } return true; } if (event.getAction() == KeyEvent.ACTION_DOWN) { SDLActivity.onNativeKeyDown(keyCode); return true; } else if (event.getAction() == KeyEvent.ACTION_UP) { SDLActivity.onNativeKeyUp(keyCode); return true; } return false; }
private boolean synthesizeKeyEvents(char inputChar) { if (mKeyCharacterMap == null) { mKeyCharacterMap = KeyCharacterMap.load(KeyCharacterMap.VIRTUAL_KEYBOARD); } // Synthesize VKB key events that could plausibly generate the input character. char[] inputChars = {inputChar}; KeyEvent[] events = mKeyCharacterMap.getEvents(inputChars); if (events == null) { if (DEBUG) { Log.d(LOGTAG, "synthesizeKeyEvents: char '" + inputChar + "' has no virtual key mapping"); } return false; } boolean sentKeyEvents = false; for (KeyEvent event : events) { if (!KeyEvent.isModifierKey(event.getKeyCode())) { if (DEBUG) { Log.d( LOGTAG, "synthesizeKeyEvents: char '" + inputChar + "' -> action=" + event.getAction() + ", keyCode=" + event.getKeyCode() + ", UnicodeChar='" + (char) event.getUnicodeChar() + "'"); } GeckoAppShell.sendEventToGecko(GeckoEvent.createKeyEvent(event)); sentKeyEvents = true; } } return sentKeyEvents; }
private void logKeyEvent(int keyCode, KeyEvent event, boolean down) { String s = down ? "KeyDown event:" : "KeyUp event:"; s += " action " + event.getAction() + " keycode " + keyCode + " " + KeyEvent.keyCodeToString(keyCode); s += " unicode " + event.getUnicodeChar() + " " + event.getDisplayLabel(); s += " ScanCode " + event.getScanCode(); s += " MetaState " + event.getMetaState() + " Flags " + event.getFlags() + " modifiers " + event.getModifiers(); s += " source " + printSource(event.getSource()) + " device " + printDevice(event.getDeviceId()); pushText(s); }
/** * @param keyCode * @param event * @return true to say we handled this, false to tell Android to handle it */ public static boolean keyDown(int keyCode, KeyEvent event) { if ((keyCode == KeyEvent.KEYCODE_BACK && event.getRepeatCount() == 0)) { if (onBackPressed()) { return true; } else { // let the Android system handle the back button return false; } } if (KeyEvent.isModifierKey(keyCode)) { /* Android sends a shift keycode (for instance), then the key that goes with the shift. We don't need the first keycode, that info is in event.getMetaState() anyway */ return false; } else { int unicodeChar = event.getUnicodeChar(); onKeyDown(unicodeChar); // return false to let Android handle certain keys // like the back and menu keys return false; } }
@Override public boolean onKey(View view, int keyCode, KeyEvent event) { builder.setLength(0); switch (event.getAction()) { case KeyEvent.ACTION_DOWN: builder.append("down, "); break; case KeyEvent.ACTION_UP: builder.append("up, "); break; } builder.append(event.getKeyCode()); builder.append(", "); builder.append((char) event.getUnicodeChar()); String text = builder.toString(); Log.d("KeyTest", text); textView.setText(text); if (event.getKeyCode() == KeyEvent.KEYCODE_HOME) Log.d("KeyTest", "HOME"); if (event.getKeyCode() == KeyEvent.KEYCODE_BACK) return false; else return true; }
@Override public boolean onKeyUp(int keyCode, KeyEvent event) { char keyPressed = (char) event.getUnicodeChar(); if (!mAnswerField.isFocused()) { if (sDisplayAnswer) { if (keyPressed == '1') { answerCard(EASE_FAILED); return true; } if (keyPressed == '2') { answerCard(EASE_HARD); return true; } if (keyPressed == '3') { answerCard(EASE_MID); return true; } if (keyPressed == '4') { answerCard(EASE_EASY); return true; } if (keyCode == KeyEvent.KEYCODE_SPACE || keyCode == KeyEvent.KEYCODE_ENTER || keyCode == KeyEvent.KEYCODE_NUMPAD_ENTER) { answerCard(getDefaultEase()); return true; } } if (keyPressed == 'e') { editCard(); return true; } if (keyPressed == '*') { DeckTask.launchDeckTask( DeckTask.TASK_TYPE_MARK_CARD, mMarkCardHandler, new DeckTask.TaskData(mSched, mCurrentCard, 0)); return true; } if (keyPressed == '-') { DeckTask.launchDeckTask( DeckTask.TASK_TYPE_DISMISS_NOTE, mDismissCardHandler, new DeckTask.TaskData(mSched, mCurrentCard, 4)); return true; } if (keyPressed == '=') { DeckTask.launchDeckTask( DeckTask.TASK_TYPE_DISMISS_NOTE, mDismissCardHandler, new DeckTask.TaskData(mSched, mCurrentCard, 0)); return true; } if (keyPressed == '@') { DeckTask.launchDeckTask( DeckTask.TASK_TYPE_DISMISS_NOTE, mDismissCardHandler, new DeckTask.TaskData(mSched, mCurrentCard, 1)); return true; } if (keyPressed == '!') { DeckTask.launchDeckTask( DeckTask.TASK_TYPE_DISMISS_NOTE, mDismissCardHandler, new DeckTask.TaskData(mSched, mCurrentCard, 2)); return true; } if (keyPressed == 'r' || keyCode == KeyEvent.KEYCODE_F5) { playSounds(true); return true; } } return super.onKeyUp(keyCode, event); }
@Override public boolean onKey(View v, int keyCode, android.view.KeyEvent e) { for (int i = 0, n = keyListeners.size(); i < n; i++) if (keyListeners.get(i).onKey(v, keyCode, e)) return true; synchronized (this) { KeyEvent event = null; if (e.getKeyCode() == android.view.KeyEvent.KEYCODE_UNKNOWN && e.getAction() == android.view.KeyEvent.ACTION_MULTIPLE) { String chars = e.getCharacters(); for (int i = 0; i < chars.length(); i++) { event = usedKeyEvents.obtain(); event.keyCode = 0; event.keyChar = chars.charAt(i); event.type = KeyEvent.KEY_TYPED; keyEvents.add(event); } return false; } char character = (char) e.getUnicodeChar(); // Android doesn't report a unicode char for back space. hrm... if (keyCode == 67) character = '\b'; switch (e.getAction()) { case android.view.KeyEvent.ACTION_DOWN: event = usedKeyEvents.obtain(); event.keyChar = 0; event.keyCode = e.getKeyCode(); event.type = KeyEvent.KEY_DOWN; // Xperia hack for circle key. gah... if (keyCode == android.view.KeyEvent.KEYCODE_BACK && e.isAltPressed()) { keyCode = Keys.BUTTON_CIRCLE; event.keyCode = keyCode; } keyEvents.add(event); keys.put(event.keyCode, null); break; case android.view.KeyEvent.ACTION_UP: event = usedKeyEvents.obtain(); event.keyChar = 0; event.keyCode = e.getKeyCode(); event.type = KeyEvent.KEY_UP; // Xperia hack for circle key. gah... if (keyCode == android.view.KeyEvent.KEYCODE_BACK && e.isAltPressed()) { keyCode = Keys.BUTTON_CIRCLE; event.keyCode = keyCode; } keyEvents.add(event); event = usedKeyEvents.obtain(); event.keyChar = character; event.keyCode = 0; event.type = KeyEvent.KEY_TYPED; keyEvents.add(event); if (keyCode == Keys.BUTTON_CIRCLE) keys.remove(Keys.BUTTON_CIRCLE); else keys.remove(e.getKeyCode()); } app.getGraphics().requestRendering(); } // circle button on Xperia Play shouldn't need catchBack == true if (keyCode == Keys.BUTTON_CIRCLE) return true; if (catchBack && keyCode == android.view.KeyEvent.KEYCODE_BACK) return true; if (catchMenu && keyCode == android.view.KeyEvent.KEYCODE_MENU) return true; return false; }
public boolean processLocalKeyEvent(int keyCode, KeyEvent evt) { android.util.Log.e(TAG, evt.toString() + " " + keyCode); if (rfb != null && rfb.isInNormalProtocol()) { RemotePointer pointer = vncCanvas.getPointer(); boolean down = (evt.getAction() == KeyEvent.ACTION_DOWN) || (evt.getAction() == KeyEvent.ACTION_MULTIPLE); boolean unicode = false; int metaState = 0, numchars = 1; int keyboardMetaState = evt.getMetaState(); // Add shift to metaState if necessary. if ((keyboardMetaState & 0x000000c1) != 0) metaState |= SHIFT_MASK; // If the keyboardMetaState contains any hint of CTRL, add CTRL_MASK to metaState if ((keyboardMetaState & 0x00007000) != 0) metaState |= CTRL_MASK; // If the keyboardMetaState contains left ALT, add ALT_MASK to metaState. // Leaving KeyEvent.KEYCODE_ALT_LEFT for symbol input on hardware keyboards. if ((keyboardMetaState & KeyEvent.META_ALT_RIGHT_ON) != 0) metaState |= ALT_MASK; if ((keyboardMetaState & (RemoteKeyboard.SUPER_MASK | 0x00010000)) != 0) metaState |= SUPER_MASK; if (keyCode == KeyEvent.KEYCODE_MENU) return true; // Ignore menu key if (pointer.handleHardwareButtons( keyCode, evt, metaState | onScreenMetaState | hardwareMetaState)) return true; int key = 0, keysym = 0; if (!down) { switch (evt.getScanCode()) { case SCAN_ESC: key = 0xff1b; break; case SCAN_LEFTCTRL: case SCAN_RIGHTCTRL: hardwareMetaState &= ~CTRL_MASK; break; case SCAN_F1: keysym = 0xffbe; break; case SCAN_F2: keysym = 0xffbf; break; case SCAN_F3: keysym = 0xffc0; break; case SCAN_F4: keysym = 0xffc1; break; case SCAN_F5: keysym = 0xffc2; break; case SCAN_F6: keysym = 0xffc3; break; case SCAN_F7: keysym = 0xffc4; break; case SCAN_F8: keysym = 0xffc5; break; case SCAN_F9: keysym = 0xffc6; break; case SCAN_F10: keysym = 0xffc7; break; } switch (keyCode) { case KeyEvent.KEYCODE_DPAD_CENTER: hardwareMetaState &= ~CTRL_MASK; break; // Leaving KeyEvent.KEYCODE_ALT_LEFT for symbol input on hardware keyboards. case KeyEvent.KEYCODE_ALT_RIGHT: hardwareMetaState &= ~ALT_MASK; break; } } switch (keyCode) { // case KeyEvent.KEYCODE_BACK: keysym = 0xff1b; break; case KeyEvent.KEYCODE_DPAD_LEFT: keysym = 0xff51; break; case KeyEvent.KEYCODE_DPAD_UP: keysym = 0xff52; break; case KeyEvent.KEYCODE_DPAD_RIGHT: keysym = 0xff53; break; case KeyEvent.KEYCODE_DPAD_DOWN: keysym = 0xff54; break; case KeyEvent.KEYCODE_DEL: keysym = 0xff08; break; case KeyEvent.KEYCODE_ENTER: keysym = 0xff0d; break; case KeyEvent.KEYCODE_TAB: keysym = 0xff09; break; case 92 /* KEYCODE_PAGE_UP */: keysym = 0xff55; break; case 93 /* KEYCODE_PAGE_DOWN */: keysym = 0xff56; break; case 111 /* KEYCODE_ESCAPE */: keysym = 0xff1b; break; case 112 /* KEYCODE_FORWARD_DEL */: keysym = 0xffff; break; case 113 /* KEYCODE_CTRL_LEFT */: keysym = 0xffe3; break; case 114 /* KEYCODE_CTRL_RIGHT */: keysym = 0xffe4; break; case 115 /* KEYCODE_CAPS_LOCK */: keysym = 0xffe5; break; case 116 /* KEYCODE_SCROLL_LOCK */: keysym = 0xff14; break; case 117 /* KEYCODE_META_LEFT */: keysym = 0xffeb; break; case 118 /* KEYCODE_META_RIGHT */: keysym = 0xffec; break; case 120 /* KEYCODE_SYSRQ */: keysym = 0xff61; break; case 121 /* KEYCODE_BREAK */: keysym = 0xff6b; break; case 122 /* KEYCODE_MOVE_HOME */: keysym = 0xff50; break; case 123 /* KEYCODE_MOVE_END */: keysym = 0xff57; break; case 124 /* KEYCODE_INSERT */: keysym = 0xff63; break; case 131 /* KEYCODE_F1 */: keysym = 0xffbe; break; case 132 /* KEYCODE_F2 */: keysym = 0xffbf; break; case 133 /* KEYCODE_F3 */: keysym = 0xffc0; break; case 134 /* KEYCODE_F4 */: keysym = 0xffc1; break; case 135 /* KEYCODE_F5 */: keysym = 0xffc2; break; case 136 /* KEYCODE_F6 */: keysym = 0xffc3; break; case 137 /* KEYCODE_F7 */: keysym = 0xffc4; break; case 138 /* KEYCODE_F8 */: keysym = 0xffc5; break; case 139 /* KEYCODE_F9 */: keysym = 0xffc6; break; case 140 /* KEYCODE_F10 */: keysym = 0xffc7; break; case 141 /* KEYCODE_F11 */: keysym = 0xffc8; break; case 142 /* KEYCODE_F12 */: keysym = 0xffc9; break; case 143 /* KEYCODE_NUM_LOCK */: keysym = 0xff7f; break; case 0 /* KEYCODE_UNKNOWN */: if (evt.getCharacters() != null) { key = evt.getCharacters().charAt(0); keysym = UnicodeToKeysym.translate(key); numchars = evt.getCharacters().length(); unicode = true; } break; default: // Modifier handling is a bit tricky. Alt, Ctrl, and Super should be passed // through to the VNC server so that they get handled there, but strip // them from the character before retrieving the Unicode char from it. // Don't clear Shift, we still want uppercase characters. int metaMask = (0x00007000 | 0x00070000); // KeyEvent.META_CTRL_MASK | KeyEvent.META_META_MASK // We still want alt-key combinations to give us symbols, so we only strip out // KeyEvent.META_ALT_MASK // if we've decided to send out ALT as a separate key modifier over. if ((metaState & ALT_MASK) != 0) metaMask |= 0x00000032; KeyEvent copy = new KeyEvent( evt.getDownTime(), evt.getEventTime(), evt.getAction(), evt.getKeyCode(), evt.getRepeatCount(), keyboardMetaState & ~metaMask, evt.getDeviceId(), evt.getScanCode()); key = copy.getUnicodeChar(); keysym = UnicodeToKeysym.translate(key); break; } if (down) { // Look for standard scan-codes from external keyboards switch (evt.getScanCode()) { case SCAN_ESC: keysym = 0xff1b; break; case SCAN_LEFTCTRL: case SCAN_RIGHTCTRL: hardwareMetaState |= CTRL_MASK; break; case SCAN_F1: keysym = 0xffbe; break; case SCAN_F2: keysym = 0xffbf; break; case SCAN_F3: keysym = 0xffc0; break; case SCAN_F4: keysym = 0xffc1; break; case SCAN_F5: keysym = 0xffc2; break; case SCAN_F6: keysym = 0xffc3; break; case SCAN_F7: keysym = 0xffc4; break; case SCAN_F8: keysym = 0xffc5; break; case SCAN_F9: keysym = 0xffc6; break; case SCAN_F10: keysym = 0xffc7; break; } switch (keyCode) { case KeyEvent.KEYCODE_DPAD_CENTER: hardwareMetaState |= CTRL_MASK; break; // Leaving KeyEvent.KEYCODE_ALT_LEFT for symbol input on hardware keyboards. case KeyEvent.KEYCODE_ALT_RIGHT: hardwareMetaState |= ALT_MASK; break; } } try { if (afterMenu) { afterMenu = false; if (!down && keysym != lastKeyDown) return true; } if (down) lastKeyDown = keysym; if (numchars == 1) { android.util.Log.e( TAG, "action down? = " + down + " key = " + key + " keysym = " + keysym + " onscreen metastate = " + onScreenMetaState + " keyboard metastate = " + keyboardMetaState + " RFB metastate = " + metaState + " keycode = " + keyCode + " unicode = " + evt.getUnicodeChar()); // TODO: UGLY HACK for Z10 devices running 10.1 which never send the down-event // for backspace... so we send it instead. Remove as soon as possible! if (backspaceWorkaround && keyCode == KeyEvent.KEYCODE_DEL) rfb.writeKeyEvent(keysym, (onScreenMetaState | hardwareMetaState | metaState), true); rfb.writeKeyEvent(keysym, (onScreenMetaState | hardwareMetaState | metaState), down); // If this is a unicode key, the up event will never come, so we artificially insert it. if (unicode) rfb.writeKeyEvent(keysym, (onScreenMetaState | hardwareMetaState | metaState), false); // TODO: UGLY HACK for BB10 devices which never send the up-event // for space, backspace and enter... so we send it instead. Remove as soon as possible! if (bb10 && (keyCode == KeyEvent.KEYCODE_SPACE || keyCode == KeyEvent.KEYCODE_DEL || keyCode == KeyEvent.KEYCODE_ENTER)) rfb.writeKeyEvent(keysym, (onScreenMetaState | hardwareMetaState | metaState), false); } else if (numchars > 1) { for (int i = 0; i < numchars; i++) { key = evt.getCharacters().charAt(i); // Log.e(TAG,"action down? = " + down + " key = " + key + " keysym = " + keysym + " // onscreen metastate = " + onScreenMetaState + " keyboard metastate = " + // keyboardMetaState + " RFB metastate = " + metaState + " keycode = " + keyCode + " // unicode = " + evt.getUnicodeChar()); keysym = UnicodeToKeysym.translate(key); rfb.writeKeyEvent(keysym, (onScreenMetaState | hardwareMetaState | metaState), true); rfb.writeKeyEvent(keysym, (onScreenMetaState | hardwareMetaState | metaState), false); } } } catch (Exception e) { e.printStackTrace(); } return true; } return false; }
/** * 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); } }
@Override public ProtoCommands.KeyEvent getMozcKeyEvent(android.view.KeyEvent keyEvent) { int metaState = keyEvent.getMetaState(); int unicodeChar = keyEvent.getUnicodeChar(); int keyCode = keyEvent.getKeyCode(); ProtoCommands.KeyEvent.Builder builder = ProtoCommands.KeyEvent.newBuilder(); switch (keyCode) { case android.view.KeyEvent.KEYCODE_SPACE: builder.setSpecialKey(SpecialKey.SPACE); break; case android.view.KeyEvent.KEYCODE_DEL: builder.setSpecialKey(SpecialKey.BACKSPACE); break; case android.view.KeyEvent.KEYCODE_TAB: builder.setSpecialKey(SpecialKey.TAB); break; case android.view.KeyEvent.KEYCODE_ENTER: builder.setSpecialKey(SpecialKey.ENTER); break; case android.view.KeyEvent.KEYCODE_HOME: builder.setSpecialKey(SpecialKey.HOME); break; case android.view.KeyEvent.KEYCODE_PAGE_UP: builder.setSpecialKey(SpecialKey.PAGE_UP); break; case android.view.KeyEvent.KEYCODE_PAGE_DOWN: builder.setSpecialKey(SpecialKey.PAGE_DOWN); break; case android.view.KeyEvent.KEYCODE_NUMPAD_DIVIDE: builder.setSpecialKey(SpecialKey.DIVIDE); break; case android.view.KeyEvent.KEYCODE_NUMPAD_MULTIPLY: builder.setSpecialKey(SpecialKey.MULTIPLY); break; case android.view.KeyEvent.KEYCODE_NUMPAD_SUBTRACT: builder.setSpecialKey(SpecialKey.SUBTRACT); break; case android.view.KeyEvent.KEYCODE_NUMPAD_ADD: builder.setSpecialKey(SpecialKey.ADD); break; case android.view.KeyEvent.KEYCODE_NUMPAD_ENTER: builder.setSpecialKey(SpecialKey.SEPARATOR); break; case android.view.KeyEvent.KEYCODE_NUMPAD_DOT: builder.setSpecialKey(SpecialKey.DECIMAL); break; case android.view.KeyEvent.KEYCODE_NUMPAD_0: builder.setSpecialKey(SpecialKey.NUMPAD0); break; case android.view.KeyEvent.KEYCODE_NUMPAD_1: builder.setSpecialKey(SpecialKey.NUMPAD1); break; case android.view.KeyEvent.KEYCODE_NUMPAD_2: builder.setSpecialKey(SpecialKey.NUMPAD2); break; case android.view.KeyEvent.KEYCODE_NUMPAD_3: builder.setSpecialKey(SpecialKey.NUMPAD3); break; case android.view.KeyEvent.KEYCODE_NUMPAD_4: builder.setSpecialKey(SpecialKey.NUMPAD4); break; case android.view.KeyEvent.KEYCODE_NUMPAD_5: builder.setSpecialKey(SpecialKey.NUMPAD5); break; case android.view.KeyEvent.KEYCODE_NUMPAD_6: builder.setSpecialKey(SpecialKey.NUMPAD6); break; case android.view.KeyEvent.KEYCODE_NUMPAD_7: builder.setSpecialKey(SpecialKey.NUMPAD7); break; case android.view.KeyEvent.KEYCODE_NUMPAD_8: builder.setSpecialKey(SpecialKey.NUMPAD8); break; case android.view.KeyEvent.KEYCODE_NUMPAD_9: builder.setSpecialKey(SpecialKey.NUMPAD9); break; default: if (unicodeChar != 0) { builder.setKeyCode(unicodeChar); } break; } if (!isPrintable(unicodeChar)) { // Mozc server accepts modifiers only if non-printable key event is sent. if ((metaState & android.view.KeyEvent.META_SHIFT_MASK) != 0) { builder.addModifierKeys(ModifierKey.SHIFT); } if ((metaState & android.view.KeyEvent.META_ALT_MASK) != 0) { builder.addModifierKeys(ModifierKey.ALT); } if ((metaState & android.view.KeyEvent.META_CTRL_MASK) != 0) { builder.addModifierKeys(ModifierKey.CTRL); } } return builder.build(); }
@Override public boolean injectString(String str) throws InjectEventSecurityException { checkNotNull(str); checkState(Looper.myLooper() == mainLooper, "Expecting to be on main thread!"); initialize(); // No-op if string is empty. if (str.length() == 0) { Log.w(TAG, "Supplied string is empty resulting in no-op (nothing is typed)."); return true; } boolean eventInjected = false; KeyCharacterMap keyCharacterMap = getKeyCharacterMap(); // TODO(user): Investigate why not use (as suggested in javadoc of keyCharacterMap.getEvents): // http://developer.android.com/reference/android/view/KeyEvent.html#KeyEvent(long, // java.lang.String, int, int) KeyEvent[] events = keyCharacterMap.getEvents(str.toCharArray()); if (events == null) { throw new RuntimeException( String.format( "Failed to get key events for string %s (i.e. current IME does not understand how to " + "translate the string into key events). As a workaround, you can use replaceText action" + " to set the text directly in the EditText field.", str)); } Log.d(TAG, String.format("Injecting string: \"%s\"", str)); for (KeyEvent event : events) { checkNotNull( event, String.format( "Failed to get event for character (%c) with key code (%s)", event.getKeyCode(), event.getUnicodeChar())); eventInjected = false; for (int attempts = 0; !eventInjected && attempts < 4; attempts++) { attempts++; // We have to change the time of an event before injecting it because // all KeyEvents returned by KeyCharacterMap.getEvents() have the same // time stamp and the system rejects too old events. Hence, it is // possible for an event to become stale before it is injected if it // takes too long to inject the preceding ones. event = KeyEvent.changeTimeRepeat(event, SystemClock.uptimeMillis(), 0); eventInjected = injectKeyEvent(event); } if (!eventInjected) { Log.e( TAG, String.format( "Failed to inject event for character (%c) with key code (%s)", event.getUnicodeChar(), event.getKeyCode())); break; } } return eventInjected; }
@Override public boolean onKeyUp(int keyCode, KeyEvent event) { NativeKeyup(event.getUnicodeChar()); return super.onKeyUp(keyCode, event); }
/** * Handle a keyDown event. * * @param keyCode the keycode of the keyDown event */ public void keyDown(int keyCode, KeyEvent event, boolean appMode, boolean allowToggle) throws IOException { if (LOG_KEYS) { Log.i(TAG, "keyDown(" + keyCode + "," + event + "," + appMode + "," + allowToggle + ")"); } if (handleKeyCode(keyCode, event, appMode)) { return; } int result = -1; boolean chordedCtrl = false; boolean setHighBit = false; switch (keyCode) { case KeyEvent.KEYCODE_ALT_RIGHT: case KeyEvent.KEYCODE_ALT_LEFT: if (allowToggle) { mAltKey.onPress(); updateCursorMode(); } break; case KeyEvent.KEYCODE_SHIFT_LEFT: case KeyEvent.KEYCODE_SHIFT_RIGHT: if (allowToggle) { mCapKey.onPress(); updateCursorMode(); } break; case KEYCODE_CTRL_LEFT: case KEYCODE_CTRL_RIGHT: // Ignore the control key. return; case KEYCODE_CAPS_LOCK: // Ignore the capslock key. return; case KEYCODE_FUNCTION: // Ignore the function key. return; case KeyEvent.KEYCODE_BACK: result = mBackKeyCode; break; default: { int metaState = event.getMetaState(); chordedCtrl = ((META_CTRL_ON & metaState) != 0); boolean effectiveCaps = allowToggle && (mCapKey.isActive()); boolean effectiveAlt = allowToggle && mAltKey.isActive(); int effectiveMetaState = metaState & (~META_CTRL_MASK); if (effectiveCaps) { effectiveMetaState |= KeyEvent.META_SHIFT_ON; } if (!allowToggle && (effectiveMetaState & META_ALT_ON) != 0) { effectiveAlt = true; } if (effectiveAlt) { if (mAltSendsEsc) { mTermSession.write(new byte[] {0x1b}, 0, 1); effectiveMetaState &= ~KeyEvent.META_ALT_MASK; } else if (SUPPORT_8_BIT_META) { setHighBit = true; effectiveMetaState &= ~KeyEvent.META_ALT_MASK; } else { // Legacy behavior: Pass Alt through to allow composing characters. effectiveMetaState |= KeyEvent.META_ALT_ON; } } // Note: The Hacker keyboard IME key labeled Alt actually sends Meta. if ((metaState & KeyEvent.META_META_ON) != 0) { if (mAltSendsEsc) { mTermSession.write(new byte[] {0x1b}, 0, 1); effectiveMetaState &= ~KeyEvent.META_META_MASK; } else { if (SUPPORT_8_BIT_META) { setHighBit = true; effectiveMetaState &= ~KeyEvent.META_META_MASK; } } } result = event.getUnicodeChar(effectiveMetaState); if ((result & KeyCharacterMap.COMBINING_ACCENT) != 0) { if (LOG_COMBINING_ACCENT) { Log.i(TAG, "Got combining accent " + result); } mCombiningAccent = result & KeyCharacterMap.COMBINING_ACCENT_MASK; return; } if (mCombiningAccent != 0) { int unaccentedChar = result; result = KeyCharacterMap.getDeadChar(mCombiningAccent, unaccentedChar); if (LOG_COMBINING_ACCENT) { Log.i( TAG, "getDeadChar(" + mCombiningAccent + ", " + unaccentedChar + ") -> " + result); } mCombiningAccent = 0; } break; } } boolean effectiveControl = chordedCtrl || mHardwareControlKey || (allowToggle && mControlKey.isActive()); boolean effectiveFn = allowToggle && mFnKey.isActive(); result = mapControlChar(effectiveControl, effectiveFn, result); if (result >= KEYCODE_OFFSET) { handleKeyCode(result - KEYCODE_OFFSET, null, appMode); } else if (result >= 0) { if (setHighBit) { result |= 0x80; } mTermSession.write(result); } }
/** * @param keyCode * @param event * @return true to say we handled this, false to tell Android to handle it */ public static boolean keyUp(int keyCode, KeyEvent event) { int unicodeChar = event.getUnicodeChar(); return onKeyUp(keyCode, unicodeChar); }