package tw.kenshinn.keyboardTerm;
import java.io.IOException;
import java.lang.reflect.Array;
import java.net.SocketException;
import java.util.ArrayList;
import java.util.Arrays;
import android.R.bool;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Typeface;
import android.graphics.Bitmap.Config;
import android.graphics.Region.Op;
import android.net.Uri;
import android.os.Bundle;
import android.os.SystemClock;
import android.preference.Preference;
import android.preference.PreferenceManager;
import android.text.InputType;
import android.text.method.MetaKeyKeyListener;
import android.util.AttributeSet;
import android.util.Log;
import android.util.TypedValue;
import android.view.KeyCharacterMap;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
import android.view.inputmethod.BaseInputConnection;
import android.view.inputmethod.CompletionInfo;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputConnection;
import android.view.inputmethod.InputMethodManager;
import com.roiding.rterm.bean.Host;
import com.roiding.rterm.util.ChineseUtils;
import de.mud.jta.Wrapper;
import de.mud.telnet.TelnetWrapper;
import de.mud.terminal.VDUBuffer;
import de.mud.terminal.VDUDisplay;
import de.mud.terminal.vt320;
public class TerminalView extends View implements VDUDisplay {
final String TAG = "TerminalView";
private ArrayList<Url>[] urls;
// private static final String M_FIXCHARS_STRING = "gjy";
// private static final String M_FIXCHARS2_STRING = "pq_";
private static final int TERM_WIDTH = 80;
private static final int TERM_HEIGHT = 24;
public float CHAR_WIDTH;
public float CHAR_HEIGHT;
private float CHAR_POS_FIX;
private int SCREEN_WIDTH;
private int SCREEN_HEIGHT;
private static final int SCROLLBACK = 0;
private static final int DEFAULT_FG_COLOR = 7;
private static final int DEFAULT_BG_COLOR = 0;
private boolean warningLoginAgain = false;
private static final int DELAY_WARNING_LOGIN_AGAIN = 15000;
public static final KeyCharacterMap DEFAULT_KEYMAP = KeyCharacterMap
.load(KeyCharacterMap.BUILT_IN_KEYBOARD);
public Bitmap bitmap;
public VDUBuffer buffer;
public int color[];
public boolean connected;
private boolean fullRedraw = true;
private final Paint defaultPaint = new Paint();
private Paint specialPaint = new Paint();
private final Paint cursorPaint = new Paint();
private Typeface specialTypeface;
private float specialDecent;
private final Canvas canvas = new Canvas();
private boolean ctrlPressed;
private long metaState = 0;
private SharedPreferences mPref;
private String mRadio = "";
public Wrapper connection;
public TerminalActivity terminalActivity;
public Host host;
public boolean debug = false;
static final int URL_RANGE = 2; // (2mm)
private float mUrlRangePx = 0;
public TerminalView(TerminalActivity context, AttributeSet attrs) {
super(context, attrs);
this.terminalActivity = context;
setFocusable(true);
setFocusableInTouchMode(true);
init();
}
public void init() {
mUrlRangePx = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_MM, URL_RANGE, terminalActivity.getResources().getDisplayMetrics());
resetColors();
mPref = PreferenceManager.getDefaultSharedPreferences(this.getContext());
buffer = new vt320() {
public void beep() {
Log.i(TAG, "beep");
}
public void write(byte[] b) {
try {
connection.write(b);
} catch (Exception e) {
terminalActivity.disconnect(e);
}
}
};
buffer.setBufferSize(SCROLLBACK);
buffer.setDisplay(this);
buffer.setScreenSize(TERM_WIDTH, TERM_HEIGHT, true);
defaultPaint.setFlags(Paint.ANTI_ALIAS_FLAG | Paint.SUBPIXEL_TEXT_FLAG);
defaultPaint.setTypeface(Typeface.MONOSPACE);
specialTypeface = Typeface.createFromAsset(this.getResources().getAssets(), "SpecialChar.ttf");
// Workaround to create array of ArrayList generic type.
urls = (ArrayList<Url>[]) Array.newInstance(ArrayList.class, TERM_HEIGHT);
}
/**
* Change the size of virtual screen.
*
* This will change the size of canvas we draw on.
* Which is planed could be bigger than actual screen size and scrolled by user.
* @param w width
* @param h height
*/
public void setVScreenSize(int w, int h){
Log.d(TAG,"Set VScreen to ("+w+","+h+")");
// Prepare bitmap and canvas for us to draw on
bitmap = Bitmap.createBitmap(w, h,Config.ARGB_8888);
canvas.setBitmap(bitmap);
defaultPaint.setColor(Color.BLACK);
canvas.drawRect(0, 0, w, h, defaultPaint);
//Calculate char size
CHAR_WIDTH = (float) w / TERM_WIDTH;
CHAR_HEIGHT = (float) h / TERM_HEIGHT;
//defaultPaint.setTextSize(CHAR_HEIGHT / 21 * 19.4f);
defaultPaint.setTextSize(CHAR_HEIGHT);
defaultPaint.setTextScaleX(CHAR_WIDTH*2/CHAR_HEIGHT);
// Because canvas.drawText() use baseline to position
// Calculate the distance between baseline and top line for convenience
// I think we have to make a private method for decent measure?
Rect bound = new Rect();
defaultPaint.getTextBounds("龜", 0, 1,bound); // I know this is dirty, anyone have a better solution?
CHAR_POS_FIX = CHAR_HEIGHT - bound.bottom;
specialPaint = new Paint(defaultPaint);
//specialPaint.setTextSize(CHAR_HEIGHT);
specialPaint.setTypeface(specialTypeface);
specialPaint.getTextBounds("▇", 0, 1,bound);
specialDecent = CHAR_HEIGHT - bound.bottom;
// Draw
fullRedraw = true;
redraw();
this.postInvalidate();
terminalActivity.refreshView();
}
@Override protected void onSizeChanged (int w, int h, int oldw, int oldh){
SCREEN_HEIGHT = h;
SCREEN_WIDTH = w;
String radio = mPref.getString("settings_screen_radio", "0");
//This is when android start us. use this w/h as base size unless user zoom
if(oldw == 0){
if(!mRadio.equals(radio)) {
if(radio.equals("5:3")) {
h = (int)(w * 0.6f);
} else if (radio.equals("6:4")) {
h = (int)(w * (4f / 6f));
} else if (radio.equals("8:5")) {
h = (int)(w * (5f / 8f));
}
mRadio = radio;
}
setVScreenSize(w,h);
}
}
/**
* Render magnifier.
*
* @param canvas
* @param drawArea
* @param Focus
*/
/* I can't come up with good way to write magnifier.
* So.. just do it the worst say. */
public void renderMagnifier(Canvas c,Rect drawArea,RectF focus){
// Create a bitmap that can cover canvas all text
Rect renderRegion = new Rect(
(int)(focus.left / CHAR_WIDTH),
(int)(focus.top / CHAR_HEIGHT),
(int)Math.ceil(focus.right / CHAR_WIDTH),
(int)Math.ceil(focus.bottom / CHAR_HEIGHT)
);
float ratioX = drawArea.width() / focus.width();
float ratioY = drawArea.height() / focus.height();
Bitmap magBitmap = Bitmap.createBitmap((int)(renderRegion.width()*CHAR_WIDTH*ratioX),(int)(renderRegion.height()*CHAR_HEIGHT*ratioY), Config.ARGB_8888);
Canvas magedCanvas = new Canvas(magBitmap);
Paint paint = new Paint(defaultPaint);
paint.setTextSize(CHAR_HEIGHT*ratioY);
paint.setTextScaleX(paint.getTextScaleX()*ratioY/ratioX);
renderText(magedCanvas,new RectF(0,0,magedCanvas.getWidth(),magedCanvas.getHeight()),paint,renderRegion.top,renderRegion.left);
c.clipRect(drawArea);
c.drawBitmap(magBitmap,
drawArea.left - (focus.left - renderRegion.left * CHAR_WIDTH)*ratioX ,
drawArea.top - (focus.top - renderRegion.top * CHAR_HEIGHT)*ratioY
, new Paint());
}
public void renderText(Canvas canvas,int row){
RectF r = new RectF(0,row*CHAR_HEIGHT,CHAR_WIDTH * TERM_WIDTH,(row+1)*CHAR_HEIGHT);
renderText(canvas,r,null,row,0);
}
private static int round(float x) {
/* I got this from com.android.internal.util.FastMath. */
/* I don't think it well make us any faster, but can help us type less code */
long lx = (long)(x * (65536 * 256f));
return (int)((lx + 0x800000) >> 24);
}
public void renderText(Canvas canvas,RectF drawArea,Paint paint,int row, int col){
// We round floats ourself because the direct use RectF to paint cause dirty lines.
// TODO: The better approach: mix the color if a pixel is shared.
// TODO: Draw rect chars our self.
canvas.clipRect(drawArea,Op.REPLACE);
float decent = CHAR_POS_FIX, sp_decent = specialDecent, charWidth = CHAR_WIDTH, charHeight = CHAR_HEIGHT;
if(paint == null){
paint = defaultPaint;
}else{
charWidth = paint.getTextSize() / 2 * paint.getTextScaleX();
charHeight = paint.getTextSize();
Rect bound = new Rect();
paint.getTextBounds("龜", 0, 1,bound);
decent = charHeight - bound.bottom;
Paint sPaint = new Paint(paint);
sPaint.setTypeface(specialTypeface);
sPaint.getTextBounds("▇", 0, 1,bound);
sp_decent = charHeight - bound.bottom;
}
int toRow = row + (int)Math.ceil(drawArea.height()/charHeight);
int toCol = col + (int)Math.ceil(drawArea.width()/charWidth);
if(toRow >= buffer.getRows())
toRow = buffer.getRows() -1;
if(toCol >= buffer.getColumns())
toCol = buffer.getColumns() -1;
for(int r=row; r <= toRow ;r++){
int c;
boolean stateHigh = false;
RectF localRect = new RectF(
drawArea.left,
drawArea.top + (r-row) * charHeight,
drawArea.right,
drawArea.top + (r-row+1) * charHeight);
for(c=0; c<col; c++) //We should move charset decode to terminal class
stateHigh = (!stateHigh && buffer.getChar(c, r) >= 128);
if(stateHigh){
c--;
stateHigh = false;
localRect.left -= charWidth;
}
for(;c <= toCol; c++){
int ptr = 0;
int currAttr = buffer.charAttributes[buffer.windowBase + r][c];
// ptr is the "length" of same attr char
while(c+ptr <= toCol && currAttr == buffer.charAttributes[buffer.windowBase + r][c+ptr]){
stateHigh = (!stateHigh && buffer.getChar(c+ptr, r) >= 128);
ptr++;
}
if(stateHigh && c+ptr+1 < TERM_WIDTH - 1){
ptr++;
stateHigh = false;
}
int color[] = getColor(currAttr);
paint.setColor(color[1]);
localRect.right = localRect.left + ptr*charWidth;
canvas.drawRect(round(localRect.left),
round(localRect.top),
round(localRect.right),
round(localRect.bottom),
paint);
char[] chars = new char[ptr];
System.arraycopy(buffer.charArray[buffer.windowBase + r],
c, chars, 0, ptr);
String encoding = (host != null)? host.getEncoding():"Big5";
String string = ChineseUtils.decode(chars, encoding,this.getResources());
paint.setColor(color[0]);
// Since Android's MONOFACE is not really MONOFACE..... We have to postion by our self
int colCount = 0;
String ch = null;
float chDecent = decent;
for(int pos = 0; pos < string.length(); pos++){
ch = string.substring(pos, pos+1);
chDecent = decent;
if( (colCount < ptr && chars[colCount] >= 0x21 && chars[colCount] <= 0x7E) ||
(colCount+1 < ptr && ((chars[colCount] == 0xA1 && chars[colCount+1] >= 0x41) ||
(chars[colCount] == 0xA2 && (
chars[colCount+1] < 0x49 ||
(chars[colCount+1] > 0x62 && chars[colCount+1]< 0xAE)))))){
paint.setTypeface(specialTypeface);
chDecent = sp_decent;
}else{
paint.setTypeface(Typeface.MONOSPACE);
}
canvas.drawText(
ch,
localRect.left + colCount*charWidth,
localRect.top+chDecent,
paint);
if(ch.charAt(0) > 128)
colCount++;
colCount++;
}
int lastColor[] = getColor(buffer.charAttributes[buffer.windowBase + r][c+ptr-1]);
if(!Arrays.equals(color,lastColor) && ch != null){ /* null happens */
localRect.left = localRect.right - charWidth;
color = getColor( buffer.charAttributes[buffer.windowBase + r][c+ptr-1]);
canvas.clipRect(localRect, Op.REPLACE);
paint.setColor(color[1]);
canvas.drawRect(round(localRect.left),
round(localRect.top),
round(localRect.right),
round(localRect.bottom),
paint);
paint.setColor(color[0]);
canvas.drawText(ch , localRect.left-charWidth, localRect.top+chDecent, paint);
}
c+= ptr -1;
canvas.clipRect(drawArea,Op.REPLACE);
localRect.left = localRect.right;
}
}
}
@Override
public void onDraw(Canvas viewCanvas) {
Log.v(TAG,"onDraw()");
// draw
if (this.bitmap == null)
return;
boolean entireDirty = buffer.update[0] || fullRedraw;
// walk through all lines in the buffer
for (int l = 0; l < buffer.height; l++) {
// check if this line needs redraw.
if (!entireDirty && !buffer.update[l + 1])
continue;
// reset dirty flag for this line
buffer.update[l + 1] = false;
// redraw
renderText(canvas,l);
// reset urls for this line
if (urls != null)
urls[l] = new ArrayList<Url>();
// look for url
int pos = 0, addr;
String current = String.valueOf(buffer.charArray[buffer.windowBase + l]);
while( (pos = current.indexOf("://",pos)) != -1){
// substring's returns a string contains [start,end-1]
if(pos >= 4 && current.substring(pos-4, pos).equalsIgnoreCase("http"))
addr = 4;
else if(pos >= 5 && current.substring(pos-5, pos).equalsIgnoreCase("https"))
addr = 5;
else
break;
pos -= addr;
addr += 3;
Log.v(TAG,"URL START AT"+pos);
while(pos+addr+1<current.length()){
char thisChar = current.charAt(pos+addr+1);
if(!Character.isLetterOrDigit(thisChar) &&
! "./@?&=-_;%#!~".contains(Character.toString(thisChar)) )
break;
addr++;
}
String url = current.substring(pos,pos+addr+1);
Log.v(TAG,"URL is:"+url);
urls[l].add(new Url(pos,pos+addr,l,url));
defaultPaint.setColor(Color.BLUE);
canvas.drawLine(pos*CHAR_WIDTH, (l+1)*CHAR_HEIGHT -1, (pos+addr+1)*CHAR_WIDTH, (l+1)*CHAR_HEIGHT-1, defaultPaint);
pos+=addr+1;
}
}
// reset entire-buffer flags
buffer.update[0] = false;
fullRedraw = false;
// Calculate cursor
int curCol = this.buffer.getCursorColumn(),
curRow = (this.buffer.getCursorRow() + this.buffer.screenBase - this.buffer.windowBase);
float cursorX = curCol * CHAR_WIDTH,
cursorY = curRow * CHAR_HEIGHT;
float scroll = 0;
// albb0920: Manually scroll, requestRectangleOnScreen(cursor,true) doesn't seems to work well
// Rewrite this if we can get requestRectangleOnScreen work well.
int viewPos[] = new int[2];
getLocationOnScreen(viewPos);
//Log.v(TAG,"On Screen Pos: "+viewPos[0]+","+viewPos[1]);
if(cursorY+viewPos[1]<0){ // ime is up and we were blocked.
scroll = -1 * (cursorY+viewPos[1]) + (SCREEN_HEIGHT + viewPos[1] - CHAR_HEIGHT)/2;
if(scroll > -1 *viewPos[1])
scroll = -1 *viewPos[1];
Log.v(TAG,"SCROLL FIX"+scroll);
}
viewCanvas.drawBitmap(this.bitmap, 0, 0+scroll, defaultPaint);
// draw cursor
if (this.buffer.isCursorVisible()) {
cursorPaint.setColor(Color.argb(128, 0, 255, 0));
viewCanvas.drawRect(cursorX, cursorY+scroll, cursorX + CHAR_WIDTH, cursorY + CHAR_HEIGHT + scroll, cursorPaint);
}
}
/**
* Chinese InputMethod
*
* @author Chen slepher (slepheric@gmail.com)
*/
@Override
public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
// We need this private flag to make candidate selection for IME usable.
// This is the value for API level between 5 and 11, value has changed since then.
// It seems that after lv11, setting this flag(0x2000000) is no longer necessary.
// sync from lunaterm 1.2b
final int IME_FLAG_NO_FULLSCREEN = 0x80000000;
outAttrs.imeOptions = EditorInfo.IME_FLAG_NO_EXTRACT_UI | IME_FLAG_NO_FULLSCREEN;
outAttrs.inputType = InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS; //albb0920.100706: Without this, HTC_CIME's Chewing KB refuse to work
InputConnection ic = new TermInputConnection(this);
return ic;
}
@Override
public boolean onCheckIsTextEditor(){
return true;
}
private class TermInputConnection extends BaseInputConnection {
public TermInputConnection(View targetView) {
super(targetView, false);
}
@Override
public boolean performEditorAction(int actionCode) {
if (actionCode == EditorInfo.IME_ACTION_UNSPECIFIED) {
long eventTime = SystemClock.uptimeMillis();
sendKeyEvent(new KeyEvent(eventTime, eventTime,
KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_ENTER, 0));
sendKeyEvent(new KeyEvent(SystemClock.uptimeMillis(),
eventTime, KeyEvent.ACTION_UP, KeyEvent.KEYCODE_ENTER,
0));
return true;
}
return false;
}
public boolean setComposingText (CharSequence text, int newCursorPosition){
Log.v(TAG,"setComposingText: "+text);
if((TerminalActivity.termActFlags & TerminalActivity.FLAG_SHOW_EXTRACT_UI) !=0 && text.length()>0 && text.charAt(0)>128){
terminalActivity.showInputHelper();
return false;
}
return super.setComposingText(text, newCursorPosition);
}
}
/**
* Chinese InputMethod
*
* @author Chen slepher (slepheric@gmail.com)
*/
@Override
public boolean onKeyMultiple(int keyCode, int repeatCount, KeyEvent event) {
Log.d(TAG, "onKeyMultiple:" + keyCode);
if (keyCode == KeyEvent.KEYCODE_UNKNOWN) {
try {
String ime_input = event.getCharacters();
connection.write(ime_input.getBytes(host.getEncoding()));
return true;
} catch (SocketException e) {
nodifyParent(e);
} catch (IOException e) {
try {
connection.flush();
} catch (IOException ioe) {
nodifyParent(e);
}
} catch (NullPointerException npe) {
Log.d(TAG, "Input before connection established ignored.");
return true;
}
}
return super.onKeyMultiple(keyCode, repeatCount, event);
}
private CharSequence urlResult = null;
public boolean checkUrlClick(MotionEvent event){
int y = (int) event.getY(); // sync lunaterm
int x = (int) event.getX(); // sync lunaterm
int l = (int) (y / CHAR_HEIGHT);
int w = (int) (x / CHAR_WIDTH);
int l_min = (int)((y - mUrlRangePx) / CHAR_HEIGHT);
int l_max = (int)((y + mUrlRangePx) / CHAR_HEIGHT);
if (urls != null) {
final ArrayList<CharSequence> list = new ArrayList<CharSequence>();
for(int i = l_min; i <= l_max; i++) {
if(i >= 0 && i < urls.length && urls[i] != null) {
for (Url url : urls[i]) {
if (url.pointIn(w, i)) {
list.add(url.url.trim());
//Log.v("Kenshinn", "set Url Handled");
// terminalActivity.showUrlDialog(url.url.trim());
// return true;
}
}
}
}
if(list.size() > 0) {
urlResult = null;
urlResult = list.get(0);
if(list.size() > 1) {
CharSequence[] array = new CharSequence[list.size()];
list.toArray(array);
AlertDialog.Builder listBuilder = terminalActivity.listBuilder;
listBuilder.setSingleChoiceItems(array, 0, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int item) {
urlResult = list.get(item);
}
});
listBuilder.setPositiveButton("Ok", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
if(urlResult != null) {
terminalActivity.showUrlDialog(urlResult.toString());
}
}
});
listBuilder.setNegativeButton("Cancel",
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
urlResult = null;
}
});
AlertDialog dialog = listBuilder.create();
dialog.show();
return true;
} else {
if(urlResult != null) {
terminalActivity.showUrlDialog(urlResult.toString());
return true;
}
}
}
}
Log.d(TAG, "onTouchEvent:" + y + "/" + CHAR_HEIGHT + "=" + l);
return false;
}
// @Override
// public boolean onTouchEvent(MotionEvent event) {
// Log.v("Kenshinn", "TerminalView.onTouchEvent: " + event.getAction());
// int eventaction = event.getAction();
// Log.d(TAG, "onTouchEvent:" + eventaction);
//
// switch (eventaction) {
//
// case MotionEvent.ACTION_DOWN: // touch down so check if the
//
// int y = (int) event.getRawY();
// int x = (int) event.getRawX();
// int l = (int) (y / CHAR_HEIGHT);
// int w = (int) (x / CHAR_WIDTH);
//
// if (urls[l] != null) {
// for (Url url : urls[l]) {
// if (url.pointIn(w, l))
// Log.v("Kenshinn", "set Url Handled");
// terminalActivity.getGestureView().setUrlHandled(true);
// terminalActivity.showUrlDialog(url.url.trim());
// }
// }
// Log.d(TAG, "onTouchEvent:" + y + "/" + CHAR_HEIGHT + "=" + l);
// return true;
//
// case MotionEvent.ACTION_MOVE: // touch drag with the ball
// break;
//
// case MotionEvent.ACTION_UP:
// break;
//
// }
//
// return false;
// }
@Override
public boolean onKeyUp(int keyCode, KeyEvent event) {
metaState = MetaKeyKeyListener.handleKeyUp(metaState, keyCode, event);
return super.onKeyUp(keyCode, event);
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (connection == null)
return false;
try {
if (keyCode == KeyEvent.KEYCODE_VOLUME_UP) {
terminalActivity.changeFunctionKeyGalleryDisplay();
return true;
} else if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN) {
terminalActivity.inputMethodManager.toggleSoftInput(
InputMethodManager.SHOW_FORCED, 0);
return true;
}
if (metaState == (metaState = MetaKeyKeyListener.handleKeyDown(
metaState, keyCode, event))) {
boolean result = processSpecialChar(keyCode, MetaKeyKeyListener
.getMetaState(metaState));
metaState = MetaKeyKeyListener
.adjustMetaAfterKeypress(metaState);
if (result)
return true;
}
} catch (SocketException e) {
e.printStackTrace();
nodifyParent(e);
} catch (IOException e) {
e.printStackTrace();
try {
connection.flush();
} catch (IOException ioe) {
nodifyParent(e);
}
} catch (NullPointerException npe) {
Log.d(TAG, "Input before connection established ignored.");
return true;
}
return false;
}
public void write(byte[] b) throws IOException {
connection.write(b);
}
public boolean processSpecialChar(int keyCode, int mState)
throws IOException {
boolean printing = (DEFAULT_KEYMAP.isPrintingKey(keyCode) || keyCode == KeyEvent.KEYCODE_SPACE);
if (printing) {
int key = DEFAULT_KEYMAP.get(keyCode, mState);
if (ctrlPressed) {
// Support CTRL-a through CTRL-z
if (key >= 0x61 && key <= 0x7A)
key -= 0x60;
// Support CTRL-A through CTRL-_
else if (key >= 0x41 && key <= 0x5F)
key -= 0x40;
else if (key == 0x20)
key = 0x00;
ctrlPressed = false;
}
connection.write(key);
return true;
}
switch (keyCode) {
case KeyEvent.KEYCODE_DEL:
connection.write(0x08);
return true;
case KeyEvent.KEYCODE_ENTER:
((vt320) buffer).keyTyped(vt320.KEY_ENTER, ' ', mState);
return true;
case KeyEvent.KEYCODE_DPAD_LEFT:
((vt320) buffer).keyPressed(vt320.KEY_LEFT, ' ', mState);
return true;
case KeyEvent.KEYCODE_DPAD_UP:
((vt320) buffer).keyPressed(vt320.KEY_UP, ' ', mState);
invalidate();
return true;
case KeyEvent.KEYCODE_DPAD_DOWN:
((vt320) buffer).keyPressed(vt320.KEY_DOWN, ' ', mState);
invalidate();
return true;
case KeyEvent.KEYCODE_DPAD_RIGHT:
((vt320) buffer).keyPressed(vt320.KEY_RIGHT, ' ', mState);
return true;
case KeyEvent.KEYCODE_TAB:
((vt320) buffer).keyPressed(vt320.KEY_TAB, ' ', mState);
return true;
case KeyEvent.KEYCODE_SEARCH:
case KeyEvent.KEYCODE_DPAD_CENTER:
// TODO: Add some visual indication of Ctrl state
if (ctrlPressed) {
((vt320) buffer).keyTyped(vt320.KEY_ESCAPE, ' ', 0);
ctrlPressed = false;
} else
ctrlPressed = true;
return true;
}
return false;
}
public void resetColors() {
//TODO: Let user customize color plate
color = new int[] {
Color.rgb( 0, 0, 0), // black
Color.rgb(128, 0, 0), // red
Color.rgb( 0,128, 0), // green
Color.rgb(128,128, 0), // yellow
Color.rgb( 0, 0,128), // blue
Color.rgb(128, 0,128), // purple
Color.rgb( 0,128,128), // cyan
Color.rgb(192,192,192), // light gray
Color.rgb(128,128,128), // dark gray
Color.rgb(255, 0, 0), // light red
Color.rgb( 0,255, 0), // light green
Color.rgb(255,255, 0), // yellow
Color.rgb( 0, 0,255), // light blue
Color.rgb(255, 0,255), // light purple
Color.rgb( 0,255,255), // light cyan
Color.rgb(255,255,255) // white
};
}
public VDUBuffer getVDUBuffer() {
return buffer;
}
/*
* albb0920:
* This was used to tell OS where to pan.
* Unfortunately, the method requestFocusRect() seems to be broken with HTC CIME
* (If you request a rect that is in the middle of the screen, It will be broken, blame HTC)
* And I can't find a good way to get IME height.
* So we lie we are in that last line to force system scroll our whole view above IME.
* And we manually scroll it back. WTF
*/
public void getFocusedRect(Rect cursor){
// Report the last line
cursor.bottom = SCREEN_HEIGHT;
cursor.top = (int) (cursor.bottom - CHAR_HEIGHT);
cursor.left = 0;
cursor.right = SCREEN_HEIGHT;
}
/**
* Return color set of currAttr
* @param currAttr
* @return array(foreground,background)
*/
private int[] getColor(int currAttr){
int fg,bg;
// reset default colors
fg = DEFAULT_FG_COLOR; //albb0920: this will be convert to color later
bg = color[DEFAULT_BG_COLOR];
// check if foreground color attribute is set
if ((currAttr & ( VDUBuffer.COLOR_FG )) != 0)
fg = ((currAttr & VDUBuffer.COLOR_FG) >> VDUBuffer.COLOR_FG_SHIFT) - 1;
// bright color
if ((currAttr & VDUBuffer.BOLD) != 0)
fg |= 8;
// albb0920.100709: now we know the fg color
fg = color[fg];
// check if background color attribute is set
if ((currAttr & VDUBuffer.COLOR_BG) != 0)
bg = color[((currAttr & VDUBuffer.COLOR_BG) >> VDUBuffer.COLOR_BG_SHIFT) - 1];
// support character inversion by swapping background and
// foreground color
if ((currAttr & VDUBuffer.INVERT) != 0) {
int swapc = bg;
bg = fg;
fg = swapc;
}
// if black-on-black, try correcting to grey
// if(fg == Color.BLACK && bg == Color.BLACK) fg = Color.GRAY;
defaultPaint
.setUnderlineText((currAttr & VDUBuffer.UNDERLINE) != 0);
return new int[] {fg,bg};
}
public void redraw() {
Log.v(TAG,"redraw()");
postInvalidate(); /* render should always be done in onDraw() */
}
public void setVDUBuffer(VDUBuffer buffer) {
this.buffer = buffer;
}
/**
* Not Implemented.
* We don't even have scroll buffer. So...(ry.
*/
public void updateScrollBar() {
}
public void setColor(int index, int red, int green, int blue) {
if (index < color.length && index >= 16)
color[index] = 0xff000000 | red << 16 | green << 8 | blue;
}
protected void startConnection(final Host host) {
this.host = host;
postDelayed(new Runnable() {
@Override
public void run() {
warningLoginAgain = true;
}
}, DELAY_WARNING_LOGIN_AGAIN);
new Thread(new Runnable() {
public void run() {
byte[] b = new byte[4096];
try {
String hostProtocal = host.getProtocal();
String hostHost = host.getHost();
int hostPort = host.getPort();
if ("telnet".equalsIgnoreCase(hostProtocal)) {
connection = new TelnetWrapper();
connection.connect(hostHost, hostPort);
} /* else if ("ssh".equalsIgnoreCase(hostProtocal)) {
connection = new SshWrapper();
connection.connect(hostHost, hostPort);
} */
int delay = 200;
if(mPref.getBoolean("settings_delay_login", false))
delay = 2000;
TerminalView.this.postDelayed(new Runnable() {
public void run() {
try {
loginHost(host);
} catch (Exception e) {
e.printStackTrace();
nodifyParent(e);
}
}
}, delay);
connected = true;
while (true) {
int n = connection.read(b);
if (n > 0) {
// long start = System.currentTimeMillis();
String fullString = new String(b, 0, n, "ISO8859-1");
// Log.i(TAG, fullString);
((vt320) buffer).putString(fullString, host
.getEncoding());
// long end1 = System.currentTimeMillis();
// long end2 = System.currentTimeMillis();
// Log.i(TAG, (end1 - start) + "," + (end2 - end1));
} else if (n < 0) {
break;
}
}
nodifyParent(null);
} catch (Exception e) {
e.printStackTrace();
nodifyParent(e);
}
}
}).start();
}
public void loginAgain() {
if(warningLoginAgain) {
new AlertDialog.Builder(terminalActivity)
.setTitle(R.string.terminal_login_again)
.setMessage(R.string.terminal_login_again)
.setPositiveButton(R.string.ok, new android.content.DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
try {
loginHost(TerminalView.this.host);
} catch (IOException e) {
e.printStackTrace();
}
}
})
.setNegativeButton(R.string.cancel, null)
.create().show();
} else {
try {
loginHost(this.host);
} catch (IOException e) {
e.printStackTrace();
}
}
}
private void loginHost(final Host host) throws IOException {
String hostUser = host.getUser();
String hostPass = host.getPass();
String hostProtocal = host.getProtocal();
if ("telnet".equalsIgnoreCase(hostProtocal)) {
if (hostUser != null && hostPass != null && hostUser.length() > 0
&& hostPass.length() > 0) {
try {
connection.send(hostUser + "\r");
connection.send(hostPass + "\r");
} catch (IOException ex) {
}
}
}
}
private void nodifyParent(Exception e) {
connected = false;
terminalActivity.disconnect(e);
}
private class Url {
public int startX;
public int endX;
public int y;
public String url;
public Url(int startX, int endX, int y, String url) {
this.startX = startX;
this.endX = endX;
this.y = y;
this.url = url;
}
public boolean pointIn(int x, int y) {
return (this.y == y && x >= startX && x <= endX);
}
}
}