1
0
Fork 0
mirror of https://github.com/syncthing/syncthing-android.git synced 2024-11-23 04:41:16 +00:00

Improved polling for web gui (fixes #7).

This commit is contained in:
Felix Ableitner 2014-05-15 14:54:21 +02:00
parent a2a3f189b3
commit 7576e2a47d
2 changed files with 109 additions and 29 deletions

View file

@ -8,7 +8,6 @@ import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.ServiceConnection; import android.content.ServiceConnection;
import android.os.Bundle; import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder; import android.os.IBinder;
import android.util.Log; import android.util.Log;
import android.view.Menu; import android.view.Menu;
@ -30,7 +29,7 @@ import java.io.InputStream;
/** /**
* Holds a WebView that shows the web ui of the local syncthing instance. * Holds a WebView that shows the web ui of the local syncthing instance.
*/ */
public class WebGuiActivity extends Activity { public class WebGuiActivity extends Activity implements SyncthingService.OnWebGuiAvailableListener {
private static final String TAG = "WebGuiActivity"; private static final String TAG = "WebGuiActivity";
@ -54,6 +53,7 @@ public class WebGuiActivity extends Activity {
public void onServiceConnected(ComponentName className, IBinder service) { public void onServiceConnected(ComponentName className, IBinder service) {
SyncthingServiceBinder binder = (SyncthingServiceBinder) service; SyncthingServiceBinder binder = (SyncthingServiceBinder) service;
mSyncthingService = binder.getService(); mSyncthingService = binder.getService();
mSyncthingService.registerOnWebGuiAvailableListener(WebGuiActivity.this);
} }
public void onServiceDisconnected(ComponentName className) { public void onServiceDisconnected(ComponentName className) {
@ -62,32 +62,14 @@ public class WebGuiActivity extends Activity {
}; };
/** /**
* Retries loading every second until the web UI becomes available. * Hides the loading screen and shows the WebView once it is fully loaded.
*/ */
private WebViewClient mWebViewClient = new WebViewClient() { private WebViewClient mWebViewClient = new WebViewClient() {
private int mError = 0;
@Override
public void onReceivedError(WebView view, int errorCode, String description,
String failingUrl) {
mError = errorCode;
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
mError = 0;
mWebView.loadUrl(SyncthingService.SYNCTHING_URL);
}
}, 1000);
}
@Override @Override
public void onPageFinished(WebView view, String url) { public void onPageFinished(WebView view, String url) {
if (mError == 0) { mWebView.setVisibility(View.VISIBLE);
mWebView.setVisibility(View.VISIBLE); mLoadingView.setVisibility(View.GONE);
mLoadingView.setVisibility(View.GONE);
}
} }
}; };
@ -100,11 +82,6 @@ public class WebGuiActivity extends Activity {
@SuppressLint("SetJavaScriptEnabled") @SuppressLint("SetJavaScriptEnabled")
public void onCreate(Bundle savedInstanceState) { public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
getApplicationContext().startService(
new Intent(this, SyncthingService.class));
getApplicationContext().bindService(
new Intent(this, SyncthingService.class),
mSyncthingServiceConnection, Context.BIND_AUTO_CREATE);
setContentView(R.layout.main); setContentView(R.layout.main);
@ -115,7 +92,6 @@ public class WebGuiActivity extends Activity {
mWebView = (WebView) findViewById(R.id.webview); mWebView = (WebView) findViewById(R.id.webview);
mWebView.getSettings().setJavaScriptEnabled(true); mWebView.getSettings().setJavaScriptEnabled(true);
mWebView.setWebViewClient(mWebViewClient); mWebView.setWebViewClient(mWebViewClient);
mWebView.loadUrl(SyncthingService.SYNCTHING_URL);
// Handle first start. // Handle first start.
File config = new File(getApplicationInfo().dataDir, CONFIG_FILE); File config = new File(getApplicationInfo().dataDir, CONFIG_FILE);
@ -130,8 +106,23 @@ public class WebGuiActivity extends Activity {
.setNeutralButton(android.R.string.ok, null) .setNeutralButton(android.R.string.ok, null)
.show(); .show();
} }
getApplicationContext().startService(
new Intent(this, SyncthingService.class));
getApplicationContext().bindService(
new Intent(this, SyncthingService.class),
mSyncthingServiceConnection, Context.BIND_AUTO_CREATE);
} }
/**
* Loads and shows WebView, hides loading view.
*/
@Override
public void onWebGuiAvailable() {
mWebView.loadUrl(SyncthingService.SYNCTHING_URL);
}
@Override @Override
public void onDestroy() { public void onDestroy() {
super.onDestroy(); super.onDestroy();

View file

@ -4,6 +4,7 @@ import android.app.Notification;
import android.app.PendingIntent; import android.app.PendingIntent;
import android.app.Service; import android.app.Service;
import android.content.Intent; import android.content.Intent;
import android.os.AsyncTask;
import android.os.IBinder; import android.os.IBinder;
import android.support.v4.app.NotificationCompat; import android.support.v4.app.NotificationCompat;
import android.util.Log; import android.util.Log;
@ -11,10 +12,21 @@ import android.util.Log;
import com.nutomic.syncthingandroid.R; import com.nutomic.syncthingandroid.R;
import com.nutomic.syncthingandroid.WebGuiActivity; import com.nutomic.syncthingandroid.WebGuiActivity;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpHead;
import org.apache.http.conn.HttpHostConnectException;
import org.apache.http.impl.client.DefaultHttpClient;
import java.io.BufferedReader; import java.io.BufferedReader;
import java.io.DataOutputStream; import java.io.DataOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader; import java.io.InputStreamReader;
import java.util.LinkedList;
/** /**
* Holds the native syncthing instance and provides an API to access it. * Holds the native syncthing instance and provides an API to access it.
@ -35,8 +47,26 @@ public class SyncthingService extends Service {
*/ */
public static final String SYNCTHING_URL = "http://127.0.0.1:8080"; public static final String SYNCTHING_URL = "http://127.0.0.1:8080";
/**
* Interval in ms, at which connections to the web gui are performed on first start
* to find out if it's online.
*/
private static final long WEB_GUI_POLL_INTERVAL = 100;
private final SyncthingServiceBinder mBinder = new SyncthingServiceBinder(this); private final SyncthingServiceBinder mBinder = new SyncthingServiceBinder(this);
/**
* Callback for when the Syncthing web interface becomes first available after service start.
*/
public interface OnWebGuiAvailableListener {
public void onWebGuiAvailable();
}
private LinkedList<OnWebGuiAvailableListener> mOnWebGuiAvailableListeners =
new LinkedList<OnWebGuiAvailableListener>();
private boolean mIsWebGuiAvailable = false;
@Override @Override
public int onStartCommand(Intent intent, int flags, int startId) { public int onStartCommand(Intent intent, int flags, int startId) {
return START_STICKY; return START_STICKY;
@ -101,6 +131,48 @@ public class SyncthingService extends Service {
} }
} }
/**
* Polls SYNCTHING_URL until it returns HTTP status OK, then calls all listeners
* in mOnWebGuiAvailableListeners and clears it.
*/
private class PollWebGuiAvailableTask extends AsyncTask<Void, Void, Void> {
@Override
protected Void doInBackground(Void... voids) {
int status = 0;
HttpClient httpclient = new DefaultHttpClient();
HttpHead head = new HttpHead(SYNCTHING_URL);
do {
try {
Thread.sleep(WEB_GUI_POLL_INTERVAL);
HttpResponse response = httpclient.execute(head);
// NOTE: status is not really needed, as HttpHostConnectException is thrown
// earlier.
status = response.getStatusLine().getStatusCode();
}
catch (HttpHostConnectException e) {
// We catch this in every call, as long as the service is not online,
// so we ignore and continue.
}
catch (IOException e) {
Log.d(TAG, "Failed to poll for web interface", e);
}
catch (InterruptedException e) {
Log.d(TAG, "Failed to poll for web interface", e);
}
} while(status != HttpStatus.SC_OK);
return null;
}
@Override
protected void onPostExecute(Void aVoid) {
mIsWebGuiAvailable = true;
for (OnWebGuiAvailableListener listener : mOnWebGuiAvailableListeners) {
listener.onWebGuiAvailable();
}
mOnWebGuiAvailableListeners.clear();
}
}
/** /**
* Creates notification, starts native binary. * Creates notification, starts native binary.
*/ */
@ -118,6 +190,8 @@ public class SyncthingService extends Service {
startForeground(NOTIFICATION_ID, n); startForeground(NOTIFICATION_ID, n);
new Thread(new NativeSyncthingRunnable()).start(); new Thread(new NativeSyncthingRunnable()).start();
new PollWebGuiAvailableTask().execute();
} }
@Override @Override
@ -134,4 +208,19 @@ public class SyncthingService extends Service {
new PostTask().execute(PostTask.URI_SHUTDOWN); new PostTask().execute(PostTask.URI_SHUTDOWN);
} }
/**
* Register a listener for the web gui becoming available..
*
* If the web gui is already available, listener will be called immediately.
* Listeners are unregistered automatically after being called.
*/
public void registerOnWebGuiAvailableListener(OnWebGuiAvailableListener listener) {
if (mIsWebGuiAvailable) {
listener.onWebGuiAvailable();
}
else {
mOnWebGuiAvailableListeners.addLast(listener);
}
}
} }