示例#1
0
 public int ConvertKeyBoardCode(int keyCode, KeyEvent event) {
   int curKeyCode = keyCode;
   Log.i(TAG, "revertKeyCode getScanCode = " + event.getScanCode());
   switch (event.getScanCode()) {
     case 231: // keyboard Menu
       curKeyCode = KeyEvent.KEYCODE_MENU;
       break;
     case 232: // keyboard Source
       curKeyCode = KeyEvent.KEYCODE_DPAD_CENTER;
       break;
     case 233: // keyboard Channel Up
       curKeyCode = KeyEvent.KEYCODE_DPAD_UP;
       break;
     case 234: // keyboard Channel Down
       curKeyCode = KeyEvent.KEYCODE_DPAD_DOWN;
       break;
     case 235: // keyboard Volume Down
       curKeyCode = KeyEvent.KEYCODE_DPAD_LEFT;
       break;
     case 236: // keyboard Volume Up
       curKeyCode = KeyEvent.KEYCODE_DPAD_RIGHT;
       break;
     default:
       break;
   }
   return curKeyCode;
 }
  public boolean handleHardwareButtons(int keyCode, KeyEvent evt, int combinedMetastate) {
    boolean down =
        (evt.getAction() == KeyEvent.ACTION_DOWN) || (evt.getAction() == KeyEvent.ACTION_MULTIPLE);

    int mouseChange =
        keyCode == KeyEvent.KEYCODE_VOLUME_DOWN
            ? RemoteVncPointer.MOUSE_BUTTON_SCROLL_DOWN
            : RemoteVncPointer.MOUSE_BUTTON_SCROLL_UP;
    if (keyCode == KeyEvent.KEYCODE_CAMERA
        || keyCode == KeyEvent.KEYCODE_BACK && evt.getScanCode() == 0) {
      if (keyCode == KeyEvent.KEYCODE_CAMERA) cameraButtonDown = down;

      if (down) pointerMask = RemoteVncPointer.MOUSE_BUTTON_RIGHT;
      else pointerMask = 0;
      rfb.writePointerEvent(getX(), getY(), combinedMetastate, pointerMask);
      return true;
    } else if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN || keyCode == KeyEvent.KEYCODE_VOLUME_UP) {
      if (evt.getAction() == KeyEvent.ACTION_DOWN) {
        // If not auto-repeat
        if (scrollRunnable.scrollButton != mouseChange) {
          pointerMask |= mouseChange;
          scrollRunnable.scrollButton = mouseChange;
          handler.postDelayed(scrollRunnable, 200);
        }
      } else {
        handler.removeCallbacks(scrollRunnable);
        scrollRunnable.scrollButton = 0;
        pointerMask &= ~mouseChange;
      }
      rfb.writePointerEvent(getX(), getY(), combinedMetastate, pointerMask);
      return true;
    }
    return false;
  }
示例#3
0
 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);
 }
  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());
  }
