mirror of
https://github.com/etesync/android
synced 2025-07-29 18:08:06 +00:00
Bring back and adjust the tasks sync manager.
This commit is contained in:
parent
e9b09453f8
commit
23ab80acc0
@ -106,6 +106,19 @@
|
|||||||
android:name="android.content.SyncAdapter"
|
android:name="android.content.SyncAdapter"
|
||||||
android:resource="@xml/sync_calendars" />
|
android:resource="@xml/sync_calendars" />
|
||||||
</service>
|
</service>
|
||||||
|
<service
|
||||||
|
android:name=".syncadapter.TasksSyncAdapterService"
|
||||||
|
android:exported="true"
|
||||||
|
android:process=":sync"
|
||||||
|
tools:ignore="ExportedService">
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.content.SyncAdapter"/>
|
||||||
|
</intent-filter>
|
||||||
|
|
||||||
|
<meta-data
|
||||||
|
android:name="android.content.SyncAdapter"
|
||||||
|
android:resource="@xml/sync_tasks"/>
|
||||||
|
</service>
|
||||||
|
|
||||||
<!-- Address book account -->
|
<!-- Address book account -->
|
||||||
<service
|
<service
|
||||||
|
@ -8,14 +8,20 @@
|
|||||||
|
|
||||||
package com.etesync.syncadapter;
|
package com.etesync.syncadapter;
|
||||||
|
|
||||||
|
import android.accounts.Account;
|
||||||
|
import android.accounts.AccountManager;
|
||||||
import android.annotation.SuppressLint;
|
import android.annotation.SuppressLint;
|
||||||
import android.content.BroadcastReceiver;
|
import android.content.BroadcastReceiver;
|
||||||
|
import android.content.ContentResolver;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
|
import android.os.Bundle;
|
||||||
import android.support.annotation.NonNull;
|
import android.support.annotation.NonNull;
|
||||||
|
|
||||||
import com.etesync.syncadapter.resource.LocalTaskList;
|
import com.etesync.syncadapter.resource.LocalTaskList;
|
||||||
|
|
||||||
|
import at.bitfire.ical4android.TaskProvider;
|
||||||
|
|
||||||
public class PackageChangedReceiver extends BroadcastReceiver {
|
public class PackageChangedReceiver extends BroadcastReceiver {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -31,7 +37,17 @@ public class PackageChangedReceiver extends BroadcastReceiver {
|
|||||||
App.log.info("Package (un)installed; OpenTasks provider now available = " + tasksInstalled);
|
App.log.info("Package (un)installed; OpenTasks provider now available = " + tasksInstalled);
|
||||||
|
|
||||||
// check all accounts and (de)activate OpenTasks if a CalDAV service is defined
|
// check all accounts and (de)activate OpenTasks if a CalDAV service is defined
|
||||||
// FIXME: Do something if we ever bring back tasks.
|
AccountManager am = AccountManager.get(context);
|
||||||
|
for (Account account : am.getAccountsByType(App.getAccountType())) {
|
||||||
|
if (tasksInstalled) {
|
||||||
|
if (ContentResolver.getIsSyncable(account, TaskProvider.ProviderName.OpenTasks.authority) <= 0) {
|
||||||
|
ContentResolver.setIsSyncable(account, TaskProvider.ProviderName.OpenTasks.authority, 1);
|
||||||
|
ContentResolver.setSyncAutomatically(account, TaskProvider.ProviderName.OpenTasks.authority, true);
|
||||||
|
ContentResolver.addPeriodicSync(account, TaskProvider.ProviderName.OpenTasks.authority, new Bundle(), Constants.DEFAULT_SYNC_INTERVAL);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ContentResolver.setIsSyncable(account, TaskProvider.ProviderName.OpenTasks.authority, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -31,7 +31,8 @@ public class CollectionInfo implements Serializable {
|
|||||||
|
|
||||||
public enum Type {
|
public enum Type {
|
||||||
ADDRESS_BOOK,
|
ADDRESS_BOOK,
|
||||||
CALENDAR
|
CALENDAR,
|
||||||
|
TASK_LIST,
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME: Shouldn't be exposed, as it's already saved in the journal. We just expose it for when we save for db.
|
// FIXME: Shouldn't be exposed, as it's already saved in the journal. We just expose it for when we save for db.
|
||||||
|
@ -0,0 +1,159 @@
|
|||||||
|
/*
|
||||||
|
* Copyright © 2013 – 2015 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 com.etesync.syncadapter.syncadapter;
|
||||||
|
|
||||||
|
import android.accounts.Account;
|
||||||
|
import android.content.AbstractThreadedSyncAdapter;
|
||||||
|
import android.content.ContentProviderClient;
|
||||||
|
import android.content.ContentResolver;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.content.SyncResult;
|
||||||
|
import android.database.sqlite.SQLiteException;
|
||||||
|
import android.os.Bundle;
|
||||||
|
|
||||||
|
import com.etesync.syncadapter.AccountSettings;
|
||||||
|
import com.etesync.syncadapter.App;
|
||||||
|
import com.etesync.syncadapter.Constants;
|
||||||
|
import com.etesync.syncadapter.NotificationHelper;
|
||||||
|
import com.etesync.syncadapter.R;
|
||||||
|
import com.etesync.syncadapter.journalmanager.Exceptions;
|
||||||
|
import com.etesync.syncadapter.model.CollectionInfo;
|
||||||
|
import com.etesync.syncadapter.model.JournalEntity;
|
||||||
|
import com.etesync.syncadapter.model.JournalModel;
|
||||||
|
import com.etesync.syncadapter.model.ServiceEntity;
|
||||||
|
import com.etesync.syncadapter.resource.LocalTaskList;
|
||||||
|
import com.etesync.syncadapter.ui.DebugInfoActivity;
|
||||||
|
|
||||||
|
import org.dmfs.provider.tasks.TaskContract;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
|
||||||
|
import at.bitfire.ical4android.CalendarStorageException;
|
||||||
|
import at.bitfire.ical4android.TaskProvider;
|
||||||
|
import io.requery.Persistable;
|
||||||
|
import io.requery.sql.EntityDataStore;
|
||||||
|
import lombok.Cleanup;
|
||||||
|
import okhttp3.HttpUrl;
|
||||||
|
|
||||||
|
import static com.etesync.syncadapter.Constants.KEY_ACCOUNT;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Synchronization manager for CalDAV collections; handles tasks ({@code VTODO}).
|
||||||
|
*/
|
||||||
|
public class TasksSyncAdapterService extends SyncAdapterService {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected AbstractThreadedSyncAdapter syncAdapter() {
|
||||||
|
return new SyncAdapter(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static class SyncAdapter extends SyncAdapterService.SyncAdapter {
|
||||||
|
|
||||||
|
public SyncAdapter(Context context) {
|
||||||
|
super(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPerformSync(Account account, Bundle extras, String authority, ContentProviderClient providerClient, SyncResult syncResult) {
|
||||||
|
super.onPerformSync(account, extras, authority, providerClient, syncResult);
|
||||||
|
|
||||||
|
NotificationHelper notificationManager = new NotificationHelper(getContext(), "journals-tasks", Constants.NOTIFICATION_CALENDAR_SYNC);
|
||||||
|
notificationManager.cancel();
|
||||||
|
|
||||||
|
try {
|
||||||
|
@Cleanup TaskProvider provider = TaskProvider.acquire(getContext().getContentResolver(), TaskProvider.ProviderName.OpenTasks);
|
||||||
|
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;
|
||||||
|
|
||||||
|
new RefreshCollections(account, CollectionInfo.Type.CALENDAR).run();
|
||||||
|
|
||||||
|
updateLocalTaskLists(provider, account, settings);
|
||||||
|
|
||||||
|
HttpUrl principal = HttpUrl.get(settings.getUri());
|
||||||
|
|
||||||
|
for (LocalTaskList taskList : (LocalTaskList[]) LocalTaskList.find(account, provider, LocalTaskList.Factory.INSTANCE, TaskContract.TaskLists.SYNC_ENABLED + "!=0", null)) {
|
||||||
|
App.log.info("Synchronizing task list #" + taskList.getId() + " [" + taskList.getSyncId() + "]");
|
||||||
|
TasksSyncManager syncManager = new TasksSyncManager(getContext(), account, settings, extras, authority, provider, syncResult, taskList, principal);
|
||||||
|
syncManager.performSync();
|
||||||
|
}
|
||||||
|
} catch (Exceptions.ServiceUnavailableException e) {
|
||||||
|
syncResult.stats.numIoExceptions++;
|
||||||
|
syncResult.delayUntil = (e.retryAfter > 0) ? e.retryAfter : Constants.DEFAULT_RETRY_DELAY;
|
||||||
|
} catch (Exception | OutOfMemoryError e) {
|
||||||
|
if (e instanceof CalendarStorageException || e instanceof SQLiteException) {
|
||||||
|
App.log.log(Level.SEVERE, "Couldn't prepare local calendars", e);
|
||||||
|
syncResult.databaseError = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
int syncPhase = R.string.sync_phase_journals;
|
||||||
|
String title = getContext().getString(R.string.sync_error_calendar, account.name);
|
||||||
|
|
||||||
|
notificationManager.setThrowable(e);
|
||||||
|
|
||||||
|
final Intent detailsIntent = notificationManager.getDetailsIntent();
|
||||||
|
detailsIntent.putExtra(KEY_ACCOUNT, account);
|
||||||
|
if (!(e instanceof Exceptions.UnauthorizedException)) {
|
||||||
|
detailsIntent.putExtra(DebugInfoActivity.KEY_AUTHORITY, authority);
|
||||||
|
detailsIntent.putExtra(DebugInfoActivity.KEY_PHASE, syncPhase);
|
||||||
|
}
|
||||||
|
|
||||||
|
notificationManager.notify(title, getContext().getString(syncPhase));
|
||||||
|
}
|
||||||
|
|
||||||
|
App.log.info("Task sync complete");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateLocalTaskLists(TaskProvider provider, Account account, AccountSettings settings) throws CalendarStorageException {
|
||||||
|
EntityDataStore<Persistable> data = ((App) getContext().getApplicationContext()).getData();
|
||||||
|
ServiceEntity service = JournalModel.Service.fetch(data, account.name, CollectionInfo.Type.TASK_LIST);
|
||||||
|
|
||||||
|
Map<String, JournalEntity> remote = new HashMap<>();
|
||||||
|
List<JournalEntity> remoteJournals = JournalEntity.getJournals(data, service);
|
||||||
|
for (JournalEntity journalEntity : remoteJournals) {
|
||||||
|
remote.put(journalEntity.getUid(), journalEntity);
|
||||||
|
}
|
||||||
|
|
||||||
|
LocalTaskList[] local = (LocalTaskList[])LocalTaskList.find(account, provider, LocalTaskList.Factory.INSTANCE, null, null);
|
||||||
|
|
||||||
|
boolean updateColors = settings.getManageCalendarColors();
|
||||||
|
|
||||||
|
// delete obsolete local task lists
|
||||||
|
for (LocalTaskList list : local) {
|
||||||
|
String url = list.getSyncId();
|
||||||
|
if (!remote.containsKey(url)) {
|
||||||
|
App.log.fine("Deleting obsolete local task list" + url);
|
||||||
|
list.delete();
|
||||||
|
} else {
|
||||||
|
// remote CollectionInfo found for this local collection, update data
|
||||||
|
JournalEntity journalEntity = remote.get(url);
|
||||||
|
App.log.fine("Updating local task list " + url + " with " + journalEntity);
|
||||||
|
list.update(journalEntity, updateColors);
|
||||||
|
// we already have a local task list for this remote collection, don't take into consideration anymore
|
||||||
|
remote.remove(url);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// create new local task lists
|
||||||
|
for (String url : remote.keySet()) {
|
||||||
|
JournalEntity journalEntity = remote.get(url);
|
||||||
|
App.log.info("Adding local task list " + journalEntity);
|
||||||
|
LocalTaskList.create(account, provider, journalEntity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,130 @@
|
|||||||
|
/*
|
||||||
|
* Copyright © 2013 – 2015 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 com.etesync.syncadapter.syncadapter;
|
||||||
|
|
||||||
|
import android.accounts.Account;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.SyncResult;
|
||||||
|
import android.os.Bundle;
|
||||||
|
|
||||||
|
import com.etesync.syncadapter.AccountSettings;
|
||||||
|
import com.etesync.syncadapter.App;
|
||||||
|
import com.etesync.syncadapter.Constants;
|
||||||
|
import com.etesync.syncadapter.InvalidAccountException;
|
||||||
|
import com.etesync.syncadapter.R;
|
||||||
|
import com.etesync.syncadapter.journalmanager.Exceptions;
|
||||||
|
import com.etesync.syncadapter.journalmanager.JournalEntryManager;
|
||||||
|
import com.etesync.syncadapter.model.CollectionInfo;
|
||||||
|
import com.etesync.syncadapter.model.SyncEntry;
|
||||||
|
import com.etesync.syncadapter.resource.LocalResource;
|
||||||
|
import com.etesync.syncadapter.resource.LocalTask;
|
||||||
|
import com.etesync.syncadapter.resource.LocalTaskList;
|
||||||
|
|
||||||
|
import org.apache.commons.codec.Charsets;
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.nio.charset.Charset;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
|
||||||
|
import at.bitfire.ical4android.CalendarStorageException;
|
||||||
|
import at.bitfire.ical4android.InvalidCalendarException;
|
||||||
|
import at.bitfire.ical4android.Task;
|
||||||
|
import at.bitfire.ical4android.TaskProvider;
|
||||||
|
import at.bitfire.vcard4android.ContactsStorageException;
|
||||||
|
import okhttp3.HttpUrl;
|
||||||
|
|
||||||
|
public class TasksSyncManager extends SyncManager {
|
||||||
|
final private HttpUrl remote;
|
||||||
|
|
||||||
|
protected static final int MAX_MULTIGET = 30;
|
||||||
|
|
||||||
|
final protected TaskProvider provider;
|
||||||
|
|
||||||
|
|
||||||
|
public TasksSyncManager(Context context, Account account, AccountSettings settings, Bundle extras, String authority, TaskProvider provider, SyncResult result, LocalTaskList taskList, HttpUrl remote) throws InvalidAccountException, Exceptions.IntegrityException, Exceptions.GenericCryptoException {
|
||||||
|
super(context, account, settings, extras, authority, result, taskList.getName(), CollectionInfo.Type.TASK_LIST, account.name);
|
||||||
|
this.provider = provider;
|
||||||
|
localCollection = taskList;
|
||||||
|
this.remote = remote;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected int notificationId() {
|
||||||
|
return Constants.NOTIFICATION_TASK_SYNC;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String getSyncErrorTitle() {
|
||||||
|
return context.getString(R.string.sync_error_tasks, account.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String getSyncSuccessfullyTitle() {
|
||||||
|
return context.getString(R.string.sync_successfully_tasks, info.displayName,
|
||||||
|
account.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean prepare() throws CalendarStorageException, ContactsStorageException {
|
||||||
|
if (!super.prepare())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
journal = new JournalEntryManager(httpClient, remote, localTaskList().getName());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void prepareDirty() throws CalendarStorageException, ContactsStorageException {
|
||||||
|
super.prepareDirty();
|
||||||
|
}
|
||||||
|
|
||||||
|
// helpers
|
||||||
|
|
||||||
|
private LocalTaskList localTaskList() { return ((LocalTaskList)localCollection); }
|
||||||
|
|
||||||
|
protected void processSyncEntry(SyncEntry cEntry) throws IOException, ContactsStorageException, CalendarStorageException, InvalidCalendarException {
|
||||||
|
InputStream is = new ByteArrayInputStream(cEntry.getContent().getBytes(Charsets.UTF_8));
|
||||||
|
|
||||||
|
Task[] tasks = Task.fromStream(is, Charsets.UTF_8);
|
||||||
|
if (tasks.length == 0) {
|
||||||
|
App.log.warning("Received VCard without data, ignoring");
|
||||||
|
return;
|
||||||
|
} else if (tasks.length > 1)
|
||||||
|
App.log.warning("Received multiple VCALs, using first one");
|
||||||
|
|
||||||
|
Task task = tasks[0];
|
||||||
|
LocalTask local = (LocalTask) localCollection.getByUid(task.uid);
|
||||||
|
|
||||||
|
if (cEntry.isAction(SyncEntry.Actions.ADD) || cEntry.isAction(SyncEntry.Actions.CHANGE)) {
|
||||||
|
processTask(task, local);
|
||||||
|
} else {
|
||||||
|
App.log.info("Removing local record #" + local.getId() + " which has been deleted on the server");
|
||||||
|
local.delete();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private LocalResource processTask(final Task newData, LocalTask localTask) throws IOException, ContactsStorageException, CalendarStorageException {
|
||||||
|
// delete local event, if it exists
|
||||||
|
if (localTask != null) {
|
||||||
|
App.log.info("Updating " + newData.uid + " in local calendar");
|
||||||
|
localTask.setETag(newData.uid);
|
||||||
|
localTask.update(newData);
|
||||||
|
syncResult.stats.numUpdates++;
|
||||||
|
} else {
|
||||||
|
App.log.info("Adding " + newData.uid + " to local calendar");
|
||||||
|
localTask = new LocalTask(localTaskList(), newData, newData.uid, newData.uid);
|
||||||
|
localTask.add();
|
||||||
|
syncResult.stats.numInserts++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return localTask;
|
||||||
|
}
|
||||||
|
}
|
@ -34,6 +34,8 @@ import com.etesync.syncadapter.R;
|
|||||||
import com.etesync.syncadapter.ui.setup.LoginCredentials;
|
import com.etesync.syncadapter.ui.setup.LoginCredentials;
|
||||||
import com.etesync.syncadapter.ui.setup.LoginCredentialsChangeFragment;
|
import com.etesync.syncadapter.ui.setup.LoginCredentialsChangeFragment;
|
||||||
|
|
||||||
|
import at.bitfire.ical4android.TaskProvider;
|
||||||
|
|
||||||
import static com.etesync.syncadapter.Constants.KEY_ACCOUNT;
|
import static com.etesync.syncadapter.Constants.KEY_ACCOUNT;
|
||||||
|
|
||||||
public class AccountSettingsActivity extends BaseActivity {
|
public class AccountSettingsActivity extends BaseActivity {
|
||||||
@ -150,6 +152,27 @@ public class AccountSettingsActivity extends BaseActivity {
|
|||||||
prefSyncCalendars.setSummary(R.string.settings_sync_summary_not_available);
|
prefSyncCalendars.setSummary(R.string.settings_sync_summary_not_available);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final ListPreference prefSyncTasks = (ListPreference)findPreference("sync_interval_tasks");
|
||||||
|
final Long syncIntervalTasks = settings.getSyncInterval(TaskProvider.ProviderName.OpenTasks.authority);
|
||||||
|
if (syncIntervalTasks != null) {
|
||||||
|
prefSyncTasks.setValue(syncIntervalTasks.toString());
|
||||||
|
if (syncIntervalTasks == AccountSettings.SYNC_INTERVAL_MANUALLY)
|
||||||
|
prefSyncTasks.setSummary(R.string.settings_sync_summary_manually);
|
||||||
|
else
|
||||||
|
prefSyncTasks.setSummary(getString(R.string.settings_sync_summary_periodically, prefSyncTasks.getEntry()));
|
||||||
|
prefSyncTasks.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
|
||||||
|
@Override
|
||||||
|
public boolean onPreferenceChange(Preference preference, Object newValue) {
|
||||||
|
settings.setSyncInterval(TaskProvider.ProviderName.OpenTasks.authority, Long.parseLong((String)newValue));
|
||||||
|
getLoaderManager().restartLoader(0, getArguments(), AccountSettingsFragment.this);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
prefSyncTasks.setEnabled(false);
|
||||||
|
prefSyncTasks.setSummary(R.string.settings_sync_summary_not_available);
|
||||||
|
}
|
||||||
|
|
||||||
final SwitchPreferenceCompat prefWifiOnly = (SwitchPreferenceCompat)findPreference("sync_wifi_only");
|
final SwitchPreferenceCompat prefWifiOnly = (SwitchPreferenceCompat)findPreference("sync_wifi_only");
|
||||||
prefWifiOnly.setChecked(settings.getSyncWifiOnly());
|
prefWifiOnly.setChecked(settings.getSyncWifiOnly());
|
||||||
prefWifiOnly.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
|
prefWifiOnly.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
|
||||||
|
@ -224,6 +224,7 @@
|
|||||||
<string name="settings_sync_summary_periodically" tools:ignore="PluralsCandidate">%s and immediately on local changes</string>
|
<string name="settings_sync_summary_periodically" tools:ignore="PluralsCandidate">%s and immediately on local changes</string>
|
||||||
<string name="settings_sync_summary_not_available">Not available</string>
|
<string name="settings_sync_summary_not_available">Not available</string>
|
||||||
<string name="settings_sync_interval_calendars">Calendars sync. interval</string>
|
<string name="settings_sync_interval_calendars">Calendars sync. interval</string>
|
||||||
|
<string name="settings_sync_interval_tasks">Tasks sync. interval</string>
|
||||||
<string-array name="settings_sync_interval_seconds" translateable="false">
|
<string-array name="settings_sync_interval_seconds" translateable="false">
|
||||||
<item>-1</item>
|
<item>-1</item>
|
||||||
<item>300</item>
|
<item>300</item>
|
||||||
@ -291,6 +292,7 @@
|
|||||||
<string name="sync_error_permissions_text">Additional permissions required</string>
|
<string name="sync_error_permissions_text">Additional permissions required</string>
|
||||||
<string name="sync_error_calendar">Calendar sync failed (%s)</string>
|
<string name="sync_error_calendar">Calendar sync failed (%s)</string>
|
||||||
<string name="sync_error_contacts">Contacts sync failed (%s)</string>
|
<string name="sync_error_contacts">Contacts sync failed (%s)</string>
|
||||||
|
<string name="sync_error_tasks">Tasks sync failed (%s)</string>
|
||||||
<string name="sync_error">Error while %s</string>
|
<string name="sync_error">Error while %s</string>
|
||||||
<string name="sync_error_integrity">Integrity error while %s</string>
|
<string name="sync_error_integrity">Integrity error while %s</string>
|
||||||
<string name="sync_error_http_dav">Server error while %s</string>
|
<string name="sync_error_http_dav">Server error while %s</string>
|
||||||
@ -308,6 +310,7 @@
|
|||||||
<string name="sync_phase_post_processing">post processing</string>
|
<string name="sync_phase_post_processing">post processing</string>
|
||||||
<string name="sync_error_unauthorized">Authentication failed</string>
|
<string name="sync_error_unauthorized">Authentication failed</string>
|
||||||
<string name="sync_error_user_inactive">User is inactive</string>
|
<string name="sync_error_user_inactive">User is inactive</string>
|
||||||
|
<string name="sync_successfully_tasks" formatted="false">Task list \"%s\" modified (%s)</string>
|
||||||
<string name="sync_successfully_calendar" formatted="false">Calendar \"%s\" modified (%s)</string>
|
<string name="sync_successfully_calendar" formatted="false">Calendar \"%s\" modified (%s)</string>
|
||||||
<string name="sync_successfully_contacts" formatted="false">Contacts modified (%s)</string>
|
<string name="sync_successfully_contacts" formatted="false">Contacts modified (%s)</string>
|
||||||
<string name="sync_successfully_modified" formatted="false">%s modified.</string>
|
<string name="sync_successfully_modified" formatted="false">%s modified.</string>
|
||||||
|
@ -50,6 +50,13 @@
|
|||||||
android:entries="@array/settings_sync_interval_names"
|
android:entries="@array/settings_sync_interval_names"
|
||||||
android:entryValues="@array/settings_sync_interval_seconds" />
|
android:entryValues="@array/settings_sync_interval_seconds" />
|
||||||
|
|
||||||
|
<ListPreference
|
||||||
|
android:key="sync_interval_tasks"
|
||||||
|
android:persistent="false"
|
||||||
|
android:title="@string/settings_sync_interval_tasks"
|
||||||
|
android:entries="@array/settings_sync_interval_names"
|
||||||
|
android:entryValues="@array/settings_sync_interval_seconds" />
|
||||||
|
|
||||||
<SwitchPreferenceCompat
|
<SwitchPreferenceCompat
|
||||||
android:key="sync_wifi_only"
|
android:key="sync_wifi_only"
|
||||||
android:persistent="false"
|
android:persistent="false"
|
||||||
|
15
app/src/main/res/xml/sync_tasks.xml
Normal file
15
app/src/main/res/xml/sync_tasks.xml
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
<!--
|
||||||
|
~ Copyright (c) 2013 – 2015 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
|
||||||
|
-->
|
||||||
|
|
||||||
|
<sync-adapter xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:accountType="com.etesync.syncadapter"
|
||||||
|
android:contentAuthority="org.dmfs.tasks"
|
||||||
|
android:allowParallelSyncs="true"
|
||||||
|
android:supportsUploading="true"
|
||||||
|
android:isAlwaysSyncable="true"
|
||||||
|
android:userVisible="true" />
|
Loading…
Reference in New Issue
Block a user