From 745de67005897ddbeae18b7f716dea83a9a7458f Mon Sep 17 00:00:00 2001 From: Felix Ableitner Date: Tue, 13 May 2014 11:09:07 +0200 Subject: [PATCH] Added proper shutdown (using hack). --- .../nutomic/syncthingandroid/RestTask.java | 38 ++++++++++++++++++ .../syncthingandroid/SyncthingService.java | 39 +++++++++++++++++- .../SyncthingServiceBinder.java | 17 ++++++++ .../syncthingandroid/WebGuiActivity.java | 40 ++++++++++++++----- 4 files changed, 123 insertions(+), 11 deletions(-) create mode 100644 src/main/java/com/nutomic/syncthingandroid/RestTask.java create mode 100644 src/main/java/com/nutomic/syncthingandroid/SyncthingServiceBinder.java diff --git a/src/main/java/com/nutomic/syncthingandroid/RestTask.java b/src/main/java/com/nutomic/syncthingandroid/RestTask.java new file mode 100644 index 00000000..0e4c4a2a --- /dev/null +++ b/src/main/java/com/nutomic/syncthingandroid/RestTask.java @@ -0,0 +1,38 @@ +package com.nutomic.syncthingandroid; + + +import android.os.AsyncTask; +import android.util.Log; + +import org.apache.http.HttpResponse; +import org.apache.http.HttpStatus; +import org.apache.http.StatusLine; +import org.apache.http.client.HttpClient; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.entity.StringEntity; +import org.apache.http.impl.client.DefaultHttpClient; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; + +/** + * Performs a POST request with no parameters to the URL in uri[0]. + */ +class RestTask extends AsyncTask { + + private static final String TAG = "RequestTask"; + + @Override + protected Void doInBackground(String... uri) { + HttpClient httpclient = new DefaultHttpClient(); + HttpPost httppost = new HttpPost(uri[0]); + String responseString = null; + try { + HttpResponse response = httpclient.execute(httppost); + } + catch (IOException e) { + Log.w(TAG, "Failed to call Rest API", e); + } + return null; + } +} diff --git a/src/main/java/com/nutomic/syncthingandroid/SyncthingService.java b/src/main/java/com/nutomic/syncthingandroid/SyncthingService.java index fc336992..07da2b3e 100644 --- a/src/main/java/com/nutomic/syncthingandroid/SyncthingService.java +++ b/src/main/java/com/nutomic/syncthingandroid/SyncthingService.java @@ -4,16 +4,33 @@ import android.app.Notification; import android.app.PendingIntent; import android.app.Service; import android.content.Intent; -import android.os.Bundle; import android.os.IBinder; import android.support.v4.app.NotificationCompat; +/** + * Holds the native syncthing instance and provides an API to access it. + */ public class SyncthingService extends Service { private static final String TAG = "SyncthingService"; private static final int NOTIFICATION_ID = 1; + /** + * URL of the local syncthing web UI. + */ + public static final String SYNCTHING_URL = "http://127.0.0.1:8080"; + + /** + * Path to call for shutdown (with POST). + */ + private static final String PATH_SHUTDOWN = "/rest/shutdown"; + + private final SyncthingServiceBinder mBinder = new SyncthingServiceBinder(this); + + /** + * Creates notification, starts native binary. + */ @Override public void onCreate() { PendingIntent pi = PendingIntent.getActivity( @@ -36,6 +53,24 @@ public class SyncthingService extends Service { @Override public IBinder onBind(Intent intent) { - return null; + return mBinder; + } + + /** + * Stops the native binary. + * + * NOTE: This stops all Activities and Services. + */ + @Override + public void onDestroy() { + super.onDestroy(); + new RestTask() { + @Override + protected void onPostExecute(Void aVoid) { + // HACK: Android does not release the memory for the native binary, so we explicitly + // stop the VM and thus also all Activities/Services. + System.exit(0); + } + }.execute(SYNCTHING_URL + PATH_SHUTDOWN); } } diff --git a/src/main/java/com/nutomic/syncthingandroid/SyncthingServiceBinder.java b/src/main/java/com/nutomic/syncthingandroid/SyncthingServiceBinder.java new file mode 100644 index 00000000..7d91d834 --- /dev/null +++ b/src/main/java/com/nutomic/syncthingandroid/SyncthingServiceBinder.java @@ -0,0 +1,17 @@ +package com.nutomic.syncthingandroid; + +import android.os.Binder; + +public class SyncthingServiceBinder extends Binder { + + SyncthingService mService; + + public SyncthingServiceBinder(SyncthingService service) { + mService = service; + } + + public SyncthingService getService() { + return mService; + } + +} \ No newline at end of file diff --git a/src/main/java/com/nutomic/syncthingandroid/WebGuiActivity.java b/src/main/java/com/nutomic/syncthingandroid/WebGuiActivity.java index de5bd24a..da64f09b 100644 --- a/src/main/java/com/nutomic/syncthingandroid/WebGuiActivity.java +++ b/src/main/java/com/nutomic/syncthingandroid/WebGuiActivity.java @@ -2,9 +2,13 @@ package com.nutomic.syncthingandroid; import android.app.Activity; import android.app.AlertDialog; +import android.content.ComponentName; +import android.content.Context; import android.content.Intent; +import android.content.ServiceConnection; import android.os.Bundle; import android.os.Handler; +import android.os.IBinder; import android.util.Log; import android.view.Menu; import android.view.MenuItem; @@ -23,11 +27,6 @@ public class WebGuiActivity extends Activity { private static final String TAG = "WebGuiActivity"; - /** - * URL of the local syncthing web UI. - */ - private static final String SYNCTHING_URL = "http://127.0.0.1:8080"; - /** * Folder where syncthing config is stored. * @@ -48,6 +47,20 @@ public class WebGuiActivity extends Activity { private WebView mWebView; private View mLoadingView; + private SyncthingService mSyncthingService; + + private ServiceConnection mSyncthingServiceConnection = new ServiceConnection() { + + public void onServiceConnected(ComponentName className, IBinder service) { + SyncthingServiceBinder binder = (SyncthingServiceBinder) service; + mSyncthingService = binder.getService(); + } + + public void onServiceDisconnected(ComponentName className) { + mSyncthingService = null; + } + }; + /** * Retries loading every second until the web UI becomes available. */ @@ -63,7 +76,7 @@ public class WebGuiActivity extends Activity { @Override public void run() { mError = 0; - mWebView.loadUrl(SYNCTHING_URL); + mWebView.loadUrl(SyncthingService.SYNCTHING_URL); } }, 1000); @@ -81,7 +94,9 @@ public class WebGuiActivity extends Activity { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - startService(new Intent(this, SyncthingService.class)); + getApplicationContext().bindService( + new Intent(this, SyncthingService.class), + mSyncthingServiceConnection, Context.BIND_AUTO_CREATE); setContentView(R.layout.main); @@ -92,7 +107,7 @@ public class WebGuiActivity extends Activity { mWebView = (WebView) findViewById(R.id.webview); mWebView.getSettings().setJavaScriptEnabled(true); mWebView.setWebViewClient(mWebViewClient); - mWebView.loadUrl(SYNCTHING_URL); + mWebView.loadUrl(SyncthingService.SYNCTHING_URL); // Handle first start. if (!new File(CONFIG_FOLDER, CERT_FILE).exists()) { @@ -108,6 +123,12 @@ public class WebGuiActivity extends Activity { } } + @Override + public void onDestroy() { + super.onDestroy(); + getApplicationContext().unbindService(mSyncthingServiceConnection); + } + @Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.menu, menu); @@ -121,8 +142,9 @@ public class WebGuiActivity extends Activity { startActivity(new Intent(this, SettingsActivity.class)); return true; case R.id.exit: - stopService(new Intent(this, SyncthingService.class)); + // Make sure we unbind first. finish(); + getApplicationContext().stopService(new Intent(this, SyncthingService.class)); return true; default: return super.onOptionsItemSelected(item);