diff --git a/res/values/strings.xml b/res/values/strings.xml index 9f99ba1..562149c 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -3,4 +3,6 @@ CouchDB Stop CouchDB Delete Databases +CouchDB is Loading... +Are you sure you want to delete, this will delete all of your existing data? diff --git a/src/org/couchdb/android/CouchCtrlListener.java b/src/org/couchdb/android/CouchCtrlListener.java index 7ef1f6b..6f1b4ce 100644 --- a/src/org/couchdb/android/CouchCtrlListener.java +++ b/src/org/couchdb/android/CouchCtrlListener.java @@ -28,7 +28,7 @@ public CouchCtrlListener(String couchUrl, String db, String user, public void start() { - Log.v(CouchFutonActivity.TAG, "Starting Listener for " + ctrl); + Log.v(CouchProcess.TAG, "Starting Listener for " + ctrl); if (!running) { try { @@ -82,7 +82,7 @@ private void changes(int seq) throws JSONException { + "/_changes?include_docs=true&feed=longpoll&since=" + Integer.toString(seq); JSONObject json = HTTPRequest.get(url, headers()).json; - Log.v(CouchFutonActivity.TAG, "Received Changes for " + ctrl); + Log.v(CouchProcess.TAG, "Received Changes for " + ctrl); seq = json.getInt("last_seq"); JSONArray results = json.getJSONArray("results"); @@ -91,13 +91,13 @@ private void changes(int seq) throws JSONException { handleChange(results.getJSONObject(i).getJSONObject("doc")); } } - Log.v(CouchFutonActivity.TAG, "Changes listener on " + ctrl + " has stopped"); + Log.v(CouchProcess.TAG, "Changes listener on " + ctrl + " has stopped"); running = false; cancelled = false; } public void cancel() { - Log.v(CouchFutonActivity.TAG, "Cancelling changes listener for " + ctrl); + Log.v(CouchProcess.TAG, "Cancelling changes listener for " + ctrl); cancelled = true; } diff --git a/src/org/couchdb/android/CouchFutonActivity.java b/src/org/couchdb/android/CouchFutonActivity.java index bcd660b..8cf89dc 100644 --- a/src/org/couchdb/android/CouchFutonActivity.java +++ b/src/org/couchdb/android/CouchFutonActivity.java @@ -16,7 +16,6 @@ import android.os.IBinder; import android.os.Message; import android.os.RemoteException; -import android.util.Log; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; @@ -28,20 +27,57 @@ public class CouchFutonActivity extends Activity { - public final static String TAG = "CouchDB"; - public final static String FUTON = "http://127.0.0.1:5984/_utils/"; + private CouchProcess couch = CouchProcess.getInstance(); private ProgressDialog loading; - private ICouchService couchService; - public Boolean serviceStarted = false; - + private WebView webView; + private static final int COUCH_STARTED = 1; + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + attemptLaunch(); + } + + @Override + public void onRestart() { + super.onRestart(); + attemptLaunch(); + } + + @Override + protected void onDestroy() { + super.onDestroy(); + if (webView != null) { + webView.destroy(); + } + if (couchService != null) { + unbindService(mConnection); + } + } + + /* + * Checks to see if Couch is fully installed, if not prompt to complete + * installation otherwise start the couchdb service + */ + private void attemptLaunch() { + if (!CouchInstaller.checkInstalled()) { + startActivity(new Intent(this, CouchInstallActivity.class)); + } else if (!CouchProcess.getInstance().couchStarted) { + String msg = this.getString(R.string.loading_dialog); + loading = ProgressDialog.show(this, "", msg, true); + bindService(new Intent(ICouchService.class.getName()), mConnection, Context.BIND_AUTO_CREATE); + } + } + + /* + * This holds the connection to the CouchDB Service + */ private ServiceConnection mConnection = new ServiceConnection() { public void onServiceConnected(ComponentName className, IBinder service) { try { - serviceStarted = true; couchService = ICouchService.Stub.asInterface(service); couchService.initCouchDB(mCallback); } catch (RemoteException e) { @@ -53,86 +89,43 @@ public void onServiceDisconnected(ComponentName className) { couchService = null; } }; - - private void setFutonView() { - - String user = CouchProcess.getInstance().adminUser; - String pass = CouchProcess.getInstance().adminPass; - - WebView webView = new WebView(CouchFutonActivity.this); - webView.setWebChromeClient(new WebChromeClient()); - webView.setWebViewClient(new CustomWebViewClient()); - webView.setHttpAuthUsernamePassword("127.0.0.1", "administrator", user, pass); - webView.getSettings().setJavaScriptEnabled(true); - webView.getSettings().setBuiltInZoomControls(true); - webView.getSettings().setCacheMode(WebSettings.LOAD_NO_CACHE); - setContentView(webView); - webView.loadUrl(FUTON); - }; - - private class CustomWebViewClient extends WebViewClient { + + /* + * Implement the callbacks that allow CouchDB to talk to this app + */ + private ICouchClient mCallback = new ICouchClient.Stub() { @Override - public boolean shouldOverrideUrlLoading(WebView view, String url) { - view.loadUrl(url); - return true; + public void couchStarted(String host, int port) throws RemoteException { + mHandler.sendMessage(mHandler.obtainMessage(COUCH_STARTED)); } @Override - public void onReceivedHttpAuthRequest(WebView view, - HttpAuthHandler handler, String host, String realm) { - String[] up = view.getHttpAuthUsernamePassword(host, realm); - handler.proceed(up[0], up[1]); + public void databaseCreated(String name, String user, String pass, + String tag) throws RemoteException { } - } - - private void showLoading() { - loading = ProgressDialog.show(this, "", "CouchDB is Loading...", true); - } + }; - private Boolean deleteDirectory(File dir) { - if (dir.isDirectory()) { - String[] children = dir.list(); - for (int i = 0; i < children.length; i++) { - boolean success = deleteDirectory(new File(dir, children[i])); - if (!success) { - return false; - } + /* + * Because the service communication happens in a seperate thread, we need + * a message handler to control the ui in this thread + */ + private Handler mHandler = new Handler() { + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case COUCH_STARTED: + loading.dismiss(); + launchFuton(); + break; + default: + super.handleMessage(msg); } } - return dir.delete(); - } - - private void deleteDatabases() { - unbindService(mConnection); - CouchProcess.getInstance().stopCouchDB(); - Log.v(TAG, "DELETING EVERYTHING"); - File couchDir = new File(Environment.getExternalStorageDirectory(), - "couch"); - // ARG THIS IS TOTALLY SCARY AND WRONG - deleteDirectory(couchDir); - finish(); - } - - private void confirmDelete() { - AlertDialog.Builder builder = new AlertDialog.Builder(this); - builder.setMessage( - "Are you sure you want to delete, this will delete all of your existing data?") - .setCancelable(false) - .setPositiveButton("Yes", - new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int id) { - CouchFutonActivity.this.deleteDatabases(); - } - }) - .setNegativeButton("No", new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int id) { - dialog.cancel(); - } - }); - AlertDialog alert = builder.create(); - alert.show(); - } + }; + /* + * Creates the menu items + */ @Override public boolean onCreateOptionsMenu(Menu menu) { MenuInflater inflater = getMenuInflater(); @@ -140,6 +133,9 @@ public boolean onCreateOptionsMenu(Menu menu) { return true; }; + /* + * Handles the menu item callbacks + */ @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { @@ -154,61 +150,82 @@ public boolean onOptionsItemSelected(MenuItem item) { } } - @Override - protected void onDestroy() { - super.onDestroy(); - if (couchService != null) { - unbindService(mConnection); - } + /* + * Confirm that the user wants to delete their databases + */ + private void confirmDelete() { + AlertDialog.Builder builder = new AlertDialog.Builder(this); + builder.setMessage(this.getString(R.string.confirm_delete)) + .setCancelable(false) + .setPositiveButton("Yes", + new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int id) { + CouchFutonActivity.this.deleteDatabases(); + } + }) + .setNegativeButton("No", new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int id) { + dialog.cancel(); + } + }); + AlertDialog alert = builder.create(); + alert.show(); } - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - boot(); + /* + * Because we store the database files on the sdcard, uninstalling the application + * will persist the databases on reinstall, this will delete the entire + * couch directory on the sdcard and quit the application, the next time the + * user starts up again they will be prompted to reinstall couch + * + * However this is dangerous and messy and the functionality should probably + * be removed entirely + */ + private void deleteDatabases() { + unbindService(mConnection); + CouchProcess.getInstance().stopCouchDB(); + File couchDir = new File(Environment.getExternalStorageDirectory(), "couch"); + deleteDirectory(couchDir); + finish(); } - - @Override - public void onRestart() { - super.onRestart(); - boot(); - }; - - private void boot() { - boolean installed = CouchInstaller.checkInstalled(); - if (!installed && !serviceStarted) { - startActivity(new Intent(this, CouchInstallActivity.class)); - } else if (!serviceStarted) { - showLoading(); - bindService(new Intent(ICouchService.class.getName()), mConnection, - Context.BIND_AUTO_CREATE); - } + + private void launchFuton() { + webView = new WebView(CouchFutonActivity.this); + webView.setWebChromeClient(new WebChromeClient()); + webView.setWebViewClient(new CustomWebViewClient()); + webView.setHttpAuthUsernamePassword(couch.host, "administrator", couch.adminUser, couch.adminPass); + webView.getSettings().setJavaScriptEnabled(true); + webView.getSettings().setBuiltInZoomControls(true); + webView.getSettings().setCacheMode(WebSettings.LOAD_NO_CACHE); + setContentView(webView); + webView.loadUrl(couch.couchUrl() + "_utils/"); }; - private ICouchClient mCallback = new ICouchClient.Stub() { - @Override - public void couchStarted(String host, int port) throws RemoteException { - mHandler.sendMessage(mHandler.obtainMessage(COUCH_STARTED)); + private Boolean deleteDirectory(File dir) { + if (dir.isDirectory()) { + String[] children = dir.list(); + for (int i = 0; i < children.length; i++) { + boolean success = deleteDirectory(new File(dir, children[i])); + if (!success) { + return false; + } + } } - + return dir.delete(); + } + + private class CustomWebViewClient extends WebViewClient { @Override - public void databaseCreated(String name, String user, String pass, - String tag) throws RemoteException { + public boolean shouldOverrideUrlLoading(WebView view, String url) { + view.loadUrl(url); + return true; } - }; - private Handler mHandler = new Handler() { @Override - public void handleMessage(Message msg) { - switch (msg.what) { - case COUCH_STARTED: - loading.dismiss(); - setFutonView(); - break; - default: - super.handleMessage(msg); - } + public void onReceivedHttpAuthRequest(WebView view, + HttpAuthHandler handler, String host, String realm) { + String[] up = view.getHttpAuthUsernamePassword(host, realm); + handler.proceed(up[0], up[1]); } - }; - + } } \ No newline at end of file diff --git a/src/org/couchdb/android/CouchInstallActivity.java b/src/org/couchdb/android/CouchInstallActivity.java index 656cccd..6a6e47e 100644 --- a/src/org/couchdb/android/CouchInstallActivity.java +++ b/src/org/couchdb/android/CouchInstallActivity.java @@ -53,7 +53,7 @@ public void onClick(DialogInterface dialog, case CouchInstallActivity.COMPLETE: installProgress.dismiss(); - Log.v(CouchFutonActivity.TAG, "Launching Couchdb activity"); + Log.v(CouchProcess.TAG, "Launching Couchdb activity"); startActivity(new Intent(getApplicationContext(), CouchFutonActivity.class)); break; } @@ -101,7 +101,6 @@ public void run() { class InstallError { public String description; - public InstallError(String description) { this.description = description; } diff --git a/src/org/couchdb/android/CouchProcess.java b/src/org/couchdb/android/CouchProcess.java index d7fab8a..7ea90e5 100644 --- a/src/org/couchdb/android/CouchProcess.java +++ b/src/org/couchdb/android/CouchProcess.java @@ -23,6 +23,8 @@ public class CouchProcess { + public static String TAG = "CouchDB"; + private static volatile CouchProcess INSTANCE = null; public static synchronized CouchProcess getInstance() { @@ -39,8 +41,8 @@ public static synchronized CouchProcess getInstance() { public String adminPass; // TODO: read from config file - final String couchHost = "127.0.0.1"; - final int couchPort = 5984; + public final String host = "127.0.0.1"; + public final int port = 5984; public Integer pid; @@ -49,7 +51,7 @@ public static synchronized CouchProcess getInstance() { public CouchService service; - public Boolean couchStarted; + public Boolean couchStarted = false; boolean notify; @@ -71,7 +73,7 @@ public void start(String binary, String arg1, String arg2, new Thread(new Runnable() { public void run() { - Log.v(CouchFutonActivity.TAG, "PID: " + pid); + Log.v(TAG, "PID: " + pid); while (fd.valid()) { String line; try { @@ -79,7 +81,7 @@ public void run() { } catch (IOException e) { break; } - Log.v(CouchFutonActivity.TAG, line); + Log.v(TAG, line); if (line.contains("has started on")) { couchStarted = true; try { @@ -92,7 +94,7 @@ public void run() { } catch (RemoteException e) { e.printStackTrace(); } - Log.v(CouchFutonActivity.TAG, "Couch has started."); + Log.v(TAG, "Couch has started."); } } } @@ -101,7 +103,6 @@ public void run() { private void ensureAdmin() throws JSONException { adminPass = readOrGeneratePass(adminUser); - Log.v(CouchFutonActivity.TAG, "admin passsword is " + adminPass); // TODO: only works because I cant overwrite, check if exists in future String url = couchUrl() + "_config/admins/" + adminUser; HTTPRequest.httpRequest("PUT", url, "\"" + adminPass + "\"", @@ -169,8 +170,8 @@ public void stopCouchDB() { } - String couchUrl() { - return "http://" + couchHost + ":" + Integer.toString(couchPort) + "/"; + public String couchUrl() { + return "http://" + host + ":" + Integer.toString(port) + "/"; } } diff --git a/src/org/couchdb/android/CouchService.java b/src/org/couchdb/android/CouchService.java index f5372ea..adc18e6 100644 --- a/src/org/couchdb/android/CouchService.java +++ b/src/org/couchdb/android/CouchService.java @@ -21,7 +21,6 @@ public class CouchService extends Service { private NotificationManager mNM; - private Boolean couchStarted = false; private CouchProcess couchProcess = CouchProcess.getInstance(); @@ -44,7 +43,7 @@ public void initCouchDB(ICouchClient cb) throws RemoteException { String packageName = packageNameFromUid(Binder.getCallingUid()); couchClients.put(packageName, cb); - if (couchStarted) { + if (couchProcess.couchStarted) { couchStarted(); } } @@ -172,7 +171,7 @@ void couchStarted() throws RemoteException { for (Entry entry : couchClients.entrySet()) { ICouchClient client = entry.getValue(); - client.couchStarted(couchProcess.couchHost, couchProcess.couchPort); + client.couchStarted(couchProcess.host, couchProcess.port); couchClients.remove(entry.getKey()); } }