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());
  }
示例#2
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);
 }
示例#3
0
  @Override
  public boolean onKeyUp(int keyCode, KeyEvent event) {
    // If back key
    if (keyCode == KeyEvent.KEYCODE_BACK) {
      // A custom view is currently displayed  (e.g. playing a video)
      if (mCustomView != null) {
        this.hideCustomView();
        return true;
      } else {
        // The webview is currently displayed
        // If back key is bound, then send event to JavaScript
        if (isButtonPlumbedToJs(KeyEvent.KEYCODE_BACK)) {
          sendJavascriptEvent("backbutton");
          return true;
        } else {
          // If not bound
          // Go to previous page in webview if it is possible to go back
          if (this.backHistory()) {
            return true;
          }
          // If not, then invoke default behavior
        }
      }
    }
    // Legacy
    else if (keyCode == KeyEvent.KEYCODE_MENU) {
      if (this.lastMenuEventTime < event.getEventTime()) {
        sendJavascriptEvent("menubutton");
      }
      this.lastMenuEventTime = event.getEventTime();
      return super.onKeyUp(keyCode, event);
    }
    // If search key
    else if (keyCode == KeyEvent.KEYCODE_SEARCH) {
      sendJavascriptEvent("searchbutton");
      return true;
    }

    // Does webkit change this behavior?
    return super.onKeyUp(keyCode, event);
  }
