Add options to stop sync when not charging or not on wifi (fixes #15).
This commit is contained in:
parent
733940cbdf
commit
9c4a85b85d
|
@ -13,6 +13,7 @@
|
||||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||||
<uses-permission android:name="android.permission.INTERNET" />
|
<uses-permission android:name="android.permission.INTERNET" />
|
||||||
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
|
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
|
||||||
|
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
|
||||||
|
|
||||||
<application
|
<application
|
||||||
android:allowBackup="false"
|
android:allowBackup="false"
|
||||||
|
@ -57,6 +58,17 @@
|
||||||
android:name="android.support.UI_OPTIONS"
|
android:name="android.support.UI_OPTIONS"
|
||||||
android:value="splitActionBarWhenNarrow" />
|
android:value="splitActionBarWhenNarrow" />
|
||||||
</activity>
|
</activity>
|
||||||
|
<receiver android:name=".syncthing.NetworkReceiver" >
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.net.conn.CONNECTIVITY_CHANGE"/>
|
||||||
|
</intent-filter>
|
||||||
|
</receiver>
|
||||||
|
<receiver android:name=".syncthing.BatteryReceiver" >
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.ACTION_POWER_CONNECTED"/>
|
||||||
|
<action android:name="android.intent.action.ACTION_POWER_DISCONNECTED"/>
|
||||||
|
</intent-filter>
|
||||||
|
</receiver>
|
||||||
</application>
|
</application>
|
||||||
|
|
||||||
</manifest>
|
</manifest>
|
||||||
|
|
|
@ -203,9 +203,10 @@ public class FolderPickerActivity extends ActionBarActivity
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onApiChange(boolean isAvailable) {
|
public void onApiChange(SyncthingService.State currentState) {
|
||||||
if (!isAvailable) {
|
if (currentState != SyncthingService.State.ACTIVE) {
|
||||||
setResult(Activity.RESULT_CANCELED);
|
setResult(Activity.RESULT_CANCELED);
|
||||||
|
SyncthingService.showDisabledDialog(this);
|
||||||
finish();
|
finish();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -111,8 +111,8 @@ public class LocalNodeInfoFragment extends Fragment
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onApiChange(boolean isAvailable) {
|
public void onApiChange(SyncthingService.State currentState) {
|
||||||
if (!isAvailable)
|
if (currentState != SyncthingService.State.ACTIVE)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
updateGui();
|
updateGui();
|
||||||
|
|
|
@ -23,6 +23,7 @@ import android.support.v7.app.ActionBar;
|
||||||
import android.support.v7.app.ActionBar.Tab;
|
import android.support.v7.app.ActionBar.Tab;
|
||||||
import android.support.v7.app.ActionBar.TabListener;
|
import android.support.v7.app.ActionBar.TabListener;
|
||||||
import android.support.v7.app.ActionBarActivity;
|
import android.support.v7.app.ActionBarActivity;
|
||||||
|
import android.util.Log;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.Menu;
|
import android.view.Menu;
|
||||||
import android.view.MenuItem;
|
import android.view.MenuItem;
|
||||||
|
@ -66,8 +67,15 @@ public class MainActivity extends ActionBarActivity
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
@SuppressLint("InflateParams")
|
@SuppressLint("InflateParams")
|
||||||
public void onApiChange(boolean isAvailable) {
|
public void onApiChange(SyncthingService.State currentState) {
|
||||||
if (!isAvailable) {
|
if (currentState != SyncthingService.State.ACTIVE && !isFinishing()) {
|
||||||
|
if (currentState == SyncthingService.State.DISABLED) {
|
||||||
|
if (mLoadingDialog != null) {
|
||||||
|
mLoadingDialog.dismiss();
|
||||||
|
}
|
||||||
|
SyncthingService.showDisabledDialog(this);
|
||||||
|
}
|
||||||
|
else if (mLoadingDialog == null) {
|
||||||
final SharedPreferences prefs =
|
final SharedPreferences prefs =
|
||||||
PreferenceManager.getDefaultSharedPreferences(MainActivity.this);
|
PreferenceManager.getDefaultSharedPreferences(MainActivity.this);
|
||||||
|
|
||||||
|
@ -96,7 +104,7 @@ public class MainActivity extends ActionBarActivity
|
||||||
})
|
})
|
||||||
.show();
|
.show();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -75,6 +75,7 @@ public class NodeSettingsActivity extends PreferenceActivity implements
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@SuppressLint("AppCompatMethod")
|
@SuppressLint("AppCompatMethod")
|
||||||
|
@TargetApi(11)
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
|
@ -109,8 +110,9 @@ public class NodeSettingsActivity extends PreferenceActivity implements
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onApiChange(boolean isAvailable) {
|
public void onApiChange(SyncthingService.State currentState) {
|
||||||
if (!isAvailable) {
|
if (currentState != SyncthingService.State.ACTIVE) {
|
||||||
|
SyncthingService.showDisabledDialog(this);
|
||||||
finish();
|
finish();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,8 +31,8 @@ public class NodesFragment extends ListFragment implements SyncthingService.OnAp
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onApiChange(boolean isAvailable) {
|
public void onApiChange(SyncthingService.State currentState) {
|
||||||
if (!isAvailable)
|
if (currentState != SyncthingService.State.ACTIVE)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
initAdapter();
|
initAdapter();
|
||||||
|
|
|
@ -118,8 +118,9 @@ public class RepoSettingsActivity extends PreferenceActivity
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onApiChange(boolean isAvailable) {
|
public void onApiChange(SyncthingService.State currentState) {
|
||||||
if (!isAvailable) {
|
if (currentState != SyncthingService.State.ACTIVE) {
|
||||||
|
SyncthingService.showDisabledDialog(this);
|
||||||
finish();
|
finish();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,8 +30,8 @@ public class ReposFragment extends ListFragment implements SyncthingService.OnAp
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onApiChange(boolean isAvailable) {
|
public void onApiChange(SyncthingService.State currentState) {
|
||||||
if (!isAvailable)
|
if (currentState != SyncthingService.State.ACTIVE)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
initAdapter();
|
initAdapter();
|
||||||
|
|
|
@ -32,6 +32,10 @@ public class SettingsActivity extends PreferenceActivity
|
||||||
|
|
||||||
private static final String SYNCTHING_VERSION_KEY = "syncthing_version";
|
private static final String SYNCTHING_VERSION_KEY = "syncthing_version";
|
||||||
|
|
||||||
|
private CheckBoxPreference mStopNotCharging;
|
||||||
|
|
||||||
|
private CheckBoxPreference mStopMobileData;
|
||||||
|
|
||||||
private Preference mVersion;
|
private Preference mVersion;
|
||||||
|
|
||||||
private PreferenceScreen mOptionsScreen;
|
private PreferenceScreen mOptionsScreen;
|
||||||
|
@ -57,14 +61,13 @@ public class SettingsActivity extends PreferenceActivity
|
||||||
};
|
};
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onApiChange(boolean isAvailable) {
|
public void onApiChange(SyncthingService.State currentState) {
|
||||||
if (!isAvailable) {
|
mOptionsScreen.setEnabled(currentState == SyncthingService.State.ACTIVE);
|
||||||
finish();
|
mGuiScreen.setEnabled(currentState == SyncthingService.State.ACTIVE);
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
mVersion.setSummary(mSyncthingService.getApi().getVersion());
|
mVersion.setSummary(mSyncthingService.getApi().getVersion());
|
||||||
|
|
||||||
|
if (currentState == SyncthingService.State.ACTIVE) {
|
||||||
for (int i = 0; i < mOptionsScreen.getPreferenceCount(); i++) {
|
for (int i = 0; i < mOptionsScreen.getPreferenceCount(); i++) {
|
||||||
Preference pref = mOptionsScreen.getPreference(i);
|
Preference pref = mOptionsScreen.getPreference(i);
|
||||||
pref.setOnPreferenceChangeListener(SettingsActivity.this);
|
pref.setOnPreferenceChangeListener(SettingsActivity.this);
|
||||||
|
@ -80,7 +83,7 @@ public class SettingsActivity extends PreferenceActivity
|
||||||
.getValue(RestApi.TYPE_GUI, pref.getKey());
|
.getValue(RestApi.TYPE_GUI, pref.getKey());
|
||||||
applyPreference(pref, value);
|
applyPreference(pref, value);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -119,6 +122,10 @@ public class SettingsActivity extends PreferenceActivity
|
||||||
|
|
||||||
addPreferencesFromResource(R.xml.app_settings);
|
addPreferencesFromResource(R.xml.app_settings);
|
||||||
PreferenceScreen screen = getPreferenceScreen();
|
PreferenceScreen screen = getPreferenceScreen();
|
||||||
|
mStopNotCharging = (CheckBoxPreference) findPreference("stop_sync_on_mobile_data");
|
||||||
|
mStopNotCharging.setOnPreferenceChangeListener(this);
|
||||||
|
mStopMobileData = (CheckBoxPreference) findPreference("stop_sync_while_not_charging");
|
||||||
|
mStopMobileData.setOnPreferenceChangeListener(this);
|
||||||
mVersion = screen.findPreference(SYNCTHING_VERSION_KEY);
|
mVersion = screen.findPreference(SYNCTHING_VERSION_KEY);
|
||||||
mOptionsScreen = (PreferenceScreen) screen.findPreference(SYNCTHING_OPTIONS_KEY);
|
mOptionsScreen = (PreferenceScreen) screen.findPreference(SYNCTHING_OPTIONS_KEY);
|
||||||
mGuiScreen = (PreferenceScreen) screen.findPreference(SYNCTHING_GUI_KEY);
|
mGuiScreen = (PreferenceScreen) screen.findPreference(SYNCTHING_GUI_KEY);
|
||||||
|
@ -159,16 +166,18 @@ public class SettingsActivity extends PreferenceActivity
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mOptionsScreen.findPreference(preference.getKey()) != null) {
|
if (preference.equals(mStopNotCharging) || preference.equals(mStopMobileData)) {
|
||||||
|
mSyncthingService.updateState();
|
||||||
|
}
|
||||||
|
else if (mOptionsScreen.findPreference(preference.getKey()) != null) {
|
||||||
mSyncthingService.getApi().setValue(RestApi.TYPE_OPTIONS, preference.getKey(), o,
|
mSyncthingService.getApi().setValue(RestApi.TYPE_OPTIONS, preference.getKey(), o,
|
||||||
preference.getKey().equals("ListenAddress"), this);
|
preference.getKey().equals("ListenAddress"), this);
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
else if (mGuiScreen.findPreference(preference.getKey()) != null) {
|
else if (mGuiScreen.findPreference(preference.getKey()) != null) {
|
||||||
mSyncthingService.getApi().setValue(
|
mSyncthingService.getApi().setValue(
|
||||||
RestApi.TYPE_GUI, preference.getKey(), o, false, this);
|
RestApi.TYPE_GUI, preference.getKey(), o, false, this);
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,20 @@
|
||||||
|
package com.nutomic.syncthingandroid.syncthing;
|
||||||
|
|
||||||
|
import android.content.BroadcastReceiver;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Receives battery plug/unplug intents and sends the charging state to {@link SyncthingService}.
|
||||||
|
*/
|
||||||
|
public class BatteryReceiver extends BroadcastReceiver {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onReceive(Context context, Intent intent) {
|
||||||
|
boolean isCharging = Intent.ACTION_POWER_CONNECTED.equals(intent.getAction());
|
||||||
|
Intent i = new Intent(context, SyncthingService.class);
|
||||||
|
i.putExtra(DeviceStateHolder.EXTRA_IS_CHARGING, isCharging);
|
||||||
|
context.startService(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,65 @@
|
||||||
|
package com.nutomic.syncthingandroid.syncthing;
|
||||||
|
|
||||||
|
import android.content.BroadcastReceiver;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.content.IntentFilter;
|
||||||
|
import android.net.ConnectivityManager;
|
||||||
|
import android.os.BatteryManager;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Holds information about the current wifi and charging state of the device.
|
||||||
|
*
|
||||||
|
* This information is actively read on construction, and then updated from intents that are passed
|
||||||
|
* to {@link #update(android.content.Intent)}.
|
||||||
|
*/
|
||||||
|
public class DeviceStateHolder extends BroadcastReceiver {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Intent extra containing a boolean saying whether wifi is connected or not.
|
||||||
|
*/
|
||||||
|
public static final String EXTRA_HAS_WIFI = "has_wifi";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Intent extra containging a boolean saying whether the device is
|
||||||
|
* charging or not (any power source).
|
||||||
|
*/
|
||||||
|
public static final String EXTRA_IS_CHARGING = "is_charging";
|
||||||
|
|
||||||
|
private boolean mIsInitialized = false;
|
||||||
|
|
||||||
|
private boolean mIsWifiConnected = false;
|
||||||
|
|
||||||
|
private boolean mIsCharging = false;
|
||||||
|
|
||||||
|
private SyncthingService mService;
|
||||||
|
|
||||||
|
public DeviceStateHolder(SyncthingService service) {
|
||||||
|
mService = service;
|
||||||
|
ConnectivityManager cm = (ConnectivityManager)
|
||||||
|
mService.getSystemService(Context.CONNECTIVITY_SERVICE);
|
||||||
|
mIsWifiConnected = cm.getNetworkInfo(ConnectivityManager.TYPE_WIFI).isConnected();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onReceive(Context context, Intent intent) {
|
||||||
|
context.unregisterReceiver(this);
|
||||||
|
int status = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1);
|
||||||
|
mIsCharging = status != 0;
|
||||||
|
mIsInitialized = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isCharging() {
|
||||||
|
return mIsCharging;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isWifiConnected() {
|
||||||
|
return mIsWifiConnected;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void update(Intent intent) {
|
||||||
|
mIsWifiConnected = intent.getBooleanExtra(EXTRA_HAS_WIFI, mIsWifiConnected);
|
||||||
|
mIsCharging = intent.getBooleanExtra(EXTRA_IS_CHARGING, mIsCharging);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,31 @@
|
||||||
|
package com.nutomic.syncthingandroid.syncthing;
|
||||||
|
|
||||||
|
import android.content.BroadcastReceiver;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.content.SharedPreferences;
|
||||||
|
import android.net.ConnectivityManager;
|
||||||
|
import android.net.NetworkInfo;
|
||||||
|
import android.os.BatteryManager;
|
||||||
|
import android.preference.PreferenceManager;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Receives network connection change intents and sends the wifi state to {@link SyncthingService}.
|
||||||
|
*/
|
||||||
|
public class NetworkReceiver extends BroadcastReceiver {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onReceive(Context context, Intent intent) {
|
||||||
|
ConnectivityManager cm =
|
||||||
|
(ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
|
||||||
|
NetworkInfo wifiInfo = cm.getNetworkInfo(ConnectivityManager.TYPE_WIFI);
|
||||||
|
NetworkInfo activeNetworkInfo = cm.getActiveNetworkInfo();
|
||||||
|
boolean isWifiConnected = (wifiInfo != null && wifiInfo.isConnected()) ||
|
||||||
|
activeNetworkInfo == null;
|
||||||
|
Intent i = new Intent(context, SyncthingService.class);
|
||||||
|
i.putExtra(DeviceStateHolder.EXTRA_HAS_WIFI, isWifiConnected);
|
||||||
|
context.startService(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -195,6 +195,7 @@ public class RestApi implements SyncthingService.OnWebGuiAvailableListener {
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void onWebGuiAvailable() {
|
public void onWebGuiAvailable() {
|
||||||
|
mAvailableCount.set(0);
|
||||||
new GetTask() {
|
new GetTask() {
|
||||||
@Override
|
@Override
|
||||||
protected void onPostExecute(String version) {
|
protected void onPostExecute(String version) {
|
||||||
|
@ -226,12 +227,13 @@ public class RestApi implements SyncthingService.OnWebGuiAvailableListener {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Increments mAvailableCount by one, and, if it reached TOTAL_STARTUP_CALLS,
|
* Increments mAvailableCount by one, and, if it reached TOTAL_STARTUP_CALLS,
|
||||||
* calls {@link SyncthingService#onApiChange(boolean)}.
|
* calls {@link SyncthingService#onApiChange()}.
|
||||||
*/
|
*/
|
||||||
private void tryIsAvailable() {
|
private void tryIsAvailable() {
|
||||||
int value = mAvailableCount.incrementAndGet();
|
int value = mAvailableCount.incrementAndGet();
|
||||||
|
assert(value <= TOTAL_STARTUP_CALLS);
|
||||||
if (value == TOTAL_STARTUP_CALLS) {
|
if (value == TOTAL_STARTUP_CALLS) {
|
||||||
mSyncthingService.onApiChange(true);
|
mSyncthingService.onApiChange();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -370,12 +372,15 @@ public class RestApi implements SyncthingService.OnWebGuiAvailableListener {
|
||||||
* Returns a list of all existing nodes.
|
* Returns a list of all existing nodes.
|
||||||
*/
|
*/
|
||||||
public List<Node> getNodes() {
|
public List<Node> getNodes() {
|
||||||
|
if (mConfig == null)
|
||||||
|
return new ArrayList<Node>();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return getNodes(mConfig.getJSONArray("Nodes"));
|
return getNodes(mConfig.getJSONArray("Nodes"));
|
||||||
}
|
}
|
||||||
catch (JSONException e) {
|
catch (JSONException e) {
|
||||||
Log.w(TAG, "Failed to read nodes", e);
|
Log.w(TAG, "Failed to read nodes", e);
|
||||||
return null;
|
return new ArrayList<Node>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,14 +1,18 @@
|
||||||
package com.nutomic.syncthingandroid.syncthing;
|
package com.nutomic.syncthingandroid.syncthing;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
import android.app.AlertDialog;
|
import android.app.AlertDialog;
|
||||||
import android.app.Notification;
|
import android.app.Notification;
|
||||||
import android.app.PendingIntent;
|
import android.app.PendingIntent;
|
||||||
import android.app.Service;
|
import android.app.Service;
|
||||||
import android.content.DialogInterface;
|
import android.content.DialogInterface;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
|
import android.content.IntentFilter;
|
||||||
|
import android.content.SharedPreferences;
|
||||||
import android.os.AsyncTask;
|
import android.os.AsyncTask;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import android.os.IBinder;
|
import android.os.IBinder;
|
||||||
|
import android.preference.PreferenceManager;
|
||||||
import android.support.v4.app.NotificationCompat;
|
import android.support.v4.app.NotificationCompat;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.util.Pair;
|
import android.util.Pair;
|
||||||
|
@ -16,6 +20,7 @@ import android.view.WindowManager;
|
||||||
|
|
||||||
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.gui.SettingsActivity;
|
||||||
import com.nutomic.syncthingandroid.util.ConfigXml;
|
import com.nutomic.syncthingandroid.util.ConfigXml;
|
||||||
|
|
||||||
import org.apache.http.HttpResponse;
|
import org.apache.http.HttpResponse;
|
||||||
|
@ -98,20 +103,47 @@ public class SyncthingService extends Service {
|
||||||
private final HashSet<OnWebGuiAvailableListener> mOnWebGuiAvailableListeners =
|
private final HashSet<OnWebGuiAvailableListener> mOnWebGuiAvailableListeners =
|
||||||
new HashSet<OnWebGuiAvailableListener>();
|
new HashSet<OnWebGuiAvailableListener>();
|
||||||
|
|
||||||
private boolean mIsWebGuiAvailable = false;
|
|
||||||
|
|
||||||
public interface OnApiChangeListener {
|
public interface OnApiChangeListener {
|
||||||
public void onApiChange(boolean isAvailable);
|
public void onApiChange(State currentState);
|
||||||
}
|
}
|
||||||
|
|
||||||
private final HashSet<WeakReference<OnApiChangeListener>> mOnApiAvailableListeners =
|
private final HashSet<WeakReference<OnApiChangeListener>> mOnApiChangeListeners =
|
||||||
new HashSet<WeakReference<OnApiChangeListener>>();
|
new HashSet<WeakReference<OnApiChangeListener>>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* INIT: Service is starting up and initializing.
|
||||||
|
* STARTING: Syncthing binary is starting (but the API is not yet ready).
|
||||||
|
* ACTIVE: Syncthing binary is up and running.
|
||||||
|
* DISABLED: Syncthing binary is stopped according to user preferences.
|
||||||
|
*/
|
||||||
|
public enum State {
|
||||||
|
INIT,
|
||||||
|
STARTING,
|
||||||
|
ACTIVE,
|
||||||
|
DISABLED
|
||||||
|
}
|
||||||
|
|
||||||
|
private State mCurrentState = State.INIT;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* True if a stop was requested while syncthing is starting, in that case, perform stop in
|
||||||
|
* {@link PollWebGuiAvailableTask}.
|
||||||
|
*/
|
||||||
|
private boolean mStopScheduled = false;
|
||||||
|
|
||||||
|
private DeviceStateHolder mDeviceStateHolder;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles intents, either {@link #ACTION_RESTART}, or intents having
|
||||||
|
* {@link DeviceStateHolder.EXTRA_HAS_WIFI} or {@link DeviceStateHolder.EXTRA_IS_CHARGING}
|
||||||
|
* (which are handled by {@link DeviceStateHolder}.
|
||||||
|
*/
|
||||||
@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())) {
|
// Just catch the empty intent and return.
|
||||||
mIsWebGuiAvailable = false;
|
if (intent == null) {
|
||||||
onApiChange(false);
|
}
|
||||||
|
else if (ACTION_RESTART.equals(intent.getAction()) && mCurrentState == State.ACTIVE) {
|
||||||
new PostTask() {
|
new PostTask() {
|
||||||
@Override
|
@Override
|
||||||
protected void onPostExecute(Void aVoid) {
|
protected void onPostExecute(Void aVoid) {
|
||||||
|
@ -123,9 +155,55 @@ public class SyncthingService extends Service {
|
||||||
}
|
}
|
||||||
}.execute(mApi.getUrl(), PostTask.URI_RESTART, mApi.getApiKey());
|
}.execute(mApi.getUrl(), PostTask.URI_RESTART, mApi.getApiKey());
|
||||||
}
|
}
|
||||||
|
else if (mCurrentState != State.INIT) {
|
||||||
|
mDeviceStateHolder.update(intent);
|
||||||
|
updateState();
|
||||||
|
}
|
||||||
return START_STICKY;
|
return START_STICKY;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks according to preferences and charging/wifi state, whether syncthing should be enabled
|
||||||
|
* or not.
|
||||||
|
*
|
||||||
|
* Depending on the result, syncthing is started or stopped, and {@link #onApiChange()} is
|
||||||
|
* called.
|
||||||
|
*/
|
||||||
|
public void updateState() {
|
||||||
|
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
|
||||||
|
boolean prefStopMobileData = prefs.getBoolean("stop_sync_on_mobile_data", true);
|
||||||
|
boolean prefStopNotCharging = prefs.getBoolean("stop_sync_while_not_charging", true);
|
||||||
|
|
||||||
|
// Start syncthing.
|
||||||
|
if ((mDeviceStateHolder.isCharging() || !prefStopNotCharging) &&
|
||||||
|
(mDeviceStateHolder.isWifiConnected() || !prefStopMobileData)) {
|
||||||
|
if (mCurrentState == State.ACTIVE || mCurrentState == State.STARTING) {
|
||||||
|
mStopScheduled = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
mCurrentState = State.STARTING;
|
||||||
|
registerOnWebGuiAvailableListener(mApi);
|
||||||
|
new PollWebGuiAvailableTask().execute();
|
||||||
|
new Thread(new SyncthingRunnable()).start();
|
||||||
|
}
|
||||||
|
// Stop syncthing.
|
||||||
|
else {
|
||||||
|
if (mCurrentState == State.DISABLED)
|
||||||
|
return;
|
||||||
|
|
||||||
|
mCurrentState = State.DISABLED;
|
||||||
|
|
||||||
|
// Syncthing is currently started, perform the stop later.
|
||||||
|
if (mCurrentState == State.STARTING) {
|
||||||
|
mStopScheduled = true;
|
||||||
|
} else if (mApi != null) {
|
||||||
|
mApi.shutdown();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
onApiChange();
|
||||||
|
}
|
||||||
|
|
||||||
private Handler mMainThreadHandler;
|
private Handler mMainThreadHandler;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -254,8 +332,15 @@ public class SyncthingService extends Service {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onPostExecute(Void aVoid) {
|
protected void onPostExecute(Void aVoid) {
|
||||||
|
if (mStopScheduled) {
|
||||||
|
mCurrentState = State.DISABLED;
|
||||||
|
onApiChange();
|
||||||
|
mApi.shutdown();
|
||||||
|
mStopScheduled = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
Log.i(TAG, "Web GUI has come online at " + mApi.getUrl());
|
Log.i(TAG, "Web GUI has come online at " + mApi.getUrl());
|
||||||
mIsWebGuiAvailable = true;
|
mCurrentState = State.ACTIVE;
|
||||||
for (OnWebGuiAvailableListener listener : mOnWebGuiAvailableListeners) {
|
for (OnWebGuiAvailableListener listener : mOnWebGuiAvailableListeners) {
|
||||||
listener.onWebGuiAvailable();
|
listener.onWebGuiAvailable();
|
||||||
}
|
}
|
||||||
|
@ -316,6 +401,9 @@ public class SyncthingService extends Service {
|
||||||
n.flags |= Notification.FLAG_ONGOING_EVENT;
|
n.flags |= Notification.FLAG_ONGOING_EVENT;
|
||||||
startForeground(NOTIFICATION_RUNNING, n);
|
startForeground(NOTIFICATION_RUNNING, n);
|
||||||
|
|
||||||
|
mMainThreadHandler = new Handler();
|
||||||
|
mDeviceStateHolder = new DeviceStateHolder(SyncthingService.this);
|
||||||
|
registerReceiver(mDeviceStateHolder, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
|
||||||
new StartupTask().execute();
|
new StartupTask().execute();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -327,7 +415,6 @@ public class SyncthingService extends Service {
|
||||||
private class StartupTask extends AsyncTask<Void, Void, Pair<String, String>> {
|
private class StartupTask extends AsyncTask<Void, Void, Pair<String, String>> {
|
||||||
@Override
|
@Override
|
||||||
protected Pair<String, String> doInBackground(Void... voids) {
|
protected Pair<String, String> doInBackground(Void... voids) {
|
||||||
//Looper.prepare();
|
|
||||||
if (isFirstStart()) {
|
if (isFirstStart()) {
|
||||||
Log.i(TAG, "App started for the first time. " +
|
Log.i(TAG, "App started for the first time. " +
|
||||||
"Copying default config, keys will be generated automatically");
|
"Copying default config, keys will be generated automatically");
|
||||||
|
@ -347,14 +434,12 @@ public class SyncthingService extends Service {
|
||||||
protected void onPostExecute(Pair<String, String> urlAndKey) {
|
protected void onPostExecute(Pair<String, String> urlAndKey) {
|
||||||
mApi = new RestApi(SyncthingService.this, urlAndKey.first, urlAndKey.second);
|
mApi = new RestApi(SyncthingService.this, urlAndKey.first, urlAndKey.second);
|
||||||
Log.i(TAG, "Web GUI will be available at " + mApi.getUrl());
|
Log.i(TAG, "Web GUI will be available at " + mApi.getUrl());
|
||||||
|
|
||||||
// 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, "");
|
mApi.shutdown();
|
||||||
registerOnWebGuiAvailableListener(mApi);
|
updateState();
|
||||||
new PollWebGuiAvailableTask().execute();
|
|
||||||
mMainThreadHandler = new Handler();
|
|
||||||
new Thread(new SyncthingRunnable()).start();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -380,7 +465,7 @@ public class SyncthingService extends Service {
|
||||||
* Listeners are unregistered automatically after being called.
|
* Listeners are unregistered automatically after being called.
|
||||||
*/
|
*/
|
||||||
public void registerOnWebGuiAvailableListener(OnWebGuiAvailableListener listener) {
|
public void registerOnWebGuiAvailableListener(OnWebGuiAvailableListener listener) {
|
||||||
if (mIsWebGuiAvailable) {
|
if (mCurrentState == State.ACTIVE) {
|
||||||
listener.onWebGuiAvailable();
|
listener.onWebGuiAvailable();
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
@ -442,20 +527,23 @@ public class SyncthingService extends Service {
|
||||||
* changes.
|
* changes.
|
||||||
*/
|
*/
|
||||||
public void registerOnApiChangeListener(OnApiChangeListener listener) {
|
public void registerOnApiChangeListener(OnApiChangeListener listener) {
|
||||||
listener.onApiChange((mApi != null) ? mApi.isApiAvailable() : false);
|
// Make sure we don't send an invalid state or syncthing might shwow a "disabled" message
|
||||||
mOnApiAvailableListeners.add(new WeakReference<OnApiChangeListener>(listener));
|
// when it's just starting up.
|
||||||
|
listener.onApiChange(mCurrentState);
|
||||||
|
mOnApiChangeListeners.add(new WeakReference<OnApiChangeListener>(listener));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when the state of the API changes.
|
* Called to notifiy listeners of an API change.
|
||||||
*
|
*
|
||||||
* Must only be called from SyncthingService or {@link RestApi}.
|
* Must only be called from SyncthingService or {@link RestApi}.
|
||||||
*/
|
*/
|
||||||
public void onApiChange(boolean isAvailable) {
|
public void onApiChange() {
|
||||||
for (Iterator<WeakReference<OnApiChangeListener>> i = mOnApiAvailableListeners.iterator(); i.hasNext();) {
|
for (Iterator<WeakReference<OnApiChangeListener>> i = mOnApiChangeListeners.iterator();
|
||||||
|
i.hasNext(); ) {
|
||||||
WeakReference<OnApiChangeListener> listener = i.next();
|
WeakReference<OnApiChangeListener> listener = i.next();
|
||||||
if (listener.get() != null) {
|
if (listener.get() != null) {
|
||||||
listener.get().onApiChange(isAvailable);
|
listener.get().onApiChange(mCurrentState);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
i.remove();
|
i.remove();
|
||||||
|
@ -463,4 +551,31 @@ public class SyncthingService extends Service {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dialog to be shown when attempting to start syncthing while it is disabled according
|
||||||
|
* to settings (because the device is not charging or wifi is disconnected).
|
||||||
|
*/
|
||||||
|
public static void showDisabledDialog(final Activity activity) {
|
||||||
|
new AlertDialog.Builder(activity)
|
||||||
|
.setTitle(R.string.syncthing_disabled_title)
|
||||||
|
.setMessage(R.string.syncthing_disabled_message)
|
||||||
|
.setPositiveButton(R.string.syncthing_disabled_change_settings,
|
||||||
|
new DialogInterface.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(DialogInterface dialogInterface, int i) {
|
||||||
|
activity.finish();
|
||||||
|
activity.startActivity(new Intent(activity, SettingsActivity.class));
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.setNegativeButton(R.string.exit,
|
||||||
|
new DialogInterface.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(DialogInterface dialogInterface, int i) {
|
||||||
|
activity.finish();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.show()
|
||||||
|
.setCancelable(false);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
|
|
||||||
<!-- RepositoriesFragment -->
|
<!-- RepositoriesFragment -->
|
||||||
|
|
||||||
|
|
||||||
<string name="repositories_fragment_title">Repositories</string>
|
<string name="repositories_fragment_title">Repositories</string>
|
||||||
|
|
||||||
<!-- Format string for repository progress. First parameter is status string, second is sync percentage -->
|
<!-- Format string for repository progress. First parameter is status string, second is sync percentage -->
|
||||||
|
@ -33,6 +34,7 @@
|
||||||
|
|
||||||
<!-- NodesFragment -->
|
<!-- NodesFragment -->
|
||||||
|
|
||||||
|
|
||||||
<string name="nodes_fragment_title">Nodes</string>
|
<string name="nodes_fragment_title">Nodes</string>
|
||||||
|
|
||||||
<!-- Shown if no nodes exist -->
|
<!-- Shown if no nodes exist -->
|
||||||
|
@ -192,6 +194,10 @@ Please report any problems you encounter.</string>
|
||||||
<!-- Activity title -->
|
<!-- Activity title -->
|
||||||
<string name="settings_title">Settings</string>
|
<string name="settings_title">Settings</string>
|
||||||
|
|
||||||
|
<string name="stop_sync_while_not_charging">Stop sync when not charging</string>
|
||||||
|
|
||||||
|
<string name="stop_sync_on_mobile_data">Stop sync on mobile data</string>
|
||||||
|
|
||||||
<!-- Settings item that opens issue tracker -->
|
<!-- Settings item that opens issue tracker -->
|
||||||
<string name="report_issue_title">Report Issue</string>
|
<string name="report_issue_title">Report Issue</string>
|
||||||
|
|
||||||
|
@ -221,7 +227,6 @@ Please report any problems you encounter.</string>
|
||||||
|
|
||||||
<!-- SyncthingService -->
|
<!-- SyncthingService -->
|
||||||
|
|
||||||
|
|
||||||
<!-- Title of the dialog shown when the syncthing binary returns an error -->
|
<!-- Title of the dialog shown when the syncthing binary returns an error -->
|
||||||
<string name="binary_crashed_title">Syncthing Binary Crashed</string>
|
<string name="binary_crashed_title">Syncthing Binary Crashed</string>
|
||||||
|
|
||||||
|
@ -230,6 +235,14 @@ Please report any problems you encounter.</string>
|
||||||
<string name="binary_crashed_message">The syncthing binary has exited with error code %1$d.\n\n
|
<string name="binary_crashed_message">The syncthing binary has exited with error code %1$d.\n\n
|
||||||
If this error persists, try reinstalling the app and restarting your device.\n\n</string>
|
If this error persists, try reinstalling the app and restarting your device.\n\n</string>
|
||||||
|
|
||||||
|
<!-- Title of the "syncthing disabled" dialog -->
|
||||||
|
<string name="syncthing_disabled_title">Syncthing is disabled</string>
|
||||||
|
|
||||||
|
<!-- Message of the "syncthing disabled" dialog -->
|
||||||
|
<string name="syncthing_disabled_message">Do you want to change your preferences?</string>
|
||||||
|
|
||||||
|
<string name="syncthing_disabled_change_settings">Change Settings</string>
|
||||||
|
|
||||||
<!-- RestApi -->
|
<!-- RestApi -->
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,16 @@
|
||||||
<?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">
|
||||||
|
|
||||||
|
<CheckBoxPreference
|
||||||
|
android:key="stop_sync_while_not_charging"
|
||||||
|
android:title="@string/stop_sync_while_not_charging"
|
||||||
|
android:defaultValue="true" />
|
||||||
|
|
||||||
|
<CheckBoxPreference
|
||||||
|
android:key="stop_sync_on_mobile_data"
|
||||||
|
android:title="@string/stop_sync_on_mobile_data"
|
||||||
|
android:defaultValue="true" />
|
||||||
|
|
||||||
<PreferenceScreen
|
<PreferenceScreen
|
||||||
android:key="syncthing_options"
|
android:key="syncthing_options"
|
||||||
android:title="Syncthing Options"
|
android:title="Syncthing Options"
|
||||||
|
|
Loading…
Reference in New Issue