예제 #1
0
    @Override
    public boolean onKeyMultiple(int keyCode, int count, KeyEvent event) {
      if (keyCode != KeyEvent.KEYCODE_UNKNOWN || event.getAction() != KeyEvent.ACTION_MULTIPLE)
        return super.onKeyMultiple(keyCode, count, event);

      if (event.getCharacters() != null) {
        int utf8Char = event.getCharacters().codePointAt(0);
        handleKeyDown(host, utf8Char, utf8Char);
        return true;
      }

      return false;
    }
예제 #2
0
  @Override
  public boolean dispatchKeyEvent(KeyEvent event) {
    if (m_started
        && event.getAction() == KeyEvent.ACTION_MULTIPLE
        && event.getCharacters() != null
        && event.getCharacters().length() == 1
        && event.getKeyCode() == 0) {
      Log.i(
          QtApplication.QtTAG,
          "dispatchKeyEvent at MULTIPLE with one character: " + event.getCharacters());
      QtApplication.keyDown(0, event.getCharacters().charAt(0), event.getMetaState());
      QtApplication.keyUp(0, event.getCharacters().charAt(0), event.getMetaState());
    }

    return super.dispatchKeyEvent(event);
  }
예제 #3
0
  @Override
  public boolean onKeyMultiple(final int inKeyCode, int repeatCount, KeyEvent event) {
    String s = event.getCharacters();
    if (s == null || s.length() == 0) return super.onKeyMultiple(inKeyCode, repeatCount, event);

    final char[] cc = s.toCharArray();
    int cnt = 0;
    for (int i = cc.length; --i >= 0; cnt += cc[i] != 0 ? 1 : 0) ;
    if (cnt == 0) return super.onKeyMultiple(inKeyCode, repeatCount, event);
    final Activity me = this;
    queueEvent(
        new Runnable() {
          // This method will be called on the rendering thread:
          public void run() {
            for (int i = 0, n = cc.length; i < n; i++) {
              int keyCode;
              if ((keyCode = cc[i]) != 0) {
                // Simulate key down and up...
                GodotLib.key(0, keyCode, true);
                GodotLib.key(0, keyCode, false);
              }
            }
          }
        });
    return true;
  }
예제 #4
0
 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;
 }
예제 #5
0
  @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;
  }
