Permalink
Browse files

Initial commit

  • Loading branch information...
0 parents commit ae00455c072f7c869f45ba40ddfa15849e4ed188 @daleharvey daleharvey committed Jan 28, 2011
7 .classpath
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+ <classpathentry kind="src" path="src"/>
+ <classpathentry kind="src" path="gen"/>
+ <classpathentry kind="con" path="com.android.ide.eclipse.adt.ANDROID_FRAMEWORK"/>
+ <classpathentry kind="output" path="bin"/>
+</classpath>
33 .project
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>LibCouch</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>com.android.ide.eclipse.adt.ResourceManagerBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>com.android.ide.eclipse.adt.PreCompilerBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.jdt.core.javabuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>com.android.ide.eclipse.adt.ApkBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>com.android.ide.eclipse.adt.AndroidNature</nature>
+ <nature>org.eclipse.jdt.core.javanature</nature>
+ </natures>
+</projectDescription>
12 AndroidManifest.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.couchone.libcouch"
+ android:versionCode="1"
+ android:versionName="1.0">
+ <application android:icon="@drawable/icon" android:label="@string/app_name">
+
+
+ </application>
+ <uses-sdk android:minSdkVersion="7" />
+
+</manifest>
12 default.properties
@@ -0,0 +1,12 @@
+# This file is automatically generated by Android Tools.
+# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
+#
+# This file must be checked in Version Control Systems.
+#
+# To customize properties used by the Ant build system use,
+# "build.properties", and override values to adapt the script to your
+# project structure.
+
+# Project target.
+target=android-7
+android.library=true
BIN res/drawable-hdpi/icon.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
BIN res/drawable-ldpi/icon.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
BIN res/drawable-mdpi/icon.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
27 res/layout/install_couchdb.xml
@@ -0,0 +1,27 @@
+<?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"
+ android:layout_gravity="center_vertical|center_horizontal"
+ android:gravity="center_vertical|center_horizontal">
+
+ <TextView
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical|center_horizontal"
+ android:id="@+id/install_couchdb_text"
+ android:layout_marginRight="20px"
+ android:layout_marginLeft="20px"
+ android:layout_marginBottom="20px" />
+
+ <Button
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:layout_gravity="center_vertical|center_horizontal"
+ android:text="@string/install_couchdb"
+ android:id="@+id/install_couchdb_btn" />
+
+</LinearLayout>
19 res/layout/missing_couchapp.xml
@@ -0,0 +1,19 @@
+<?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"
+ android:layout_gravity="center_vertical|center_horizontal"
+ android:gravity="center_vertical|center_horizontal">
+
+ <TextView
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical|center_horizontal"
+ android:text="@string/missing_couchapp"
+ android:layout_marginRight="20px"
+ android:layout_marginLeft="20px" />
+
+</LinearLayout>
7 res/values/strings.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+
+ <string name="app_name">CouchAppLauncher</string>
+<string name="install_couchdb">Click to Install CouchDB</string>
+<string name="missing_couchapp">Congratulations, CouchAppLauncher works correctly!</string>
+</resources>
34 src/com/couchone/libcouch/AeSimpleSHA1.java
@@ -0,0 +1,34 @@
+package com.couchone.libcouch;
+
+import java.io.UnsupportedEncodingException;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+
+public class AeSimpleSHA1 {
+
+ private static String convertToHex(byte[] data) {
+ StringBuffer buf = new StringBuffer();
+ for (int i = 0; i < data.length; i++) {
+ int halfbyte = (data[i] >>> 4) & 0x0F;
+ int two_halfs = 0;
+ do {
+ if ((0 <= halfbyte) && (halfbyte <= 9))
+ buf.append((char) ('0' + halfbyte));
+ else
+ buf.append((char) ('a' + (halfbyte - 10)));
+ halfbyte = data[i] & 0x0F;
+ } while(two_halfs++ < 1);
+ }
+ return buf.toString();
+ }
+
+ public static String SHA1(String text) throws NoSuchAlgorithmException, UnsupportedEncodingException {
+ MessageDigest md;
+ md = MessageDigest.getInstance("SHA-1");
+ byte[] sha1hash = new byte[40];
+ md.update(text.getBytes("iso-8859-1"), 0, text.length());
+ sha1hash = md.digest();
+ return convertToHex(sha1hash);
+ }
+}
+
226 src/com/couchone/libcouch/Base64Coder.java
@@ -0,0 +1,226 @@
+// Copyright 2003-2010 Christian d'Heureuse, Inventec Informatik AG, Zurich, Switzerland
+// www.source-code.biz, www.inventec.ch/chdh
+//
+// This module is multi-licensed and may be used under the terms
+// of any of the following licenses:
+//
+// EPL, Eclipse Public License, V1.0 or later, http://www.eclipse.org/legal
+// LGPL, GNU Lesser General Public License, V2.1 or later, http://www.gnu.org/licenses/lgpl.html
+// GPL, GNU General Public License, V2 or later, http://www.gnu.org/licenses/gpl.html
+// AL, Apache License, V2.0 or later, http://www.apache.org/licenses
+// BSD, BSD License, http://www.opensource.org/licenses/bsd-license.php
+//
+// Please contact the author if you need another license.
+// This module is provided "as is", without warranties of any kind.
+
+package com.couchone.libcouch;
+
+/**
+* A Base64 encoder/decoder.
+*
+* <p>
+* This class is used to encode and decode data in Base64 format as described in RFC 1521.
+*
+* <p>
+* Project home page: <a href="http://www.source-code.biz/base64coder/java/">www.source-code.biz/base64coder/java</a><br>
+* Author: Christian d'Heureuse, Inventec Informatik AG, Zurich, Switzerland<br>
+* Multi-licensed: EPL / LGPL / GPL / AL / BSD.
+*/
+public class Base64Coder {
+
+// The line separator string of the operating system.
+private static final String systemLineSeparator = System.getProperty("line.separator");
+
+// Mapping table from 6-bit nibbles to Base64 characters.
+private static char[] map1 = new char[64];
+ static {
+ int i=0;
+ for (char c='A'; c<='Z'; c++) map1[i++] = c;
+ for (char c='a'; c<='z'; c++) map1[i++] = c;
+ for (char c='0'; c<='9'; c++) map1[i++] = c;
+ map1[i++] = '+'; map1[i++] = '/'; }
+
+// Mapping table from Base64 characters to 6-bit nibbles.
+private static byte[] map2 = new byte[128];
+ static {
+ for (int i=0; i<map2.length; i++) map2[i] = -1;
+ for (int i=0; i<64; i++) map2[map1[i]] = (byte)i; }
+
+/**
+* Encodes a string into Base64 format.
+* No blanks or line breaks are inserted.
+* @param s A String to be encoded.
+* @return A String containing the Base64 encoded data.
+*/
+public static String encodeString (String s) {
+ return new String(encode(s.getBytes())); }
+
+/**
+* Encodes a byte array into Base 64 format and breaks the output into lines of 76 characters.
+* This method is compatible with <code>sun.misc.BASE64Encoder.encodeBuffer(byte[])</code>.
+* @param in An array containing the data bytes to be encoded.
+* @return A String containing the Base64 encoded data, broken into lines.
+*/
+public static String encodeLines (byte[] in) {
+ return encodeLines(in, 0, in.length, 76, systemLineSeparator); }
+
+/**
+* Encodes a byte array into Base 64 format and breaks the output into lines.
+* @param in An array containing the data bytes to be encoded.
+* @param iOff Offset of the first byte in <code>in</code> to be processed.
+* @param iLen Number of bytes to be processed in <code>in</code>, starting at <code>iOff</code>.
+* @param lineLen Line length for the output data. Should be a multiple of 4.
+* @param lineSeparator The line separator to be used to separate the output lines.
+* @return A String containing the Base64 encoded data, broken into lines.
+*/
+public static String encodeLines (byte[] in, int iOff, int iLen, int lineLen, String lineSeparator) {
+ int blockLen = (lineLen*3) / 4;
+ if (blockLen <= 0) throw new IllegalArgumentException();
+ int lines = (iLen+blockLen-1) / blockLen;
+ int bufLen = ((iLen+2)/3)*4 + lines*lineSeparator.length();
+ StringBuilder buf = new StringBuilder(bufLen);
+ int ip = 0;
+ while (ip < iLen) {
+ int l = Math.min(iLen-ip, blockLen);
+ buf.append (encode(in, iOff+ip, l));
+ buf.append (lineSeparator);
+ ip += l; }
+ return buf.toString(); }
+
+/**
+* Encodes a byte array into Base64 format.
+* No blanks or line breaks are inserted in the output.
+* @param in An array containing the data bytes to be encoded.
+* @return A character array containing the Base64 encoded data.
+*/
+public static char[] encode (byte[] in) {
+ return encode(in, 0, in.length); }
+
+/**
+* Encodes a byte array into Base64 format.
+* No blanks or line breaks are inserted in the output.
+* @param in An array containing the data bytes to be encoded.
+* @param iLen Number of bytes to process in <code>in</code>.
+* @return A character array containing the Base64 encoded data.
+*/
+public static char[] encode (byte[] in, int iLen) {
+ return encode(in, 0, iLen); }
+
+/**
+* Encodes a byte array into Base64 format.
+* No blanks or line breaks are inserted in the output.
+* @param in An array containing the data bytes to be encoded.
+* @param iOff Offset of the first byte in <code>in</code> to be processed.
+* @param iLen Number of bytes to process in <code>in</code>, starting at <code>iOff</code>.
+* @return A character array containing the Base64 encoded data.
+*/
+public static char[] encode (byte[] in, int iOff, int iLen) {
+ int oDataLen = (iLen*4+2)/3; // output length without padding
+ int oLen = ((iLen+2)/3)*4; // output length including padding
+ char[] out = new char[oLen];
+ int ip = iOff;
+ int iEnd = iOff + iLen;
+ int op = 0;
+ while (ip < iEnd) {
+ int i0 = in[ip++] & 0xff;
+ int i1 = ip < iEnd ? in[ip++] & 0xff : 0;
+ int i2 = ip < iEnd ? in[ip++] & 0xff : 0;
+ int o0 = i0 >>> 2;
+ int o1 = ((i0 & 3) << 4) | (i1 >>> 4);
+ int o2 = ((i1 & 0xf) << 2) | (i2 >>> 6);
+ int o3 = i2 & 0x3F;
+ out[op++] = map1[o0];
+ out[op++] = map1[o1];
+ out[op] = op < oDataLen ? map1[o2] : '='; op++;
+ out[op] = op < oDataLen ? map1[o3] : '='; op++; }
+ return out; }
+
+/**
+* Decodes a string from Base64 format.
+* No blanks or line breaks are allowed within the Base64 encoded input data.
+* @param s A Base64 String to be decoded.
+* @return A String containing the decoded data.
+* @throws IllegalArgumentException If the input is not valid Base64 encoded data.
+*/
+public static String decodeString (String s) {
+ return new String(decode(s)); }
+
+/**
+* Decodes a byte array from Base64 format and ignores line separators, tabs and blanks.
+* CR, LF, Tab and Space characters are ignored in the input data.
+* This method is compatible with <code>sun.misc.BASE64Decoder.decodeBuffer(String)</code>.
+* @param s A Base64 String to be decoded.
+* @return An array containing the decoded data bytes.
+* @throws IllegalArgumentException If the input is not valid Base64 encoded data.
+*/
+public static byte[] decodeLines (String s) {
+ char[] buf = new char[s.length()];
+ int p = 0;
+ for (int ip = 0; ip < s.length(); ip++) {
+ char c = s.charAt(ip);
+ if (c != ' ' && c != '\r' && c != '\n' && c != '\t')
+ buf[p++] = c; }
+ return decode(buf, 0, p); }
+
+/**
+* Decodes a byte array from Base64 format.
+* No blanks or line breaks are allowed within the Base64 encoded input data.
+* @param s A Base64 String to be decoded.
+* @return An array containing the decoded data bytes.
+* @throws IllegalArgumentException If the input is not valid Base64 encoded data.
+*/
+public static byte[] decode (String s) {
+ return decode(s.toCharArray()); }
+
+/**
+* Decodes a byte array from Base64 format.
+* No blanks or line breaks are allowed within the Base64 encoded input data.
+* @param in A character array containing the Base64 encoded data.
+* @return An array containing the decoded data bytes.
+* @throws IllegalArgumentException If the input is not valid Base64 encoded data.
+*/
+public static byte[] decode (char[] in) {
+ return decode(in, 0, in.length); }
+
+/**
+* Decodes a byte array from Base64 format.
+* No blanks or line breaks are allowed within the Base64 encoded input data.
+* @param in A character array containing the Base64 encoded data.
+* @param iOff Offset of the first character in <code>in</code> to be processed.
+* @param iLen Number of characters to process in <code>in</code>, starting at <code>iOff</code>.
+* @return An array containing the decoded data bytes.
+* @throws IllegalArgumentException If the input is not valid Base64 encoded data.
+*/
+public static byte[] decode (char[] in, int iOff, int iLen) {
+ if (iLen%4 != 0) throw new IllegalArgumentException ("Length of Base64 encoded input string is not a multiple of 4.");
+ while (iLen > 0 && in[iOff+iLen-1] == '=') iLen--;
+ int oLen = (iLen*3) / 4;
+ byte[] out = new byte[oLen];
+ int ip = iOff;
+ int iEnd = iOff + iLen;
+ int op = 0;
+ while (ip < iEnd) {
+ int i0 = in[ip++];
+ int i1 = in[ip++];
+ int i2 = ip < iEnd ? in[ip++] : 'A';
+ int i3 = ip < iEnd ? in[ip++] : 'A';
+ if (i0 > 127 || i1 > 127 || i2 > 127 || i3 > 127)
+ throw new IllegalArgumentException ("Illegal character in Base64 encoded data.");
+ int b0 = map2[i0];
+ int b1 = map2[i1];
+ int b2 = map2[i2];
+ int b3 = map2[i3];
+ if (b0 < 0 || b1 < 0 || b2 < 0 || b3 < 0)
+ throw new IllegalArgumentException ("Illegal character in Base64 encoded data.");
+ int o0 = ( b0 <<2) | (b1>>>4);
+ int o1 = ((b1 & 0xf)<<4) | (b2>>>2);
+ int o2 = ((b2 & 3)<<6) | b3;
+ out[op++] = (byte)o0;
+ if (op<oLen) out[op++] = (byte)o1;
+ if (op<oLen) out[op++] = (byte)o2; }
+ return out; }
+
+// Dummy constructor.
+private Base64Coder() {}
+
+} // end class Base64Coder
418 src/com/couchone/libcouch/CouchAppLauncher.java
@@ -0,0 +1,418 @@
+package com.couchone.libcouch;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileReader;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Random;
+
+import org.json.JSONException;
+
+import android.app.Activity;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Environment;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Message;
+import android.os.RemoteException;
+import android.util.Log;
+import android.view.KeyEvent;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.Window;
+import android.webkit.HttpAuthHandler;
+import android.webkit.WebChromeClient;
+import android.webkit.WebSettings;
+import android.webkit.WebView;
+import android.webkit.WebViewClient;
+import android.widget.Button;
+import android.widget.TextView;
+
+public class CouchAppLauncher extends Activity {
+
+ // Put the databases you want to be created here, if you have
+ // a design doc you want to be created then place it in the
+ // assets folder eg:
+ // private String[] bootstrapDatabases = {"couchnotes", "noteshistory"};
+ public String[] bootstrapDatabases = {};
+
+ // If you want to instantiate replication from your application
+ // you will need a command database eg:
+ // private String[] requiresCommandDatabase = {"couchnotes"};
+ public String[] requiresCommandDatabase = {};
+
+ // If you name a database here, its _design/db/index.html
+ // will be launched once the databases have been initialised eg:
+ // private String appToLaunch = "couchnotes";
+ public String appToLaunch = null;
+
+ // This contains a mapping of database tags to their
+ // actual database names
+ private Map<String, String> tagToDbName = new HashMap<String, String>();
+
+ private int initialisedDatabases = 0;
+
+ public final static String COUCHDB_MARKET = "market://details?id=com.couchone.couchdb";
+ public final static String TAG = "CouchApp";
+
+ private static final int COUCH_STARTED = 1;
+ private static final int DATABASES_INITIALISED = 2;
+
+ // Store the main details to communicate with CouchDB
+ public String couchHost;
+ public int couchPort;
+
+ public String adminUser = null;
+ public String adminPass = null; //readOrGeneratePass("default");
+
+ private WebView webView = null;
+ private ICouchService couchService = null;
+ private Boolean couchStarted = false;
+
+ /*
+ * This is the entry point of your application, attempt to launch CouchDB
+ * first
+ */
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ this.requestWindowFeature(Window.FEATURE_NO_TITLE);
+ super.onCreate(savedInstanceState);
+ attemptLaunch();
+ };
+
+ /*
+ * This will be called when app comes back from the background, in the case
+ * that CouchDB had to be installed prior to launching, this will attempt to
+ * relaunch it.
+ */
+ @Override
+ public void onResume() {
+ super.onResume();
+ if (!couchStarted) {
+ attemptLaunch();
+ }
+ }
+
+ /*
+ * When the app is killed we unbind from CouchDB, so it can stop itself if
+ * not used
+ */
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+
+ if (webView != null) {
+ webView.destroy();
+ }
+
+ if (couchService != null) {
+ try {
+ couchService.quitCouchDB();
+ } catch (RemoteException e) {
+ e.printStackTrace();
+ }
+ }
+ unbindService(couchServiceConn);
+ }
+
+ /*
+ * Attempt to launch the CouchDB service, if CouchDB starts then we will be
+ * notified in the couchClient callback stubs, if not we assume CouchDB is
+ * not started, show a notification screen that then opens the market so the
+ * user can install.
+ */
+ public void attemptLaunch() {
+
+ Intent intent = new Intent(ICouchService.class.getName());
+ Boolean canStart = bindService(intent, couchServiceConn,
+ Context.BIND_AUTO_CREATE);
+
+ if (!canStart) {
+
+ setContentView(R.layout.install_couchdb);
+
+ TextView label = (TextView) findViewById(R.id.install_couchdb_text);
+ Button btn = (Button) this.findViewById(R.id.install_couchdb_btn);
+
+ String text = getString(R.string.app_name)
+ + " requires Apache CouchDB to be installed.";
+ label.setText(text);
+
+ // Launching the market will fail on emulators
+ btn.setOnClickListener(new OnClickListener() {
+ public void onClick(View v) {
+ launchMarket();
+ finish();
+ }
+ });
+ }
+ }
+
+ /*
+ * This implements the API that is specified in ICouchClient.aidl, these are
+ * the functions that the CouchDBService will call in your application.
+ */
+ private ICouchClient couchClient = new ICouchClient.Stub() {
+
+ @Override
+ public void couchStarted(String host, int port) throws RemoteException {
+ couchStarted = true;
+ couchHost = host;
+ couchPort = port;
+ localClient.sendMessage(localClient.obtainMessage(COUCH_STARTED));
+ initDatabases();
+ }
+
+ @Override
+ public void databaseCreated(String db, String name, String pass,
+ String tag) throws RemoteException {
+
+ initialisedDatabases += 1;
+ tagToDbName.put(tag, db);
+
+ // All databases use the same user credentials
+ adminUser = name;
+ adminPass = pass;
+
+ ensureDesignDoc(db, tag);
+ initDatabases();
+ }
+ };
+
+ /*
+ * This handles the connection to the CouchService
+ */
+ private ServiceConnection couchServiceConn = new ServiceConnection() {
+
+ public void onServiceConnected(ComponentName className, IBinder service) {
+ couchService = ICouchService.Stub.asInterface(service);
+ try {
+ couchService.initCouchDB(couchClient);
+ } catch (RemoteException e) {
+ e.printStackTrace();
+ }
+ }
+
+ public void onServiceDisconnected(ComponentName className) {
+ finish();
+ couchService = null;
+ }
+ };
+
+ /*
+ * The communication with the CouchService is done within a seperate thread,
+ * if we want to do anything to the UI we need to get back into the main
+ * thread, this handler recieves messages from the comms thread
+ */
+ private Handler localClient = new Handler() {
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ // If you want to do something when couch has started, do
+ // it here
+ case COUCH_STARTED:
+ break;
+ // This gets sent when all the boostrapDatabases
+ // have been created
+ case DATABASES_INITIALISED:
+ launchWebview();
+ break;
+ }
+ }
+ };
+
+ /*
+ * This will loop through "dbfiles" in the assets folder and initialise a db
+ * for each JSON file, db's that do not exist will be created, databases are
+ * identified by their tag although that is not their actual name.
+ */
+ private void initDatabases() throws RemoteException {
+
+ if (initialisedDatabases == bootstrapDatabases.length) {
+ localClient.sendMessage(localClient
+ .obtainMessage(DATABASES_INITIALISED));
+ return;
+ }
+
+ String dbTag = bootstrapDatabases[initialisedDatabases];
+ boolean hasCmdDb = inArray(requiresCommandDatabase, dbTag);
+ couchService.initDatabase(couchClient, dbTag, adminPass, hasCmdDb);
+ };
+
+ /*
+ * Will check for the existence of a design doc and if it doesnt exist,
+ * upload the json found at dataPath to create it
+ */
+ private void ensureDesignDoc(String dbName, String dbTag) {
+
+ try {
+ String data = readAsset(dbTag + ".json");
+ String ddocUrl = couchUrl() + dbName + "/_design/" + dbTag;
+
+ // Check to see if a design doc already exists
+ String auth = Base64Coder.encodeString(adminUser + ":" + adminPass);
+ String[][] headers = { { "Authorization", "Basic " + auth } };
+ HTTPRequest req = HTTPRequest.get(ddocUrl, headers);
+
+ if (req.status == 404) {
+ HTTPRequest.put(ddocUrl, data, headers);
+ }
+
+ } catch (IOException e) {
+ e.printStackTrace();
+ // There is no design doc to load
+ } catch (JSONException e) {
+ e.printStackTrace();
+ }
+ };
+
+ /*
+ * The appToLaunch member should be set to a database whos index.html is
+ * launched here, if none is set then display a notice.
+ */
+ private void launchWebview() {
+ if (appToLaunch != null) {
+ String dbName = tagToDbName.get(appToLaunch);
+ launchUrl(couchUrl() + dbName + "/_design/" + appToLaunch
+ + "/index.html");
+ } else {
+ setContentView(R.layout.missing_couchapp);
+ }
+ };
+
+ /*
+ * Create a WebView with the sensible defaults, and load a url within it
+ */
+ private void launchUrl(String url) {
+ webView = new WebView(this);
+ webView.setWebChromeClient(new WebChromeClient());
+ webView.setWebViewClient(new CustomWebViewClient());
+ webView.getSettings().setJavaScriptEnabled(true);
+ webView.getSettings().setDomStorageEnabled(true);
+ webView.setScrollBarStyle(WebView.SCROLLBARS_OUTSIDE_OVERLAY);
+ webView.setHttpAuthUsernamePassword("127.0.0.1", "administrator",
+ adminUser, adminPass);
+ webView.requestFocusFromTouch();
+ webView.getSettings().setCacheMode(WebSettings.LOAD_NO_CACHE);
+ setContentView(webView);
+ webView.loadUrl(url);
+ };
+
+ /*
+ * If a user pressed the physical back button then make the webview go back
+ * a page if the webview cant go back the default action applied (will
+ * usually close the app)
+ */
+ @Override
+ public boolean onKeyDown(int keyCode, KeyEvent event) {
+ if ((keyCode == KeyEvent.KEYCODE_BACK) && webView != null
+ && webView.canGoBack()) {
+ webView.goBack();
+ return true;
+ }
+ return super.onKeyDown(keyCode, event);
+ };
+
+ /*
+ * The custom webview client ensures that when users click links, it opens
+ * in the current webview and doesnt open the browser
+ */
+ private class CustomWebViewClient extends WebViewClient {
+ @Override
+ public boolean shouldOverrideUrlLoading(WebView view, String url) {
+ view.loadUrl(url);
+ return true;
+ }
+
+ @Override
+ public void onReceivedHttpAuthRequest(WebView view,
+ HttpAuthHandler handler, String host, String realm) {
+ String[] up = view.getHttpAuthUsernamePassword(host, realm);
+ handler.proceed(up[0], up[1]);
+ }
+ }
+
+ private String couchUrl() {
+ return "http://" + couchHost + ":" + Integer.toString(couchPort) + "/";
+ }
+
+ private void launchMarket() {
+ startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(COUCHDB_MARKET)));
+ }
+
+ private String readAsset(String path) throws IOException {
+ InputStream is = getAssets().open(path);
+ int size = is.available();
+ byte[] buffer = new byte[size];
+ is.read(buffer);
+ is.close();
+ return new String(buffer);
+ }
+
+ private boolean inArray(String[] haystack, String needle) {
+ for (int i = 0; i < haystack.length; i++) {
+ if (haystack[0].equals(needle)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public String readOrGeneratePass(String username) {
+ String passFile = getFilesDir() + "/" + username + ".passwd";
+ File f = new File(passFile);
+ if (!f.exists()) {
+ String pass = generatePassword(8);
+ writeFile(passFile, username + ":" + pass);
+ return pass;
+ } else {
+ return readFile(passFile).split(":")[1];
+ }
+ }
+
+ public String generatePassword(int length) {
+ String charset = "!0123456789abcdefghijklmnopqrstuvwxyz";
+ Random rand = new Random(System.currentTimeMillis());
+ StringBuffer sb = new StringBuffer();
+ for (int i = 0; i < length; i++) {
+ int pos = rand.nextInt(charset.length());
+ sb.append(charset.charAt(pos));
+ }
+ return sb.toString();
+ }
+
+ private String readFile(String filePath) {
+ String contents = "";
+ try {
+ File file = new File(filePath);
+ BufferedReader reader = new BufferedReader(new FileReader(file));
+ contents = reader.readLine();
+ } catch (FileNotFoundException e) {
+ e.printStackTrace();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ return contents;
+ };
+
+ private void writeFile(String filePath, String data) {
+ try {
+ FileWriter writer = new FileWriter(filePath);
+ writer.write(data);
+ writer.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+}
121 src/com/couchone/libcouch/HTTPRequest.java
@@ -0,0 +1,121 @@
+package com.couchone.libcouch;
+
+import java.io.BufferedReader;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.net.HttpURLConnection;
+import java.net.URL;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+
+public class HTTPRequest {
+
+ public String[][] headers;
+ public JSONObject json;
+ public String result;
+ public int status;
+
+ public HTTPRequest(String[][] headers, JSONObject json, String result,
+ int status) {
+ this.headers = headers;
+ this.json = json;
+ this.result = result;
+ this.status = status;
+ }
+
+ public static HTTPRequest post(String url, String data) throws JSONException {
+ return post(url, data, new String[][]{});
+ }
+
+ public static HTTPRequest post(String url, String data, String[][] headers)
+ throws JSONException {
+ return HTTPRequest.httpRequest("POST", url, data, headers);
+ }
+
+ public static HTTPRequest put(String url, String data) throws JSONException {
+ return put(url, data, new String[][]{});
+ }
+
+ public static HTTPRequest put(String url, String data, String[][] headers)
+ throws JSONException {
+ return HTTPRequest.httpRequest("PUT", url, data, headers);
+ }
+
+ public static HTTPRequest get(String url) throws JSONException {
+ return get(url, new String[][] {});
+ }
+
+ public static HTTPRequest get(String url, String[][] headers)
+ throws JSONException {
+ return HTTPRequest.httpRequest("GET", url, null, headers);
+ }
+
+ public static HTTPRequest httpRequest(String method, String url,
+ String data, String[][] headers) throws JSONException {
+
+ StringBuffer sb = new StringBuffer();
+ int statusCode = 0;
+
+ try {
+
+ HttpURLConnection c = (HttpURLConnection) new URL(url).openConnection();
+ String charEncoding = "iso-8859-1";
+
+ c.setDoOutput(true);
+ c.setUseCaches(false);
+
+ c.setRequestMethod(method);
+ c.setRequestProperty("Content-type", "application/json; charset="
+ + "UTF-8");
+
+ for (String[] tmp : headers) {
+ c.setRequestProperty(tmp[0], tmp[1]);
+ }
+
+ if (method != "GET" && data != null) {
+ c.setDoInput(true);
+ c.setRequestProperty("Content-Length",
+ Integer.toString(data.length()));
+ c.getOutputStream().write(data.getBytes(charEncoding));
+ }
+
+ c.connect();
+
+ statusCode = c.getResponseCode();
+
+ // TODO: Nasty
+ try {
+ BufferedReader rd = new BufferedReader(new InputStreamReader(
+ c.getInputStream()));
+ String line;
+ while ((line = rd.readLine()) != null) {
+ sb.append(line);
+ }
+ rd.close();
+ } catch (FileNotFoundException e) {
+
+ } catch (NullPointerException e) {
+
+ }
+
+ finally {
+ c.disconnect();
+ }
+
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+
+ JSONObject json= sb.toString().length() == 0
+ ? new JSONObject()
+ : new JSONObject(sb.toString());
+
+ return new HTTPRequest(headers, json, sb.toString(), statusCode);
+ };
+
+ public String toString() {
+ return "HTTPResult -> status: " + Integer.toString(status);
+ }
+}
14 src/com/couchone/libcouch/ICouchClient.aidl
@@ -0,0 +1,14 @@
+package com.couchone.libcouch;
+
+interface ICouchClient
+{
+ /* Callback to notify when CouchDB has started */
+ void couchStarted(String host, int port);
+
+ /* Callback notifies when the database requested
+ * has been created, each database has a related
+ * Control Database, which is used to manage replication
+ * and sharing etc, both use the same credentials
+ */
+ void databaseCreated(String dbName, String user, String pass, String tag);
+}
21 src/com/couchone/libcouch/ICouchService.aidl
@@ -0,0 +1,21 @@
+package com.couchone.libcouch;
+
+import com.couchone.libcouch.ICouchClient;
+
+interface ICouchService
+{
+ /* Starts couchDB, calls "couchStarted" callback when
+ * complete
+ */
+ void initCouchDB(ICouchClient callback);
+
+ /* The database may not be named as hinted here, this is to
+ * prevent conflicts
+ */
+ void initDatabase(ICouchClient callback, String name, String pass, boolean cmdDb);
+
+ /*
+ *
+ */
+ void quitCouchDB();
+}

0 comments on commit ae00455

Please sign in to comment.