mirror of
https://github.com/syncthing/syncthing-android.git
synced 2024-12-23 03:11:30 +00:00
Added proper restart handling (new config data is automatically loaded after restart).
This commit is contained in:
parent
b67c625318
commit
dff9156d77
10 changed files with 233 additions and 188 deletions
|
@ -34,9 +34,8 @@ import com.nutomic.syncthingandroid.syncthing.SyncthingService;
|
|||
/**
|
||||
* {@link android.support.v4.app.ListFragment} that shows a configurable loading text.
|
||||
*/
|
||||
public abstract class LoadingListFragment extends Fragment implements RestApi.OnApiAvailableListener, AdapterView.OnItemClickListener {
|
||||
|
||||
private boolean mInitialized = false;
|
||||
public abstract class LoadingListFragment extends Fragment implements
|
||||
SyncthingService.OnApiAvailableListener, AdapterView.OnItemClickListener {
|
||||
|
||||
private ListFragment mListFragment;
|
||||
|
||||
|
@ -109,11 +108,10 @@ public abstract class LoadingListFragment extends Fragment implements RestApi.On
|
|||
@Override
|
||||
public void onApiAvailable() {
|
||||
MainActivity activity = (MainActivity) getActivity();
|
||||
if (!mInitialized && getActivity() != null &&
|
||||
if (getActivity() != null &&
|
||||
activity.getApi() != null && mListFragment != null) {
|
||||
onInitAdapter(activity);
|
||||
getListView().setOnItemClickListener(this);
|
||||
mInitialized = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -26,7 +26,7 @@ import java.util.TimerTask;
|
|||
*/
|
||||
public class LocalNodeInfoFragment extends Fragment
|
||||
implements RestApi.OnReceiveSystemInfoListener, RestApi.OnReceiveConnectionsListener,
|
||||
RestApi.OnApiAvailableListener {
|
||||
SyncthingService.OnApiAvailableListener {
|
||||
|
||||
private TextView mNodeId;
|
||||
|
||||
|
|
|
@ -54,9 +54,9 @@ public class MainActivity extends ActionBarActivity
|
|||
*/
|
||||
@Override
|
||||
public void onWebGuiAvailable() {
|
||||
mSyncthingService.getApi().registerOnApiAvailableListener(mRepositoriesFragment);
|
||||
mSyncthingService.getApi().registerOnApiAvailableListener(mNodesFragment);
|
||||
mSyncthingService.getApi().registerOnApiAvailableListener(mLocalNodeInfoFragment);
|
||||
mSyncthingService.registerOnApiAvailableListener(mRepositoriesFragment);
|
||||
mSyncthingService.registerOnApiAvailableListener(mNodesFragment);
|
||||
mSyncthingService.registerOnApiAvailableListener(mLocalNodeInfoFragment);
|
||||
mDrawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED);
|
||||
mDrawerLayout.setDrawerListener(mDrawerToggle);
|
||||
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
|
||||
|
@ -220,14 +220,14 @@ public class MainActivity extends ActionBarActivity
|
|||
}
|
||||
|
||||
switch (item.getItemId()) {
|
||||
case R.id.add_node:
|
||||
Intent intent = new Intent(this, NodeSettingsActivity.class);
|
||||
intent.setAction(NodeSettingsActivity.ACTION_CREATE);
|
||||
case R.id.add_repository:
|
||||
Intent intent = new Intent(this, RepoSettingsActivity.class);
|
||||
intent.setAction(RepoSettingsActivity.ACTION_CREATE);
|
||||
startActivity(intent);
|
||||
return true;
|
||||
case R.id.add_repository:
|
||||
intent = new Intent(this, RepoSettingsActivity.class);
|
||||
intent.setAction(RepoSettingsActivity.ACTION_CREATE);
|
||||
case R.id.add_node:
|
||||
intent = new Intent(this, NodeSettingsActivity.class);
|
||||
intent.setAction(NodeSettingsActivity.ACTION_CREATE);
|
||||
startActivity(intent);
|
||||
return true;
|
||||
case R.id.web_gui:
|
||||
|
|
|
@ -28,7 +28,7 @@ import java.util.Map;
|
|||
*/
|
||||
public class NodeSettingsActivity extends PreferenceActivity implements
|
||||
Preference.OnPreferenceChangeListener, Preference.OnPreferenceClickListener,
|
||||
RestApi.OnReceiveConnectionsListener, RestApi.OnApiAvailableListener {
|
||||
RestApi.OnReceiveConnectionsListener, SyncthingService.OnApiAvailableListener {
|
||||
|
||||
public static final String ACTION_CREATE = "create";
|
||||
|
||||
|
@ -43,8 +43,7 @@ public class NodeSettingsActivity extends PreferenceActivity implements
|
|||
public void onServiceConnected(ComponentName className, IBinder service) {
|
||||
SyncthingServiceBinder binder = (SyncthingServiceBinder) service;
|
||||
mSyncthingService = binder.getService();
|
||||
mSyncthingService.getApi()
|
||||
.registerOnApiAvailableListener(NodeSettingsActivity.this);
|
||||
mSyncthingService.registerOnApiAvailableListener(NodeSettingsActivity.this);
|
||||
}
|
||||
|
||||
public void onServiceDisconnected(ComponentName className) {
|
||||
|
|
|
@ -17,7 +17,7 @@ import java.util.TimerTask;
|
|||
* Displays a list of all existing nodes.
|
||||
*/
|
||||
public class NodesFragment extends LoadingListFragment implements
|
||||
RestApi.OnApiAvailableListener, ListView.OnItemClickListener {
|
||||
SyncthingService.OnApiAvailableListener, ListView.OnItemClickListener {
|
||||
|
||||
private NodeAdapter mAdapter;
|
||||
|
||||
|
|
|
@ -31,7 +31,7 @@ import java.util.List;
|
|||
*/
|
||||
public class RepoSettingsActivity extends PreferenceActivity
|
||||
implements Preference.OnPreferenceChangeListener, Preference.OnPreferenceClickListener,
|
||||
RestApi.OnApiAvailableListener {
|
||||
SyncthingService.OnApiAvailableListener {
|
||||
|
||||
public static final String ACTION_CREATE = "create";
|
||||
|
||||
|
@ -48,8 +48,7 @@ public class RepoSettingsActivity extends PreferenceActivity
|
|||
public void onServiceConnected(ComponentName className, IBinder service) {
|
||||
SyncthingServiceBinder binder = (SyncthingServiceBinder) service;
|
||||
mSyncthingService = binder.getService();
|
||||
mSyncthingService.getApi()
|
||||
.registerOnApiAvailableListener(RepoSettingsActivity.this);
|
||||
mSyncthingService.registerOnApiAvailableListener(RepoSettingsActivity.this);
|
||||
}
|
||||
|
||||
public void onServiceDisconnected(ComponentName className) {
|
||||
|
|
|
@ -16,7 +16,7 @@ import java.util.TimerTask;
|
|||
* Displays a list of all existing repositories.
|
||||
*/
|
||||
public class ReposFragment extends LoadingListFragment implements
|
||||
RestApi.OnApiAvailableListener, AdapterView.OnItemClickListener {
|
||||
SyncthingService.OnApiAvailableListener, AdapterView.OnItemClickListener {
|
||||
|
||||
private ReposAdapter mAdapter;
|
||||
|
||||
|
|
|
@ -14,6 +14,7 @@ import org.json.JSONArray;
|
|||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.text.DecimalFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
|
@ -115,16 +116,9 @@ public class RestApi implements SyncthingService.OnWebGuiAvailableListener {
|
|||
public String invalid;
|
||||
}
|
||||
|
||||
public interface OnApiAvailableListener {
|
||||
public void onApiAvailable();
|
||||
}
|
||||
|
||||
private final LinkedList<OnApiAvailableListener> mOnApiAvailableListeners =
|
||||
new LinkedList<OnApiAvailableListener>();
|
||||
|
||||
private static final int NOTIFICATION_RESTART = 2;
|
||||
|
||||
private final Context mContext;
|
||||
private final SyncthingService mSyncthingService;
|
||||
|
||||
private String mVersion;
|
||||
|
||||
|
@ -149,12 +143,12 @@ public class RestApi implements SyncthingService.OnWebGuiAvailableListener {
|
|||
*/
|
||||
private long mPreviousConnectionTime = 0;
|
||||
|
||||
public RestApi(Context context, String url, String apiKey) {
|
||||
mContext = context;
|
||||
public RestApi(SyncthingService syncthingService, String url, String apiKey) {
|
||||
mSyncthingService = syncthingService;
|
||||
mUrl = url;
|
||||
mApiKey = apiKey;
|
||||
mNotificationManager = (NotificationManager)
|
||||
mContext.getSystemService(Context.NOTIFICATION_SERVICE);
|
||||
mSyncthingService.getSystemService(Context.NOTIFICATION_SERVICE);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -164,6 +158,13 @@ public class RestApi implements SyncthingService.OnWebGuiAvailableListener {
|
|||
return mUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the API key needed to access the Rest API.
|
||||
*/
|
||||
public String getApiKey() {
|
||||
return mApiKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* Number of previous calls to {@link #tryIsAvailable()}.
|
||||
*/
|
||||
|
@ -208,18 +209,14 @@ public class RestApi implements SyncthingService.OnWebGuiAvailableListener {
|
|||
});
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Increments mAvailableCount by one, and, if it reached TOTAL_STARTUP_CALLS, notifies
|
||||
* all registered {@link OnApiAvailableListener} listeners.
|
||||
* Increments mAvailableCount by one, and, if it reached TOTAL_STARTUP_CALLS,
|
||||
* calls {@link SyncthingService#onApiAvailable()}.
|
||||
*/
|
||||
private void tryIsAvailable() {
|
||||
int value = mAvailableCount.incrementAndGet();
|
||||
if (value == TOTAL_STARTUP_CALLS) {
|
||||
for (OnApiAvailableListener listener : mOnApiAvailableListeners) {
|
||||
listener.onApiAvailable();
|
||||
}
|
||||
mOnApiAvailableListeners.clear();
|
||||
mSyncthingService.onApiAvailable();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -238,13 +235,6 @@ public class RestApi implements SyncthingService.OnWebGuiAvailableListener {
|
|||
new PostTask().execute(mUrl, PostTask.URI_SHUTDOWN, mApiKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* Restarts the syncthing binary.
|
||||
*/
|
||||
public void restart() {
|
||||
new PostTask().execute(mUrl, PostTask.URI_RESTART, mApiKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a value from config,
|
||||
*
|
||||
|
@ -308,13 +298,13 @@ public class RestApi implements SyncthingService.OnWebGuiAvailableListener {
|
|||
private void configUpdated() {
|
||||
new PostTask().execute(mUrl, PostTask.URI_CONFIG, mApiKey, mConfig.toString());
|
||||
|
||||
Intent i = new Intent(mContext, SyncthingService.class)
|
||||
Intent i = new Intent(mSyncthingService, SyncthingService.class)
|
||||
.setAction(SyncthingService.ACTION_RESTART);
|
||||
PendingIntent pi = PendingIntent.getService(mContext, 0, i, 0);
|
||||
PendingIntent pi = PendingIntent.getService(mSyncthingService, 0, i, 0);
|
||||
|
||||
Notification n = new NotificationCompat.Builder(mContext)
|
||||
.setContentTitle(mContext.getString(R.string.restart_notif_title))
|
||||
.setContentText(mContext.getString(R.string.restart_notif_text))
|
||||
Notification n = new NotificationCompat.Builder(mSyncthingService)
|
||||
.setContentTitle(mSyncthingService.getString(R.string.restart_notif_title))
|
||||
.setContentText(mSyncthingService.getString(R.string.restart_notif_text))
|
||||
.setSmallIcon(R.drawable.ic_launcher)
|
||||
.setContentIntent(pi)
|
||||
.build();
|
||||
|
@ -433,21 +423,6 @@ public class RestApi implements SyncthingService.OnWebGuiAvailableListener {
|
|||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 registerOnApiAvailableListener(OnApiAvailableListener listener) {
|
||||
if (mConfig != null) {
|
||||
listener.onApiAvailable();
|
||||
}
|
||||
else {
|
||||
mOnApiAvailableListeners.addLast(listener);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a number of bytes to a human readable file size (eg 3.5 GB).
|
||||
*/
|
||||
|
@ -715,4 +690,8 @@ public class RestApi implements SyncthingService.OnWebGuiAvailableListener {
|
|||
return newArray;
|
||||
}
|
||||
|
||||
public boolean isApiAvailable() {
|
||||
return mAvailableCount.get() == TOTAL_STARTUP_CALLS;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -6,7 +6,6 @@ import android.app.Service;
|
|||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Handler;
|
||||
import android.os.IBinder;
|
||||
import android.os.Looper;
|
||||
import android.support.v4.app.NotificationCompat;
|
||||
|
@ -15,6 +14,7 @@ import android.util.Pair;
|
|||
|
||||
import com.nutomic.syncthingandroid.R;
|
||||
import com.nutomic.syncthingandroid.gui.MainActivity;
|
||||
import com.nutomic.syncthingandroid.util.ConfigXml;
|
||||
|
||||
import org.apache.http.HttpResponse;
|
||||
import org.apache.http.HttpStatus;
|
||||
|
@ -22,10 +22,6 @@ import org.apache.http.client.HttpClient;
|
|||
import org.apache.http.client.methods.HttpHead;
|
||||
import org.apache.http.conn.HttpHostConnectException;
|
||||
import org.apache.http.impl.client.DefaultHttpClient;
|
||||
import org.w3c.dom.Document;
|
||||
import org.w3c.dom.Element;
|
||||
import org.w3c.dom.NodeList;
|
||||
import org.xml.sax.SAXException;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.DataOutputStream;
|
||||
|
@ -35,20 +31,10 @@ import java.io.FilenameFilter;
|
|||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.LinkedList;
|
||||
import java.util.Random;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
import javax.xml.parsers.DocumentBuilder;
|
||||
import javax.xml.parsers.DocumentBuilderFactory;
|
||||
import javax.xml.parsers.ParserConfigurationException;
|
||||
import javax.xml.transform.OutputKeys;
|
||||
import javax.xml.transform.Transformer;
|
||||
import javax.xml.transform.TransformerException;
|
||||
import javax.xml.transform.TransformerFactory;
|
||||
import javax.xml.transform.dom.DOMSource;
|
||||
import javax.xml.transform.stream.StreamResult;
|
||||
|
||||
/**
|
||||
* Holds the native syncthing instance and provides an API to access it.
|
||||
*/
|
||||
|
@ -114,10 +100,27 @@ public class SyncthingService extends Service {
|
|||
|
||||
private boolean mIsWebGuiAvailable = false;
|
||||
|
||||
public interface OnApiAvailableListener {
|
||||
public void onApiAvailable();
|
||||
}
|
||||
|
||||
private final LinkedList<WeakReference<OnApiAvailableListener>> mOnApiAvailableListeners =
|
||||
new LinkedList<WeakReference<OnApiAvailableListener>>();
|
||||
|
||||
@Override
|
||||
public int onStartCommand(Intent intent, int flags, int startId) {
|
||||
if (intent != null && ACTION_RESTART.equals(intent.getAction())) {
|
||||
mApi.restart();
|
||||
mIsWebGuiAvailable = false;
|
||||
new PostTask() {
|
||||
@Override
|
||||
protected void onPostExecute(Void aVoid) {
|
||||
ConfigXml config = new ConfigXml(getConfigFile());
|
||||
mApi = new RestApi(SyncthingService.this,
|
||||
config.getWebGuiUrl(), config.getApiKey());
|
||||
registerOnWebGuiAvailableListener(mApi);
|
||||
new PollWebGuiAvailableTask().execute();
|
||||
}
|
||||
}.execute(mApi.getUrl(), PostTask.URI_RESTART, mApi.getApiKey());
|
||||
}
|
||||
return START_STICKY;
|
||||
}
|
||||
|
@ -325,30 +328,9 @@ public class SyncthingService extends Service {
|
|||
copyDefaultConfig();
|
||||
}
|
||||
moveConfigFiles();
|
||||
updateConfig();
|
||||
|
||||
String syncthingUrl = null;
|
||||
String apiKey = null;
|
||||
try {
|
||||
DocumentBuilder db = DocumentBuilderFactory.newInstance().newDocumentBuilder();
|
||||
Document d = db.parse(getConfigFile());
|
||||
Element gui = (Element) d.getDocumentElement()
|
||||
.getElementsByTagName("gui").item(0);
|
||||
syncthingUrl = gui.getElementsByTagName("address").item(0).getTextContent();
|
||||
apiKey = gui.getElementsByTagName("apikey").item(0).getTextContent();
|
||||
}
|
||||
catch (SAXException e) {
|
||||
throw new RuntimeException("Failed to read gui url, aborting", e);
|
||||
}
|
||||
catch (ParserConfigurationException e) {
|
||||
throw new RuntimeException("Failed to read gui url, aborting", e);
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new RuntimeException("Failed to read gui url, aborting", e);
|
||||
}
|
||||
finally {
|
||||
return new Pair<String, String>("http://" + syncthingUrl, apiKey);
|
||||
}
|
||||
ConfigXml config = new ConfigXml(getConfigFile());
|
||||
config.update();
|
||||
return new Pair<String, String>(config.getWebGuiUrl(), config.getApiKey());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -403,85 +385,6 @@ public class SyncthingService extends Service {
|
|||
return new File(getFilesDir(), CONFIG_FILE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the config file.
|
||||
*
|
||||
* Coming from 0.2.0 and earlier, globalAnnounceServer value "announce.syncthing.net:22025" is
|
||||
* replaced with "194.126.249.5:22025" (as domain resolve is broken).
|
||||
*
|
||||
* Coming from 0.3.0 and earlier, the ignorePerms flag is set to true on every repository.
|
||||
*/
|
||||
private void updateConfig() {
|
||||
try {
|
||||
Log.i(TAG, "Checking for needed config updates");
|
||||
boolean changed = false;
|
||||
DocumentBuilder db = DocumentBuilderFactory.newInstance().newDocumentBuilder();
|
||||
Document doc = db.parse(getConfigFile());
|
||||
Element options = (Element) doc.getDocumentElement()
|
||||
.getElementsByTagName("options").item(0);
|
||||
Element gui = (Element) doc.getDocumentElement()
|
||||
.getElementsByTagName("gui").item(0);
|
||||
|
||||
// Create an API key if it does not exist.
|
||||
if (gui.getElementsByTagName("apikey").getLength() == 0) {
|
||||
Log.i(TAG, "Initializing API key with random string");
|
||||
char[] chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789".toCharArray();
|
||||
StringBuilder sb = new StringBuilder();
|
||||
Random random = new Random();
|
||||
for (int i = 0; i < 20; i++) {
|
||||
sb.append(chars[random.nextInt(chars.length)]);
|
||||
}
|
||||
Element apiKey = doc.createElement("apikey");
|
||||
apiKey.setTextContent(sb.toString());
|
||||
gui.appendChild(apiKey);
|
||||
changed = true;
|
||||
}
|
||||
|
||||
// Hardcode default globalAnnounceServer ip.
|
||||
Element globalAnnounceServer = (Element)
|
||||
options.getElementsByTagName("globalAnnounceServer").item(0);
|
||||
if (globalAnnounceServer.getTextContent().equals("announce.syncthing.net:22025")) {
|
||||
Log.i(TAG, "Replacing globalAnnounceServer host with ip");
|
||||
globalAnnounceServer.setTextContent("194.126.249.5:22025");
|
||||
changed = true;
|
||||
}
|
||||
|
||||
// Set ignorePerms attribute.
|
||||
NodeList repos = doc.getDocumentElement().getElementsByTagName("repository");
|
||||
for (int i = 0; i < repos.getLength(); i++) {
|
||||
Element r = (Element) repos.item(i);
|
||||
if (!r.hasAttribute("ignorePerms") ||
|
||||
!Boolean.parseBoolean(r.getAttribute("ignorePerms"))) {
|
||||
Log.i(TAG, "Set 'ignorePerms' on repository " + r.getAttribute("id"));
|
||||
r.setAttribute("ignorePerms", Boolean.toString(true));
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Write the changes back to file.
|
||||
if (changed) {
|
||||
Log.i(TAG, "Writing updated config back to file");
|
||||
TransformerFactory transformerFactory = TransformerFactory.newInstance();
|
||||
Transformer transformer = transformerFactory.newTransformer();
|
||||
DOMSource domSource = new DOMSource(doc);
|
||||
StreamResult streamResult = new StreamResult(getConfigFile());
|
||||
transformer.transform(domSource, streamResult);
|
||||
}
|
||||
}
|
||||
catch (ParserConfigurationException e) {
|
||||
Log.w(TAG, "Failed to parse config", e);
|
||||
}
|
||||
catch (IOException e) {
|
||||
Log.w(TAG, "Failed to parse config", e);
|
||||
}
|
||||
catch (SAXException e) {
|
||||
Log.w(TAG, "Failed to parse config", e);
|
||||
}
|
||||
catch (TransformerException e) {
|
||||
Log.w(TAG, "Failed to save updated config", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if this service has not been started before (ie config.xml does not exist).
|
||||
*
|
||||
|
@ -525,4 +428,37 @@ public class SyncthingService extends Service {
|
|||
return mApi;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a listener for the syncthing API becoming available..
|
||||
*
|
||||
* If the API is already available, listener will be called immediately.
|
||||
*
|
||||
* Listeners are kept around (as weak reference) and called again after any configuration
|
||||
* changes to allow a data refresh.
|
||||
*/
|
||||
public void registerOnApiAvailableListener(OnApiAvailableListener listener) {
|
||||
if (mApi.isApiAvailable()) {
|
||||
listener.onApiAvailable();
|
||||
}
|
||||
else {
|
||||
mOnApiAvailableListeners.addLast(new WeakReference<OnApiAvailableListener>(listener));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by {@link RestApi} once it is fully initialized.
|
||||
*
|
||||
* Must not be called from anywhere else.
|
||||
*/
|
||||
public void onApiAvailable() {
|
||||
for (WeakReference<OnApiAvailableListener> listener : mOnApiAvailableListeners) {
|
||||
if (listener.get() != null) {
|
||||
listener.get().onApiAvailable();
|
||||
}
|
||||
else {
|
||||
mOnApiAvailableListeners.remove(listener);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
134
src/main/java/com/nutomic/syncthingandroid/util/ConfigXml.java
Normal file
134
src/main/java/com/nutomic/syncthingandroid/util/ConfigXml.java
Normal file
|
@ -0,0 +1,134 @@
|
|||
package com.nutomic.syncthingandroid.util;
|
||||
|
||||
import android.util.Log;
|
||||
import android.util.Pair;
|
||||
|
||||
import com.nutomic.syncthingandroid.syncthing.SyncthingService;
|
||||
|
||||
import org.w3c.dom.Document;
|
||||
import org.w3c.dom.Element;
|
||||
import org.w3c.dom.NodeList;
|
||||
import org.xml.sax.SAXException;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.Random;
|
||||
|
||||
import javax.xml.parsers.DocumentBuilder;
|
||||
import javax.xml.parsers.DocumentBuilderFactory;
|
||||
import javax.xml.parsers.ParserConfigurationException;
|
||||
import javax.xml.transform.Transformer;
|
||||
import javax.xml.transform.TransformerException;
|
||||
import javax.xml.transform.TransformerFactory;
|
||||
import javax.xml.transform.dom.DOMSource;
|
||||
import javax.xml.transform.stream.StreamResult;
|
||||
|
||||
/**
|
||||
* Provides direct access to the config.xml file in the file system.
|
||||
*
|
||||
* This class should only be used if the syncthing API is not available (usually during startup).
|
||||
*/
|
||||
public class ConfigXml {
|
||||
|
||||
private static final String TAG = "ConfigXml";
|
||||
|
||||
private File mConfigFile;
|
||||
|
||||
private Document mConfig;
|
||||
|
||||
public ConfigXml(File configFile) {
|
||||
mConfigFile = configFile;
|
||||
try {
|
||||
DocumentBuilder db = DocumentBuilderFactory.newInstance().newDocumentBuilder();
|
||||
mConfig = db.parse(configFile);
|
||||
} catch (SAXException e) {
|
||||
throw new RuntimeException("Failed to parse config file", e);
|
||||
} catch (ParserConfigurationException e) {
|
||||
throw new RuntimeException("Failed to parse config file", e);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("Failed to open config file", e);
|
||||
}
|
||||
}
|
||||
|
||||
public String getWebGuiUrl() {
|
||||
return "http://" + getGuiElement().getElementsByTagName("address").item(0).getTextContent();
|
||||
}
|
||||
|
||||
public String getApiKey() {
|
||||
return getGuiElement().getElementsByTagName("apikey").item(0).getTextContent();
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the config file.
|
||||
*
|
||||
* Coming from 0.2.0 and earlier, globalAnnounceServer value "announce.syncthing.net:22025" is
|
||||
* replaced with "194.126.249.5:22025" (as domain resolve is broken).
|
||||
*
|
||||
* Coming from 0.3.0 and earlier, the ignorePerms flag is set to true on every repository.
|
||||
*/
|
||||
public void update() {
|
||||
try {
|
||||
Log.i(TAG, "Checking for needed config updates");
|
||||
boolean changed = false;
|
||||
Element options = (Element) mConfig.getDocumentElement()
|
||||
.getElementsByTagName("options").item(0);
|
||||
Element gui = (Element) mConfig.getDocumentElement()
|
||||
.getElementsByTagName("gui").item(0);
|
||||
|
||||
// Create an API key if it does not exist.
|
||||
if (gui.getElementsByTagName("apikey").getLength() == 0) {
|
||||
Log.i(TAG, "Initializing API key with random string");
|
||||
char[] chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789".toCharArray();
|
||||
StringBuilder sb = new StringBuilder();
|
||||
Random random = new Random();
|
||||
for (int i = 0; i < 20; i++) {
|
||||
sb.append(chars[random.nextInt(chars.length)]);
|
||||
}
|
||||
Element apiKey = mConfig.createElement("apikey");
|
||||
apiKey.setTextContent(sb.toString());
|
||||
gui.appendChild(apiKey);
|
||||
changed = true;
|
||||
}
|
||||
|
||||
// Hardcode default globalAnnounceServer ip.
|
||||
Element globalAnnounceServer = (Element)
|
||||
options.getElementsByTagName("globalAnnounceServer").item(0);
|
||||
if (globalAnnounceServer.getTextContent().equals("announce.syncthing.net:22025")) {
|
||||
Log.i(TAG, "Replacing globalAnnounceServer host with ip");
|
||||
globalAnnounceServer.setTextContent("194.126.249.5:22025");
|
||||
changed = true;
|
||||
}
|
||||
|
||||
// Set ignorePerms attribute.
|
||||
NodeList repos = mConfig.getDocumentElement().getElementsByTagName("repository");
|
||||
for (int i = 0; i < repos.getLength(); i++) {
|
||||
Element r = (Element) repos.item(i);
|
||||
if (!r.hasAttribute("ignorePerms") ||
|
||||
!Boolean.parseBoolean(r.getAttribute("ignorePerms"))) {
|
||||
Log.i(TAG, "Set 'ignorePerms' on repository " + r.getAttribute("id"));
|
||||
r.setAttribute("ignorePerms", Boolean.toString(true));
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Write the changes back to file.
|
||||
if (changed) {
|
||||
Log.i(TAG, "Writing updated config back to file");
|
||||
TransformerFactory transformerFactory = TransformerFactory.newInstance();
|
||||
Transformer transformer = transformerFactory.newTransformer();
|
||||
DOMSource domSource = new DOMSource(mConfig);
|
||||
StreamResult streamResult = new StreamResult(mConfigFile);
|
||||
transformer.transform(domSource, streamResult);
|
||||
}
|
||||
}
|
||||
catch (TransformerException e) {
|
||||
Log.w(TAG, "Failed to save updated config", e);
|
||||
}
|
||||
}
|
||||
|
||||
private Element getGuiElement() {
|
||||
return (Element) mConfig.getDocumentElement()
|
||||
.getElementsByTagName("gui").item(0);
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in a new issue