1
0
mirror of https://github.com/etesync/android synced 2024-11-22 07:58:09 +00:00

New feature: only sync in WiFi

* new setting: only sync in WiFi (or when sync is triggered manually)
* new setting: only sync in specific WiFI (by SSID)
* lower default sync interval when account is created to 4 hours (was 1 day)
* version bump to 1.0.6
This commit is contained in:
Ricki Hirner 2016-04-06 21:04:16 +02:00
parent 03ee9a037b
commit 6ffa6fa9a7
17 changed files with 179 additions and 99 deletions

View File

@ -17,8 +17,8 @@ android {
minSdkVersion 14
targetSdkVersion 22
versionCode 95
versionName "1.0.5"
versionCode 96
versionName "1.0.6"
buildConfigField "long", "buildTime", System.currentTimeMillis() + "L"
}

View File

@ -12,6 +12,8 @@
android:installLocation="internalOnly">
<!-- normal permissions -->
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.GET_ACCOUNTS"/>
<uses-permission android:name="android.permission.MANAGE_ACCOUNTS"/>

View File

@ -24,6 +24,7 @@ import android.os.Bundle;
import android.provider.CalendarContract;
import android.provider.ContactsContract;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v7.app.NotificationCompat;
import android.text.TextUtils;
@ -49,10 +50,13 @@ import okhttp3.HttpUrl;
public class AccountSettings {
private final static int CURRENT_VERSION = 3;
private final static String
KEY_SETTINGS_VERSION = "version",
KEY_SETTINGS_VERSION = "version",
KEY_USERNAME = "user_name",
KEY_AUTH_PREEMPTIVE = "auth_preemptive";
KEY_USERNAME = "user_name",
KEY_AUTH_PREEMPTIVE = "auth_preemptive",
KEY_WIFI_ONLY = "wifi_only", // sync on WiFi only (default: false)
KEY_WIFI_ONLY_SSID = "wifi_only_ssid"; // restrict sync to specific WiFi SSID
/** Time range limitation to the past [in days]
value = null default value (DEFAULT_TIME_RANGE_PAST_DAYS)
@ -159,6 +163,26 @@ public class AccountSettings {
}
}
public boolean getSyncWifiOnly() {
return accountManager.getUserData(account, KEY_WIFI_ONLY) != null;
}
public void setSyncWiFiOnly(boolean wiFiOnly) {
accountManager.setUserData(account, KEY_WIFI_ONLY, wiFiOnly ? "1" : null);
}
@Nullable
public String getSyncWifiOnlySSID() {
return accountManager.getUserData(account, KEY_WIFI_ONLY_SSID);
}
public void setSyncWifiOnlySSID(String ssid) {
accountManager.setUserData(account, KEY_WIFI_ONLY_SSID, ssid);
}
// CalDAV settings
public Integer getTimeRangePastDays() {
String strDays = accountManager.getUserData(account, KEY_TIME_RANGE_PAST_DAYS);
if (strDays != null) {
@ -173,8 +197,7 @@ public class AccountSettings {
}
public boolean getManageCalendarColors() {
String manage = accountManager.getUserData(account, KEY_MANAGE_CALENDAR_COLORS);
return manage == null;
return accountManager.getUserData(account, KEY_MANAGE_CALENDAR_COLORS) == null;
}
public void setManageCalendarColors(boolean manage) {

View File

@ -1,29 +0,0 @@
/*
* Copyright © 2013 2016 Ricki Hirner (bitfire web engineering).
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the GNU Public License v3.0
* which accompanies this distribution, and is available at
* http://www.gnu.org/licenses/gpl.html
*/
package at.bitfire.davdroid.model;
import android.content.ContentValues;
import lombok.ToString;
@ToString
public class HomeSet {
public long id, serviceID;
public String URL;
public static HomeSet fromDB(ContentValues values) {
HomeSet homeSet = new HomeSet();
homeSet.id = values.getAsLong(ServiceDB.HomeSets.ID);
homeSet.serviceID = values.getAsLong(ServiceDB.HomeSets.SERVICE_ID);
homeSet.URL = values.getAsString(ServiceDB.HomeSets.URL);
return homeSet;
}
}

View File

@ -1,29 +0,0 @@
/*
* Copyright © 2013 2016 Ricki Hirner (bitfire web engineering).
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the GNU Public License v3.0
* which accompanies this distribution, and is available at
* http://www.gnu.org/licenses/gpl.html
*/
package at.bitfire.davdroid.model;
import android.content.ContentValues;
public class Service {
public long id;
public String accountName, service, principal;
public long lastRefresh;
public static Service fromDB(ContentValues values) {
Service service = new Service();
service.id = values.getAsLong(ServiceDB.Services.ID);
service.accountName = values.getAsString(ServiceDB.Services.ACCOUNT_NAME);
service.service = values.getAsString(ServiceDB.Services.SERVICE);
service.principal = values.getAsString(ServiceDB.Services.PRINCIPAL);
return service;
}
}

View File

@ -9,12 +9,9 @@
package at.bitfire.davdroid.syncadapter;
import android.accounts.Account;
import android.content.ContentValues;
import android.content.Context;
import android.content.SyncResult;
import android.os.Bundle;
import android.provider.CalendarContract.Calendars;
import android.text.TextUtils;
import org.apache.commons.codec.Charsets;
import org.apache.commons.lang3.StringUtils;
@ -34,12 +31,11 @@ import at.bitfire.dav4android.DavCalendar;
import at.bitfire.dav4android.DavResource;
import at.bitfire.dav4android.exception.DavException;
import at.bitfire.dav4android.exception.HttpException;
import at.bitfire.dav4android.property.CalendarColor;
import at.bitfire.dav4android.property.CalendarData;
import at.bitfire.dav4android.property.DisplayName;
import at.bitfire.dav4android.property.GetCTag;
import at.bitfire.dav4android.property.GetContentType;
import at.bitfire.dav4android.property.GetETag;
import at.bitfire.davdroid.AccountSettings;
import at.bitfire.davdroid.App;
import at.bitfire.davdroid.ArrayUtils;
import at.bitfire.davdroid.Constants;
@ -63,8 +59,8 @@ public class CalendarSyncManager extends SyncManager {
protected static final int MAX_MULTIGET = 20;
public CalendarSyncManager(Context context, Account account, Bundle extras, String authority, SyncResult result, LocalCalendar calendar) throws InvalidAccountException {
super(context, account, extras, authority, result, "calendar/" + calendar.getId());
public CalendarSyncManager(Context context, Account account, AccountSettings settings, Bundle extras, String authority, SyncResult result, LocalCalendar calendar) throws InvalidAccountException {
super(context, account, settings, extras, authority, result, "calendar/" + calendar.getId());
localCollection = calendar;
}

View File

@ -10,6 +10,7 @@ package at.bitfire.davdroid.syncadapter;
import android.accounts.Account;
import android.content.AbstractThreadedSyncAdapter;
import android.content.ContentProviderClient;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
import android.content.SyncResult;
@ -56,11 +57,15 @@ public class CalendarsSyncAdapterService extends SyncAdapterService {
super.onPerformSync(account, extras, authority, provider, syncResult);
try {
updateLocalCalendars(provider, account);
AccountSettings settings = new AccountSettings(getContext(), account);
if (!extras.containsKey(ContentResolver.SYNC_EXTRAS_MANUAL) && !checkSyncConditions(settings))
return;
updateLocalCalendars(provider, account, settings);
for (LocalCalendar calendar : (LocalCalendar[])LocalCalendar.find(account, provider, LocalCalendar.Factory.INSTANCE, CalendarContract.Calendars.SYNC_EVENTS + "!=0", null)) {
App.log.info("Synchronizing calendar #" + calendar.getId() + ", URL: " + calendar.getName());
CalendarSyncManager syncManager = new CalendarSyncManager(getContext(), account, extras, authority, syncResult, calendar);
CalendarSyncManager syncManager = new CalendarSyncManager(getContext(), account, settings, extras, authority, syncResult, calendar);
syncManager.performSync();
}
} catch (CalendarStorageException e) {
@ -73,7 +78,7 @@ public class CalendarsSyncAdapterService extends SyncAdapterService {
App.log.info("Calendar sync complete");
}
private void updateLocalCalendars(ContentProviderClient provider, Account account) throws CalendarStorageException, InvalidAccountException {
private void updateLocalCalendars(ContentProviderClient provider, Account account, AccountSettings settings) throws CalendarStorageException {
SQLiteOpenHelper dbHelper = new ServiceDB.OpenHelper(getContext());
try {
// enumerate remote and local calendars
@ -83,7 +88,6 @@ public class CalendarsSyncAdapterService extends SyncAdapterService {
LocalCalendar[] local = (LocalCalendar[])LocalCalendar.find(account, provider, LocalCalendar.Factory.INSTANCE, null, null);
AccountSettings settings = new AccountSettings(getContext(), account);
boolean updateColors = settings.getManageCalendarColors();
// delete obsolete local calendar

View File

@ -10,6 +10,7 @@ package at.bitfire.davdroid.syncadapter;
import android.accounts.Account;
import android.content.AbstractThreadedSyncAdapter;
import android.content.ContentProviderClient;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
import android.content.SyncResult;
@ -23,6 +24,7 @@ import android.support.annotation.Nullable;
import java.util.logging.Level;
import at.bitfire.davdroid.AccountSettings;
import at.bitfire.davdroid.App;
import at.bitfire.davdroid.InvalidAccountException;
import at.bitfire.davdroid.model.CollectionInfo;
@ -50,13 +52,17 @@ public class ContactsSyncAdapterService extends SyncAdapterService {
SQLiteOpenHelper dbHelper = new ServiceDB.OpenHelper(getContext());
try {
AccountSettings settings = new AccountSettings(getContext(), account);
if (!extras.containsKey(ContentResolver.SYNC_EXTRAS_MANUAL) && !checkSyncConditions(settings))
return;
SQLiteDatabase db = dbHelper.getReadableDatabase();
Long service = getService(db, account);
if (service != null) {
CollectionInfo remote = remoteAddressBook(db, service);
if (remote != null)
try {
ContactsSyncManager syncManager = new ContactsSyncManager(getContext(), account, extras, authority, provider, syncResult, remote);
ContactsSyncManager syncManager = new ContactsSyncManager(getContext(), account, settings, extras, authority, provider, syncResult, remote);
syncManager.performSync();
} catch(InvalidAccountException e) {
App.log.log(Level.SEVERE, "Couldn't get account settings", e);
@ -65,6 +71,8 @@ public class ContactsSyncAdapterService extends SyncAdapterService {
App.log.info("No address book collection selected for synchronization");
} else
App.log.info("No CardDAV service found in DB");
} catch (InvalidAccountException e) {
App.log.log(Level.SEVERE, "Couldn't get account settings", e);
} finally {
dbHelper.close();
}

View File

@ -37,6 +37,7 @@ import at.bitfire.dav4android.property.GetCTag;
import at.bitfire.dav4android.property.GetContentType;
import at.bitfire.dav4android.property.GetETag;
import at.bitfire.dav4android.property.SupportedAddressData;
import at.bitfire.davdroid.AccountSettings;
import at.bitfire.davdroid.App;
import at.bitfire.davdroid.ArrayUtils;
import at.bitfire.davdroid.Constants;
@ -71,8 +72,8 @@ public class ContactsSyncManager extends SyncManager {
private boolean hasVCard4;
public ContactsSyncManager(Context context, Account account, Bundle extras, String authority, ContentProviderClient provider, SyncResult result, CollectionInfo remote) throws InvalidAccountException {
super(context, account, extras, authority, result, "addressBook");
public ContactsSyncManager(Context context, Account account, AccountSettings settings, Bundle extras, String authority, ContentProviderClient provider, SyncResult result, CollectionInfo remote) throws InvalidAccountException {
super(context, account, settings, extras, authority, result, "addressBook");
this.provider = provider;
this.remote = remote;
}

View File

@ -16,9 +16,19 @@ import android.content.Context;
import android.content.Intent;
import android.content.SyncResult;
import android.database.sqlite.SQLiteDatabase;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
import android.os.Bundle;
import android.os.IBinder;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.net.ConnectivityManagerCompat;
import android.text.TextUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.text.WordUtils;
import java.util.logging.Level;
@ -46,11 +56,39 @@ public abstract class SyncAdapterService extends Service {
@Override
public void onPerformSync(Account account, Bundle extras, String authority, ContentProviderClient provider, SyncResult syncResult) {
App.log.info("Starting " + authority + " sync");
App.log.info("Sync for " + authority + " has been initiated");
// required for dav4android (ServiceLoader)
Thread.currentThread().setContextClassLoader(getContext().getClassLoader());
}
protected boolean checkSyncConditions(@NonNull AccountSettings settings) {
if (settings.getSyncWifiOnly()) {
ConnectivityManager cm = (ConnectivityManager)getContext().getSystemService(CONNECTIVITY_SERVICE);
NetworkInfo network = cm.getActiveNetworkInfo();
if (network == null) {
App.log.info("No network available, stopping");
return false;
}
if (network.getType() != ConnectivityManager.TYPE_WIFI || !network.isConnected()) {
App.log.info("Not on connected WiFi, stopping");
return false;
}
String onlySSID = settings.getSyncWifiOnlySSID();
if (onlySSID != null) {
onlySSID = "\"" + onlySSID + "\"";
WifiManager wifi = (WifiManager)getContext().getSystemService(WIFI_SERVICE);
WifiInfo info = wifi.getConnectionInfo();
if (info == null || !onlySSID.equals(info.getSSID())) {
App.log.info("Connected to wrong WiFi network (" + info.getSSID() + ", required: " + onlySSID + "), ignoring");
return false;
}
}
}
return true;
}
}
}

View File

@ -98,16 +98,14 @@ abstract public class SyncManager {
public SyncManager(Context context, Account account, Bundle extras, String authority, SyncResult syncResult, String uniqueCollectionId) throws InvalidAccountException {
public SyncManager(Context context, Account account, AccountSettings settings, Bundle extras, String authority, SyncResult syncResult, String uniqueCollectionId) throws InvalidAccountException {
this.context = context;
this.account = account;
this.settings = settings;
this.extras = extras;
this.authority = authority;
this.syncResult = syncResult;
// get account settings (for sync interval etc.)
settings = new AccountSettings(context, account);
// create HttpClient with given logger
httpClient = HttpClient.create(context, account);

View File

@ -10,6 +10,7 @@ package at.bitfire.davdroid.syncadapter;
import android.accounts.Account;
import android.content.AbstractThreadedSyncAdapter;
import android.content.ContentProviderClient;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
import android.content.SyncResult;
@ -25,6 +26,7 @@ import java.util.LinkedHashMap;
import java.util.Map;
import java.util.logging.Level;
import at.bitfire.davdroid.AccountSettings;
import at.bitfire.davdroid.App;
import at.bitfire.davdroid.InvalidAccountException;
import at.bitfire.davdroid.model.CollectionInfo;
@ -59,11 +61,15 @@ public class TasksSyncAdapterService extends SyncAdapterService {
if (provider == null)
throw new CalendarStorageException("Couldn't access OpenTasks provider");
AccountSettings settings = new AccountSettings(getContext(), account);
if (!extras.containsKey(ContentResolver.SYNC_EXTRAS_MANUAL) && !checkSyncConditions(settings))
return;
updateLocalTaskLists(provider, account);
for (LocalTaskList taskList : (LocalTaskList[])LocalTaskList.find(account, provider, LocalTaskList.Factory.INSTANCE, null, null)) {
App.log.info("Synchronizing task list #" + taskList.getId() + ", URL: " + taskList.getSyncId());
TasksSyncManager syncManager = new TasksSyncManager(getContext(), account, extras, authority, provider, syncResult, taskList);
TasksSyncManager syncManager = new TasksSyncManager(getContext(), account, settings, extras, authority, provider, syncResult, taskList);
syncManager.performSync();
}
} catch (CalendarStorageException e) {

View File

@ -9,22 +9,17 @@
package at.bitfire.davdroid.syncadapter;
import android.accounts.Account;
import android.content.ContentValues;
import android.content.Context;
import android.content.SyncResult;
import android.os.Bundle;
import android.text.TextUtils;
import org.apache.commons.codec.Charsets;
import org.apache.commons.lang3.StringUtils;
import org.dmfs.provider.tasks.TaskContract.TaskLists;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.Charset;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
@ -34,12 +29,11 @@ import at.bitfire.dav4android.DavCalendar;
import at.bitfire.dav4android.DavResource;
import at.bitfire.dav4android.exception.DavException;
import at.bitfire.dav4android.exception.HttpException;
import at.bitfire.dav4android.property.CalendarColor;
import at.bitfire.dav4android.property.CalendarData;
import at.bitfire.dav4android.property.DisplayName;
import at.bitfire.dav4android.property.GetCTag;
import at.bitfire.dav4android.property.GetContentType;
import at.bitfire.dav4android.property.GetETag;
import at.bitfire.davdroid.AccountSettings;
import at.bitfire.davdroid.App;
import at.bitfire.davdroid.ArrayUtils;
import at.bitfire.davdroid.Constants;
@ -65,8 +59,8 @@ public class TasksSyncManager extends SyncManager {
final protected TaskProvider provider;
public TasksSyncManager(Context context, Account account, Bundle extras, String authority, TaskProvider provider, SyncResult result, LocalTaskList taskList) throws InvalidAccountException {
super(context, account, extras, authority, result, "taskList/" + taskList.getId());
public TasksSyncManager(Context context, Account account, AccountSettings settings, Bundle extras, String authority, TaskProvider provider, SyncResult result, LocalTaskList taskList) throws InvalidAccountException {
super(context, account, settings, extras, authority, result, "taskList/" + taskList.getId());
this.provider = provider;
localCollection = taskList;
}

View File

@ -11,6 +11,9 @@ package at.bitfire.davdroid.ui;
import android.accounts.Account;
import android.content.DialogInterface;
import android.content.Intent;
import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
import android.os.Bundle;
import android.provider.CalendarContract;
import android.provider.ContactsContract;
@ -22,8 +25,12 @@ import android.support.v7.preference.ListPreference;
import android.support.v7.preference.Preference;
import android.support.v7.preference.PreferenceFragmentCompat;
import android.support.v7.preference.SwitchPreferenceCompat;
import android.text.TextUtils;
import android.view.MenuItem;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.logging.Level;
import at.bitfire.davdroid.AccountSettings;
@ -183,6 +190,34 @@ public class AccountSettingsActivity extends AppCompatActivity {
prefSyncTasks.setSummary(R.string.settings_sync_summary_not_available);
}
final SwitchPreferenceCompat prefWifiOnly = (SwitchPreferenceCompat)findPreference("sync_wifi_only");
prefWifiOnly.setChecked(settings.getSyncWifiOnly());
prefWifiOnly.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
@Override
public boolean onPreferenceChange(Preference preference, Object wifiOnly) {
settings.setSyncWiFiOnly((Boolean)wifiOnly);
refresh();
return false;
}
});
final EditTextPreference prefWifiOnlySSID = (EditTextPreference)findPreference("sync_wifi_only_ssid");
final String onlySSID = settings.getSyncWifiOnlySSID();
prefWifiOnlySSID.setText(onlySSID);
if (onlySSID != null)
prefWifiOnlySSID.setSummary(getString(R.string.settings_sync_wifi_only_ssid_on, onlySSID));
else
prefWifiOnlySSID.setSummary(R.string.settings_sync_wifi_only_ssid_off);
prefWifiOnlySSID.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
@Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
String ssid = (String)newValue;
settings.setSyncWifiOnlySSID(!TextUtils.isEmpty(ssid) ? ssid : null);
refresh(); return false;
}
});
// category: CalDAV
final EditTextPreference prefTimeRangePastDays = (EditTextPreference)findPreference("caldav_time_range_past_days");
Integer pastDays = settings.getTimeRangePastDays();
if (pastDays != null) {
@ -212,7 +247,8 @@ public class AccountSettingsActivity extends AppCompatActivity {
@Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
settings.setManageCalendarColors((Boolean)newValue);
refresh(); return false;
refresh();
return false;
}
});

View File

@ -32,6 +32,7 @@ import at.bitfire.davdroid.AccountSettings;
import at.bitfire.davdroid.App;
import at.bitfire.davdroid.Constants;
import at.bitfire.davdroid.DavService;
import at.bitfire.davdroid.InvalidAccountException;
import at.bitfire.davdroid.R;
import at.bitfire.davdroid.model.CollectionInfo;
import at.bitfire.davdroid.model.ServiceDB.Collections;
@ -45,6 +46,7 @@ import lombok.Cleanup;
public class AccountDetailsFragment extends Fragment {
private static final String KEY_CONFIG = "config";
private static final int DEFAULT_SYNC_INTERVAL = 4 * 3600; // 4 hours
public static AccountDetailsFragment newInstance(DavResourceFinder.Configuration config) {
AccountDetailsFragment frag = new AccountDetailsFragment();
@ -107,6 +109,8 @@ public class AccountDetailsFragment extends Fragment {
SQLiteDatabase db = dbHelper.getWritableDatabase();
db.beginTransactionNonExclusive();
try {
AccountSettings settings = new AccountSettings(getContext(), account);
Intent refreshIntent = new Intent(getActivity(), DavService.class);
refreshIntent.setAction(DavService.ACTION_REFRESH_COLLECTIONS);
@ -116,7 +120,7 @@ public class AccountDetailsFragment extends Fragment {
getActivity().startService(refreshIntent);
ContentResolver.setIsSyncable(account, ContactsContract.AUTHORITY, 1);
ContentResolver.setSyncAutomatically(account, ContactsContract.AUTHORITY, true);
settings.setSyncInterval(ContactsContract.AUTHORITY, DEFAULT_SYNC_INTERVAL);
} else
ContentResolver.setIsSyncable(account, ContactsContract.AUTHORITY, 0);
@ -126,12 +130,12 @@ public class AccountDetailsFragment extends Fragment {
getActivity().startService(refreshIntent);
ContentResolver.setIsSyncable(account, CalendarContract.AUTHORITY, 1);
ContentResolver.setSyncAutomatically(account, CalendarContract.AUTHORITY, true);
settings.setSyncInterval(CalendarContract.AUTHORITY, DEFAULT_SYNC_INTERVAL);
if (LocalTaskList.tasksProviderAvailable(getContext().getContentResolver())) {
// will only do something if OpenTasks is installed and accessible
ContentResolver.setIsSyncable(account, TaskProvider.ProviderName.OpenTasks.authority, 1);
ContentResolver.setSyncAutomatically(account, TaskProvider.ProviderName.OpenTasks.authority, true);
settings.setSyncInterval(TaskProvider.ProviderName.OpenTasks.authority, DEFAULT_SYNC_INTERVAL);
} else
// If OpenTasks is installed after DAVdroid, DAVdroid won't get task permissions and crash at every task sync
// unless we disable task sync here (before OpenTasks is available).
@ -142,6 +146,8 @@ public class AccountDetailsFragment extends Fragment {
}
db.setTransactionSuccessful();
} catch(InvalidAccountException e) {
App.log.log(Level.SEVERE, "Couldn't access account settings", e);
} finally {
db.endTransaction();
}

View File

@ -166,6 +166,13 @@
<item quantity="other">Events more than %d days in the past will be ignored</item>
</plurals>
<string name="settings_sync_time_range_past_message">Events which are more than this number of days in the past will be ignored (may be 0). Leave blank to synchronize all events.</string>
<string name="settings_sync_wifi_only">Sync over WiFi only</string>
<string name="settings_sync_wifi_only_on">Synchronization is restricted to WiFi connections</string>
<string name="settings_sync_wifi_only_off">Connection type is not taken into consideration</string>
<string name="settings_sync_wifi_only_ssid">WiFi SSID restriction</string>
<string name="settings_sync_wifi_only_ssid_on">Will only synchronize over %s</string>
<string name="settings_sync_wifi_only_ssid_off">All WiFi connections may be used</string>
<string name="settings_sync_wifi_only_ssid_message">Enter the name of a WiFi network (SSID) to restrict synchronization to this network, or leave blank for all WiFi connections.</string>
<string name="settings_manage_calendar_colors">Manage calendar colors</string>
<string name="settings_manage_calendar_colors_on">Calendar colors are managed by DAVdroid</string>
<string name="settings_manage_calendar_colors_off">Calendar colors are not set by DAVdroid</string>

View File

@ -57,6 +57,25 @@
android:entries="@array/settings_sync_interval_names"
android:entryValues="@array/settings_sync_interval_seconds" />
<SwitchPreferenceCompat
android:key="sync_wifi_only"
android:persistent="false"
android:title="@string/settings_sync_wifi_only"
android:summaryOn="@string/settings_sync_wifi_only_on"
android:summaryOff="@string/settings_sync_wifi_only_off"
/>
<EditTextPreference
android:key="sync_wifi_only_ssid"
android:dependency="sync_wifi_only"
android:persistent="false"
android:title="@string/settings_sync_wifi_only_ssid"
android:dialogMessage="@string/settings_sync_wifi_only_ssid_message"/>
</PreferenceCategory>
<PreferenceCategory android:title="CalDAV">
<EditTextPreference
android:key="caldav_time_range_past_days"
android:persistent="false"
@ -71,6 +90,6 @@
android:summaryOn="@string/settings_manage_calendar_colors_on"
android:summaryOff="@string/settings_manage_calendar_colors_off"/>
</PreferenceCategory>
</PreferenceCategory>
</PreferenceScreen>