Browse files

Initial git checkin

  • Loading branch information...
0 parents commit fd0772bc06d9fe150c623f3d3b253af4cf595ff7 @apage43 apage43 committed Jul 7, 2010
24 AndroidManifest.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="org.couchdb.android"
+ android:versionCode="1"
+ android:versionName="1.0">
+ <application android:icon="@drawable/icon" android:label="@string/app_name">
+ <activity android:name=".CouchDB"
+ android:label="@string/app_name">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+
+ <activity android:name="CouchInstallActivity"></activity>
+<service android:name="CouchService" android:exported="true" android:enabled="true"></service>
+</application>
+ <uses-sdk android:minSdkVersion="7" />
+
+<uses-permission android:name="android.permission.INTERNET"></uses-permission>
+<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"></uses-permission>
+
+<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"></uses-permission>
+</manifest>
8 README.md
@@ -0,0 +1,8 @@
+CouchDB Android Launcher
+============
+
+If you have any difficulties installing this application, *uninstall* any previous version on your phone.
+
+Also, the Android browser caches fairly aggressively. If you experience issues with Futon, try clearing the browser cache, or refreshing.
+
+Uses Android Scripting Engine's external process launching code.
11 default.properties
@@ -0,0 +1,11 @@
+# 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
29 gen/org/couchdb/android/R.java
@@ -0,0 +1,29 @@
+/* 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.couchdb.android;
+
+public final class R {
+ public static final class attr {
+ }
+ public static final class drawable {
+ public static final int icon=0x7f020000;
+ }
+ public static final class id {
+ public static final int InstallButton=0x7f050000;
+ public static final int StartButton=0x7f050001;
+ public static final int StopButton=0x7f050002;
+ }
+ public static final class layout {
+ public static final int install=0x7f030000;
+ public static final int main=0x7f030001;
+ }
+ public static final class string {
+ public static final int app_name=0x7f040001;
+ public static final int hello=0x7f040000;
+ }
+}
9 jni/Android.mk
@@ -0,0 +1,9 @@
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := com_google_ase_Exec
+LOCAL_SRC_FILES := com_google_ase_Exec.cpp
+LOCAL_LDLIBS := -llog
+
+include $(BUILD_SHARED_LIBRARY)
175 jni/com_google_ase_Exec.cpp
@@ -0,0 +1,175 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ * Copyright (C) 2009 Google, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "com_google_ase_Exec.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <termios.h>
+#include <unistd.h>
+
+#include "android/log.h"
+
+#define LOG_TAG "Exec"
+#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)
+
+int CreateSubprocess(const char* cmd, const char* arg0, const char* arg1, pid_t* pid) {
+ char* devname;
+ int ptm = open("/dev/ptmx", O_RDWR);
+ if(ptm < 0){
+ LOGE("Cannot open /dev/ptmx: %s\n", strerror(errno));
+ return -1;
+ }
+ fcntl(ptm, F_SETFD, FD_CLOEXEC);
+
+ if (grantpt(ptm) || unlockpt(ptm) ||
+ ((devname = (char*) ptsname(ptm)) == 0)) {
+ LOGE("Trouble with /dev/ptmx: %s\n", strerror(errno));
+ return -1;
+ }
+
+ *pid = fork();
+ if(*pid < 0) {
+ LOGE("Fork failed: %s\n", strerror(errno));
+ return -1;
+ }
+
+ if(*pid == 0){
+ int pts;
+ setsid();
+ pts = open(devname, O_RDWR);
+ if(pts < 0) {
+ exit(-1);
+ }
+ dup2(pts, 0);
+ dup2(pts, 1);
+ dup2(pts, 2);
+ close(ptm);
+ execl(cmd, cmd, arg0, arg1, NULL);
+ exit(-1);
+ } else {
+ return ptm;
+ }
+}
+
+void JNU_ThrowByName(JNIEnv* env, const char* name, const char* msg) {
+ jclass clazz = env->FindClass(name);
+ if (clazz != NULL) {
+ env->ThrowNew(clazz, msg);
+ }
+ env->DeleteLocalRef(clazz);
+}
+
+char* JNU_GetStringNativeChars(JNIEnv* env, jstring jstr) {
+ if (jstr == NULL) {
+ return NULL;
+ }
+ jbyteArray bytes = 0;
+ jthrowable exc;
+ char* result = 0;
+ if (env->EnsureLocalCapacity(2) < 0) {
+ return 0; /* out of memory error */
+ }
+ jclass Class_java_lang_String = env->FindClass("java/lang/String");
+ jmethodID MID_String_getBytes = env->GetMethodID(
+ Class_java_lang_String, "getBytes", "()[B");
+ bytes = (jbyteArray) env->CallObjectMethod(jstr, MID_String_getBytes);
+ exc = env->ExceptionOccurred();
+ if (!exc) {
+ jint len = env->GetArrayLength(bytes);
+ result = (char*) malloc(len + 1);
+ if (result == 0) {
+ JNU_ThrowByName(env, "java/lang/OutOfMemoryError", 0);
+ env->DeleteLocalRef(bytes);
+ return 0;
+ }
+ env->GetByteArrayRegion(bytes, 0, len, (jbyte*) result);
+ result[len] = 0; /* NULL-terminate */
+ } else {
+ env->DeleteLocalRef(exc);
+ }
+ env->DeleteLocalRef(bytes);
+ return result;
+}
+
+int JNU_GetFdFromFileDescriptor(JNIEnv* env, jobject fileDescriptor) {
+ jclass Class_java_io_FileDescriptor = env->FindClass("java/io/FileDescriptor");
+ jfieldID descriptor = env->GetFieldID(Class_java_io_FileDescriptor, "descriptor", "I");
+ return env->GetIntField(fileDescriptor, descriptor);
+}
+
+JNIEXPORT jobject JNICALL Java_com_google_ase_Exec_createSubprocess(
+ JNIEnv* env, jclass clazz, jstring cmd, jstring arg0, jstring arg1,
+ jintArray processIdArray) {
+ char* cmd_native = JNU_GetStringNativeChars(env, cmd);
+ char* arg0_native = JNU_GetStringNativeChars(env, arg0);
+ char* arg1_native = JNU_GetStringNativeChars(env, arg1);
+ pid_t pid;
+ int ptm = CreateSubprocess(cmd_native, arg0_native, arg1_native, &pid);
+ if (processIdArray) {
+ if (env->GetArrayLength(processIdArray) > 0) {
+ jboolean isCopy;
+ int* proccessId = (int*) env->GetPrimitiveArrayCritical(processIdArray, &isCopy);
+ if (proccessId) {
+ *proccessId = (int) pid;
+ env->ReleasePrimitiveArrayCritical(processIdArray, proccessId, 0);
+ }
+ }
+ }
+
+ jclass Class_java_io_FileDescriptor =
+ env->FindClass("java/io/FileDescriptor");
+ jmethodID init = env->GetMethodID(Class_java_io_FileDescriptor, "<init>", "()V");
+ jobject result = env->NewObject(Class_java_io_FileDescriptor, init);
+
+ if (!result) {
+ LOGE("Couldn't create a FileDescriptor.");
+ } else {
+ jfieldID descriptor = env->GetFieldID(Class_java_io_FileDescriptor, "descriptor", "I");
+ env->SetIntField(result, descriptor, ptm);
+ }
+ return result;
+}
+
+JNIEXPORT void Java_com_google_ase_Exec_setPtyWindowSize(
+ JNIEnv* env, jclass clazz, jobject fileDescriptor, jint row, jint col, jint xpixel,
+ jint ypixel) {
+ struct winsize sz;
+ int fd = JNU_GetFdFromFileDescriptor(env, fileDescriptor);
+ if (env->ExceptionOccurred() != NULL) {
+ return;
+ }
+ sz.ws_row = row;
+ sz.ws_col = col;
+ sz.ws_xpixel = xpixel;
+ sz.ws_ypixel = ypixel;
+ ioctl(fd, TIOCSWINSZ, &sz);
+}
+
+JNIEXPORT jint Java_com_google_ase_Exec_waitFor(JNIEnv* env, jclass clazz, jint procId) {
+ int status;
+ waitpid(procId, &status, 0);
+ int result = 0;
+ if (WIFEXITED(status)) {
+ result = WEXITSTATUS(status);
+ }
+ return result;
+}
45 jni/com_google_ase_Exec.h
@@ -0,0 +1,45 @@
+/* DO NOT EDIT THIS FILE - it is machine generated */
+#include <jni.h>
+/* Header for class com_google_ase_Exec */
+
+#ifndef _Included_com_google_ase_Exec
+#define _Included_com_google_ase_Exec
+#ifdef __cplusplus
+extern "C" {
+#endif
+/*
+ * Class: com_google_ase_Exec
+ * Method: createSubprocess
+ * Signature: (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;[I)Ljava/io/FileDescriptor;
+ */
+JNIEXPORT jobject JNICALL Java_com_google_ase_Exec_createSubprocess
+ (JNIEnv *, jclass, jstring, jstring, jstring, jintArray);
+
+/*
+ * Class: com_google_ase_Exec
+ * Method: setPtyWindowSize
+ * Signature: (Ljava/io/FileDescriptor;IIII)V
+ */
+JNIEXPORT void JNICALL Java_com_google_ase_Exec_setPtyWindowSize
+ (JNIEnv *, jclass, jobject, jint, jint, jint, jint);
+
+/*
+ * Class: com_google_ase_Exec
+ * Method: waitFor
+ * Signature: (I)I
+ */
+JNIEXPORT jint JNICALL Java_com_google_ase_Exec_waitFor
+ (JNIEnv *, jclass, jint);
+
+/*
+ * Class: com_google_ase_Exec
+ * Method: register
+ * Signature: ()I
+ */
+JNIEXPORT jint JNICALL Java_com_google_ase_Exec_register
+ (JNIEnv *, jclass);
+
+#ifdef __cplusplus
+}
+#endif
+#endif
BIN lib/commons-compress-1.0.jar
Binary file not shown.
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.
7 res/layout/install.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_height="wrap_content" android:focusableInTouchMode="true" android:layout_gravity="center_vertical|center_horizontal" android:orientation="vertical" android:layout_width="wrap_content">
+<TextView android:layout_height="wrap_content" android:text="CouchDB Not Installed" android:layout_width="wrap_content"></TextView><Button android:layout_height="wrap_content" android:gravity="center" android:id="@+id/InstallButton" android:text="Install CouchDB" android:layout_width="fill_parent"></Button>
+
+</LinearLayout>
9 res/layout/main.xml
@@ -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"
+ android:layout_gravity="center_vertical|center_horizontal" android:gravity="center_vertical|center_horizontal">
+
+<Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/StartButton" android:text="Start CouchDB" android:layout_gravity="center_vertical|center_horizontal"></Button><Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_vertical|center_horizontal" android:id="@+id/StopButton" android:text="Stop CouchDB" android:enabled="false"></Button>
+</LinearLayout>
5 res/values/strings.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <string name="hello">Hello World, CouchDB!</string>
+ <string name="app_name"></string>
+</resources>
69 src/com/google/ase/Exec.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.ase;
+
+import java.io.FileDescriptor;
+
+/**
+ * Tools for executing commands.
+ */
+public class Exec {
+ /**
+ * @param cmd
+ * The command to execute
+ * @param arg0
+ * The first argument to the command, may be null
+ * @param arg1
+ * the second argument to the command, may be null
+ * @return the file descriptor of the started process.
+ *
+ */
+ public static FileDescriptor createSubprocess(String cmd, String arg0, String arg1) {
+ return createSubprocess(cmd, arg0, arg1, null);
+ }
+
+ /**
+ * @param cmd
+ * The command to execute
+ * @param arg0
+ * The first argument to the command, may be null
+ * @param arg1
+ * the second argument to the command, may be null
+ * @param processId
+ * A one-element array to which the process ID of the started process will be written.
+ * @return the file descriptor of the started process.
+ *
+ */
+ public static native FileDescriptor createSubprocess(String cmd, String arg0, String arg1,
+ int[] processId);
+
+ public static native void setPtyWindowSize(FileDescriptor fd, int row, int col, int xpixel,
+ int ypixel);
+
+ /**
+ * Causes the calling thread to wait for the process associated with the receiver to finish
+ * executing.
+ *
+ * @return The exit value of the Process being waited on
+ *
+ */
+ public static native int waitFor(int processId);
+
+ static {
+ System.loadLibrary("com_google_ase_Exec");
+ }
+}
41 src/org/couchdb/android/CouchDB.java
@@ -0,0 +1,41 @@
+package org.couchdb.android;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.widget.Button;
+
+public class CouchDB extends Activity {
+ public final static String TAG = "CouchDB";
+
+ /** Called when the activity is first created. */
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.main);
+ boolean installed = CouchInstaller.checkInstalled();
+ if(!installed)
+ startActivity(new Intent(this, CouchInstallActivity.class));
+ final Button startButton = (Button) findViewById(R.id.StartButton);
+ final Button stopButton = (Button) findViewById(R.id.StopButton);
+ startButton.setOnClickListener(new OnClickListener(){
+
+ public void onClick(View v) {
+ startService(new Intent(getApplicationContext(), CouchService.class));
+ startButton.setEnabled(false);
+ stopButton.setEnabled(true);
+ }
+
+ });
+
+ stopButton.setOnClickListener(new OnClickListener(){
+ public void onClick(View v) {
+ stopService(new Intent(getApplicationContext(), CouchService.class));
+ startButton.setEnabled(true);
+ stopButton.setEnabled(false);
+ }
+ });
+ }
+}
55 src/org/couchdb/android/CouchInstallActivity.java
@@ -0,0 +1,55 @@
+package org.couchdb.android;
+
+import java.io.IOException;
+
+import android.app.Activity;
+import android.app.ProgressDialog;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.widget.Button;
+
+public class CouchInstallActivity extends Activity {
+ private ProgressDialog installProgress;
+
+ private Handler pHandler = new Handler() {
+ @Override
+ public void handleMessage(Message msg) {
+ installProgress.setMessage("Unpacking files... " + msg.arg1);
+ if(msg.arg2 == 1)
+ {
+ installProgress.dismiss();
+ startActivity(new Intent(getApplicationContext(), CouchDB.class));
+ }
+ }
+ };
+
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.install);
+ final Button installButton = (Button) findViewById(R.id.InstallButton);
+ final Context currentCtx = this;
+ installButton.setOnClickListener(new OnClickListener() {
+ public void onClick(View v) {
+ installButton.setEnabled(false);
+ installProgress = ProgressDialog.show(currentCtx, "Installing CouchDB", "Unpacking files...", true, false);
+ new Thread() {
+ public void run()
+ {
+ try {
+ CouchInstaller.doInstall(pHandler);
+ } catch (IOException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ }
+ }.start();
+
+ }
+ });
+ }
+}
120 src/org/couchdb/android/CouchInstaller.java
@@ -0,0 +1,120 @@
+package org.couchdb.android;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.zip.GZIPInputStream;
+
+import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
+import org.apache.commons.compress.archivers.tar.TarArchiveInputStream;
+import org.apache.commons.compress.utils.IOUtils;
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpResponse;
+import org.apache.http.StatusLine;
+import org.apache.http.client.HttpClient;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.impl.client.DefaultHttpClient;
+
+import android.os.Handler;
+import android.os.Message;
+import android.util.Log;
+
+public class CouchInstaller {
+ final static String baseUrl = "http://couchdb-android.s3.amazonaws.com/";
+ final static String dataPath = "/data/data/org.couchdb.android";
+ // SD card isn't -ideal-, but right now we are really hefty.
+ final static String TAG = "CouchDB";
+
+ private static void installPackage(String pkg, Handler handler)
+ throws IOException {
+ HttpClient pkgHttpClient = new DefaultHttpClient();
+ HttpGet tgzrequest = new HttpGet(baseUrl + pkg + ".tgz");
+ HttpResponse response = pkgHttpClient.execute(tgzrequest);
+ ArrayList<String> installedfiles = new ArrayList<String>();
+ StatusLine status = response.getStatusLine();
+ Log.d(TAG, "Request returned status " + status);
+ if (status.getStatusCode() == 200) {
+ HttpEntity entity = response.getEntity();
+ InputStream instream = entity.getContent();
+ // int fileSize = (int) entity.getContentLength();
+ TarArchiveInputStream tarstream = new TarArchiveInputStream(
+ new GZIPInputStream(instream));
+ TarArchiveEntry e = null;
+ int files = 0;
+ while ((e = tarstream.getNextTarEntry()) != null) {
+ if (e.isDirectory()) {
+ new File(e.getName()).mkdir();
+ Log.v(TAG, "MKDIR: " + e.getName());
+ } else if (!"".equals(e.getLinkName())) {
+ Log.v(TAG,
+ "LINK: " + e.getName() + " -> " + e.getLinkName());
+ Runtime.getRuntime().exec(
+ new String[] { "/system/bin/ln", "-s", e.getName(),
+ e.getLinkName() });
+ installedfiles.add(e.getName());
+ } else {
+ File target = new File(e.getName());
+ if(target.getParent() != null) new File(target.getParent()).mkdirs();
+ Log.v(TAG, "Extracting " + e.getName());
+ IOUtils.copy(tarstream, new FileOutputStream(target));
+ installedfiles.add(e.getName());
+ }
+ Runtime.getRuntime().exec("/system/bin/chmod 755 " + e.getName()); //TODO: Set to actual tar perms.
+ files++;
+ Message progress = new Message();
+ progress.arg1 = files++;
+ progress.arg2 = 0;
+ handler.sendMessage(progress);
+ }
+
+ tarstream.close();
+ instream.close();
+ FileWriter iLOWriter = new FileWriter(dataPath + "/" + pkg + ".installedfiles");
+ for (String file : installedfiles)
+ iLOWriter.write(file+"\n");
+ iLOWriter.close();
+ } else {
+ throw new IOException();
+ }
+ }
+
+ public static boolean checkInstalled() {
+ for (String pkg : packageSet()) {
+ File f = new File(dataPath + "/" + pkg + ".installedfiles");
+ if (!f.exists())
+ return false;
+ }
+ return true;
+ }
+
+ public static List<String> packageSet() {
+ ArrayList<String> packages = new ArrayList<String>();
+
+ // TODO: Different CPU arch support.
+ packages.add("couch-erlang-dev"); // CouchDB, Erlang, CouchJS
+ if (android.os.Build.VERSION.SDK_INT == 7)
+ packages.add("couch-icu-driver-eclair");
+ else if (android.os.Build.VERSION.SDK_INT == 8)
+ packages.add("couch-icu-driver-froyo");
+ else {
+ // TODO: Probably should throw some exception here.
+ Log.e(TAG, "Unsupported OS/arch combo");
+ return null;
+ }
+ return packages;
+ }
+
+ public static void doInstall(Handler handler) throws IOException {
+ for (String pkg : packageSet())
+ installPackage(pkg, handler);
+
+ Message done = Message.obtain();
+ done.arg2 = 1;
+ handler.sendMessage(done);
+ }
+
+}
110 src/org/couchdb/android/CouchService.java
@@ -0,0 +1,110 @@
+package org.couchdb.android;
+
+import java.io.BufferedReader;
+import java.io.FileDescriptor;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.PrintStream;
+
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.app.Service;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.IBinder;
+import android.util.Log;
+
+import com.google.ase.Exec;
+
+public class CouchService extends Service {
+ final String TAG = "CouchDB";
+ private NotificationManager mNM;
+ CouchProcess couch;
+
+ public class CouchProcess {
+
+ public Integer pid;
+ FileDescriptor fd;
+ PrintStream out;
+ BufferedReader in;
+
+ public void start(String binary, String arg1, String arg2) {
+ int[] pidbuffer = new int[1];
+ fd = Exec.createSubprocess(binary, arg1, arg2, pidbuffer);
+ pid = pidbuffer[0];
+ out = new PrintStream(new FileOutputStream(fd), true);
+ in = new BufferedReader(new InputStreamReader(new FileInputStream(fd)));
+
+ new Thread(new Runnable() {
+ public void run() {
+ Log.v(TAG, "PID: " + pid);
+ while(fd.valid())
+ {
+ String line;
+ try {
+ line = in.readLine();
+ } catch (IOException e) {
+ break; //TODO: Notify of couch death
+ }
+ Log.v(TAG, line);
+ if(line.contains("has started on"))
+ {
+ Log.v(TAG, "Couch has started.");
+ int icon = R.drawable.icon;
+ CharSequence tickerText = "CouchDB Running";
+ long when = System.currentTimeMillis();
+ Notification notification = new Notification(icon, tickerText, when);
+ notification.flags = Notification.FLAG_ONGOING_EVENT;
+ Intent i = new Intent(Intent.ACTION_VIEW);
+ i.setData(Uri.parse("http://127.0.0.1:5984/_utils"));
+ notification.setLatestEventInfo(getApplicationContext(), "CouchDB Running", "Press to open Futon", PendingIntent.getActivity(getApplicationContext(), 0, i, PendingIntent.FLAG_CANCEL_CURRENT));
+ mNM.cancel(1);
+ mNM.notify(2, notification);
+ startForeground(2, notification);
+ }
+ }
+ }
+ }).start();
+
+ }
+ }
+
+
+ @Override
+ public int onStartCommand(Intent intent, int flags, int startId) {
+ mNM = (NotificationManager)getSystemService(NOTIFICATION_SERVICE);
+ couch = new CouchProcess();
+ int icon = R.drawable.icon;
+ CharSequence tickerText = "CouchDB Starting";
+ long when = System.currentTimeMillis();
+ Notification notification = new Notification(icon, tickerText, when);
+ Intent notificationIntent = new Intent(this, CouchDB.class);
+ PendingIntent contentIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0);
+ notification.setLatestEventInfo(getApplicationContext(), "CouchDB Starting", "Please Wait...", contentIntent);
+ mNM.notify(1, notification);
+ couch.start("/system/bin/sh", "/sdcard/couch/bin/couchdb", "");
+ return START_STICKY;
+ }
+
+
+ @Override
+ public void onDestroy() {
+ try {
+ Runtime.getRuntime().exec("/system/bin/kill " + couch.pid);
+ Runtime.getRuntime().exec("/system/bin/killall beam"); //This is safe since couch can only kill couch.
+ } catch (IOException e) {
+ //Failed to kill couch?
+ }
+ mNM.cancelAll();
+ }
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+}

0 comments on commit fd0772b

Please sign in to comment.