From edea290ea65cca09cd191c8e10f31747b48120f7 Mon Sep 17 00:00:00 2001 From: Felix Ableitner Date: Fri, 30 May 2014 15:16:38 +0200 Subject: [PATCH] Add syncthing preferences to app settings. --- .../syncthingandroid/SettingsActivity.java | 86 ++++++++++++++++- .../syncthingandroid/syncthing/GetTask.java | 2 + .../syncthingandroid/syncthing/PostTask.java | 5 + .../syncthingandroid/syncthing/RestApi.java | 94 ++++++++++++++++++- .../syncthing/SyncthingService.java | 2 +- src/main/res/raw/config_default.xml | 2 +- src/main/res/xml/settings.xml | 77 +++++++++++++++ 7 files changed, 260 insertions(+), 8 deletions(-) diff --git a/src/main/java/com/nutomic/syncthingandroid/SettingsActivity.java b/src/main/java/com/nutomic/syncthingandroid/SettingsActivity.java index e17a658d..11b6d92b 100644 --- a/src/main/java/com/nutomic/syncthingandroid/SettingsActivity.java +++ b/src/main/java/com/nutomic/syncthingandroid/SettingsActivity.java @@ -9,34 +9,64 @@ import android.net.Uri; import android.os.Build; import android.os.Bundle; import android.os.IBinder; +import android.preference.CheckBoxPreference; +import android.preference.EditTextPreference; import android.preference.Preference; import android.preference.PreferenceActivity; import android.preference.PreferenceScreen; import android.support.v4.app.NavUtils; +import android.text.InputType; +import android.util.Log; import android.view.MenuItem; +import com.nutomic.syncthingandroid.syncthing.RestApi; import com.nutomic.syncthingandroid.syncthing.SyncthingService; import com.nutomic.syncthingandroid.syncthing.SyncthingServiceBinder; -public class SettingsActivity extends PreferenceActivity { +public class SettingsActivity extends PreferenceActivity + implements Preference.OnPreferenceChangeListener { private static final String REPORT_ISSUE_KEY = "report_issue"; + private static final String SYNCTHING_OPTIONS_KEY = "syncthing_options"; + + private static final String SYNCTHING_GUI_KEY = "syncthing_gui"; + private static final String SYNCTHING_VERSION_KEY = "syncthing_version"; + private Preference mVersion; + + private PreferenceScreen mOptionsScreen; + + private PreferenceScreen mGuiScreen; + private SyncthingService mSyncthingService; /** - * Binds to service and sets version name. The version name can not be retrieved if the service - * is just started (as we don't wait until the api is up). + * Binds to service and sets syncthing preferences from Rest API. */ private ServiceConnection mSyncthingServiceConnection = new ServiceConnection() { public void onServiceConnected(ComponentName className, IBinder service) { SyncthingServiceBinder binder = (SyncthingServiceBinder) service; mSyncthingService = binder.getService(); - Preference version = getPreferenceScreen().findPreference(SYNCTHING_VERSION_KEY); - version.setSummary(mSyncthingService.getApi().getVersion()); + mVersion.setSummary(mSyncthingService.getApi().getVersion()); + + for (int i = 0; i < mOptionsScreen.getPreferenceCount(); i++) { + Preference pref = mOptionsScreen.getPreference(i); + pref.setOnPreferenceChangeListener(SettingsActivity.this); + String value = mSyncthingService.getApi() + .getValue(RestApi.TYPE_OPTIONS, pref.getKey()); + applyPreference(pref, value); + } + + for (int i = 0; i < mGuiScreen.getPreferenceCount(); i++) { + Preference pref = mGuiScreen.getPreference(i); + pref.setOnPreferenceChangeListener(SettingsActivity.this); + String value = mSyncthingService.getApi() + .getValue(RestApi.TYPE_GUI, pref.getKey()); + applyPreference(pref, value); + } } public void onServiceDisconnected(ComponentName className) { @@ -44,6 +74,22 @@ public class SettingsActivity extends PreferenceActivity { } }; + /** + * Applies the given value to the preference. + * + * If pref is an EditTextPreference, setText is used and the value shown as summary. If pref is + * a CheckBoxPreference, setChecked is used (by parsing value as Boolean). + */ + private void applyPreference(Preference pref, String value) { + if (pref instanceof EditTextPreference) { + ((EditTextPreference) pref).setText(value); + pref.setSummary(value); + } + else if (pref instanceof CheckBoxPreference) { + ((CheckBoxPreference) pref).setChecked(Boolean.parseBoolean(value)); + } + } + /** * Loads layout, sets version from Rest API. * @@ -64,6 +110,10 @@ public class SettingsActivity extends PreferenceActivity { mSyncthingServiceConnection, Context.BIND_AUTO_CREATE); addPreferencesFromResource(R.xml.settings); + PreferenceScreen screen = getPreferenceScreen(); + mVersion = screen.findPreference(SYNCTHING_VERSION_KEY); + mOptionsScreen = (PreferenceScreen) screen.findPreference(SYNCTHING_OPTIONS_KEY); + mGuiScreen = (PreferenceScreen) screen.findPreference(SYNCTHING_GUI_KEY); } @Override @@ -99,4 +149,30 @@ public class SettingsActivity extends PreferenceActivity { } } + /** + * Sends the updated value to {@link }RestApi}, and sets it as the summary + * for EditTextPreference. + */ + @Override + public boolean onPreferenceChange(Preference preference, Object o) { + if (preference instanceof EditTextPreference) { + String value = (String) o; + preference.setSummary(value); + EditTextPreference etp = (EditTextPreference) preference; + if (etp.getEditText().getInputType() == InputType.TYPE_CLASS_NUMBER) { + o = Integer.parseInt((String) o); + } + } + + if (mOptionsScreen.findPreference(preference.getKey()) != null) { + mSyncthingService.getApi().setValue(RestApi.TYPE_OPTIONS, preference.getKey(), o, + preference.getKey().equals("ListenAddress")); + return true; + } + else if (mGuiScreen.findPreference(preference.getKey()) != null) { + mSyncthingService.getApi().setValue(RestApi.TYPE_GUI, preference.getKey(), o, false); + return true; + } + return false; + } } diff --git a/src/main/java/com/nutomic/syncthingandroid/syncthing/GetTask.java b/src/main/java/com/nutomic/syncthingandroid/syncthing/GetTask.java index d9ec6d26..e0aba1d9 100644 --- a/src/main/java/com/nutomic/syncthingandroid/syncthing/GetTask.java +++ b/src/main/java/com/nutomic/syncthingandroid/syncthing/GetTask.java @@ -23,6 +23,8 @@ public class GetTask extends AsyncTask { private static final String TAG = "GetTask"; + public static final String URI_CONFIG = "/rest/config"; + public static final String URI_VERSION = "/rest/version"; /** diff --git a/src/main/java/com/nutomic/syncthingandroid/syncthing/PostTask.java b/src/main/java/com/nutomic/syncthingandroid/syncthing/PostTask.java index dfe9cf0d..e90e1509 100644 --- a/src/main/java/com/nutomic/syncthingandroid/syncthing/PostTask.java +++ b/src/main/java/com/nutomic/syncthingandroid/syncthing/PostTask.java @@ -6,6 +6,7 @@ import android.util.Log; import org.apache.http.HttpResponse; 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 org.apache.http.message.BasicHeader; @@ -20,10 +21,13 @@ public class PostTask extends AsyncTask { public static final String URI_SHUTDOWN = "/rest/shutdown"; + public static final String URI_CONFIG = "/rest/config"; + /** * params[0] Syncthing hostname * params[1] URI to call * params[2] Syncthing API key + * params[3] The request content */ @Override protected Void doInBackground(String... params) { @@ -33,6 +37,7 @@ public class PostTask extends AsyncTask { HttpPost post = new HttpPost(fullUri); post.addHeader(new BasicHeader("X-API-Key", params[2])); try { + post.setEntity(new StringEntity(params[3])); httpclient.execute(post); } catch (IOException e) { diff --git a/src/main/java/com/nutomic/syncthingandroid/syncthing/RestApi.java b/src/main/java/com/nutomic/syncthingandroid/syncthing/RestApi.java index 8faaa77d..ecd5a663 100644 --- a/src/main/java/com/nutomic/syncthingandroid/syncthing/RestApi.java +++ b/src/main/java/com/nutomic/syncthingandroid/syncthing/RestApi.java @@ -5,6 +5,13 @@ import android.util.Log; import com.nutomic.syncthingandroid.R; +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import java.lang.reflect.Array; +import java.util.Arrays; + /** * Provides functions to interact with the syncthing REST API. */ @@ -12,12 +19,24 @@ public class RestApi implements SyncthingService.OnWebGuiAvailableListener { private static final String TAG = "RestApi"; + /** + * Parameter for {@link #getValue} or {@link #setValue} referring to "options" config item. + */ + public static final String TYPE_OPTIONS = "Options"; + + /** + * Parameter for {@link #getValue} or {@link #setValue} referring to "gui" config item. + */ + public static final String TYPE_GUI = "GUI"; + private String mVersion; private String mUrl; private String mApiKey; + private JSONObject mConfig; + public RestApi(String url, String apiKey) { mUrl = url; mApiKey = apiKey; @@ -39,6 +58,17 @@ public class RestApi implements SyncthingService.OnWebGuiAvailableListener { Log.i(TAG, "Syncthing version is " + mVersion); } }.execute(mUrl, GetTask.URI_VERSION, mApiKey); + new GetTask() { + @Override + protected void onPostExecute(String config) { + try { + mConfig = new JSONObject(config); + } + catch (JSONException e) { + Log.w(TAG, "Failed to parse config", e); + } + } + }.execute(mUrl, GetTask.URI_CONFIG, mApiKey); } /** @@ -52,7 +82,69 @@ public class RestApi implements SyncthingService.OnWebGuiAvailableListener { * Stops syncthing. You should probably use SyncthingService.stopService() instead. */ public void shutdown() { - new PostTask().execute(mUrl, PostTask.URI_SHUTDOWN, mApiKey); + new PostTask().execute(mUrl, PostTask.URI_SHUTDOWN, mApiKey, ""); + } + + /** + * Gets a value from config, + * + * Booleans are returned as {@link }Boolean#toString}, arrays as space seperated string. + * + * @param name {@link #TYPE_OPTIONS} or {@link #TYPE_GUI} + * @param key The key to read from. + * @return The value as a String, or null on failure. + */ + public String getValue(String name, String key) { + try { + Object value = mConfig.getJSONObject(name).get(key); + return (value instanceof JSONArray) + // TODO: also remove " + ? ((JSONArray) value).join(" ").replace("\"", "") + : String.valueOf(value); + } + catch (JSONException e) { + Log.w(TAG, "Failed to get value for " + key, e); + return null; + } + } + + /** + * Sets a value to config and sends it via Rest API. + * + * Booleans must be passed as {@link Boolean}, arrays as space seperated string + * with isArray true. + * + * @param name {@link #TYPE_OPTIONS} or {@link #TYPE_GUI} + * @param key The key to write to. + * @param value The new value to set, either String, Boolean or Integer. + * @param isArray True iff value is a space seperated String that should be converted to array. + */ + public void setValue(String name, String key, T value, boolean isArray) { + try { + if (isArray) { + JSONArray json = new JSONArray(); + for (String s : ((String) value).split(" ")) { + json.put(s); + } + mConfig.getJSONObject(name).put(key, json); + } + else { + mConfig.getJSONObject(name).put(key, value); + } + configUpdated(); + } + catch (JSONException e) { + Log.w(TAG, "Failed to set value for " + key, e); + } + } + + /** + * Sends the updated mConfig via Rest API to syncthing. + * + * TODO: Show a restart notification + */ + private void configUpdated() { + new PostTask().execute(mUrl, PostTask.URI_CONFIG, mConfig.toString()); } } diff --git a/src/main/java/com/nutomic/syncthingandroid/syncthing/SyncthingService.java b/src/main/java/com/nutomic/syncthingandroid/syncthing/SyncthingService.java index d0e96dd2..48a1eacc 100644 --- a/src/main/java/com/nutomic/syncthingandroid/syncthing/SyncthingService.java +++ b/src/main/java/com/nutomic/syncthingandroid/syncthing/SyncthingService.java @@ -347,7 +347,7 @@ public class SyncthingService extends Service { // HACK: Make sure there is no syncthing binary left running from an improper // shutdown (eg Play Store update). // NOTE: This will log an exception if syncthing is not actually running. - new PostTask().execute(mApi.getUrl(), PostTask.URI_SHUTDOWN, urlAndKey.second); + new PostTask().execute(mApi.getUrl(), PostTask.URI_SHUTDOWN, urlAndKey.second, ""); registerOnWebGuiAvailableListener(mApi); new PollWebGuiAvailableTask().execute(); new Thread(new SyncthingRunnable()).start(); diff --git a/src/main/res/raw/config_default.xml b/src/main/res/raw/config_default.xml index 7151ba9b..8ef5c89f 100644 --- a/src/main/res/raw/config_default.xml +++ b/src/main/res/raw/config_default.xml @@ -3,7 +3,7 @@
127.0.0.1:8080
- :22000 + 0.0.0.0:22000 194.126.249.5:22025 true true diff --git a/src/main/res/xml/settings.xml b/src/main/res/xml/settings.xml index aba5d7e7..0d005605 100644 --- a/src/main/res/xml/settings.xml +++ b/src/main/res/xml/settings.xml @@ -1,6 +1,83 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +