mirror of
https://github.com/syncthing/syncthing-android.git
synced 2024-11-22 12:21:15 +00:00
Add syncthing preferences to app settings.
This commit is contained in:
parent
870c451477
commit
edea290ea6
7 changed files with 260 additions and 8 deletions
|
@ -9,34 +9,64 @@ import android.net.Uri;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.IBinder;
|
import android.os.IBinder;
|
||||||
|
import android.preference.CheckBoxPreference;
|
||||||
|
import android.preference.EditTextPreference;
|
||||||
import android.preference.Preference;
|
import android.preference.Preference;
|
||||||
import android.preference.PreferenceActivity;
|
import android.preference.PreferenceActivity;
|
||||||
import android.preference.PreferenceScreen;
|
import android.preference.PreferenceScreen;
|
||||||
import android.support.v4.app.NavUtils;
|
import android.support.v4.app.NavUtils;
|
||||||
|
import android.text.InputType;
|
||||||
|
import android.util.Log;
|
||||||
import android.view.MenuItem;
|
import android.view.MenuItem;
|
||||||
|
|
||||||
|
import com.nutomic.syncthingandroid.syncthing.RestApi;
|
||||||
import com.nutomic.syncthingandroid.syncthing.SyncthingService;
|
import com.nutomic.syncthingandroid.syncthing.SyncthingService;
|
||||||
import com.nutomic.syncthingandroid.syncthing.SyncthingServiceBinder;
|
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 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 static final String SYNCTHING_VERSION_KEY = "syncthing_version";
|
||||||
|
|
||||||
|
private Preference mVersion;
|
||||||
|
|
||||||
|
private PreferenceScreen mOptionsScreen;
|
||||||
|
|
||||||
|
private PreferenceScreen mGuiScreen;
|
||||||
|
|
||||||
private SyncthingService mSyncthingService;
|
private SyncthingService mSyncthingService;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Binds to service and sets version name. The version name can not be retrieved if the service
|
* Binds to service and sets syncthing preferences from Rest API.
|
||||||
* is just started (as we don't wait until the api is up).
|
|
||||||
*/
|
*/
|
||||||
private ServiceConnection mSyncthingServiceConnection = new ServiceConnection() {
|
private ServiceConnection mSyncthingServiceConnection = new ServiceConnection() {
|
||||||
|
|
||||||
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();
|
||||||
Preference version = getPreferenceScreen().findPreference(SYNCTHING_VERSION_KEY);
|
mVersion.setSummary(mSyncthingService.getApi().getVersion());
|
||||||
version.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) {
|
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.
|
* Loads layout, sets version from Rest API.
|
||||||
*
|
*
|
||||||
|
@ -64,6 +110,10 @@ public class SettingsActivity extends PreferenceActivity {
|
||||||
mSyncthingServiceConnection, Context.BIND_AUTO_CREATE);
|
mSyncthingServiceConnection, Context.BIND_AUTO_CREATE);
|
||||||
|
|
||||||
addPreferencesFromResource(R.xml.settings);
|
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
|
@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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,6 +23,8 @@ public class GetTask extends AsyncTask<String, Void, String> {
|
||||||
|
|
||||||
private static final String TAG = "GetTask";
|
private static final String TAG = "GetTask";
|
||||||
|
|
||||||
|
public static final String URI_CONFIG = "/rest/config";
|
||||||
|
|
||||||
public static final String URI_VERSION = "/rest/version";
|
public static final String URI_VERSION = "/rest/version";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -6,6 +6,7 @@ import android.util.Log;
|
||||||
import org.apache.http.HttpResponse;
|
import org.apache.http.HttpResponse;
|
||||||
import org.apache.http.client.HttpClient;
|
import org.apache.http.client.HttpClient;
|
||||||
import org.apache.http.client.methods.HttpPost;
|
import org.apache.http.client.methods.HttpPost;
|
||||||
|
import org.apache.http.entity.StringEntity;
|
||||||
import org.apache.http.impl.client.DefaultHttpClient;
|
import org.apache.http.impl.client.DefaultHttpClient;
|
||||||
import org.apache.http.message.BasicHeader;
|
import org.apache.http.message.BasicHeader;
|
||||||
|
|
||||||
|
@ -20,10 +21,13 @@ public class PostTask extends AsyncTask<String, Void, Void> {
|
||||||
|
|
||||||
public static final String URI_SHUTDOWN = "/rest/shutdown";
|
public static final String URI_SHUTDOWN = "/rest/shutdown";
|
||||||
|
|
||||||
|
public static final String URI_CONFIG = "/rest/config";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* params[0] Syncthing hostname
|
* params[0] Syncthing hostname
|
||||||
* params[1] URI to call
|
* params[1] URI to call
|
||||||
* params[2] Syncthing API key
|
* params[2] Syncthing API key
|
||||||
|
* params[3] The request content
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
protected Void doInBackground(String... params) {
|
protected Void doInBackground(String... params) {
|
||||||
|
@ -33,6 +37,7 @@ public class PostTask extends AsyncTask<String, Void, Void> {
|
||||||
HttpPost post = new HttpPost(fullUri);
|
HttpPost post = new HttpPost(fullUri);
|
||||||
post.addHeader(new BasicHeader("X-API-Key", params[2]));
|
post.addHeader(new BasicHeader("X-API-Key", params[2]));
|
||||||
try {
|
try {
|
||||||
|
post.setEntity(new StringEntity(params[3]));
|
||||||
httpclient.execute(post);
|
httpclient.execute(post);
|
||||||
}
|
}
|
||||||
catch (IOException e) {
|
catch (IOException e) {
|
||||||
|
|
|
@ -5,6 +5,13 @@ import android.util.Log;
|
||||||
|
|
||||||
import com.nutomic.syncthingandroid.R;
|
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.
|
* 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";
|
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 mVersion;
|
||||||
|
|
||||||
private String mUrl;
|
private String mUrl;
|
||||||
|
|
||||||
private String mApiKey;
|
private String mApiKey;
|
||||||
|
|
||||||
|
private JSONObject mConfig;
|
||||||
|
|
||||||
public RestApi(String url, String apiKey) {
|
public RestApi(String url, String apiKey) {
|
||||||
mUrl = url;
|
mUrl = url;
|
||||||
mApiKey = apiKey;
|
mApiKey = apiKey;
|
||||||
|
@ -39,6 +58,17 @@ public class RestApi implements SyncthingService.OnWebGuiAvailableListener {
|
||||||
Log.i(TAG, "Syncthing version is " + mVersion);
|
Log.i(TAG, "Syncthing version is " + mVersion);
|
||||||
}
|
}
|
||||||
}.execute(mUrl, GetTask.URI_VERSION, mApiKey);
|
}.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.
|
* Stops syncthing. You should probably use SyncthingService.stopService() instead.
|
||||||
*/
|
*/
|
||||||
public void shutdown() {
|
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 <T> 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());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -347,7 +347,7 @@ public class SyncthingService extends Service {
|
||||||
// HACK: Make sure there is no syncthing binary left running from an improper
|
// HACK: Make sure there is no syncthing binary left running from an improper
|
||||||
// shutdown (eg Play Store update).
|
// shutdown (eg Play Store update).
|
||||||
// NOTE: This will log an exception if syncthing is not actually running.
|
// 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);
|
registerOnWebGuiAvailableListener(mApi);
|
||||||
new PollWebGuiAvailableTask().execute();
|
new PollWebGuiAvailableTask().execute();
|
||||||
new Thread(new SyncthingRunnable()).start();
|
new Thread(new SyncthingRunnable()).start();
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
<address>127.0.0.1:8080</address>
|
<address>127.0.0.1:8080</address>
|
||||||
</gui>
|
</gui>
|
||||||
<options>
|
<options>
|
||||||
<listenAddress>:22000</listenAddress>
|
<listenAddress>0.0.0.0:22000</listenAddress>
|
||||||
<globalAnnounceServer>194.126.249.5:22025</globalAnnounceServer>
|
<globalAnnounceServer>194.126.249.5:22025</globalAnnounceServer>
|
||||||
<globalAnnounceEnabled>true</globalAnnounceEnabled>
|
<globalAnnounceEnabled>true</globalAnnounceEnabled>
|
||||||
<localAnnounceEnabled>true</localAnnounceEnabled>
|
<localAnnounceEnabled>true</localAnnounceEnabled>
|
||||||
|
|
|
@ -1,6 +1,83 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
|
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
|
||||||
|
<PreferenceScreen
|
||||||
|
android:key="syncthing_options"
|
||||||
|
android:title="Syncthing Options"
|
||||||
|
android:persistent="false" >
|
||||||
|
|
||||||
|
<EditTextPreference
|
||||||
|
android:key="ListenAddress"
|
||||||
|
android:title="Sync Protocol Listen Addresses"
|
||||||
|
android:defaultValue="0.0.0.0:22000" />
|
||||||
|
|
||||||
|
<EditTextPreference
|
||||||
|
android:key="MaxSendKbps"
|
||||||
|
android:title="Outgoing Rate Limit (KiB/s)"
|
||||||
|
android:numeric="integer" />
|
||||||
|
|
||||||
|
<EditTextPreference
|
||||||
|
android:key="RescanIntervalS"
|
||||||
|
android:title="Rescan Interval (s)"
|
||||||
|
android:numeric="integer" />
|
||||||
|
|
||||||
|
<EditTextPreference
|
||||||
|
android:key="ReconnectIntervalS"
|
||||||
|
android:title="Reconnect Interval (s)"
|
||||||
|
android:numeric="integer" />
|
||||||
|
|
||||||
|
<EditTextPreference
|
||||||
|
android:key="ParallelRequests"
|
||||||
|
android:title="Max Outstanding Requests"
|
||||||
|
android:numeric="integer" />
|
||||||
|
|
||||||
|
<EditTextPreference
|
||||||
|
android:key="MaxChangeKbps"
|
||||||
|
android:title="Max File Change Rate (KiB/s)"
|
||||||
|
android:numeric="integer" />
|
||||||
|
|
||||||
|
<CheckBoxPreference
|
||||||
|
android:key="GlobalAnnEnabled"
|
||||||
|
android:title="Global Discovery" />
|
||||||
|
|
||||||
|
<CheckBoxPreference
|
||||||
|
android:key="LocalAnnEnabled"
|
||||||
|
android:title="Local Discovery" />
|
||||||
|
|
||||||
|
<EditTextPreference
|
||||||
|
android:key="LocalAnnPort"
|
||||||
|
android:title="Local Discovery Port"
|
||||||
|
android:numeric="integer" />
|
||||||
|
|
||||||
|
<CheckBoxPreference
|
||||||
|
android:key="UPnPEnabled"
|
||||||
|
android:title="Enable UPnP" />
|
||||||
|
|
||||||
|
</PreferenceScreen>
|
||||||
|
|
||||||
|
<PreferenceScreen
|
||||||
|
android:key="syncthing_gui"
|
||||||
|
android:title="Syncthing GUI"
|
||||||
|
android:persistent="false" >
|
||||||
|
|
||||||
|
<EditTextPreference
|
||||||
|
android:key="Address"
|
||||||
|
android:title="GUI Listen Addresses" />
|
||||||
|
|
||||||
|
<EditTextPreference
|
||||||
|
android:key="User"
|
||||||
|
android:title="GUI Authentication User" />
|
||||||
|
|
||||||
|
<EditTextPreference
|
||||||
|
android:key="Password"
|
||||||
|
android:title="GUI Authentication Password" />
|
||||||
|
|
||||||
|
<CheckBoxPreference
|
||||||
|
android:key="UseTLS"
|
||||||
|
android:title="Use HTTPS for GUI" />
|
||||||
|
|
||||||
|
</PreferenceScreen>
|
||||||
|
|
||||||
<Preference
|
<Preference
|
||||||
android:key="report_issue"
|
android:key="report_issue"
|
||||||
android:title="@string/report_issue_title"
|
android:title="@string/report_issue_title"
|
||||||
|
|
Loading…
Reference in a new issue