From 9925e2732ac337579fe0307e53c21de58688267b Mon Sep 17 00:00:00 2001
From: hypnosis-i2p <hypnosis.i2p@gmail.com>
Date: Fri, 24 Feb 2017 16:30:47 +0800
Subject: [PATCH] rework + now restarts after app kill event

---
 android/res/values/strings.xml                |  2 +
 .../org/purplei2p/i2pd/DaemonSingleton.java   | 98 ++++++++++---------
 .../org/purplei2p/i2pd/ForegroundService.java | 20 +++-
 android/src/org/purplei2p/i2pd/I2PD.java      | 26 ++---
 4 files changed, 81 insertions(+), 65 deletions(-)

diff --git a/android/res/values/strings.xml b/android/res/values/strings.xml
index 8c78e88b..0b8bef38 100755
--- a/android/res/values/strings.xml
+++ b/android/res/values/strings.xml
@@ -2,6 +2,8 @@
 <resources>
     <string name="app_name">i2pd</string>
     <string name="i2pd_started">i2pd started</string>
+    <string name="i2pd_service_started">i2pd service started</string>
+    <string name="i2pd_service_stopped">i2pd service stopped</string>
     <string name="action_quit">Quit</string>
     <string name="action_graceful_quit">Graceful Quit</string>
     <string name="graceful_quit_is_already_in_progress">Graceful quit is already in progress</string>
