Example #1
0
  @SuppressLint("WrongCall") // Suppress the warning since this does not refer to Android's onDraw()
  @Override
  public void onDraw(Canvas canvas) {
    if (bridge.bitmap != null) {
      // draw the bitmap
      bridge.onDraw();

      // draw the bridge bitmap if it exists
      canvas.drawBitmap(bridge.bitmap, 0, 0, paint);

      // also draw cursor if visible
      if (bridge.buffer.isCursorVisible()) {
        int cursorColumn = bridge.buffer.getCursorColumn();
        final int cursorRow = bridge.buffer.getCursorRow();

        final int columns = bridge.buffer.getColumns();

        if (cursorColumn == columns) cursorColumn = columns - 1;

        if (cursorColumn < 0 || cursorRow < 0) return;

        int currentAttribute = bridge.buffer.getAttributes(cursorColumn, cursorRow);
        boolean onWideCharacter = (currentAttribute & VDUBuffer.FULLWIDTH) != 0;

        int x = cursorColumn * bridge.charWidth;
        int y =
            (bridge.buffer.getCursorRow() + bridge.buffer.screenBase - bridge.buffer.windowBase)
                * bridge.charHeight;

        // Save the current clip and translation
        canvas.save();

        canvas.translate(x, y);
        canvas.clipRect(0, 0, bridge.charWidth * (onWideCharacter ? 2 : 1), bridge.charHeight);
        canvas.drawPaint(cursorPaint);

        final int deadKey = bridge.getKeyHandler().getDeadKey();
        if (deadKey != 0) {
          canvas.drawText(new char[] {(char) deadKey}, 0, 1, 0, 0, cursorStrokePaint);
        }

        // Make sure we scale our decorations to the correct size.
        canvas.concat(scaleMatrix);

        int metaState = bridge.getKeyHandler().getMetaState();

        if ((metaState & TerminalKeyListener.OUR_SHIFT_ON) != 0)
          canvas.drawPath(shiftCursor, cursorStrokePaint);
        else if ((metaState & TerminalKeyListener.OUR_SHIFT_LOCK) != 0)
          canvas.drawPath(shiftCursor, cursorPaint);

        if ((metaState & TerminalKeyListener.OUR_ALT_ON) != 0)
          canvas.drawPath(altCursor, cursorStrokePaint);
        else if ((metaState & TerminalKeyListener.OUR_ALT_LOCK) != 0)
          canvas.drawPath(altCursor, cursorPaint);

        if ((metaState & TerminalKeyListener.OUR_CTRL_ON) != 0)
          canvas.drawPath(ctrlCursor, cursorStrokePaint);
        else if ((metaState & TerminalKeyListener.OUR_CTRL_LOCK) != 0)
          canvas.drawPath(ctrlCursor, cursorPaint);

        // Restore previous clip region
        canvas.restore();
      }

      // draw any highlighted area
      if (bridge.isSelectingForCopy()) {
        SelectionArea area = bridge.getSelectionArea();
        canvas.save(Canvas.CLIP_SAVE_FLAG);
        canvas.clipRect(
            area.getLeft() * bridge.charWidth,
            area.getTop() * bridge.charHeight,
            (area.getRight() + 1) * bridge.charWidth,
            (area.getBottom() + 1) * bridge.charHeight);
        canvas.drawPaint(cursorPaint);
        canvas.restore();
      }
    }
  }
  /**
   * 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;
  }