Add GoogleTaskSyncAdapter

pull/437/head
Alex Baker 10 years ago
parent 8bf14181b5
commit b0d14a15f9

@ -1,30 +0,0 @@
package com.todoroo.astrid.gtasks.sync;
import com.todoroo.astrid.gtasks.GtasksList;
import com.todoroo.astrid.sync.SyncResultCallback;
import javax.inject.Inject;
public class GtasksSyncV2Provider {
@Inject
public GtasksSyncV2Provider() {
}
public boolean isActive() {
return false;
}
public void synchronizeActiveTasks(SyncResultCallback callback) {
}
public void synchronizeList(GtasksList list, SyncResultCallback callback) {
}
public void clearCompleted(GtasksList list, SyncResultCallback callback) {
}
}

@ -1,5 +1,7 @@
package org.tasks;
import android.accounts.Account;
import java.util.Collections;
import java.util.List;
@ -15,7 +17,7 @@ public class AccountManager {
return Collections.emptyList();
}
public boolean hasAccount(String account) {
return false;
public Account getAccount(String userName) {
return null;
}
}

@ -0,0 +1,10 @@
package org.tasks.injection;
import javax.inject.Singleton;
import dagger.Component;
@Singleton
@Component(modules = ApplicationModule.class)
public interface ApplicationComponent extends BaseApplicationComponent {
}

@ -1,10 +0,0 @@
package org.tasks.scheduling;
import javax.inject.Inject;
public class GtasksBackgroundService {
@Inject
public GtasksBackgroundService() {
}
}

