diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/org/connectbot/ConsoleActivity.java | 77 | ||||
-rw-r--r-- | src/org/connectbot/HostEditorActivity.java | 1 | ||||
-rw-r--r-- | src/org/connectbot/HostListActivity.java | 71 | ||||
-rw-r--r-- | src/org/connectbot/service/BridgeDisconnectedListener.java | 23 | ||||
-rw-r--r-- | src/org/connectbot/service/TerminalBridge.java | 111 | ||||
-rw-r--r-- | src/org/connectbot/service/TerminalManager.java | 35 | ||||
-rw-r--r-- | src/org/connectbot/util/HostBinder.java | 25 | ||||
-rw-r--r-- | src/org/connectbot/util/HostDatabase.java | 25 |
8 files changed, 283 insertions, 85 deletions
diff --git a/src/org/connectbot/ConsoleActivity.java b/src/org/connectbot/ConsoleActivity.java index d85e085..d63f5aa 100644 --- a/src/org/connectbot/ConsoleActivity.java +++ b/src/org/connectbot/ConsoleActivity.java @@ -65,6 +65,9 @@ public class ConsoleActivity extends Activity { public void onServiceConnected(ComponentName className, IBinder service) { bound = ((TerminalManager.TerminalBinder) service).getService(); + // let manager know about our event handling services + bound.parentHandler = parentHandler; + Log.d(TAG, String.format("Connected to TerminalManager and found bridges.size=%d", bound.bridges.size())); // clear out any existing bridges and record requested index @@ -93,7 +96,7 @@ public class ConsoleActivity extends Activity { for(TerminalBridge bridge : bound.bridges) { // let them know about our password services - bridge.passwordHandler = passwordHandler; + bridge.parentHandler = parentHandler; // inflate each terminal view RelativeLayout view = (RelativeLayout)inflater.inflate(R.layout.item_terminal, flip, false); @@ -119,16 +122,18 @@ public class ConsoleActivity extends Activity { // show the requested bridge if found, also fade out overlay flip.setDisplayedChild(requestedIndex); flip.getCurrentView().findViewById(R.id.terminal_overlay).startAnimation(fade_out); - updatePasswordVisible(); + updatePromptVisible(); + updateEmptyVisible(); } public void onServiceDisconnected(ComponentName className) { // remove all bridge views for(TerminalBridge bridge : bound.bridges) - bridge.passwordHandler = null; + bridge.parentHandler = null; flip.removeAllViews(); + updateEmptyVisible(); bound = null; } @@ -139,35 +144,59 @@ public class ConsoleActivity extends Activity { if(!(view instanceof TerminalView)) return null; return ((TerminalView)view).bridge.nickname; } - - public Handler passwordHandler = new Handler() { + public final static int HANDLE_PROMPT = 1, HANDLE_DISCONNECT = 2; + + public Handler parentHandler = new Handler() { @Override public void handleMessage(Message msg) { // someone below us requested to display a password dialog // they are sending nickname and requested - String nickname = (String)msg.obj; + TerminalBridge bridge = (TerminalBridge)msg.obj; + + switch(msg.what) { + case HANDLE_PROMPT: + // someone is requesting a prompt field update + updatePromptVisible(); + break; + + case HANDLE_DISCONNECT: + // remove this bridge becase its been disconnected + Log.d(TAG, "Someone sending HANDLE_DISCONNECT to parentHandler"); + for(int i = 0; i < flip.getChildCount(); i++) { + View view = flip.getChildAt(i).findViewById(R.id.console_flip); + if(!(view instanceof TerminalView)) continue; + TerminalView terminal = (TerminalView)view; + if(terminal.bridge.equals(bridge)) { + // weve found the terminal to remove + // shift something into its place if currently visible + if(flip.getDisplayedChild() == i) { + shiftLeft(); + } + flip.removeViewAt(i); + updateEmptyVisible(); + break; + } + } + break; - // if they are currently active, then obey request - if(nickname.equals(getCurrentNickname())) { - updatePasswordVisible(); } - + } }; - protected void updatePasswordVisible() { + protected void updatePromptVisible() { // check if our currently-visible terminalbridge is requesting password services View view = findCurrentView(R.id.console_flip); boolean requested = false; if(view instanceof TerminalView) - requested = ((TerminalView)view).bridge.passwordRequested; + requested = ((TerminalView)view).bridge.promptRequested; // handle showing/hiding password field and transferring focus if(requested) { this.password.setVisibility(View.VISIBLE); this.password.setText(""); - this.password.setHint(((TerminalView)view).bridge.passwordHint); + this.password.setHint(((TerminalView)view).bridge.promptHint); this.password.requestFocus(); } else { this.password.setVisibility(View.GONE); @@ -219,6 +248,7 @@ public class ConsoleActivity extends Activity { protected ClipboardManager clipboard; protected EditText password; + protected TextView empty; protected Animation slide_left_in, slide_left_out, slide_right_in, slide_right_out, fade_stay_hidden, fade_out; @@ -237,6 +267,7 @@ public class ConsoleActivity extends Activity { this.inflater = (LayoutInflater)this.getSystemService(Context.LAYOUT_INFLATER_SERVICE); this.flip = (ViewFlipper)this.findViewById(R.id.console_flip); + this.empty = (TextView)this.findViewById(android.R.id.empty); this.password = (EditText)this.findViewById(R.id.console_password); this.password.setOnKeyListener(new OnKeyListener() { @@ -361,7 +392,13 @@ public class ConsoleActivity extends Activity { }); } + + protected void updateEmptyVisible() { + // update visibility of empty status message + this.empty.setVisibility((flip.getChildCount() == 0) ? View.VISIBLE : View.GONE); + } + protected void shiftLeft() { // keep current overlay from popping up again View overlay = findCurrentView(R.id.terminal_overlay); @@ -376,7 +413,7 @@ public class ConsoleActivity extends Activity { overlay = findCurrentView(R.id.terminal_overlay); if(overlay != null) overlay.startAnimation(fade_out); - updatePasswordVisible(); + updatePromptVisible(); } @@ -395,7 +432,7 @@ public class ConsoleActivity extends Activity { overlay = findCurrentView(R.id.terminal_overlay); if(overlay != null) overlay.startAnimation(fade_out); - updatePasswordVisible(); + updatePromptVisible(); } @@ -414,8 +451,9 @@ public class ConsoleActivity extends Activity { if(view == null) return false; TerminalView terminal = (TerminalView)view; bound.disconnect(terminal.bridge); - flip.removeView(flip.getCurrentView()); - shiftLeft(); + // movement should now be happening over in onDisconnect() handler + //flip.removeView(flip.getCurrentView()); + //shiftLeft(); return true; } }); @@ -438,10 +476,7 @@ public class ConsoleActivity extends Activity { // pull string from clipboard and generate all events to force down String clip = clipboard.getText().toString(); - KeyEvent[] events = terminal.bridge.keymap.getEvents(clip.toCharArray()); - for(KeyEvent event : events) { - terminal.bridge.onKey(terminal, event.getKeyCode(), event); - } + terminal.bridge.injectString(clip); return true; } diff --git a/src/org/connectbot/HostEditorActivity.java b/src/org/connectbot/HostEditorActivity.java index 6d6e80a..86a517c 100644 --- a/src/org/connectbot/HostEditorActivity.java +++ b/src/org/connectbot/HostEditorActivity.java @@ -211,6 +211,7 @@ public class HostEditorActivity extends PreferenceActivity implements OnSharedPr public void updateSummaries() { // for all text preferences, set hint as current database value for(String key : this.pref.values.keySet()) { + if(key.equals("postlogin")) continue; Preference pref = this.findPreference(key); if(pref == null) continue; if(pref instanceof CheckBoxPreference) continue; diff --git a/src/org/connectbot/HostListActivity.java b/src/org/connectbot/HostListActivity.java index 6958569..9cf1063 100644 --- a/src/org/connectbot/HostListActivity.java +++ b/src/org/connectbot/HostListActivity.java @@ -20,15 +20,18 @@ package org.connectbot; import java.util.regex.Pattern; +import org.connectbot.service.TerminalBridge; import org.connectbot.service.TerminalManager; import org.connectbot.util.HostBinder; import org.connectbot.util.HostDatabase; import org.connectbot.util.UpdateHelper; import android.app.Activity; +import android.app.AlertDialog; import android.app.ListActivity; import android.content.ComponentName; import android.content.Context; +import android.content.DialogInterface; import android.content.Intent; import android.content.ServiceConnection; import android.content.SharedPreferences; @@ -40,6 +43,7 @@ import android.os.Bundle; import android.os.Handler; import android.os.IBinder; import android.os.Message; +import android.preference.PreferenceManager; import android.view.ContextMenu; import android.view.KeyEvent; import android.view.Menu; @@ -90,6 +94,8 @@ public class HostListActivity extends ListActivity { // start the terminal manager service this.startService(new Intent(this, TerminalManager.class)); this.bindService(new Intent(this, TerminalManager.class), connection, Context.BIND_AUTO_CREATE); + + this.updateCursor(); } @@ -98,11 +104,16 @@ public class HostListActivity extends ListActivity { super.onStop(); this.unbindService(connection); + if(this.hosts != null) { + this.hosts.close(); + this.hosts = null; + } + } - public final static String EULA = "eula"; + public final static String PREF_EULA = "eula", PREF_SORTBYCOLOR = "sortByColor"; public final static int REQUEST_EDIT = 1; public final static int REQUEST_EULA = 2; @@ -117,7 +128,7 @@ public class HostListActivity extends ListActivity { if(resultCode == Activity.RESULT_OK) { // yay they agreed, so store that info Editor edit = prefs.edit(); - edit.putBoolean(EULA, true); + edit.putBoolean(PREF_EULA, true); edit.commit(); } else { // user didnt agree, so close @@ -134,7 +145,7 @@ public class HostListActivity extends ListActivity { } - protected boolean shortcut = false; + protected boolean makingShortcut = false; @Override public void onCreate(Bundle icicle) { @@ -143,9 +154,9 @@ public class HostListActivity extends ListActivity { // check for eula agreement - this.prefs = this.getSharedPreferences(EULA, Context.MODE_PRIVATE); + this.prefs = PreferenceManager.getDefaultSharedPreferences(this); - boolean agreed = prefs.getBoolean(EULA, false); + boolean agreed = prefs.getBoolean(PREF_EULA, false); if(!agreed) { this.startActivityForResult(new Intent(this, WizardActivity.class), REQUEST_EULA); } @@ -156,13 +167,15 @@ public class HostListActivity extends ListActivity { - this.shortcut = Intent.ACTION_CREATE_SHORTCUT.equals(getIntent().getAction()); + this.makingShortcut = Intent.ACTION_CREATE_SHORTCUT.equals(getIntent().getAction()); // connect with hosts database and populate list this.hostdb = new HostDatabase(this); ListView list = this.getListView(); - this.updateCursor(); + this.sortedByColor = prefs.getBoolean(PREF_SORTBYCOLOR, false); + this.updateCursor(); + //this.list.setSelector(R.drawable.highlight_disabled_pressed); this.COL_ID = hosts.getColumnIndexOrThrow("_id"); @@ -189,7 +202,7 @@ public class HostListActivity extends ListActivity { contents.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); - if (shortcut) { + if (makingShortcut) { // create shortcut if requested ShortcutIconResource icon = Intent.ShortcutIconResource.fromContext(HostListActivity.this, R.drawable.icon); @@ -217,6 +230,7 @@ public class HostListActivity extends ListActivity { final Pattern hostmask = Pattern.compile(".+@.+(:\\d+)?"); final TextView text = (TextView) this.findViewById(R.id.front_quickconnect); + text.setVisibility(makingShortcut ? View.GONE : View.VISIBLE); text.setOnKeyListener(new OnKeyListener() { public boolean onKey(View v, int keyCode, KeyEvent event) { @@ -259,11 +273,15 @@ public class HostListActivity extends ListActivity { }); } - + public MenuItem sortcolor, sortlast; public boolean sortedByColor = false; protected void updateCursor() { + + Editor edit = prefs.edit(); + edit.putBoolean(PREF_SORTBYCOLOR, sortedByColor); + edit.commit(); // refresh cursor because of possible sorting change if(this.hosts != null) @@ -294,9 +312,11 @@ public class HostListActivity extends ListActivity { @Override public boolean onCreateOptionsMenu(Menu menu) { super.onCreateOptionsMenu(menu); + + // dont offer menus when creating shortcut + if(makingShortcut) return true; // add host, ssh keys, about - sortcolor = menu.add("Sort by color"); sortcolor.setIcon(android.R.drawable.ic_menu_share); sortcolor.setOnMenuItemClickListener(new OnMenuItemClickListener() { @@ -343,15 +363,19 @@ public class HostListActivity extends ListActivity { AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuInfo; Cursor cursor = (Cursor) this.getListView().getItemAtPosition(info.position); - menu.setHeaderTitle(cursor.getString(COL_NICKNAME)); + final String nickname = cursor.getString(COL_NICKNAME); + menu.setHeaderTitle(nickname); final int id = cursor.getInt(COL_ID); // edit, disconnect, delete - // TODO: change disconnect/connect based on status MenuItem connect = menu.add("Disconnect"); + final TerminalBridge bridge = bound.findBridge(nickname); + connect.setEnabled((bridge != null)); connect.setOnMenuItemClickListener(new OnMenuItemClickListener() { public boolean onMenuItemClick(MenuItem item) { - return false; + bound.disconnect(bridge); + updateHandler.sendEmptyMessage(-1); + return true; } }); @@ -361,16 +385,29 @@ public class HostListActivity extends ListActivity { Intent intent = new Intent(HostListActivity.this, HostEditorActivity.class); intent.putExtra(Intent.EXTRA_TITLE, id); HostListActivity.this.startActivityForResult(intent, REQUEST_EDIT); - return false; + return true; } }); MenuItem delete = menu.add("Delete host"); delete.setOnMenuItemClickListener(new OnMenuItemClickListener() { public boolean onMenuItemClick(MenuItem item) { - hostdb.deleteHost(id); - updateHandler.sendEmptyMessage(-1); - return false; + // prompt user to make sure they really want this + new AlertDialog.Builder(HostListActivity.this) + .setMessage(String.format("Are you sure you want to delete '%s'?", nickname)) + .setPositiveButton("Yes, delete", new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + // make sure we disconnect + if(bridge != null) + bound.disconnect(bridge); + + hostdb.deleteHost(id); + updateHandler.sendEmptyMessage(-1); + } + }) + .setNegativeButton("Cancel", null).create().show(); + + return true; } }); diff --git a/src/org/connectbot/service/BridgeDisconnectedListener.java b/src/org/connectbot/service/BridgeDisconnectedListener.java new file mode 100644 index 0000000..74ab0f2 --- /dev/null +++ b/src/org/connectbot/service/BridgeDisconnectedListener.java @@ -0,0 +1,23 @@ +/* + 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 <http://www.gnu.org/licenses/>. +*/ + +package org.connectbot.service; + +public interface BridgeDisconnectedListener { + public void onDisconnected(TerminalBridge bridge); +} diff --git a/src/org/connectbot/service/TerminalBridge.java b/src/org/connectbot/service/TerminalBridge.java index ab24528..93aeb53 100644 --- a/src/org/connectbot/service/TerminalBridge.java +++ b/src/org/connectbot/service/TerminalBridge.java @@ -23,6 +23,7 @@ import java.io.InputStream; import java.io.OutputStream; import java.util.concurrent.Semaphore; +import org.connectbot.ConsoleActivity; import org.connectbot.TerminalView; import android.graphics.Bitmap; @@ -41,6 +42,7 @@ import android.view.View; import android.view.View.OnKeyListener; import com.trilead.ssh2.Connection; +import com.trilead.ssh2.ConnectionMonitor; import com.trilead.ssh2.InteractiveCallback; import com.trilead.ssh2.KnownHosts; import com.trilead.ssh2.ServerHostKeyVerifier; @@ -60,7 +62,7 @@ import de.mud.terminal.vt320; * This class also provides SSH hostkey verification prompting, and password * prompting. */ -public class TerminalBridge implements VDUDisplay, OnKeyListener, InteractiveCallback { +public class TerminalBridge implements VDUDisplay, OnKeyListener, InteractiveCallback, ConnectionMonitor { public final static String TAG = TerminalBridge.class.toString(); @@ -91,6 +93,7 @@ public class TerminalBridge implements VDUDisplay, OnKeyListener, InteractiveCal public final String nickname; protected final String username; + public String postlogin = null; protected final Connection connection; protected Session session; @@ -199,34 +202,43 @@ public class TerminalBridge implements VDUDisplay, OnKeyListener, InteractiveCal // try opening ssh connection this.outputLine(String.format("Connecting to %s:%d", hostname, port)); this.connection = new Connection(hostname, port); + this.connection.addConnectionMonitor(this); + } + + /** + * Spawn thread to open connection and start login process. + */ + public void startLogin() { new Thread(new Runnable() { - public void run() { try { connection.connect(new HostKeyVerifier()); outputLine("Trying to authenticate"); + + // TODO: insert publickey auth check here + if(connection.isAuthMethodAvailable(username, AUTH_PASSWORD)) { currentMethod = AUTH_PASSWORD; - // show auth prompt in window - requestPasswordVisible(true, "Password"); - //promptPassword(); + requestPromptVisible(true, "Password"); + } else if (connection.isAuthMethodAvailable(username, AUTH_KEYBOARDINTERACTIVE)) { currentMethod = AUTH_KEYBOARDINTERACTIVE; + // this auth method will talk with us using InteractiveCallback interface + // it blocks until that auth is finished, which means if (connection.authenticateWithKeyboardInteractive(username, TerminalBridge.this)) { - TerminalBridge.this.buffer.deleteArea(0, 0, TerminalBridge.this.buffer.getColumns(), TerminalBridge.this.buffer.getRows()); - requestPasswordVisible(false, null); + requestPromptVisible(false, null); finishConnection(); } + } else { - outputLine("Looks like your host doesn't support 'password' authentication."); - outputLine("Other auth methods, such as interactive and publickey, are still being written."); + outputLine("[Your host doesn't support 'password' or 'keyboard-interactive' authentication.]"); + } } catch (IOException e) { Log.e(TAG, "Problem in SSH connection thread", e); } } - }).start(); } @@ -240,21 +252,18 @@ public class TerminalBridge implements VDUDisplay, OnKeyListener, InteractiveCal this.redraw(); } -// protected void promptPassword() { -// this.outputLine("Password: "); -// } + public Handler parentHandler = null; - public boolean passwordRequested = false; - public Handler passwordHandler = null; - public String passwordHint = null; + public boolean promptRequested = false; + public String promptHint = null; - protected void requestPasswordVisible(boolean visible, String hint) { - this.passwordRequested = visible; - this.passwordHint = hint; + protected void requestPromptVisible(boolean visible, String hint) { + this.promptRequested = visible; + this.promptHint = hint; // pass notification up to any attached gui - if(this.passwordHandler != null) - Message.obtain(this.passwordHandler, -1, this.nickname).sendToTarget(); + if(this.parentHandler != null) + Message.obtain(this.parentHandler, ConsoleActivity.HANDLE_PROMPT, this).sendToTarget(); } /** @@ -263,11 +272,9 @@ public class TerminalBridge implements VDUDisplay, OnKeyListener, InteractiveCal public void incomingPassword(String password) { try { if (currentMethod == AUTH_PASSWORD) { - // try authenticating with given password Log.d(TAG, "Attempting to try password authentication"); if (this.connection.authenticateWithPassword(this.username, password)) { - this.buffer.deleteArea(0, 0, this.buffer.getColumns(), this.buffer.getRows()); - requestPasswordVisible(false, null); + requestPromptVisible(false, null); finishConnection(); return; } @@ -280,7 +287,18 @@ public class TerminalBridge implements VDUDisplay, OnKeyListener, InteractiveCal Log.e(TAG, "Problem while trying to authenticate with password", e); } this.outputLine("Permission denied, please try again."); -// this.promptPassword(); + } + + /** + * Inject a specific string into this terminal. Used for post-login strings + * and pasting clipboard. + */ + public void injectString(String string) { + if(string == null || string.length() == 0) return; + KeyEvent[] events = keymap.getEvents(string.toCharArray()); + for(KeyEvent event : events) { + this.onKey(null, event.getKeyCode(), event); + } } /** @@ -291,6 +309,7 @@ public class TerminalBridge implements VDUDisplay, OnKeyListener, InteractiveCal try { this.session = connection.openSession(); + buffer.deleteArea(0, 0, TerminalBridge.this.buffer.getColumns(), TerminalBridge.this.buffer.getRows()); // previously tried vt100 and xterm for emulation modes // "screen" works the best for color and escape codes @@ -326,6 +345,9 @@ public class TerminalBridge implements VDUDisplay, OnKeyListener, InteractiveCal // force font-size to make sure we resizePTY as needed this.setFontSize(this.fontSize); + + // finally send any post-login string, if requested + this.injectString(postlogin); } catch (IOException e1) { Log.e(TAG, "Problem while trying to create PTY in finishConnection()", e1); @@ -333,10 +355,16 @@ public class TerminalBridge implements VDUDisplay, OnKeyListener, InteractiveCal } + protected BridgeDisconnectedListener disconnectListener = null; + + public void setOnDisconnectedListener(BridgeDisconnectedListener disconnectListener) { + this.disconnectListener = disconnectListener; + } + /** * Force disconnection of this terminal bridge. */ - public void dispose() { + public void disconnect() { // disconnection request hangs if we havent really connected to a host yet // temporary fix is to just spawn disconnection into a thread new Thread(new Runnable() { @@ -346,6 +374,12 @@ public class TerminalBridge implements VDUDisplay, OnKeyListener, InteractiveCal connection.close(); } }).start(); + + // pass notification back up to terminal manager + // the manager will do any gui notification if applicable + if(this.disconnectListener != null) + this.disconnectListener.onDisconnected(this); + } public KeyCharacterMap keymap = KeyCharacterMap.load(KeyCharacterMap.BUILT_IN_KEYBOARD); @@ -484,6 +518,14 @@ public class TerminalBridge implements VDUDisplay, OnKeyListener, InteractiveCal int width = parent.getWidth(); int height = parent.getHeight(); + // recalculate buffer size + int termWidth = width / charWidth; + int termHeight = height / charHeight; + + // convert our height/width to integral values + width = termWidth * charWidth; + height = termHeight * charHeight; + // reallocate new bitmap if needed boolean newBitmap = (this.bitmap == null); if(this.bitmap != null) @@ -497,10 +539,6 @@ public class TerminalBridge implements VDUDisplay, OnKeyListener, InteractiveCal this.defaultPaint.setColor(Color.BLACK); this.canvas.drawRect(0, 0, width, height, this.defaultPaint); - // recalculate buffer size and update buffer - int termWidth = width / charWidth; - int termHeight = height / charHeight; - try { // request a terminal pty resize buffer.setScreenSize(termWidth, termHeight, true); @@ -624,14 +662,18 @@ public class TerminalBridge implements VDUDisplay, OnKeyListener, InteractiveCal public void updateScrollBar() { } - public String[] replyToChallenge(String name, String instruction, - int numPrompts, String[] prompt, boolean[] echo) throws Exception { + public void connectionLost(Throwable reason) { + // weve lost our ssh connection, so pass along to manager and gui + Log.e(TAG, "Somehow our underlying SSH socket died", reason); + this.disconnect(); + } + + public String[] replyToChallenge(String name, String instruction, int numPrompts, String[] prompt, boolean[] echo) throws Exception { String[] responses = new String[numPrompts]; - waitChallengeResponse = new Semaphore(0); for (int i = 0; i < numPrompts; i++) { - requestPasswordVisible(true, prompt[i]); + requestPromptVisible(true, prompt[i]); waitChallengeResponse.acquire(); responses[i] = currentChallengeResponse; } @@ -639,6 +681,5 @@ public class TerminalBridge implements VDUDisplay, OnKeyListener, InteractiveCal return responses; } - } diff --git a/src/org/connectbot/service/TerminalManager.java b/src/org/connectbot/service/TerminalManager.java index 2c5e6c2..bcc9dde 100644 --- a/src/org/connectbot/service/TerminalManager.java +++ b/src/org/connectbot/service/TerminalManager.java @@ -21,6 +21,7 @@ package org.connectbot.service; import java.util.LinkedList; import java.util.List; +import org.connectbot.ConsoleActivity; import org.connectbot.R; import org.connectbot.util.HostDatabase; @@ -29,7 +30,9 @@ import android.content.Intent; import android.content.SharedPreferences; import android.net.Uri; import android.os.Binder; +import android.os.Handler; import android.os.IBinder; +import android.os.Message; import android.preference.PreferenceManager; import android.util.Log; @@ -40,13 +43,15 @@ import android.util.Log; * * @author jsharkey */ -public class TerminalManager extends Service { +public class TerminalManager extends Service implements BridgeDisconnectedListener { public final static String TAG = TerminalManager.class.toString(); public List<TerminalBridge> bridges = new LinkedList<TerminalBridge>(); public TerminalBridge defaultBridge = null; + public List<String> disconnected = new LinkedList<String>(); + protected SharedPreferences prefs; protected String pref_emulation, pref_scrollback; @@ -64,7 +69,7 @@ public class TerminalManager extends Service { // disconnect and dispose of any existing bridges for(TerminalBridge bridge : bridges) - bridge.dispose(); + bridge.disconnect(); } @@ -83,8 +88,17 @@ public class TerminalManager extends Service { scrollback = Integer.parseInt(prefs.getString(this.pref_scrollback, "140")); } catch(Exception e) { } + + // find the post-connection string for this host + HostDatabase hostdb = new HostDatabase(this); + String postlogin = hostdb.getPostLogin(nickname); + hostdb.close(); + TerminalBridge bridge = new TerminalBridge(nickname, username, hostname, port, emulation, scrollback); + bridge.disconnectListener = this; + bridge.postlogin = postlogin; + bridge.startLogin(); this.bridges.add(bridge); // also update database with new connected time @@ -126,14 +140,29 @@ public class TerminalManager extends Service { } return null; } + + public Handler parentHandler = null; /** * Force disconnection of this {@link TerminalBridge} and remove it from our * internal list of active connections. */ public void disconnect(TerminalBridge bridge) { - bridge.dispose(); + // we will be notified about this through call back up to disconnected() + bridge.disconnect(); + } + + /** + * Called by child bridge when somehow it's been disconnected. + */ + public void onDisconnected(TerminalBridge bridge) { + // remove this bridge from our list this.bridges.remove(bridge); + this.disconnected.add(bridge.nickname); + + // pass notification back up to gui + if(this.parentHandler != null) + Message.obtain(this.parentHandler, ConsoleActivity.HANDLE_DISCONNECT, bridge).sendToTarget(); } diff --git a/src/org/connectbot/util/HostBinder.java b/src/org/connectbot/util/HostBinder.java index 927a740..39439c7 100644 --- a/src/org/connectbot/util/HostBinder.java +++ b/src/org/connectbot/util/HostBinder.java @@ -51,14 +51,19 @@ public class HostBinder implements ViewBinder { } + public final static int STATE_UNKNOWN = 1, STATE_CONNECTED = 2, STATE_DISCONNECTED = 3; + /** * Check if we're connected to a terminal with the given nickname. */ - protected boolean isConnected(String nickname) { + protected int getConnectedState(String nickname) { // always disconnected if we dont have backend service - if(this.manager == null) return false; - return (this.manager.findBridge(nickname) != null); - + if(this.manager == null) return STATE_UNKNOWN; + boolean connected = (this.manager.findBridge(nickname) != null); + boolean disconnected = (this.manager.disconnected.contains(nickname)); + if(connected) return STATE_CONNECTED; + if(disconnected) return STATE_DISCONNECTED; + return STATE_UNKNOWN; } public boolean setViewValue(View view, Cursor cursor, int columnIndex) { @@ -72,10 +77,16 @@ public class HostBinder implements ViewBinder { COL_NICKNAME = cursor.getColumnIndexOrThrow(HostDatabase.FIELD_HOST_NICKNAME); String nickname = cursor.getString(COL_NICKNAME); - if(this.isConnected(nickname)) { - icon.setImageState(new int[] { android.R.attr.state_checked }, true); - } else { + switch(this.getConnectedState(nickname)) { + case STATE_UNKNOWN: icon.setImageState(new int[] { }, true); + break; + case STATE_CONNECTED: + icon.setImageState(new int[] { android.R.attr.state_checked }, true); + break; + case STATE_DISCONNECTED: + icon.setImageState(new int[] { android.R.attr.state_expanded }, true); + break; } return true; diff --git a/src/org/connectbot/util/HostDatabase.java b/src/org/connectbot/util/HostDatabase.java index d5d1c22..0e4a30c 100644 --- a/src/org/connectbot/util/HostDatabase.java +++ b/src/org/connectbot/util/HostDatabase.java @@ -33,7 +33,7 @@ import android.database.sqlite.SQLiteOpenHelper; public class HostDatabase extends SQLiteOpenHelper { public final static String DB_NAME = "hosts"; - public final static int DB_VERSION = 8; + public final static int DB_VERSION = 9; public final static String TABLE_HOSTS = "hosts"; public final static String FIELD_HOST_NICKNAME = "nickname"; @@ -44,6 +44,7 @@ public class HostDatabase extends SQLiteOpenHelper { public final static String FIELD_HOST_LASTCONNECT = "lastconnect"; public final static String FIELD_HOST_COLOR = "color"; public final static String FIELD_HOST_USEKEYS = "usekeys"; + public final static String FIELD_HOST_POSTLOGIN = "postlogin"; public final static String COLOR_RED = "red"; public final static String COLOR_GREEN = "green"; @@ -65,7 +66,8 @@ public class HostDatabase extends SQLiteOpenHelper { + FIELD_HOST_HOSTKEY + " TEXT, " + FIELD_HOST_LASTCONNECT + " INTEGER, " + FIELD_HOST_COLOR + " TEXT, " - + FIELD_HOST_USEKEYS + " TEXT)"); + + FIELD_HOST_USEKEYS + " TEXT, " + + FIELD_HOST_POSTLOGIN + ")"); // insert a few sample hosts, none of which probably connect this.createHost(db, "connectbot@bravo", "connectbot", "192.168.254.230", 22, null); @@ -147,5 +149,24 @@ public class HostDatabase extends SQLiteOpenHelper { } + /** + * Find the post-login command string for the given nickname. + */ + public String getPostLogin(String nickname) { + + String result = null; + SQLiteDatabase db = this.getReadableDatabase(); + Cursor c = db.query(TABLE_HOSTS, new String[] { FIELD_HOST_POSTLOGIN }, + FIELD_HOST_NICKNAME + " = ?", new String[] { nickname }, null, null, null); + if(c == null || !c.moveToFirst()) { + result = null; + } else { + result = c.getString(c.getColumnIndexOrThrow(FIELD_HOST_POSTLOGIN)); + } + + return result; + + } + } |