1
0
Fork 0
mirror of https://github.com/syncthing/syncthing-android.git synced 2024-11-29 15:51:17 +00:00

Add individual sync conditions for devices (#96)

* SyncConditionsActivity - Rename "folder" to "object" as it can mean a folder or device.

* Implement per-device sync conditions

* Default custom wifi whitelist to "all enabled"

* Update APK version to 0.14.51.7 / 4170

* Add checkbox "use Wi-Fi whitelist" in global run conditions

* Rename variable
This commit is contained in:
Catfriend1 2018-10-16 10:18:15 +02:00 committed by GitHub
parent b84d4da34f
commit f8692f02ef
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 277 additions and 64 deletions

View file

@ -35,8 +35,8 @@ android {
applicationId "com.github.catfriend1.syncthingandroid"
minSdkVersion 16
targetSdkVersion 26
versionCode 4169
versionName "0.14.51.6"
versionCode 4170
versionName "0.14.51.7"
testApplicationId 'com.github.catfriend1.syncthingandroid.test'
testInstrumentationRunner 'android.support.test.runner.AndroidJUnitRunner'
playAccountConfig = playAccountConfigs.defaultAccountConfig

View file

@ -1,5 +1,6 @@
package com.nutomic.syncthingandroid;
import com.nutomic.syncthingandroid.activities.DeviceActivity;
import com.nutomic.syncthingandroid.activities.FirstStartActivity;
import com.nutomic.syncthingandroid.activities.FolderActivity;
import com.nutomic.syncthingandroid.activities.FolderPickerActivity;
@ -26,6 +27,7 @@ public interface DaggerComponent {
void inject(SyncthingApp app);
void inject(MainActivity activity);
void inject(FirstStartActivity activity);
void inject(DeviceActivity activity);
void inject(FolderActivity activity);
void inject(FolderPickerActivity activity);
void inject(SyncConditionsActivity activity);

View file

@ -4,6 +4,7 @@ import android.app.Dialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.support.v4.content.ContextCompat;
@ -24,10 +25,14 @@ import android.widget.Toast;
import com.google.gson.Gson;
import com.google.zxing.integration.android.IntentIntegrator;
import com.google.zxing.integration.android.IntentResult;
import com.nutomic.syncthingandroid.R;
import com.nutomic.syncthingandroid.model.Connections;
import com.nutomic.syncthingandroid.model.Device;
import com.nutomic.syncthingandroid.service.Constants;
import com.nutomic.syncthingandroid.service.RestApi;
import com.nutomic.syncthingandroid.service.SyncthingService;
import com.nutomic.syncthingandroid.SyncthingApp;
import com.nutomic.syncthingandroid.util.Compression;
import com.nutomic.syncthingandroid.util.TextWatcherAdapter;
import com.nutomic.syncthingandroid.util.Util;
@ -36,6 +41,8 @@ import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import javax.inject.Inject;
import static android.text.TextUtils.isEmpty;
import static android.view.View.GONE;
import static android.view.View.VISIBLE;
@ -46,7 +53,11 @@ import static com.nutomic.syncthingandroid.util.Compression.METADATA;
/**
* Shows device details and allows changing them.
*/
public class DeviceActivity extends SyncthingActivity implements View.OnClickListener {
public class DeviceActivity extends SyncthingActivity
implements
View.OnClickListener,
SyncthingActivity.OnServiceConnectedListener,
SyncthingService.OnServiceStateChangeListener {
public static final String EXTRA_NOTIFICATION_ID =
"com.nutomic.syncthingandroid.activities.DeviceActivity.NOTIFICATION_ID";
@ -84,10 +95,19 @@ public class DeviceActivity extends SyncthingActivity implements View.OnClickLis
private SwitchCompat mDevicePaused;
private SwitchCompat mCustomSyncConditionsSwitch;
private TextView mCustomSyncConditionsDescription;
private TextView mCustomSyncConditionsDialog;
private TextView mSyncthingVersionView;
private View mCompressionContainer;
@Inject
SharedPreferences mPreferences;
private boolean mIsCreateMode;
private boolean mDeviceNeedsToUpdate;
@ -154,6 +174,12 @@ public class DeviceActivity extends SyncthingActivity implements View.OnClickLis
mDevice.paused = isChecked;
mDeviceNeedsToUpdate = true;
break;
case R.id.customSyncConditionsSwitch:
mCustomSyncConditionsDescription.setEnabled(isChecked);
mCustomSyncConditionsDialog.setEnabled(isChecked);
// This is needed to display the "discard changes dialog".
mDeviceNeedsToUpdate = true;
break;
}
}
};
@ -161,11 +187,12 @@ public class DeviceActivity extends SyncthingActivity implements View.OnClickLis
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
((SyncthingApp) getApplication()).component().inject(this);
setContentView(R.layout.fragment_device);
mIsCreateMode = getIntent().getBooleanExtra(EXTRA_IS_CREATE, false);
registerOnServiceConnectedListener(this::onServiceConnected);
setTitle(mIsCreateMode ? R.string.add_device : R.string.edit_device);
registerOnServiceConnectedListener(this);
mIdContainer = findViewById(R.id.idContainer);
mIdView = findViewById(R.id.id);
@ -177,9 +204,13 @@ public class DeviceActivity extends SyncthingActivity implements View.OnClickLis
mCompressionValueView = findViewById(R.id.compressionValue);
mIntroducerView = findViewById(R.id.introducer);
mDevicePaused = findViewById(R.id.devicePause);
mCustomSyncConditionsSwitch = findViewById(R.id.customSyncConditionsSwitch);
mCustomSyncConditionsDescription = findViewById(R.id.customSyncConditionsDescription);
mCustomSyncConditionsDialog = findViewById(R.id.customSyncConditionsDialog);
mSyncthingVersionView = findViewById(R.id.syncthingVersion);
mQrButton.setOnClickListener(this);
mCustomSyncConditionsDialog.setOnClickListener(view -> onCustomSyncConditionsDialogClick());
mCompressionContainer.setOnClickListener(this);
if (savedInstanceState != null){
@ -198,6 +229,19 @@ public class DeviceActivity extends SyncthingActivity implements View.OnClickLis
}
}
/**
* Invoked after user clicked on the {@link mCustomSyncConditionsDialog} label.
*/
private void onCustomSyncConditionsDialogClick() {
startActivityForResult(
SyncConditionsActivity.createIntent(
this, Constants.PREF_OBJECT_PREFIX_DEVICE + mDevice.deviceID, mDevice.name
),
0
);
return;
}
private void restoreDialogStates(Bundle savedInstanceState) {
if (savedInstanceState.getBoolean(IS_SHOWING_COMPRESSION_DIALOG)){
showCompressionDialog();
@ -257,20 +301,32 @@ public class DeviceActivity extends SyncthingActivity implements View.OnClickLis
Util.dismissDialogSafe(mDeleteDialog, this);
}
private void onServiceConnected() {
/**
* Register for service state change events.
*/
@Override
public void onServiceConnected() {
Log.v(TAG, "onServiceConnected");
SyncthingService syncthingService = (SyncthingService) getService();
syncthingService.getNotificationHandler().cancelConsentNotification(getIntent().getIntExtra(EXTRA_NOTIFICATION_ID, 0));
syncthingService.registerOnServiceStateChangeListener(this::onServiceStateChange);
syncthingService.registerOnServiceStateChangeListener(this);
}
/**
* Sets version and current address of the device.
* <p/>
* NOTE: This is only called once on startup, should be called more often to properly display
* version/address changes.
*/
private void onReceiveConnections(Connections connections) {
if (connections == null || connections.connections == null) {
Log.e(TAG, "onReceiveConnections: connections == null || connections.connections == null");
return;
}
if (mDevice == null) {
Log.e(TAG, "onReceiveConnections: mDevice == null");
return;
}
boolean viewsExist = mSyncthingVersionView != null && mCurrentAddressView != null;
if (viewsExist && connections.connections.containsKey(mDevice.deviceID)) {
mCurrentAddressView.setVisibility(VISIBLE);
@ -280,18 +336,21 @@ public class DeviceActivity extends SyncthingActivity implements View.OnClickLis
}
}
private void onServiceStateChange(SyncthingService.State currentState) {
@Override
public void onServiceStateChange(SyncthingService.State currentState) {
if (currentState != ACTIVE) {
finish();
return;
}
if (!mIsCreateMode) {
List<Device> devices = getApi().getDevices(false);
RestApi restApi = getApi(); // restApi != null because of State.ACTIVE
List<Device> devices = restApi.getDevices(false);
String passedId = getIntent().getStringExtra(EXTRA_DEVICE_ID);
mDevice = null;
for (Device device : devices) {
if (device.deviceID.equals(getIntent().getStringExtra(EXTRA_DEVICE_ID))) {
mDevice = device;
for (Device currentDevice : devices) {
if (currentDevice.deviceID.equals(passedId)) {
mDevice = currentDevice;
break;
}
}
@ -300,10 +359,10 @@ public class DeviceActivity extends SyncthingActivity implements View.OnClickLis
finish();
return;
}
if (restApi != null) {
restApi.getConnections(this::onReceiveConnections);
}
}
getApi().getConnections(this::onReceiveConnections);
updateViewsAndSetListeners();
}
@ -313,6 +372,7 @@ public class DeviceActivity extends SyncthingActivity implements View.OnClickLis
mAddressesView.removeTextChangedListener(mAddressesTextWatcher);
mIntroducerView.setOnCheckedChangeListener(null);
mDevicePaused.setOnCheckedChangeListener(null);
mCustomSyncConditionsSwitch.setOnCheckedChangeListener(null);
// Update views
mIdView.setText(mDevice.deviceID);
@ -322,12 +382,26 @@ public class DeviceActivity extends SyncthingActivity implements View.OnClickLis
mIntroducerView.setChecked(mDevice.introducer);
mDevicePaused.setChecked(mDevice.paused);
// Update views - custom sync conditions.
mCustomSyncConditionsSwitch.setChecked(false);
if (mIsCreateMode) {
findViewById(R.id.customSyncConditionsContainer).setVisibility(View.GONE);
} else {
mCustomSyncConditionsSwitch.setChecked(mPreferences.getBoolean(
Constants.DYN_PREF_OBJECT_CUSTOM_SYNC_CONDITIONS(Constants.PREF_OBJECT_PREFIX_DEVICE + mDevice.deviceID), false
));
}
mCustomSyncConditionsSwitch.setEnabled(!mIsCreateMode);
mCustomSyncConditionsDescription.setEnabled(mCustomSyncConditionsSwitch.isChecked());
mCustomSyncConditionsDialog.setEnabled(mCustomSyncConditionsSwitch.isChecked());
// Keep state updated
mIdView.addTextChangedListener(mIdTextWatcher);
mNameView.addTextChangedListener(mNameTextWatcher);
mAddressesView.addTextChangedListener(mAddressesTextWatcher);
mIntroducerView.setOnCheckedChangeListener(mCheckedListener);
mDevicePaused.setOnCheckedChangeListener(mCheckedListener);
mCustomSyncConditionsSwitch.setOnCheckedChangeListener(mCheckedListener);
}
@Override
@ -423,11 +497,34 @@ public class DeviceActivity extends SyncthingActivity implements View.OnClickLis
/**
* Sends the updated device info if in edit mode.
* Preconditions: mDeviceNeedsToUpdate == true
*/
private void updateDevice() {
if (!mIsCreateMode && mDeviceNeedsToUpdate && mDevice != null) {
getApi().editDevice(mDevice);
if (mIsCreateMode) {
// If we are about to create this folder, we cannot update via restApi.
return;
}
if (mDevice == null) {
Log.e(TAG, "updateDevice: mDevice == null");
return;
}
// Save device specific preferences.
Log.v(TAG, "updateDevice: mDevice.deviceID = \'" + mDevice.deviceID + "\'");
SharedPreferences.Editor editor = mPreferences.edit();
editor.putBoolean(
Constants.DYN_PREF_OBJECT_CUSTOM_SYNC_CONDITIONS(Constants.PREF_OBJECT_PREFIX_DEVICE + mDevice.deviceID),
mCustomSyncConditionsSwitch.isChecked()
);
editor.apply();
// Update device via restApi and send the config to REST endpoint.
RestApi restApi = getApi();
if (restApi == null) {
Log.e(TAG, "updateDevice: restApi == null");
return;
}
restApi.updateDevice(mDevice);
}
private List<String> persistableAddresses(CharSequence userInput) {

View file

@ -263,7 +263,6 @@ public class FolderActivity extends SyncthingActivity
/**
* Invoked after user clicked on the {@link mCustomSyncConditionsDialog} label.
*/
@SuppressLint("InlinedAPI")
private void onCustomSyncConditionsDialogClick() {
startActivityForResult(
SyncConditionsActivity.createIntent(
@ -367,7 +366,7 @@ public class FolderActivity extends SyncthingActivity
}
/**
* Save current settings in case we are in create mode and they aren't yet stored in the config.
* Register for service state change events.
*/
@Override
public void onServiceConnected() {
@ -701,11 +700,19 @@ public class FolderActivity extends SyncthingActivity
deviceView.setOnCheckedChangeListener(mCheckedListener);
}
/**
* Sends the updated folder info if in edit mode.
* Preconditions: mFolderNeedsToUpdate == true
*/
private void updateFolder() {
if (mIsCreateMode) {
// If we are about to create this folder, we cannot update via restApi.
return;
}
if (mFolder == null) {
Log.e(TAG, "updateFolder: mFolder == null");
return;
}
// Save folder specific preferences.
Log.v(TAG, "updateFolder: mFolder.id = \'" + mFolder.id + "\'");
@ -716,18 +723,13 @@ public class FolderActivity extends SyncthingActivity
);
editor.apply();
// Update folder via restApi.
// Update folder via restApi and send the config to REST endpoint.
RestApi restApi = getApi();
/**
* RestApi is guaranteed not to be null as {@link onServiceStateChange}
* immediately finishes this activity if SyncthingService shuts down.
*/
/*
if (restApi == null) {
Log.e(TAG, "updateFolder: restApi == null");
return;
}
*/
// Update ignore list.
String[] ignore = mEditIgnoreListContent.getText().toString().split("\n");
restApi.postFolderIgnoreList(mFolder.id, ignore);

View file

@ -105,6 +105,7 @@ public class SettingsActivity extends SyncthingActivity {
private CheckBoxPreference mRunOnMobileData;
private CheckBoxPreference mRunOnWifi;
private CheckBoxPreference mRunOnMeteredWifi;
private CheckBoxPreference mUseWifiWhitelist;
private WifiSsidPreference mWifiSsidWhitelist;
private CheckBoxPreference mRunInFlightMode;
@ -173,6 +174,8 @@ public class SettingsActivity extends SyncthingActivity {
(CheckBoxPreference) findPreference(Constants.PREF_RUN_ON_WIFI);
mRunOnMeteredWifi =
(CheckBoxPreference) findPreference(Constants.PREF_RUN_ON_METERED_WIFI);
mUseWifiWhitelist =
(CheckBoxPreference) findPreference(Constants.PREF_USE_WIFI_SSID_WHITELIST);
mWifiSsidWhitelist =
(WifiSsidPreference) findPreference(Constants.PREF_WIFI_SSID_WHITELIST);
mRunInFlightMode =
@ -225,7 +228,8 @@ public class SettingsActivity extends SyncthingActivity {
Preference appVersion = screen.findPreference("app_version");
mRunOnMeteredWifi.setEnabled(mRunOnWifi.isChecked());
mWifiSsidWhitelist.setEnabled(mRunOnWifi.isChecked());
mUseWifiWhitelist.setEnabled(mRunOnWifi.isChecked());
mWifiSsidWhitelist.setEnabled(mRunOnWifi.isChecked() && mUseWifiWhitelist.isChecked());
/* Experimental options */
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
/* Wakelocks are only valid on Android 5 or lower. */
@ -262,7 +266,7 @@ public class SettingsActivity extends SyncthingActivity {
screen.findPreference(Constants.PREF_POWER_SOURCE).setSummary(mPowerSource.getEntry());
String wifiSsidSummary = TextUtils.join(", ", mPreferences.getStringSet(Constants.PREF_WIFI_SSID_WHITELIST, new HashSet<>()));
screen.findPreference(Constants.PREF_WIFI_SSID_WHITELIST).setSummary(TextUtils.isEmpty(wifiSsidSummary) ?
getString(R.string.run_on_all_wifi_networks) :
getString(R.string.wifi_ssid_whitelist_empty) :
getString(R.string.run_on_whitelisted_wifi_networks, wifiSsidSummary)
);
handleSocksProxyPreferenceChange(screen.findPreference(Constants.PREF_SOCKS_PROXY_ADDRESS), mPreferences.getString(Constants.PREF_SOCKS_PROXY_ADDRESS, ""));
@ -366,12 +370,16 @@ public class SettingsActivity extends SyncthingActivity {
break;
case Constants.PREF_RUN_ON_WIFI:
mRunOnMeteredWifi.setEnabled((Boolean) o);
mUseWifiWhitelist.setEnabled((Boolean) o);
mWifiSsidWhitelist.setEnabled((Boolean) o && mUseWifiWhitelist.isChecked());
break;
case Constants.PREF_USE_WIFI_SSID_WHITELIST:
mWifiSsidWhitelist.setEnabled((Boolean) o);
break;
case Constants.PREF_WIFI_SSID_WHITELIST:
String wifiSsidSummary = TextUtils.join(", ", (Set<String>) o);
preference.setSummary(TextUtils.isEmpty(wifiSsidSummary) ?
getString(R.string.run_on_all_wifi_networks) :
getString(R.string.wifi_ssid_whitelist_empty) :
getString(R.string.run_on_whitelisted_wifi_networks, wifiSsidSummary)
);
break;
@ -386,7 +394,7 @@ public class SettingsActivity extends SyncthingActivity {
case "deviceName":
Device localDevice = mRestApi.getLocalDevice();
localDevice.name = (String) o;
mRestApi.editDevice(localDevice);
mRestApi.updateDevice(localDevice);
break;
case "listenAddresses":
mOptions.listenAddresses = Iterables.toArray(splitter.split((String) o), String.class);

View file

@ -60,7 +60,8 @@ public class SyncConditionsActivity extends SyncthingActivity
private SwitchCompat mSyncOnMobileData;
/**
* Shared preferences names for custom per-folder settings.
* Shared preferences names for custom object settings.
* Object can e.g. be a folder or device.
*/
private String mObjectPrefixAndId;
private String mPrefSyncOnWifi;
@ -108,7 +109,7 @@ public class SyncConditionsActivity extends SyncthingActivity
mObjectPrefixAndId = intent.getStringExtra(EXTRA_OBJECT_PREFIX_AND_ID);
Log.v(TAG, "Prefix is \'" + mObjectPrefixAndId + "\' (" + mObjectReadableName + ")");
mPrefSyncOnWifi = Constants.DYN_PREF_OBJECT_SYNC_ON_WIFI(mObjectPrefixAndId);
mPrefSyncOnWhitelistedWifi = Constants.DYN_PREF_OBJECT_SYNC_ON_WHITELISTED_WIFI(mObjectPrefixAndId);
mPrefSyncOnWhitelistedWifi = Constants.DYN_PREF_OBJECT_USE_WIFI_SSID_WHITELIST(mObjectPrefixAndId);
mPrefSelectedWhitelistSsid = Constants.DYN_PREF_OBJECT_SELECTED_WHITELIST_SSID(mObjectPrefixAndId);
mPrefSyncOnMeteredWifi = Constants.DYN_PREF_OBJECT_SYNC_ON_METERED_WIFI(mObjectPrefixAndId);
mPrefSyncOnMobileData = Constants.DYN_PREF_OBJECT_SYNC_ON_MOBILE_DATA(mObjectPrefixAndId);
@ -117,14 +118,13 @@ public class SyncConditionsActivity extends SyncthingActivity
* Load global run conditions.
*/
Boolean globalRunOnWifiEnabled = mPreferences.getBoolean(Constants.PREF_RUN_ON_WIFI, true);
Boolean globalWhitelistEnabled = !mPreferences.getStringSet(Constants.PREF_WIFI_SSID_WHITELIST, new HashSet<>())
.isEmpty();
Set<String> globalWhitelistedSsid = mPreferences.getStringSet(Constants.PREF_WIFI_SSID_WHITELIST, new HashSet<>());
Boolean globalWhitelistEnabled = mPreferences.getBoolean(Constants.PREF_USE_WIFI_SSID_WHITELIST, false);
Boolean globalRunOnMeteredWifiEnabled = mPreferences.getBoolean(Constants.PREF_RUN_ON_METERED_WIFI, false);
Boolean globalRunOnMobileDataEnabled = mPreferences.getBoolean(Constants.PREF_RUN_ON_MOBILE_DATA, false);
/**
* Load custom folder preferences. If unset, use global setting as default.
* Load custom object preferences. If unset, use global setting as default.
*/
mSyncOnWifi.setChecked(globalRunOnWifiEnabled && mPreferences.getBoolean(mPrefSyncOnWifi, globalRunOnWifiEnabled));
mSyncOnWifi.setEnabled(globalRunOnWifiEnabled);
@ -143,7 +143,7 @@ public class SyncConditionsActivity extends SyncthingActivity
mSyncOnMobileData.setOnCheckedChangeListener(mCheckedListener);
// Read selected WiFi Ssid whitelist items.
Set<String> selectedWhitelistedSsid = mPreferences.getStringSet(mPrefSelectedWhitelistSsid, new HashSet<>());
Set<String> selectedWhitelistedSsid = mPreferences.getStringSet(mPrefSelectedWhitelistSsid, globalWhitelistedSsid);
// Removes any network that is no longer part of the global WiFi Ssid whitelist.
selectedWhitelistedSsid.retainAll(globalWhitelistedSsid);
@ -162,7 +162,7 @@ public class SyncConditionsActivity extends SyncthingActivity
setMarginEnd(params, contentInset);
TextView emptyView = new TextView(mWifiSsidContainer.getContext());
emptyView.setGravity(CENTER_VERTICAL);
emptyView.setText(R.string.wifi_ssid_whitelist_empty);
emptyView.setText(R.string.custom_wifi_ssid_whitelist_empty);
mWifiSsidContainer.addView(emptyView, params);
mWifiSsidContainer.setEnabled(false);
} else {
@ -224,7 +224,7 @@ public class SyncConditionsActivity extends SyncthingActivity
if (mUnsavedChanges) {
Log.v(TAG, "onPause: mUnsavedChanges == true. Saving prefs ...");
/**
* Save custom folder preferences.
* Save custom object preferences.
*/
SharedPreferences.Editor editor = mPreferences.edit();
editor.putBoolean(mPrefSyncOnWifi, mSyncOnWifi.isChecked());

View file

@ -15,6 +15,7 @@ public class Constants {
public static final String PREF_RUN_ON_MOBILE_DATA = "run_on_mobile_data";
public static final String PREF_RUN_ON_WIFI = "run_on_wifi";
public static final String PREF_RUN_ON_METERED_WIFI = "run_on_metered_wifi";
public static final String PREF_USE_WIFI_SSID_WHITELIST = "use_wifi_whitelist";
public static final String PREF_WIFI_SSID_WHITELIST = "wifi_ssid_whitelist";
public static final String PREF_POWER_SOURCE = "power_source";
public static final String PREF_RESPECT_BATTERY_SAVING = "respect_battery_saving";
@ -43,8 +44,8 @@ public class Constants {
return objectPrefixAndId + "_" + PREF_RUN_ON_WIFI;
}
public static String DYN_PREF_OBJECT_SYNC_ON_WHITELISTED_WIFI(String objectPrefixAndId) {
return objectPrefixAndId + "_" + "use_wifi_whitelist";
public static String DYN_PREF_OBJECT_USE_WIFI_SSID_WHITELIST(String objectPrefixAndId) {
return objectPrefixAndId + "_" + PREF_USE_WIFI_SSID_WHITELIST;
}
public static String DYN_PREF_OBJECT_SELECTED_WHITELIST_SSID(String objectPrefixAndId) {

View file

@ -518,7 +518,7 @@ public class RestApi {
}, errorListener);
}
public void editDevice(Device newDevice) {
public void updateDevice(Device newDevice) {
synchronized (mConfigLock) {
removeDeviceInternal(newDevice.deviceID);
mConfig.devices.add(newDevice);
@ -782,11 +782,16 @@ public class RestApi {
Log.v(TAG, "onSyncPreconditionChanged: Event fired.");
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(mContext);
synchronized (mConfigLock) {
if (mConfig == null || mConfig.folders == null) {
Log.d(TAG, "onSyncPreconditionChanged: mConfig.folders is not ready yet.");
Boolean configChanged = false;
// Check if the config has been loaded.
if (mConfig == null) {
Log.d(TAG, "onSyncPreconditionChanged: mConfig is not ready yet.");
return;
}
Boolean configChanged = false;
// Check if the folders are available from config.
if (mConfig.folders != null) {
for (Folder folder : mConfig.folders) {
Boolean folderCustomSyncConditionsEnabled = sharedPreferences.getBoolean(
Constants.DYN_PREF_OBJECT_CUSTOM_SYNC_CONDITIONS(Constants.PREF_OBJECT_PREFIX_FOLDER + folder.id), false
@ -802,6 +807,33 @@ public class RestApi {
}
}
}
} else {
Log.d(TAG, "onSyncPreconditionChanged: mConfig.folders is not ready yet.");
return;
}
// Check if the devices are available from config.
if (mConfig.devices != null) {
for (Device device : mConfig.devices) {
Boolean deviceCustomSyncConditionsEnabled = sharedPreferences.getBoolean(
Constants.DYN_PREF_OBJECT_CUSTOM_SYNC_CONDITIONS(Constants.PREF_OBJECT_PREFIX_DEVICE + device.deviceID), false
);
if (deviceCustomSyncConditionsEnabled) {
Boolean syncConditionsMet = runConditionMonitor.checkObjectSyncConditions(
Constants.PREF_OBJECT_PREFIX_DEVICE + device.deviceID
);
Log.v(TAG, "onSyncPreconditionChanged: syncDevice(" + device.deviceID + ")=" + (syncConditionsMet ? "1" : "0"));
if (device.paused != !syncConditionsMet) {
device.paused = !syncConditionsMet;
configChanged = true;
}
}
}
} else {
Log.d(TAG, "onSyncPreconditionChanged: mConfig.devices is not ready yet.");
return;
}
if (configChanged) {
Log.v(TAG, "onSyncPreconditionChanged: Sending changed config ...");
sendConfig();

View file

@ -228,11 +228,13 @@ public class RunConditionMonitor {
/**
* Constants.PREF_WIFI_SSID_WHITELIST
*/
private SyncConditionResult checkConditionSyncOnWhitelistedWifi(String prefNameSyncOnWhitelistedWifi) {
Set<String> whitelistedWifiSsids = mPreferences.getStringSet(prefNameSyncOnWhitelistedWifi, new HashSet<>());
boolean prefWifiWhitelistEnabled = !whitelistedWifiSsids.isEmpty();
private SyncConditionResult checkConditionSyncOnWhitelistedWifi(
String prefNameUseWifiWhitelist,
String prefNameSelectedWhitelistSsid) {
boolean wifiWhitelistEnabled = mPreferences.getBoolean(prefNameUseWifiWhitelist, false);
Set<String> whitelistedWifiSsids = mPreferences.getStringSet(prefNameSelectedWhitelistSsid, new HashSet<>());
try {
if (wifiWhitelistConditionMet(prefWifiWhitelistEnabled, whitelistedWifiSsids)) {
if (wifiWhitelistConditionMet(wifiWhitelistEnabled, whitelistedWifiSsids)) {
return new SyncConditionResult(true, "\n" + res.getString(R.string.reason_on_whitelisted_wifi));
}
return new SyncConditionResult(false, "\n" + res.getString(R.string.reason_not_on_whitelisted_wifi));
@ -348,7 +350,7 @@ public class RunConditionMonitor {
// Wifi type is allowed.
Log.v(TAG, "decideShouldRun: checkConditionSyncOnWifi && checkConditionSyncOnMeteredWifi");
scr = checkConditionSyncOnWhitelistedWifi(Constants.PREF_WIFI_SSID_WHITELIST);
scr = checkConditionSyncOnWhitelistedWifi(Constants.PREF_USE_WIFI_SSID_WHITELIST, Constants.PREF_WIFI_SSID_WHITELIST);
mRunDecisionExplanation += scr.explanation;
if (scr.conditionMet) {
// Wifi is whitelisted.
@ -395,7 +397,10 @@ public class RunConditionMonitor {
// Wifi type is allowed.
Log.v(TAG, "checkObjectSyncConditions: checkConditionSyncOnWifi && checkConditionSyncOnMeteredWifi");
scr = checkConditionSyncOnWhitelistedWifi(Constants.DYN_PREF_OBJECT_SELECTED_WHITELIST_SSID(objectPrefixAndId));
scr = checkConditionSyncOnWhitelistedWifi(
Constants.DYN_PREF_OBJECT_USE_WIFI_SSID_WHITELIST(objectPrefixAndId),
Constants.DYN_PREF_OBJECT_SELECTED_WHITELIST_SSID(objectPrefixAndId)
);
if (scr.conditionMet) {
// Wifi is whitelisted.
Log.v(TAG, "checkObjectSyncConditions: checkConditionSyncOnWifi && checkConditionSyncOnMeteredWifi && checkConditionSyncOnWhitelistedWifi");

View file

@ -1,5 +1,5 @@
Enhancements
* Specify sync conditions differently for each folder [NEW]
* Specify sync conditions differently for each folder, device [NEW]
* UI explains why syncthing is running (or not)
* Support in-app editing of folder's ignore list items
Fixes

View file

@ -122,6 +122,59 @@
android:drawableStart="@drawable/ic_pause_circle_outline_black_24dp"
android:text="@string/pause_device" />
<!-- Custom sync conditions -->
<LinearLayout
android:id="@+id/customSyncConditionsContainer"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?selectableItemBackground"
android:orientation="vertical"
android:gravity="center_vertical">
<android.support.v7.widget.SwitchCompat
android:id="@+id/customSyncConditionsSwitch"
style="@style/Widget.Syncthing.TextView.Label.Details"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:checked="false"
android:drawableLeft="@drawable/ic_autorenew_black_24dp"
android:drawableStart="@drawable/ic_autorenew_black_24dp"
android:text="@string/custom_sync_conditions_title" />
<TextView
android:id="@+id/customSyncConditionsDescription"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="75dp"
android:layout_marginStart="75dp"
android:layout_marginTop="-20dp"
android:textAppearance="@style/TextAppearance.AppCompat.Caption"
android:text="@string/custom_sync_conditions_description"
android:focusable="false"/>
<TextView
android:id="@+id/customSyncConditionsDialog"
style="@style/Widget.Syncthing.TextView.Label.Details"
android:layout_marginLeft="56dp"
android:layout_marginStart="56dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:focusable="true"
android:text="@string/custom_sync_conditions_dialog"/>
<TextView
android:id="@+id/customSyncConditionsCurrent"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="75dp"
android:layout_marginStart="75dp"
android:layout_marginTop="-20dp"
android:textAppearance="@style/TextAppearance.AppCompat.Caption"
android:text="@null"
android:focusable="false"/>
</LinearLayout>
<TextView
android:id="@+id/currentAddress"
style="@style/Widget.Syncthing.TextView.Label.Details"

View file

@ -316,8 +316,10 @@ Please report any problems you encounter via Github.</string>
<string name="run_on_metered_wifi_summary">Run when device is connected to a metered Wi-Fi network e.g. a hotspot or tethered network. Attention: This can consume large portion of your data plan if you sync a lot of data.</string>
<string name="run_on_whitelisted_wifi_title">Run on specified Wi-Fi networks</string>
<string name="run_on_whitelisted_wifi_networks">Run only on selected Wi-Fi networks: %1$s</string>
<string name="specify_wifi_ssid_whitelist">Select Wi-Fi networks</string>
<string name="run_on_whitelisted_wifi_networks">Selected Wi-Fi networks: %1$s</string>
<string name="run_on_all_wifi_networks">Run on all Wi-Fi networks.</string>
<string name="wifi_ssid_whitelist_empty">No Wi-Fi networks specified. Click to specify networks.</string>
<string name="sync_only_wifi_ssids_wifi_turn_on_wifi">Please turn on Wi-Fi to select networks.</string>
@ -627,7 +629,7 @@ Please report any problems you encounter via Github.</string>
<!-- Sync Conditions Dialog -->
<string name="wifi_ssid_whitelist_empty">No WiFi SSID\'s whitelisted. Please specify some in the settings.</string>
<string name="custom_wifi_ssid_whitelist_empty">No WiFi SSID\'s whitelisted. Please specify some in the settings.</string>
<!-- SyncthingService -->

View file

@ -12,23 +12,34 @@
android:title="@string/run_conditions_title"
android:summary="@string/run_conditions_summary"/>
<!-- Sync on WiFi -->
<CheckBoxPreference
android:key="run_on_wifi"
android:title="@string/run_on_wifi_title"
android:summary="@string/run_on_wifi_summary"
android:defaultValue="true" />
<!-- Sync on metered WiFi -->
<CheckBoxPreference
android:key="run_on_metered_wifi"
android:title="@string/run_on_metered_wifi_title"
android:summary="@string/run_on_metered_wifi_summary"
android:defaultValue="false" />
<!-- Use WiFi Ssid whitelist -->
<CheckBoxPreference
android:key="use_wifi_whitelist"
android:title="@string/run_on_whitelisted_wifi_title"
android:summary="@string/run_on_whitelisted_wifi_summary"
android:defaultValue="false" />
<!-- Select whitelisted WiFi Ssid -->
<com.nutomic.syncthingandroid.views.WifiSsidPreference
android:key="wifi_ssid_whitelist"
android:title="@string/run_on_whitelisted_wifi_title"
android:title="@string/specify_wifi_ssid_whitelist"
android:summary="@null" />
<!-- Sync on mobile data -->
<CheckBoxPreference
android:key="run_on_mobile_data"
android:title="@string/run_on_mobile_data_title"