@ -30,7 +30,7 @@ public class GtasksMetadataServiceTest extends DatabaseTestCase {
private final GtasksTestPreferenceService service;
public GtasksMetadataServiceTestModule(Context context) {
service = new GtasksTestPreferenceService(context, new Preferences(context, null), null);
service = new GtasksTestPreferenceService(new Preferences(context, null));
}
@Provides

@ -5,21 +5,12 @@
*/
package com.todoroo.astrid.gtasks;
import android.content.Context;
import org.tasks.AccountManager;
import org.tasks.preferences.PermissionChecker;
import org.tasks.preferences.Preferences;
public class GtasksTestPreferenceService extends GtasksPreferenceService {
public GtasksTestPreferenceService(Context context, Preferences preferences, AccountManager accountManager) {
super(context, preferences, accountManager);
}
@Override
public boolean isLoggedIn() {
return false;
public GtasksTestPreferenceService(Preferences preferences) {
super(preferences);
}
@Override

@ -1,4 +1,4 @@
package com.todoroo.astrid.gtasks.sync;
package org.tasks.gtasks;
import android.test.AndroidTestCase;
@ -9,13 +9,13 @@ import org.tasks.time.DateTime;
import static com.natpryce.makeiteasy.MakeItEasy.with;
import static com.todoroo.astrid.data.Task.HIDE_UNTIL_DUE;
import static com.todoroo.astrid.data.Task.HIDE_UNTIL_DUE_TIME;
import static com.todoroo.astrid.gtasks.sync.GtasksSyncV2Provider.mergeDates;
import static org.tasks.gtasks.GoogleTaskSyncAdapter.mergeDates;
import static org.tasks.makers.TaskMaker.DUE_DATE;
import static org.tasks.makers.TaskMaker.DUE_TIME;
import static org.tasks.makers.TaskMaker.HIDE_TYPE;
import static org.tasks.makers.TaskMaker.newTask;
public class GtasksSyncV2ProviderTest extends AndroidTestCase {
public class GoogleTaskSyncAdapterTest extends AndroidTestCase {
public void testMergeDate() {
Task remote = newTask(with(DUE_DATE, new DateTime(2016, 3, 11)));

@ -1,30 +0,0 @@
package com.todoroo.astrid.gtasks.sync;
import com.todoroo.astrid.gtasks.GtasksList;
import com.todoroo.astrid.sync.SyncResultCallback;
import javax.inject.Inject;
public class GtasksSyncV2Provider {
@Inject
public GtasksSyncV2Provider() {
}
public boolean isActive() {
return false;
}
public void synchronizeActiveTasks(SyncResultCallback callback) {
}
public void synchronizeList(GtasksList list, SyncResultCallback callback) {
}
public void clearCompleted(GtasksList list, SyncResultCallback callback) {
}
}

@ -1,5 +1,7 @@
package org.tasks;
import android.accounts.Account;
import java.util.Collections;
import java.util.List;
@ -15,7 +17,7 @@ public class AccountManager {
return Collections.emptyList();
}
public boolean hasAccount(String account) {
return false;
public Account getAccount(String userName) {
return null;
}
}

@ -0,0 +1,10 @@
package org.tasks.injection;
import javax.inject.Singleton;
import dagger.Component;
@Singleton
@Component(modules = ApplicationModule.class)
public interface ApplicationComponent extends BaseApplicationComponent {
}

@ -1,10 +0,0 @@
package org.tasks.scheduling;
import javax.inject.Inject;
public class GtasksBackgroundService {
@Inject
public GtasksBackgroundService() {
}
}

@ -26,6 +26,9 @@
<uses-permission android:name="android.permission.MANAGE_ACCOUNTS" />
<uses-permission android:name="com.google.android.googleapps.permission.GOOGLE_AUTH" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.READ_SYNC_STATS"/>
<uses-permission android:name="android.permission.READ_SYNC_SETTINGS"/>
<uses-permission android:name="android.permission.WRITE_SYNC_SETTINGS"/>
<application>
@ -147,6 +150,19 @@
android:label="@string/app_name"
android:exported="true" />
<!-- SyncService for Google Tasks -->
<service
android:name=".gtasks.GoogleTaskSyncService"
android:exported="true">
<intent-filter>
<action android:name="android.content.SyncAdapter" />
</intent-filter>
<meta-data
android:name="android.content.SyncAdapter"
android:resource="@xml/syncadapter" />
</service>
</application>
</manifest>

@ -8,6 +8,8 @@ package com.todoroo.astrid.gtasks;
import com.todoroo.astrid.api.Filter;
import com.todoroo.astrid.api.GtasksFilter;
import org.tasks.gtasks.SyncAdapterHelper;
import java.util.List;
import javax.inject.Inject;
@ -24,17 +26,17 @@ import static java.util.Collections.emptyList;
public class GtasksFilterExposer {
private final GtasksListService gtasksListService;
private final GtasksPreferenceService gtasksPreferenceService;
private final SyncAdapterHelper syncAdapterHelper;
@Inject
public GtasksFilterExposer(GtasksListService gtasksListService, GtasksPreferenceService gtasksPreferenceService) {
public GtasksFilterExposer(GtasksListService gtasksListService, SyncAdapterHelper syncAdapterHelper) {
this.gtasksListService = gtasksListService;
this.gtasksPreferenceService = gtasksPreferenceService;
this.syncAdapterHelper = syncAdapterHelper;
}
public List<Filter> getFilters() {
// if we aren't logged in (or we are logged in to astrid.com), don't expose features
if(!gtasksPreferenceService.isLoggedIn()) {
if(!syncAdapterHelper.isEnabled()) {
return emptyList();
}
@ -46,7 +48,7 @@ public class GtasksFilterExposer {
}
public Filter getFilter(long id) {
if (gtasksPreferenceService.isLoggedIn()) {
if (syncAdapterHelper.isEnabled()) {
GtasksList list = gtasksListService.getList(id);
if (list != null) {
return filterFromList(list);

@ -27,8 +27,6 @@ import org.tasks.Broadcaster;
import org.tasks.R;
import org.tasks.dialogs.DialogBuilder;
import org.tasks.preferences.Preferences;
import org.tasks.sync.IndeterminateProgressBarSyncResultCallback;
import org.tasks.sync.SyncThrottle;
import org.tasks.themes.ThemeCache;
import org.tasks.ui.CheckBoxes;
@ -52,7 +50,6 @@ public class GtasksListFragment extends SubtasksListFragment {
@Inject SyncV2Service syncService;
@Inject TaskAttachmentDao taskAttachmentDao;
@Inject Preferences preferences;
@Inject SyncThrottle syncThrottle;
@Inject DialogBuilder dialogBuilder;
@Inject Broadcaster broadcaster;
@Inject CheckBoxes checkBoxes;
@ -91,17 +88,6 @@ public class GtasksListFragment extends SubtasksListFragment {
outState.putParcelable(EXTRA_STORE_OBJECT, list.getStoreObject());
}
@Override
protected void initiateAutomaticSyncImpl() {
if (list != null && syncThrottle.canSync(list.getId())) {
syncData();
}
}
private void syncData() {
syncService.synchronizeList(list, new IndeterminateProgressBarSyncResultCallback(this, gtasksPreferenceService, broadcaster));
}
@Override
protected void onTaskDelete(Task task) {
helper.onDeleteTask(task);
@ -129,7 +115,7 @@ public class GtasksListFragment extends SubtasksListFragment {
public void finished() {
setSyncOngoing(false);
syncData();
onRefresh();
}
});
}

@ -21,11 +21,11 @@ import org.tasks.activities.GoogleTaskListSelectionDialog;
import org.tasks.analytics.Tracker;
import org.tasks.analytics.Tracking;
import org.tasks.gtasks.GoogleTaskListSelectionHandler;
import org.tasks.gtasks.SyncAdapterHelper;
import org.tasks.injection.ActivityComponent;
import org.tasks.injection.InjectingPreferenceActivity;
import org.tasks.preferences.ActivityPermissionRequestor;
import org.tasks.preferences.PermissionRequestor;
import org.tasks.scheduling.BackgroundScheduler;
import javax.inject.Inject;
@ -37,10 +37,10 @@ public class GtasksPreferences extends InjectingPreferenceActivity implements Go
private static final int REQUEST_LOGOUT = 1;
@Inject GtasksPreferenceService gtasksPreferenceService;
@Inject BackgroundScheduler backgroundScheduler;
@Inject ActivityPermissionRequestor permissionRequestor;
@Inject GtasksListService gtasksListService;
@Inject Tracker tracker;
@Inject SyncAdapterHelper syncAdapterHelper;
@Override
public void onCreate(Bundle savedInstanceState) {
@ -49,7 +49,7 @@ public class GtasksPreferences extends InjectingPreferenceActivity implements Go
addPreferencesFromResource(R.xml.preferences_gtasks);
Preference gtaskPreference = findPreference(getString(R.string.sync_gtasks));
((CheckBoxPreference) gtaskPreference).setChecked(gtasksPreferenceService.isLoggedIn());
((CheckBoxPreference) gtaskPreference).setChecked(syncAdapterHelper.isEnabled());
gtaskPreference.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
@Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
@ -59,6 +59,7 @@ public class GtasksPreferences extends InjectingPreferenceActivity implements Go
}
return false;
} else {
syncAdapterHelper.enableSynchronization(false);
tracker.reportEvent(Tracking.Events.GTASK_DISABLED);
gtasksPreferenceService.stopOngoing();
return true;
@ -70,6 +71,13 @@ public class GtasksPreferences extends InjectingPreferenceActivity implements Go
DateUtilities.getDateStringWithTime(GtasksPreferences.this,
gtasksPreferenceService.getLastSyncDate())));
}
findPreference(getString(R.string.gtasks_GPr_interval_key)).setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
@Override
public boolean onPreferenceChange(Preference preference, Object o) {
syncAdapterHelper.setSynchronizationInterval(Integer.parseInt((String) o));
return true;
}
});
findPreference(getString(R.string.sync_SPr_forget_key)).setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
@Override
public boolean onPreferenceClick(Preference preference) {
@ -101,11 +109,13 @@ public class GtasksPreferences extends InjectingPreferenceActivity implements Go
if (requestCode == REQUEST_LOGIN) {
boolean enabled = resultCode == RESULT_OK;
if (enabled) {
syncAdapterHelper.enableSynchronization(true);
tracker.reportEvent(Tracking.Events.GTASK_ENABLED);
}
((CheckBoxPreference) findPreference(getString(R.string.sync_gtasks))).setChecked(enabled);
} else if(requestCode == REQUEST_LOGOUT) {
if (resultCode == RESULT_OK) {
syncAdapterHelper.enableSynchronization(false);
tracker.reportEvent(Tracking.Events.GTASK_LOGOUT);
finish();
}
@ -125,12 +135,6 @@ public class GtasksPreferences extends InjectingPreferenceActivity implements Go
}
}
@Override
protected void onPause() {
super.onPause();
backgroundScheduler.scheduleGtaskSync();
}
@Override
public void inject(ActivityComponent component) {
component.inject(this);

@ -23,9 +23,10 @@ import com.todoroo.astrid.gtasks.api.GtasksInvoker;
import com.todoroo.astrid.gtasks.api.HttpNotFoundException;
import com.todoroo.astrid.gtasks.api.MoveRequest;
import org.tasks.gtasks.SyncAdapterHelper;
import java.io.IOException;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.Semaphore;
import javax.inject.Inject;
import javax.inject.Singleton;
@ -44,17 +45,20 @@ public class GtasksSyncService {
private final GtasksMetadata gtasksMetadataFactory;
private final GtasksInvoker gtasksInvoker;
private final LinkedBlockingQueue<SyncOnSaveOperation> operationQueue = new LinkedBlockingQueue<>();
private final SyncAdapterHelper syncAdapterHelper;
@Inject
public GtasksSyncService(MetadataDao metadataDao, GtasksMetadataService gtasksMetadataService,
TaskDao taskDao, GtasksPreferenceService gtasksPreferenceService,
GtasksMetadata gtasksMetadataFactory, GtasksInvoker gtasksInvoker) {
GtasksMetadata gtasksMetadataFactory, GtasksInvoker gtasksInvoker,
SyncAdapterHelper syncAdapterHelper) {
this.metadataDao = metadataDao;
this.gtasksMetadataService = gtasksMetadataService;
this.taskDao = taskDao;
this.gtasksPreferenceService = gtasksPreferenceService;
this.gtasksMetadataFactory = gtasksMetadataFactory;
this.gtasksInvoker = gtasksInvoker;
this.syncAdapterHelper = syncAdapterHelper;
new OperationPushThread(operationQueue).start();
}
@ -88,19 +92,6 @@ public class GtasksSyncService {
}
}
private class NotifyOp implements SyncOnSaveOperation {
private final Semaphore sema;
public NotifyOp(Semaphore sema) {
this.sema = sema;
}
@Override
public void op(GtasksInvoker invoker) throws IOException {
sema.release();
}
}
public void enqueue(SyncOnSaveOperation operation) {
operationQueue.offer(operation);
}
@ -131,17 +122,6 @@ public class GtasksSyncService {
}
}
public void waitUntilEmpty() {
Semaphore sema = new Semaphore(0);
operationQueue.offer(new NotifyOp(sema));
try {
sema.acquire();
} catch (InterruptedException e) {
// Ignored
Timber.e(e, e.getMessage());
}
}
public void clearCompleted(String listId) {
operationQueue.offer(new ClearOp(listId));
}
@ -161,7 +141,7 @@ public class GtasksSyncService {
{
return;
}
if (!checkForToken()) {
if (!syncAdapterHelper.isEnabled()) {
return;
}
@ -290,8 +270,4 @@ public class GtasksSyncService {
metadataDao.saveExisting(model);
}
}
private boolean checkForToken() {
return gtasksPreferenceService.isLoggedIn();
}
}

@ -1,333 +0,0 @@
/**
* Copyright (c) 2012 Todoroo Inc
*
* See the file "LICENSE" for the full license governing this code.
*/
package com.todoroo.astrid.gtasks.sync;
import android.content.Context;
import android.text.TextUtils;
import com.google.api.services.tasks.model.TaskList;
import com.google.api.services.tasks.model.TaskLists;
import com.google.api.services.tasks.model.Tasks;
import com.todoroo.andlib.data.AbstractModel;
import com.todoroo.andlib.data.TodorooCursor;
import com.todoroo.andlib.sql.Criterion;
import com.todoroo.andlib.sql.Join;
import com.todoroo.andlib.sql.Query;
import com.todoroo.andlib.utility.DateUtilities;
import com.todoroo.astrid.dao.MetadataDao.MetadataCriteria;
import com.todoroo.astrid.dao.StoreObjectDao;
import com.todoroo.astrid.dao.TaskDao;
import com.todoroo.astrid.data.Metadata;
import com.todoroo.astrid.data.SyncFlags;
import com.todoroo.astrid.data.Task;
import com.todoroo.astrid.gtasks.GtasksList;
import com.todoroo.astrid.gtasks.GtasksListService;
import com.todoroo.astrid.gtasks.GtasksMetadata;
import com.todoroo.astrid.gtasks.GtasksMetadataService;
import com.todoroo.astrid.gtasks.GtasksPreferenceService;
import com.todoroo.astrid.gtasks.GtasksTaskListUpdater;
import com.todoroo.astrid.gtasks.api.GtasksInvoker;
import com.todoroo.astrid.service.TaskService;
import com.todoroo.astrid.sync.SyncResultCallback;
import org.tasks.R;
import org.tasks.injection.ForApplication;
import org.tasks.preferences.Preferences;
import org.tasks.sync.SyncExecutor;
import org.tasks.time.DateTime;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import javax.inject.Inject;
import javax.inject.Singleton;
import timber.log.Timber;
import static org.tasks.date.DateTimeUtils.newDateTime;
@Singleton
public class GtasksSyncV2Provider {
public class SyncExceptionHandler {
public void handleException(Exception e) {
Timber.e(e, e.getMessage());
}
}
private final SyncExceptionHandler handler = new SyncExceptionHandler();
private void finishSync(SyncResultCallback callback) {
gtasksPreferenceService.recordSuccessfulSync();
callback.finished();
}
@Override
public String toString() {
return getName();
}
private final TaskService taskService;
private final StoreObjectDao storeObjectDao;
private final GtasksPreferenceService gtasksPreferenceService;
private final GtasksSyncService gtasksSyncService;
private final GtasksListService gtasksListService;
private final GtasksMetadataService gtasksMetadataService;
private final GtasksTaskListUpdater gtasksTaskListUpdater;
private final Context context;
private final Preferences preferences;
private final SyncExecutor executor;
private final GtasksInvoker gtasksInvoker;
@Inject
public GtasksSyncV2Provider(TaskService taskService, StoreObjectDao storeObjectDao, GtasksPreferenceService gtasksPreferenceService,
GtasksSyncService gtasksSyncService, GtasksListService gtasksListService, GtasksMetadataService gtasksMetadataService,
GtasksTaskListUpdater gtasksTaskListUpdater, @ForApplication Context context, Preferences preferences,
SyncExecutor executor, GtasksInvoker gtasksInvoker) {
this.taskService = taskService;
this.storeObjectDao = storeObjectDao;
this.gtasksPreferenceService = gtasksPreferenceService;
this.gtasksSyncService = gtasksSyncService;
this.gtasksListService = gtasksListService;
this.gtasksMetadataService = gtasksMetadataService;
this.gtasksTaskListUpdater = gtasksTaskListUpdater;
this.context = context;
this.preferences = preferences;
this.executor = executor;
this.gtasksInvoker = gtasksInvoker;
}
private String getName() {
return context.getString(R.string.gtasks_GPr_header);
}
public void signOut() {
gtasksPreferenceService.clearLastSyncDate();
gtasksPreferenceService.setUserName(null);
gtasksMetadataService.clearMetadata();
}
public boolean isActive() {
return gtasksPreferenceService.isLoggedIn();
}
public void synchronizeActiveTasks(final SyncResultCallback callback) {
executor.execute(callback, new Runnable() {
@Override
public void run() {
callback.started();
try {
TaskLists remoteLists = null;
try {
List<TaskList> gtaskLists = new ArrayList<>();
String nextPageToken = null;
do {
remoteLists = gtasksInvoker.allGtaskLists(nextPageToken);
if (remoteLists == null) {
break;
}
List<TaskList> items = remoteLists.getItems();
if (items != null) {
gtaskLists.addAll(items);
}
nextPageToken = remoteLists.getNextPageToken();
} while (nextPageToken != null);
gtasksListService.updateLists(gtaskLists);
if (gtasksListService.getList(gtasksPreferenceService.getDefaultList()) == null) {
gtasksPreferenceService.setDefaultList(null);
}
} catch (IOException e) {
handler.handleException(e);
}
if (remoteLists == null) {
finishSync(callback);
return;
}
List<GtasksList> listsToUpdate = gtasksListService.getListsToUpdate(remoteLists);
if (listsToUpdate.isEmpty()) {
finishSync(callback);
return;
}
final AtomicInteger finisher = new AtomicInteger(listsToUpdate.size());
for (final GtasksList list : listsToUpdate) {
executor.execute(callback, new Runnable() {
@Override
public void run() {
synchronizeListHelper(list, gtasksInvoker, handler);
if (finisher.decrementAndGet() == 0) {
pushUpdated(gtasksInvoker);
finishSync(callback);
}
}
});
}
} catch (Exception e) {
handler.handleException(e);
callback.finished();
}
}
});
}
private synchronized void pushUpdated(GtasksInvoker invoker) {
TodorooCursor<Task> queued = taskService.query(Query.select(Task.PROPERTIES).
join(Join.left(Metadata.TABLE, Criterion.and(MetadataCriteria.withKey(GtasksMetadata.METADATA_KEY), Task.ID.eq(Metadata.TASK)))).where(
Criterion.or(Task.MODIFICATION_DATE.gt(GtasksMetadata.LAST_SYNC), Metadata.KEY.isNull())));
pushTasks(queued, invoker);
}
private synchronized void pushTasks(TodorooCursor<Task> queued, GtasksInvoker invoker) {
try {
for (queued.moveToFirst(); !queued.isAfterLast(); queued.moveToNext()) {
Task task = new Task(queued);
try {
gtasksSyncService.pushTaskOnSave(task, task.getMergedValues(), invoker);
} catch (IOException e) {
handler.handleException(e);
}
}
} finally {
queued.close();
}
}
public void clearCompleted(final GtasksList list, final SyncResultCallback callback) {
executor.execute(callback, new Runnable() {
@Override
public void run() {
callback.started();
try {
gtasksSyncService.clearCompleted(list.getRemoteId());
} finally {
callback.finished();
}
}
});
}
public void synchronizeList(final GtasksList gtasksList, final SyncResultCallback callback) {
executor.execute(callback, new Runnable() {
@Override
public void run() {
callback.started();
try {
gtasksSyncService.waitUntilEmpty();
synchronizeListHelper(gtasksList, gtasksInvoker, null);
} finally {
callback.finished();
}
}
});
}
private synchronized void synchronizeListHelper(GtasksList list, GtasksInvoker invoker,
SyncExceptionHandler errorHandler) {
String listId = list.getRemoteId();
long lastSyncDate = list.getLastSync();
/**
* Find tasks which have been associated with the list internally, but have not yet been
* pushed to Google Tasks (and so haven't yet got a valid ID).
*/
Criterion not_pushed_tasks = Criterion.and(
Metadata.KEY.eq(GtasksMetadata.METADATA_KEY),
GtasksMetadata.LIST_ID.eq(listId),
GtasksMetadata.ID.eq("")
);
TodorooCursor<Task> qs = taskService.query(Query.select(Task.PROPERTIES).
join(Join.left(Metadata.TABLE, Criterion.and(MetadataCriteria.withKey(GtasksMetadata.METADATA_KEY), Task.ID.eq(Metadata.TASK)))).where(not_pushed_tasks)
);
pushTasks(qs, invoker);
boolean includeDeletedAndHidden = lastSyncDate != 0;
try {
List<com.google.api.services.tasks.model.Task> tasks = new ArrayList<>();
String nextPageToken = null;
do {
Tasks taskList = invoker.getAllGtasksFromListId(listId, includeDeletedAndHidden,
includeDeletedAndHidden, lastSyncDate + 1000L, nextPageToken);
if (taskList == null) {
break;
}
List<com.google.api.services.tasks.model.Task> items = taskList.getItems();
if (items != null) {
tasks.addAll(items);
}
nextPageToken = taskList.getNextPageToken();
} while (nextPageToken != null);
if (!tasks.isEmpty()) {
for (com.google.api.services.tasks.model.Task t : tasks) {
GtasksTaskContainer container = new GtasksTaskContainer(t, listId, GtasksMetadata.createEmptyMetadataWithoutList(AbstractModel.NO_ID));
gtasksMetadataService.findLocalMatch(container);
container.gtaskMetadata.setValue(GtasksMetadata.GTASKS_ORDER, Long.parseLong(t.getPosition()));
container.gtaskMetadata.setValue(GtasksMetadata.PARENT_TASK, gtasksMetadataService.localIdForGtasksId(t.getParent()));
container.gtaskMetadata.setValue(GtasksMetadata.LAST_SYNC, DateUtilities.now() + 1000L);
write(container);
lastSyncDate = Math.max(lastSyncDate, container.getUpdateTime());
}
list.setLastSync(lastSyncDate);
storeObjectDao.persist(list);
gtasksTaskListUpdater.correctOrderAndIndentForList(listId);
}
} catch (IOException e) {
if (errorHandler != null) {
errorHandler.handleException(e);
} else {
Timber.e(e, e.getMessage());
}
}
}
private void write(GtasksTaskContainer task) {
// merge astrid dates with google dates
if(task.task.isSaved()) {
Task local = taskService.fetchById(task.task.getId(), Task.PROPERTIES);
if (local == null) {
task.task.clearValue(Task.ID);
task.task.clearValue(Task.UUID);
} else {
mergeDates(task.task, local);
}
} else { // Set default importance and reminders for remotely created tasks
task.task.setImportance(preferences.getIntegerFromString(
R.string.p_default_importance_key, Task.IMPORTANCE_SHOULD_DO));
TaskDao.setDefaultReminders(preferences, task.task);
}
if (!TextUtils.isEmpty(task.task.getTitle())) {
task.task.putTransitory(SyncFlags.GTASKS_SUPPRESS_SYNC, true);
gtasksMetadataService.saveTaskAndMetadata(task);
}
}
static void mergeDates(Task remote, Task local) {
if (remote.hasDueDate() && local.hasDueTime()) {
DateTime oldDate = newDateTime(local.getDueDate());
DateTime newDate = newDateTime(remote.getDueDate())
.withHourOfDay(oldDate.getHourOfDay())
.withMinuteOfHour(oldDate.getMinuteOfHour())
.withSecondOfMinute(oldDate.getSecondOfMinute());
local.setDueDateAdjustingHideUntil(
Task.createDueDate(Task.URGENCY_SPECIFIC_DAY_TIME, newDate.getMillis()));
} else {
local.setDueDateAdjustingHideUntil(remote.getDueDate());
}
remote.setHideUntil(local.getHideUntil());
remote.setDueDate(local.getDueDate());
}
}

@ -0,0 +1,56 @@
/**
* Copyright (c) 2012 Todoroo Inc
*
* See the file "LICENSE" for the full license governing this code.
*/
package com.todoroo.astrid.service;
import com.todoroo.astrid.gtasks.GtasksList;
import com.todoroo.astrid.gtasks.sync.GtasksSyncService;
import com.todoroo.astrid.sync.SyncResultCallback;
import org.tasks.gtasks.SyncAdapterHelper;
import org.tasks.sync.SyncExecutor;
import javax.inject.Inject;
/**
* SyncV2Service is a simplified synchronization interface for supporting
* next-generation sync interfaces such as Google Tasks and Astrid.com
*
* @author Tim Su <tim@astrid.com>
*
*/
public class SyncV2Service {
/*
* At present, sync provider interactions are handled through code. If
* there is enough interest, the Astrid team could create an interface
* for responding to sync requests through this new API.
*/
private final SyncExecutor syncExecutor;
private final SyncAdapterHelper syncAdapterHelper;
private final GtasksSyncService gtasksSyncService;
@Inject
public SyncV2Service(SyncExecutor syncExecutor, SyncAdapterHelper syncAdapterHelper, GtasksSyncService gtasksSyncService) {
this.syncExecutor = syncExecutor;
this.syncAdapterHelper = syncAdapterHelper;
this.gtasksSyncService = gtasksSyncService;
}
public void clearCompleted(final GtasksList list, final SyncResultCallback callback) {
if (syncAdapterHelper.isEnabled()) {
syncExecutor.execute(callback, new Runnable() {
@Override
public void run() {
callback.started();
try {
gtasksSyncService.clearCompleted(list.getRemoteId());
} finally {
callback.finished();
}
}
});
}
}
}

@ -2,11 +2,14 @@ package org.tasks;
import com.todoroo.astrid.gtasks.GtasksPreferenceService;
import com.todoroo.astrid.gtasks.GtasksTaskListUpdater;
import com.todoroo.astrid.gtasks.sync.GtasksSyncService;
import org.tasks.billing.InventoryHelper;
import org.tasks.billing.PurchaseHelper;
import org.tasks.gtasks.SyncAdapterHelper;
import org.tasks.preferences.Preferences;
import org.tasks.receivers.TeslaUnreadReceiver;
import org.tasks.sync.SyncExecutor;
import javax.inject.Inject;
@ -16,21 +19,26 @@ public class FlavorSetup {
private final GtasksPreferenceService gtasksPreferenceService;
private final TeslaUnreadReceiver teslaUnreadReceiver;
private final InventoryHelper inventoryHelper;
private final SyncAdapterHelper syncAdapterHelper;
@Inject
public FlavorSetup(Preferences preferences, GtasksPreferenceService gtasksPreferenceService,
@SuppressWarnings("UnusedParameters") GtasksTaskListUpdater gtasksTaskListUpdater,
@SuppressWarnings("UnusedParameters") PurchaseHelper purchaseHelper,
TeslaUnreadReceiver teslaUnreadReceiver, InventoryHelper inventoryHelper) {
@SuppressWarnings("UnusedParameters") SyncExecutor syncExecutor,
TeslaUnreadReceiver teslaUnreadReceiver, InventoryHelper inventoryHelper,
SyncAdapterHelper syncAdapterHelper) {
this.preferences = preferences;
this.gtasksPreferenceService = gtasksPreferenceService;
this.teslaUnreadReceiver = teslaUnreadReceiver;
this.inventoryHelper = inventoryHelper;
this.syncAdapterHelper = syncAdapterHelper;
}
public void setup() {
inventoryHelper.initialize();
teslaUnreadReceiver.setEnabled(preferences.getBoolean(R.string.p_tesla_unread_enabled, false));
gtasksPreferenceService.stopOngoing(); // if sync ongoing flag was set, clear it
syncAdapterHelper.enableSynchronization(syncAdapterHelper.isEnabled());
}
}

@ -3,7 +3,8 @@ package org.tasks.activities;
import android.content.DialogInterface;
import android.os.Bundle;
import com.todoroo.astrid.gtasks.sync.GtasksSyncV2Provider;
import com.todoroo.astrid.gtasks.GtasksMetadataService;
import com.todoroo.astrid.gtasks.GtasksPreferenceService;
import org.tasks.R;
import org.tasks.dialogs.DialogBuilder;
@ -14,8 +15,9 @@ import javax.inject.Inject;
public class ClearGtaskDataActivity extends InjectingAppCompatActivity {
@Inject GtasksSyncV2Provider gtasksSyncV2Provider;
@Inject DialogBuilder dialogBuilder;
@Inject GtasksPreferenceService gtasksPreferenceService;
@Inject GtasksMetadataService gtasksMetadataService;
@Override
protected void onCreate(Bundle savedInstanceState) {
@ -25,7 +27,9 @@ public class ClearGtaskDataActivity extends InjectingAppCompatActivity {
.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
gtasksSyncV2Provider.signOut();
gtasksPreferenceService.clearLastSyncDate();
gtasksPreferenceService.setUserName(null);
gtasksMetadataService.clearMetadata();
setResult(RESULT_OK);
}
})

@ -0,0 +1,296 @@
/*
* Copyright 2013 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.tasks.gtasks;
import android.accounts.Account;
import android.content.ContentProviderClient;
import android.content.ContentResolver;
import android.content.Context;
import android.content.SyncResult;
import android.os.Bundle;
import android.text.TextUtils;
import com.google.api.services.tasks.model.TaskList;
import com.google.api.services.tasks.model.TaskLists;
import com.google.api.services.tasks.model.Tasks;
import com.todoroo.andlib.data.AbstractModel;
import com.todoroo.andlib.data.TodorooCursor;
import com.todoroo.andlib.sql.Criterion;
import com.todoroo.andlib.sql.Join;
import com.todoroo.andlib.sql.Query;
import com.todoroo.andlib.utility.DateUtilities;
import com.todoroo.astrid.dao.MetadataDao;
import com.todoroo.astrid.dao.StoreObjectDao;
import com.todoroo.astrid.dao.TaskDao;
import com.todoroo.astrid.data.Metadata;
import com.todoroo.astrid.data.SyncFlags;
import com.todoroo.astrid.data.Task;
import com.todoroo.astrid.gtasks.GtasksList;
import com.todoroo.astrid.gtasks.GtasksListService;
import com.todoroo.astrid.gtasks.GtasksMetadata;
import com.todoroo.astrid.gtasks.GtasksMetadataService;
import com.todoroo.astrid.gtasks.GtasksPreferenceService;
import com.todoroo.astrid.gtasks.GtasksTaskListUpdater;
import com.todoroo.astrid.gtasks.api.GtasksInvoker;
import com.todoroo.astrid.gtasks.sync.GtasksSyncService;
import com.todoroo.astrid.gtasks.sync.GtasksTaskContainer;
import com.todoroo.astrid.service.TaskService;
import org.tasks.Broadcaster;
import org.tasks.R;
import org.tasks.injection.InjectingAbstractThreadedSyncAdapter;
import org.tasks.injection.SyncAdapterComponent;
import org.tasks.preferences.Preferences;
import org.tasks.sync.RecordSyncStatusCallback;
import org.tasks.time.DateTime;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import javax.inject.Inject;
import timber.log.Timber;
import static org.tasks.date.DateTimeUtils.newDateTime;
/**
* Define a sync adapter for the app.
*
* <p>This class is instantiated in {@link GoogleTaskSyncService}, which also binds SyncAdapter to the system.
* SyncAdapter should only be initialized in SyncService, never anywhere else.
*
* <p>The system calls onPerformSync() via an RPC call through the IBinder object supplied by
* SyncService.
*/
public class GoogleTaskSyncAdapter extends InjectingAbstractThreadedSyncAdapter {
@Inject GtasksPreferenceService gtasksPreferenceService;
@Inject Broadcaster broadcaster;
@Inject TaskService taskService;
@Inject StoreObjectDao storeObjectDao;
@Inject GtasksSyncService gtasksSyncService;
@Inject GtasksListService gtasksListService;
@Inject GtasksMetadataService gtasksMetadataService;
@Inject GtasksTaskListUpdater gtasksTaskListUpdater;
@Inject Preferences preferences;
@Inject GtasksInvoker gtasksInvoker;
public GoogleTaskSyncAdapter(Context context, boolean autoInitialize) {
super(context, autoInitialize);
}
public GoogleTaskSyncAdapter(Context context, boolean autoInitialize, boolean allowParallelSyncs) {
super(context, autoInitialize, allowParallelSyncs);
}
/**
* Called by the Android system in response to a request to run the sync adapter. The work
* required to read data from the network, parse it, and store it in the content provider is
* done here. Extending AbstractThreadedSyncAdapter ensures that all methods within SyncAdapter
* run on a background thread. For this reason, blocking I/O and other long-running tasks can be
* run <em>in situ</em>, and you don't have to set up a separate thread for them.
.
*
* <p>This is where we actually perform any work required to perform a sync.
* {@link android.content.AbstractThreadedSyncAdapter} guarantees that this will be called on a non-UI thread,
* so it is safe to peform blocking I/O here.
*
* <p>The syncResult argument allows you to pass information back to the method that triggered
* the sync.
*/
@Override
public void onPerformSync(Account account, Bundle extras, String authority,
ContentProviderClient provider, SyncResult syncResult) {
if (!account.name.equals(gtasksPreferenceService.getUserName())) {
Timber.d("Sync not enabled for %s", account);
syncResult.stats.numAuthExceptions++;
return;
}
if (!extras.getBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, false)) {
preferences.setBoolean(R.string.p_sync_warning_shown, false);
}
Timber.d("%s: start sync", account);
RecordSyncStatusCallback callback = new RecordSyncStatusCallback(gtasksPreferenceService, broadcaster);
try {
callback.started();
synchronize();
gtasksPreferenceService.recordSuccessfulSync();
} catch (Exception e) {
Timber.e(e, e.getMessage());
} finally {
callback.finished();
Timber.d("%s: end sync", account);
}
}
private void synchronize() {
TaskLists remoteLists = null;
try {
List<TaskList> gtaskLists = new ArrayList<>();
String nextPageToken = null;
do {
remoteLists = gtasksInvoker.allGtaskLists(nextPageToken);
if (remoteLists == null) {
break;
}
List<TaskList> items = remoteLists.getItems();
if (items != null) {
gtaskLists.addAll(items);
}
nextPageToken = remoteLists.getNextPageToken();
} while (nextPageToken != null);
gtasksListService.updateLists(gtaskLists);
if (gtasksListService.getList(gtasksPreferenceService.getDefaultList()) == null) {
gtasksPreferenceService.setDefaultList(null);
}
} catch (IOException e) {
Timber.e(e, e.getMessage());
}
if (remoteLists == null) {
return;
}
for (final GtasksList list : gtasksListService.getListsToUpdate(remoteLists)) {
synchronizeListHelper(list, gtasksInvoker);
}
pushUpdated(gtasksInvoker);
}
private synchronized void pushUpdated(GtasksInvoker invoker) {
TodorooCursor<Task> queued = taskService.query(Query.select(Task.PROPERTIES).
join(Join.left(Metadata.TABLE, Criterion.and(MetadataDao.MetadataCriteria.withKey(GtasksMetadata.METADATA_KEY), Task.ID.eq(Metadata.TASK)))).where(
Criterion.or(Task.MODIFICATION_DATE.gt(GtasksMetadata.LAST_SYNC), Metadata.KEY.isNull())));
pushTasks(queued, invoker);
}
private synchronized void pushTasks(TodorooCursor<Task> queued, GtasksInvoker invoker) {
try {
for (queued.moveToFirst(); !queued.isAfterLast(); queued.moveToNext()) {
Task task = new Task(queued);
try {
gtasksSyncService.pushTaskOnSave(task, task.getMergedValues(), invoker);
} catch (IOException e) {
Timber.e(e, e.getMessage());
}
}
} finally {
queued.close();
}
}
private synchronized void synchronizeListHelper(GtasksList list, GtasksInvoker invoker) {
String listId = list.getRemoteId();
long lastSyncDate = list.getLastSync();
/**
* Find tasks which have been associated with the list internally, but have not yet been
* pushed to Google Tasks (and so haven't yet got a valid ID).
*/
Criterion not_pushed_tasks = Criterion.and(
Metadata.KEY.eq(GtasksMetadata.METADATA_KEY),
GtasksMetadata.LIST_ID.eq(listId),
GtasksMetadata.ID.eq("")
);
TodorooCursor<Task> qs = taskService.query(Query.select(Task.PROPERTIES).
join(Join.left(Metadata.TABLE, Criterion.and(MetadataDao.MetadataCriteria.withKey(GtasksMetadata.METADATA_KEY), Task.ID.eq(Metadata.TASK)))).where(not_pushed_tasks)
);
pushTasks(qs, invoker);
boolean includeDeletedAndHidden = lastSyncDate != 0;
try {
List<com.google.api.services.tasks.model.Task> tasks = new ArrayList<>();
String nextPageToken = null;
do {
Tasks taskList = invoker.getAllGtasksFromListId(listId, includeDeletedAndHidden,
includeDeletedAndHidden, lastSyncDate + 1000L, nextPageToken);
if (taskList == null) {
break;
}
List<com.google.api.services.tasks.model.Task> items = taskList.getItems();
if (items != null) {
tasks.addAll(items);
}
nextPageToken = taskList.getNextPageToken();
} while (nextPageToken != null);
if (!tasks.isEmpty()) {
for (com.google.api.services.tasks.model.Task t : tasks) {
GtasksTaskContainer container = new GtasksTaskContainer(t, listId, GtasksMetadata.createEmptyMetadataWithoutList(AbstractModel.NO_ID));
gtasksMetadataService.findLocalMatch(container);
container.gtaskMetadata.setValue(GtasksMetadata.GTASKS_ORDER, Long.parseLong(t.getPosition()));
container.gtaskMetadata.setValue(GtasksMetadata.PARENT_TASK, gtasksMetadataService.localIdForGtasksId(t.getParent()));
container.gtaskMetadata.setValue(GtasksMetadata.LAST_SYNC, DateUtilities.now() + 1000L);
write(container);
lastSyncDate = Math.max(lastSyncDate, container.getUpdateTime());
}
list.setLastSync(lastSyncDate);
storeObjectDao.persist(list);
gtasksTaskListUpdater.correctOrderAndIndentForList(listId);
}
} catch (IOException e) {
Timber.e(e, e.getMessage());
}
}
private void write(GtasksTaskContainer task) {
// merge astrid dates with google dates
if(task.task.isSaved()) {
Task local = taskService.fetchById(task.task.getId(), Task.PROPERTIES);
if (local == null) {
task.task.clearValue(Task.ID);
task.task.clearValue(Task.UUID);
} else {
mergeDates(task.task, local);
}
} else { // Set default importance and reminders for remotely created tasks
task.task.setImportance(preferences.getIntegerFromString(
R.string.p_default_importance_key, Task.IMPORTANCE_SHOULD_DO));
TaskDao.setDefaultReminders(preferences, task.task);
}
if (!TextUtils.isEmpty(task.task.getTitle())) {
task.task.putTransitory(SyncFlags.GTASKS_SUPPRESS_SYNC, true);
gtasksMetadataService.saveTaskAndMetadata(task);
}
}
static void mergeDates(Task remote, Task local) {
if (remote.hasDueDate() && local.hasDueTime()) {
DateTime oldDate = newDateTime(local.getDueDate());
DateTime newDate = newDateTime(remote.getDueDate())
.withHourOfDay(oldDate.getHourOfDay())
.withMinuteOfHour(oldDate.getMinuteOfHour())
.withSecondOfMinute(oldDate.getSecondOfMinute());
local.setDueDateAdjustingHideUntil(
Task.createDueDate(Task.URGENCY_SPECIFIC_DAY_TIME, newDate.getMillis()));
} else {
local.setDueDateAdjustingHideUntil(remote.getDueDate());
}
remote.setHideUntil(local.getHideUntil());
remote.setDueDate(local.getDueDate());
}
@Override
protected void inject(SyncAdapterComponent component) {
component.inject(this);
}
}

@ -0,0 +1,75 @@
/*
* Copyright 2013 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.tasks.gtasks;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import timber.log.Timber;
/** Service to handle sync requests.
*
* <p>This service is invoked in response to Intents with action android.content.SyncAdapter, and
* returns a Binder connection to SyncAdapter.
*
* <p>For performance, only one sync adapter will be initialized within this application's context.
*
* <p>Note: The SyncService itself is not notified when a new sync occurs. It's role is to
* manage the lifecycle of our {@link GoogleTaskSyncAdapter} and provide a handle to said SyncAdapter to the
* OS on request.
*/
public class GoogleTaskSyncService extends Service {
private static final Object sSyncAdapterLock = new Object();
private static GoogleTaskSyncAdapter sGoogleTaskSyncAdapter = null;
/**
* Thread-safe constructor, creates static {@link GoogleTaskSyncAdapter} instance.
*/
@Override
public void onCreate() {
super.onCreate();
Timber.d("Service created");
synchronized (sSyncAdapterLock) {
if (sGoogleTaskSyncAdapter == null) {
sGoogleTaskSyncAdapter = new GoogleTaskSyncAdapter(getApplicationContext(), true);
}
}
}
@Override
/**
* Logging-only destructor.
*/
public void onDestroy() {
super.onDestroy();
Timber.d("Service destroyed");
}
/**
* Return Binder handle for IPC communication with {@link GoogleTaskSyncAdapter}.
*
* <p>New sync requests will be sent directly to the SyncAdapter using this channel.
*
* @param intent Calling intent
* @return Binder handle for {@link GoogleTaskSyncAdapter}
*/
@Override
public IBinder onBind(Intent intent) {
return sGoogleTaskSyncAdapter.getSyncAdapterBinder();
}
}

@ -0,0 +1,11 @@
package org.tasks.injection;
import javax.inject.Singleton;
import dagger.Component;
@Singleton
@Component(modules = ApplicationModule.class)
public interface ApplicationComponent extends BaseApplicationComponent {
SyncAdapterComponent plus(SyncAdapterModule syncAdapterModule);
}

@ -0,0 +1,25 @@
package org.tasks.injection;
import android.content.AbstractThreadedSyncAdapter;
import android.content.Context;
public abstract class InjectingAbstractThreadedSyncAdapter extends AbstractThreadedSyncAdapter {
public InjectingAbstractThreadedSyncAdapter(Context context, boolean autoInitialize) {
super(context, autoInitialize);
inject(context);
}
public InjectingAbstractThreadedSyncAdapter(Context context, boolean autoInitialize, boolean allowParallelSyncs) {
super(context, autoInitialize, allowParallelSyncs);
inject(context);
}
private void inject(Context context) {
inject(((InjectingApplication) context.getApplicationContext())
.getComponent()
.plus(new SyncAdapterModule()));
}
protected abstract void inject(SyncAdapterComponent component);
}

@ -0,0 +1,10 @@
package org.tasks.injection;
import org.tasks.gtasks.GoogleTaskSyncAdapter;
import dagger.Subcomponent;
@Subcomponent(modules = SyncAdapterModule.class)
public interface SyncAdapterComponent {
void inject(GoogleTaskSyncAdapter googleTaskSyncAdapter);
}

@ -0,0 +1,7 @@
package org.tasks.injection;
import dagger.Module;
@Module
public class SyncAdapterModule {
}

@ -16,6 +16,7 @@ import com.todoroo.astrid.gtasks.api.GtasksInvoker;
import com.todoroo.astrid.gtasks.sync.GtasksSyncService;
import com.todoroo.astrid.service.TaskService;
import org.tasks.gtasks.SyncAdapterHelper;
import org.tasks.injection.BroadcastComponent;
import org.tasks.injection.InjectingBroadcastReceiver;
@ -31,12 +32,13 @@ public class GoogleTaskPushReceiver extends InjectingBroadcastReceiver {
@Inject GtasksPreferenceService gtasksPreferenceService;
@Inject GtasksSyncService gtasksSyncService;
@Inject TaskDao taskDao;
@Inject SyncAdapterHelper syncAdapterHelper;
@Override
public void onReceive(Context context, Intent intent) {
super.onReceive(context, intent);
if(!gtasksPreferenceService.isLoggedIn()) {
if(!syncAdapterHelper.isEnabled()) {
return;
}

@ -1,60 +0,0 @@
package org.tasks.scheduling;
import com.todoroo.astrid.gtasks.GtasksPreferenceService;
import com.todoroo.astrid.gtasks.sync.GtasksSyncV2Provider;
import org.tasks.Broadcaster;
import org.tasks.R;
import org.tasks.injection.IntentServiceComponent;
import org.tasks.preferences.Preferences;
import org.tasks.sync.RecordSyncStatusCallback;
import javax.inject.Inject;
import timber.log.Timber;
import static java.util.concurrent.TimeUnit.SECONDS;
public class GtasksBackgroundService extends RecurringIntervalIntentService {
@Inject Preferences preferences;
@Inject GtasksPreferenceService gtasksPreferenceService;
@Inject GtasksSyncV2Provider gtasksSyncV2Provider;
@Inject Broadcaster broadcaster;
public GtasksBackgroundService() {
super(GtasksBackgroundService.class.getSimpleName());
}
@Override
void run() {
if (gtasksPreferenceService.isOngoing()) {
Timber.d("aborting: sync ongoing");
return;
}
if(gtasksSyncV2Provider.isActive()) {
gtasksSyncV2Provider.synchronizeActiveTasks(new RecordSyncStatusCallback(gtasksPreferenceService, broadcaster));
}
}
@Override
long intervalMillis() {
try {
return SECONDS.toMillis(preferences.getIntegerFromString(R.string.gtasks_GPr_interval_key, 0));
} catch(Exception e) {
Timber.e(e, e.getMessage());
preferences.setString(R.string.gtasks_GPr_interval_key, "0");
return 0;
}
}
@Override
String getLastRunPreference() {
return "gtasks_last_sync";
}
@Override
protected void inject(IntentServiceComponent component) {
component.inject(this);
}
}

@ -1,6 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<bool name="sync_supported">true</bool>
<bool name="location_enabled">true</bool>
<bool name="google_play_store_available">true</bool>
</resources>

@ -11,7 +11,6 @@ import android.os.Bundle;
import android.support.v7.widget.Toolbar;
import android.view.MenuItem;
import com.todoroo.andlib.utility.DateUtilities;
import com.todoroo.astrid.activity.TaskListActivity;
import com.todoroo.astrid.activity.TaskListFragment;
import com.todoroo.astrid.api.AstridApiConstants;
@ -117,17 +116,6 @@ public class TagViewFragment extends TaskListFragment {
outState.putParcelable(EXTRA_TAG_DATA, tagData);
}
@Override
protected void initiateAutomaticSyncImpl() {
if (tagData != null) {
long lastAutosync = tagData.getLastAutosync();
if(DateUtilities.now() - lastAutosync > AUTOSYNC_INTERVAL) {
tagData.setLastAutosync(DateUtilities.now());
tagDataDao.saveExisting(tagData);
}
}
}
@Override
protected boolean hasDraggableOption() {
return tagData != null;

@ -12,6 +12,8 @@ import android.content.ContentValues;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.provider.Settings;
import android.support.design.widget.Snackbar;
import android.support.v4.widget.DrawerLayout;
import android.view.View;
import android.view.inputmethod.InputMethodManager;
@ -46,6 +48,7 @@ import org.tasks.dialogs.SortDialog;
import org.tasks.fragments.CommentBarFragment;
import org.tasks.fragments.TaskEditControlSetFragmentManager;
import org.tasks.gtasks.GoogleTaskListSelectionHandler;
import org.tasks.gtasks.SyncAdapterHelper;
import org.tasks.injection.ActivityComponent;
import org.tasks.injection.InjectingAppCompatActivity;
import org.tasks.intents.TaskIntents;
@ -94,6 +97,7 @@ public class TaskListActivity extends InjectingAppCompatActivity implements
@Inject Theme theme;
@Inject Broadcaster broadcaster;
@Inject ThemeCache themeCache;
@Inject SyncAdapterHelper syncAdapterHelper;
@BindView(R.id.drawer_layout) DrawerLayout drawerLayout;
@ -233,6 +237,28 @@ public class TaskListActivity extends InjectingAppCompatActivity implements
registerReceiver(
repeatConfirmationReceiver,
new IntentFilter(AstridApiConstants.BROADCAST_EVENT_TASK_REPEATED));
if (syncAdapterHelper.shouldShowBackgroundSyncWarning() && !preferences.getBoolean(R.string.p_sync_warning_shown, false)) {
TaskListFragment taskListFragment = getTaskListFragment();
if (taskListFragment != null) {
taskListFragment.makeSnackbar(R.string.master_sync_warning)
.setAction(R.string.TLA_menu_settings, new View.OnClickListener() {
@Override
public void onClick(View view) {
startActivity(new Intent(Settings.ACTION_SYNC_SETTINGS) {{
setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
}});
}
})
.setCallback(new Snackbar.Callback() {
@Override
public void onShown(Snackbar snackbar) {
preferences.setBoolean(R.string.p_sync_warning_shown, true);
}
})
.show();
}
}
}
@Override

@ -14,6 +14,8 @@ import android.content.IntentFilter;
import android.database.Cursor;
import android.database.sqlite.SQLiteException;
import android.os.Bundle;
import android.support.design.widget.CoordinatorLayout;
import android.support.design.widget.Snackbar;
import android.support.v4.view.MenuItemCompat;
import android.support.v4.widget.SwipeRefreshLayout;
import android.support.v7.widget.SearchView;
@ -57,7 +59,6 @@ import com.todoroo.astrid.data.Task;
import com.todoroo.astrid.data.TaskAttachment;
import com.todoroo.astrid.gtasks.GtasksListFragment;
import com.todoroo.astrid.gtasks.GtasksPreferenceService;
import com.todoroo.astrid.helper.SyncActionHelper;
import com.todoroo.astrid.service.TaskCreator;
import com.todoroo.astrid.service.TaskDeleter;
import com.todoroo.astrid.service.TaskDuplicator;
@ -73,6 +74,7 @@ import org.tasks.Broadcaster;
import org.tasks.R;
import org.tasks.dialogs.DialogBuilder;
import org.tasks.dialogs.SortDialog;
import org.tasks.gtasks.SyncAdapterHelper;
import org.tasks.injection.ForActivity;
import org.tasks.injection.FragmentComponent;
import org.tasks.injection.InjectingListFragment;
@ -120,7 +122,6 @@ public class TaskListFragment extends InjectingListFragment implements
// --- activities
public static final long AUTOSYNC_INTERVAL = 90000L;
public static final int ACTIVITY_REQUEST_NEW_FILTER = 5;
// --- menu codes
@ -132,6 +133,7 @@ public class TaskListFragment extends InjectingListFragment implements
// --- instance variables
@Inject SyncAdapterHelper syncAdapterHelper;
@Inject TaskService taskService;
@Inject TaskDeleter taskDeleter;
@Inject TaskDuplicator taskDuplicator;
@ -141,7 +143,6 @@ public class TaskListFragment extends InjectingListFragment implements
@Inject TaskAttachmentDao taskAttachmentDao;
@Inject GtasksPreferenceService gtasksPreferenceService;
@Inject DialogBuilder dialogBuilder;
@Inject SyncActionHelper syncActionHelper;
@Inject CheckBoxes checkBoxes;
@Inject VoiceInputAssistant voiceInputAssistant;
@Inject TaskCreator taskCreator;
@ -152,6 +153,7 @@ public class TaskListFragment extends InjectingListFragment implements
@BindView(R.id.swipe_layout) SwipeRefreshLayout swipeRefreshLayout;
@BindView(R.id.swipe_layout_empty) SwipeRefreshLayout emptyView;
@BindView(R.id.toolbar) Toolbar toolbar;
@BindView(R.id.task_list_coordinator) CoordinatorLayout coordinatorLayout;
private TaskAdapter taskAdapter = null;
private RefreshReceiver refreshReceiver = new RefreshReceiver();
@ -168,7 +170,7 @@ public class TaskListFragment extends InjectingListFragment implements
@Override
public void onRefresh() {
if (!syncActionHelper.performSyncAction()) {
if (!syncAdapterHelper.initiateManualSync()) {
refresh();
}
}
@ -470,19 +472,18 @@ public class TaskListFragment extends InjectingListFragment implements
getActivity().registerReceiver(refreshReceiver, new IntentFilter(AstridApiConstants.BROADCAST_EVENT_REFRESH));
initiateAutomaticSyncImpl();
refresh();
}
/**
* Implementation of initiation automatic sync. Subclasses should override this method;
* the above method takes care of calling it in the correct way
*/
protected void initiateAutomaticSyncImpl() {
if (BuiltInFilterExposer.isInbox(context, filter)) {
syncActionHelper.initiateAutomaticSync();
}
public Snackbar makeSnackbar(int resId) {
return makeSnackbar(getString(resId));
}
public Snackbar makeSnackbar(String text) {
Snackbar snackbar = Snackbar.make(coordinatorLayout, text, 8000)
.setActionTextColor(getResources().getColor(R.color.snackbar_text_color));
snackbar.getView().setBackgroundColor(getResources().getColor(R.color.snackbar_background));
return snackbar;
}
@Override

@ -5,17 +5,12 @@
*/
package com.todoroo.astrid.gtasks;
import android.content.Context;
import com.todoroo.andlib.utility.DateUtilities;
import org.tasks.AccountManager;
import org.tasks.R;
import org.tasks.injection.ForApplication;
import org.tasks.preferences.Preferences;
import javax.inject.Inject;
import javax.inject.Singleton;
/**
* Methods for working with GTasks preferences
@ -23,12 +18,9 @@ import javax.inject.Singleton;
* @author timsu
*
*/
@Singleton
public class GtasksPreferenceService {
private final Context context;
private final Preferences preferences;
private final AccountManager accountManager;
public static final String IDENTIFIER = "gtasks"; //$NON-NLS-1$
@ -36,11 +28,8 @@ public class GtasksPreferenceService {
private static final String PREF_USER_NAME = IDENTIFIER + "_user"; //$NON-NLS-1$
@Inject
public GtasksPreferenceService(@ForApplication Context context, Preferences preferences,
AccountManager accountManager) {
this.context = context;
public GtasksPreferenceService(Preferences preferences) {
this.preferences = preferences;
this.accountManager = accountManager;
}
public String getDefaultList() {
@ -63,15 +52,6 @@ public class GtasksPreferenceService {
protected static final String PREF_ONGOING = "_ongoing"; //$NON-NLS-1$
/**
* @return true if we have a token for this user, false otherwise
*/
public boolean isLoggedIn() {
return context.getResources().getBoolean(R.bool.sync_supported) &&
preferences.getBoolean(R.string.sync_gtasks, false) &&
accountManager.hasAccount(preferences.getStringValue(PREF_USER_NAME));
}
/** @return Last Successful Sync Date, or 0 */
public long getLastSyncDate() {
return preferences.getLong(IDENTIFIER + PREF_LAST_SYNC, 0);
@ -101,4 +81,8 @@ public class GtasksPreferenceService {
public void recordSyncStart() {
preferences.setBoolean(IDENTIFIER + PREF_ONGOING, true);
}
public int getSyncInterval() {
return preferences.getIntegerFromString(R.string.gtasks_GPr_interval_key, 0);
}
}

@ -1,77 +0,0 @@
/**
* Copyright (c) 2012 Todoroo Inc
*
* See the file "LICENSE" for the full license governing this code.
*/
package com.todoroo.astrid.helper;
import com.todoroo.andlib.utility.DateUtilities;
import com.todoroo.astrid.activity.TaskListFragment;
import com.todoroo.astrid.gtasks.GtasksPreferenceService;
import com.todoroo.astrid.service.SyncV2Service;
import com.todoroo.astrid.sync.SyncResultCallback;
import org.tasks.Broadcaster;
import org.tasks.preferences.Preferences;
import org.tasks.sync.RecordSyncStatusCallback;
import javax.inject.Inject;
/**
* SyncActionHelper is a helper class for encapsulating UI actions
* responsible for performing sync and prompting user to sign up for a new
* sync service.
* <p/>
* In order to make this work you need to call register() and unregister() in
* onResume and onPause, respectively.
*
* @author Tim Su <tim@astrid.com>
*/
public class SyncActionHelper {
public static final String PREF_LAST_AUTO_SYNC = "taskListLastAutoSync"; //$NON-NLS-1$
public final SyncResultCallback syncResultCallback;
private final SyncV2Service syncService;
private final Preferences preferences;
// --- boilerplate
@Inject
public SyncActionHelper(GtasksPreferenceService gtasksPreferenceService, SyncV2Service syncService,
Preferences preferences, Broadcaster broadcaster) {
this.syncService = syncService;
this.preferences = preferences;
syncResultCallback = new RecordSyncStatusCallback(gtasksPreferenceService, broadcaster);
}
// --- automatic sync logic
public void initiateAutomaticSync() {
long tasksPushedAt = preferences.getLong(PREF_LAST_AUTO_SYNC, 0);
if (DateUtilities.now() - tasksPushedAt > TaskListFragment.AUTOSYNC_INTERVAL) {
performSyncServiceV2Sync();
}
}
// --- sync logic
protected void performSyncServiceV2Sync() {
boolean syncOccurred = syncService.synchronizeActiveTasks(syncResultCallback);
if (syncOccurred) {
preferences.setLong(PREF_LAST_AUTO_SYNC, DateUtilities.now());
}
}
public boolean performSyncAction() {
if (syncService.isActive()) {
syncService.synchronizeActiveTasks(syncResultCallback);
return true;
} else {
return false;
}
}
}

@ -1,72 +0,0 @@
/**
* Copyright (c) 2012 Todoroo Inc
*
* See the file "LICENSE" for the full license governing this code.
*/
package com.todoroo.astrid.service;
import com.todoroo.astrid.gtasks.GtasksList;
import com.todoroo.astrid.gtasks.sync.GtasksSyncV2Provider;
import com.todoroo.astrid.sync.SyncResultCallback;
import javax.inject.Inject;
import javax.inject.Singleton;
/**
* SyncV2Service is a simplified synchronization interface for supporting
* next-generation sync interfaces such as Google Tasks and Astrid.com
*
* @author Tim Su <tim@astrid.com>
*
*/
@Singleton
public class SyncV2Service {
/*
* At present, sync provider interactions are handled through code. If
* there is enough interest, the Astrid team could create an interface
* for responding to sync requests through this new API.
*/
private final GtasksSyncV2Provider provider;
@Inject
public SyncV2Service(GtasksSyncV2Provider gtasksSyncV2Provider) {
provider = gtasksSyncV2Provider;
}
public boolean isActive() {
return provider.isActive();
}
/**
* Initiate synchronization of active tasks
*
* @param callback result callback
* @return true if any servide was logged in and initiated a sync
*/
public boolean synchronizeActiveTasks(SyncResultCallback callback) {
if (provider.isActive()) {
provider.synchronizeActiveTasks(callback);
return true;
}
return false;
}
/**
* Initiate synchronization of task list
*
* @param list list object
* @param callback result callback
*/
public void synchronizeList(GtasksList list, SyncResultCallback callback) {
if(provider.isActive()) {
provider.synchronizeList(list, callback);
}
}
public void clearCompleted(GtasksList list, SyncResultCallback callback) {
if (provider.isActive()) {
provider.clearCompleted(list, callback);
}
}
}

@ -8,16 +8,13 @@ import com.todoroo.astrid.dao.TaskAttachmentDao;
import com.todoroo.astrid.dao.TaskDao;
import com.todoroo.astrid.dao.TaskListMetadataDao;
import com.todoroo.astrid.dao.UserActivityDao;
import com.todoroo.astrid.service.SyncV2Service;
import com.todoroo.astrid.service.TaskService;
import com.todoroo.astrid.tags.TagService;
import org.tasks.analytics.Tracker;
import org.tasks.filters.FilterCounter;
import org.tasks.injection.ApplicationComponent;
import org.tasks.injection.InjectingApplication;
import org.tasks.preferences.Preferences;
import org.tasks.sync.SyncThrottle;
import javax.inject.Inject;
@ -33,10 +30,8 @@ public class Tasks extends InjectingApplication {
@Inject TaskAttachmentDao taskAttachmentDao;
@Inject TaskListMetadataDao taskListMetadataDao;
@Inject TaskService taskService;
@Inject SyncV2Service syncV2Service;
@Inject TagService tagService;
@Inject Broadcaster broadcaster;
@Inject SyncThrottle syncThrottle;
@Inject Preferences preferences;
@Inject Tracker tracker;
@Inject FlavorSetup flavorSetup;

@ -22,11 +22,11 @@ import com.todoroo.astrid.data.Task;
import com.todoroo.astrid.gtasks.GtasksList;
import com.todoroo.astrid.gtasks.GtasksListService;
import com.todoroo.astrid.gtasks.GtasksMetadata;
import com.todoroo.astrid.gtasks.GtasksPreferenceService;
import com.todoroo.astrid.tags.TagService;
import com.todoroo.astrid.tags.TaskToTagMetadata;
import org.tasks.R;
import org.tasks.gtasks.SyncAdapterHelper;
import org.tasks.injection.ForApplication;
import java.util.List;
@ -48,18 +48,20 @@ public class FilterCriteriaProvider {
private Context context;
private TagService tagService;
private GtasksPreferenceService gtasksPreferenceService;
private GtasksListService gtasksListService;
private GtasksMetadata gtasksMetadata;
private Resources r;
private final SyncAdapterHelper syncAdapterHelper;
@Inject
public FilterCriteriaProvider(@ForApplication Context context, TagService tagService, GtasksPreferenceService gtasksPreferenceService, GtasksListService gtasksListService, GtasksMetadata gtasksMetadata) {
public FilterCriteriaProvider(@ForApplication Context context, TagService tagService,
GtasksListService gtasksListService,
GtasksMetadata gtasksMetadata, SyncAdapterHelper syncAdapterHelper) {
this.context = context;
this.tagService = tagService;
this.gtasksPreferenceService = gtasksPreferenceService;
this.gtasksListService = gtasksListService;
this.gtasksMetadata = gtasksMetadata;
this.syncAdapterHelper = syncAdapterHelper;
r = context.getResources();
}
@ -72,7 +74,7 @@ public class FilterCriteriaProvider {
result.add(getDueDateFilter());
result.add(getImportanceFilter());
result.add(getTaskTitleContainsFilter());
if (gtasksPreferenceService.isLoggedIn()) {
if (syncAdapterHelper.isEnabled()) {
result.add(getGtasksFilterCriteria());
}

@ -6,7 +6,6 @@ import android.app.FragmentManager;
import com.todoroo.astrid.activity.BeastModePreferences;
import com.todoroo.astrid.data.Task;
import com.todoroo.astrid.files.FilesControlSet;
import com.todoroo.astrid.gtasks.GtasksPreferenceService;
import com.todoroo.astrid.repeats.RepeatControlSet;
import com.todoroo.astrid.tags.TagsControlSet;
import com.todoroo.astrid.timers.TimerControlSet;
@ -16,6 +15,7 @@ import com.todoroo.astrid.ui.ReminderControlSet;
import org.tasks.BuildConfig;
import org.tasks.R;
import org.tasks.gtasks.SyncAdapterHelper;
import org.tasks.preferences.Preferences;
import org.tasks.ui.CalendarControlSet;
import org.tasks.ui.DeadlineControlSet;
@ -78,12 +78,12 @@ public class TaskEditControlSetFragmentManager {
private final Map<String, Integer> controlSetFragments = new LinkedHashMap<>();
private final List<String> displayOrder;
private final FragmentManager fragmentManager;
private final SyncAdapterHelper syncAdapterHelper;
private int numRows;
private GtasksPreferenceService gtasksPreferenceService;
@Inject
public TaskEditControlSetFragmentManager(Activity activity, Preferences preferences, GtasksPreferenceService gtasksPreferenceService) {
this.gtasksPreferenceService = gtasksPreferenceService;
public TaskEditControlSetFragmentManager(Activity activity, Preferences preferences, SyncAdapterHelper syncAdapterHelper) {
this.syncAdapterHelper = syncAdapterHelper;
displayOrder = BeastModePreferences.constructOrderedControlList(preferences, activity);
displayOrder.add(0, activity.getString(EditTitleControlSet.TAG));
displayOrder.add(1, activity.getString(CommentBarFragment.TAG));
@ -166,7 +166,7 @@ public class TaskEditControlSetFragmentManager {
case CommentBarFragment.TAG:
return new CommentBarFragment();
case GoogleTaskListFragment.TAG:
return gtasksPreferenceService.isLoggedIn()
return syncAdapterHelper.isEnabled()
? new GoogleTaskListFragment()
: null;
default:

@ -0,0 +1,99 @@
package org.tasks.gtasks;
import android.accounts.Account;
import android.content.ContentResolver;
import android.os.Bundle;
import com.todoroo.astrid.gtasks.GtasksPreferenceService;
import org.tasks.AccountManager;
import org.tasks.R;
import org.tasks.preferences.Preferences;
import javax.inject.Inject;
import timber.log.Timber;
public class SyncAdapterHelper {
private static final String AUTHORITY = "org.tasks";
private final AccountManager accountManager;
private final Preferences preferences;
private final GtasksPreferenceService gtasksPreferenceService;
@Inject
public SyncAdapterHelper(AccountManager accountManager, Preferences preferences,
GtasksPreferenceService gtasksPreferenceService) {
this.accountManager = accountManager;
this.preferences = preferences;
this.gtasksPreferenceService = gtasksPreferenceService;
}
/**
* Helper method to trigger an immediate sync ("refresh").
*
* <p>This should only be used when we need to preempt the normal sync schedule. Typically, this
* means the user has pressed the "refresh" button.
*
* Note that SYNC_EXTRAS_MANUAL will cause an immediate sync, without any optimization to
* preserve battery life. If you know new data is available (perhaps via a GCM notification),
* but the user is not actively waiting for that data, you should omit this flag; this will give
* the OS additional freedom in scheduling your sync request.
*/
public boolean initiateManualSync() {
Account account = getAccount();
if (account == null) {
return false;
}
Bundle extras = new Bundle();
// Disable sync backoff and ignore sync preferences. In other words...perform sync NOW!
extras.putBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, true);
extras.putBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, true);
ContentResolver.requestSync(account, AUTHORITY, extras);
return true;
}
public boolean isEnabled() {
return preferences.getBoolean(R.string.sync_gtasks, false) && getAccount() != null;
}
public boolean masterSyncEnabled() {
return ContentResolver.getMasterSyncAutomatically();
}
public void enableSynchronization(boolean enabled) {
Account account = getAccount();
if (account != null) {
Timber.d("enableSynchronization=%s", enabled);
ContentResolver.setIsSyncable(account, AUTHORITY, 1);
if (enabled) {
setSynchronizationInterval(preferences.getIntegerFromString(R.string.gtasks_GPr_interval_key, 0));
} else {
setSynchronizationInterval(0);
}
}
}
public void setSynchronizationInterval(int seconds) {
Account account = getAccount();
if (account != null) {
boolean syncAutomatically = seconds > 0;
ContentResolver.setSyncAutomatically(account, AUTHORITY, syncAutomatically);
Timber.d("syncAutomatically=%s, syncInterval=%s", syncAutomatically, seconds);
if (syncAutomatically) {
ContentResolver.addPeriodicSync(account, AUTHORITY, Bundle.EMPTY, seconds);
} else {
ContentResolver.removePeriodicSync(account, AUTHORITY, Bundle.EMPTY);
}
}
}
private Account getAccount() {
return accountManager.getAccount(gtasksPreferenceService.getUserName());
}
public boolean shouldShowBackgroundSyncWarning() {
return isEnabled() && !masterSyncEnabled() && !ContentResolver.getPeriodicSyncs(getAccount(), AUTHORITY).isEmpty();
}
}

@ -6,10 +6,7 @@ import javax.inject.Singleton;
import dagger.Component;
@Singleton
@Component(modules = ApplicationModule.class)
public interface ApplicationComponent {
public interface BaseApplicationComponent {
Tasks inject(Tasks tasks);
ActivityComponent plus(ActivityModule module);

@ -4,7 +4,6 @@ import org.tasks.location.GeofenceTransitionsIntentService;
import org.tasks.scheduling.BackupIntentService;
import org.tasks.scheduling.CalendarNotificationIntentService;
import org.tasks.scheduling.GeofenceSchedulingIntentService;
import org.tasks.scheduling.GtasksBackgroundService;
import org.tasks.scheduling.RefreshSchedulerIntentService;
import org.tasks.scheduling.ReminderSchedulerIntentService;
@ -23,6 +22,4 @@ public interface IntentServiceComponent {
void inject(BackupIntentService backupIntentService);
void inject(GeofenceTransitionsIntentService geofenceTransitionsIntentService);
void inject(GtasksBackgroundService gtasksBackgroundService);
}

@ -6,10 +6,11 @@ import android.content.Context;
import android.content.Intent;
import android.support.design.widget.Snackbar;
import android.view.View;
import android.view.WindowManager;
import com.todoroo.andlib.data.Property;
import com.todoroo.andlib.utility.DateUtilities;
import com.todoroo.astrid.activity.TaskListActivity;
import com.todoroo.astrid.activity.TaskListFragment;
import com.todoroo.astrid.api.AstridApiConstants;
import com.todoroo.astrid.data.Task;
import com.todoroo.astrid.service.TaskService;
@ -45,6 +46,14 @@ public class RepeatConfirmationReceiver extends BroadcastReceiver {
@Override
public void onReceive(final Context context, final Intent intent) {
TaskListFragment taskListFragment = null;
if (activity instanceof TaskListActivity) {
taskListFragment = ((TaskListActivity) activity).getTaskListFragment();
}
if (taskListFragment == null) {
Timber.d("No task list fragment");
return;
}
long taskId = intent.getLongExtra(AstridApiConstants.EXTRAS_TASK_ID, 0);
if (taskId > 0) {
@ -53,15 +62,7 @@ public class RepeatConfirmationReceiver extends BroadcastReceiver {
Task task = taskService.fetchById(taskId, REPEAT_RESCHEDULED_PROPERTIES);
try {
showSnackbar(activity.findViewById(R.id.task_list_coordinator), task, oldDueDate, newDueDate);
} catch (WindowManager.BadTokenException e) { // Activity not running when tried to show dialog--rebroadcast
Timber.e(e, e.getMessage());
new Thread() {
@Override
public void run() {
context.sendBroadcast(intent);
}
}.start();
showSnackbar(taskListFragment, task, oldDueDate, newDueDate);
} catch (Exception e) {
Timber.e(e, e.getMessage());
tracker.reportException(e);
@ -69,12 +70,10 @@ public class RepeatConfirmationReceiver extends BroadcastReceiver {
}
}
private void showSnackbar(View view, final Task task, final long oldDueDate, final long newDueDate) {
private void showSnackbar(TaskListFragment taskListFragment, final Task task, final long oldDueDate, final long newDueDate) {
String dueDateString = getRelativeDateAndTimeString(activity, newDueDate);
String snackbarText = activity.getString(R.string.repeat_snackbar, task.getTitle(), dueDateString);
Snackbar snackbar = Snackbar.make(view, snackbarText, Snackbar.LENGTH_LONG)
.setActionTextColor(activity.getResources().getColor(R.color.snackbar_text_color))
taskListFragment.makeSnackbar(snackbarText)
.setAction(R.string.DLG_undo, new View.OnClickListener() {
@Override
public void onClick(View v) {
@ -82,9 +81,8 @@ public class RepeatConfirmationReceiver extends BroadcastReceiver {
task.setCompletionDate(0L);
taskService.save(task);
}
});
snackbar.getView().setBackgroundColor(activity.getResources().getColor(R.color.snackbar_background));
snackbar.show();
})
.show();
}
private String getRelativeDateAndTimeString(Context context, long date) {

@ -3,7 +3,6 @@ package org.tasks.scheduling;
import android.content.Context;
import android.content.Intent;
import org.tasks.R;
import org.tasks.injection.ForApplication;
import javax.inject.Inject;
@ -22,9 +21,6 @@ public class BackgroundScheduler {
scheduleBackupService();
scheduleMidnightRefresh();
scheduleCalendarNotifications();
if (context.getResources().getBoolean(R.bool.sync_supported)) {
scheduleGtaskSync();
}
}
public void scheduleBackupService() {
@ -35,10 +31,6 @@ public class BackgroundScheduler {
context.startService(new Intent(context, RefreshSchedulerIntentService.class));
}
public void scheduleGtaskSync() {
context.startService(new Intent(context, GtasksBackgroundService.class));
}
public void scheduleCalendarNotifications() {
context.startService(new Intent(context, CalendarNotificationIntentService.class));
}

@ -1,32 +0,0 @@
package org.tasks.sync;
import com.todoroo.astrid.activity.TaskListFragment;
import com.todoroo.astrid.gtasks.GtasksPreferenceService;
import org.tasks.Broadcaster;
public class IndeterminateProgressBarSyncResultCallback extends RecordSyncStatusCallback {
private final TaskListFragment taskListFragment;
private final GtasksPreferenceService gtasksPreferenceService;
public IndeterminateProgressBarSyncResultCallback(TaskListFragment taskListFragment, GtasksPreferenceService gtasksPreferenceService, Broadcaster broadcaster) {
super(gtasksPreferenceService, broadcaster);
this.taskListFragment = taskListFragment;
this.gtasksPreferenceService = gtasksPreferenceService;
}
@Override
public void started() {
super.started();
taskListFragment.setSyncOngoing(gtasksPreferenceService.isOngoing());
}
@Override
public void finished() {
super.finished();
taskListFragment.setSyncOngoing(gtasksPreferenceService.isOngoing());
}
}

@ -18,6 +18,7 @@ public class RecordSyncStatusCallback implements SyncResultCallback {
@Override
public void started() {
gtasksPreferenceService.recordSyncStart();
broadcaster.refresh();
}
@Override

@ -9,11 +9,13 @@ import java.util.concurrent.ExecutorService;
import java.util.concurrent.RejectedExecutionException;
import javax.inject.Inject;
import javax.inject.Singleton;
import timber.log.Timber;
import static java.util.concurrent.Executors.newSingleThreadExecutor;
@Singleton
public class SyncExecutor {
private final ExecutorService executor = newSingleThreadExecutor(

@ -1,26 +0,0 @@
package org.tasks.sync;
import org.tasks.time.DateTime;
import java.util.HashMap;
import java.util.Map;
import javax.inject.Inject;
import javax.inject.Singleton;
@Singleton
public class SyncThrottle {
private final Map<Long, DateTime> lastSync = new HashMap<>();
@Inject
public SyncThrottle() {
}
public synchronized boolean canSync(long listId) {
DateTime now = new DateTime();
DateTime last = lastSync.get(listId);
lastSync.put(listId, now);
return last == null || last.isBefore(now.minusMinutes(10));
}
}

@ -185,16 +185,12 @@
<string name="randomly">عشوائياً</string>
<string name="pick_a_date_and_time">اختر تاريخ و وقت</string>
<string name="pick_a_location">أختر الموقع</string>
<string-array name="sync_SPr_interval_entries">
<item>تعطيل</item>
<item>كل 15 دقيقة</item>
<item>كل ثلاثين دقيقة</item>
<item>كل ساعة</item>
<item>كل ثلاث ساعات</item>
<item>كل ست ساعات</item>
<item>كل 12 ساعة</item>
<item>كل يوم</item>
<item>كل ثلاث أيام</item>
<item>كل أسبوع</item>
</string-array>
<string name="sync_interval_disable">تعطيل</string>
<string name="sync_interval_one_hour">كل ساعة</string>
<string name="sync_interval_three_hours">كل ثلاث ساعات</string>
<string name="sync_interval_six_hours">كل ست ساعات</string>
<string name="sync_interval_twelve_hours">كل 12 ساعة</string>
<string name="sync_interval_one_day">كل يوم</string>
<string name="sync_interval_three_days">كل ثلاث أيام</string>
<string name="sync_interval_one_week">كل أسبوع</string>
</resources>

@ -414,16 +414,12 @@
<string name="theme_light">Светла</string>
<string name="theme_dark">Тъмна</string>
<string name="default_value">По подразбиране</string>
<string-array name="sync_SPr_interval_entries">
<item>изключено</item>
<item>на всеки 15 минути</item>
<item>на всеки 30 минути</item>
<item>на всеки час</item>
<item>на всеки три часа</item>
<item>на всеки шест часа</item>
<item>на всеки дванадесет часа</item>
<item>всеки ден</item>
<item>на всеки три дена</item>
<item>всяка седмица</item>
</string-array>
<string name="sync_interval_disable">изключено</string>
<string name="sync_interval_one_hour">на всеки час</string>
<string name="sync_interval_three_hours">на всеки три часа</string>
<string name="sync_interval_six_hours">на всеки шест часа</string>
<string name="sync_interval_twelve_hours">на всеки дванадесет часа</string>
<string name="sync_interval_one_day">всеки ден</string>
<string name="sync_interval_three_days">на всеки три дена</string>
<string name="sync_interval_one_week">всяка седмица</string>
</resources>

@ -145,16 +145,12 @@
<string name="sync_SPr_forget">Surt</string>
<string name="sync_SPr_forget_description">Esborra tota la informació de sincronització</string>
<string name="sync_forget_confirm">Tancar sessió / esborra la informació de sincronització?</string>
<string-array name="sync_SPr_interval_entries">
<item>desactivat</item>
<item>cada quince minuts</item>
<item>cada trenta minuts</item>
<item>cada hora</item>
<item>cada tres hores</item>
<item>cada sis hores</item>
<item>cada dotze hores</item>
<item>diàriament</item>
<item>cada tres dies</item>
<item>setmanalment</item>
</string-array>
<string name="sync_interval_disable">desactivat</string>
<string name="sync_interval_one_hour">cada hora</string>
<string name="sync_interval_three_hours">cada tres hores</string>
<string name="sync_interval_six_hours">cada sis hores</string>
<string name="sync_interval_twelve_hours">cada dotze hores</string>
<string name="sync_interval_one_day">diàriament</string>
<string name="sync_interval_three_days">cada tres dies</string>
<string name="sync_interval_one_week">setmanalment</string>
</resources>

@ -249,16 +249,12 @@
<string name="default_priority">Výchozí</string>
<string name="low_priority">Nízká</string>
<string name="notification_priority">Priorita oznámení</string>
<string-array name="sync_SPr_interval_entries">
<item>zakázat</item>
<item>každých patnáct minut</item>
<item>každých třicet minut</item>
<item>každou hodinu</item>
<item>každé tři hodiny</item>
<item>každých šest hodin</item>
<item>každých dvanáct hodin</item>
<item>každý den</item>
<item>každé tři dny</item>
<item>každý týden</item>
</string-array>
<string name="sync_interval_disable">zakázat</string>
<string name="sync_interval_one_hour">každou hodinu</string>
<string name="sync_interval_three_hours">každé tři hodiny</string>
<string name="sync_interval_six_hours">každých šest hodin</string>
<string name="sync_interval_twelve_hours">každých dvanáct hodin</string>
<string name="sync_interval_one_day">každý den</string>
<string name="sync_interval_three_days">každé tři dny</string>
<string name="sync_interval_one_week">každý týden</string>
</resources>

@ -132,16 +132,11 @@
<string name="sync_SPr_interval_title">Baggrunds Synk</string>
<string name="sync_SPr_forget">Log af</string>
<string name="sync_SPr_forget_description">Sletter al synkroniserings data</string>
<string-array name="sync_SPr_interval_entries">
<item>disable</item>
<item>every fifteen minutes</item>
<item>every thirty minutes</item>
<item>hver time</item>
<item>hver 3. time</item>
<item>hver 6. time</item>
<item>hver 12. time</item>
<item>hver dag</item>
<item>hver 3. dag</item>
<item>hver uge</item>
</string-array>
<string name="sync_interval_one_hour">hver time</string>
<string name="sync_interval_three_hours">hver 3. time</string>
<string name="sync_interval_six_hours">hver 6. time</string>
<string name="sync_interval_twelve_hours">hver 12. time</string>
<string name="sync_interval_one_day">hver dag</string>
<string name="sync_interval_three_days">hver 3. dag</string>
<string name="sync_interval_one_week">hver uge</string>
</resources>

@ -398,16 +398,12 @@
<string name="theme_light">Hell</string>
<string name="theme_dark">Dunkel</string>
<string name="default_value">Standard</string>
<string-array name="sync_SPr_interval_entries">
<item>deaktivieren</item>
<item>alle 15 Minuten</item>
<item>alle 30 Minuten</item>
<item>stündlich</item>
<item>alle 3 Stunden</item>
<item>alle 6 Stunden</item>
<item>alle 12 Stunden</item>
<item>täglich</item>
<item>jeden dritten Tag</item>
<item>wöchentlich</item>
</string-array>
<string name="sync_interval_disable">deaktivieren</string>
<string name="sync_interval_one_hour">stündlich</string>
<string name="sync_interval_three_hours">alle 3 Stunden</string>
<string name="sync_interval_six_hours">alle 6 Stunden</string>
<string name="sync_interval_twelve_hours">alle 12 Stunden</string>
<string name="sync_interval_one_day">täglich</string>
<string name="sync_interval_three_days">jeden dritten Tag</string>
<string name="sync_interval_one_week">wöchentlich</string>
</resources>

@ -238,16 +238,11 @@
<string name="sync_SPr_interval_title">Συγχρονισμός παρασκηνίου</string>
<string name="sync_SPr_forget">Αποσύνδεση</string>
<string name="TLA_menu_donate">Δωρίστε</string>
<string-array name="sync_SPr_interval_entries">
<item>disable</item>
<item>every fifteen minutes</item>
<item>κάθε τριάντα λεπτά</item>
<item>κάθε ώρα</item>
<item>κάθε τρεις ώρες</item>
<item>κάθε έξι ώρες</item>
<item>κάθε δώδεκα ώρες</item>
<item>κάθε μέρα</item>
<item>κάθε τρεις ημέρες</item>
<item>κάθε εβδομάδα</item>
</string-array>
<string name="sync_interval_one_hour">κάθε ώρα</string>
<string name="sync_interval_three_hours">κάθε τρεις ώρες</string>
<string name="sync_interval_six_hours">κάθε έξι ώρες</string>
<string name="sync_interval_twelve_hours">κάθε δώδεκα ώρες</string>
<string name="sync_interval_one_day">κάθε μέρα</string>
<string name="sync_interval_three_days">κάθε τρεις ημέρες</string>
<string name="sync_interval_one_week">κάθε εβδομάδα</string>
</resources>

@ -408,16 +408,12 @@
<string name="theme_light">Claro</string>
<string name="theme_dark">Oscuro</string>
<string name="default_value">Predeterminado</string>
<string-array name="sync_SPr_interval_entries">
<item>deshabilitar</item>
<item>cada quince minutos</item>
<item>cada treinta minutos</item>
<item>cada hora</item>
<item>cada tres horas</item>
<item>cada seis horas</item>
<item>cada doce horas</item>
<item>cada día</item>
<item>cada tres días</item>
<item>cada semana</item>
</string-array>
<string name="sync_interval_disable">deshabilitar</string>
<string name="sync_interval_one_hour">cada hora</string>
<string name="sync_interval_three_hours">cada tres horas</string>
<string name="sync_interval_six_hours">cada seis horas</string>
<string name="sync_interval_twelve_hours">cada doce horas</string>
<string name="sync_interval_one_day">cada día</string>
<string name="sync_interval_three_days">cada tres días</string>
<string name="sync_interval_one_week">cada semana</string>
</resources>

@ -272,16 +272,12 @@
<string name="disable_notification_light">خاموش کردن چراغ اعلان</string>
<string name="no_title">(بدون عنوان)</string>
<string name="default_list">لیست پیش فرض</string>
<string-array name="sync_SPr_interval_entries">
<item>غیرفعال</item>
<item>هر پانزده دقیقه</item>
<item>هر سی دقیقه</item>
<item>هر ساعت</item>
<item>هر سه ساعت</item>
<item>هر شش ساعت</item>
<item>هر دوازده ساعت</item>
<item>هر روز</item>
<item>هر سه روز</item>
<item>هر هفته</item>
</string-array>
<string name="sync_interval_disable">غیرفعال</string>
<string name="sync_interval_one_hour">هر ساعت</string>
<string name="sync_interval_three_hours">هر سه ساعت</string>
<string name="sync_interval_six_hours">هر شش ساعت</string>
<string name="sync_interval_twelve_hours">هر دوازده ساعت</string>
<string name="sync_interval_one_day">هر روز</string>
<string name="sync_interval_three_days">هر سه روز</string>
<string name="sync_interval_one_week">هر هفته</string>
</resources>

@ -369,16 +369,12 @@
<string name="select_filter">Sélectionner le filtre</string>
<string name="filter">Filtre</string>
<string name="opacity">Transparence</string>
<string-array name="sync_SPr_interval_entries">
<item>désactiver</item>
<item>toutes les quinze minutes</item>
<item>toutes les trente minutes</item>
<item>toutes les heures</item>
<item>toutes les trois heures</item>
<item>toutes les six heures</item>
<item>toutes les douze heures</item>
<item>tous les jours</item>
<item>tous les trois jours</item>
<item>toutes les semaines</item>
</string-array>
<string name="sync_interval_disable">désactiver</string>
<string name="sync_interval_one_hour">toutes les heures</string>
<string name="sync_interval_three_hours">toutes les trois heures</string>
<string name="sync_interval_six_hours">toutes les six heures</string>
<string name="sync_interval_twelve_hours">toutes les douze heures</string>
<string name="sync_interval_one_day">tous les jours</string>
<string name="sync_interval_three_days">tous les trois jours</string>
<string name="sync_interval_one_week">toutes les semaines</string>
</resources>

@ -411,16 +411,12 @@ Se visualizzi questo errore più volte, ti consigliamo di cancellare tutti i dat
<string name="theme_light">Chiaro</string>
<string name="theme_dark">Scuro</string>
<string name="default_value">Predefinito</string>
<string-array name="sync_SPr_interval_entries">
<item>disabilita</item>
<item>ogni quindici minuti</item>
<item>ogni trenta minuti</item>
<item>ogni ora</item>
<item>ogni tre ore</item>
<item>ogni sei ore</item>
<item>ogni dodici ore</item>
<item>ogni giorno</item>
<item>ogni tre giorni</item>
<item>Ogni settimana</item>
</string-array>
<string name="sync_interval_disable">disabilita</string>
<string name="sync_interval_one_hour">ogni ora</string>
<string name="sync_interval_three_hours">ogni tre ore</string>
<string name="sync_interval_six_hours">ogni sei ore</string>
<string name="sync_interval_twelve_hours">ogni dodici ore</string>
<string name="sync_interval_one_day">ogni giorno</string>
<string name="sync_interval_three_days">ogni tre giorni</string>
<string name="sync_interval_one_week">Ogni settimana</string>
</resources>

@ -406,16 +406,12 @@
<string name="theme_grey">אפור</string>
<string name="theme_blue_grey">כחול אפור</string>
<string name="theme_black">שחור</string>
<string-array name="sync_SPr_interval_entries">
<item>מִנְעִי</item>
<item>כל רבע שעה</item>
<item>כל חצי שעה</item>
<item>כל שעה</item>
<item>כל שלוש שעות</item>
<item>כל שש שעות</item>
<item>כל שתים עשרה שעות</item>
<item>כל יום</item>
<item>כל שלושה ימים</item>
<item>כל שבוע</item>
</string-array>
<string name="sync_interval_disable">מִנְעִי</string>
<string name="sync_interval_one_hour">כל שעה</string>
<string name="sync_interval_three_hours">כל שלוש שעות</string>
<string name="sync_interval_six_hours">כל שש שעות</string>
<string name="sync_interval_twelve_hours">כל שתים עשרה שעות</string>
<string name="sync_interval_one_day">כל יום</string>
<string name="sync_interval_three_days">כל שלושה ימים</string>
<string name="sync_interval_one_week">כל שבוע</string>
</resources>

@ -414,16 +414,12 @@
<string name="theme_light">ライト</string>
<string name="theme_dark">ダーク</string>
<string name="default_value">デフォルト</string>
<string-array name="sync_SPr_interval_entries">
<item>無効</item>
<item>15分毎</item>
<item>30分毎</item>
<item>1時間毎</item>
<item>3時間毎</item>
<item>6時間毎</item>
<item>12時間毎</item>
<item>毎日</item>
<item>3日に一度</item>
<item>毎週</item>
</string-array>
<string name="sync_interval_disable">無効</string>
<string name="sync_interval_one_hour">1時間毎</string>
<string name="sync_interval_three_hours">3時間毎</string>
<string name="sync_interval_six_hours">6時間毎</string>
<string name="sync_interval_twelve_hours">12時間毎</string>
<string name="sync_interval_one_day">毎日</string>
<string name="sync_interval_three_days">3日に一度</string>
<string name="sync_interval_one_week">毎週</string>
</resources>

@ -409,16 +409,12 @@ Tasks의 백업에서 당신의 일정을 복구하시기 바랍니다.
<string name="theme_grey">회색</string>
<string name="theme_blue_grey">회청색</string>
<string name="theme_black">검정</string>
<string-array name="sync_SPr_interval_entries">
<item>사용안함</item>
<item>15분마다</item>
<item>30분마다</item>
<item>매시간</item>
<item>3시간마다</item>
<item>6시간마다</item>
<item>12시간마다</item>
<item>매일</item>
<item>3일마다</item>
<item>일주일마다</item>
</string-array>
<string name="sync_interval_disable">사용안함</string>
<string name="sync_interval_one_hour">매시간</string>
<string name="sync_interval_three_hours">3시간마다</string>
<string name="sync_interval_six_hours">6시간마다</string>
<string name="sync_interval_twelve_hours">12시간마다</string>
<string name="sync_interval_one_day">매일</string>
<string name="sync_interval_three_days">3일마다</string>
<string name="sync_interval_one_week">일주일마다</string>
</resources>

@ -124,16 +124,12 @@
<string name="sync_SPr_forget">Logg ut</string>
<string name="sync_SPr_forget_description">Sletter all synkroniseringsdata</string>
<string name="sync_forget_confirm">Logg ut / slett synkroniseringsdata?</string>
<string-array name="sync_SPr_interval_entries">
<item>deaktiver</item>
<item>hvert kvarter</item>
<item>hver halvtime</item>
<item>hver time</item>
<item>hver tredje time</item>
<item>hver sjette time</item>
<item>hver tolvte time</item>
<item>daglig</item>
<item>hver tredje dag</item>
<item>hver uke</item>
</string-array>
<string name="sync_interval_disable">deaktiver</string>
<string name="sync_interval_one_hour">hver time</string>
<string name="sync_interval_three_hours">hver tredje time</string>
<string name="sync_interval_six_hours">hver sjette time</string>
<string name="sync_interval_twelve_hours">hver tolvte time</string>
<string name="sync_interval_one_day">daglig</string>
<string name="sync_interval_three_days">hver tredje dag</string>
<string name="sync_interval_one_week">hver uke</string>
</resources>

@ -382,16 +382,12 @@
<string name="theme_grey">Grijs</string>
<string name="theme_blue_grey">Blauwgrijs</string>
<string name="theme_black">Zwart</string>
<string-array name="sync_SPr_interval_entries">
<item>uitschakelen</item>
<item>elke 15 min.</item>
<item>elke 30 min.</item>
<item>elk uur</item>
<item>elke 3 uur</item>
<item>elke 6 uur</item>
<item>elke 12 uur</item>
<item>elke dag</item>
<item>elke 3 dagen</item>
<item>elke week</item>
</string-array>
<string name="sync_interval_disable">uitschakelen</string>
<string name="sync_interval_one_hour">elk uur</string>
<string name="sync_interval_three_hours">elke 3 uur</string>
<string name="sync_interval_six_hours">elke 6 uur</string>
<string name="sync_interval_twelve_hours">elke 12 uur</string>
<string name="sync_interval_one_day">elke dag</string>
<string name="sync_interval_three_days">elke 3 dagen</string>
<string name="sync_interval_one_week">elke week</string>
</resources>

@ -276,16 +276,12 @@ i odzyskanie zadań z kopi zapasowej (Settings-&gt;Sync and backup-&gt;Backup-&g
<string name="doze_notifications_on">Android będzie pozwalał na ograniczone przerwania kiedy urządzenie jest w trybie drzemki.</string>
<string name="plugin_description">Tasks jest projektem na licencji open source utrzymywanym przez jednego developera. Płatne funkcje wspierają rozwój aplikacji.</string>
<string name="opacity">Przezroczystość</string>
<string-array name="sync_SPr_interval_entries">
<item>Wyłączone</item>
<item>co 15 minut</item>
<item>co 30 minut</item>
<item>co godzinę</item>
<item>co 3 godziny</item>
<item>co 6 godzin</item>
<item>co 12 godzin</item>
<item>raz dziennie</item>
<item>co 3 dni</item>
<item>co tydzień</item>
</string-array>
<string name="sync_interval_disable">Wyłączone</string>
<string name="sync_interval_one_hour">co godzinę</string>
<string name="sync_interval_three_hours">co 3 godziny</string>
<string name="sync_interval_six_hours">co 6 godzin</string>
<string name="sync_interval_twelve_hours">co 12 godzin</string>
<string name="sync_interval_one_day">raz dziennie</string>
<string name="sync_interval_three_days">co 3 dni</string>
<string name="sync_interval_one_week">co tydzień</string>
</resources>

@ -336,16 +336,12 @@
<string name="theme_blue_grey">Azul Acinzentado</string>
<string name="theme_light">Claro</string>
<string name="theme_dark">Escuro</string>
<string-array name="sync_SPr_interval_entries">
<item>desabilitar</item>
<item>a cada quinze minutos</item>
<item>a cada trinta minutos</item>
<item>a cada hora</item>
<item>a cada três horas</item>
<item>a cada seis horas</item>
<item>a cada doze horas</item>
<item>diariamente</item>
<item>a cada três dias</item>
<item>semanalmente</item>
</string-array>
<string name="sync_interval_disable">desabilitar</string>
<string name="sync_interval_one_hour">a cada hora</string>
<string name="sync_interval_three_hours">a cada três horas</string>
<string name="sync_interval_six_hours">a cada seis horas</string>
<string name="sync_interval_twelve_hours">a cada doze horas</string>
<string name="sync_interval_one_day">diariamente</string>
<string name="sync_interval_three_days">a cada três dias</string>
<string name="sync_interval_one_week">semanalmente</string>
</resources>

@ -328,16 +328,12 @@ das tarefas através de um backup em Definições-&gt;Sincronização e backup-&
<string name="show_completed">Mostrar terminadas</string>
<string name="reverse">Reverter</string>
<string name="task_count">%s tarefas</string>
<string-array name="sync_SPr_interval_entries">
<item>desativar</item>
<item>cada 15 minutos</item>
<item>cada 30 minutos</item>
<item>cada hora</item>
<item>cada 3 horas</item>
<item>cada 6 horas</item>
<item>cada 12 horas</item>
<item>todos os dias</item>
<item>cada 3 dias</item>
<item>todas as semanas</item>
</string-array>
<string name="sync_interval_disable">desativar</string>
<string name="sync_interval_one_hour">cada hora</string>
<string name="sync_interval_three_hours">cada 3 horas</string>
<string name="sync_interval_six_hours">cada 6 horas</string>
<string name="sync_interval_twelve_hours">cada 12 horas</string>
<string name="sync_interval_one_day">todos os dias</string>
<string name="sync_interval_three_days">cada 3 dias</string>
<string name="sync_interval_one_week">todas as semanas</string>
</resources>

@ -413,16 +413,12 @@
<string name="theme_light">Светлая</string>
<string name="theme_dark">Темная</string>
<string name="default_value">По умолчанию</string>
<string-array name="sync_SPr_interval_entries">
<item>отключить</item>
<item>каждые 15 минут</item>
<item>каждые 30 минут</item>
<item>каждый час</item>
<item>каждые 3 часа</item>
<item>каждые 6 часов</item>
<item>каждые 12 часов</item>
<item>каждый день</item>
<item>каждые 3 дня</item>
<item>каждую неделю</item>
</string-array>
<string name="sync_interval_disable">отключить</string>
<string name="sync_interval_one_hour">каждый час</string>
<string name="sync_interval_three_hours">каждые 3 часа</string>
<string name="sync_interval_six_hours">каждые 6 часов</string>
<string name="sync_interval_twelve_hours">каждые 12 часов</string>
<string name="sync_interval_one_day">каждый день</string>
<string name="sync_interval_three_days">каждые 3 дня</string>
<string name="sync_interval_one_week">каждую неделю</string>
</resources>

@ -308,16 +308,12 @@
<string name="show_hidden">Zobraziť skryté</string>
<string name="show_completed">Zobraziť dokončené</string>
<string name="task_count">%s úlohy</string>
<string-array name="sync_SPr_interval_entries">
<item>zakázať</item>
<item>každých pätnásť minút</item>
<item>každú tretiu minútu</item>
<item>každú hodinu</item>
<item>každé tri hodiny</item>
<item>každých šesť hodín</item>
<item>každých dvanásť hodín</item>
<item>každý deň</item>
<item>každý tretí deň</item>
<item>každý týždeň</item>
</string-array>
<string name="sync_interval_disable">zakázať</string>
<string name="sync_interval_one_hour">každú hodinu</string>
<string name="sync_interval_three_hours">každé tri hodiny</string>
<string name="sync_interval_six_hours">každých šesť hodín</string>
<string name="sync_interval_twelve_hours">každých dvanásť hodín</string>
<string name="sync_interval_one_day">každý deň</string>
<string name="sync_interval_three_days">každý tretí deň</string>
<string name="sync_interval_one_week">každý týždeň</string>
</resources>

@ -247,16 +247,12 @@
<string name="sync_SPr_forget_description">Zbriše vse usklajene podatke</string>
<string name="sync_forget_confirm">Odjava/brisanje usklajenih podatkov?</string>
<string name="TLA_menu_donate">Donirajte</string>
<string-array name="sync_SPr_interval_entries">
<item>onemogoči</item>
<item>vsakih 15 minut</item>
<item>vsakih 30 minut</item>
<item>vsako uro</item>
<item>vsake 3 ure</item>
<item>vsakih 6 ur</item>
<item>vsakih 12 ur</item>
<item>vsak dan</item>
<item>vsake 3 dni</item>
<item>vsak teden</item>
</string-array>
<string name="sync_interval_disable">onemogoči</string>
<string name="sync_interval_one_hour">vsako uro</string>
<string name="sync_interval_three_hours">vsake 3 ure</string>
<string name="sync_interval_six_hours">vsakih 6 ur</string>
<string name="sync_interval_twelve_hours">vsakih 12 ur</string>
<string name="sync_interval_one_day">vsak dan</string>
<string name="sync_interval_three_days">vsake 3 dni</string>
<string name="sync_interval_one_week">vsak teden</string>
</resources>

@ -402,16 +402,12 @@ och återställer dina aktuella uppgifter från en backup
<string name="theme_grey">Grå</string>
<string name="theme_blue_grey">Blågrå</string>
<string name="theme_black">Svart</string>
<string-array name="sync_SPr_interval_entries">
<item>inaktivera</item>
<item>varje kvartstimme</item>
<item>varje halvtimme</item>
<item>varje timme</item>
<item>var tredje timme</item>
<item>var sjätte timme</item>
<item>var tolfte timme</item>
<item>varje dag</item>
<item>var tredje dag</item>
<item>varje vecka</item>
</string-array>
<string name="sync_interval_disable">inaktivera</string>
<string name="sync_interval_one_hour">varje timme</string>
<string name="sync_interval_three_hours">var tredje timme</string>
<string name="sync_interval_six_hours">var sjätte timme</string>
<string name="sync_interval_twelve_hours">var tolfte timme</string>
<string name="sync_interval_one_day">varje dag</string>
<string name="sync_interval_three_days">var tredje dag</string>
<string name="sync_interval_one_week">varje vecka</string>
</resources>

@ -215,16 +215,12 @@
<string name="sync_SPr_forget">Çıkış Yap</string>
<string name="sync_SPr_forget_description">Bütün eşleme verilerini temizle</string>
<string name="sync_forget_confirm">Çıkış Yap / Senkron verisini sil?</string>
<string-array name="sync_SPr_interval_entries">
<item>devre dışı bırak</item>
<item>her 15 dakika</item>
<item>her 30 dakika</item>
<item>her saat</item>
<item>her 3 saat</item>
<item>her 6 saat</item>
<item>her 12 saat</item>
<item>hergün</item>
<item>her 3 gün</item>
<item>her hafta</item>
</string-array>
<string name="sync_interval_disable">devre dışı bırak</string>
<string name="sync_interval_one_hour">her saat</string>
<string name="sync_interval_three_hours">her 3 saat</string>
<string name="sync_interval_six_hours">her 6 saat</string>
<string name="sync_interval_twelve_hours">her 12 saat</string>
<string name="sync_interval_one_day">hergün</string>
<string name="sync_interval_three_days">her 3 gün</string>
<string name="sync_interval_one_week">her hafta</string>
</resources>

@ -277,16 +277,12 @@
<string name="doze_notifications">Переривати режим \"Doze\" для повідомленнь</string>
<string name="doze_notifications_off">Андроїд зазвичай буде значно відкладати повідомлення коли пристрій в режимі \"Doze\"</string>
<string name="doze_notifications_on">Android дозволить деякі переривання в режимі \"Doze\"</string>
<string-array name="sync_SPr_interval_entries">
<item>вимкнути</item>
<item>що 15 хвилин</item>
<item>що 30 хвилин</item>
<item>кожну годину</item>
<item>кожних 3 години</item>
<item>кожних 6 годин</item>
<item>кожних 12 годин</item>
<item>щодня</item>
<item>кожного 3-го дня</item>
<item>кожного тижня</item>
</string-array>
<string name="sync_interval_disable">вимкнути</string>
<string name="sync_interval_one_hour">кожну годину</string>
<string name="sync_interval_three_hours">кожних 3 години</string>
<string name="sync_interval_six_hours">кожних 6 годин</string>
<string name="sync_interval_twelve_hours">кожних 12 годин</string>
<string name="sync_interval_one_day">щодня</string>
<string name="sync_interval_three_days">кожного 3-го дня</string>
<string name="sync_interval_one_week">кожного тижня</string>
</resources>

@ -207,16 +207,12 @@
<string name="sync_SPr_forget">登出</string>
<string name="sync_SPr_forget_description">清除所有同步资料</string>
<string name="sync_forget_confirm">登出/清除同步资料?</string>
<string-array name="sync_SPr_interval_entries">
<item>停用</item>
<item>每15分钟</item>
<item>每30分钟</item>
<item>每小时</item>
<item>每3小时</item>
<item>每6小时</item>
<item>每12小时</item>
<item>每天</item>
<item>每3天</item>
<item>每周</item>
</string-array>
<string name="sync_interval_disable">停用</string>
<string name="sync_interval_one_hour">每小时</string>
<string name="sync_interval_three_hours">每3小时</string>
<string name="sync_interval_six_hours">每6小时</string>
<string name="sync_interval_twelve_hours">每12小时</string>
<string name="sync_interval_one_day">每天</string>
<string name="sync_interval_three_days">每3天</string>
<string name="sync_interval_one_week">每周</string>
</resources>

@ -233,16 +233,12 @@
<string name="high_priority">最優先</string>
<string name="default_priority">預設</string>
<string name="low_priority">最不急迫</string>
<string-array name="sync_SPr_interval_entries">
<item>停用</item>
<item>每15分鐘</item>
<item>每30分鐘</item>
<item>每小時</item>
<item>每3小時</item>
<item>每6小時</item>
<item>每12小時</item>
<item>每天</item>
<item>每3天</item>
<item>每週</item>
</string-array>
<string name="sync_interval_disable">停用</string>
<string name="sync_interval_one_hour">每小時</string>
<string name="sync_interval_three_hours">每3小時</string>
<string name="sync_interval_six_hours">每6小時</string>
<string name="sync_interval_twelve_hours">每12小時</string>
<string name="sync_interval_one_day">每天</string>
<string name="sync_interval_three_days">每3天</string>
<string name="sync_interval_one_week">每週</string>
</resources>

@ -40,6 +40,18 @@
<item>600</item>
</string-array>
<string-array name="sync_interval_entries">
<!-- sync_SPr_interval_entries: Synchronization Intervals -->
<item>@string/sync_interval_disable</item>
<item>@string/sync_interval_one_hour</item>
<item>@string/sync_interval_three_hours</item>
<item>@string/sync_interval_six_hours</item>
<item>@string/sync_interval_twelve_hours</item>
<item>@string/sync_interval_one_day</item>
<item>@string/sync_interval_three_days</item>
<item>@string/sync_interval_one_week</item>
</string-array>
<string-array name="TEA_hideUntil">
<item>@string/due_date</item>
<item>@string/due_time</item>

@ -1,7 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<bool name="two_pane_layout">false</bool>
<bool name="sync_supported">false</bool>
<bool name="location_enabled">false</bool>
<bool name="google_play_store_available">false</bool>
</resources>

@ -178,8 +178,6 @@
<string-array name="sync_SPr_interval_values">
<!-- sync_SPr_interval_values: interval in seconds for sync entries (do not edit) -->
<item>0</item>
<item>900</item>
<item>1800</item>
<item>3600</item>
<item>10800</item>
<item>21600</item>
@ -306,5 +304,6 @@
<string name="p_theme_color">theme_color</string>
<string name="p_theme_accent">theme_accent</string>
<string name="p_gtasks_default_list">default_gtasks_list</string>
<string name="p_sync_warning_shown">sync_warning_shown</string>
</resources>

@ -894,18 +894,14 @@ File %1$s contained %2$s.\n\n
<string name="theme_dark">Dark</string>
<string name="default_value">Default</string>
<string-array name="sync_SPr_interval_entries">
<!-- sync_SPr_interval_entries: Synchronization Intervals -->
<item>disable</item>
<item>every fifteen minutes</item>
<item>every thirty minutes</item>
<item>every hour</item>
<item>every three hours</item>
<item>every six hours</item>
<item>every twelve hours</item>
<item>every day</item>
<item>every three days</item>
<item>every week</item>
</string-array>
<string name="sync_interval_disable">disable</string>
<string name="sync_interval_one_hour">every hour</string>
<string name="sync_interval_three_hours">every three hours</string>
<string name="sync_interval_six_hours">every six hours</string>
<string name="sync_interval_twelve_hours">every twelve hours</string>
<string name="sync_interval_one_day">every day</string>
<string name="sync_interval_three_days">every three days</string>
<string name="sync_interval_one_week">every week</string>
<string name="master_sync_warning">Automatic synchronization is currently disabled by Android</string>
</resources>

@ -19,9 +19,9 @@
android:title="@string/default_list" />
<com.todoroo.astrid.ui.MultilineListPreference
android:defaultValue="0"
android:defaultValue="3600"
android:dependency="@string/sync_gtasks"
android:entries="@array/sync_SPr_interval_entries"
android:entries="@array/sync_interval_entries"
android:entryValues="@array/sync_SPr_interval_values"
android:key="@string/gtasks_GPr_interval_key"
android:title="@string/sync_SPr_interval_title" />

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<sync-adapter xmlns:android="http://schemas.android.com/apk/res/android"
android:accountType="com.google"
android:contentAuthority="org.tasks"
android:supportsUploading="true"
android:userVisible="false" />
Loading…
Cancel
Save