Permalink
Browse files

save database connection information

Use the built in SQLite3 database, encapsulated in the
PGConnectionOpenHelper class, to store database connection information.
Add a new preferences activity (PGSettings) for adding (PGAddDatabase)
and removing (PGRemoveDatabase) database connections.  Use a spinner
widget on the main screen to select the database to connect to.
  • Loading branch information...
1 parent 2e3f161 commit a2de760af1a74c25d9915cef7dd2c4f48c3b8a59 @markwkm committed Aug 19, 2010
View
@@ -9,9 +9,12 @@
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
+ <activity android:name=".PGAddDatabase"></activity>
+ <activity android:name=".PGRemoveDatabase"></activity>
+ <activity android:name=".PGSettings"></activity>
<activity android:name=".PGStatActivity"></activity>
<activity android:name=".PGStatBgwriter"></activity>
<activity android:name=".PGStatDatabase"></activity>
</application>
<uses-permission android:name="android.permission.INTERNET"></uses-permission>
-</manifest>
+</manifest>
View
1 TODO
@@ -3,5 +3,4 @@
* Document why some statistics won't be shown.
* Add support for pg_proctab to show more system stats.
* Add preferences options to adjust the refresh rate of the stats displays.
-* Add ability to save database connection information.
* Follow Android guidelines for making a launcher icon.
View
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<ScrollView android:id="@+id/widget54" android:layout_width="fill_parent"
+ android:layout_height="fill_parent" xmlns:android="http://schemas.android.com/apk/res/android">
+ <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical" android:layout_width="fill_parent"
+ android:layout_height="fill_parent">
+ <TextView android:text="Host:" android:id="@+id/TextView01"
+ android:layout_width="wrap_content" android:layout_height="wrap_content"
+ android:layout_below="@+id/heading"></TextView>
+ <EditText android:id="@+id/pghost" android:layout_width="fill_parent"
+ android:layout_height="wrap_content"></EditText>
+ <TextView android:text="Port:" android:id="@+id/TextView02"
+ android:layout_width="wrap_content" android:layout_height="wrap_content"></TextView>
+ <EditText android:id="@+id/pgport" android:layout_width="fill_parent"
+ android:layout_height="wrap_content"></EditText>
+ <TextView android:text="Database:" android:id="@+id/TextView03"
+ android:layout_width="wrap_content" android:layout_height="wrap_content"></TextView>
+ <EditText android:id="@+id/pgdatabase" android:layout_width="fill_parent"
+ android:layout_height="wrap_content"></EditText>
+ <TextView android:text="User:" android:id="@+id/TextView04"
+ android:layout_width="wrap_content" android:layout_height="wrap_content"></TextView>
+ <EditText android:id="@+id/pguser" android:layout_width="fill_parent"
+ android:layout_height="wrap_content"></EditText>
+ <TextView android:text="Password:" android:id="@+id/TextView05"
+ android:layout_width="wrap_content" android:layout_height="wrap_content"></TextView>
+ <EditText android:id="@+id/pgpassword" android:layout_width="fill_parent"
+ android:layout_height="wrap_content" android:password="true"></EditText>
+ <CheckBox android:text="Use SSL" android:id="@+id/use_ssl"
+ android:layout_width="wrap_content" android:layout_height="wrap_content"
+ android:checked="true"></CheckBox>
+ <Button android:text="Save" android:id="@+id/save"
+ android:layout_width="fill_parent" android:layout_height="wrap_content"></Button>
+ </LinearLayout>
+</ScrollView>
View
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical" android:layout_width="fill_parent"
+ android:layout_height="fill_parent">
+ <TextView android:text="Select database:"
+ android:layout_width="wrap_content" android:layout_height="wrap_content"></TextView>
+ <Spinner android:id="@+id/connection" android:layout_width="fill_parent"
+ android:layout_height="wrap_content"></Spinner>
+ <Button android:text="Remove" android:id="@+id/remove"
+ android:layout_width="fill_parent" android:layout_height="wrap_content"></Button>
+</LinearLayout>
View
@@ -1,42 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
-<ScrollView android:id="@+id/widget54" android:layout_width="fill_parent"
- android:layout_height="fill_parent" xmlns:android="http://schemas.android.com/apk/res/android">
- <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:orientation="vertical" android:layout_width="fill_parent"
- android:layout_height="fill_parent">
- <TextView android:id="@+id/heading" android:layout_width="fill_parent"
- android:layout_height="wrap_content" android:text="@string/hello"
- android:layout_alignParentTop="true" android:layout_alignParentLeft="true" />
- <TextView android:text="Host:" android:id="@+id/TextView01"
- android:layout_width="wrap_content" android:layout_height="wrap_content"
- android:layout_below="@+id/heading"></TextView>
- <EditText android:id="@+id/pghost" android:layout_width="fill_parent"
- android:layout_height="wrap_content"></EditText>
- <TextView android:text="Port:" android:id="@+id/TextView02"
- android:layout_width="wrap_content" android:layout_height="wrap_content"></TextView>
- <EditText android:id="@+id/pgport" android:layout_width="fill_parent"
- android:layout_height="wrap_content"></EditText>
- <TextView android:text="Database:" android:id="@+id/TextView03"
- android:layout_width="wrap_content" android:layout_height="wrap_content"></TextView>
- <EditText android:id="@+id/pgdatabase" android:layout_width="fill_parent"
- android:layout_height="wrap_content"></EditText>
- <TextView android:text="User:" android:id="@+id/TextView04"
- android:layout_width="wrap_content" android:layout_height="wrap_content"></TextView>
- <EditText android:id="@+id/pguser" android:layout_width="fill_parent"
- android:layout_height="wrap_content"></EditText>
- <TextView android:text="Password:" android:id="@+id/TextView05"
- android:layout_width="wrap_content" android:layout_height="wrap_content"></TextView>
- <EditText android:id="@+id/pgpassword" android:layout_width="fill_parent"
- android:layout_height="wrap_content" android:password="true"></EditText>
- <CheckBox android:text="Use SSL" android:id="@+id/use_ssl"
- android:layout_width="wrap_content" android:layout_height="wrap_content"
- android:checked="true"></CheckBox>
- <Button android:text="Monitor Database Stats" android:id="@+id/database"
- android:layout_width="fill_parent" android:layout_height="wrap_content"></Button>
- <Button android:text="Monitor Background Writer Stats"
- android:id="@+id/bgwriter" android:layout_width="fill_parent"
- android:layout_height="wrap_content"></Button>
- <Button android:text="Monitor Activity" android:id="@+id/activity"
- android:layout_width="fill_parent" android:layout_height="wrap_content"></Button>
-</LinearLayout>
-</ScrollView>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical" android:layout_width="fill_parent"
+ android:layout_height="fill_parent">
+ <TextView android:text="Select database:"
+ android:layout_width="wrap_content" android:layout_height="wrap_content"></TextView>
+ <Spinner android:id="@+id/connection" android:layout_width="fill_parent"
+ android:layout_height="wrap_content"></Spinner>
+ <Button android:text="Monitor Database Stats" android:id="@+id/database"
+ android:layout_width="fill_parent" android:layout_height="wrap_content"></Button>
+ <Button android:text="Monitor Background Writer Stats"
+ android:id="@+id/bgwriter" android:layout_width="fill_parent"
+ android:layout_height="wrap_content"></Button>
+ <Button android:text="Monitor Activity" android:id="@+id/activity"
+ android:layout_width="fill_parent" android:layout_height="wrap_content"></Button>
+</LinearLayout>
View
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical" android:layout_width="fill_parent"
+ android:layout_height="fill_parent">
+ <Button android:text="Add Database Connection" android:id="@+id/add"
+ android:layout_width="fill_parent" android:layout_height="wrap_content"></Button>
+ <Button android:text="Remove Database Connection" android:id="@+id/remove"
+ android:layout_width="fill_parent" android:layout_height="wrap_content"></Button>
+</LinearLayout>
@@ -1,4 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:id="@+id/settings" android:title="Settings" />
<item android:id="@+id/exit" android:title="Exit" />
</menu>
View
@@ -1,5 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
- <string name="hello">Welcome to PGTop for Android!</string>
<string name="app_name">PostgreSQL Top</string>
</resources>
@@ -0,0 +1,63 @@
+package org.postgresql.top;
+
+import android.app.Activity;
+import android.database.sqlite.SQLiteConstraintException;
+import android.database.sqlite.SQLiteDatabase;
+import android.os.Bundle;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.widget.Button;
+import android.widget.CheckBox;
+import android.widget.EditText;
+import android.widget.Toast;
+
+public class PGAddDatabase extends Activity implements OnClickListener {
+ public void onClick(View view) {
+ switch (view.getId()) {
+ case R.id.save:
+ final EditText pghostEditText = (EditText) findViewById(R.id.pghost);
+ final EditText pgportEditText = (EditText) findViewById(R.id.pgport);
+ final EditText pgdatabaseEditText = (EditText) findViewById(R.id.pgdatabase);
+ final EditText pguserEditText = (EditText) findViewById(R.id.pguser);
+ final EditText pgpasswordEditText = (EditText) findViewById(R.id.pgpassword);
+ final CheckBox sslCheckBox = (CheckBox) findViewById(R.id.use_ssl);
+
+ PGConnectionOpenHelper openHelper = new PGConnectionOpenHelper(
+ getApplicationContext());
+ SQLiteDatabase db = openHelper.getWritableDatabase();
+ try {
+ db
+ .execSQL("INSERT INTO "
+ + PGConnectionOpenHelper.TABLE_NAME
+ + " (host, port, database, user, password, ssl) VALUES ('"
+ + pghostEditText.getText().toString() + "', '"
+ + pgportEditText.getText().toString() + "', '"
+ + pgdatabaseEditText.getText().toString()
+ + "', '" + pguserEditText.getText().toString()
+ + "', '"
+ + pgpasswordEditText.getText().toString()
+ + "', " + (sslCheckBox.isChecked() ? "1" : "0")
+ + ");");
+ db.close();
+ finish();
+ } catch (SQLiteConstraintException e) {
+ Toast.makeText(
+ PGAddDatabase.this,
+ "These database connection parameters are already saved. "
+ + "Remove and add again to change a password.",
+ Toast.LENGTH_LONG).show();
+ db.close();
+ }
+ break;
+ }
+ }
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.db_add);
+
+ final Button activityButton = (Button) findViewById(R.id.save);
+ activityButton.setOnClickListener(this);
+ }
+}
@@ -0,0 +1,74 @@
+package org.postgresql.top;
+
+import android.content.Context;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteOpenHelper;
+import android.widget.ArrayAdapter;
+import android.widget.Spinner;
+
+public class PGConnectionOpenHelper extends SQLiteOpenHelper {
+ private static final String DATABASE_NAME = "connection.db";
+ private static final int DATABASE_VERSION = 1;
+ public static final String TABLE_NAME = "connection";
+ private static final String CREATE_TABLE = "CREATE TABLE "
+ + TABLE_NAME
+ + " (host TEXT, port INTEGER, database TEXT, user TEXT, password TEXT, ssl INTEGER, "
+ + "PRIMARY KEY (host, port, database, user, ssl));";
+
+ public static final String SELECT_CONNECTIONS = "SELECT host, port, database, user, ssl FROM "
+ + PGConnectionOpenHelper.TABLE_NAME
+ + " ORDER BY host, port, database, user, ssl;";
+
+ public PGConnectionOpenHelper(Context context) {
+ super(context, DATABASE_NAME, null, DATABASE_VERSION);
+ }
+
+ @Override
+ public void onCreate(SQLiteDatabase db) {
+ db.execSQL(CREATE_TABLE);
+ }
+
+ @Override
+ public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
+ }
+
+ public final static String getConnectionLabel(Cursor c) {
+ final int host = c.getColumnIndex("host");
+ final int port = c.getColumnIndex("port");
+ final int database = c.getColumnIndex("database");
+ final int user = c.getColumnIndex("user");
+ final int ssl = c.getColumnIndex("ssl");
+ String tmpPortString;
+
+ final String portString = c.getString(port);
+ if (portString.length() > 0) {
+ tmpPortString = ":" + portString;
+ } else {
+ tmpPortString = "";
+ }
+
+ return c.getString(host) + tmpPortString + "/" + c.getString(database)
+ + " [" + c.getString(user) + "] ("
+ + (c.getInt(ssl) == 1 ? "SSL" : "CLEAR") + ")";
+ }
+
+ public final static void populateConnectionSpinner(
+ Spinner connectionSpinner,
+ ArrayAdapter<CharSequence> spinnerAdapter, Context context) {
+ spinnerAdapter
+ .setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
+ connectionSpinner.setAdapter(spinnerAdapter);
+
+ spinnerAdapter.clear();
+
+ PGConnectionOpenHelper openHelper = new PGConnectionOpenHelper(context);
+ SQLiteDatabase db = openHelper.getReadableDatabase();
+ Cursor c = db.rawQuery(PGConnectionOpenHelper.SELECT_CONNECTIONS, null);
+ while (c.moveToNext()) {
+ spinnerAdapter.add(PGConnectionOpenHelper.getConnectionLabel(c));
+ }
+ c.close();
+ db.close();
+ }
+}
@@ -0,0 +1,94 @@
+package org.postgresql.top;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import android.app.Activity;
+import android.database.sqlite.SQLiteDatabase;
+import android.os.Bundle;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.widget.ArrayAdapter;
+import android.widget.Button;
+import android.widget.Spinner;
+import android.widget.Toast;
+
+public class PGRemoveDatabase extends Activity implements OnClickListener {
+ private Spinner connectionSpinner;
+ private ArrayAdapter<CharSequence> spinnerAdapter;
+
+ private String host;
+ private String port;
+ private String database;
+ private String user;
+ private String ssl;
+
+ public void onClick(View view) {
+ switch (view.getId()) {
+ case R.id.remove:
+ String selectedItem = (String) connectionSpinner.getSelectedItem();
+
+ Pattern pattern = Pattern
+ .compile("(.*):(.*)/(.*) \\[(.*)\\] \\((.*)\\)");
+ Matcher matcher = pattern.matcher(selectedItem);
+
+ if (matcher.find()) {
+ host = matcher.group(1);
+ port = matcher.group(2);
+ database = matcher.group(3);
+ user = matcher.group(4);
+ ssl = (matcher.group(5).equals("SSL") ? "1" : "0");
+ } else {
+ pattern = Pattern.compile("(.*)/(.*) \\[(.*)\\] \\((.*)\\)");
+ matcher = pattern.matcher(selectedItem);
+ if (matcher.find()) {
+ host = matcher.group(1);
+ port = "";
+ database = matcher.group(2);
+ user = matcher.group(3);
+ ssl = (matcher.group(4).equals("SSL") ? "1" : "0");
+ } else {
+ Toast.makeText(PGRemoveDatabase.this,
+ "Cannot figure out how to remove this connection.",
+ Toast.LENGTH_LONG).show();
+ return;
+ }
+ }
+
+ PGConnectionOpenHelper openHelper = new PGConnectionOpenHelper(
+ getApplicationContext());
+ SQLiteDatabase db = openHelper.getWritableDatabase();
+
+ db.execSQL("DELETE FROM " + PGConnectionOpenHelper.TABLE_NAME
+ + " WHERE host = '" + host + "' AND port = '" + port
+ + "' AND database = '" + database + "' AND user = '" + user
+ + "' AND ssl = " + ssl + ";");
+ db.close();
+
+ PGConnectionOpenHelper.populateConnectionSpinner(connectionSpinner,
+ spinnerAdapter, getApplicationContext());
+ break;
+ }
+ }
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.db_remove);
+
+ final Button removeButton = (Button) findViewById(R.id.remove);
+ removeButton.setOnClickListener(this);
+
+ connectionSpinner = (Spinner) findViewById(R.id.connection);
+ connectionSpinner.setPrompt("Choose a connection");
+ spinnerAdapter = new ArrayAdapter<CharSequence>(this,
+ android.R.layout.simple_spinner_item);
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ PGConnectionOpenHelper.populateConnectionSpinner(connectionSpinner,
+ spinnerAdapter, getApplicationContext());
+ }
+}
Oops, something went wrong.

0 comments on commit a2de760

Please sign in to comment.