示例#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
  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 void onReceive(Context context, Intent intent) {

    String intentAction = intent.getAction();
    if (AudioManager.ACTION_AUDIO_BECOMING_NOISY.equals(intentAction)) {
      Intent i = new Intent(context, MusicPlaybackService.class);
      i.setAction(SERVICECMD);
      i.putExtra(CMDNAME, CMDPAUSE);
      context.startService(i);
    } else if (Intent.ACTION_MEDIA_BUTTON.equals(intentAction)) {
      KeyEvent event = (KeyEvent) intent.getParcelableExtra(Intent.EXTRA_KEY_EVENT);

      if (event == null) return;

      int keycode = event.getKeyCode();
      int action = event.getAction();
      long eventtime = event.getEventTime();

      switch (keycode) {
        case KeyEvent.KEYCODE_MEDIA_STOP:
          sendMediaCommand(context, CMDSTOP);
          break;
        case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:
          sendMediaCommand(context, CMDTOGGLEPAUSE);
          break;
        case KeyEvent.KEYCODE_MEDIA_NEXT:
          sendMediaCommand(context, CMDNEXT);
          break;
        case KeyEvent.KEYCODE_MEDIA_PREVIOUS:
          sendMediaCommand(context, CMDPREVIOUS);
          break;
        case KeyEvent.KEYCODE_HEADSETHOOK:
          processHeadsetHookEvent(context, action, eventtime);
          break;
      }
    }
  }
  @Override
  public boolean onKeyDown(int keyCode, KeyEvent event) {
    if (mViewImage.mPaused) return false;

    // Don't respond to arrow keys if trackball scrolling is not enabled
    if (!mEnableTrackballScroll) {
      if ((keyCode >= KeyEvent.KEYCODE_DPAD_UP) && (keyCode <= KeyEvent.KEYCODE_DPAD_RIGHT)) {
        return super.onKeyDown(keyCode, event);
      }
    }

    int current = mViewImage.mCurrentPosition;

    int nextImagePos = -2; // default no next image
    try {
      switch (keyCode) {
        case KeyEvent.KEYCODE_DPAD_CENTER:
          {
            if (mViewImage.isPickIntent()) {
              IImage img = mViewImage.mAllImages.getImageAt(mViewImage.mCurrentPosition);
              mViewImage.setResult(
                  ViewImage.RESULT_OK, new Intent().setData(img.fullSizeImageUri()));
              mViewImage.finish();
            }
            break;
          }
        case KeyEvent.KEYCODE_DPAD_LEFT:
          {
            if (getScale() <= 1F && event.getEventTime() >= mNextChangePositionTime) {
              nextImagePos = current - 1;
              mNextChangePositionTime = event.getEventTime() + 500;
            } else {
              panBy(PAN_RATE, 0);
              center(true, false);
            }
            return true;
          }
        case KeyEvent.KEYCODE_DPAD_RIGHT:
          {
            if (getScale() <= 1F && event.getEventTime() >= mNextChangePositionTime) {
              nextImagePos = current + 1;
              mNextChangePositionTime = event.getEventTime() + 500;
            } else {
              panBy(-PAN_RATE, 0);
              center(true, false);
            }
            return true;
          }
        case KeyEvent.KEYCODE_DPAD_UP:
          {
            panBy(0, PAN_RATE);
            center(false, true);
            return true;
          }
        case KeyEvent.KEYCODE_DPAD_DOWN:
          {
            panBy(0, -PAN_RATE);
            center(false, true);
            return true;
          }
        case KeyEvent.KEYCODE_DEL:
          MenuHelper.deletePhoto(mViewImage, mViewImage.mDeletePhotoRunnable);
          break;
      }
    } finally {
      if (nextImagePos >= 0 && nextImagePos < mViewImage.mAllImages.getCount()) {
        synchronized (mViewImage) {
          mViewImage.setMode(ViewImage.MODE_NORMAL);
          mViewImage.setImage(nextImagePos, true);
        }
      } else if (nextImagePos != -2) {
        center(true, true);
      }
    }

    return super.onKeyDown(keyCode, event);
  }
  @Override
  public void onReceive(Context context, Intent intent) {
    String intentAction = intent.getAction();
    if (AudioManager.ACTION_AUDIO_BECOMING_NOISY.equals(intentAction)) {
      Intent i = new Intent(context, MediaPlaybackService.class);
      i.setAction(MediaPlaybackService.SERVICECMD);
      i.putExtra(MediaPlaybackService.CMDNAME, MediaPlaybackService.CMDPAUSE);
      context.startService(i);
    } else if (Intent.ACTION_MEDIA_BUTTON.equals(intentAction)) {
      KeyEvent event = (KeyEvent) intent.getParcelableExtra(Intent.EXTRA_KEY_EVENT);

      if (event == null) {
        return;
      }

      int keycode = event.getKeyCode();
      int action = event.getAction();
      long eventtime = event.getEventTime();

      // single quick press: pause/resume.
      // double press: next track
      // long press: start auto-shuffle mode.

      String command = null;
      switch (keycode) {
        case KeyEvent.KEYCODE_MEDIA_STOP:
          command = MediaPlaybackService.CMDSTOP;
          break;
        case KeyEvent.KEYCODE_HEADSETHOOK:
        case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:
          command = MediaPlaybackService.CMDTOGGLEPAUSE;
          break;
        case KeyEvent.KEYCODE_MEDIA_NEXT:
          command = MediaPlaybackService.CMDNEXT;
          break;
        case KeyEvent.KEYCODE_MEDIA_PREVIOUS:
          command = MediaPlaybackService.CMDPREVIOUS;
          break;
        case KeyEvent.KEYCODE_MEDIA_PAUSE:
          command = MediaPlaybackService.CMDPAUSE;
          break;
        case KeyEvent.KEYCODE_MEDIA_PLAY:
          command = MediaPlaybackService.CMDPLAY;
          break;
      }

      if (command != null) {
        if (action == KeyEvent.ACTION_DOWN) {
          if (mDown) {
            if ((MediaPlaybackService.CMDTOGGLEPAUSE.equals(command)
                    || MediaPlaybackService.CMDPLAY.equals(command))
                && mLastClickTime != 0
                && eventtime - mLastClickTime > LONG_PRESS_DELAY) {
              mHandler.sendMessage(mHandler.obtainMessage(MSG_LONGPRESS_TIMEOUT, context));
            }
          } else if (event.getRepeatCount() == 0) {
            // only consider the first event in a sequence, not the repeat events,
            // so that we don't trigger in cases where the first event went to
            // a different app (e.g. when the user ends a phone call by
            // long pressing the headset button)

            // The service may or may not be running, but we need to send it
            // a command.
            Intent i = new Intent(context, MediaPlaybackService.class);
            i.setAction(MediaPlaybackService.SERVICECMD);
            if (keycode == KeyEvent.KEYCODE_HEADSETHOOK && eventtime - mLastClickTime < 300) {
              i.putExtra(MediaPlaybackService.CMDNAME, MediaPlaybackService.CMDNEXT);
              context.startService(i);
              mLastClickTime = 0;
            } else {
              i.putExtra(MediaPlaybackService.CMDNAME, command);
              context.startService(i);
              mLastClickTime = eventtime;
            }

            mLaunched = false;
            mDown = true;
          }
        } else {
          mHandler.removeMessages(MSG_LONGPRESS_TIMEOUT);
          mDown = false;
        }
        if (isOrderedBroadcast()) {
          abortBroadcast();
        }
      }
    }
  }
  @Override
  public void onReceive(final Context context, final Intent intent) {
    if (DEBUG) Log.v(TAG, "Received intent: " + intent);
    final String intentAction = intent.getAction();
    if (AudioManager.ACTION_AUDIO_BECOMING_NOISY.equals(intentAction)) {
      startService(context, MusicService.CMDPAUSE);
    } else if (Intent.ACTION_MEDIA_BUTTON.equals(intentAction)) {
      final KeyEvent event = (KeyEvent) intent.getParcelableExtra(Intent.EXTRA_KEY_EVENT);
      if (event == null) {
        return;
      }

      final int keycode = event.getKeyCode();
      final int action = event.getAction();
      final long eventtime = event.getEventTime();

      String command = null;
      switch (keycode) {
        case KeyEvent.KEYCODE_MEDIA_STOP:
          command = MusicService.CMDSTOP;
          break;
        case KeyEvent.KEYCODE_HEADSETHOOK:
        case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:
          command = MusicService.CMDTOGGLEPAUSE;
          break;
        case KeyEvent.KEYCODE_MEDIA_NEXT:
          command = MusicService.CMDNEXT;
          break;
        case KeyEvent.KEYCODE_MEDIA_PREVIOUS:
          command = MusicService.CMDPREVIOUS;
          break;
        case KeyEvent.KEYCODE_MEDIA_PAUSE:
          command = MusicService.CMDPAUSE;
          break;
        case KeyEvent.KEYCODE_MEDIA_PLAY:
          command = MusicService.CMDPLAY;
          break;
      }
      if (command != null) {
        if (action == KeyEvent.ACTION_DOWN) {
          if (mDown) {
            if (MusicService.CMDTOGGLEPAUSE.equals(command)
                || MusicService.CMDPLAY.equals(command)) {
              if (mLastClickTime != 0 && eventtime - mLastClickTime > LONG_PRESS_DELAY) {
                acquireWakeLockAndSendMessage(
                    context, mHandler.obtainMessage(MSG_LONGPRESS_TIMEOUT, context), 0);
              }
            }
          } else if (event.getRepeatCount() == 0) {

            if (keycode == KeyEvent.KEYCODE_HEADSETHOOK) {
              if (eventtime - mLastClickTime >= DOUBLE_CLICK) {
                mClickCounter = 0;
              }

              mClickCounter++;
              if (DEBUG) Log.v(TAG, "Got headset click, count = " + mClickCounter);
              mHandler.removeMessages(MSG_HEADSET_DOUBLE_CLICK_TIMEOUT);

              Message msg =
                  mHandler.obtainMessage(
                      MSG_HEADSET_DOUBLE_CLICK_TIMEOUT, mClickCounter, 0, context);

              long delay = mClickCounter < 3 ? DOUBLE_CLICK : 0;
              if (mClickCounter >= 3) {
                mClickCounter = 0;
              }
              mLastClickTime = eventtime;
              acquireWakeLockAndSendMessage(context, msg, delay);
            } else {
              startService(context, command);
            }
            mLaunched = false;
            mDown = true;
          }
        } else {
          mHandler.removeMessages(MSG_LONGPRESS_TIMEOUT);
          mDown = false;
        }
        if (isOrderedBroadcast()) {
          abortBroadcast();
        }
        releaseWakeLockIfHandlerIdle();
      }
    }
  }
示例#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;
  }