From f383393ce4280e3182ab84ad0c240c85870ff296 Mon Sep 17 00:00:00 2001 From: Felix Ableitner Date: Wed, 1 Oct 2014 13:01:47 +0300 Subject: [PATCH] Added functionality to manually stop sync (fixes #131). This functionality can not be used together with wifi/charging settings. By default, this is set so that syncthing is only active when requested, and can be stopped by the user. --- .../test/syncthing/BatteryReceiverTest.java | 29 ++++++- .../test/syncthing/BootReceiverTest.java | 23 ++++- .../test/syncthing/NetworkReceiverTest.java | 21 +++++ .../test/syncthing/SyncthingRunnableTest.java | 4 + .../test/syncthing/SyncthingServiceTest.java | 87 ++++++++++++++----- .../activities/MainActivity.java | 4 + .../fragments/SettingsFragment.java | 23 ++++- .../syncthing/BatteryReceiver.java | 5 ++ .../syncthing/BootReceiver.java | 5 ++ .../syncthing/NetworkReceiver.java | 3 + .../syncthingandroid/syncthing/RestApi.java | 2 +- .../syncthing/SyncthingService.java | 31 +++++-- .../syncthingandroid/util/RepoObserver.java | 2 +- src/main/res/menu/main.xml | 4 + src/main/res/values/strings.xml | 11 ++- src/main/res/xml/app_settings.xml | 5 ++ 16 files changed, 224 insertions(+), 35 deletions(-) diff --git a/src/androidTest/java/com/nutomic/syncthingandroid/test/syncthing/BatteryReceiverTest.java b/src/androidTest/java/com/nutomic/syncthingandroid/test/syncthing/BatteryReceiverTest.java index 66e6b39c..8a0469c4 100644 --- a/src/androidTest/java/com/nutomic/syncthingandroid/test/syncthing/BatteryReceiverTest.java +++ b/src/androidTest/java/com/nutomic/syncthingandroid/test/syncthing/BatteryReceiverTest.java @@ -1,6 +1,7 @@ package com.nutomic.syncthingandroid.test.syncthing; import android.content.Intent; +import android.preference.PreferenceManager; import android.test.AndroidTestCase; import android.test.suitebuilder.annotation.MediumTest; @@ -18,11 +19,21 @@ public class BatteryReceiverTest extends AndroidTestCase { protected void setUp() throws Exception { super.setUp(); mReceiver = new BatteryReceiver(); - mContext = new MockContext(null); + mContext = new MockContext(getContext()); + } + + @Override + protected void tearDown() throws Exception { + PreferenceManager.getDefaultSharedPreferences(mContext).edit().clear().commit(); + super.tearDown(); } @MediumTest public void testOnReceiveCharging() { + PreferenceManager.getDefaultSharedPreferences(mContext) + .edit() + .putBoolean(SyncthingService.PREF_ALWAYS_RUN_IN_BACKGROUND, true) + .commit(); Intent intent = new Intent(Intent.ACTION_POWER_CONNECTED); mReceiver.onReceive(mContext, intent); @@ -35,6 +46,10 @@ public class BatteryReceiverTest extends AndroidTestCase { @MediumTest public void testOnReceiveNotCharging() { + PreferenceManager.getDefaultSharedPreferences(mContext) + .edit() + .putBoolean(SyncthingService.PREF_ALWAYS_RUN_IN_BACKGROUND, true) + .commit(); Intent intent = new Intent(Intent.ACTION_POWER_DISCONNECTED); mReceiver.onReceive(mContext, intent); @@ -46,4 +61,16 @@ public class BatteryReceiverTest extends AndroidTestCase { mContext.clearReceivedIntents(); } + @MediumTest + public void testOnlyRunInForeground() { + PreferenceManager.getDefaultSharedPreferences(getContext()) + .edit() + .putBoolean(SyncthingService.PREF_ALWAYS_RUN_IN_BACKGROUND, false) + .commit(); + mReceiver.onReceive(mContext, new Intent(Intent.ACTION_POWER_CONNECTED)); + assertEquals(0, mContext.getReceivedIntents().size()); + mReceiver.onReceive(mContext, new Intent(Intent.ACTION_POWER_DISCONNECTED)); + assertEquals(0, mContext.getReceivedIntents().size()); + } + } diff --git a/src/androidTest/java/com/nutomic/syncthingandroid/test/syncthing/BootReceiverTest.java b/src/androidTest/java/com/nutomic/syncthingandroid/test/syncthing/BootReceiverTest.java index 5e756bc7..933cada7 100644 --- a/src/androidTest/java/com/nutomic/syncthingandroid/test/syncthing/BootReceiverTest.java +++ b/src/androidTest/java/com/nutomic/syncthingandroid/test/syncthing/BootReceiverTest.java @@ -1,6 +1,7 @@ package com.nutomic.syncthingandroid.test.syncthing; import android.content.Intent; +import android.preference.PreferenceManager; import android.test.AndroidTestCase; import android.test.suitebuilder.annotation.MediumTest; @@ -21,11 +22,21 @@ public class BootReceiverTest extends AndroidTestCase { protected void setUp() throws Exception { super.setUp(); mReceiver = new BootReceiver(); - mContext = new MockContext(null); + mContext = new MockContext(getContext()); + } + + @Override + protected void tearDown() throws Exception { + PreferenceManager.getDefaultSharedPreferences(mContext).edit().clear().commit(); + super.tearDown(); } @MediumTest public void testOnReceiveCharging() { + PreferenceManager.getDefaultSharedPreferences(mContext) + .edit() + .putBoolean(SyncthingService.PREF_ALWAYS_RUN_IN_BACKGROUND, true) + .commit(); mReceiver.onReceive(mContext, null); assertEquals(1, mContext.getReceivedIntents().size()); @@ -33,4 +44,14 @@ public class BootReceiverTest extends AndroidTestCase { assertEquals(SyncthingService.class.getName(), receivedIntent.getComponent().getClassName()); mContext.clearReceivedIntents(); } + + public void testOnlyRunInForeground() { + PreferenceManager.getDefaultSharedPreferences(getContext()) + .edit() + .putBoolean(SyncthingService.PREF_ALWAYS_RUN_IN_BACKGROUND, false) + .commit(); + mReceiver.onReceive(mContext, null); + assertEquals(0, mContext.getReceivedIntents().size()); + } + } diff --git a/src/androidTest/java/com/nutomic/syncthingandroid/test/syncthing/NetworkReceiverTest.java b/src/androidTest/java/com/nutomic/syncthingandroid/test/syncthing/NetworkReceiverTest.java index 0a2c8138..43209ac4 100644 --- a/src/androidTest/java/com/nutomic/syncthingandroid/test/syncthing/NetworkReceiverTest.java +++ b/src/androidTest/java/com/nutomic/syncthingandroid/test/syncthing/NetworkReceiverTest.java @@ -1,6 +1,7 @@ package com.nutomic.syncthingandroid.test.syncthing; import android.content.Intent; +import android.preference.PreferenceManager; import android.test.AndroidTestCase; import android.test.suitebuilder.annotation.MediumTest; @@ -28,8 +29,18 @@ public class NetworkReceiverTest extends AndroidTestCase { mContext = new MockContext(getContext()); } + @Override + protected void tearDown() throws Exception { + PreferenceManager.getDefaultSharedPreferences(mContext).edit().clear().commit(); + super.tearDown(); + } + @MediumTest public void testOnReceive() { + PreferenceManager.getDefaultSharedPreferences(mContext) + .edit() + .putBoolean(SyncthingService.PREF_ALWAYS_RUN_IN_BACKGROUND, true) + .commit(); mReceiver.onReceive(mContext, null); assertEquals(1, mContext.getReceivedIntents().size()); @@ -40,4 +51,14 @@ public class NetworkReceiverTest extends AndroidTestCase { mContext.clearReceivedIntents(); } + @MediumTest + public void testOnlyRunInForeground() { + PreferenceManager.getDefaultSharedPreferences(getContext()) + .edit() + .putBoolean(SyncthingService.PREF_ALWAYS_RUN_IN_BACKGROUND, false) + .commit(); + mReceiver.onReceive(mContext, null); + assertEquals(0, mContext.getReceivedIntents().size()); + } + } diff --git a/src/androidTest/java/com/nutomic/syncthingandroid/test/syncthing/SyncthingRunnableTest.java b/src/androidTest/java/com/nutomic/syncthingandroid/test/syncthing/SyncthingRunnableTest.java index 3eda8cdc..c6e78677 100644 --- a/src/androidTest/java/com/nutomic/syncthingandroid/test/syncthing/SyncthingRunnableTest.java +++ b/src/androidTest/java/com/nutomic/syncthingandroid/test/syncthing/SyncthingRunnableTest.java @@ -8,6 +8,10 @@ import com.nutomic.syncthingandroid.test.MockContext; import java.io.File; +/** + * NOTE: This test will cause a "syncthing binary crashed" notification, because + * {@code -home " + mContext.getFilesDir()} is run as a "command" and fails. + */ public class SyncthingRunnableTest extends AndroidTestCase { @SmallTest diff --git a/src/androidTest/java/com/nutomic/syncthingandroid/test/syncthing/SyncthingServiceTest.java b/src/androidTest/java/com/nutomic/syncthingandroid/test/syncthing/SyncthingServiceTest.java index 30aa8ec5..fd64ec6f 100644 --- a/src/androidTest/java/com/nutomic/syncthingandroid/test/syncthing/SyncthingServiceTest.java +++ b/src/androidTest/java/com/nutomic/syncthingandroid/test/syncthing/SyncthingServiceTest.java @@ -7,6 +7,7 @@ import android.test.ServiceTestCase; import android.test.suitebuilder.annotation.LargeTest; import android.test.suitebuilder.annotation.MediumTest; import android.test.suitebuilder.annotation.SmallTest; +import android.util.Pair; import com.nutomic.syncthingandroid.syncthing.DeviceStateHolder; import com.nutomic.syncthingandroid.syncthing.SyncthingService; @@ -16,19 +17,27 @@ import com.nutomic.syncthingandroid.test.Util; import java.io.File; import java.io.IOException; +import java.util.ArrayList; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; /** - * FIXME: There are some problems with shutting down the service after tests. It may be that the - * service remains running after short tests. As a workaround, kill the app in Android. - * NOTE: It seems that @link #tearDown()} is not executed if a test fails, so the test data folder - * is not deleted (which may cause following tests to fail). + * These tests assume that syncthing keys have already been generated. If not, tests may fail + * because startup takes too long. + * + * FIXME: These tests are rather fragile and may fail even if they shouldn't. Repeating them + * should fix this. + * NOTE: If a test fails with "expected: but was:", you may have to increase + * {@link #STARTUP_TIME_SECONDS}. */ public class SyncthingServiceTest extends ServiceTestCase { + private static final int STARTUP_TIME_SECONDS = 90; + private Context mContext; + private CountDownLatch mLatch; + public SyncthingServiceTest() { super(SyncthingService.class); } @@ -37,19 +46,18 @@ public class SyncthingServiceTest extends ServiceTestCase { protected void setUp() throws Exception { super.setUp(); mContext = new MockContext(getContext()); - setContext(mContext); } @Override protected void tearDown() throws Exception { - Util.deleteRecursive(getContext().getFilesDir()); + Util.deleteRecursive(mContext.getFilesDir()); PreferenceManager.getDefaultSharedPreferences(getContext()).edit().clear().commit(); super.tearDown(); } @LargeTest public void testStartService() throws InterruptedException { - startService(new Intent(mContext, SyncthingService.class)); + startService(new Intent(getContext(), SyncthingService.class)); final CountDownLatch latch = new CountDownLatch(2); getService().registerOnWebGuiAvailableListener(new SyncthingService.OnWebGuiAvailableListener() { @Override @@ -70,12 +78,14 @@ public class SyncthingServiceTest extends ServiceTestCase { @SmallTest public void testFirstStart() { + setContext(mContext); startService(new Intent(mContext, SyncthingService.class)); assertTrue(getService().isFirstStart()); } @MediumTest public void testNotFirstStart() throws IOException { + setContext(mContext); startService(new Intent(mContext, SyncthingService.class)); new File(mContext.getFilesDir(), SyncthingService.PUBLIC_KEY_FILE).createNewFile(); assertFalse(getService().isFirstStart()); @@ -84,7 +94,7 @@ public class SyncthingServiceTest extends ServiceTestCase { @SmallTest public void testBindService() throws InterruptedException { SyncthingServiceBinder binder = (SyncthingServiceBinder) - bindService(new Intent(mContext, SyncthingService.class)); + bindService(new Intent(getContext(), SyncthingService.class)); SyncthingService service = binder.getService(); final CountDownLatch latch = new CountDownLatch(2); getService().registerOnWebGuiAvailableListener(new SyncthingService.OnWebGuiAvailableListener() { @@ -109,6 +119,8 @@ public class SyncthingServiceTest extends ServiceTestCase { @Override public void onApiChange(SyncthingService.State currentState) { + mLatch.countDown(); + mLastState = currentState; } @@ -122,9 +134,10 @@ public class SyncthingServiceTest extends ServiceTestCase { @MediumTest public void testStatesAllRequired() throws InterruptedException { - setupStatesTest(true, true); + setupStatesTest(true, true, true); assertState(true, true, SyncthingService.State.ACTIVE); + assertState(true, false, SyncthingService.State.DISABLED); assertState(false, true, SyncthingService.State.DISABLED); assertState(false, false, SyncthingService.State.DISABLED); @@ -132,27 +145,29 @@ public class SyncthingServiceTest extends ServiceTestCase { @MediumTest public void testStatesWifiRequired() throws InterruptedException { - setupStatesTest(true, false); + setupStatesTest(true, true, false); assertState(true, true, SyncthingService.State.ACTIVE); - assertState(true, false, SyncthingService.State.DISABLED); assertState(false, true, SyncthingService.State.ACTIVE); + + assertState(true, false, SyncthingService.State.DISABLED); assertState(false, false, SyncthingService.State.DISABLED); } @MediumTest public void testStatesChargingRequired() throws InterruptedException { - setupStatesTest(false, true); + setupStatesTest(true, false, true); assertState(true, true, SyncthingService.State.ACTIVE); assertState(true, false, SyncthingService.State.ACTIVE); + assertState(false, true, SyncthingService.State.DISABLED); assertState(false, false, SyncthingService.State.DISABLED); } @MediumTest public void testStatesNoneRequired() throws InterruptedException { - setupStatesTest(false, false); + setupStatesTest(true, false, false); assertState(true, true, SyncthingService.State.ACTIVE); assertState(true, false, SyncthingService.State.ACTIVE); @@ -162,25 +177,55 @@ public class SyncthingServiceTest extends ServiceTestCase { public void assertState(boolean charging, boolean wifi, SyncthingService.State expected) throws InterruptedException { - Intent i = new Intent(mContext, SyncthingService.class); + Intent i = new Intent(getContext(), SyncthingService.class); i.putExtra(DeviceStateHolder.EXTRA_IS_CHARGING, charging); i.putExtra(DeviceStateHolder.EXTRA_HAS_WIFI, wifi); + mLatch = new CountDownLatch(1); startService(i); // Wait for service to react to preference change. - Thread.sleep(7500); + mLatch.await(1, TimeUnit.SECONDS); assertEquals(expected, mListener.getLastState()); } - public void setupStatesTest(boolean syncOnlyWifi, boolean syncOnlyCharging) - throws InterruptedException { - PreferenceManager.getDefaultSharedPreferences(getContext()).edit() + public void setupStatesTest(boolean alwaysRunInBackground, + boolean syncOnlyWifi, boolean syncOnlyCharging) throws InterruptedException { + PreferenceManager.getDefaultSharedPreferences(getContext()) + .edit() + .putBoolean(SyncthingService.PREF_ALWAYS_RUN_IN_BACKGROUND, alwaysRunInBackground) .putBoolean(SyncthingService.PREF_SYNC_ONLY_WIFI, syncOnlyWifi) .putBoolean(SyncthingService.PREF_SYNC_ONLY_CHARGING, syncOnlyCharging) .commit(); - // Wait for service to react to preference change. - Thread.sleep(1000); + startService(new Intent(getContext(), SyncthingService.class)); + // 3 calls plus 1 call immediately when registering. + mLatch = new CountDownLatch(4); getService().registerOnApiChangeListener(mListener); - assertEquals(SyncthingService.State.INIT, mListener.getLastState()); + if (mListener.getLastState() != SyncthingService.State.ACTIVE) { + // Wait for service to start. + mLatch.await(STARTUP_TIME_SECONDS, TimeUnit.SECONDS); + assertEquals(SyncthingService.State.ACTIVE, mListener.getLastState()); + } } + + /** + * For all possible settings and charging/wifi states, service should be active. + */ + @LargeTest + public void testOnlyForeground() throws InterruptedException { + ArrayList> values = new ArrayList<>(); + values.add(new Pair(true, true)); + values.add(new Pair(true, false)); + values.add(new Pair(false, true)); + values.add(new Pair(false, false)); + + for (Pair v : values) { + setupStatesTest(false, v.first, v.second); + + assertState(true, true, SyncthingService.State.ACTIVE); + assertState(true, false, SyncthingService.State.ACTIVE); + assertState(false, true, SyncthingService.State.ACTIVE); + assertState(false, false, SyncthingService.State.ACTIVE); + } + } + } diff --git a/src/main/java/com/nutomic/syncthingandroid/activities/MainActivity.java b/src/main/java/com/nutomic/syncthingandroid/activities/MainActivity.java index b9e568dc..d58f4b51 100644 --- a/src/main/java/com/nutomic/syncthingandroid/activities/MainActivity.java +++ b/src/main/java/com/nutomic/syncthingandroid/activities/MainActivity.java @@ -247,6 +247,7 @@ public class MainActivity extends SyncthingActivity public boolean onPrepareOptionsMenu(Menu menu) { boolean drawerOpen = mDrawerLayout.isDrawerOpen(findViewById(R.id.drawer)); menu.findItem(R.id.share_node_id).setVisible(drawerOpen); + menu.findItem(R.id.exit).setVisible(!SyncthingService.alwaysRunInBackground(this)); return true; } @@ -277,6 +278,9 @@ public class MainActivity extends SyncthingActivity startActivity(new Intent(this, SettingsActivity.class) .setAction(SettingsActivity.ACTION_APP_SETTINGS_FRAGMENT)); return true; + case R.id.exit: + stopService(new Intent(this, SyncthingService.class)); + finish(); default: return super.onOptionsItemSelected(item); } diff --git a/src/main/java/com/nutomic/syncthingandroid/fragments/SettingsFragment.java b/src/main/java/com/nutomic/syncthingandroid/fragments/SettingsFragment.java index a85b5c52..9e09974a 100644 --- a/src/main/java/com/nutomic/syncthingandroid/fragments/SettingsFragment.java +++ b/src/main/java/com/nutomic/syncthingandroid/fragments/SettingsFragment.java @@ -29,6 +29,8 @@ public class SettingsFragment extends PreferenceFragment private static final String SYNCTHING_VERSION_KEY = "syncthing_version"; + private CheckBoxPreference mAlwaysRunInBackground; + private CheckBoxPreference mSyncOnlyCharging; private CheckBoxPreference mSyncOnlyWifi; @@ -96,18 +98,24 @@ public class SettingsFragment extends PreferenceFragment addPreferencesFromResource(R.xml.app_settings); PreferenceScreen screen = getPreferenceScreen(); + mAlwaysRunInBackground = (CheckBoxPreference) + findPreference(SyncthingService.PREF_ALWAYS_RUN_IN_BACKGROUND); mSyncOnlyCharging = (CheckBoxPreference) findPreference(SyncthingService.PREF_SYNC_ONLY_CHARGING); - mSyncOnlyCharging.setOnPreferenceChangeListener(this); mSyncOnlyWifi = (CheckBoxPreference) findPreference(SyncthingService.PREF_SYNC_ONLY_WIFI); + mVersion = screen.findPreference(SYNCTHING_VERSION_KEY); + mOptionsScreen = (PreferenceScreen) screen.findPreference(SYNCTHING_OPTIONS_KEY); + mGuiScreen = (PreferenceScreen) screen.findPreference(SYNCTHING_GUI_KEY); + + mAlwaysRunInBackground.setOnPreferenceChangeListener(this); + mSyncOnlyCharging.setOnPreferenceChangeListener(this); mSyncOnlyWifi.setOnPreferenceChangeListener(this); + // Force summary update and wifi/charging preferences enable/disable. + onPreferenceChange(mAlwaysRunInBackground, mAlwaysRunInBackground.isChecked()); Preference sttrace = findPreference("sttrace"); sttrace.setOnPreferenceChangeListener(this); sttrace.setSummary(PreferenceManager .getDefaultSharedPreferences(getActivity()).getString("sttrace", "")); - mVersion = screen.findPreference(SYNCTHING_VERSION_KEY); - mOptionsScreen = (PreferenceScreen) screen.findPreference(SYNCTHING_OPTIONS_KEY); - mGuiScreen = (PreferenceScreen) screen.findPreference(SYNCTHING_GUI_KEY); } @Override @@ -146,6 +154,13 @@ public class SettingsFragment extends PreferenceFragment if (preference.equals(mSyncOnlyCharging) || preference.equals(mSyncOnlyWifi)) { mSyncthingService.updateState(); + } else if (preference.equals(mAlwaysRunInBackground)) { + preference.setSummary(((Boolean) o) + ? R.string.always_run_in_background_enabled + : R.string.always_run_in_background_disabled); + mSyncOnlyCharging.setEnabled((Boolean) o); + mSyncOnlyWifi.setEnabled((Boolean) o); + } else if (mOptionsScreen.findPreference(preference.getKey()) != null) { mSyncthingService.getApi().setValue(RestApi.TYPE_OPTIONS, preference.getKey(), o, preference.getKey().equals("ListenAddress"), getActivity()); diff --git a/src/main/java/com/nutomic/syncthingandroid/syncthing/BatteryReceiver.java b/src/main/java/com/nutomic/syncthingandroid/syncthing/BatteryReceiver.java index 27cea9e2..5d307414 100644 --- a/src/main/java/com/nutomic/syncthingandroid/syncthing/BatteryReceiver.java +++ b/src/main/java/com/nutomic/syncthingandroid/syncthing/BatteryReceiver.java @@ -3,6 +3,8 @@ package com.nutomic.syncthingandroid.syncthing; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; +import android.content.SharedPreferences; +import android.preference.PreferenceManager; import android.util.Log; /** @@ -14,6 +16,9 @@ public class BatteryReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { + if (!SyncthingService.alwaysRunInBackground(context)) + return; + boolean isCharging = Intent.ACTION_POWER_CONNECTED.equals(intent.getAction()); Log.v(TAG, "Received charger " + (isCharging ? "connected" : "disconnected") + " event"); Intent i = new Intent(context, SyncthingService.class); diff --git a/src/main/java/com/nutomic/syncthingandroid/syncthing/BootReceiver.java b/src/main/java/com/nutomic/syncthingandroid/syncthing/BootReceiver.java index 5cfc756f..2632a036 100644 --- a/src/main/java/com/nutomic/syncthingandroid/syncthing/BootReceiver.java +++ b/src/main/java/com/nutomic/syncthingandroid/syncthing/BootReceiver.java @@ -3,11 +3,16 @@ package com.nutomic.syncthingandroid.syncthing; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; +import android.content.SharedPreferences; +import android.preference.PreferenceManager; public class BootReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { + if (!SyncthingService.alwaysRunInBackground(context)) + return; + context.startService(new Intent(context, SyncthingService.class)); } diff --git a/src/main/java/com/nutomic/syncthingandroid/syncthing/NetworkReceiver.java b/src/main/java/com/nutomic/syncthingandroid/syncthing/NetworkReceiver.java index 9f52bd7b..dc967857 100644 --- a/src/main/java/com/nutomic/syncthingandroid/syncthing/NetworkReceiver.java +++ b/src/main/java/com/nutomic/syncthingandroid/syncthing/NetworkReceiver.java @@ -16,6 +16,9 @@ public class NetworkReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { + if (!SyncthingService.alwaysRunInBackground(context)) + return; + ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); NetworkInfo wifiInfo = cm.getNetworkInfo(ConnectivityManager.TYPE_WIFI); diff --git a/src/main/java/com/nutomic/syncthingandroid/syncthing/RestApi.java b/src/main/java/com/nutomic/syncthingandroid/syncthing/RestApi.java index aaa4da04..f9ea7ea0 100644 --- a/src/main/java/com/nutomic/syncthingandroid/syncthing/RestApi.java +++ b/src/main/java/com/nutomic/syncthingandroid/syncthing/RestApi.java @@ -849,7 +849,7 @@ public class RestApi implements SyncthingService.OnWebGuiAvailableListener, normalized = json.optString("id", null); error = json.optString("error", null); } catch (JSONException e) { - Log.d(TAG, "Failed to parse normalized node ID JSON", e); + Log.w(TAG, "Failed to parse normalized node ID JSON", e); } listener.onNodeIdNormalized(normalized, error); } diff --git a/src/main/java/com/nutomic/syncthingandroid/syncthing/SyncthingService.java b/src/main/java/com/nutomic/syncthingandroid/syncthing/SyncthingService.java index 046ab9e8..82b5bf17 100644 --- a/src/main/java/com/nutomic/syncthingandroid/syncthing/SyncthingService.java +++ b/src/main/java/com/nutomic/syncthingandroid/syncthing/SyncthingService.java @@ -3,6 +3,7 @@ package com.nutomic.syncthingandroid.syncthing; import android.app.Activity; import android.app.AlertDialog; import android.app.Service; +import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.content.IntentFilter; @@ -57,6 +58,8 @@ public class SyncthingService extends Service { */ public static final String BINARY_NAME = "lib/libsyncthing.so"; + public static final String PREF_ALWAYS_RUN_IN_BACKGROUND = "always_run_in_background"; + public static final String PREF_SYNC_ONLY_WIFI = "sync_only_wifi"; public static final String PREF_SYNC_ONLY_CHARGING = "sync_only_charging"; @@ -141,13 +144,23 @@ public class SyncthingService extends Service { * called. */ public void updateState() { - SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this); - boolean prefStopMobileData = prefs.getBoolean(PREF_SYNC_ONLY_WIFI, false); - boolean prefStopNotCharging = prefs.getBoolean(PREF_SYNC_ONLY_CHARGING, false); + boolean shouldRun; + if (!alwaysRunInBackground(this)) { + // Always run, ignoring wifi/charging state. + shouldRun = true; + } + else { + // Check wifi/charging state against preferences and start if ok. + SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this); + boolean prefStopMobileData = prefs.getBoolean(PREF_SYNC_ONLY_WIFI, false); + boolean prefStopNotCharging = prefs.getBoolean(PREF_SYNC_ONLY_CHARGING, false); + + shouldRun = (mDeviceStateHolder.isCharging() || !prefStopNotCharging) && + (mDeviceStateHolder.isWifiConnected() || !prefStopMobileData); + } // Start syncthing. - if ((mDeviceStateHolder.isCharging() || !prefStopNotCharging) && - (mDeviceStateHolder.isWifiConnected() || !prefStopMobileData)) { + if (shouldRun) { if (mCurrentState == State.ACTIVE || mCurrentState == State.STARTING) { mStopScheduled = false; return; @@ -396,4 +409,12 @@ public class SyncthingService extends Service { return mConfig.getWebGuiUrl(); } + /** + * Returns the value of "always_run_in_background" preference. + */ + public static boolean alwaysRunInBackground(Context context) { + SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context); + return sp.getBoolean(PREF_ALWAYS_RUN_IN_BACKGROUND, false); + } + } diff --git a/src/main/java/com/nutomic/syncthingandroid/util/RepoObserver.java b/src/main/java/com/nutomic/syncthingandroid/util/RepoObserver.java index 4482a2dd..b1a4e127 100644 --- a/src/main/java/com/nutomic/syncthingandroid/util/RepoObserver.java +++ b/src/main/java/com/nutomic/syncthingandroid/util/RepoObserver.java @@ -56,7 +56,7 @@ public class RepoObserver extends FileObserver { } }); - mChilds = new ArrayList<>(directories.length); + mChilds = new ArrayList<>(); for (File f : directories) { mChilds.add(new RepoObserver(mListener, mRepo, path + "/" + f.getName())); } diff --git a/src/main/res/menu/main.xml b/src/main/res/menu/main.xml index a5c00729..96054cf0 100644 --- a/src/main/res/menu/main.xml +++ b/src/main/res/menu/main.xml @@ -28,4 +28,8 @@ android:id="@+id/settings" android:title="@string/settings_title" /> + + diff --git a/src/main/res/values/strings.xml b/src/main/res/values/strings.xml index d0620da9..49f5f45e 100644 --- a/src/main/res/values/strings.xml +++ b/src/main/res/values/strings.xml @@ -199,6 +199,15 @@ Please report any problems you encounter via Github. Settings + + Always run in background + + + Syncthing always runs in the background, according to preferences below. + + + Syncthing only runs when explicitly started, and can be stopped by menu button. + Sync only when charging Sync only on wifi @@ -252,7 +261,7 @@ If this error persists, try restarting your device. Change Settings - + Exit diff --git a/src/main/res/xml/app_settings.xml b/src/main/res/xml/app_settings.xml index 39f77b69..6d884657 100644 --- a/src/main/res/xml/app_settings.xml +++ b/src/main/res/xml/app_settings.xml @@ -1,6 +1,11 @@ + +