diff --git a/android/src/org/purplei2p/i2pd/DaemonSingleton.java b/android/src/org/purplei2p/i2pd/DaemonSingleton.java
index 5e0ac4d0..e1ebc269 100644
--- a/android/src/org/purplei2p/i2pd/DaemonSingleton.java
+++ b/android/src/org/purplei2p/i2pd/DaemonSingleton.java
@@ -8,20 +8,20 @@ import android.util.Log;
 public class DaemonSingleton {
 	private static final String TAG="i2pd";
 	private static final DaemonSingleton instance = new DaemonSingleton();
-	public static interface StateChangeListener { void daemonStateChanged(); }
-	private final Set<StateChangeListener> stateChangeListeners = new HashSet<StateChangeListener>();
+	public static interface StateUpdateListener { void daemonStateUpdate(); }
+	private final Set<StateUpdateListener> stateUpdateListeners = new HashSet<StateUpdateListener>();
 
 	public static DaemonSingleton getInstance() {
 		return instance;
 	}
 	
-	public synchronized void addStateChangeListener(StateChangeListener listener) { stateChangeListeners.add(listener); }
-	public synchronized void removeStateChangeListener(StateChangeListener listener) { stateChangeListeners.remove(listener); }
+	public synchronized void addStateChangeListener(StateUpdateListener listener) { stateUpdateListeners.add(listener); }
+	public synchronized void removeStateChangeListener(StateUpdateListener listener) { stateUpdateListeners.remove(listener); }
 	
 	public synchronized void stopAcceptingTunnels() {
 		if(isStartedOkay()){
 			state=State.gracefulShutdownInProgress;
-			fireStateChange();
+			fireStateUpdate();
 			I2PD_JNI.stopAcceptingTunnels();
 		}
 	}
@@ -32,61 +32,63 @@ public class DaemonSingleton {
 	
 	private boolean startedOkay;
 
-	public static enum State {starting,jniLibraryLoaded,startedOkay,startFailed,gracefulShutdownInProgress};
+	public static enum State {uninitialized,starting,jniLibraryLoaded,startedOkay,startFailed,gracefulShutdownInProgress};
 	
-	private State state = State.starting;
+	private State state = State.uninitialized;
 	
 	public State getState() { return state; }
 	
-	{
-		synchronized(this){
-			fireStateChange();
-			new Thread(new Runnable(){
-	
-				@Override
-				public void run() {
-					try {
-						I2PD_JNI.loadLibraries();
-						synchronized (DaemonSingleton.this) {
-							state = State.jniLibraryLoaded;
-							fireStateChange();
-						}
-					} catch (Throwable tr) {
-						lastThrowable=tr;
-						synchronized (DaemonSingleton.this) {
-							state = State.startFailed;
-							fireStateChange();
-						}
-						return;
+	public synchronized void start() {
+		if(state != State.uninitialized)return;
+		state = State.starting;
+		fireStateUpdate();
+		new Thread(new Runnable(){
+
+			@Override
+			public void run() {
+				try {
+					I2PD_JNI.loadLibraries();
+					synchronized (DaemonSingleton.this) {
+						state = State.jniLibraryLoaded;
+						fireStateUpdate();
 					}
-					try {
-						synchronized (DaemonSingleton.this) {
-							daemonStartResult = I2PD_JNI.startDaemon();
-							if("ok".equals(daemonStartResult)){state=State.startedOkay;setStartedOkay(true);}
-							else state=State.startFailed;
-							fireStateChange();
-						}
-					} catch (Throwable tr) {
-						lastThrowable=tr;
-						synchronized (DaemonSingleton.this) {
-							state = State.startFailed;
-							fireStateChange();
-						}
-						return;
-					}				
+				} catch (Throwable tr) {
+					lastThrowable=tr;
+					synchronized (DaemonSingleton.this) {
+						state = State.startFailed;
+						fireStateUpdate();
+					}
+					return;
 				}
-				
-			}, "i2pdDaemonStart").start();
-		}
+				try {
+					synchronized (DaemonSingleton.this) {
+						daemonStartResult = I2PD_JNI.startDaemon();
+						if("ok".equals(daemonStartResult)){
+							state=State.startedOkay;
+							setStartedOkay(true);
+						}else state=State.startFailed;
+						fireStateUpdate();
+					}
+				} catch (Throwable tr) {
+					lastThrowable=tr;
+					synchronized (DaemonSingleton.this) {
+						state = State.startFailed;
+						fireStateUpdate();
+					}
+					return;
+				}				
+			}
+			
+		}, "i2pdDaemonStart").start();
 	}
 	private Throwable lastThrowable;
 	private String daemonStartResult="N/A";
 
-	private synchronized void fireStateChange() {
+	private synchronized void fireStateUpdate() {
 		Log.i(TAG, "daemon state change: "+state);
-		for(StateChangeListener listener : stateChangeListeners) {
+		for(StateUpdateListener listener : stateUpdateListeners) {
 			try { 
-				listener.daemonStateChanged(); 
+				listener.daemonStateUpdate(); 
 			} catch (Throwable tr) { 
 				Log.e(TAG, "exception in listener ignored", tr); 
 			}
diff --git a/android/src/org/purplei2p/i2pd/ForegroundService.java b/android/src/org/purplei2p/i2pd/ForegroundService.java
index 16155651..d25d0a88 100644
--- a/android/src/org/purplei2p/i2pd/ForegroundService.java
+++ b/android/src/org/purplei2p/i2pd/ForegroundService.java
@@ -1,15 +1,17 @@
 package org.purplei2p.i2pd;
 
 import android.app.Notification;
+import android.app.NotificationManager;
 import android.app.PendingIntent;
 import android.app.Service;
 import android.content.Intent;
 import android.os.Binder;
 import android.os.IBinder;
 import android.util.Log;
+import android.widget.Toast;
 
 public class ForegroundService extends Service {
-//    private NotificationManager mNM;
+    private NotificationManager notificationManager;
 
     // Unique Identification Number for the Notification.
     // We use it on Notification start, and to cancel it.
@@ -28,26 +30,31 @@ public class ForegroundService extends Service {
 
     @Override
     public void onCreate() {
-//        mNM = (NotificationManager)getSystemService(NOTIFICATION_SERVICE);
+        notificationManager = (NotificationManager)getSystemService(NOTIFICATION_SERVICE);
 
         // Display a notification about us starting.  We put an icon in the status bar.
         showNotification();
+        daemon.start();
+        // Tell the user we started.
+        Toast.makeText(this, R.string.i2pd_service_started, Toast.LENGTH_SHORT).show();
     }
 
     @Override
     public int onStartCommand(Intent intent, int flags, int startId) {
         Log.i("ForegroundService", "Received start id " + startId + ": " + intent);
-        return START_NOT_STICKY;
+        daemon.start();
+        return START_STICKY;
     }
 
     @Override
     public void onDestroy() {
         // Cancel the persistent notification.
-        //mNM.cancel(NOTIFICATION);
+        notificationManager.cancel(NOTIFICATION);
+        
         stopForeground(true);
 
         // Tell the user we stopped.
-        //Toast.makeText(this, R.string.local_service_stopped, Toast.LENGTH_SHORT).show();
+        Toast.makeText(this, R.string.i2pd_service_stopped, Toast.LENGTH_SHORT).show();
     }
 
     @Override
@@ -84,4 +91,7 @@ public class ForegroundService extends Service {
         //mNM.notify(NOTIFICATION, notification);
         startForeground(NOTIFICATION, notification);
     }
+    
+	private final DaemonSingleton daemon = DaemonSingleton.getInstance();
 }
+
diff --git a/android/src/org/purplei2p/i2pd/I2PD.java b/android/src/org/purplei2p/i2pd/I2PD.java
index 0397cf03..86b877ac 100755
--- a/android/src/org/purplei2p/i2pd/I2PD.java
+++ b/android/src/org/purplei2p/i2pd/I2PD.java
@@ -22,12 +22,16 @@ import android.widget.Toast;
 
 public class I2PD extends Activity {
 	private static final String TAG = "i2pd";
-	private DaemonSingleton daemon = DaemonSingleton.getInstance();
-	private DaemonSingleton.StateChangeListener daemonStateChangeListener = 
-			new DaemonSingleton.StateChangeListener() {
+
+	private TextView textView;
+	
+	private final DaemonSingleton daemon = DaemonSingleton.getInstance();
+	
+	private DaemonSingleton.StateUpdateListener daemonStateUpdatedListener = 
+			new DaemonSingleton.StateUpdateListener() {
 		
 		@Override
-		public void daemonStateChanged() {
+		public void daemonStateUpdate() {
 			runOnUiThread(new Runnable(){
 
 				@Override
@@ -50,19 +54,17 @@ public class I2PD extends Activity {
 		}
 	};
 	
-	private TextView textView;
-	
     @Override
     public void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
 
-        //set the app be foreground (do not unload when RAM needed)
-        doBindService();
-
         textView = new TextView(this);
         setContentView(textView);
-        daemonStateChangeListener.daemonStateChanged();
-        daemon.addStateChangeListener(daemonStateChangeListener);
+        DaemonSingleton.getInstance().addStateChangeListener(daemonStateUpdatedListener);
+        daemonStateUpdatedListener.daemonStateUpdate();
+
+        //set the app be foreground
+        doBindService();
     }
 
     @Override
@@ -73,7 +75,7 @@ public class I2PD extends Activity {
 
 	private void localDestroy() {
 		textView = null;
-		daemon.removeStateChangeListener(daemonStateChangeListener);
+		DaemonSingleton.getInstance().removeStateChangeListener(daemonStateUpdatedListener);
 		Timer gracefulQuitTimer = getGracefulQuitTimer();
 		if(gracefulQuitTimer!=null) {
 			gracefulQuitTimer.cancel();