public void handleFnKey(boolean down) {
   if (down) {
     mFnKey.onPress();
   } else {
     mFnKey.onRelease();
   }
   updateCursorMode();
 }
  /**
   * 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);
    }
  }