mirror of
https://github.com/syncthing/syncthing-android.git
synced 2024-12-02 01:01: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:
parent
b84d4da34f
commit
f8692f02ef
13 changed files with 277 additions and 64 deletions
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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,26 +782,58 @@ public class RestApi {
|
|||
Log.v(TAG, "onSyncPreconditionChanged: Event fired.");
|
||||
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(mContext);
|
||||
synchronized (mConfigLock) {
|
||||
if (mConfig == null || mConfig.folders == null) {
|
||||
Boolean configChanged = false;
|
||||
|
||||
// Check if the config has been loaded.
|
||||
if (mConfig == null) {
|
||||
Log.d(TAG, "onSyncPreconditionChanged: mConfig is not ready yet.");
|
||||
return;
|
||||
}
|
||||
|
||||
// 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
|
||||
);
|
||||
if (folderCustomSyncConditionsEnabled) {
|
||||
Boolean syncConditionsMet = runConditionMonitor.checkObjectSyncConditions(
|
||||
Constants.PREF_OBJECT_PREFIX_FOLDER + folder.id
|
||||
);
|
||||
Log.v(TAG, "onSyncPreconditionChanged: syncFolder(" + folder.id + ")=" + (syncConditionsMet ? "1" : "0"));
|
||||
if (folder.paused != !syncConditionsMet) {
|
||||
folder.paused = !syncConditionsMet;
|
||||
configChanged = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Log.d(TAG, "onSyncPreconditionChanged: mConfig.folders is not ready yet.");
|
||||
return;
|
||||
}
|
||||
Boolean configChanged = false;
|
||||
for (Folder folder : mConfig.folders) {
|
||||
Boolean folderCustomSyncConditionsEnabled = sharedPreferences.getBoolean(
|
||||
Constants.DYN_PREF_OBJECT_CUSTOM_SYNC_CONDITIONS(Constants.PREF_OBJECT_PREFIX_FOLDER + folder.id), false
|
||||
);
|
||||
if (folderCustomSyncConditionsEnabled) {
|
||||
Boolean syncConditionsMet = runConditionMonitor.checkObjectSyncConditions(
|
||||
Constants.PREF_OBJECT_PREFIX_FOLDER + folder.id
|
||||
|
||||
// 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
|
||||
);
|
||||
Log.v(TAG, "onSyncPreconditionChanged: syncFolder(" + folder.id + ")=" + (syncConditionsMet ? "1" : "0"));
|
||||
if (folder.paused != !syncConditionsMet) {
|
||||
folder.paused = !syncConditionsMet;
|
||||
configChanged = true;
|
||||
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();
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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 -->
|
||||
|
||||
|
|
|
@ -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"
|
||||
|
|
Loading…
Reference in a new issue