From 5bfc8f84db11ddd47b2e818835bb40b4d5b7d4ba Mon Sep 17 00:00:00 2001 From: Kenny Root Date: Wed, 19 Nov 2008 17:12:59 +0000 Subject: * Fix disconnect prompt when in port orientation * Fix answerback to send back current terminal emulation mode * Abstract beans to allow serialization into XML --- src/org/connectbot/ConsoleActivity.java | 108 ++++++++++++------------- src/org/connectbot/bean/AbstractBean.java | 48 +++++++++++ src/org/connectbot/bean/HostBean.java | 2 +- src/org/connectbot/bean/PortForwardBean.java | 2 +- src/org/connectbot/service/TerminalBridge.java | 20 +++-- src/org/connectbot/util/XmlBuilder.java | 71 ++++++++++++++++ 6 files changed, 185 insertions(+), 66 deletions(-) create mode 100644 src/org/connectbot/bean/AbstractBean.java create mode 100644 src/org/connectbot/util/XmlBuilder.java (limited to 'src') diff --git a/src/org/connectbot/ConsoleActivity.java b/src/org/connectbot/ConsoleActivity.java index 8ab2890..1c605d8 100644 --- a/src/org/connectbot/ConsoleActivity.java +++ b/src/org/connectbot/ConsoleActivity.java @@ -84,10 +84,11 @@ public class ConsoleActivity extends Activity { protected Uri requested; protected ClipboardManager clipboard; + protected RelativeLayout stringPromptGroup; protected EditText stringPrompt; + protected RelativeLayout booleanPromptGroup; protected TextView booleanPrompt; - protected Button booleanYes, booleanNo; protected TextView empty; @@ -139,7 +140,7 @@ public class ConsoleActivity extends Activity { bridge.promptHelper.setHandler(promptHandler); bridge.refreshKeymode(); - // inflate each terminal view + // inflate each terminal view RelativeLayout view = (RelativeLayout)inflater.inflate(R.layout.item_terminal, flip, false); // set the terminal overlay text @@ -250,10 +251,10 @@ public class ConsoleActivity extends Activity { Log.e(TAG, "Problem trying to create portForward", e); } - Toast.makeText(ConsoleActivity.this, summary, Toast.LENGTH_LONG).show(); + Toast.makeText(ConsoleActivity.this, summary, Toast.LENGTH_LONG).show(); } protected View findCurrentView(int id) { - View view = this.flip.getCurrentView(); + View view = flip.getCurrentView(); if(view == null) return null; return view.findViewById(id); } @@ -268,55 +269,54 @@ public class ConsoleActivity extends Activity { View view = findCurrentView(R.id.console_flip); if(!(view instanceof TerminalView)) return null; return ((TerminalView)view).bridge.promptHelper; - } + } protected void hideAllPrompts() { - this.stringPrompt.setVisibility(View.GONE); - this.booleanPrompt.setVisibility(View.GONE); - this.booleanYes.setVisibility(View.GONE); - this.booleanNo.setVisibility(View.GONE); + stringPromptGroup.setVisibility(View.GONE); + booleanPromptGroup.setVisibility(View.GONE); } @Override public void onCreate(Bundle icicle) { super.onCreate(icicle); - this.requestWindowFeature(Window.FEATURE_NO_TITLE); + requestWindowFeature(Window.FEATURE_NO_TITLE); this.setContentView(R.layout.act_console); - this.clipboard = (ClipboardManager)this.getSystemService(CLIPBOARD_SERVICE); - this.prefs = PreferenceManager.getDefaultSharedPreferences(this); + clipboard = (ClipboardManager)getSystemService(CLIPBOARD_SERVICE); + prefs = PreferenceManager.getDefaultSharedPreferences(this); // hide status bar if requested by user - if (this.prefs.getBoolean(getString(R.string.pref_fullscreen), false)) { - this.getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, + if (prefs.getBoolean(getString(R.string.pref_fullscreen), false)) { + getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN); } // request a forced orientation if requested by user - String rotate = this.prefs.getString(getString(R.string.pref_rotation), getString(R.string.list_rotation_land)); + String rotate = prefs.getString(getString(R.string.pref_rotation), getString(R.string.list_rotation_land)); if(getString(R.string.list_rotation_land).equals(rotate)) { - this.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE); + setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE); } else if (getString(R.string.list_rotation_port).equals(rotate)) { - this.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); + setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); } PowerManager manager = (PowerManager)getSystemService(Context.POWER_SERVICE); wakelock = manager.newWakeLock(PowerManager.SCREEN_DIM_WAKE_LOCK, TAG); - this.PREF_KEEPALIVE = this.getResources().getString(R.string.pref_keepalive); + PREF_KEEPALIVE = getResources().getString(R.string.pref_keepalive); // handle requested console from incoming intent - this.requested = this.getIntent().getData(); + requested = getIntent().getData(); - this.inflater = (LayoutInflater)this.getSystemService(Context.LAYOUT_INFLATER_SERVICE); + inflater = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE); - this.flip = (ViewFlipper)this.findViewById(R.id.console_flip); - this.empty = (TextView)this.findViewById(android.R.id.empty); + flip = (ViewFlipper)findViewById(R.id.console_flip); + empty = (TextView)findViewById(android.R.id.empty); - this.stringPrompt = (EditText)this.findViewById(R.id.console_password); - this.stringPrompt.setOnKeyListener(new OnKeyListener() { + stringPromptGroup = (RelativeLayout) findViewById(R.id.console_password_group); + stringPrompt = (EditText)findViewById(R.id.console_password); + stringPrompt.setOnKeyListener(new OnKeyListener() { public boolean onKey(View v, int keyCode, KeyEvent event) { if(event.getAction() == KeyEvent.ACTION_UP) return false; if(keyCode != KeyEvent.KEYCODE_ENTER) return false; @@ -336,10 +336,11 @@ public class ConsoleActivity extends Activity { } }); - this.booleanPrompt = (TextView)this.findViewById(R.id.console_prompt); + booleanPromptGroup = (RelativeLayout) findViewById(R.id.console_boolean_group); + booleanPrompt = (TextView)findViewById(R.id.console_prompt); - this.booleanYes = (Button)this.findViewById(R.id.console_prompt_yes); - this.booleanYes.setOnClickListener(new OnClickListener() { + booleanYes = (Button)findViewById(R.id.console_prompt_yes); + booleanYes.setOnClickListener(new OnClickListener() { public void onClick(View v) { PromptHelper helper = getCurrentPromptHelper(); if(helper == null) return; @@ -348,8 +349,8 @@ public class ConsoleActivity extends Activity { } }); - this.booleanNo = (Button)this.findViewById(R.id.console_prompt_no); - this.booleanNo.setOnClickListener(new OnClickListener() { + booleanNo = (Button)findViewById(R.id.console_prompt_no); + booleanNo.setOnClickListener(new OnClickListener() { public void onClick(View v) { PromptHelper helper = getCurrentPromptHelper(); if(helper == null) return; @@ -359,13 +360,13 @@ public class ConsoleActivity extends Activity { }); // preload animations for terminal switching - this.slide_left_in = AnimationUtils.loadAnimation(this, R.anim.slide_left_in); - this.slide_left_out = AnimationUtils.loadAnimation(this, R.anim.slide_left_out); - this.slide_right_in = AnimationUtils.loadAnimation(this, R.anim.slide_right_in); - this.slide_right_out = AnimationUtils.loadAnimation(this, R.anim.slide_right_out); + slide_left_in = AnimationUtils.loadAnimation(this, R.anim.slide_left_in); + slide_left_out = AnimationUtils.loadAnimation(this, R.anim.slide_left_out); + slide_right_in = AnimationUtils.loadAnimation(this, R.anim.slide_right_in); + slide_right_out = AnimationUtils.loadAnimation(this, R.anim.slide_right_out); - this.fade_out = AnimationUtils.loadAnimation(this, R.anim.fade_out); - this.fade_stay_hidden = AnimationUtils.loadAnimation(this, R.anim.fade_stay_hidden); + fade_out = AnimationUtils.loadAnimation(this, R.anim.fade_out); + fade_stay_hidden = AnimationUtils.loadAnimation(this, R.anim.fade_stay_hidden); // detect fling gestures to switch between terminals final GestureDetector detect = new GestureDetector(new GestureDetector.SimpleOnGestureListener() { @@ -417,7 +418,7 @@ public class ConsoleActivity extends Activity { TerminalView terminal = (TerminalView)flip; // estimate how many rows we have scrolled through - // accumulate distance that doesn't trigger immediate scroll + // accumulate distance that doesn't trigger immediate scroll totalY += distanceY; int moved = (int)(totalY / terminal.bridge.charHeight); @@ -670,11 +671,11 @@ public class ConsoleActivity extends Activity { // connect with manager service to find all bridges // when connected it will insert all views - this.bindService(new Intent(this, TerminalManager.class), connection, Context.BIND_AUTO_CREATE); + bindService(new Intent(this, TerminalManager.class), connection, Context.BIND_AUTO_CREATE); // make sure we dont let the screen fall asleep // this also keeps the wifi chipset from disconnecting us - if(this.wakelock != null && prefs.getBoolean(PREF_KEEPALIVE, true)) + if(wakelock != null && prefs.getBoolean(PREF_KEEPALIVE, true)) wakelock.acquire(); } @@ -682,10 +683,10 @@ public class ConsoleActivity extends Activity { @Override public void onStop() { super.onStop(); - this.unbindService(connection); + unbindService(connection); // allow the screen to dim and fall asleep - if(this.wakelock != null && this.wakelock.isHeld()) + if(wakelock != null && wakelock.isHeld()) wakelock.release(); } @@ -699,7 +700,7 @@ public class ConsoleActivity extends Activity { View overlay = findCurrentView(R.id.terminal_overlay); if(overlay != null) overlay.startAnimation(fade_stay_hidden); - flip.setInAnimation(slide_left_in); + flip.setInAnimation(slide_left_in); flip.setOutAnimation(slide_left_out); flip.showNext(); ConsoleActivity.this.updateDefault(); @@ -749,7 +750,7 @@ public class ConsoleActivity extends Activity { protected void updateEmptyVisible() { // update visibility of empty status message - this.empty.setVisibility((flip.getChildCount() == 0) ? View.VISIBLE : View.GONE); + empty.setVisibility((flip.getChildCount() == 0) ? View.VISIBLE : View.GONE); } /** @@ -760,30 +761,25 @@ public class ConsoleActivity extends Activity { View view = findCurrentView(R.id.console_flip); if(!(view instanceof TerminalView)) { // we dont have an active view, so hide any prompts - this.hideAllPrompts(); + hideAllPrompts(); return; } PromptHelper prompt = ((TerminalView)view).bridge.promptHelper; if(String.class.equals(prompt.promptRequested)) { - this.stringPrompt.setVisibility(View.VISIBLE); - this.stringPrompt.setText(""); - this.stringPrompt.setHint(prompt.promptHint); - this.stringPrompt.requestFocus(); + stringPromptGroup.setVisibility(View.VISIBLE); + stringPrompt.setText(""); + stringPrompt.setHint(prompt.promptHint); + stringPrompt.requestFocus(); } else if(Boolean.class.equals(prompt.promptRequested)) { - this.booleanPrompt.setVisibility(View.VISIBLE); - this.booleanPrompt.setText(prompt.promptHint); - this.booleanYes.setVisibility(View.VISIBLE); - this.booleanYes.requestFocus(); - this.booleanNo.setVisibility(View.VISIBLE); + booleanPromptGroup.setVisibility(View.VISIBLE); + booleanPrompt.setText(prompt.promptHint); + booleanYes.requestFocus(); } else { - this.hideAllPrompts(); + hideAllPrompts(); view.requestFocus(); - } - } - } diff --git a/src/org/connectbot/bean/AbstractBean.java b/src/org/connectbot/bean/AbstractBean.java new file mode 100644 index 0000000..3f855ad --- /dev/null +++ b/src/org/connectbot/bean/AbstractBean.java @@ -0,0 +1,48 @@ +/* + ConnectBot: simple, powerful, open-source SSH client for Android + Copyright (C) 2007-2008 Kenny Root, Jeffrey Sharkey + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ +package org.connectbot.bean; + +import java.util.Map.Entry; + +import org.connectbot.util.XmlBuilder; + +import android.content.ContentValues; + +/** + * @author Kenny Root + * + */ +public abstract class AbstractBean { + public abstract ContentValues getValues(); + + public String toXML() { + XmlBuilder xml = new XmlBuilder(); + + xml.append(""); + + ContentValues values = getValues(); + for (Entry entry : values.valueSet()) { + Object value = entry.getValue(); + if (value != null) + xml.append(entry.getKey(), value); + } + xml.append(""); + + return xml.toString(); + } +} diff --git a/src/org/connectbot/bean/HostBean.java b/src/org/connectbot/bean/HostBean.java index 81fe5dc..3ee48d5 100644 --- a/src/org/connectbot/bean/HostBean.java +++ b/src/org/connectbot/bean/HostBean.java @@ -25,7 +25,7 @@ import android.content.ContentValues; * @author kenny * */ -public class HostBean { +public class HostBean extends AbstractBean { /* Database fields */ private long id = -1; private String nickname = null; diff --git a/src/org/connectbot/bean/PortForwardBean.java b/src/org/connectbot/bean/PortForwardBean.java index 6cbf6fa..50d9a3b 100644 --- a/src/org/connectbot/bean/PortForwardBean.java +++ b/src/org/connectbot/bean/PortForwardBean.java @@ -26,7 +26,7 @@ import android.content.ContentValues; * @author Kenny Root * */ -public class PortForwardBean { +public class PortForwardBean extends AbstractBean { /* Database fields */ private long id = -1; private long hostId = -1; diff --git a/src/org/connectbot/service/TerminalBridge.java b/src/org/connectbot/service/TerminalBridge.java index eb93f23..5f63b30 100644 --- a/src/org/connectbot/service/TerminalBridge.java +++ b/src/org/connectbot/service/TerminalBridge.java @@ -299,6 +299,7 @@ public class TerminalBridge implements VDUDisplay, OnKeyListener, InteractiveCal connection.connect(new HostKeyVerifier()); } catch (IOException e) { Log.e(TAG, "Problem in SSH connection thread during authentication", e); + // TODO report cause to user: Log.d(TAG, String.format("Cause is: %s", e.getCause().toString())); dispatchDisconnect(); return; @@ -539,15 +540,10 @@ public class TerminalBridge implements VDUDisplay, OnKeyListener, InteractiveCal // previously tried vt100 and xterm for emulation modes // "screen" works the best for color and escape codes // TODO: pull this value from the preferences + ((vt320) buffer).setAnswerBack(emulation); session.requestPTY(emulation, termWidth, termHeight, 0, 0, null); session.startShell(); - new Thread(new Runnable() { - public void run() { - session.waitForCondition(ChannelCondition.CLOSED, 0); - } - }).start(); - // grab stdin/out from newly formed session stdin = session.getStdin(); stdout = session.getStdout(); @@ -560,7 +556,8 @@ public class TerminalBridge implements VDUDisplay, OnKeyListener, InteractiveCal int n = 0; int conditions = ChannelCondition.STDOUT_DATA | ChannelCondition.STDERR_DATA - | ChannelCondition.CLOSED; + | ChannelCondition.CLOSED + | ChannelCondition.EXIT_STATUS; int newConditions = 0; while((newConditions & ChannelCondition.CLOSED) == 0) { try { @@ -575,9 +572,16 @@ public class TerminalBridge implements VDUDisplay, OnKeyListener, InteractiveCal if ((newConditions & ChannelCondition.STDERR_DATA) == ChannelCondition.STDERR_DATA) { n = stderr.read(b); - /* I don't know.. do we want this? */ + // TODO I don't know.. do we want this? We were ignoring it before Log.d(TAG, String.format("Read data from stderr: %s", new String(b, 0, n, ENCODING))); } + + if ((newConditions & ChannelCondition.EXIT_STATUS) == ChannelCondition.EXIT_STATUS) { + // The other side closed our channel, so let's disconnect. + // TODO review whether any tunnel is in use currently. + dispatchDisconnect(); + break; + } } catch (IOException e) { Log.e(TAG, "Problem while handling incoming data in relay thread", e); break; diff --git a/src/org/connectbot/util/XmlBuilder.java b/src/org/connectbot/util/XmlBuilder.java new file mode 100644 index 0000000..d2c2923 --- /dev/null +++ b/src/org/connectbot/util/XmlBuilder.java @@ -0,0 +1,71 @@ +/* + ConnectBot: simple, powerful, open-source SSH client for Android + Copyright (C) 2007-2008 Kenny Root, Jeffrey Sharkey + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ +package org.connectbot.util; + +import com.trilead.ssh2.crypto.Base64; + +/** + * @author Kenny Root + * + */ +public class XmlBuilder { + private StringBuilder sb; + + public XmlBuilder() { + sb = new StringBuilder(); + } + + public XmlBuilder append(String data) { + sb.append(data); + + return this; + } + + public XmlBuilder append(String field, Object data) { + if (data == null) { + sb.append(String.format("<%s/>", field)); + } else if (data instanceof String) { + String input = (String) data; + boolean binary = false; + + for (byte b : input.getBytes()) { + if (b < 0x20 || b > 0x7e) { + binary = true; + break; + } + } + + sb.append(String.format("<%s>%s", field, + binary ? new String(Base64.encode(input.getBytes())) : input, field)); + } else if (data instanceof Integer) { + sb.append(String.format("<%s>%d", field, (Integer) data, field)); + } else if (data instanceof Long) { + sb.append(String.format("<%s>%d", field, (Long) data, field)); + } else if (data instanceof byte[]) { + sb.append(String.format("<%s>%s", field, new String(Base64.encode((byte[]) data)), field)); + } else if (data instanceof Boolean) { + sb.append(String.format("<%s>%s", field, (Boolean) data, field)); + } + + return this; + } + + public String toString() { + return sb.toString(); + } +} -- cgit v1.2.3