2 * Copyright (C) 2007 The Android Open Source Project
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 package jackpal.androidterm;
19 import java.io.FileOutputStream;
20 import java.io.IOException;
21 import java.io.OutputStream;
23 import android.content.Context;
24 import android.content.res.Resources;
25 import android.graphics.Bitmap;
26 import android.graphics.BitmapFactory;
27 import android.graphics.Canvas;
28 import android.graphics.ColorMatrixColorFilter;
29 import android.graphics.Paint;
30 import android.graphics.PorterDuff;
31 import android.graphics.PorterDuffXfermode;
32 import android.graphics.Rect;
33 import android.graphics.Typeface;
34 import android.os.Bundle;
35 import android.os.Handler;
36 import android.text.ClipboardManager;
37 import android.util.AttributeSet;
38 import android.util.DisplayMetrics;
39 import android.util.Log;
40 import android.view.GestureDetector;
41 import android.view.KeyEvent;
42 import android.view.MotionEvent;
43 import android.view.View;
44 import android.view.inputmethod.BaseInputConnection;
45 import android.view.inputmethod.CompletionInfo;
46 import android.view.inputmethod.CorrectionInfo;
47 import android.view.inputmethod.EditorInfo;
48 import android.view.inputmethod.ExtractedText;
49 import android.view.inputmethod.ExtractedTextRequest;
50 import android.view.inputmethod.InputConnection;
52 import jackpal.androidterm.model.TextRenderer;
53 import jackpal.androidterm.model.UpdateCallback;
54 import jackpal.androidterm.session.TerminalEmulator;
55 import jackpal.androidterm.session.TermSession;
56 import jackpal.androidterm.session.TranscriptScreen;
57 import jackpal.androidterm.util.TermSettings;
60 * A view on a transcript and a terminal emulator. Displays the text of the
61 * transcript and the current cursor position of the terminal emulator.
63 public class EmulatorView extends View implements GestureDetector.OnGestureListener {
65 private final String TAG = "EmulatorView";
66 private final boolean LOG_KEY_EVENTS = TermDebug.DEBUG && false;
68 private TermSettings mSettings;
69 private TermViewFlipper mViewFlipper;
72 * We defer some initialization until we have been layed out in the view
73 * hierarchy. The boolean tracks when we know what our size is.
75 private boolean mKnownSize;
77 private int mVisibleWidth;
78 private int mVisibleHeight;
79 private Rect mVisibleRect = new Rect();
81 private TermSession mTermSession;
84 * Our transcript. Contains the screen and the transcript.
86 private TranscriptScreen mTranscriptScreen;
89 * Total width of each character, in pixels
91 private int mCharacterWidth;
94 * Total height of each character, in pixels
96 private int mCharacterHeight;
101 private TextRenderer mTextRenderer;
104 * Text size. Zero means 4 x 8 font.
106 private int mTextSize;
108 private int mCursorStyle;
109 private int mCursorBlink;
114 private int mForeground;
119 private int mBackground;
122 * Used to paint the cursor
124 private Paint mCursorPaint;
126 private Paint mBackgroundPaint;
128 private boolean mUseCookedIme;
131 * Our terminal emulator. We use this to get the current cursor position.
133 private TerminalEmulator mEmulator;
136 * The number of rows of text to display.
141 * The number of columns of text to display.
143 private int mColumns;
146 * The number of columns that are visible on the display.
149 private int mVisibleColumns;
152 * The top row of text to display. Ranges from -activeTranscriptRows to 0
156 private int mLeftColumn;
159 * Used to receive data from the remote process.
161 private FileOutputStream mTermOut;
163 private static final int SCREEN_CHECK_PERIOD = 1000;
164 private static final int CURSOR_BLINK_PERIOD = 1000;
166 private boolean mCursorVisible = true;
168 private boolean mIsSelectingText = false;
171 private float mDensity;
173 private float mScaledDensity;
174 private static final int SELECT_TEXT_OFFSET_Y = -40;
175 private int mSelXAnchor = -1;
176 private int mSelYAnchor = -1;
177 private int mSelX1 = -1;
178 private int mSelY1 = -1;
179 private int mSelX2 = -1;
180 private int mSelY2 = -1;
183 * Used to poll if the view has changed size. Wish there was a better way to do this.
185 private Runnable mCheckSize = new Runnable() {
189 mHandler.postDelayed(this, SCREEN_CHECK_PERIOD);
193 private Runnable mBlinkCursor = new Runnable() {
195 if (mCursorBlink != 0) {
196 mCursorVisible = ! mCursorVisible;
197 mHandler.postDelayed(this, CURSOR_BLINK_PERIOD);
199 mCursorVisible = true;
201 // Perhaps just invalidate the character with the cursor.
206 private GestureDetector mGestureDetector;
207 private float mScrollRemainder;
208 private TermKeyListener mKeyListener;
210 private String mImeBuffer = "";
213 * Our message handler class. Implements a periodic callback.
215 private final Handler mHandler = new Handler();
218 * Called by the TermSession when the contents of the view need updating
220 private UpdateCallback mUpdateNotify = new UpdateCallback() {
222 public void onUpdate() {
223 if ( mIsSelectingText ) {
224 int rowShift = mEmulator.getScrollCounter();
227 mSelYAnchor -= rowShift;
229 mEmulator.clearScrollCounter();
230 ensureCursorVisible();
235 public UpdateCallback getUpdateCallback() {
236 return mUpdateNotify;
239 public EmulatorView(Context context, TermSession session, TermViewFlipper viewFlipper, DisplayMetrics metrics) {
241 commonConstructor(session, viewFlipper);
245 public void setDensity(DisplayMetrics metrics) {
246 mDensity = metrics.density;
247 mScaledDensity = metrics.scaledDensity;
250 public void onResume() {
252 mHandler.postDelayed(mCheckSize, SCREEN_CHECK_PERIOD);
253 if (mCursorBlink != 0) {
254 mHandler.postDelayed(mBlinkCursor, CURSOR_BLINK_PERIOD);
258 public void onPause() {
259 mHandler.removeCallbacks(mCheckSize);
260 if (mCursorBlink != 0) {
261 mHandler.removeCallbacks(mBlinkCursor);
265 public void updatePrefs(TermSettings settings) {
266 mSettings = settings;
267 setTextSize((int) (mSettings.getFontSize() * mDensity));
268 setCursorStyle(mSettings.getCursorStyle(), mSettings.getCursorBlink());
269 setUseCookedIME(mSettings.useCookedIME());
273 public void setColors() {
274 int[] scheme = mSettings.getColorScheme();
275 mForeground = scheme[0];
276 mBackground = scheme[1];
280 public void resetTerminal() {
286 public boolean onCheckIsTextEditor() {
291 public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
292 outAttrs.inputType = mUseCookedIme ?
293 EditorInfo.TYPE_CLASS_TEXT :
294 EditorInfo.TYPE_NULL;
295 return new InputConnection() {
296 private boolean mInBatchEdit;
298 * Used to handle composing text requests
301 private int mComposingTextStart;
302 private int mComposingTextEnd;
303 private int mSelectedTextStart;
304 private int mSelectedTextEnd;
306 private void sendChar(int c) {
309 } catch (IOException ex) {
314 private void sendText(CharSequence text) {
315 int n = text.length();
317 for(int i = 0; i < n; i++) {
318 char c = text.charAt(i);
322 } catch (IOException e) {
323 Log.e(TAG, "error writing ", e);
327 private void mapAndSend(int c) throws IOException {
328 int result = mKeyListener.mapControlChar(c);
329 if (result < TermKeyListener.KEYCODE_OFFSET) {
330 mTermOut.write(result);
332 mKeyListener.handleKeyCode(result - TermKeyListener.KEYCODE_OFFSET, mTermOut, getKeypadApplicationMode());
336 public boolean beginBatchEdit() {
337 if (TermDebug.LOG_IME) {
338 Log.w(TAG, "beginBatchEdit");
342 mComposingTextStart = 0;
343 mComposingTextEnd = 0;
348 public boolean clearMetaKeyStates(int arg0) {
349 if (TermDebug.LOG_IME) {
350 Log.w(TAG, "clearMetaKeyStates " + arg0);
355 public boolean commitCompletion(CompletionInfo arg0) {
356 if (TermDebug.LOG_IME) {
357 Log.w(TAG, "commitCompletion " + arg0);
362 public boolean endBatchEdit() {
363 if (TermDebug.LOG_IME) {
364 Log.w(TAG, "endBatchEdit");
366 mInBatchEdit = false;
370 public boolean finishComposingText() {
371 if (TermDebug.LOG_IME) {
372 Log.w(TAG, "finishComposingText");
374 sendText(mImeBuffer);
376 mComposingTextStart = 0;
377 mComposingTextEnd = 0;
382 public int getCursorCapsMode(int arg0) {
383 if (TermDebug.LOG_IME) {
384 Log.w(TAG, "getCursorCapsMode(" + arg0 + ")");
389 public ExtractedText getExtractedText(ExtractedTextRequest arg0,
391 if (TermDebug.LOG_IME) {
392 Log.w(TAG, "getExtractedText" + arg0 + "," + arg1);
397 public CharSequence getTextAfterCursor(int n, int flags) {
398 if (TermDebug.LOG_IME) {
399 Log.w(TAG, "getTextAfterCursor(" + n + "," + flags + ")");
401 int len = Math.min(n, mImeBuffer.length() - mCursor);
402 if (len <= 0 || mCursor < 0 || mCursor >= mImeBuffer.length()) {
405 return mImeBuffer.substring(mCursor, mCursor + len);
408 public CharSequence getTextBeforeCursor(int n, int flags) {
409 if (TermDebug.LOG_IME) {
410 Log.w(TAG, "getTextBeforeCursor(" + n + "," + flags + ")");
412 int len = Math.min(n, mCursor);
413 if (len <= 0 || mCursor < 0 || mCursor >= mImeBuffer.length()) {
416 return mImeBuffer.substring(mCursor-len, mCursor);
419 public boolean performContextMenuAction(int arg0) {
420 if (TermDebug.LOG_IME) {
421 Log.w(TAG, "performContextMenuAction" + arg0);
426 public boolean performPrivateCommand(String arg0, Bundle arg1) {
427 if (TermDebug.LOG_IME) {
428 Log.w(TAG, "performPrivateCommand" + arg0 + "," + arg1);
433 public boolean reportFullscreenMode(boolean arg0) {
434 if (TermDebug.LOG_IME) {
435 Log.w(TAG, "reportFullscreenMode" + arg0);
440 public boolean commitCorrection (CorrectionInfo correctionInfo) {
441 if (TermDebug.LOG_IME) {
442 Log.w(TAG, "commitCorrection");
447 public boolean commitText(CharSequence text, int newCursorPosition) {
448 if (TermDebug.LOG_IME) {
449 Log.w(TAG, "commitText(\"" + text + "\", " + newCursorPosition + ")");
451 clearComposingText();
458 private void clearComposingText() {
459 setImeBuffer(mImeBuffer.substring(0, mComposingTextStart) +
460 mImeBuffer.substring(mComposingTextEnd));
461 if (mCursor < mComposingTextStart) {
463 } else if (mCursor < mComposingTextEnd) {
464 mCursor = mComposingTextStart;
466 mCursor -= mComposingTextEnd - mComposingTextStart;
468 mComposingTextEnd = mComposingTextStart = 0;
471 public boolean deleteSurroundingText(int leftLength, int rightLength) {
472 if (TermDebug.LOG_IME) {
473 Log.w(TAG, "deleteSurroundingText(" + leftLength +
474 "," + rightLength + ")");
476 if (leftLength > 0) {
477 for (int i = 0; i < leftLength; i++) {
479 new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DEL));
481 } else if ((leftLength == 0) && (rightLength == 0)) {
482 // Delete key held down / repeating
484 new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DEL));
486 // TODO: handle forward deletes.
490 public boolean performEditorAction(int actionCode) {
491 if (TermDebug.LOG_IME) {
492 Log.w(TAG, "performEditorAction(" + actionCode + ")");
494 if (actionCode == EditorInfo.IME_ACTION_UNSPECIFIED) {
495 // The "return" key has been pressed on the IME.
501 public boolean sendKeyEvent(KeyEvent event) {
502 if (TermDebug.LOG_IME) {
503 Log.w(TAG, "sendKeyEvent(" + event + ")");
505 // Some keys are sent here rather than to commitText.
506 // In particular, del and the digit keys are sent here.
507 // (And I have reports that the HTC Magic also sends Return here.)
508 // As a bit of defensive programming, handle every key.
509 dispatchKeyEvent(event);
513 public boolean setComposingText(CharSequence text, int newCursorPosition) {
514 if (TermDebug.LOG_IME) {
515 Log.w(TAG, "setComposingText(\"" + text + "\", " + newCursorPosition + ")");
517 setImeBuffer(mImeBuffer.substring(0, mComposingTextStart) +
518 text + mImeBuffer.substring(mComposingTextEnd));
519 mComposingTextEnd = mComposingTextStart + text.length();
520 mCursor = newCursorPosition > 0 ? mComposingTextEnd + newCursorPosition - 1
521 : mComposingTextStart - newCursorPosition;
525 public boolean setSelection(int start, int end) {
526 if (TermDebug.LOG_IME) {
527 Log.w(TAG, "setSelection" + start + "," + end);
529 int length = mImeBuffer.length();
530 if (start == end && start > 0 && start < length) {
531 mSelectedTextStart = mSelectedTextEnd = 0;
533 } else if (start < end && start > 0 && end < length) {
534 mSelectedTextStart = start;
535 mSelectedTextEnd = end;
541 public boolean setComposingRegion(int start, int end) {
542 if (TermDebug.LOG_IME) {
543 Log.w(TAG, "setComposingRegion " + start + "," + end);
545 if (start < end && start > 0 && end < mImeBuffer.length()) {
546 clearComposingText();
547 mComposingTextStart = start;
548 mComposingTextEnd = end;
553 public CharSequence getSelectedText(int flags) {
554 if (TermDebug.LOG_IME) {
555 Log.w(TAG, "getSelectedText " + flags);
557 return mImeBuffer.substring(mSelectedTextStart, mSelectedTextEnd+1);
563 private void setImeBuffer(String buffer) {
564 if (!buffer.equals(mImeBuffer)) {
570 public boolean getKeypadApplicationMode() {
571 return mEmulator.getKeypadApplicationMode();
574 private void commonConstructor(TermSession session, TermViewFlipper viewFlipper) {
575 mTextRenderer = null;
576 mCursorPaint = new Paint();
577 mCursorPaint.setARGB(255,128,128,128);
578 mBackgroundPaint = new Paint();
581 mGestureDetector = new GestureDetector(this);
582 // mGestureDetector.setIsLongpressEnabled(false);
583 setVerticalScrollBarEnabled(true);
585 setFocusableInTouchMode(true);
587 initialize(session, viewFlipper);
591 protected int computeVerticalScrollRange() {
592 return mTranscriptScreen.getActiveRows();
596 protected int computeVerticalScrollExtent() {
601 protected int computeVerticalScrollOffset() {
602 return mTranscriptScreen.getActiveRows() + mTopRow - mRows;
606 * Call this to initialize the view.
608 * @param session The terminal session this view will be displaying
610 private void initialize(TermSession session, TermViewFlipper viewFlipper) {
611 mTermSession = session;
612 mTranscriptScreen = session.getTranscriptScreen();
613 mEmulator = session.getEmulator();
614 mTermOut = session.getTermOut();
616 mViewFlipper = viewFlipper;
618 mKeyListener = new TermKeyListener();
620 mForeground = TermSettings.WHITE;
621 mBackground = TermSettings.BLACK;
627 public TermSession getTermSession() {
632 * Page the terminal view (scroll it up or down by delta screenfulls.)
634 * @param delta the number of screens to scroll. Positive means scroll down,
635 * negative means scroll up.
637 public void page(int delta) {
639 Math.min(0, Math.max(-(mTranscriptScreen
640 .getActiveTranscriptRows()), mTopRow + mRows * delta));
645 * Page the terminal view horizontally.
647 * @param deltaColumns the number of columns to scroll. Positive scrolls to
650 public void pageHorizontal(int deltaColumns) {
652 Math.max(0, Math.min(mLeftColumn + deltaColumns, mColumns
658 * Sets the text size, which in turn sets the number of rows and columns
660 * @param fontSize the new font size, in pixels.
662 public void setTextSize(int fontSize) {
663 mTextSize = fontSize;
667 public void setCursorStyle(int style, int blink) {
668 mCursorStyle = style;
669 if (blink != 0 && mCursorBlink == 0) {
670 mHandler.postDelayed(mBlinkCursor, CURSOR_BLINK_PERIOD);
671 } else if (blink == 0 && mCursorBlink != 0) {
672 mHandler.removeCallbacks(mBlinkCursor);
674 mCursorBlink = blink;
677 public void setUseCookedIME(boolean useCookedIME) {
678 mUseCookedIme = useCookedIME;
681 // Begin GestureDetector.OnGestureListener methods
683 public boolean onSingleTapUp(MotionEvent e) {
687 public void onLongPress(MotionEvent e) {
691 public boolean onScroll(MotionEvent e1, MotionEvent e2,
692 float distanceX, float distanceY) {
693 distanceY += mScrollRemainder;
694 int deltaRows = (int) (distanceY / mCharacterHeight);
695 mScrollRemainder = distanceY - deltaRows * mCharacterHeight;
697 Math.min(0, Math.max(-(mTranscriptScreen
698 .getActiveTranscriptRows()), mTopRow + deltaRows));
704 public void onSingleTapConfirmed(MotionEvent e) {
707 public boolean onJumpTapDown(MotionEvent e1, MotionEvent e2) {
714 public boolean onJumpTapUp(MotionEvent e1, MotionEvent e2) {
716 mTopRow = -mTranscriptScreen.getActiveTranscriptRows();
721 public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
723 if (Math.abs(velocityX) > Math.abs(velocityY)) {
724 // Assume user wanted side to side movement
726 // Left to right swipe -- previous window
727 mViewFlipper.showPrevious();
729 // Right to left swipe -- next window
730 mViewFlipper.showNext();
733 // TODO: add animation man's (non animated) fling
734 mScrollRemainder = 0.0f;
735 onScroll(e1, e2, 2 * velocityX, -2 * velocityY);
740 public void onShowPress(MotionEvent e) {
743 public boolean onDown(MotionEvent e) {
744 mScrollRemainder = 0.0f;
748 // End GestureDetector.OnGestureListener methods
750 @Override public boolean onTouchEvent(MotionEvent ev) {
751 if (mIsSelectingText) {
752 return onTouchEventWhileSelectingText(ev);
754 return mGestureDetector.onTouchEvent(ev);
758 private boolean onTouchEventWhileSelectingText(MotionEvent ev) {
759 int action = ev.getAction();
760 int cx = (int)(ev.getX() / mCharacterWidth);
762 (int)((ev.getY() + SELECT_TEXT_OFFSET_Y * mScaledDensity)
763 / mCharacterHeight) + mTopRow);
765 case MotionEvent.ACTION_DOWN:
773 case MotionEvent.ACTION_MOVE:
774 case MotionEvent.ACTION_UP:
775 int minx = Math.min(mSelXAnchor, cx);
776 int maxx = Math.max(mSelXAnchor, cx);
777 int miny = Math.min(mSelYAnchor, cy);
778 int maxy = Math.max(mSelYAnchor, cy);
783 if (action == MotionEvent.ACTION_UP) {
784 ClipboardManager clip = (ClipboardManager)
785 getContext().getApplicationContext()
786 .getSystemService(Context.CLIPBOARD_SERVICE);
787 clip.setText(getSelectedText().trim());
788 toggleSelectingText();
793 toggleSelectingText();
801 public boolean onKeyDown(int keyCode, KeyEvent event) {
802 if (LOG_KEY_EVENTS) {
803 Log.w(TAG, "onKeyDown " + keyCode);
805 if (handleControlKey(keyCode, true)) {
807 } else if (handleFnKey(keyCode, true)) {
809 } else if (isSystemKey(keyCode, event)) {
810 // Don't intercept the system keys
811 return super.onKeyDown(keyCode, event);
814 // Translate the keyCode into an ASCII character.
817 mKeyListener.keyDown(keyCode, event, mTermOut,
818 getKeypadApplicationMode());
819 } catch (IOException e) {
820 // Ignore I/O exceptions
826 public boolean onKeyUp(int keyCode, KeyEvent event) {
827 if (LOG_KEY_EVENTS) {
828 Log.w(TAG, "onKeyUp " + keyCode);
830 if (handleControlKey(keyCode, false)) {
832 } else if (handleFnKey(keyCode, false)) {
834 } else if (isSystemKey(keyCode, event)) {
835 // Don't intercept the system keys
836 return super.onKeyUp(keyCode, event);
839 mKeyListener.keyUp(keyCode);
844 private boolean handleControlKey(int keyCode, boolean down) {
845 if (keyCode == mSettings.getControlKeyCode()) {
846 if (LOG_KEY_EVENTS) {
847 Log.w(TAG, "handleControlKey " + keyCode);
849 mKeyListener.handleControlKey(down);
855 private boolean handleFnKey(int keyCode, boolean down) {
856 if (keyCode == mSettings.getFnKeyCode()) {
857 if (LOG_KEY_EVENTS) {
858 Log.w(TAG, "handleFnKey " + keyCode);
860 mKeyListener.handleFnKey(down);
866 private boolean isSystemKey(int keyCode, KeyEvent event) {
867 return event.isSystem();
870 private void updateText() {
872 mTextRenderer = new PaintRenderer(mTextSize, mForeground,
876 mTextRenderer = new Bitmap4x8FontRenderer(getResources(),
877 mForeground, mBackground);
879 mBackgroundPaint.setColor(mBackground);
880 mCharacterWidth = mTextRenderer.getCharacterWidth();
881 mCharacterHeight = mTextRenderer.getCharacterHeight();
887 protected void onSizeChanged(int w, int h, int oldw, int oldh) {
888 boolean oldKnownSize = mKnownSize;
895 private void updateSize(int w, int h) {
896 mColumns = Math.max(1, w / mCharacterWidth);
897 mRows = Math.max(1, h / mCharacterHeight);
898 mVisibleColumns = mVisibleWidth / mCharacterWidth;
900 mTermSession.updateSize(mColumns, mRows);
909 public void updateSize(boolean force) {
911 getWindowVisibleDisplayFrame(mVisibleRect);
912 int w = mVisibleRect.width();
913 int h = mVisibleRect.height();
914 // Log.w("Term", "(" + w + ", " + h + ")");
915 if (force || w != mVisibleWidth || h != mVisibleHeight) {
918 updateSize(mVisibleWidth, mVisibleHeight);
924 protected void onDraw(Canvas canvas) {
928 canvas.drawRect(0, 0, w, h, mBackgroundPaint);
929 float x = -mLeftColumn * mCharacterWidth;
930 float y = mCharacterHeight;
931 int endLine = mTopRow + mRows;
932 int cx = mEmulator.getCursorCol();
933 int cy = mEmulator.getCursorRow();
934 for (int i = mTopRow; i < endLine; i++) {
936 if (i == cy && mCursorVisible) {
941 if ( i >= mSelY1 && i <= mSelY2 ) {
951 mTranscriptScreen.drawText(i, canvas, x, y, mTextRenderer, cursorX, selx1, selx2, mImeBuffer);
952 y += mCharacterHeight;
956 private void ensureCursorVisible() {
958 if (mVisibleColumns > 0) {
959 int cx = mEmulator.getCursorCol();
960 int visibleCursorX = mEmulator.getCursorCol() - mLeftColumn;
961 if (visibleCursorX < 0) {
963 } else if (visibleCursorX >= mVisibleColumns) {
964 mLeftColumn = (cx - mVisibleColumns) + 1;
969 public void toggleSelectingText() {
970 mIsSelectingText = ! mIsSelectingText;
971 setVerticalScrollBarEnabled( ! mIsSelectingText );
972 if ( ! mIsSelectingText ) {
980 public boolean getSelectingText() {
981 return mIsSelectingText;
984 public String getSelectedText() {
985 return mEmulator.getSelectedText(mSelX1, mSelY1, mSelX2, mSelY2);
989 abstract class BaseTextRenderer implements TextRenderer {
990 protected int[] mForePaint = {
994 0xffffff00, // yellow
996 0xffff00ff, // magenta
998 0xffffffff // white -- is overridden by constructor
1000 protected int[] mBackPaint = {
1001 0xff000000, // Black -- is overridden by constructor
1003 0xff00cc00, // green
1004 0xffcccc00, // yellow
1006 0xffff00cc, // magenta
1010 protected final static int mCursorPaint = 0xff808080;
1012 public BaseTextRenderer(int forePaintColor, int backPaintColor) {
1013 mForePaint[7] = forePaintColor;
1014 mBackPaint[0] = backPaintColor;
1019 class Bitmap4x8FontRenderer extends BaseTextRenderer {
1020 private final static int kCharacterWidth = 4;
1021 private final static int kCharacterHeight = 8;
1022 private Bitmap mFont;
1023 private int mCurrentForeColor;
1024 private int mCurrentBackColor;
1025 private float[] mColorMatrix;
1026 private Paint mPaint;
1027 private static final float BYTE_SCALE = 1.0f / 255.0f;
1029 public Bitmap4x8FontRenderer(Resources resources,
1030 int forePaintColor, int backPaintColor) {
1031 super(forePaintColor, backPaintColor);
1032 mFont = BitmapFactory.decodeResource(resources,
1033 R.drawable.atari_small);
1034 mPaint = new Paint();
1035 mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
1038 public int getCharacterWidth() {
1039 return kCharacterWidth;
1042 public int getCharacterHeight() {
1043 return kCharacterHeight;
1046 public void drawTextRun(Canvas canvas, float x, float y,
1047 int lineOffset, char[] text, int index, int count,
1048 boolean cursor, int foreColor, int backColor) {
1049 setColorMatrix(mForePaint[foreColor & 7],
1050 cursor ? mCursorPaint : mBackPaint[backColor & 7]);
1051 int destX = (int) x + kCharacterWidth * lineOffset;
1052 int destY = (int) y;
1053 Rect srcRect = new Rect();
1054 Rect destRect = new Rect();
1055 destRect.top = (destY - kCharacterHeight);
1056 destRect.bottom = destY;
1057 for(int i = 0; i < count; i++) {
1058 char c = text[i + index];
1059 if ((cursor || (c != 32)) && (c < 128)) {
1061 int cellY = (c >> 5) & 3;
1062 int srcX = cellX * kCharacterWidth;
1063 int srcY = cellY * kCharacterHeight;
1064 srcRect.set(srcX, srcY,
1065 srcX + kCharacterWidth, srcY + kCharacterHeight);
1066 destRect.left = destX;
1067 destRect.right = destX + kCharacterWidth;
1068 canvas.drawBitmap(mFont, srcRect, destRect, mPaint);
1070 destX += kCharacterWidth;
1074 private void setColorMatrix(int foreColor, int backColor) {
1075 if ((foreColor != mCurrentForeColor)
1076 || (backColor != mCurrentBackColor)
1077 || (mColorMatrix == null)) {
1078 mCurrentForeColor = foreColor;
1079 mCurrentBackColor = backColor;
1080 if (mColorMatrix == null) {
1081 mColorMatrix = new float[20];
1082 mColorMatrix[18] = 1.0f; // Just copy Alpha
1084 for (int component = 0; component < 3; component++) {
1085 int rightShift = (2 - component) << 3;
1086 int fore = 0xff & (foreColor >> rightShift);
1087 int back = 0xff & (backColor >> rightShift);
1088 int delta = back - fore;
1089 mColorMatrix[component * 6] = delta * BYTE_SCALE;
1090 mColorMatrix[component * 5 + 4] = fore;
1092 mPaint.setColorFilter(new ColorMatrixColorFilter(mColorMatrix));
1097 class PaintRenderer extends BaseTextRenderer {
1098 public PaintRenderer(int fontSize, int forePaintColor, int backPaintColor) {
1099 super(forePaintColor, backPaintColor);
1100 mTextPaint = new Paint();
1101 mTextPaint.setTypeface(Typeface.MONOSPACE);
1102 mTextPaint.setAntiAlias(true);
1103 mTextPaint.setTextSize(fontSize);
1105 mCharHeight = (int) Math.ceil(mTextPaint.getFontSpacing());
1106 mCharAscent = (int) Math.ceil(mTextPaint.ascent());
1107 mCharDescent = mCharHeight + mCharAscent;
1108 mCharWidth = (int) mTextPaint.measureText(EXAMPLE_CHAR, 0, 1);
1111 public void drawTextRun(Canvas canvas, float x, float y, int lineOffset,
1112 char[] text, int index, int count,
1113 boolean cursor, int foreColor, int backColor) {
1115 mTextPaint.setColor(mCursorPaint);
1117 mTextPaint.setColor(mBackPaint[backColor & 0x7]);
1119 float left = x + lineOffset * mCharWidth;
1120 canvas.drawRect(left, y + mCharAscent,
1121 left + count * mCharWidth, y + mCharDescent,
1123 boolean bold = ( foreColor & 0x8 ) != 0;
1124 boolean underline = (backColor & 0x8) != 0;
1126 mTextPaint.setFakeBoldText(true);
1129 mTextPaint.setUnderlineText(true);
1131 mTextPaint.setColor(mForePaint[foreColor & 0x7]);
1132 canvas.drawText(text, index, count, left, y, mTextPaint);
1134 mTextPaint.setFakeBoldText(false);
1137 mTextPaint.setUnderlineText(false);
1141 public int getCharacterHeight() {
1145 public int getCharacterWidth() {
1150 private Paint mTextPaint;
1151 private int mCharWidth;
1152 private int mCharHeight;
1153 private int mCharAscent;
1154 private int mCharDescent;
1155 private static final char[] EXAMPLE_CHAR = {'X'};
1159 * An ASCII key listener. Supports control characters and escape. Keeps track of
1160 * the current state of the alt, shift, and control keys.
1162 class TermKeyListener {
1164 * Android key codes that are defined in the Android 2.3 API.
1165 * We want to recognize these codes, because they will be sent to our
1166 * app when we run on Android 2.3 systems.
1167 * But we don't want to accidentally use 2.3-specific APIs.
1168 * So we compile against the Android 1.6 APIs, and have a copy of the codes here.
1171 /** Key code constant: Unknown key code. */
1172 public static final int KEYCODE_UNKNOWN = 0;
1173 /** Key code constant: Soft Left key.
1174 * Usually situated below the display on phones and used as a multi-function
1175 * feature key for selecting a software defined function shown on the bottom left
1176 * of the display. */
1177 public static final int KEYCODE_SOFT_LEFT = 1;
1178 /** Key code constant: Soft Right key.
1179 * Usually situated below the display on phones and used as a multi-function
1180 * feature key for selecting a software defined function shown on the bottom right
1181 * of the display. */
1182 public static final int KEYCODE_SOFT_RIGHT = 2;
1183 /** Key code constant: Home key.
1184 * This key is handled by the framework and is never delivered to applications. */
1185 public static final int KEYCODE_HOME = 3;
1186 /** Key code constant: Back key. */
1187 public static final int KEYCODE_BACK = 4;
1188 /** Key code constant: Call key. */
1189 public static final int KEYCODE_CALL = 5;
1190 /** Key code constant: End Call key. */
1191 public static final int KEYCODE_ENDCALL = 6;
1192 /** Key code constant: '0' key. */
1193 public static final int KEYCODE_0 = 7;
1194 /** Key code constant: '1' key. */
1195 public static final int KEYCODE_1 = 8;
1196 /** Key code constant: '2' key. */
1197 public static final int KEYCODE_2 = 9;
1198 /** Key code constant: '3' key. */
1199 public static final int KEYCODE_3 = 10;
1200 /** Key code constant: '4' key. */
1201 public static final int KEYCODE_4 = 11;
1202 /** Key code constant: '5' key. */
1203 public static final int KEYCODE_5 = 12;
1204 /** Key code constant: '6' key. */
1205 public static final int KEYCODE_6 = 13;
1206 /** Key code constant: '7' key. */
1207 public static final int KEYCODE_7 = 14;
1208 /** Key code constant: '8' key. */
1209 public static final int KEYCODE_8 = 15;
1210 /** Key code constant: '9' key. */
1211 public static final int KEYCODE_9 = 16;
1212 /** Key code constant: '*' key. */
1213 public static final int KEYCODE_STAR = 17;
1214 /** Key code constant: '#' key. */
1215 public static final int KEYCODE_POUND = 18;
1216 /** Key code constant: Directional Pad Up key.
1217 * May also be synthesized from trackball motions. */
1218 public static final int KEYCODE_DPAD_UP = 19;
1219 /** Key code constant: Directional Pad Down key.
1220 * May also be synthesized from trackball motions. */
1221 public static final int KEYCODE_DPAD_DOWN = 20;
1222 /** Key code constant: Directional Pad Left key.
1223 * May also be synthesized from trackball motions. */
1224 public static final int KEYCODE_DPAD_LEFT = 21;
1225 /** Key code constant: Directional Pad Right key.
1226 * May also be synthesized from trackball motions. */
1227 public static final int KEYCODE_DPAD_RIGHT = 22;
1228 /** Key code constant: Directional Pad Center key.
1229 * May also be synthesized from trackball motions. */
1230 public static final int KEYCODE_DPAD_CENTER = 23;
1231 /** Key code constant: Volume Up key.
1232 * Adjusts the speaker volume up. */
1233 public static final int KEYCODE_VOLUME_UP = 24;
1234 /** Key code constant: Volume Down key.
1235 * Adjusts the speaker volume down. */
1236 public static final int KEYCODE_VOLUME_DOWN = 25;
1237 /** Key code constant: Power key. */
1238 public static final int KEYCODE_POWER = 26;
1239 /** Key code constant: Camera key.
1240 * Used to launch a camera application or take pictures. */
1241 public static final int KEYCODE_CAMERA = 27;
1242 /** Key code constant: Clear key. */
1243 public static final int KEYCODE_CLEAR = 28;
1244 /** Key code constant: 'A' key. */
1245 public static final int KEYCODE_A = 29;
1246 /** Key code constant: 'B' key. */
1247 public static final int KEYCODE_B = 30;
1248 /** Key code constant: 'C' key. */
1249 public static final int KEYCODE_C = 31;
1250 /** Key code constant: 'D' key. */
1251 public static final int KEYCODE_D = 32;
1252 /** Key code constant: 'E' key. */
1253 public static final int KEYCODE_E = 33;
1254 /** Key code constant: 'F' key. */
1255 public static final int KEYCODE_F = 34;
1256 /** Key code constant: 'G' key. */
1257 public static final int KEYCODE_G = 35;
1258 /** Key code constant: 'H' key. */
1259 public static final int KEYCODE_H = 36;
1260 /** Key code constant: 'I' key. */
1261 public static final int KEYCODE_I = 37;
1262 /** Key code constant: 'J' key. */
1263 public static final int KEYCODE_J = 38;
1264 /** Key code constant: 'K' key. */
1265 public static final int KEYCODE_K = 39;
1266 /** Key code constant: 'L' key. */
1267 public static final int KEYCODE_L = 40;
1268 /** Key code constant: 'M' key. */
1269 public static final int KEYCODE_M = 41;
1270 /** Key code constant: 'N' key. */
1271 public static final int KEYCODE_N = 42;
1272 /** Key code constant: 'O' key. */
1273 public static final int KEYCODE_O = 43;
1274 /** Key code constant: 'P' key. */
1275 public static final int KEYCODE_P = 44;
1276 /** Key code constant: 'Q' key. */
1277 public static final int KEYCODE_Q = 45;
1278 /** Key code constant: 'R' key. */
1279 public static final int KEYCODE_R = 46;
1280 /** Key code constant: 'S' key. */
1281 public static final int KEYCODE_S = 47;
1282 /** Key code constant: 'T' key. */
1283 public static final int KEYCODE_T = 48;
1284 /** Key code constant: 'U' key. */
1285 public static final int KEYCODE_U = 49;
1286 /** Key code constant: 'V' key. */
1287 public static final int KEYCODE_V = 50;
1288 /** Key code constant: 'W' key. */
1289 public static final int KEYCODE_W = 51;
1290 /** Key code constant: 'X' key. */
1291 public static final int KEYCODE_X = 52;
1292 /** Key code constant: 'Y' key. */
1293 public static final int KEYCODE_Y = 53;
1294 /** Key code constant: 'Z' key. */
1295 public static final int KEYCODE_Z = 54;
1296 /** Key code constant: ',' key. */
1297 public static final int KEYCODE_COMMA = 55;
1298 /** Key code constant: '.' key. */
1299 public static final int KEYCODE_PERIOD = 56;
1300 /** Key code constant: Left Alt modifier key. */
1301 public static final int KEYCODE_ALT_LEFT = 57;
1302 /** Key code constant: Right Alt modifier key. */
1303 public static final int KEYCODE_ALT_RIGHT = 58;
1304 /** Key code constant: Left Shift modifier key. */
1305 public static final int KEYCODE_SHIFT_LEFT = 59;
1306 /** Key code constant: Right Shift modifier key. */
1307 public static final int KEYCODE_SHIFT_RIGHT = 60;
1308 /** Key code constant: Tab key. */
1309 public static final int KEYCODE_TAB = 61;
1310 /** Key code constant: Space key. */
1311 public static final int KEYCODE_SPACE = 62;
1312 /** Key code constant: Symbol modifier key.
1313 * Used to enter alternate symbols. */
1314 public static final int KEYCODE_SYM = 63;
1315 /** Key code constant: Explorer special function key.
1316 * Used to launch a browser application. */
1317 public static final int KEYCODE_EXPLORER = 64;
1318 /** Key code constant: Envelope special function key.
1319 * Used to launch a mail application. */
1320 public static final int KEYCODE_ENVELOPE = 65;
1321 /** Key code constant: Enter key. */
1322 public static final int KEYCODE_ENTER = 66;
1323 /** Key code constant: Backspace key.
1324 * Deletes characters before the insertion point, unlike {@link #KEYCODE_FORWARD_DEL}. */
1325 public static final int KEYCODE_DEL = 67;
1326 /** Key code constant: '`' (backtick) key. */
1327 public static final int KEYCODE_GRAVE = 68;
1328 /** Key code constant: '-'. */
1329 public static final int KEYCODE_MINUS = 69;
1330 /** Key code constant: '=' key. */
1331 public static final int KEYCODE_EQUALS = 70;
1332 /** Key code constant: '[' key. */
1333 public static final int KEYCODE_LEFT_BRACKET = 71;
1334 /** Key code constant: ']' key. */
1335 public static final int KEYCODE_RIGHT_BRACKET = 72;
1336 /** Key code constant: '\' key. */
1337 public static final int KEYCODE_BACKSLASH = 73;
1338 /** Key code constant: ';' key. */
1339 public static final int KEYCODE_SEMICOLON = 74;
1340 /** Key code constant: ''' (apostrophe) key. */
1341 public static final int KEYCODE_APOSTROPHE = 75;
1342 /** Key code constant: '/' key. */
1343 public static final int KEYCODE_SLASH = 76;
1344 /** Key code constant: '@' key. */
1345 public static final int KEYCODE_AT = 77;
1346 /** Key code constant: Number modifier key.
1347 * Used to enter numeric symbols.
1348 * This key is not Num Lock; it is more like {@link #KEYCODE_ALT_LEFT} and is
1349 * interpreted as an ALT key by {@link android.text.method.MetaKeyKeyListener}. */
1350 public static final int KEYCODE_NUM = 78;
1351 /** Key code constant: Headset Hook key.
1352 * Used to hang up calls and stop media. */
1353 public static final int KEYCODE_HEADSETHOOK = 79;
1354 /** Key code constant: Camera Focus key.
1355 * Used to focus the camera. */
1356 public static final int KEYCODE_FOCUS = 80; // *Camera* focus
1357 /** Key code constant: '+' key. */
1358 public static final int KEYCODE_PLUS = 81;
1359 /** Key code constant: Menu key. */
1360 public static final int KEYCODE_MENU = 82;
1361 /** Key code constant: Notification key. */
1362 public static final int KEYCODE_NOTIFICATION = 83;
1363 /** Key code constant: Search key. */
1364 public static final int KEYCODE_SEARCH = 84;
1365 /** Key code constant: Play/Pause media key. */
1366 public static final int KEYCODE_MEDIA_PLAY_PAUSE= 85;
1367 /** Key code constant: Stop media key. */
1368 public static final int KEYCODE_MEDIA_STOP = 86;
1369 /** Key code constant: Play Next media key. */
1370 public static final int KEYCODE_MEDIA_NEXT = 87;
1371 /** Key code constant: Play Previous media key. */
1372 public static final int KEYCODE_MEDIA_PREVIOUS = 88;
1373 /** Key code constant: Rewind media key. */
1374 public static final int KEYCODE_MEDIA_REWIND = 89;
1375 /** Key code constant: Fast Forward media key. */
1376 public static final int KEYCODE_MEDIA_FAST_FORWARD = 90;
1377 /** Key code constant: Mute key.
1378 * Mutes the microphone, unlike {@link #KEYCODE_VOLUME_MUTE}. */
1379 public static final int KEYCODE_MUTE = 91;
1380 /** Key code constant: Page Up key. */
1381 public static final int KEYCODE_PAGE_UP = 92;
1382 /** Key code constant: Page Down key. */
1383 public static final int KEYCODE_PAGE_DOWN = 93;
1384 /** Key code constant: Picture Symbols modifier key.
1385 * Used to switch symbol sets (Emoji, Kao-moji). */
1386 public static final int KEYCODE_PICTSYMBOLS = 94; // switch symbol-sets (Emoji,Kao-moji)
1387 /** Key code constant: Switch Charset modifier key.
1388 * Used to switch character sets (Kanji, Katakana). */
1389 public static final int KEYCODE_SWITCH_CHARSET = 95; // switch char-sets (Kanji,Katakana)
1390 /** Key code constant: A Button key.
1391 * On a game controller, the A button should be either the button labeled A
1392 * or the first button on the upper row of controller buttons. */
1393 public static final int KEYCODE_BUTTON_A = 96;
1394 /** Key code constant: B Button key.
1395 * On a game controller, the B button should be either the button labeled B
1396 * or the second button on the upper row of controller buttons. */
1397 public static final int KEYCODE_BUTTON_B = 97;
1398 /** Key code constant: C Button key.
1399 * On a game controller, the C button should be either the button labeled C
1400 * or the third button on the upper row of controller buttons. */
1401 public static final int KEYCODE_BUTTON_C = 98;
1402 /** Key code constant: X Button key.
1403 * On a game controller, the X button should be either the button labeled X
1404 * or the first button on the lower row of controller buttons. */
1405 public static final int KEYCODE_BUTTON_X = 99;
1406 /** Key code constant: Y Button key.
1407 * On a game controller, the Y button should be either the button labeled Y
1408 * or the second button on the lower row of controller buttons. */
1409 public static final int KEYCODE_BUTTON_Y = 100;
1410 /** Key code constant: Z Button key.
1411 * On a game controller, the Z button should be either the button labeled Z
1412 * or the third button on the lower row of controller buttons. */
1413 public static final int KEYCODE_BUTTON_Z = 101;
1414 /** Key code constant: L1 Button key.
1415 * On a game controller, the L1 button should be either the button labeled L1 (or L)
1416 * or the top left trigger button. */
1417 public static final int KEYCODE_BUTTON_L1 = 102;
1418 /** Key code constant: R1 Button key.
1419 * On a game controller, the R1 button should be either the button labeled R1 (or R)
1420 * or the top right trigger button. */
1421 public static final int KEYCODE_BUTTON_R1 = 103;
1422 /** Key code constant: L2 Button key.
1423 * On a game controller, the L2 button should be either the button labeled L2
1424 * or the bottom left trigger button. */
1425 public static final int KEYCODE_BUTTON_L2 = 104;
1426 /** Key code constant: R2 Button key.
1427 * On a game controller, the R2 button should be either the button labeled R2
1428 * or the bottom right trigger button. */
1429 public static final int KEYCODE_BUTTON_R2 = 105;
1430 /** Key code constant: Left Thumb Button key.
1431 * On a game controller, the left thumb button indicates that the left (or only)
1432 * joystick is pressed. */
1433 public static final int KEYCODE_BUTTON_THUMBL = 106;
1434 /** Key code constant: Right Thumb Button key.
1435 * On a game controller, the right thumb button indicates that the right
1436 * joystick is pressed. */
1437 public static final int KEYCODE_BUTTON_THUMBR = 107;
1438 /** Key code constant: Start Button key.
1439 * On a game controller, the button labeled Start. */
1440 public static final int KEYCODE_BUTTON_START = 108;
1441 /** Key code constant: Select Button key.
1442 * On a game controller, the button labeled Select. */
1443 public static final int KEYCODE_BUTTON_SELECT = 109;
1444 /** Key code constant: Mode Button key.
1445 * On a game controller, the button labeled Mode. */
1446 public static final int KEYCODE_BUTTON_MODE = 110;
1447 /** Key code constant: Escape key. */
1448 public static final int KEYCODE_ESCAPE = 111;
1449 /** Key code constant: Forward Delete key.
1450 * Deletes characters ahead of the insertion point, unlike {@link #KEYCODE_DEL}. */
1451 public static final int KEYCODE_FORWARD_DEL = 112;
1452 /** Key code constant: Left Control modifier key. */
1453 public static final int KEYCODE_CTRL_LEFT = 113;
1454 /** Key code constant: Right Control modifier key. */
1455 public static final int KEYCODE_CTRL_RIGHT = 114;
1456 /** Key code constant: Caps Lock modifier key. */
1457 public static final int KEYCODE_CAPS_LOCK = 115;
1458 /** Key code constant: Scroll Lock key. */
1459 public static final int KEYCODE_SCROLL_LOCK = 116;
1460 /** Key code constant: Left Meta modifier key. */
1461 public static final int KEYCODE_META_LEFT = 117;
1462 /** Key code constant: Right Meta modifier key. */
1463 public static final int KEYCODE_META_RIGHT = 118;
1464 /** Key code constant: Function modifier key. */
1465 public static final int KEYCODE_FUNCTION = 119;
1466 /** Key code constant: System Request / Print Screen key. */
1467 public static final int KEYCODE_SYSRQ = 120;
1468 /** Key code constant: Break / Pause key. */
1469 public static final int KEYCODE_BREAK = 121;
1470 /** Key code constant: Home Movement key.
1471 * Used for scrolling or moving the cursor around to the start of a line
1472 * or to the top of a list. */
1473 public static final int KEYCODE_MOVE_HOME = 122;
1474 /** Key code constant: End Movement key.
1475 * Used for scrolling or moving the cursor around to the end of a line
1476 * or to the bottom of a list. */
1477 public static final int KEYCODE_MOVE_END = 123;
1478 /** Key code constant: Insert key.
1479 * Toggles insert / overwrite edit mode. */
1480 public static final int KEYCODE_INSERT = 124;
1481 /** Key code constant: Forward key.
1482 * Navigates forward in the history stack. Complement of {@link #KEYCODE_BACK}. */
1483 public static final int KEYCODE_FORWARD = 125;
1484 /** Key code constant: Play media key. */
1485 public static final int KEYCODE_MEDIA_PLAY = 126;
1486 /** Key code constant: Pause media key. */
1487 public static final int KEYCODE_MEDIA_PAUSE = 127;
1488 /** Key code constant: Close media key.
1489 * May be used to close a CD tray, for example. */
1490 public static final int KEYCODE_MEDIA_CLOSE = 128;
1491 /** Key code constant: Eject media key.
1492 * May be used to eject a CD tray, for example. */
1493 public static final int KEYCODE_MEDIA_EJECT = 129;
1494 /** Key code constant: Record media key. */
1495 public static final int KEYCODE_MEDIA_RECORD = 130;
1496 /** Key code constant: F1 key. */
1497 public static final int KEYCODE_F1 = 131;
1498 /** Key code constant: F2 key. */
1499 public static final int KEYCODE_F2 = 132;
1500 /** Key code constant: F3 key. */
1501 public static final int KEYCODE_F3 = 133;
1502 /** Key code constant: F4 key. */
1503 public static final int KEYCODE_F4 = 134;
1504 /** Key code constant: F5 key. */
1505 public static final int KEYCODE_F5 = 135;
1506 /** Key code constant: F6 key. */
1507 public static final int KEYCODE_F6 = 136;
1508 /** Key code constant: F7 key. */
1509 public static final int KEYCODE_F7 = 137;
1510 /** Key code constant: F8 key. */
1511 public static final int KEYCODE_F8 = 138;
1512 /** Key code constant: F9 key. */
1513 public static final int KEYCODE_F9 = 139;
1514 /** Key code constant: F10 key. */
1515 public static final int KEYCODE_F10 = 140;
1516 /** Key code constant: F11 key. */
1517 public static final int KEYCODE_F11 = 141;
1518 /** Key code constant: F12 key. */
1519 public static final int KEYCODE_F12 = 142;
1520 /** Key code constant: Num Lock modifier key.
1521 * This is the Num Lock key; it is different from {@link #KEYCODE_NUM}.
1522 * This key generally modifies the behavior of other keys on the numeric keypad. */
1523 public static final int KEYCODE_NUM_LOCK = 143;
1524 /** Key code constant: Numeric keypad '0' key. */
1525 public static final int KEYCODE_NUMPAD_0 = 144;
1526 /** Key code constant: Numeric keypad '1' key. */
1527 public static final int KEYCODE_NUMPAD_1 = 145;
1528 /** Key code constant: Numeric keypad '2' key. */
1529 public static final int KEYCODE_NUMPAD_2 = 146;
1530 /** Key code constant: Numeric keypad '3' key. */
1531 public static final int KEYCODE_NUMPAD_3 = 147;
1532 /** Key code constant: Numeric keypad '4' key. */
1533 public static final int KEYCODE_NUMPAD_4 = 148;
1534 /** Key code constant: Numeric keypad '5' key. */
1535 public static final int KEYCODE_NUMPAD_5 = 149;
1536 /** Key code constant: Numeric keypad '6' key. */
1537 public static final int KEYCODE_NUMPAD_6 = 150;
1538 /** Key code constant: Numeric keypad '7' key. */
1539 public static final int KEYCODE_NUMPAD_7 = 151;
1540 /** Key code constant: Numeric keypad '8' key. */
1541 public static final int KEYCODE_NUMPAD_8 = 152;
1542 /** Key code constant: Numeric keypad '9' key. */
1543 public static final int KEYCODE_NUMPAD_9 = 153;
1544 /** Key code constant: Numeric keypad '/' key (for division). */
1545 public static final int KEYCODE_NUMPAD_DIVIDE = 154;
1546 /** Key code constant: Numeric keypad '*' key (for multiplication). */
1547 public static final int KEYCODE_NUMPAD_MULTIPLY = 155;
1548 /** Key code constant: Numeric keypad '-' key (for subtraction). */
1549 public static final int KEYCODE_NUMPAD_SUBTRACT = 156;
1550 /** Key code constant: Numeric keypad '+' key (for addition). */
1551 public static final int KEYCODE_NUMPAD_ADD = 157;
1552 /** Key code constant: Numeric keypad '.' key (for decimals or digit grouping). */
1553 public static final int KEYCODE_NUMPAD_DOT = 158;
1554 /** Key code constant: Numeric keypad ',' key (for decimals or digit grouping). */
1555 public static final int KEYCODE_NUMPAD_COMMA = 159;
1556 /** Key code constant: Numeric keypad Enter key. */
1557 public static final int KEYCODE_NUMPAD_ENTER = 160;
1558 /** Key code constant: Numeric keypad '=' key. */
1559 public static final int KEYCODE_NUMPAD_EQUALS = 161;
1560 /** Key code constant: Numeric keypad '(' key. */
1561 public static final int KEYCODE_NUMPAD_LEFT_PAREN = 162;
1562 /** Key code constant: Numeric keypad ')' key. */
1563 public static final int KEYCODE_NUMPAD_RIGHT_PAREN = 163;
1564 /** Key code constant: Volume Mute key.
1565 * Mutes the speaker, unlike {@link #KEYCODE_MUTE}.
1566 * This key should normally be implemented as a toggle such that the first press
1567 * mutes the speaker and the second press restores the original volume. */
1568 public static final int KEYCODE_VOLUME_MUTE = 164;
1569 /** Key code constant: Info key.
1570 * Common on TV remotes to show additional information related to what is
1571 * currently being viewed. */
1572 public static final int KEYCODE_INFO = 165;
1573 /** Key code constant: Channel up key.
1574 * On TV remotes, increments the television channel. */
1575 public static final int KEYCODE_CHANNEL_UP = 166;
1576 /** Key code constant: Channel down key.
1577 * On TV remotes, decrements the television channel. */
1578 public static final int KEYCODE_CHANNEL_DOWN = 167;
1579 /** Key code constant: Zoom in key. */
1580 public static final int KEYCODE_ZOOM_IN = 168;
1581 /** Key code constant: Zoom out key. */
1582 public static final int KEYCODE_ZOOM_OUT = 169;
1583 /** Key code constant: TV key.
1584 * On TV remotes, switches to viewing live TV. */
1585 public static final int KEYCODE_TV = 170;
1586 /** Key code constant: Window key.
1587 * On TV remotes, toggles picture-in-picture mode or other windowing functions. */
1588 public static final int KEYCODE_WINDOW = 171;
1589 /** Key code constant: Guide key.
1590 * On TV remotes, shows a programming guide. */
1591 public static final int KEYCODE_GUIDE = 172;
1592 /** Key code constant: DVR key.
1593 * On some TV remotes, switches to a DVR mode for recorded shows. */
1594 public static final int KEYCODE_DVR = 173;
1595 /** Key code constant: Bookmark key.
1596 * On some TV remotes, bookmarks content or web pages. */
1597 public static final int KEYCODE_BOOKMARK = 174;
1598 /** Key code constant: Toggle captions key.
1599 * Switches the mode for closed-captioning text, for example during television shows. */
1600 public static final int KEYCODE_CAPTIONS = 175;
1601 /** Key code constant: Settings key.
1602 * Starts the system settings activity. */
1603 public static final int KEYCODE_SETTINGS = 176;
1604 /** Key code constant: TV power key.
1605 * On TV remotes, toggles the power on a television screen. */
1606 public static final int KEYCODE_TV_POWER = 177;
1607 /** Key code constant: TV input key.
1608 * On TV remotes, switches the input on a television screen. */
1609 public static final int KEYCODE_TV_INPUT = 178;
1610 /** Key code constant: Set-top-box power key.
1611 * On TV remotes, toggles the power on an external Set-top-box. */
1612 public static final int KEYCODE_STB_POWER = 179;
1613 /** Key code constant: Set-top-box input key.
1614 * On TV remotes, switches the input mode on an external Set-top-box. */
1615 public static final int KEYCODE_STB_INPUT = 180;
1616 /** Key code constant: A/V Receiver power key.
1617 * On TV remotes, toggles the power on an external A/V Receiver. */
1618 public static final int KEYCODE_AVR_POWER = 181;
1619 /** Key code constant: A/V Receiver input key.
1620 * On TV remotes, switches the input mode on an external A/V Receiver. */
1621 public static final int KEYCODE_AVR_INPUT = 182;
1622 /** Key code constant: Red "programmable" key.
1623 * On TV remotes, acts as a contextual/programmable key. */
1624 public static final int KEYCODE_PROG_RED = 183;
1625 /** Key code constant: Green "programmable" key.
1626 * On TV remotes, actsas a contextual/programmable key. */
1627 public static final int KEYCODE_PROG_GREEN = 184;
1628 /** Key code constant: Yellow "programmable" key.
1629 * On TV remotes, acts as a contextual/programmable key. */
1630 public static final int KEYCODE_PROG_YELLOW = 185;
1631 /** Key code constant: Blue "programmable" key.
1632 * On TV remotes, acts as a contextual/programmable key. */
1633 public static final int KEYCODE_PROG_BLUE = 186;
1635 private static final int LAST_KEYCODE = KEYCODE_PROG_BLUE;
1637 private String[] mKeyCodes = new String[256];
1638 private String[] mAppKeyCodes = new String[256];
1640 private void initKeyCodes() {
1641 mKeyCodes[KEYCODE_DPAD_CENTER] = "\015";
1642 mKeyCodes[KEYCODE_DPAD_UP] = "\033[A";
1643 mKeyCodes[KEYCODE_DPAD_DOWN] = "\033[B";
1644 mKeyCodes[KEYCODE_DPAD_RIGHT] = "\033[C";
1645 mKeyCodes[KEYCODE_DPAD_LEFT] = "\033[D";
1646 mKeyCodes[KEYCODE_F1] = "\033[OP";
1647 mKeyCodes[KEYCODE_F2] = "\033[OQ";
1648 mKeyCodes[KEYCODE_F3] = "\033[OR";
1649 mKeyCodes[KEYCODE_F4] = "\033[OS";
1650 mKeyCodes[KEYCODE_F5] = "\033[15~";
1651 mKeyCodes[KEYCODE_F6] = "\033[17~";
1652 mKeyCodes[KEYCODE_F7] = "\033[18~";
1653 mKeyCodes[KEYCODE_F8] = "\033[19~";
1654 mKeyCodes[KEYCODE_F9] = "\033[20~";
1655 mKeyCodes[KEYCODE_F10] = "\033[21~";
1656 mKeyCodes[KEYCODE_F11] = "\033[23~";
1657 mKeyCodes[KEYCODE_F12] = "\033[24~";
1658 mKeyCodes[KEYCODE_SYSRQ] = "\033[32~"; // Sys Request / Print
1659 // Is this Scroll lock? mKeyCodes[Cancel] = "\033[33~";
1660 mKeyCodes[KEYCODE_BREAK] = "\033[34~"; // Pause/Break
1662 mKeyCodes[KEYCODE_TAB] = "\011";
1663 mKeyCodes[KEYCODE_ENTER] = "\015";
1664 mKeyCodes[KEYCODE_ESCAPE] = "\033";
1666 mKeyCodes[KEYCODE_INSERT] = "\033[2~";
1667 mKeyCodes[KEYCODE_FORWARD_DEL] = "\033[3~";
1668 mKeyCodes[KEYCODE_MOVE_HOME] = "\033[1~";
1669 mKeyCodes[KEYCODE_MOVE_END] = "\033[4~";
1670 mKeyCodes[KEYCODE_PAGE_UP] = "\033[5~";
1671 mKeyCodes[KEYCODE_PAGE_DOWN] = "\033[6~";
1672 mKeyCodes[KEYCODE_DEL]= "\177";
1673 mKeyCodes[KEYCODE_NUM_LOCK] = "\033OP";
1674 mKeyCodes[KEYCODE_NUMPAD_DIVIDE] = "/";
1675 mKeyCodes[KEYCODE_NUMPAD_MULTIPLY] = "*";
1676 mKeyCodes[KEYCODE_NUMPAD_SUBTRACT] = "-";
1677 mKeyCodes[KEYCODE_NUMPAD_ADD] = "+";
1678 mKeyCodes[KEYCODE_NUMPAD_ENTER] = "\015";
1679 mKeyCodes[KEYCODE_NUMPAD_EQUALS] = "=";
1680 mKeyCodes[KEYCODE_NUMPAD_DOT] = ".";
1681 mKeyCodes[KEYCODE_NUMPAD_COMMA] = ",";
1682 mKeyCodes[KEYCODE_NUMPAD_0] = "0";
1683 mKeyCodes[KEYCODE_NUMPAD_1] = "1";
1684 mKeyCodes[KEYCODE_NUMPAD_2] = "2";
1685 mKeyCodes[KEYCODE_NUMPAD_3] = "3";
1686 mKeyCodes[KEYCODE_NUMPAD_4] = "4";
1687 mKeyCodes[KEYCODE_NUMPAD_5] = "5";
1688 mKeyCodes[KEYCODE_NUMPAD_6] = "6";
1689 mKeyCodes[KEYCODE_NUMPAD_7] = "7";
1690 mKeyCodes[KEYCODE_NUMPAD_8] = "8";
1691 mKeyCodes[KEYCODE_NUMPAD_9] = "9";
1693 mAppKeyCodes[KEYCODE_DPAD_UP] = "\033OA";
1694 mAppKeyCodes[KEYCODE_DPAD_DOWN] = "\033OB";
1695 mAppKeyCodes[KEYCODE_DPAD_RIGHT] = "\033OC";
1696 mAppKeyCodes[KEYCODE_DPAD_LEFT] = "\033OD";
1697 mAppKeyCodes[KEYCODE_NUMPAD_DIVIDE] = "\033Oo";
1698 mAppKeyCodes[KEYCODE_NUMPAD_MULTIPLY] = "\033Oj";
1699 mAppKeyCodes[KEYCODE_NUMPAD_SUBTRACT] = "\033Om";
1700 mAppKeyCodes[KEYCODE_NUMPAD_ADD] = "\033Ok";
1701 mAppKeyCodes[KEYCODE_NUMPAD_ENTER] = "\033OM";
1702 mAppKeyCodes[KEYCODE_NUMPAD_EQUALS] = "\033OX";
1703 mAppKeyCodes[KEYCODE_NUMPAD_DOT] = "\033On";
1704 mAppKeyCodes[KEYCODE_NUMPAD_COMMA] = "\033Ol";
1705 mAppKeyCodes[KEYCODE_NUMPAD_0] = "\033Op";
1706 mAppKeyCodes[KEYCODE_NUMPAD_1] = "\033Oq";
1707 mAppKeyCodes[KEYCODE_NUMPAD_2] = "\033Or";
1708 mAppKeyCodes[KEYCODE_NUMPAD_3] = "\033Os";
1709 mAppKeyCodes[KEYCODE_NUMPAD_4] = "\033Ot";
1710 mAppKeyCodes[KEYCODE_NUMPAD_5] = "\033Ou";
1711 mAppKeyCodes[KEYCODE_NUMPAD_6] = "\033Ov";
1712 mAppKeyCodes[KEYCODE_NUMPAD_7] = "\033Ow";
1713 mAppKeyCodes[KEYCODE_NUMPAD_8] = "\033Ox";
1714 mAppKeyCodes[KEYCODE_NUMPAD_9] = "\033Oy";
1718 * The state engine for a modifier key. Can be pressed, released, locked,
1722 private class ModifierKey {
1726 private static final int UNPRESSED = 0;
1728 private static final int PRESSED = 1;
1730 private static final int RELEASED = 2;
1732 private static final int USED = 3;
1734 private static final int LOCKED = 4;
1737 * Construct a modifier key. UNPRESSED by default.
1740 public ModifierKey() {
1744 public void onPress() {
1747 // This is a repeat before use
1753 // This is a repeat after use
1764 public void onRelease() {
1773 // Leave state alone
1778 public void adjustAfterKeypress() {
1787 // Leave state alone
1792 public boolean isActive() {
1793 return mState != UNPRESSED;
1797 private ModifierKey mAltKey = new ModifierKey();
1799 private ModifierKey mCapKey = new ModifierKey();
1801 private ModifierKey mControlKey = new ModifierKey();
1803 private ModifierKey mFnKey = new ModifierKey();
1805 private boolean mCapsLock;
1807 static public final int KEYCODE_OFFSET = 1000;
1810 * Construct a term key listener.
1813 public TermKeyListener() {
1817 public void handleControlKey(boolean down) {
1819 mControlKey.onPress();
1821 mControlKey.onRelease();
1825 public void handleFnKey(boolean down) {
1833 public int mapControlChar(int ch) {
1835 if (mControlKey.isActive()) {
1836 // Search is the control key.
1837 if (result >= 'a' && result <= 'z') {
1838 result = (char) (result - 'a' + '\001');
1839 } else if (result >= 'A' && result <= 'Z') {
1840 result = (char) (result - 'A' + '\001');
1841 } else if (result == ' ' || result == '2') {
1843 } else if (result == '[' || result == '3') {
1844 result = 27; // ^[ (Esc)
1845 } else if (result == '\\' || result == '4') {
1847 } else if (result == ']' || result == '5') {
1849 } else if (result == '^' || result == '6') {
1850 result = 30; // control-^
1851 } else if (result == '_' || result == '7') {
1853 } else if (result == '8') {
1854 result = 127; // DEL
1855 } else if (result == '9') {
1856 result = KEYCODE_OFFSET + TermKeyListener.KEYCODE_F11;
1857 } else if (result == '0') {
1858 result = KEYCODE_OFFSET + TermKeyListener.KEYCODE_F12;
1860 } else if (mFnKey.isActive()) {
1861 if (result == 'w' || result == 'W') {
1862 result = KEYCODE_OFFSET + KeyEvent.KEYCODE_DPAD_UP;
1863 } else if (result == 'a' || result == 'A') {
1864 result = KEYCODE_OFFSET + KeyEvent.KEYCODE_DPAD_LEFT;
1865 } else if (result == 's' || result == 'S') {
1866 result = KEYCODE_OFFSET + KeyEvent.KEYCODE_DPAD_DOWN;
1867 } else if (result == 'd' || result == 'D') {
1868 result = KEYCODE_OFFSET + KeyEvent.KEYCODE_DPAD_RIGHT;
1869 } else if (result == 'p' || result == 'P') {
1870 result = KEYCODE_OFFSET + TermKeyListener.KEYCODE_PAGE_UP;
1871 } else if (result == 'n' || result == 'N') {
1872 result = KEYCODE_OFFSET + TermKeyListener.KEYCODE_PAGE_DOWN;
1873 } else if (result == 't' || result == 'T') {
1874 result = KEYCODE_OFFSET + KeyEvent.KEYCODE_TAB;
1875 } else if (result == 'l' || result == 'L') {
1877 } else if (result == 'u' || result == 'U') {
1879 } else if (result == 'e' || result == 'E') {
1880 result = 27; // ^[ (Esc)
1881 } else if (result == '.') {
1883 } else if (result > '0' && result <= '9') {
1885 result = (char)(result + KEYCODE_OFFSET + TermKeyListener.KEYCODE_F1 - 1);
1886 } else if (result == '0') {
1887 result = KEYCODE_OFFSET + TermKeyListener.KEYCODE_F10;
1888 } else if (result == 'i' || result == 'I') {
1889 result = KEYCODE_OFFSET + TermKeyListener.KEYCODE_INSERT;
1890 } else if (result == 'x' || result == 'X') {
1891 result = KEYCODE_OFFSET + TermKeyListener.KEYCODE_FORWARD_DEL;
1892 } else if (result == 'h' || result == 'H') {
1893 result = KEYCODE_OFFSET + TermKeyListener.KEYCODE_MOVE_HOME;
1894 } else if (result == 'f' || result == 'F') {
1895 result = KEYCODE_OFFSET + TermKeyListener.KEYCODE_MOVE_END;
1900 mAltKey.adjustAfterKeypress();
1901 mCapKey.adjustAfterKeypress();
1902 mControlKey.adjustAfterKeypress();
1903 mFnKey.adjustAfterKeypress();
1910 * Handle a keyDown event.
1912 * @param keyCode the keycode of the keyDown event
1915 public void keyDown(int keyCode, KeyEvent event, OutputStream out, boolean appMode) throws IOException {
1916 if (handleKeyCode(keyCode, out, appMode)) {
1921 case KeyEvent.KEYCODE_ALT_RIGHT:
1922 case KeyEvent.KEYCODE_ALT_LEFT:
1926 case KeyEvent.KEYCODE_SHIFT_LEFT:
1927 case KeyEvent.KEYCODE_SHIFT_RIGHT:
1931 case KEYCODE_CTRL_LEFT:
1932 case KEYCODE_CTRL_RIGHT:
1933 mControlKey.onPress();
1936 case KEYCODE_CAPS_LOCK:
1937 if (event.getRepeatCount() == 0) {
1938 mCapsLock = !mCapsLock;
1943 result = event.getUnicodeChar(
1944 (mCapKey.isActive() || mCapsLock ? KeyEvent.META_SHIFT_ON : 0) |
1945 (mAltKey.isActive() ? KeyEvent.META_ALT_ON : 0));
1950 result = mapControlChar(result);
1952 if (result >= KEYCODE_OFFSET) {
1953 handleKeyCode(result - KEYCODE_OFFSET, out, appMode);
1954 } else if (result >= 0) {
1959 public boolean handleKeyCode(int keyCode, OutputStream out, boolean appMode) throws IOException {
1960 if (keyCode >= 0 && keyCode < mKeyCodes.length) {
1963 code = mAppKeyCodes[keyCode];
1966 code = mKeyCodes[keyCode];
1969 int length = code.length();
1970 for (int i = 0; i < length; i++) {
1971 out.write(code.charAt(i));
1980 * Handle a keyUp event.
1982 * @param keyCode the keyCode of the keyUp event
1984 public void keyUp(int keyCode) {
1986 case KeyEvent.KEYCODE_ALT_LEFT:
1987 case KeyEvent.KEYCODE_ALT_RIGHT:
1988 mAltKey.onRelease();
1990 case KeyEvent.KEYCODE_SHIFT_LEFT:
1991 case KeyEvent.KEYCODE_SHIFT_RIGHT:
1992 mCapKey.onRelease();
1995 case KEYCODE_CTRL_LEFT:
1996 case KEYCODE_CTRL_RIGHT:
1997 mControlKey.onRelease();
2001 // Ignore other keyUps