mirror of
https://github.com/syncthing/syncthing-android.git
synced 2024-11-26 22:31:16 +00:00
Add options to stop sync when not charging or not on wifi (fixes #15).
This commit is contained in:
parent
733940cbdf
commit
9c4a85b85d
16 changed files with 383 additions and 91 deletions
|
@ -13,6 +13,7 @@
|
|||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
|
||||
|
||||
<application
|
||||
android:allowBackup="false"
|
||||
|
@ -57,6 +58,17 @@
|
|||
android:name="android.support.UI_OPTIONS"
|
||||
android:value="splitActionBarWhenNarrow" />
|
||||
</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>
|
||||
|
||||
</manifest>
|
||||
|
|
|
@ -203,9 +203,10 @@ public class FolderPickerActivity extends ActionBarActivity
|
|||
}
|
||||
|
||||
@Override
|
||||
public void onApiChange(boolean isAvailable) {
|
||||
if (!isAvailable) {
|
||||
public void onApiChange(SyncthingService.State currentState) {
|
||||
if (currentState != SyncthingService.State.ACTIVE) {
|
||||
setResult(Activity.RESULT_CANCELED);
|
||||
SyncthingService.showDisabledDialog(this);
|
||||
finish();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -111,8 +111,8 @@ public class LocalNodeInfoFragment extends Fragment
|
|||
}
|
||||
|
||||
@Override
|
||||
public void onApiChange(boolean isAvailable) {
|
||||
if (!isAvailable)
|
||||
public void onApiChange(SyncthingService.State currentState) {
|
||||
if (currentState != SyncthingService.State.ACTIVE)
|
||||
return;
|
||||
|
||||
updateGui();
|
||||
|
|
|
@ -23,6 +23,7 @@ import android.support.v7.app.ActionBar;
|
|||
import android.support.v7.app.ActionBar.Tab;
|
||||
import android.support.v7.app.ActionBar.TabListener;
|
||||
import android.support.v7.app.ActionBarActivity;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
|
@ -66,37 +67,44 @@ public class MainActivity extends ActionBarActivity
|
|||
*/
|
||||
@Override
|
||||
@SuppressLint("InflateParams")
|
||||
public void onApiChange(boolean isAvailable) {
|
||||
if (!isAvailable) {
|
||||
final SharedPreferences prefs =
|
||||
PreferenceManager.getDefaultSharedPreferences(MainActivity.this);
|
||||
|
||||
LayoutInflater inflater = getLayoutInflater();
|
||||
View dialogLayout = inflater.inflate(R.layout.loading_dialog, null);
|
||||
TextView loadingText = (TextView) dialogLayout.findViewById(R.id.loading_text);
|
||||
loadingText.setText((mSyncthingService.isFirstStart())
|
||||
? R.string.web_gui_creating_key
|
||||
: R.string.api_loading);
|
||||
|
||||
mLoadingDialog = new AlertDialog.Builder(this)
|
||||
.setCancelable(false)
|
||||
.setView(dialogLayout)
|
||||
.show();
|
||||
|
||||
// Make sure the first start dialog is shown on top.
|
||||
if (prefs.getBoolean("first_start", true)) {
|
||||
new AlertDialog.Builder(this)
|
||||
.setTitle(R.string.welcome_title)
|
||||
.setMessage(R.string.welcome_text)
|
||||
.setNeutralButton(android.R.string.ok, new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialogInterface, int i) {
|
||||
prefs.edit().putBoolean("first_start", false).commit();
|
||||
}
|
||||
})
|
||||
.show();
|
||||
public void onApiChange(SyncthingService.State currentState) {
|
||||
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 =
|
||||
PreferenceManager.getDefaultSharedPreferences(MainActivity.this);
|
||||
|
||||
LayoutInflater inflater = getLayoutInflater();
|
||||
View dialogLayout = inflater.inflate(R.layout.loading_dialog, null);
|
||||
TextView loadingText = (TextView) dialogLayout.findViewById(R.id.loading_text);
|
||||
loadingText.setText((mSyncthingService.isFirstStart())
|
||||
? R.string.web_gui_creating_key
|
||||
: R.string.api_loading);
|
||||
|
||||
mLoadingDialog = new AlertDialog.Builder(this)
|
||||
.setCancelable(false)
|
||||
.setView(dialogLayout)
|
||||
.show();
|
||||
|
||||
// Make sure the first start dialog is shown on top.
|
||||
if (prefs.getBoolean("first_start", true)) {
|
||||
new AlertDialog.Builder(this)
|
||||
.setTitle(R.string.welcome_title)
|
||||
.setMessage(R.string.welcome_text)
|
||||
.setNeutralButton(android.R.string.ok, new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialogInterface, int i) {
|
||||
prefs.edit().putBoolean("first_start", false).commit();
|
||||
}
|
||||
})
|
||||
.show();
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -75,6 +75,7 @@ public class NodeSettingsActivity extends PreferenceActivity implements
|
|||
|
||||
@Override
|
||||
@SuppressLint("AppCompatMethod")
|
||||
@TargetApi(11)
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
|
@ -109,8 +110,9 @@ public class NodeSettingsActivity extends PreferenceActivity implements
|
|||
}
|
||||
|
||||
@Override
|
||||
public void onApiChange(boolean isAvailable) {
|
||||
if (!isAvailable) {
|
||||
public void onApiChange(SyncthingService.State currentState) {
|
||||
if (currentState != SyncthingService.State.ACTIVE) {
|
||||
SyncthingService.showDisabledDialog(this);
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -31,8 +31,8 @@ public class NodesFragment extends ListFragment implements SyncthingService.OnAp
|
|||
}
|
||||
|
||||
@Override
|
||||
public void onApiChange(boolean isAvailable) {
|
||||
if (!isAvailable)
|
||||
public void onApiChange(SyncthingService.State currentState) {
|
||||
if (currentState != SyncthingService.State.ACTIVE)
|
||||
return;
|
||||
|
||||
initAdapter();
|
||||
|
|
|
@ -118,8 +118,9 @@ public class RepoSettingsActivity extends PreferenceActivity
|
|||
}
|
||||
|
||||
@Override
|
||||
public void onApiChange(boolean isAvailable) {
|
||||
if (!isAvailable) {
|
||||
public void onApiChange(SyncthingService.State currentState) {
|
||||
if (currentState != SyncthingService.State.ACTIVE) {
|
||||
SyncthingService.showDisabledDialog(this);
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -30,8 +30,8 @@ public class ReposFragment extends ListFragment implements SyncthingService.OnAp
|
|||
}
|
||||
|
||||
@Override
|
||||
public void onApiChange(boolean isAvailable) {
|
||||
if (!isAvailable)
|
||||
public void onApiChange(SyncthingService.State currentState) {
|
||||
if (currentState != SyncthingService.State.ACTIVE)
|
||||
return;
|
||||
|
||||
initAdapter();
|
||||
|
|
|
@ -32,6 +32,10 @@ public class SettingsActivity extends PreferenceActivity
|
|||
|
||||
private static final String SYNCTHING_VERSION_KEY = "syncthing_version";
|
||||
|
||||
private CheckBoxPreference mStopNotCharging;
|
||||
|
||||
private CheckBoxPreference mStopMobileData;
|
||||
|
||||
private Preference mVersion;
|
||||
|
||||
private PreferenceScreen mOptionsScreen;
|
||||
|
@ -57,30 +61,29 @@ public class SettingsActivity extends PreferenceActivity
|
|||
};
|
||||
|
||||
@Override
|
||||
public void onApiChange(boolean isAvailable) {
|
||||
if (!isAvailable) {
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
public void onApiChange(SyncthingService.State currentState) {
|
||||
mOptionsScreen.setEnabled(currentState == SyncthingService.State.ACTIVE);
|
||||
mGuiScreen.setEnabled(currentState == SyncthingService.State.ACTIVE);
|
||||
|
||||
mVersion.setSummary(mSyncthingService.getApi().getVersion());
|
||||
|
||||
for (int i = 0; i < mOptionsScreen.getPreferenceCount(); i++) {
|
||||
Preference pref = mOptionsScreen.getPreference(i);
|
||||
pref.setOnPreferenceChangeListener(SettingsActivity.this);
|
||||
String value = mSyncthingService.getApi()
|
||||
.getValue(RestApi.TYPE_OPTIONS, pref.getKey());
|
||||
applyPreference(pref, value);
|
||||
}
|
||||
if (currentState == SyncthingService.State.ACTIVE) {
|
||||
for (int i = 0; i < mOptionsScreen.getPreferenceCount(); i++) {
|
||||
Preference pref = mOptionsScreen.getPreference(i);
|
||||
pref.setOnPreferenceChangeListener(SettingsActivity.this);
|
||||
String value = mSyncthingService.getApi()
|
||||
.getValue(RestApi.TYPE_OPTIONS, pref.getKey());
|
||||
applyPreference(pref, value);
|
||||
}
|
||||
|
||||
for (int i = 0; i < mGuiScreen.getPreferenceCount(); i++) {
|
||||
Preference pref = mGuiScreen.getPreference(i);
|
||||
pref.setOnPreferenceChangeListener(SettingsActivity.this);
|
||||
String value = mSyncthingService.getApi()
|
||||
.getValue(RestApi.TYPE_GUI, pref.getKey());
|
||||
applyPreference(pref, value);
|
||||
for (int i = 0; i < mGuiScreen.getPreferenceCount(); i++) {
|
||||
Preference pref = mGuiScreen.getPreference(i);
|
||||
pref.setOnPreferenceChangeListener(SettingsActivity.this);
|
||||
String value = mSyncthingService.getApi()
|
||||
.getValue(RestApi.TYPE_GUI, pref.getKey());
|
||||
applyPreference(pref, value);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -119,6 +122,10 @@ public class SettingsActivity extends PreferenceActivity
|
|||
|
||||
addPreferencesFromResource(R.xml.app_settings);
|
||||
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);
|
||||
mOptionsScreen = (PreferenceScreen) screen.findPreference(SYNCTHING_OPTIONS_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,
|
||||
preference.getKey().equals("ListenAddress"), this);
|
||||
return true;
|
||||
}
|
||||
else if (mGuiScreen.findPreference(preference.getKey()) != null) {
|
||||
mSyncthingService.getApi().setValue(
|
||||
RestApi.TYPE_GUI, preference.getKey(), o, false, this);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
public void onWebGuiAvailable() {
|
||||
mAvailableCount.set(0);
|
||||
new GetTask() {
|
||||
@Override
|
||||
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,
|
||||
* calls {@link SyncthingService#onApiChange(boolean)}.
|
||||
* calls {@link SyncthingService#onApiChange()}.
|
||||
*/
|
||||
private void tryIsAvailable() {
|
||||
int value = mAvailableCount.incrementAndGet();
|
||||
assert(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.
|
||||
*/
|
||||
public List<Node> getNodes() {
|
||||
if (mConfig == null)
|
||||
return new ArrayList<Node>();
|
||||
|
||||
try {
|
||||
return getNodes(mConfig.getJSONArray("Nodes"));
|
||||
}
|
||||
catch (JSONException e) {
|
||||
Log.w(TAG, "Failed to read nodes", e);
|
||||
return null;
|
||||
return new ArrayList<Node>();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,14 +1,18 @@
|
|||
package com.nutomic.syncthingandroid.syncthing;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.AlertDialog;
|
||||
import android.app.Notification;
|
||||
import android.app.PendingIntent;
|
||||
import android.app.Service;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.content.SharedPreferences;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Handler;
|
||||
import android.os.IBinder;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.support.v4.app.NotificationCompat;
|
||||
import android.util.Log;
|
||||
import android.util.Pair;
|
||||
|
@ -16,6 +20,7 @@ import android.view.WindowManager;
|
|||
|
||||
import com.nutomic.syncthingandroid.R;
|
||||
import com.nutomic.syncthingandroid.gui.MainActivity;
|
||||
import com.nutomic.syncthingandroid.gui.SettingsActivity;
|
||||
import com.nutomic.syncthingandroid.util.ConfigXml;
|
||||
|
||||
import org.apache.http.HttpResponse;
|
||||
|
@ -98,20 +103,47 @@ public class SyncthingService extends Service {
|
|||
private final HashSet<OnWebGuiAvailableListener> mOnWebGuiAvailableListeners =
|
||||
new HashSet<OnWebGuiAvailableListener>();
|
||||
|
||||
private boolean mIsWebGuiAvailable = false;
|
||||
|
||||
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>>();
|
||||
|
||||
/**
|
||||
* 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
|
||||
public int onStartCommand(Intent intent, int flags, int startId) {
|
||||
if (intent != null && ACTION_RESTART.equals(intent.getAction())) {
|
||||
mIsWebGuiAvailable = false;
|
||||
onApiChange(false);
|
||||
// Just catch the empty intent and return.
|
||||
if (intent == null) {
|
||||
}
|
||||
else if (ACTION_RESTART.equals(intent.getAction()) && mCurrentState == State.ACTIVE) {
|
||||
new PostTask() {
|
||||
@Override
|
||||
protected void onPostExecute(Void aVoid) {
|
||||
|
@ -123,9 +155,55 @@ public class SyncthingService extends Service {
|
|||
}
|
||||
}.execute(mApi.getUrl(), PostTask.URI_RESTART, mApi.getApiKey());
|
||||
}
|
||||
else if (mCurrentState != State.INIT) {
|
||||
mDeviceStateHolder.update(intent);
|
||||
updateState();
|
||||
}
|
||||
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;
|
||||
|
||||
/**
|
||||
|
@ -254,8 +332,15 @@ public class SyncthingService extends Service {
|
|||
|
||||
@Override
|
||||
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());
|
||||
mIsWebGuiAvailable = true;
|
||||
mCurrentState = State.ACTIVE;
|
||||
for (OnWebGuiAvailableListener listener : mOnWebGuiAvailableListeners) {
|
||||
listener.onWebGuiAvailable();
|
||||
}
|
||||
|
@ -316,6 +401,9 @@ public class SyncthingService extends Service {
|
|||
n.flags |= Notification.FLAG_ONGOING_EVENT;
|
||||
startForeground(NOTIFICATION_RUNNING, n);
|
||||
|
||||
mMainThreadHandler = new Handler();
|
||||
mDeviceStateHolder = new DeviceStateHolder(SyncthingService.this);
|
||||
registerReceiver(mDeviceStateHolder, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
|
||||
new StartupTask().execute();
|
||||
}
|
||||
|
||||
|
@ -327,7 +415,6 @@ public class SyncthingService extends Service {
|
|||
private class StartupTask extends AsyncTask<Void, Void, Pair<String, String>> {
|
||||
@Override
|
||||
protected Pair<String, String> doInBackground(Void... voids) {
|
||||
//Looper.prepare();
|
||||
if (isFirstStart()) {
|
||||
Log.i(TAG, "App started for the first time. " +
|
||||
"Copying default config, keys will be generated automatically");
|
||||
|
@ -347,14 +434,12 @@ public class SyncthingService extends Service {
|
|||
protected void onPostExecute(Pair<String, String> urlAndKey) {
|
||||
mApi = new RestApi(SyncthingService.this, urlAndKey.first, urlAndKey.second);
|
||||
Log.i(TAG, "Web GUI will be available at " + mApi.getUrl());
|
||||
|
||||
// HACK: Make sure there is no syncthing binary left running from an improper
|
||||
// shutdown (eg Play Store update).
|
||||
// NOTE: This will log an exception if syncthing is not actually running.
|
||||
new PostTask().execute(mApi.getUrl(), PostTask.URI_SHUTDOWN, urlAndKey.second, "");
|
||||
registerOnWebGuiAvailableListener(mApi);
|
||||
new PollWebGuiAvailableTask().execute();
|
||||
mMainThreadHandler = new Handler();
|
||||
new Thread(new SyncthingRunnable()).start();
|
||||
mApi.shutdown();
|
||||
updateState();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -380,7 +465,7 @@ public class SyncthingService extends Service {
|
|||
* Listeners are unregistered automatically after being called.
|
||||
*/
|
||||
public void registerOnWebGuiAvailableListener(OnWebGuiAvailableListener listener) {
|
||||
if (mIsWebGuiAvailable) {
|
||||
if (mCurrentState == State.ACTIVE) {
|
||||
listener.onWebGuiAvailable();
|
||||
}
|
||||
else {
|
||||
|
@ -442,20 +527,23 @@ public class SyncthingService extends Service {
|
|||
* changes.
|
||||
*/
|
||||
public void registerOnApiChangeListener(OnApiChangeListener listener) {
|
||||
listener.onApiChange((mApi != null) ? mApi.isApiAvailable() : false);
|
||||
mOnApiAvailableListeners.add(new WeakReference<OnApiChangeListener>(listener));
|
||||
// Make sure we don't send an invalid state or syncthing might shwow a "disabled" message
|
||||
// when it's just starting up.
|
||||
listener.onApiChange(mCurrentState);
|
||||
mOnApiChangeListeners.add(new WeakReference<OnApiChangeListener>(listener));
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the state of the API changes.
|
||||
*
|
||||
* Must only be called from SyncthingService or {@link RestApi}.
|
||||
* Called to notifiy listeners of an API change.
|
||||
*
|
||||
* Must only be called from SyncthingService or {@link RestApi}.
|
||||
*/
|
||||
public void onApiChange(boolean isAvailable) {
|
||||
for (Iterator<WeakReference<OnApiChangeListener>> i = mOnApiAvailableListeners.iterator(); i.hasNext();) {
|
||||
public void onApiChange() {
|
||||
for (Iterator<WeakReference<OnApiChangeListener>> i = mOnApiChangeListeners.iterator();
|
||||
i.hasNext(); ) {
|
||||
WeakReference<OnApiChangeListener> listener = i.next();
|
||||
if (listener.get() != null) {
|
||||
listener.get().onApiChange(isAvailable);
|
||||
listener.get().onApiChange(mCurrentState);
|
||||
}
|
||||
else {
|
||||
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 -->
|
||||
|
||||
|
||||
<string name="repositories_fragment_title">Repositories</string>
|
||||
|
||||
<!-- Format string for repository progress. First parameter is status string, second is sync percentage -->
|
||||
|
@ -33,6 +34,7 @@
|
|||
|
||||
<!-- NodesFragment -->
|
||||
|
||||
|
||||
<string name="nodes_fragment_title">Nodes</string>
|
||||
|
||||
<!-- Shown if no nodes exist -->
|
||||
|
@ -192,6 +194,10 @@ Please report any problems you encounter.</string>
|
|||
<!-- Activity title -->
|
||||
<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 -->
|
||||
<string name="report_issue_title">Report Issue</string>
|
||||
|
||||
|
@ -221,7 +227,6 @@ Please report any problems you encounter.</string>
|
|||
|
||||
<!-- SyncthingService -->
|
||||
|
||||
|
||||
<!-- Title of the dialog shown when the syncthing binary returns an error -->
|
||||
<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
|
||||
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 -->
|
||||
|
||||
|
||||
|
|
|
@ -1,6 +1,16 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<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
|
||||
android:key="syncthing_options"
|
||||
android:title="Syncthing Options"
|
||||
|
|
Loading…
Reference in a new issue