示例#5
0
  @Override
  public boolean onKeyDown(int keyCode, KeyEvent event) {
    int dir = 0;
    if (keyCode == 0) keyCode = event.getScanCode();
    if (DeviceInfo.SONY_NAVIGATION_KEYS) {
      if (keyCode == ReaderView.SONY_DPAD_RIGHT_SCANCODE
          || keyCode == ReaderView.SONY_DPAD_DOWN_SCANCODE
          || keyCode == KeyEvent.KEYCODE_DPAD_DOWN
          || keyCode == KeyEvent.KEYCODE_DPAD_RIGHT) dir = 1;
      else if (keyCode == ReaderView.SONY_DPAD_LEFT_SCANCODE
          || keyCode == ReaderView.SONY_DPAD_UP_SCANCODE
          || keyCode == KeyEvent.KEYCODE_DPAD_UP
          || keyCode == KeyEvent.KEYCODE_DPAD_LEFT) dir = -1;
    } else {
      if (keyCode == ReaderView.NOOK_KEY_NEXT_RIGHT
          || keyCode == KeyEvent.KEYCODE_DPAD_RIGHT
          || keyCode == ReaderView.NOOK_KEY_SHIFT_DOWN) dir = 1;
      if (keyCode == ReaderView.NOOK_KEY_PREV_RIGHT
          || keyCode == KeyEvent.KEYCODE_DPAD_LEFT
          || keyCode == ReaderView.NOOK_KEY_SHIFT_UP) dir = -1;
    }
    if (dir != 0) {
      int firstPos = getFirstVisiblePosition();
      int lastPos = getLastVisiblePosition();
      int count = getCount();

      int delta = 1;
      if (dir < 0) {
        View v = getChildAt(0);
        if (v != null) {
          int fh = v.getHeight();
          Rect r = new Rect(0, 0, v.getWidth(), fh);
          getChildVisibleRect(v, r, null);
          delta = (r.height() < fh) ? 1 : 0;
        }
      }

      int nextPos =
          (dir > 0)
              ? Math.min(lastPos, count - 1)
              : Math.max(0, firstPos - (lastPos - firstPos) + delta);

      // Log.w("CoolReader", "first =" + firstPos + " last = " + lastPos + " next = " + nextPos + "
      // count = " + count);

      setSelection(nextPos);
      clearFocus();

      return true;
    }
    return super.onKeyDown(keyCode, event);
  }
  public boolean handleKeyCode(int keyCode, KeyEvent event, boolean appMode) throws IOException {
    String code = null;
    if (event != null) {
      int keyMod = 0;
      // META_CTRL_ON was added only in API 11, so don't use it,
      // use our own tracking of Ctrl key instead.
      // (event.getMetaState() & META_CTRL_ON) != 0
      if (mHardwareControlKey || mControlKey.isActive()) {
        keyMod |= KEYMOD_CTRL;
      }
      if ((event.getMetaState() & META_ALT_ON) != 0) {
        keyMod |= KEYMOD_ALT;
      }
      if ((event.getMetaState() & META_SHIFT_ON) != 0) {
        keyMod |= KEYMOD_SHIFT;
      }
      // First try to map scancode
      code = mKeyMap.get(event.getScanCode() | KEYMOD_SCAN | keyMod);
      if (code == null) {
        code = mKeyMap.get(keyCode | keyMod);
      }
    }

    if (code == null && keyCode >= 0 && keyCode < mKeyCodes.length) {
      if (appMode) {
        code = mAppKeyCodes[keyCode];
      }
      if (code == null) {
        code = mKeyCodes[keyCode];
      }
    }

    if (code != null) {
      if (EmulatorDebug.LOG_CHARACTERS_FLAG) {
        byte[] bytes = code.getBytes();
        Log.d(
            EmulatorDebug.LOG_TAG,
            "Out: '" + EmulatorDebug.bytesToString(bytes, 0, bytes.length) + "'");
      }
      mTermSession.write(code);
      return true;
    }
    return false;
  }
 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);
 }
  @Override
  public boolean handleKeyEvent(KeyEvent event) {

    if (DEBUG_KEYEVENT) {
      Log.d(
          TAG,
          "KeyEvent: action="
              + event.getAction()
              + ", flags="
              + event.getFlags()
              + ", canceled="
              + event.isCanceled()
              + ", keyCode="
              + event.getKeyCode()
              + ", scanCode="
              + event.getScanCode()
              + ", metaState="
              + event.getMetaState()
              + ", repeatCount="
              + event.getRepeatCount());
    }

    if (event.getAction() != KeyEvent.ACTION_UP || event.getRepeatCount() != 0) {
      return false;
    }

    switch (event.getScanCode()) {
      case SCANCODE_TOGGLE_WIFI:
        toggleWifi();
        break;
      case SCANCODE_TOGGLE_BT:
        toggleBluetooth();
        break;
      case SCANCODE_TOGGLE_TOUCHPAD:
        toggleTouchpad();
        break;
      case SCANCODE_BRIGHTNESS_DOWN:
        brightnessDown();
        break;
      case SCANCODE_BRIGHTNESS_UP:
        brightnessUp();
        break;
      case SCANCODE_BRIGHTNESS_AUTO:
        toggleAutoBrightness();
        break;
      case SCANCODE_SCREENSHOT:
        takeScreenshot();
        break;
      case SCANCODE_EXPLORER:
        launchExplorer();
        return false;
      case SCANCODE_SETTINGS:
        launchSettings();
        break;
      case SCANCODE_VOLUME_MUTE:
        // KEYCODE_VOLUME_MUTE is part of the aosp keyevent intercept handling, but
        // aosp uses it stop ringing in phone devices (no system volume mute toggle).
        // Since transformer devices doesn't have a telephony subsystem, we handle and
        // treat this event as a volume mute toggle action. the asusdec KeyHandler
        // mustn't mark the key event as consumed.
        toggleAudioMute();
        return false;
      case SCANCODE_VOLUME_DOWN:
        volumeDown();
        return false;
      case SCANCODE_VOLUME_UP:
        volumeUp();
        return false;
      case SCANCODE_MEDIA_PLAY_PAUSE:
        mediaPlayPause();
        return false;
      case SCANCODE_MEDIA_PREVIOUS:
        mediaPrevious();
        return false;
      case SCANCODE_MEDIA_NEXT:
        mediaNext();
        return false;
      case SCANCODE_CAPS_LOCK:
        capsLock(event);
        return false;

      default:
        return false;
    }

    return true;
  }
 private static CompactKeyEvent getCompactKeyEvent(android.view.KeyEvent keyEvent) {
   return new CompactKeyEvent(
       keyEvent.getScanCode(), getCompactMetaState(keyEvent.getMetaState()));
 }
示例#10
0
  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;
  }
示例#11
0
  /**
   * 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);
    }
  }