mirror of
https://github.com/syncthing/syncthing-android.git
synced 2024-12-23 11:21:29 +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.
|
* {@link android.support.v4.app.ListFragment} that shows a configurable loading text.
|
||||||
*/
|
*/
|
||||||
public abstract class LoadingListFragment extends Fragment implements RestApi.OnApiAvailableListener, AdapterView.OnItemClickListener {
|
public abstract class LoadingListFragment extends Fragment implements
|
||||||
|
SyncthingService.OnApiAvailableListener, AdapterView.OnItemClickListener {
|
||||||
private boolean mInitialized = false;
|
|
||||||
|
|
||||||
private ListFragment mListFragment;
|
private ListFragment mListFragment;
|
||||||
|
|
||||||
|
@ -109,11 +108,10 @@ public abstract class LoadingListFragment extends Fragment implements RestApi.On
|
||||||
@Override
|
@Override
|
||||||
public void onApiAvailable() {
|
public void onApiAvailable() {
|
||||||
MainActivity activity = (MainActivity) getActivity();
|
MainActivity activity = (MainActivity) getActivity();
|
||||||
if (!mInitialized && getActivity() != null &&
|
if (getActivity() != null &&
|
||||||
activity.getApi() != null && mListFragment != null) {
|
activity.getApi() != null && mListFragment != null) {
|
||||||
onInitAdapter(activity);
|
onInitAdapter(activity);
|
||||||
getListView().setOnItemClickListener(this);
|
getListView().setOnItemClickListener(this);
|
||||||
mInitialized = true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -26,7 +26,7 @@ import java.util.TimerTask;
|
||||||
*/
|
*/
|
||||||
public class LocalNodeInfoFragment extends Fragment
|
public class LocalNodeInfoFragment extends Fragment
|
||||||
implements RestApi.OnReceiveSystemInfoListener, RestApi.OnReceiveConnectionsListener,
|
implements RestApi.OnReceiveSystemInfoListener, RestApi.OnReceiveConnectionsListener,
|
||||||
RestApi.OnApiAvailableListener {
|
SyncthingService.OnApiAvailableListener {
|
||||||
|
|
||||||
private TextView mNodeId;
|
private TextView mNodeId;
|
||||||
|
|
||||||
|
|
|
@ -54,9 +54,9 @@ public class MainActivity extends ActionBarActivity
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void onWebGuiAvailable() {
|
public void onWebGuiAvailable() {
|
||||||
mSyncthingService.getApi().registerOnApiAvailableListener(mRepositoriesFragment);
|
mSyncthingService.registerOnApiAvailableListener(mRepositoriesFragment);
|
||||||
mSyncthingService.getApi().registerOnApiAvailableListener(mNodesFragment);
|
mSyncthingService.registerOnApiAvailableListener(mNodesFragment);
|
||||||
mSyncthingService.getApi().registerOnApiAvailableListener(mLocalNodeInfoFragment);
|
mSyncthingService.registerOnApiAvailableListener(mLocalNodeInfoFragment);
|
||||||
mDrawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED);
|
mDrawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED);
|
||||||
mDrawerLayout.setDrawerListener(mDrawerToggle);
|
mDrawerLayout.setDrawerListener(mDrawerToggle);
|
||||||
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
|
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
|
||||||
|
@ -220,14 +220,14 @@ public class MainActivity extends ActionBarActivity
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (item.getItemId()) {
|
switch (item.getItemId()) {
|
||||||
case R.id.add_node:
|
case R.id.add_repository:
|
||||||
Intent intent = new Intent(this, NodeSettingsActivity.class);
|
Intent intent = new Intent(this, RepoSettingsActivity.class);
|
||||||
intent.setAction(NodeSettingsActivity.ACTION_CREATE);
|
intent.setAction(RepoSettingsActivity.ACTION_CREATE);
|
||||||
startActivity(intent);
|
startActivity(intent);
|
||||||
return true;
|
return true;
|
||||||
case R.id.add_repository:
|
case R.id.add_node:
|
||||||
intent = new Intent(this, RepoSettingsActivity.class);
|
intent = new Intent(this, NodeSettingsActivity.class);
|
||||||
intent.setAction(RepoSettingsActivity.ACTION_CREATE);
|
intent.setAction(NodeSettingsActivity.ACTION_CREATE);
|
||||||
startActivity(intent);
|
startActivity(intent);
|
||||||
return true;
|
return true;
|
||||||
case R.id.web_gui:
|
case R.id.web_gui:
|
||||||
|
|
|
@ -28,7 +28,7 @@ import java.util.Map;
|
||||||
*/
|
*/
|
||||||
public class NodeSettingsActivity extends PreferenceActivity implements
|
public class NodeSettingsActivity extends PreferenceActivity implements
|
||||||
Preference.OnPreferenceChangeListener, Preference.OnPreferenceClickListener,
|
Preference.OnPreferenceChangeListener, Preference.OnPreferenceClickListener,
|
||||||
RestApi.OnReceiveConnectionsListener, RestApi.OnApiAvailableListener {
|
RestApi.OnReceiveConnectionsListener, SyncthingService.OnApiAvailableListener {
|
||||||
|
|
||||||
public static final String ACTION_CREATE = "create";
|
public static final String ACTION_CREATE = "create";
|
||||||
|
|
||||||
|
@ -43,8 +43,7 @@ public class NodeSettingsActivity extends PreferenceActivity implements
|
||||||
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.getApi()
|
mSyncthingService.registerOnApiAvailableListener(NodeSettingsActivity.this);
|
||||||
.registerOnApiAvailableListener(NodeSettingsActivity.this);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onServiceDisconnected(ComponentName className) {
|
public void onServiceDisconnected(ComponentName className) {
|
||||||
|
|
|
@ -17,7 +17,7 @@ import java.util.TimerTask;
|
||||||
* Displays a list of all existing nodes.
|
* Displays a list of all existing nodes.
|
||||||
*/
|
*/
|
||||||
public class NodesFragment extends LoadingListFragment implements
|
public class NodesFragment extends LoadingListFragment implements
|
||||||
RestApi.OnApiAvailableListener, ListView.OnItemClickListener {
|
SyncthingService.OnApiAvailableListener, ListView.OnItemClickListener {
|
||||||
|
|
||||||
private NodeAdapter mAdapter;
|
private NodeAdapter mAdapter;
|
||||||
|
|
||||||
|
|
|
@ -31,7 +31,7 @@ import java.util.List;
|
||||||
*/
|
*/
|
||||||
public class RepoSettingsActivity extends PreferenceActivity
|
public class RepoSettingsActivity extends PreferenceActivity
|
||||||
implements Preference.OnPreferenceChangeListener, Preference.OnPreferenceClickListener,
|
implements Preference.OnPreferenceChangeListener, Preference.OnPreferenceClickListener,
|
||||||
RestApi.OnApiAvailableListener {
|
SyncthingService.OnApiAvailableListener {
|
||||||
|
|
||||||
public static final String ACTION_CREATE = "create";
|
public static final String ACTION_CREATE = "create";
|
||||||
|
|
||||||
|
@ -48,8 +48,7 @@ public class RepoSettingsActivity extends PreferenceActivity
|
||||||
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.getApi()
|
mSyncthingService.registerOnApiAvailableListener(RepoSettingsActivity.this);
|
||||||
.registerOnApiAvailableListener(RepoSettingsActivity.this);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onServiceDisconnected(ComponentName className) {
|
public void onServiceDisconnected(ComponentName className) {
|
||||||
|
|
|
@ -16,7 +16,7 @@ import java.util.TimerTask;
|
||||||
* Displays a list of all existing repositories.
|
* Displays a list of all existing repositories.
|
||||||
*/
|
*/
|
||||||
public class ReposFragment extends LoadingListFragment implements
|
public class ReposFragment extends LoadingListFragment implements
|
||||||
RestApi.OnApiAvailableListener, AdapterView.OnItemClickListener {
|
SyncthingService.OnApiAvailableListener, AdapterView.OnItemClickListener {
|
||||||
|
|
||||||
private ReposAdapter mAdapter;
|
private ReposAdapter mAdapter;
|
||||||
|
|
||||||
|
|
|
@ -14,6 +14,7 @@ import org.json.JSONArray;
|
||||||
import org.json.JSONException;
|
import org.json.JSONException;
|
||||||
import org.json.JSONObject;
|
import org.json.JSONObject;
|
||||||
|
|
||||||
|
import java.lang.ref.WeakReference;
|
||||||
import java.text.DecimalFormat;
|
import java.text.DecimalFormat;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
@ -115,16 +116,9 @@ public class RestApi implements SyncthingService.OnWebGuiAvailableListener {
|
||||||
public String invalid;
|
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 static final int NOTIFICATION_RESTART = 2;
|
||||||
|
|
||||||
private final Context mContext;
|
private final SyncthingService mSyncthingService;
|
||||||
|
|
||||||
private String mVersion;
|
private String mVersion;
|
||||||
|
|
||||||
|
@ -149,12 +143,12 @@ public class RestApi implements SyncthingService.OnWebGuiAvailableListener {
|
||||||
*/
|
*/
|
||||||
private long mPreviousConnectionTime = 0;
|
private long mPreviousConnectionTime = 0;
|
||||||
|
|
||||||
public RestApi(Context context, String url, String apiKey) {
|
public RestApi(SyncthingService syncthingService, String url, String apiKey) {
|
||||||
mContext = context;
|
mSyncthingService = syncthingService;
|
||||||
mUrl = url;
|
mUrl = url;
|
||||||
mApiKey = apiKey;
|
mApiKey = apiKey;
|
||||||
mNotificationManager = (NotificationManager)
|
mNotificationManager = (NotificationManager)
|
||||||
mContext.getSystemService(Context.NOTIFICATION_SERVICE);
|
mSyncthingService.getSystemService(Context.NOTIFICATION_SERVICE);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -164,6 +158,13 @@ public class RestApi implements SyncthingService.OnWebGuiAvailableListener {
|
||||||
return mUrl;
|
return mUrl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the API key needed to access the Rest API.
|
||||||
|
*/
|
||||||
|
public String getApiKey() {
|
||||||
|
return mApiKey;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Number of previous calls to {@link #tryIsAvailable()}.
|
* 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
|
* Increments mAvailableCount by one, and, if it reached TOTAL_STARTUP_CALLS,
|
||||||
* all registered {@link OnApiAvailableListener} listeners.
|
* calls {@link SyncthingService#onApiAvailable()}.
|
||||||
*/
|
*/
|
||||||
private void tryIsAvailable() {
|
private void tryIsAvailable() {
|
||||||
int value = mAvailableCount.incrementAndGet();
|
int value = mAvailableCount.incrementAndGet();
|
||||||
if (value == TOTAL_STARTUP_CALLS) {
|
if (value == TOTAL_STARTUP_CALLS) {
|
||||||
for (OnApiAvailableListener listener : mOnApiAvailableListeners) {
|
mSyncthingService.onApiAvailable();
|
||||||
listener.onApiAvailable();
|
|
||||||
}
|
|
||||||
mOnApiAvailableListeners.clear();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -238,13 +235,6 @@ public class RestApi implements SyncthingService.OnWebGuiAvailableListener {
|
||||||
new PostTask().execute(mUrl, PostTask.URI_SHUTDOWN, mApiKey);
|
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,
|
* Gets a value from config,
|
||||||
*
|
*
|
||||||
|
@ -308,13 +298,13 @@ public class RestApi implements SyncthingService.OnWebGuiAvailableListener {
|
||||||
private void configUpdated() {
|
private void configUpdated() {
|
||||||
new PostTask().execute(mUrl, PostTask.URI_CONFIG, mApiKey, mConfig.toString());
|
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);
|
.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)
|
Notification n = new NotificationCompat.Builder(mSyncthingService)
|
||||||
.setContentTitle(mContext.getString(R.string.restart_notif_title))
|
.setContentTitle(mSyncthingService.getString(R.string.restart_notif_title))
|
||||||
.setContentText(mContext.getString(R.string.restart_notif_text))
|
.setContentText(mSyncthingService.getString(R.string.restart_notif_text))
|
||||||
.setSmallIcon(R.drawable.ic_launcher)
|
.setSmallIcon(R.drawable.ic_launcher)
|
||||||
.setContentIntent(pi)
|
.setContentIntent(pi)
|
||||||
.build();
|
.build();
|
||||||
|
@ -433,21 +423,6 @@ public class RestApi implements SyncthingService.OnWebGuiAvailableListener {
|
||||||
return ret;
|
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).
|
* 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;
|
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.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.os.AsyncTask;
|
import android.os.AsyncTask;
|
||||||
import android.os.Handler;
|
|
||||||
import android.os.IBinder;
|
import android.os.IBinder;
|
||||||
import android.os.Looper;
|
import android.os.Looper;
|
||||||
import android.support.v4.app.NotificationCompat;
|
import android.support.v4.app.NotificationCompat;
|
||||||
|
@ -15,6 +14,7 @@ import android.util.Pair;
|
||||||
|
|
||||||
import com.nutomic.syncthingandroid.R;
|
import com.nutomic.syncthingandroid.R;
|
||||||
import com.nutomic.syncthingandroid.gui.MainActivity;
|
import com.nutomic.syncthingandroid.gui.MainActivity;
|
||||||
|
import com.nutomic.syncthingandroid.util.ConfigXml;
|
||||||
|
|
||||||
import org.apache.http.HttpResponse;
|
import org.apache.http.HttpResponse;
|
||||||
import org.apache.http.HttpStatus;
|
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.client.methods.HttpHead;
|
||||||
import org.apache.http.conn.HttpHostConnectException;
|
import org.apache.http.conn.HttpHostConnectException;
|
||||||
import org.apache.http.impl.client.DefaultHttpClient;
|
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.BufferedReader;
|
||||||
import java.io.DataOutputStream;
|
import java.io.DataOutputStream;
|
||||||
|
@ -35,20 +31,10 @@ import java.io.FilenameFilter;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.InputStreamReader;
|
import java.io.InputStreamReader;
|
||||||
|
import java.lang.ref.WeakReference;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.Random;
|
|
||||||
import java.util.concurrent.locks.ReentrantLock;
|
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.
|
* 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;
|
private boolean mIsWebGuiAvailable = false;
|
||||||
|
|
||||||
|
public interface OnApiAvailableListener {
|
||||||
|
public void onApiAvailable();
|
||||||
|
}
|
||||||
|
|
||||||
|
private final LinkedList<WeakReference<OnApiAvailableListener>> mOnApiAvailableListeners =
|
||||||
|
new LinkedList<WeakReference<OnApiAvailableListener>>();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int onStartCommand(Intent intent, int flags, int startId) {
|
public int onStartCommand(Intent intent, int flags, int startId) {
|
||||||
if (intent != null && ACTION_RESTART.equals(intent.getAction())) {
|
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;
|
return START_STICKY;
|
||||||
}
|
}
|
||||||
|
@ -325,30 +328,9 @@ public class SyncthingService extends Service {
|
||||||
copyDefaultConfig();
|
copyDefaultConfig();
|
||||||
}
|
}
|
||||||
moveConfigFiles();
|
moveConfigFiles();
|
||||||
updateConfig();
|
ConfigXml config = new ConfigXml(getConfigFile());
|
||||||
|
config.update();
|
||||||
String syncthingUrl = null;
|
return new Pair<String, String>(config.getWebGuiUrl(), config.getApiKey());
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -403,85 +385,6 @@ public class SyncthingService extends Service {
|
||||||
return new File(getFilesDir(), CONFIG_FILE);
|
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).
|
* 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;
|
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