mirror of
https://github.com/syncthing/syncthing-android.git
synced 2024-11-26 14:21:16 +00:00
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.
This commit is contained in:
parent
e76f354ddd
commit
f383393ce4
16 changed files with 224 additions and 35 deletions
|
@ -1,6 +1,7 @@
|
||||||
package com.nutomic.syncthingandroid.test.syncthing;
|
package com.nutomic.syncthingandroid.test.syncthing;
|
||||||
|
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
|
import android.preference.PreferenceManager;
|
||||||
import android.test.AndroidTestCase;
|
import android.test.AndroidTestCase;
|
||||||
import android.test.suitebuilder.annotation.MediumTest;
|
import android.test.suitebuilder.annotation.MediumTest;
|
||||||
|
|
||||||
|
@ -18,11 +19,21 @@ public class BatteryReceiverTest extends AndroidTestCase {
|
||||||
protected void setUp() throws Exception {
|
protected void setUp() throws Exception {
|
||||||
super.setUp();
|
super.setUp();
|
||||||
mReceiver = new BatteryReceiver();
|
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
|
@MediumTest
|
||||||
public void testOnReceiveCharging() {
|
public void testOnReceiveCharging() {
|
||||||
|
PreferenceManager.getDefaultSharedPreferences(mContext)
|
||||||
|
.edit()
|
||||||
|
.putBoolean(SyncthingService.PREF_ALWAYS_RUN_IN_BACKGROUND, true)
|
||||||
|
.commit();
|
||||||
Intent intent = new Intent(Intent.ACTION_POWER_CONNECTED);
|
Intent intent = new Intent(Intent.ACTION_POWER_CONNECTED);
|
||||||
|
|
||||||
mReceiver.onReceive(mContext, intent);
|
mReceiver.onReceive(mContext, intent);
|
||||||
|
@ -35,6 +46,10 @@ public class BatteryReceiverTest extends AndroidTestCase {
|
||||||
|
|
||||||
@MediumTest
|
@MediumTest
|
||||||
public void testOnReceiveNotCharging() {
|
public void testOnReceiveNotCharging() {
|
||||||
|
PreferenceManager.getDefaultSharedPreferences(mContext)
|
||||||
|
.edit()
|
||||||
|
.putBoolean(SyncthingService.PREF_ALWAYS_RUN_IN_BACKGROUND, true)
|
||||||
|
.commit();
|
||||||
Intent intent = new Intent(Intent.ACTION_POWER_DISCONNECTED);
|
Intent intent = new Intent(Intent.ACTION_POWER_DISCONNECTED);
|
||||||
|
|
||||||
mReceiver.onReceive(mContext, intent);
|
mReceiver.onReceive(mContext, intent);
|
||||||
|
@ -46,4 +61,16 @@ public class BatteryReceiverTest extends AndroidTestCase {
|
||||||
mContext.clearReceivedIntents();
|
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());
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package com.nutomic.syncthingandroid.test.syncthing;
|
package com.nutomic.syncthingandroid.test.syncthing;
|
||||||
|
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
|
import android.preference.PreferenceManager;
|
||||||
import android.test.AndroidTestCase;
|
import android.test.AndroidTestCase;
|
||||||
import android.test.suitebuilder.annotation.MediumTest;
|
import android.test.suitebuilder.annotation.MediumTest;
|
||||||
|
|
||||||
|
@ -21,11 +22,21 @@ public class BootReceiverTest extends AndroidTestCase {
|
||||||
protected void setUp() throws Exception {
|
protected void setUp() throws Exception {
|
||||||
super.setUp();
|
super.setUp();
|
||||||
mReceiver = new BootReceiver();
|
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
|
@MediumTest
|
||||||
public void testOnReceiveCharging() {
|
public void testOnReceiveCharging() {
|
||||||
|
PreferenceManager.getDefaultSharedPreferences(mContext)
|
||||||
|
.edit()
|
||||||
|
.putBoolean(SyncthingService.PREF_ALWAYS_RUN_IN_BACKGROUND, true)
|
||||||
|
.commit();
|
||||||
mReceiver.onReceive(mContext, null);
|
mReceiver.onReceive(mContext, null);
|
||||||
assertEquals(1, mContext.getReceivedIntents().size());
|
assertEquals(1, mContext.getReceivedIntents().size());
|
||||||
|
|
||||||
|
@ -33,4 +44,14 @@ public class BootReceiverTest extends AndroidTestCase {
|
||||||
assertEquals(SyncthingService.class.getName(), receivedIntent.getComponent().getClassName());
|
assertEquals(SyncthingService.class.getName(), receivedIntent.getComponent().getClassName());
|
||||||
mContext.clearReceivedIntents();
|
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());
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package com.nutomic.syncthingandroid.test.syncthing;
|
package com.nutomic.syncthingandroid.test.syncthing;
|
||||||
|
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
|
import android.preference.PreferenceManager;
|
||||||
import android.test.AndroidTestCase;
|
import android.test.AndroidTestCase;
|
||||||
import android.test.suitebuilder.annotation.MediumTest;
|
import android.test.suitebuilder.annotation.MediumTest;
|
||||||
|
|
||||||
|
@ -28,8 +29,18 @@ public class NetworkReceiverTest extends AndroidTestCase {
|
||||||
mContext = new MockContext(getContext());
|
mContext = new MockContext(getContext());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void tearDown() throws Exception {
|
||||||
|
PreferenceManager.getDefaultSharedPreferences(mContext).edit().clear().commit();
|
||||||
|
super.tearDown();
|
||||||
|
}
|
||||||
|
|
||||||
@MediumTest
|
@MediumTest
|
||||||
public void testOnReceive() {
|
public void testOnReceive() {
|
||||||
|
PreferenceManager.getDefaultSharedPreferences(mContext)
|
||||||
|
.edit()
|
||||||
|
.putBoolean(SyncthingService.PREF_ALWAYS_RUN_IN_BACKGROUND, true)
|
||||||
|
.commit();
|
||||||
mReceiver.onReceive(mContext, null);
|
mReceiver.onReceive(mContext, null);
|
||||||
assertEquals(1, mContext.getReceivedIntents().size());
|
assertEquals(1, mContext.getReceivedIntents().size());
|
||||||
|
|
||||||
|
@ -40,4 +51,14 @@ public class NetworkReceiverTest extends AndroidTestCase {
|
||||||
mContext.clearReceivedIntents();
|
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());
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,10 @@ import com.nutomic.syncthingandroid.test.MockContext;
|
||||||
|
|
||||||
import java.io.File;
|
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 {
|
public class SyncthingRunnableTest extends AndroidTestCase {
|
||||||
|
|
||||||
@SmallTest
|
@SmallTest
|
||||||
|
|
|
@ -7,6 +7,7 @@ import android.test.ServiceTestCase;
|
||||||
import android.test.suitebuilder.annotation.LargeTest;
|
import android.test.suitebuilder.annotation.LargeTest;
|
||||||
import android.test.suitebuilder.annotation.MediumTest;
|
import android.test.suitebuilder.annotation.MediumTest;
|
||||||
import android.test.suitebuilder.annotation.SmallTest;
|
import android.test.suitebuilder.annotation.SmallTest;
|
||||||
|
import android.util.Pair;
|
||||||
|
|
||||||
import com.nutomic.syncthingandroid.syncthing.DeviceStateHolder;
|
import com.nutomic.syncthingandroid.syncthing.DeviceStateHolder;
|
||||||
import com.nutomic.syncthingandroid.syncthing.SyncthingService;
|
import com.nutomic.syncthingandroid.syncthing.SyncthingService;
|
||||||
|
@ -16,19 +17,27 @@ import com.nutomic.syncthingandroid.test.Util;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.concurrent.CountDownLatch;
|
import java.util.concurrent.CountDownLatch;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* FIXME: There are some problems with shutting down the service after tests. It may be that the
|
* These tests assume that syncthing keys have already been generated. If not, tests may fail
|
||||||
* service remains running after short tests. As a workaround, kill the app in Android.
|
* because startup takes too long.
|
||||||
* 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).
|
* 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:<ACTIVE> but was:<INIT>", you may have to increase
|
||||||
|
* {@link #STARTUP_TIME_SECONDS}.
|
||||||
*/
|
*/
|
||||||
public class SyncthingServiceTest extends ServiceTestCase<SyncthingService> {
|
public class SyncthingServiceTest extends ServiceTestCase<SyncthingService> {
|
||||||
|
|
||||||
|
private static final int STARTUP_TIME_SECONDS = 90;
|
||||||
|
|
||||||
private Context mContext;
|
private Context mContext;
|
||||||
|
|
||||||
|
private CountDownLatch mLatch;
|
||||||
|
|
||||||
public SyncthingServiceTest() {
|
public SyncthingServiceTest() {
|
||||||
super(SyncthingService.class);
|
super(SyncthingService.class);
|
||||||
}
|
}
|
||||||
|
@ -37,19 +46,18 @@ public class SyncthingServiceTest extends ServiceTestCase<SyncthingService> {
|
||||||
protected void setUp() throws Exception {
|
protected void setUp() throws Exception {
|
||||||
super.setUp();
|
super.setUp();
|
||||||
mContext = new MockContext(getContext());
|
mContext = new MockContext(getContext());
|
||||||
setContext(mContext);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void tearDown() throws Exception {
|
protected void tearDown() throws Exception {
|
||||||
Util.deleteRecursive(getContext().getFilesDir());
|
Util.deleteRecursive(mContext.getFilesDir());
|
||||||
PreferenceManager.getDefaultSharedPreferences(getContext()).edit().clear().commit();
|
PreferenceManager.getDefaultSharedPreferences(getContext()).edit().clear().commit();
|
||||||
super.tearDown();
|
super.tearDown();
|
||||||
}
|
}
|
||||||
|
|
||||||
@LargeTest
|
@LargeTest
|
||||||
public void testStartService() throws InterruptedException {
|
public void testStartService() throws InterruptedException {
|
||||||
startService(new Intent(mContext, SyncthingService.class));
|
startService(new Intent(getContext(), SyncthingService.class));
|
||||||
final CountDownLatch latch = new CountDownLatch(2);
|
final CountDownLatch latch = new CountDownLatch(2);
|
||||||
getService().registerOnWebGuiAvailableListener(new SyncthingService.OnWebGuiAvailableListener() {
|
getService().registerOnWebGuiAvailableListener(new SyncthingService.OnWebGuiAvailableListener() {
|
||||||
@Override
|
@Override
|
||||||
|
@ -70,12 +78,14 @@ public class SyncthingServiceTest extends ServiceTestCase<SyncthingService> {
|
||||||
|
|
||||||
@SmallTest
|
@SmallTest
|
||||||
public void testFirstStart() {
|
public void testFirstStart() {
|
||||||
|
setContext(mContext);
|
||||||
startService(new Intent(mContext, SyncthingService.class));
|
startService(new Intent(mContext, SyncthingService.class));
|
||||||
assertTrue(getService().isFirstStart());
|
assertTrue(getService().isFirstStart());
|
||||||
}
|
}
|
||||||
|
|
||||||
@MediumTest
|
@MediumTest
|
||||||
public void testNotFirstStart() throws IOException {
|
public void testNotFirstStart() throws IOException {
|
||||||
|
setContext(mContext);
|
||||||
startService(new Intent(mContext, SyncthingService.class));
|
startService(new Intent(mContext, SyncthingService.class));
|
||||||
new File(mContext.getFilesDir(), SyncthingService.PUBLIC_KEY_FILE).createNewFile();
|
new File(mContext.getFilesDir(), SyncthingService.PUBLIC_KEY_FILE).createNewFile();
|
||||||
assertFalse(getService().isFirstStart());
|
assertFalse(getService().isFirstStart());
|
||||||
|
@ -84,7 +94,7 @@ public class SyncthingServiceTest extends ServiceTestCase<SyncthingService> {
|
||||||
@SmallTest
|
@SmallTest
|
||||||
public void testBindService() throws InterruptedException {
|
public void testBindService() throws InterruptedException {
|
||||||
SyncthingServiceBinder binder = (SyncthingServiceBinder)
|
SyncthingServiceBinder binder = (SyncthingServiceBinder)
|
||||||
bindService(new Intent(mContext, SyncthingService.class));
|
bindService(new Intent(getContext(), SyncthingService.class));
|
||||||
SyncthingService service = binder.getService();
|
SyncthingService service = binder.getService();
|
||||||
final CountDownLatch latch = new CountDownLatch(2);
|
final CountDownLatch latch = new CountDownLatch(2);
|
||||||
getService().registerOnWebGuiAvailableListener(new SyncthingService.OnWebGuiAvailableListener() {
|
getService().registerOnWebGuiAvailableListener(new SyncthingService.OnWebGuiAvailableListener() {
|
||||||
|
@ -109,6 +119,8 @@ public class SyncthingServiceTest extends ServiceTestCase<SyncthingService> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onApiChange(SyncthingService.State currentState) {
|
public void onApiChange(SyncthingService.State currentState) {
|
||||||
|
mLatch.countDown();
|
||||||
|
|
||||||
mLastState = currentState;
|
mLastState = currentState;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -122,9 +134,10 @@ public class SyncthingServiceTest extends ServiceTestCase<SyncthingService> {
|
||||||
|
|
||||||
@MediumTest
|
@MediumTest
|
||||||
public void testStatesAllRequired() throws InterruptedException {
|
public void testStatesAllRequired() throws InterruptedException {
|
||||||
setupStatesTest(true, true);
|
setupStatesTest(true, true, true);
|
||||||
|
|
||||||
assertState(true, true, SyncthingService.State.ACTIVE);
|
assertState(true, true, SyncthingService.State.ACTIVE);
|
||||||
|
|
||||||
assertState(true, false, SyncthingService.State.DISABLED);
|
assertState(true, false, SyncthingService.State.DISABLED);
|
||||||
assertState(false, true, SyncthingService.State.DISABLED);
|
assertState(false, true, SyncthingService.State.DISABLED);
|
||||||
assertState(false, false, SyncthingService.State.DISABLED);
|
assertState(false, false, SyncthingService.State.DISABLED);
|
||||||
|
@ -132,27 +145,29 @@ public class SyncthingServiceTest extends ServiceTestCase<SyncthingService> {
|
||||||
|
|
||||||
@MediumTest
|
@MediumTest
|
||||||
public void testStatesWifiRequired() throws InterruptedException {
|
public void testStatesWifiRequired() throws InterruptedException {
|
||||||
setupStatesTest(true, false);
|
setupStatesTest(true, true, false);
|
||||||
|
|
||||||
assertState(true, true, SyncthingService.State.ACTIVE);
|
assertState(true, true, SyncthingService.State.ACTIVE);
|
||||||
assertState(true, false, SyncthingService.State.DISABLED);
|
|
||||||
assertState(false, true, SyncthingService.State.ACTIVE);
|
assertState(false, true, SyncthingService.State.ACTIVE);
|
||||||
|
|
||||||
|
assertState(true, false, SyncthingService.State.DISABLED);
|
||||||
assertState(false, false, SyncthingService.State.DISABLED);
|
assertState(false, false, SyncthingService.State.DISABLED);
|
||||||
}
|
}
|
||||||
|
|
||||||
@MediumTest
|
@MediumTest
|
||||||
public void testStatesChargingRequired() throws InterruptedException {
|
public void testStatesChargingRequired() throws InterruptedException {
|
||||||
setupStatesTest(false, true);
|
setupStatesTest(true, false, true);
|
||||||
|
|
||||||
assertState(true, true, SyncthingService.State.ACTIVE);
|
assertState(true, true, SyncthingService.State.ACTIVE);
|
||||||
assertState(true, false, SyncthingService.State.ACTIVE);
|
assertState(true, false, SyncthingService.State.ACTIVE);
|
||||||
|
|
||||||
assertState(false, true, SyncthingService.State.DISABLED);
|
assertState(false, true, SyncthingService.State.DISABLED);
|
||||||
assertState(false, false, SyncthingService.State.DISABLED);
|
assertState(false, false, SyncthingService.State.DISABLED);
|
||||||
}
|
}
|
||||||
|
|
||||||
@MediumTest
|
@MediumTest
|
||||||
public void testStatesNoneRequired() throws InterruptedException {
|
public void testStatesNoneRequired() throws InterruptedException {
|
||||||
setupStatesTest(false, false);
|
setupStatesTest(true, false, false);
|
||||||
|
|
||||||
assertState(true, true, SyncthingService.State.ACTIVE);
|
assertState(true, true, SyncthingService.State.ACTIVE);
|
||||||
assertState(true, false, SyncthingService.State.ACTIVE);
|
assertState(true, false, SyncthingService.State.ACTIVE);
|
||||||
|
@ -162,25 +177,55 @@ public class SyncthingServiceTest extends ServiceTestCase<SyncthingService> {
|
||||||
|
|
||||||
public void assertState(boolean charging, boolean wifi, SyncthingService.State expected)
|
public void assertState(boolean charging, boolean wifi, SyncthingService.State expected)
|
||||||
throws InterruptedException {
|
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_IS_CHARGING, charging);
|
||||||
i.putExtra(DeviceStateHolder.EXTRA_HAS_WIFI, wifi);
|
i.putExtra(DeviceStateHolder.EXTRA_HAS_WIFI, wifi);
|
||||||
|
mLatch = new CountDownLatch(1);
|
||||||
startService(i);
|
startService(i);
|
||||||
// Wait for service to react to preference change.
|
// Wait for service to react to preference change.
|
||||||
Thread.sleep(7500);
|
mLatch.await(1, TimeUnit.SECONDS);
|
||||||
assertEquals(expected, mListener.getLastState());
|
assertEquals(expected, mListener.getLastState());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setupStatesTest(boolean syncOnlyWifi, boolean syncOnlyCharging)
|
public void setupStatesTest(boolean alwaysRunInBackground,
|
||||||
throws InterruptedException {
|
boolean syncOnlyWifi, boolean syncOnlyCharging) throws InterruptedException {
|
||||||
PreferenceManager.getDefaultSharedPreferences(getContext()).edit()
|
PreferenceManager.getDefaultSharedPreferences(getContext())
|
||||||
|
.edit()
|
||||||
|
.putBoolean(SyncthingService.PREF_ALWAYS_RUN_IN_BACKGROUND, alwaysRunInBackground)
|
||||||
.putBoolean(SyncthingService.PREF_SYNC_ONLY_WIFI, syncOnlyWifi)
|
.putBoolean(SyncthingService.PREF_SYNC_ONLY_WIFI, syncOnlyWifi)
|
||||||
.putBoolean(SyncthingService.PREF_SYNC_ONLY_CHARGING, syncOnlyCharging)
|
.putBoolean(SyncthingService.PREF_SYNC_ONLY_CHARGING, syncOnlyCharging)
|
||||||
.commit();
|
.commit();
|
||||||
// Wait for service to react to preference change.
|
|
||||||
Thread.sleep(1000);
|
|
||||||
startService(new Intent(getContext(), SyncthingService.class));
|
startService(new Intent(getContext(), SyncthingService.class));
|
||||||
|
// 3 calls plus 1 call immediately when registering.
|
||||||
|
mLatch = new CountDownLatch(4);
|
||||||
getService().registerOnApiChangeListener(mListener);
|
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<Pair<Boolean, Boolean>> 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<Boolean, Boolean> 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -247,6 +247,7 @@ public class MainActivity extends SyncthingActivity
|
||||||
public boolean onPrepareOptionsMenu(Menu menu) {
|
public boolean onPrepareOptionsMenu(Menu menu) {
|
||||||
boolean drawerOpen = mDrawerLayout.isDrawerOpen(findViewById(R.id.drawer));
|
boolean drawerOpen = mDrawerLayout.isDrawerOpen(findViewById(R.id.drawer));
|
||||||
menu.findItem(R.id.share_node_id).setVisible(drawerOpen);
|
menu.findItem(R.id.share_node_id).setVisible(drawerOpen);
|
||||||
|
menu.findItem(R.id.exit).setVisible(!SyncthingService.alwaysRunInBackground(this));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -277,6 +278,9 @@ public class MainActivity extends SyncthingActivity
|
||||||
startActivity(new Intent(this, SettingsActivity.class)
|
startActivity(new Intent(this, SettingsActivity.class)
|
||||||
.setAction(SettingsActivity.ACTION_APP_SETTINGS_FRAGMENT));
|
.setAction(SettingsActivity.ACTION_APP_SETTINGS_FRAGMENT));
|
||||||
return true;
|
return true;
|
||||||
|
case R.id.exit:
|
||||||
|
stopService(new Intent(this, SyncthingService.class));
|
||||||
|
finish();
|
||||||
default:
|
default:
|
||||||
return super.onOptionsItemSelected(item);
|
return super.onOptionsItemSelected(item);
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,6 +29,8 @@ public class SettingsFragment extends PreferenceFragment
|
||||||
|
|
||||||
private static final String SYNCTHING_VERSION_KEY = "syncthing_version";
|
private static final String SYNCTHING_VERSION_KEY = "syncthing_version";
|
||||||
|
|
||||||
|
private CheckBoxPreference mAlwaysRunInBackground;
|
||||||
|
|
||||||
private CheckBoxPreference mSyncOnlyCharging;
|
private CheckBoxPreference mSyncOnlyCharging;
|
||||||
|
|
||||||
private CheckBoxPreference mSyncOnlyWifi;
|
private CheckBoxPreference mSyncOnlyWifi;
|
||||||
|
@ -96,18 +98,24 @@ public class SettingsFragment extends PreferenceFragment
|
||||||
|
|
||||||
addPreferencesFromResource(R.xml.app_settings);
|
addPreferencesFromResource(R.xml.app_settings);
|
||||||
PreferenceScreen screen = getPreferenceScreen();
|
PreferenceScreen screen = getPreferenceScreen();
|
||||||
|
mAlwaysRunInBackground = (CheckBoxPreference)
|
||||||
|
findPreference(SyncthingService.PREF_ALWAYS_RUN_IN_BACKGROUND);
|
||||||
mSyncOnlyCharging = (CheckBoxPreference)
|
mSyncOnlyCharging = (CheckBoxPreference)
|
||||||
findPreference(SyncthingService.PREF_SYNC_ONLY_CHARGING);
|
findPreference(SyncthingService.PREF_SYNC_ONLY_CHARGING);
|
||||||
mSyncOnlyCharging.setOnPreferenceChangeListener(this);
|
|
||||||
mSyncOnlyWifi = (CheckBoxPreference) findPreference(SyncthingService.PREF_SYNC_ONLY_WIFI);
|
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);
|
mSyncOnlyWifi.setOnPreferenceChangeListener(this);
|
||||||
|
// Force summary update and wifi/charging preferences enable/disable.
|
||||||
|
onPreferenceChange(mAlwaysRunInBackground, mAlwaysRunInBackground.isChecked());
|
||||||
Preference sttrace = findPreference("sttrace");
|
Preference sttrace = findPreference("sttrace");
|
||||||
sttrace.setOnPreferenceChangeListener(this);
|
sttrace.setOnPreferenceChangeListener(this);
|
||||||
sttrace.setSummary(PreferenceManager
|
sttrace.setSummary(PreferenceManager
|
||||||
.getDefaultSharedPreferences(getActivity()).getString("sttrace", ""));
|
.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
|
@Override
|
||||||
|
@ -146,6 +154,13 @@ public class SettingsFragment extends PreferenceFragment
|
||||||
|
|
||||||
if (preference.equals(mSyncOnlyCharging) || preference.equals(mSyncOnlyWifi)) {
|
if (preference.equals(mSyncOnlyCharging) || preference.equals(mSyncOnlyWifi)) {
|
||||||
mSyncthingService.updateState();
|
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) {
|
} 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"), getActivity());
|
preference.getKey().equals("ListenAddress"), getActivity());
|
||||||
|
|
|
@ -3,6 +3,8 @@ package com.nutomic.syncthingandroid.syncthing;
|
||||||
import android.content.BroadcastReceiver;
|
import android.content.BroadcastReceiver;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
|
import android.content.SharedPreferences;
|
||||||
|
import android.preference.PreferenceManager;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -14,6 +16,9 @@ public class BatteryReceiver extends BroadcastReceiver {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onReceive(Context context, Intent intent) {
|
public void onReceive(Context context, Intent intent) {
|
||||||
|
if (!SyncthingService.alwaysRunInBackground(context))
|
||||||
|
return;
|
||||||
|
|
||||||
boolean isCharging = Intent.ACTION_POWER_CONNECTED.equals(intent.getAction());
|
boolean isCharging = Intent.ACTION_POWER_CONNECTED.equals(intent.getAction());
|
||||||
Log.v(TAG, "Received charger " + (isCharging ? "connected" : "disconnected") + " event");
|
Log.v(TAG, "Received charger " + (isCharging ? "connected" : "disconnected") + " event");
|
||||||
Intent i = new Intent(context, SyncthingService.class);
|
Intent i = new Intent(context, SyncthingService.class);
|
||||||
|
|
|
@ -3,11 +3,16 @@ package com.nutomic.syncthingandroid.syncthing;
|
||||||
import android.content.BroadcastReceiver;
|
import android.content.BroadcastReceiver;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
|
import android.content.SharedPreferences;
|
||||||
|
import android.preference.PreferenceManager;
|
||||||
|
|
||||||
public class BootReceiver extends BroadcastReceiver {
|
public class BootReceiver extends BroadcastReceiver {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onReceive(Context context, Intent intent) {
|
public void onReceive(Context context, Intent intent) {
|
||||||
|
if (!SyncthingService.alwaysRunInBackground(context))
|
||||||
|
return;
|
||||||
|
|
||||||
context.startService(new Intent(context, SyncthingService.class));
|
context.startService(new Intent(context, SyncthingService.class));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -16,6 +16,9 @@ public class NetworkReceiver extends BroadcastReceiver {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onReceive(Context context, Intent intent) {
|
public void onReceive(Context context, Intent intent) {
|
||||||
|
if (!SyncthingService.alwaysRunInBackground(context))
|
||||||
|
return;
|
||||||
|
|
||||||
ConnectivityManager cm =
|
ConnectivityManager cm =
|
||||||
(ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
|
(ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
|
||||||
NetworkInfo wifiInfo = cm.getNetworkInfo(ConnectivityManager.TYPE_WIFI);
|
NetworkInfo wifiInfo = cm.getNetworkInfo(ConnectivityManager.TYPE_WIFI);
|
||||||
|
|
|
@ -849,7 +849,7 @@ public class RestApi implements SyncthingService.OnWebGuiAvailableListener,
|
||||||
normalized = json.optString("id", null);
|
normalized = json.optString("id", null);
|
||||||
error = json.optString("error", null);
|
error = json.optString("error", null);
|
||||||
} catch (JSONException e) {
|
} 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);
|
listener.onNodeIdNormalized(normalized, error);
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@ package com.nutomic.syncthingandroid.syncthing;
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.app.AlertDialog;
|
import android.app.AlertDialog;
|
||||||
import android.app.Service;
|
import android.app.Service;
|
||||||
|
import android.content.Context;
|
||||||
import android.content.DialogInterface;
|
import android.content.DialogInterface;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.IntentFilter;
|
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 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_WIFI = "sync_only_wifi";
|
||||||
|
|
||||||
public static final String PREF_SYNC_ONLY_CHARGING = "sync_only_charging";
|
public static final String PREF_SYNC_ONLY_CHARGING = "sync_only_charging";
|
||||||
|
@ -141,13 +144,23 @@ public class SyncthingService extends Service {
|
||||||
* called.
|
* called.
|
||||||
*/
|
*/
|
||||||
public void updateState() {
|
public void updateState() {
|
||||||
|
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);
|
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
|
||||||
boolean prefStopMobileData = prefs.getBoolean(PREF_SYNC_ONLY_WIFI, false);
|
boolean prefStopMobileData = prefs.getBoolean(PREF_SYNC_ONLY_WIFI, false);
|
||||||
boolean prefStopNotCharging = prefs.getBoolean(PREF_SYNC_ONLY_CHARGING, false);
|
boolean prefStopNotCharging = prefs.getBoolean(PREF_SYNC_ONLY_CHARGING, false);
|
||||||
|
|
||||||
|
shouldRun = (mDeviceStateHolder.isCharging() || !prefStopNotCharging) &&
|
||||||
|
(mDeviceStateHolder.isWifiConnected() || !prefStopMobileData);
|
||||||
|
}
|
||||||
|
|
||||||
// Start syncthing.
|
// Start syncthing.
|
||||||
if ((mDeviceStateHolder.isCharging() || !prefStopNotCharging) &&
|
if (shouldRun) {
|
||||||
(mDeviceStateHolder.isWifiConnected() || !prefStopMobileData)) {
|
|
||||||
if (mCurrentState == State.ACTIVE || mCurrentState == State.STARTING) {
|
if (mCurrentState == State.ACTIVE || mCurrentState == State.STARTING) {
|
||||||
mStopScheduled = false;
|
mStopScheduled = false;
|
||||||
return;
|
return;
|
||||||
|
@ -396,4 +409,12 @@ public class SyncthingService extends Service {
|
||||||
return mConfig.getWebGuiUrl();
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -56,7 +56,7 @@ public class RepoObserver extends FileObserver {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
mChilds = new ArrayList<>(directories.length);
|
mChilds = new ArrayList<>();
|
||||||
for (File f : directories) {
|
for (File f : directories) {
|
||||||
mChilds.add(new RepoObserver(mListener, mRepo, path + "/" + f.getName()));
|
mChilds.add(new RepoObserver(mListener, mRepo, path + "/" + f.getName()));
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,4 +28,8 @@
|
||||||
android:id="@+id/settings"
|
android:id="@+id/settings"
|
||||||
android:title="@string/settings_title" />
|
android:title="@string/settings_title" />
|
||||||
|
|
||||||
|
<item
|
||||||
|
android:id="@+id/exit"
|
||||||
|
android:title="@string/exit" />
|
||||||
|
|
||||||
</menu>
|
</menu>
|
||||||
|
|
|
@ -199,6 +199,15 @@ Please report any problems you encounter via Github.</string>
|
||||||
<!-- Activity title -->
|
<!-- Activity title -->
|
||||||
<string name="settings_title">Settings</string>
|
<string name="settings_title">Settings</string>
|
||||||
|
|
||||||
|
<!-- Preference title -->
|
||||||
|
<string name="always_run_in_background">Always run in background</string>
|
||||||
|
|
||||||
|
<!-- Preference summary in case it is enabled -->
|
||||||
|
<string name="always_run_in_background_enabled">Syncthing always runs in the background, according to preferences below.</string>
|
||||||
|
|
||||||
|
<!-- Preference summary in case it is disabled -->
|
||||||
|
<string name="always_run_in_background_disabled">Syncthing only runs when explicitly started, and can be stopped by menu button.</string>
|
||||||
|
|
||||||
<string name="sync_only_charging">Sync only when charging</string>
|
<string name="sync_only_charging">Sync only when charging</string>
|
||||||
|
|
||||||
<string name="sync_only_wifi">Sync only on wifi</string>
|
<string name="sync_only_wifi">Sync only on wifi</string>
|
||||||
|
@ -252,7 +261,7 @@ If this error persists, try restarting your device.</string>
|
||||||
<!-- Button text on the "syncthing disabled" dialog -->
|
<!-- Button text on the "syncthing disabled" dialog -->
|
||||||
<string name="syncthing_disabled_change_settings">Change Settings</string>
|
<string name="syncthing_disabled_change_settings">Change Settings</string>
|
||||||
|
|
||||||
<!-- Button text on the "syncthing disabled" dialog -->
|
<!-- Button text on the "syncthing disabled" dialog, used as menu item to stop syncthing service if "always_run_in_background" is true -->
|
||||||
<string name="exit">Exit</string>
|
<string name="exit">Exit</string>
|
||||||
|
|
||||||
<!-- RestApi -->
|
<!-- RestApi -->
|
||||||
|
|
|
@ -1,6 +1,11 @@
|
||||||
<?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="always_run_in_background"
|
||||||
|
android:title="@string/always_run_in_background"
|
||||||
|
android:defaultValue="false" />
|
||||||
|
|
||||||
<CheckBoxPreference
|
<CheckBoxPreference
|
||||||
android:key="sync_only_charging"
|
android:key="sync_only_charging"
|
||||||
android:title="@string/sync_only_charging"
|
android:title="@string/sync_only_charging"
|
||||||
|
|
Loading…
Reference in a new issue