aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--AndroidManifest.xml42
-rw-r--r--res/color/blue.xml6
-rw-r--r--res/color/green.xml6
-rw-r--r--res/color/red.xml6
-rw-r--r--res/drawable/highlight_disabled_pressed.9.pngbin0 -> 898 bytes
-rw-r--r--res/layout-land/item_host.xml59
-rw-r--r--res/layout-port/item_host.xml34
-rw-r--r--res/values/arrays.xml7
-rw-r--r--res/xml/host_prefs.xml41
-rw-r--r--src/com/trilead/ssh2/channel/ChannelManager.java3
-rw-r--r--src/org/connectbot/Console.java1
-rw-r--r--src/org/connectbot/HostEditor.java208
-rw-r--r--src/org/connectbot/HostList.java259
-rw-r--r--src/org/connectbot/R.java113
-rw-r--r--src/org/connectbot/util/HostAdapter.java128
-rw-r--r--src/org/connectbot/util/HostDatabase.java101
-rw-r--r--src/org/theb/ssh/HostEditor.java2
-rw-r--r--src/org/theb/ssh/HostsList.java2
-rw-r--r--src/org/theb/ssh/PasswordDialog.java2
-rw-r--r--src/org/theb/ssh/Pubkey.java2
-rw-r--r--src/org/theb/ssh/SecureShell.java2
-rw-r--r--src/org/theb/ssh/TouchEntropy.java2
22 files changed, 990 insertions, 36 deletions
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 083076f..a046a20 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -1,20 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
-<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="org.theb.ssh">
- <application android:icon="@drawable/icon" android:label="ConnectBot">
+<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="org.connectbot">
+ <application android:icon="@drawable/icon" android:label="newcb">
-<!--
- <activity android:name=".FrontPage">
+ <activity android:name=".HostList">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
</activity>
- <activity android:name=".Console" android:screenOrientation="landscape">
+ <activity android:name=".HostEditor">
</activity>
+
+<!-- pack=org.theb.ssh
- <activity android:name=".HostEditor" android:screenOrientation="landscape">
- <intent-filter>
- <action android:name="android.intent.action.MAIN" />
- <category android:name="android.intent.category.LAUNCHER" />
- </intent-filter>
+ <activity android:name=".Console" android:screenOrientation="landscape">
</activity>
@@ -25,14 +26,9 @@
<activity android:name="org.connectbot.Console" android:screenOrientation="landscape">
</activity>
- <provider android:name="HostDbProvider"
- android:authorities="org.theb.provider.HostDb"/>
+ <provider android:name="org.theb.ssh.HostDbProvider" android:authorities="org.theb.provider.HostDb"/>
- <activity android:name="HostsList" android:label="@string/title_hosts_list">
- <intent-filter>
- <action android:name="android.intent.action.MAIN" />
- <category android:name="android.intent.category.LAUNCHER" />
- </intent-filter>
+ <activity android:name="org.theb.ssh.HostsList" android:label="@string/title_hosts_list">
<intent-filter>
<action android:name="android.intent.action.PICK" />
<action android:name="android.intent.action.VIEW" />
@@ -42,7 +38,7 @@
</intent-filter>
</activity>
-
+ <!--
<activity android:name="HostEditor" android:label="@string/title_host"
android:theme="@android:style/Theme.Dialog">
<intent-filter android:label="@string/resolve_edit">
@@ -64,8 +60,8 @@
<data android:mimeType="vnd.android.cursor.dir/vnd.theb.host" />
</intent-filter>
</activity>
-
- <activity android:name="SecureShell" android:label="@string/title_shell">
+ -->
+ <activity android:name="org.theb.ssh.SecureShell" android:label="@string/title_shell">
<intent-filter android:label="@string/resolve_connect">
<action android:name="org.theb.ssh.action.CONNECT_HOST" />
<action android:name="android.intent.action.PICK" />
@@ -74,15 +70,15 @@
</intent-filter>
</activity>
- <activity android:name="PasswordDialog" android:label="@string/title_password"
+ <activity android:name="org.theb.ssh.PasswordDialog" android:label="@string/title_password"
android:theme="@android:style/Theme.Dialog">
</activity>
- <activity android:name="TouchEntropy" android:label="@string/title_entropy"
+ <activity android:name="org.theb.ssh.TouchEntropy" android:label="@string/title_entropy"
android:theme="@android:style/Theme.Dialog">
</activity>
- <activity android:name="Pubkey" android:label="@string/title_pubkey"
+ <activity android:name="org.theb.ssh.Pubkey" android:label="@string/title_pubkey"
android:theme="@android:style/Theme.Dialog">
</activity>
diff --git a/res/color/blue.xml b/res/color/blue.xml
new file mode 100644
index 0000000..8111aeb
--- /dev/null
+++ b/res/color/blue.xml
@@ -0,0 +1,6 @@
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_selected="true" android:color="#000" />
+ <item android:state_focused="true" android:color="#000" />
+ <item android:state_pressed="true" android:color="#000" />
+ <item android:color="#88f" />
+</selector>
diff --git a/res/color/green.xml b/res/color/green.xml
new file mode 100644
index 0000000..a8c12ab
--- /dev/null
+++ b/res/color/green.xml
@@ -0,0 +1,6 @@
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_selected="true" android:color="#000" />
+ <item android:state_focused="true" android:color="#000" />
+ <item android:state_pressed="true" android:color="#000" />
+ <item android:color="#8f8" />
+</selector>
diff --git a/res/color/red.xml b/res/color/red.xml
new file mode 100644
index 0000000..2b5f6c6
--- /dev/null
+++ b/res/color/red.xml
@@ -0,0 +1,6 @@
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_selected="true" android:color="#000" />
+ <item android:state_focused="true" android:color="#000" />
+ <item android:state_pressed="true" android:color="#000" />
+ <item android:color="#f00" />
+</selector>
diff --git a/res/drawable/highlight_disabled_pressed.9.png b/res/drawable/highlight_disabled_pressed.9.png
new file mode 100644
index 0000000..807fcb5
--- /dev/null
+++ b/res/drawable/highlight_disabled_pressed.9.png
Binary files differ
diff --git a/res/layout-land/item_host.xml b/res/layout-land/item_host.xml
new file mode 100644
index 0000000..34abd59
--- /dev/null
+++ b/res/layout-land/item_host.xml
@@ -0,0 +1,59 @@
+<?xml version="1.0" encoding="utf-8"?>
+<RelativeLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:padding="10dip"
+ >
+
+ <ImageView
+ android:id="@+id/host_connected"
+ android:src="@android:drawable/presence_online"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentRight="true"
+ android:paddingTop="5dip"
+ />
+
+ <!--
+
+ <TextView android:id="@+id/host_title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textAppearance="?android:attr/textAppearanceLarge"
+ />
+
+ <TextView android:id="@+id/host_caption"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:layout_below="@id/host_title"
+ />
+
+ -->
+
+ <LinearLayout
+ android:orientation="horizontal"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ >
+
+ <TextView android:id="@+id/host_title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textAppearance="?android:attr/textAppearanceLarge"
+ android:textColor="@color/blue"
+ />
+
+ <TextView android:id="@+id/host_caption"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:paddingLeft="10dip"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:textColor="@color/blue"
+ />
+
+
+ </LinearLayout>
+
+</RelativeLayout> \ No newline at end of file
diff --git a/res/layout-port/item_host.xml b/res/layout-port/item_host.xml
new file mode 100644
index 0000000..7fa381e
--- /dev/null
+++ b/res/layout-port/item_host.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<RelativeLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:padding="10dip"
+ >
+
+ <ImageView
+ android:id="@+id/host_connected"
+ android:src="@android:drawable/presence_online"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentRight="true"
+ android:paddingTop="5dip"
+ />
+
+ <TextView android:id="@+id/host_title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textAppearance="?android:attr/textAppearanceLarge"
+ android:textColor="@color/blue"
+ />
+
+ <TextView android:id="@+id/host_caption"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:textColor="@color/blue"
+ android:layout_below="@id/host_title"
+ />
+
+
+</RelativeLayout> \ No newline at end of file
diff --git a/res/values/arrays.xml b/res/values/arrays.xml
index c1cdefe..f4ef7c3 100644
--- a/res/values/arrays.xml
+++ b/res/values/arrays.xml
@@ -7,4 +7,11 @@
<item>xterm</item>
</string-array>
+
+ <string-array name="list_colors">
+ <item>red</item>
+ <item>green</item>
+ <item>blue</item>
+ </string-array>
+
</resources>
diff --git a/res/xml/host_prefs.xml b/res/xml/host_prefs.xml
new file mode 100644
index 0000000..9786416
--- /dev/null
+++ b/res/xml/host_prefs.xml
@@ -0,0 +1,41 @@
+<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
+
+ <EditTextPreference
+ android:key="nickname"
+ android:title="Nickname"
+ />
+
+ <ListPreference
+ android:key="color"
+ android:title="Color category"
+ android:entries="@array/list_colors"
+ android:entryValues="@array/list_colors"
+ />
+
+ <CheckBoxPreference
+ android:key="usekeys"
+ android:title="Use SSH keys"
+ />
+
+ <PreferenceCategory
+ android:title="Connection settings">
+
+ <EditTextPreference
+ android:key="username"
+ android:title="Username"
+ />
+
+ <EditTextPreference
+ android:key="hostname"
+ android:title="Host"
+ />
+
+ <EditTextPreference
+ android:key="port"
+ android:title="Port"
+ />
+
+ </PreferenceCategory>
+
+
+</PreferenceScreen> \ No newline at end of file
diff --git a/src/com/trilead/ssh2/channel/ChannelManager.java b/src/com/trilead/ssh2/channel/ChannelManager.java
index 74906d3..e582a52 100644
--- a/src/com/trilead/ssh2/channel/ChannelManager.java
+++ b/src/com/trilead/ssh2/channel/ChannelManager.java
@@ -698,7 +698,8 @@ public class ChannelManager implements MessageHandler
}
try {
- waitForChannelSuccessOrFailure(c);
+ //waitForChannelSuccessOrFailure(c);
+ this.waitForChannelRequestResult(c);
} catch (IOException e) {
throw (IOException) new IOException("PTY request failed")
.initCause(e);
diff --git a/src/org/connectbot/Console.java b/src/org/connectbot/Console.java
index 2895ed5..4493ac7 100644
--- a/src/org/connectbot/Console.java
+++ b/src/org/connectbot/Console.java
@@ -5,7 +5,6 @@ import org.connectbot.service.TerminalBridgeSurface;
import org.connectbot.service.TerminalManager;
import org.theb.ssh.InteractiveHostKeyVerifier;
-import org.theb.ssh.R;
import com.trilead.ssh2.Connection;
import de.mud.terminal.vt320;
diff --git a/src/org/connectbot/HostEditor.java b/src/org/connectbot/HostEditor.java
index 373897d..97ddc14 100644
--- a/src/org/connectbot/HostEditor.java
+++ b/src/org/connectbot/HostEditor.java
@@ -1,15 +1,213 @@
package org.connectbot;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+import org.connectbot.util.HostDatabase;
+
+
+
import android.app.Activity;
+import android.content.ContentValues;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteDatabase;
import android.os.Bundle;
-import org.theb.ssh.R;
+import android.preference.CheckBoxPreference;
+import android.preference.Preference;
+import android.preference.PreferenceActivity;
+import android.util.Log;
+
+public class HostEditor extends PreferenceActivity implements OnSharedPreferenceChangeListener {
+
+
+ public class CursorPreferenceHack implements SharedPreferences {
+
+ protected final SQLiteDatabase db;
+ protected final String table;
+ protected final int id;
+
+ protected Map<String, String> values = new HashMap<String, String>();
+
+ public CursorPreferenceHack(SQLiteDatabase db, String table, int id) {
+ this.db = db;
+ this.table = table;
+ this.id = id;
+
+ this.cacheValues();
+
+ }
+
+ protected void cacheValues() {
+ // fill a cursor and cache the values locally
+ // this makes sure we dont have any floating cursor to dispose later
+
+ Cursor cursor = db.query(table, null, "_id = ?",
+ new String[] { Integer.toString(id) }, null, null, null);
+ cursor.moveToFirst();
+
+ for(int i = 0; i < cursor.getColumnCount(); i++) {
+ String key = cursor.getColumnName(i);
+ String value = cursor.getString(i);
+ values.put(key, value);
+ }
+
+ cursor.close();
+
+ }
+
+ public boolean contains(String key) {
+ return values.containsKey(key);
+ }
+
+ public class Editor implements SharedPreferences.Editor {
+
+ public ContentValues update = new ContentValues();
+
+ public SharedPreferences.Editor clear() {
+ Log.d(this.getClass().toString(), "clear()");
+ update = new ContentValues();
+ return this;
+ }
+
+ public boolean commit() {
+ Log.d(this.getClass().toString(), "commit() changes back to database");
+ db.update(table, update, "_id = ?", new String[] { Integer.toString(id) });
+
+ // make sure we refresh the parent cached values
+ cacheValues();
+
+ // and update any listeners
+ for(OnSharedPreferenceChangeListener listener : listeners) {
+ listener.onSharedPreferenceChanged(CursorPreferenceHack.this, null);
+ }
+
+ return true;
+ }
+
+ public android.content.SharedPreferences.Editor putBoolean(String key, boolean value) {
+ return this.putString(key, Boolean.toString(value));
+ }
+
+ public android.content.SharedPreferences.Editor putFloat(String key, float value) {
+ return this.putString(key, Float.toString(value));
+ }
+
+ public android.content.SharedPreferences.Editor putInt(String key, int value) {
+ return this.putString(key, Integer.toString(value));
+ }
+
+ public android.content.SharedPreferences.Editor putLong(String key, long value) {
+ return this.putString(key, Long.toString(value));
+ }
+
+ public android.content.SharedPreferences.Editor putString(String key, String value) {
+ Log.d(this.getClass().toString(), String.format("Editor.putString(key=%s, value=%s)", key, value));
+ update.put(key, value);
+ return this;
+ }
+
+ public android.content.SharedPreferences.Editor remove(String key) {
+ Log.d(this.getClass().toString(), String.format("Editor.remove(key=%s)", key));
+ update.remove(key);
+ return this;
+ }
+
+ }
+
-public class HostEditor extends Activity {
+ public Editor edit() {
+ Log.d(this.getClass().toString(), "edit()");
+ return new Editor();
+ }
+
+ public Map<String, ?> getAll() {
+ return values;
+ }
+
+ public boolean getBoolean(String key, boolean defValue) {
+ return Boolean.valueOf(this.getString(key, Boolean.toString(defValue)));
+ }
+
+ public float getFloat(String key, float defValue) {
+ return Float.valueOf(this.getString(key, Float.toString(defValue)));
+ }
+
+ public int getInt(String key, int defValue) {
+ return Integer.valueOf(this.getString(key, Integer.toString(defValue)));
+ }
+
+ public long getLong(String key, long defValue) {
+ return Long.valueOf(this.getString(key, Long.toString(defValue)));
+ }
+
+ public String getString(String key, String defValue) {
+ Log.d(this.getClass().toString(), String.format("getString(key=%s, defValue=%s)", key, defValue));
+
+ if(!values.containsKey(key)) return defValue;
+ return values.get(key);
+ }
+
+ public List<OnSharedPreferenceChangeListener> listeners = new LinkedList<OnSharedPreferenceChangeListener>();
+
+ public void registerOnSharedPreferenceChangeListener(OnSharedPreferenceChangeListener listener) {
+ listeners.add(listener);
+ }
+
+ public void unregisterOnSharedPreferenceChangeListener(OnSharedPreferenceChangeListener listener) {
+ listeners.remove(listener);
+ }
+
+ }
+
+
+ @Override
+ public SharedPreferences getSharedPreferences(String name, int mode) {
+ Log.d(this.getClass().toString(), String.format("getSharedPreferences(name=%s)", name));
+ return this.pref;
+ }
+
+ public CursorPreferenceHack pref;
@Override
- public void onCreate(Bundle icicle) {
- super.onCreate(icicle);
- setContentView(R.layout.act_hosteditor);
+ public void onCreate(Bundle icicle) {
+ super.onCreate(icicle);
+
+ HostDatabase db = new HostDatabase(this);
+ int id = this.getIntent().getIntExtra(Intent.EXTRA_TITLE, -1);
+
+ // TODO: we could pass through a specific ContentProvider uri here
+ //this.getPreferenceManager().setSharedPreferencesName(uri);
+
+ this.pref = new CursorPreferenceHack(db.getWritableDatabase(), db.TABLE_HOSTS, id);
+ this.pref.registerOnSharedPreferenceChangeListener(this);
+
+ this.addPreferencesFromResource(R.xml.host_prefs);
+
+ this.updateSummaries();
+
+
+ }
+
+ public void updateSummaries() {
+ // for all text preferences, set hint as current database value
+ for(String key : this.pref.values.keySet()) {
+ Preference pref = this.findPreference(key);
+ if(pref == null) continue;
+ if(pref instanceof CheckBoxPreference) continue;
+ pref.setSummary(this.pref.getString(key, ""));
+ }
+
+ }
+
+ public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
+ // update values on changed preference
+ this.updateSummaries();
+
}
}
diff --git a/src/org/connectbot/HostList.java b/src/org/connectbot/HostList.java
new file mode 100644
index 0000000..3689fbc
--- /dev/null
+++ b/src/org/connectbot/HostList.java
@@ -0,0 +1,259 @@
+package org.connectbot;
+
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.regex.Pattern;
+
+import org.connectbot.service.TerminalBridge;
+import org.connectbot.service.TerminalManager;
+import org.connectbot.util.HostAdapter;
+import org.connectbot.util.HostDatabase;
+import org.theb.ssh.InteractiveHostKeyVerifier;
+
+import com.trilead.ssh2.Connection;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.database.Cursor;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.view.ContextMenu;
+import android.view.KeyEvent;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.MenuItem.OnMenuItemClickListener;
+import android.view.View.OnClickListener;
+import android.view.View.OnKeyListener;
+import android.widget.AdapterView;
+import android.widget.ListView;
+import android.widget.SimpleAdapter;
+import android.widget.TextView;
+import android.widget.AdapterView.OnItemClickListener;
+
+public class HostList extends Activity {
+
+ public TerminalManager bound = null;
+
+ private ServiceConnection connection = new ServiceConnection() {
+ public void onServiceConnected(ComponentName className, IBinder service) {
+ bound = ((TerminalManager.TerminalBinder) service).getService();
+
+ // TODO: update our green bulb icons by checking for existing
+ // bridges
+ // open up some test sessions
+ // try {
+ // bound.openConnection("192.168.254.230", 22, "connectbot", "b0tt",
+ // "screen", 100);
+ // bound.openConnection("192.168.254.230", 22, "connectbot", "b0tt",
+ // "screen", 100);
+ // bound.openConnection("192.168.254.230", 22, "connectbot", "b0tt",
+ // "screen", 100);
+ // } catch(Exception e) {
+ // e.printStackTrace();
+ // }
+
+ }
+
+ public void onServiceDisconnected(ComponentName className) {
+ bound = null;
+ }
+ };
+
+ public HostDatabase hostdb;
+ public Cursor hosts;
+ public ListView list;
+ public HostAdapter adapter;
+
+ public int COL_ID, COL_NICKNAME, COL_USERNAME, COL_HOSTNAME, COL_CONNECTED;
+
+ @Override
+ public void onCreate(Bundle icicle) {
+ super.onCreate(icicle);
+ setContentView(R.layout.act_frontpage);
+
+ // start the terminal manager service
+ this.startService(new Intent(this, TerminalManager.class));
+ this.bindService(new Intent(this, TerminalManager.class), connection,
+ Context.BIND_AUTO_CREATE);
+
+ // connect with hosts database and populate list
+ this.hostdb = new HostDatabase(this);
+ this.list = (ListView) this.findViewById(R.id.front_hostlist);
+ this.updateCursor();
+
+ //this.list.setSelector(R.drawable.highlight_disabled_pressed);
+
+ this.COL_ID = hosts.getColumnIndexOrThrow("_id");
+ this.COL_NICKNAME = hosts.getColumnIndexOrThrow(HostDatabase.FIELD_HOST_NICKNAME);
+ this.COL_USERNAME = hosts.getColumnIndexOrThrow(HostDatabase.FIELD_HOST_USERNAME);
+ this.COL_HOSTNAME = hosts.getColumnIndexOrThrow(HostDatabase.FIELD_HOST_HOSTNAME);
+ this.COL_CONNECTED = hosts.getColumnIndexOrThrow(HostDatabase.FIELD_HOST_LASTCONNECT);
+
+ this.list.setOnItemClickListener(new OnItemClickListener() {
+
+ public void onItemClick(AdapterView<?> parent, View view,
+ int position, long id) {
+
+ // launch off to console details
+ // TODO: package information about connection selected
+ HostList.this.startActivity(new Intent(HostList.this,
+ Console.class));
+
+ }
+
+ });
+
+ this.registerForContextMenu(this.list);
+
+ final Pattern hostmask = Pattern.compile(".+@.+(:\\d+)?");
+ final TextView text = (TextView) this
+ .findViewById(R.id.front_quickconnect);
+ text.setOnKeyListener(new OnKeyListener() {
+
+ public boolean onKey(View v, int keyCode, KeyEvent event) {
+
+ // make sure we follow pattern
+ if (text.getText().length() < 1)
+ return false;
+
+ // TODO: only show error when trying to hit enter
+ if (!hostmask.matcher(text.getText().toString()).find()) {
+ text.setError("Use the format 'username@hostname:port'");
+ }
+
+ // set list filter based on text
+ // String filter = text.getText().toString();
+ // list.setTextFilterEnabled((filter.length() > 0));
+ // list.setFilterText(filter);
+
+ // TODO Auto-generated method stub
+ return false;
+ }
+
+ });
+
+ }
+
+ public MenuItem sortcolor, sortlast;
+ public boolean sortedByColor = false;
+
+ public void updateCursor() {
+
+ // refresh cursor because of possible sorting change
+ if(this.hosts != null)
+ this.hosts.close();
+ this.hosts = this.hostdb.allHosts(sortedByColor);
+ this.adapter = new HostAdapter(this, this.hosts);
+ this.list.setAdapter(adapter);
+
+ }
+
+ @Override
+ public boolean onPrepareOptionsMenu(Menu menu) {
+ super.onPrepareOptionsMenu(menu);
+
+ sortcolor.setVisible(!sortedByColor);
+ sortlast.setVisible(sortedByColor);
+
+ return true;
+
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ super.onCreateOptionsMenu(menu);
+
+ // add host, ssh keys, about
+
+ MenuItem add = menu.add(0, 0, Menu.NONE, "New host");
+ add.setIcon(android.R.drawable.ic_menu_add);
+ add.setOnMenuItemClickListener(new OnMenuItemClickListener() {
+ public boolean onMenuItemClick(MenuItem item) {
+ return false;
+ }
+ });
+
+ sortcolor = menu.add(0, 0, Menu.NONE, "Sort by color");
+ sortcolor.setIcon(android.R.drawable.ic_menu_share);
+ sortcolor.setOnMenuItemClickListener(new OnMenuItemClickListener() {
+ public boolean onMenuItemClick(MenuItem item) {
+ sortedByColor = true;
+ updateCursor();
+ return false;
+ }
+ });
+
+ sortlast = menu.add(0, 0, Menu.NONE, "Sort by last");
+ sortlast.setIcon(android.R.drawable.ic_menu_share);
+ sortlast.setOnMenuItemClickListener(new OnMenuItemClickListener() {
+ public boolean onMenuItemClick(MenuItem item) {
+ sortedByColor = false;
+ updateCursor();
+ return false;
+ }
+ });
+
+ MenuItem keys = menu.add(0, 0, Menu.NONE, "Manage keys");
+ keys.setIcon(android.R.drawable.ic_lock_lock);
+
+ MenuItem settings = menu.add(0, 0, Menu.NONE, "Settings");
+ settings.setIcon(android.R.drawable.ic_menu_preferences);
+
+ MenuItem about = menu.add(0, 0, Menu.NONE, "About");
+ about.setIcon(android.R.drawable.ic_menu_help);
+
+ return true;
+
+ }
+
+ @Override
+ public void onCreateContextMenu(ContextMenu menu, View v,
+ ContextMenu.ContextMenuInfo menuInfo) {
+
+ // create menu to handle hosts
+
+ // create menu to handle deleting and sharing lists
+ AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuInfo;
+ Cursor cursor = (Cursor) this.list.getItemAtPosition(info.position);
+
+ menu.setHeaderTitle(cursor.getString(COL_NICKNAME));
+ final int id = cursor.getInt(COL_ID);
+
+ // edit, disconnect, delete
+ // TODO: change disconnect/connect based on status
+ MenuItem connect = menu.add(0, 0, Menu.NONE, "Disconnect");
+ connect.setOnMenuItemClickListener(new OnMenuItemClickListener() {
+ public boolean onMenuItemClick(MenuItem item) {
+ return false;
+ }
+ });
+
+ MenuItem edit = menu.add(0, 0, Menu.NONE, "Edit host");
+ edit.setOnMenuItemClickListener(new OnMenuItemClickListener() {
+ public boolean onMenuItemClick(MenuItem item) {
+ Intent intent = new Intent(HostList.this, HostEditor.class);
+ intent.putExtra(Intent.EXTRA_TITLE, id);
+ HostList.this.startActivity(intent);
+ return false;
+ }
+ });
+
+ MenuItem delete = menu.add(0, 0, Menu.NONE, "Delete host");
+ delete.setOnMenuItemClickListener(new OnMenuItemClickListener() {
+ public boolean onMenuItemClick(MenuItem item) {
+ return false;
+ }
+ });
+
+ }
+
+}
diff --git a/src/org/connectbot/R.java b/src/org/connectbot/R.java
new file mode 100644
index 0000000..b1a337f
--- /dev/null
+++ b/src/org/connectbot/R.java
@@ -0,0 +1,113 @@
+/* AUTO-GENERATED FILE. DO NOT MODIFY.
+ *
+ * This class was automatically generated by the
+ * aapt tool from the resource data it found. It
+ * should not be modified by hand.
+ */
+
+package org.connectbot;
+
+public final class R {
+ public static final class anim {
+ public static final int fade_out=0x7f040000;
+ public static final int fade_stay_hidden=0x7f040001;
+ public static final int slide_left_in=0x7f040002;
+ public static final int slide_left_out=0x7f040003;
+ public static final int slide_right_in=0x7f040004;
+ public static final int slide_right_out=0x7f040005;
+ }
+ public static final class array {
+ public static final int list_colors=0x7f060001;
+ public static final int list_emulation_modes=0x7f060000;
+ }
+ public static final class attr {
+ }
+ public static final class color {
+ public static final int blue=0x7f080000;
+ public static final int green=0x7f080001;
+ public static final int red=0x7f080002;
+ }
+ public static final class drawable {
+ public static final int blue=0x7f020002;
+ public static final int even_stripe=0x7f020003;
+ public static final int highlight_disabled_pressed=0x7f020000;
+ public static final int icon=0x7f020001;
+ public static final int odd_stripe=0x7f020004;
+ }
+ public static final class id {
+ public static final int add=0x7f090011;
+ public static final int cancel=0x7f090012;
+ public static final int console_flip=0x7f090002;
+ public static final int copyright=0x7f090001;
+ public static final int dismiss=0x7f090019;
+ public static final int edit_emulation=0x7f090009;
+ public static final int edit_hostname=0x7f090007;
+ public static final int edit_nickname=0x7f090005;
+ public static final int edit_port=0x7f090008;
+ public static final int edit_scrollback=0x7f09000a;
+ public static final int edit_username=0x7f090006;
+ public static final int front_hostlist=0x7f090003;
+ public static final int front_quickconnect=0x7f090004;
+ public static final int generate=0x7f09001d;
+ public static final int host_caption=0x7f090015;
+ public static final int host_connected=0x7f090013;
+ public static final int host_title=0x7f090014;
+ public static final int hostname=0x7f09000e;
+ public static final int hostnameLabel=0x7f09000d;
+ public static final int icon=0x7f090000;
+ public static final int keyName=0x7f09001e;
+ public static final int message=0x7f090018;
+ public static final int ok=0x7f09001c;
+ public static final int password=0x7f09001b;
+ public static final int passwordLabel=0x7f09001a;
+ public static final int port=0x7f090010;
+ public static final int portLabel=0x7f09000f;
+ public static final int shell=0x7f090017;
+ public static final int terminal=0x7f09001f;
+ public static final int terminal_overlay=0x7f090016;
+ public static final int username=0x7f09000c;
+ public static final int usernameLabel=0x7f09000b;
+ }
+ public static final class layout {
+ public static final int about_dialog=0x7f030000;
+ public static final int act_console=0x7f030001;
+ public static final int act_frontpage=0x7f030002;
+ public static final int act_hosteditor=0x7f030003;
+ public static final int host_editor=0x7f030004;
+ public static final int item_host=0x7f030005;
+ public static final int item_terminal=0x7f030006;
+ public static final int main=0x7f030007;
+ public static final int message_dialog=0x7f030008;
+ public static final int password_dialog=0x7f030009;
+ public static final int pubkey=0x7f03000a;
+ public static final int secure_shell=0x7f03000b;
+ }
+ public static final class string {
+ public static final int alert_disconnect_msg=0x7f070014;
+ public static final int app_name=0x7f070000;
+ public static final int button_add=0x7f070010;
+ public static final int button_cancel=0x7f070011;
+ public static final int button_change=0x7f070012;
+ public static final int button_generate=0x7f070013;
+ public static final int button_ok=0x7f07000f;
+ public static final int menu_about=0x7f07000d;
+ public static final int menu_delete=0x7f07000a;
+ public static final int menu_insert=0x7f070009;
+ public static final int menu_preferences=0x7f07000b;
+ public static final int menu_pubkey=0x7f07000c;
+ public static final int msg_copyright=0x7f070015;
+ public static final int msg_version=0x7f070016;
+ public static final int prompt_touch=0x7f07000e;
+ public static final int resolve_connect=0x7f070008;
+ public static final int resolve_edit=0x7f070007;
+ public static final int title_entropy=0x7f070006;
+ public static final int title_host=0x7f070002;
+ public static final int title_hosts_list=0x7f070001;
+ public static final int title_password=0x7f070004;
+ public static final int title_pubkey=0x7f070005;
+ public static final int title_shell=0x7f070003;
+ }
+ public static final class xml {
+ public static final int host_prefs=0x7f050000;
+ }
+}
diff --git a/src/org/connectbot/util/HostAdapter.java b/src/org/connectbot/util/HostAdapter.java
new file mode 100644
index 0000000..3dec83c
--- /dev/null
+++ b/src/org/connectbot/util/HostAdapter.java
@@ -0,0 +1,128 @@
+package org.connectbot.util;
+
+
+import org.connectbot.R;
+
+import android.content.Context;
+import android.content.res.ColorStateList;
+import android.content.res.Resources;
+import android.database.Cursor;
+import android.graphics.Bitmap;
+import android.os.Handler;
+import android.os.Message;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.BaseAdapter;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+
+public class HostAdapter extends BaseAdapter {
+
+
+ public final Context context;
+ public final LayoutInflater inflater;
+ public final Cursor source;
+
+ public final static String TABLE_HOSTS = "hosts";
+ public final static String FIELD_HOST_NICKNAME = "nickname";
+ public final static String FIELD_HOST_USERNAME = "username";
+ public final static String FIELD_HOST_HOSTNAME = "hostname";
+ public final static String FIELD_HOST_PORT = "port";
+ public final static String FIELD_HOST_HOSTKEY = "hostkey";
+ public final static String FIELD_HOST_CONNECTED = "connected";
+
+ public final int COL_ID, COL_NICKNAME, COL_USERNAME, COL_HOSTNAME, COL_CONNECTED, COL_COLOR;
+
+ public final ColorStateList red, green, blue;
+
+ public HostAdapter(Context context, Cursor source) {
+ this.context = context;
+ this.inflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ this.source = source;
+
+ this.COL_ID = source.getColumnIndexOrThrow("_id");
+ this.COL_NICKNAME = source.getColumnIndexOrThrow(HostDatabase.FIELD_HOST_NICKNAME);
+ this.COL_USERNAME = source.getColumnIndexOrThrow(HostDatabase.FIELD_HOST_USERNAME);
+ this.COL_HOSTNAME = source.getColumnIndexOrThrow(HostDatabase.FIELD_HOST_HOSTNAME);
+ this.COL_CONNECTED = source.getColumnIndexOrThrow(HostDatabase.FIELD_HOST_LASTCONNECT);
+ this.COL_COLOR = source.getColumnIndexOrThrow(HostDatabase.FIELD_HOST_COLOR);
+
+ Resources res = this.context.getResources();
+ this.red = res.getColorStateList(R.color.red);
+ this.green = res.getColorStateList(R.color.green);
+ this.blue = res.getColorStateList(R.color.blue);
+
+ }
+
+ public Object getItem(int position) {
+ source.moveToPosition(position);
+ return source;
+ }
+
+ public boolean hasStableIds() {
+ return true;
+ }
+
+ public int getCount() {
+ return source.getCount();
+ }
+
+ public long getItemId(int position) {
+ return position;
+ }
+
+ protected ColorStateList resolve(String color) {
+ if(HostDatabase.COLOR_RED.equals(color)) return this.red;
+ if(HostDatabase.COLOR_GREEN.equals(color)) return this.green;
+ if(HostDatabase.COLOR_BLUE.equals(color)) return this.blue;
+ return null;
+ }
+
+ public synchronized View getView(int position, View convertView, ViewGroup parent) {
+
+ this.source.moveToPosition(position);
+
+ if(convertView == null) {
+ convertView = this.inflater.inflate(R.layout.item_host, parent, false);
+ }
+
+ String nice = "never";
+ int minutes = ((int)(System.currentTimeMillis() / 1000) - source.getInt(COL_CONNECTED)) / 60;
+ if(minutes > 0) {
+ nice = String.format("%d minutes ago", minutes);
+ if(minutes >= 60) {
+ int hours = minutes / 60;
+ nice = String.format("%d hours ago", hours);
+ if(hours >= 24) {
+ int days = hours / 24;
+ nice = String.format("%d days ago", days);
+ }
+ }
+ }
+
+ boolean connected = true;
+
+ TextView title = (TextView)convertView.findViewById(R.id.host_title);
+ title.setText(source.getString(COL_NICKNAME));
+
+ TextView caption = (TextView)convertView.findViewById(R.id.host_caption);
+ caption.setText(String.format("%s%s", nice, connected ? ", connected" : ""));
+
+ // correctly update text color as needed
+ ColorStateList resolved = this.resolve(source.getString(COL_COLOR));
+ if(resolved != null) {
+ title.setTextColor(resolved);
+ caption.setTextColor(resolved);
+ }
+
+ ((ImageView)convertView.findViewById(R.id.host_connected)).setImageResource(connected ? android.R.drawable.presence_online : android.R.drawable.presence_offline);
+
+ // update icon correctly if service is connected
+
+ return convertView;
+ }
+
+} \ No newline at end of file
diff --git a/src/org/connectbot/util/HostDatabase.java b/src/org/connectbot/util/HostDatabase.java
new file mode 100644
index 0000000..bbc27f9
--- /dev/null
+++ b/src/org/connectbot/util/HostDatabase.java
@@ -0,0 +1,101 @@
+package org.connectbot.util;
+
+import java.util.LinkedList;
+import java.util.List;
+
+import android.content.ContentValues;
+import android.content.Context;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteOpenHelper;
+
+
+public class HostDatabase extends SQLiteOpenHelper {
+
+ public final static String DB_NAME = "hosts";
+ public final static int DB_VERSION = 4;
+
+ public final static String TABLE_HOSTS = "hosts";
+ public final static String FIELD_HOST_NICKNAME = "nickname";
+ public final static String FIELD_HOST_USERNAME = "username";
+ public final static String FIELD_HOST_HOSTNAME = "hostname";
+ public final static String FIELD_HOST_PORT = "port";
+ public final static String FIELD_HOST_HOSTKEY = "hostkey";
+ public final static String FIELD_HOST_LASTCONNECT = "lastconnect";
+ public final static String FIELD_HOST_COLOR = "color";
+
+ public final static String TABLE_PRIVKEYS = "keys";
+ public final static String FIELD_KEY_NAME = "name";
+ public final static String FIELD_KEY_PRIVATE = "private";
+
+ public final static String COLOR_RED = "red";
+ public final static String COLOR_GREEN = "green";
+ public final static String COLOR_BLUE = "blue";
+
+ public HostDatabase(Context context) {
+ super(context, DB_NAME, null, DB_VERSION);
+ }
+
+ @Override
+ public void onCreate(SQLiteDatabase db) {
+ db.execSQL("CREATE TABLE " + TABLE_HOSTS
+ + " (_id INTEGER PRIMARY KEY, "
+ + FIELD_HOST_NICKNAME + " TEXT, "
+ + FIELD_HOST_USERNAME + " TEXT, "
+ + FIELD_HOST_HOSTNAME + " TEXT, "
+ + FIELD_HOST_PORT + " INTEGER, "
+ + FIELD_HOST_HOSTKEY + " TEXT, "
+ + FIELD_HOST_LASTCONNECT + " INTEGER, "
+ + FIELD_HOST_COLOR + " TEXT)");
+
+ db.execSQL("CREATE TABLE " + TABLE_PRIVKEYS
+ + " (_id INTEGER PRIMARY KEY, "
+ + FIELD_KEY_NAME + " TEXT, "
+ + FIELD_KEY_PRIVATE + " TEXT)");
+
+ this.createHost(db, "connectbot@bravo", "connectbot", "192.168.254.230", 22, COLOR_RED);
+ this.createHost(db, "root@google.com", "root", "google.com", 22, COLOR_GREEN);
+ this.createHost(db, "cron@server.example.com", "cron", "server.example.com", 22, COLOR_BLUE);
+ this.createHost(db, "backup@example.net", "backup", "example.net", 22, COLOR_RED);
+
+ }
+
+ @Override
+ public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
+ db.execSQL("DROP TABLE IF EXISTS " + TABLE_HOSTS);
+ db.execSQL("DROP TABLE IF EXISTS " + TABLE_PRIVKEYS);
+ onCreate(db);
+ }
+
+ public long createHost(SQLiteDatabase db, String nickname, String username, String hostname, int port, String color) {
+ // create and insert new host
+
+ if(db == null) db = this.getWritableDatabase();
+
+ ContentValues values = new ContentValues();
+ values.put(FIELD_HOST_NICKNAME, nickname);
+ values.put(FIELD_HOST_USERNAME, username);
+ values.put(FIELD_HOST_HOSTNAME, hostname);
+ values.put(FIELD_HOST_PORT, port);
+ values.put(FIELD_HOST_LASTCONNECT, Integer.MAX_VALUE);
+ if(color != null)
+ values.put(FIELD_HOST_COLOR, color);
+
+ return db.insert(TABLE_HOSTS, null, values);
+
+ }
+
+ public Cursor allHosts(boolean sortColors) {
+
+ String sortField = sortColors ? FIELD_HOST_COLOR : FIELD_HOST_LASTCONNECT;
+
+ SQLiteDatabase db = this.getReadableDatabase();
+ return db.query(TABLE_HOSTS, new String[] { "_id", FIELD_HOST_NICKNAME,
+ FIELD_HOST_USERNAME, FIELD_HOST_HOSTNAME, FIELD_HOST_PORT,
+ FIELD_HOST_HOSTKEY, FIELD_HOST_LASTCONNECT, FIELD_HOST_COLOR },
+ null, null, null, null, sortField + " DESC");
+
+ }
+
+
+}
diff --git a/src/org/theb/ssh/HostEditor.java b/src/org/theb/ssh/HostEditor.java
index 11bb32b..2b31ccd 100644
--- a/src/org/theb/ssh/HostEditor.java
+++ b/src/org/theb/ssh/HostEditor.java
@@ -18,7 +18,7 @@
*/
package org.theb.ssh;
-import org.theb.ssh.R;
+import org.connectbot.R;
import org.theb.provider.HostDb;
import org.theb.provider.HostDb.Hosts;
diff --git a/src/org/theb/ssh/HostsList.java b/src/org/theb/ssh/HostsList.java
index 1db0f8a..5760f46 100644
--- a/src/org/theb/ssh/HostsList.java
+++ b/src/org/theb/ssh/HostsList.java
@@ -23,7 +23,7 @@ import java.util.concurrent.Semaphore;
import org.connectbot.Console;
import org.connectbot.service.TerminalBridge;
import org.connectbot.service.TerminalManager;
-import org.theb.ssh.R;
+import org.connectbot.R;
import org.theb.provider.HostDb;
import com.trilead.ssh2.Connection;
diff --git a/src/org/theb/ssh/PasswordDialog.java b/src/org/theb/ssh/PasswordDialog.java
index adcca36..4bbe89e 100644
--- a/src/org/theb/ssh/PasswordDialog.java
+++ b/src/org/theb/ssh/PasswordDialog.java
@@ -18,7 +18,7 @@
*/
package org.theb.ssh;
-import org.theb.ssh.R;
+import org.connectbot.R;
import android.app.Activity;
import android.content.Intent;
diff --git a/src/org/theb/ssh/Pubkey.java b/src/org/theb/ssh/Pubkey.java
index 95abba7..b39bcab 100644
--- a/src/org/theb/ssh/Pubkey.java
+++ b/src/org/theb/ssh/Pubkey.java
@@ -26,7 +26,7 @@ import java.security.SecureRandom;
import java.security.Security;
import java.util.concurrent.Semaphore;
-import org.theb.ssh.R;
+import org.connectbot.R;
import android.app.Activity;
import android.content.Intent;
diff --git a/src/org/theb/ssh/SecureShell.java b/src/org/theb/ssh/SecureShell.java
index e2bb4f4..9c2d125 100644
--- a/src/org/theb/ssh/SecureShell.java
+++ b/src/org/theb/ssh/SecureShell.java
@@ -21,7 +21,7 @@ package org.theb.ssh;
import java.io.IOException;
import java.io.OutputStream;
-import org.theb.ssh.R;
+import org.connectbot.R;
import org.theb.provider.HostDb;
import com.trilead.ssh2.ConnectionMonitor;
diff --git a/src/org/theb/ssh/TouchEntropy.java b/src/org/theb/ssh/TouchEntropy.java
index fc633b0..772585e 100644
--- a/src/org/theb/ssh/TouchEntropy.java
+++ b/src/org/theb/ssh/TouchEntropy.java
@@ -1,6 +1,6 @@
package org.theb.ssh;
-import org.theb.ssh.R;
+import org.connectbot.R;
import android.app.Activity;
import android.content.Context;