예제 #6
0
  /**
   * 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);
    }
  }
예제 #7
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;
  }
예제 #8
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);
    }
  }
  /**
   * Handle onKey() events coming down from a {@link TerminalView} above us. Modify the keys to make
   * more sense to a host then pass it to the transport.
   */
  public boolean onKey(View v, int keyCode, KeyEvent event) {
    try {
      final boolean hardKeyboardHidden = manager.hardKeyboardHidden;

      // Ignore all key-up events except for the special keys
      if (event.getAction() == KeyEvent.ACTION_UP) {
        // There's nothing here for virtual keyboard users.
        if (!hardKeyboard || (hardKeyboard && hardKeyboardHidden)) return false;

        // skip keys if we aren't connected yet or have been disconnected
        if (bridge.isDisconnected() || bridge.transport == null) return false;

        if (PreferenceConstants.KEYMODE_RIGHT.equals(keymode)) {
          if (keyCode == KeyEvent.KEYCODE_ALT_RIGHT && (metaState & META_SLASH) != 0) {
            metaState &= ~(META_SLASH | META_TRANSIENT);
            bridge.transport.write('/');
            return true;
          } else if (keyCode == KeyEvent.KEYCODE_SHIFT_RIGHT && (metaState & META_TAB) != 0) {
            metaState &= ~(META_TAB | META_TRANSIENT);
            bridge.transport.write(0x09);
            return true;
          }
        } else if (PreferenceConstants.KEYMODE_LEFT.equals(keymode)) {
          if (keyCode == KeyEvent.KEYCODE_ALT_LEFT && (metaState & META_SLASH) != 0) {
            metaState &= ~(META_SLASH | META_TRANSIENT);
            bridge.transport.write('/');
            return true;
          } else if (keyCode == KeyEvent.KEYCODE_SHIFT_LEFT && (metaState & META_TAB) != 0) {
            metaState &= ~(META_TAB | META_TRANSIENT);
            bridge.transport.write(0x09);
            return true;
          }
        }

        return false;
      }

      // check for terminal resizing keys
      if (keyCode == KeyEvent.KEYCODE_VOLUME_UP) {
        bridge.increaseFontSize();
        return true;
      } else if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN) {
        bridge.decreaseFontSize();
        return true;
      }

      // skip keys if we aren't connected yet or have been disconnected
      if (bridge.isDisconnected() || bridge.transport == null) return false;

      bridge.resetScrollPosition();

      if (keyCode == KeyEvent.KEYCODE_UNKNOWN && event.getAction() == KeyEvent.ACTION_MULTIPLE) {
        byte[] input = event.getCharacters().getBytes(encoding);
        bridge.transport.write(input);
        return true;
      }

      int curMetaState = event.getMetaState();
      final int orgMetaState = curMetaState;

      if ((metaState & META_SHIFT_MASK) != 0) {
        curMetaState |= KeyEvent.META_SHIFT_ON;
      }

      if ((metaState & META_ALT_MASK) != 0) {
        curMetaState |= KeyEvent.META_ALT_ON;
      }

      int key = event.getUnicodeChar(curMetaState);
      // no hard keyboard?  ALT-k should pass through to below
      if ((orgMetaState & KeyEvent.META_ALT_ON) != 0 && (!hardKeyboard || hardKeyboardHidden)) {
        key = 0;
      }

      if ((key & KeyCharacterMap.COMBINING_ACCENT) != 0) {
        mDeadKey = key & KeyCharacterMap.COMBINING_ACCENT_MASK;
        return true;
      }

      if (mDeadKey != 0) {
        key = KeyCharacterMap.getDeadChar(mDeadKey, keyCode);
        mDeadKey = 0;
      }

      final boolean printing = (key != 0 && keyCode != KeyEvent.KEYCODE_ENTER);

      // otherwise pass through to existing session
      // print normal keys
      if (printing) {
        metaState &= ~(META_SLASH | META_TAB);

        // Remove shift and alt modifiers
        final int lastMetaState = metaState;
        metaState &= ~(META_SHIFT_ON | META_ALT_ON);
        if (metaState != lastMetaState) {
          bridge.redraw();
        }

        if ((metaState & META_CTRL_MASK) != 0) {
          metaState &= ~META_CTRL_ON;
          bridge.redraw();

          // If there is no hard keyboard or there is a hard keyboard currently hidden,
          // CTRL-1 through CTRL-9 will send F1 through F9
          if ((!hardKeyboard || (hardKeyboard && hardKeyboardHidden)) && sendFunctionKey(keyCode))
            return true;

          key = keyAsControl(key);
        }

        // handle pressing f-keys
        if ((hardKeyboard && !hardKeyboardHidden)
            && (curMetaState & KeyEvent.META_SHIFT_ON) != 0
            && sendFunctionKey(keyCode)) return true;

        if (key < 0x80) bridge.transport.write(key);
        else
          // TODO write encoding routine that doesn't allocate each time
          bridge.transport.write(new String(Character.toChars(key)).getBytes(encoding));

        return true;
      }

      // send ctrl and meta-keys as appropriate
      if (!hardKeyboard || hardKeyboardHidden) {
        int k = event.getUnicodeChar(0);
        int k0 = k;
        boolean sendCtrl = false;
        boolean sendMeta = false;
        if (k != 0) {
          if ((orgMetaState & HC_META_CTRL_ON) != 0) {
            k = keyAsControl(k);
            if (k != k0) sendCtrl = true;
            // send F1-F10 via CTRL-1 through CTRL-0
            if (!sendCtrl && sendFunctionKey(keyCode)) return true;
          } else if ((orgMetaState & KeyEvent.META_ALT_ON) != 0) {
            sendMeta = true;
            sendEscape();
          }
          if (sendMeta || sendCtrl) {
            bridge.transport.write(k);
            return true;
          }
        }
      }
      // try handling keymode shortcuts
      if (hardKeyboard && !hardKeyboardHidden && event.getRepeatCount() == 0) {
        if (PreferenceConstants.KEYMODE_RIGHT.equals(keymode)) {
          switch (keyCode) {
            case KeyEvent.KEYCODE_ALT_RIGHT:
              metaState |= META_SLASH;
              return true;
            case KeyEvent.KEYCODE_SHIFT_RIGHT:
              metaState |= META_TAB;
              return true;
            case KeyEvent.KEYCODE_SHIFT_LEFT:
              metaPress(META_SHIFT_ON);
              return true;
            case KeyEvent.KEYCODE_ALT_LEFT:
              metaPress(META_ALT_ON);
              return true;
          }
        } else if (PreferenceConstants.KEYMODE_LEFT.equals(keymode)) {
          switch (keyCode) {
            case KeyEvent.KEYCODE_ALT_LEFT:
              metaState |= META_SLASH;
              return true;
            case KeyEvent.KEYCODE_SHIFT_LEFT:
              metaState |= META_TAB;
              return true;
            case KeyEvent.KEYCODE_SHIFT_RIGHT:
              metaPress(META_SHIFT_ON);
              return true;
            case KeyEvent.KEYCODE_ALT_RIGHT:
              metaPress(META_ALT_ON);
              return true;
          }
        } else {
          switch (keyCode) {
            case KeyEvent.KEYCODE_ALT_LEFT:
            case KeyEvent.KEYCODE_ALT_RIGHT:
              metaPress(META_ALT_ON);
              return true;
            case KeyEvent.KEYCODE_SHIFT_LEFT:
            case KeyEvent.KEYCODE_SHIFT_RIGHT:
              metaPress(META_SHIFT_ON);
              return true;
          }
        }
      }

      // look for special chars
      switch (keyCode) {
        case KEYCODE_ESCAPE:
          sendEscape();
          return true;
        case KeyEvent.KEYCODE_TAB:
          bridge.transport.write(0x09);
          return true;
        case KeyEvent.KEYCODE_SEARCH:
          // check to see which shortcut the search button triggers
          String search =
              manager.prefs.getString(PreferenceConstants.SEARCH, PreferenceConstants.SEARCH_ESC);
          if (PreferenceConstants.SEARCH_BACKBTN.equals(search)) {
            // TODO: figure out what to do here!
          } else if (PreferenceConstants.SEARCH_ESC.equals(search)) {
            sendEscape();
          }
          return true;
        case KeyEvent.KEYCODE_CAMERA:

          // check to see which shortcut the camera button triggers
          String camera =
              manager.prefs.getString(
                  PreferenceConstants.CAMERA, PreferenceConstants.CAMERA_CTRLA_SPACE);
          if (PreferenceConstants.CAMERA_CTRLA_SPACE.equals(camera)) {
            bridge.transport.write(0x01);
            bridge.transport.write(' ');
          } else if (PreferenceConstants.CAMERA_CTRLA.equals(camera)) {
            bridge.transport.write(0x01);
          } else if (PreferenceConstants.CAMERA_ESC.equals(camera)) {
            ((vt320) buffer).keyTyped(vt320.KEY_ESCAPE, ' ', 0);
          } else if (PreferenceConstants.CAMERA_ESC_A.equals(camera)) {
            ((vt320) buffer).keyTyped(vt320.KEY_ESCAPE, ' ', 0);
            bridge.transport.write('a');
          }

          break;

        case KeyEvent.KEYCODE_DEL:
          ((vt320) buffer).keyPressed(vt320.KEY_BACK_SPACE, ' ', getStateForBuffer());
          metaState &= ~META_TRANSIENT;
          return true;
        case KeyEvent.KEYCODE_ENTER:
          ((vt320) buffer).keyTyped(vt320.KEY_ENTER, ' ', 0);
          metaState &= ~META_TRANSIENT;
          return true;

        case KeyEvent.KEYCODE_DPAD_LEFT:
          if (selectingForCopy) {
            selectionArea.decrementColumn();
            bridge.redraw();
          } else {
            ((vt320) buffer).keyPressed(vt320.KEY_LEFT, ' ', getStateForBuffer());
            metaState &= ~META_TRANSIENT;
            bridge.tryKeyVibrate();
          }
          return true;

        case KeyEvent.KEYCODE_DPAD_UP:
          if (selectingForCopy) {
            selectionArea.decrementRow();
            bridge.redraw();
          } else {
            ((vt320) buffer).keyPressed(vt320.KEY_UP, ' ', getStateForBuffer());
            metaState &= ~META_TRANSIENT;
            bridge.tryKeyVibrate();
          }
          return true;

        case KeyEvent.KEYCODE_DPAD_DOWN:
          if (selectingForCopy) {
            selectionArea.incrementRow();
            bridge.redraw();
          } else {
            ((vt320) buffer).keyPressed(vt320.KEY_DOWN, ' ', getStateForBuffer());
            metaState &= ~META_TRANSIENT;
            bridge.tryKeyVibrate();
          }
          return true;

        case KeyEvent.KEYCODE_DPAD_RIGHT:
          if (selectingForCopy) {
            selectionArea.incrementColumn();
            bridge.redraw();
          } else {
            ((vt320) buffer).keyPressed(vt320.KEY_RIGHT, ' ', getStateForBuffer());
            metaState &= ~META_TRANSIENT;
            bridge.tryKeyVibrate();
          }
          return true;

        case KeyEvent.KEYCODE_DPAD_CENTER:
          if (selectingForCopy) {
            if (selectionArea.isSelectingOrigin()) selectionArea.finishSelectingOrigin();
            else {
              if (clipboard != null) {
                // copy selected area to clipboard
                String copiedText = selectionArea.copyFrom(buffer);

                clipboard.setText(copiedText);
                // XXX STOPSHIP
                //							manager.notifyUser(manager.getString(
                //									R.string.console_copy_done,
                //									copiedText.length()));

                selectingForCopy = false;
                selectionArea.reset();
              }
            }
          } else {
            if ((metaState & META_CTRL_ON) != 0) {
              sendEscape();
              metaState &= ~META_CTRL_ON;
            } else metaPress(META_CTRL_ON);
          }

          bridge.redraw();

          return true;
        case KeyEvent.KEYCODE_PAGE_UP:
          ((vt320) buffer).keyPressed(vt320.KEY_PAGE_UP, ' ', getStateForBuffer());
          metaState &= ~META_TRANSIENT;
          bridge.tryKeyVibrate();
          return true;
        case KeyEvent.KEYCODE_PAGE_DOWN:
          ((vt320) buffer).keyPressed(vt320.KEY_PAGE_DOWN, ' ', getStateForBuffer());
          metaState &= ~META_TRANSIENT;
          bridge.tryKeyVibrate();
          return true;
        case KeyEvent.KEYCODE_MOVE_HOME:
          //				((vt320) buffer).keyPressed(vt320.KEY_HOME, ' ',getStateForBuffer());
          ((vt320) buffer).keyTyped(vt320.KEY_ESCAPE, ' ', 0);
          bridge.transport.write("[1~".getBytes());
          metaState &= ~META_TRANSIENT;
          bridge.tryKeyVibrate();
          return true;
        case KeyEvent.KEYCODE_MOVE_END:
          //				((vt320) buffer).keyPressed(vt320.KEY_END, ' ',getStateForBuffer());
          ((vt320) buffer).keyTyped(vt320.KEY_ESCAPE, ' ', 0);
          bridge.transport.write("[4~".getBytes());
          metaState &= ~META_TRANSIENT;
          bridge.tryKeyVibrate();
          return true;
      }

    } catch (IOException e) {
      Log.e(TAG, "Problem while trying to handle an onKey() event", e);
      try {
        bridge.transport.flush();
      } catch (IOException ioe) {
        Log.d(TAG, "Our transport was closed, dispatching disconnect event");
        bridge.dispatchDisconnect(false);
      }
    } catch (NullPointerException npe) {
      Log.d(TAG, "Input before connection established ignored.");
      return true;
    }

    return false;
  }