diff --git a/src/amazon/java/com/todoroo/astrid/gtasks/sync/GtasksSyncV2Provider.java b/src/amazon/java/com/todoroo/astrid/gtasks/sync/GtasksSyncV2Provider.java deleted file mode 100644 index 906563cec..000000000 --- a/src/amazon/java/com/todoroo/astrid/gtasks/sync/GtasksSyncV2Provider.java +++ /dev/null @@ -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) { - - } -} diff --git a/src/amazon/java/org/tasks/AccountManager.java b/src/amazon/java/org/tasks/AccountManager.java index 46249d50b..7bd55acfd 100644 --- a/src/amazon/java/org/tasks/AccountManager.java +++ b/src/amazon/java/org/tasks/AccountManager.java @@ -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; } } diff --git a/src/amazon/java/org/tasks/injection/ApplicationComponent.java b/src/amazon/java/org/tasks/injection/ApplicationComponent.java new file mode 100644 index 000000000..8abff6051 --- /dev/null +++ b/src/amazon/java/org/tasks/injection/ApplicationComponent.java @@ -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 { +} diff --git a/src/amazon/java/org/tasks/scheduling/GtasksBackgroundService.java b/src/amazon/java/org/tasks/scheduling/GtasksBackgroundService.java deleted file mode 100644 index e6ef38aa2..000000000 --- a/src/amazon/java/org/tasks/scheduling/GtasksBackgroundService.java +++ /dev/null @@ -1,10 +0,0 @@ -package org.tasks.scheduling; - -import javax.inject.Inject; - -public class GtasksBackgroundService { - @Inject - public GtasksBackgroundService() { - - } -} diff --git a/src/androidTestGoogleplay/java/com/todoroo/astrid/gtasks/GtasksMetadataServiceTest.java b/src/androidTestGoogleplay/java/com/todoroo/astrid/gtasks/GtasksMetadataServiceTest.java index c837e1859..c39727803 100644 --- a/src/androidTestGoogleplay/java/com/todoroo/astrid/gtasks/GtasksMetadataServiceTest.java +++ b/src/androidTestGoogleplay/java/com/todoroo/astrid/gtasks/GtasksMetadataServiceTest.java @@ -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 diff --git a/src/androidTestGoogleplay/java/com/todoroo/astrid/gtasks/GtasksTestPreferenceService.java b/src/androidTestGoogleplay/java/com/todoroo/astrid/gtasks/GtasksTestPreferenceService.java index f62f6f33e..8de7f2b21 100644 --- a/src/androidTestGoogleplay/java/com/todoroo/astrid/gtasks/GtasksTestPreferenceService.java +++ b/src/androidTestGoogleplay/java/com/todoroo/astrid/gtasks/GtasksTestPreferenceService.java @@ -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 diff --git a/src/androidTestGoogleplay/java/com/todoroo/astrid/gtasks/sync/GtasksSyncV2ProviderTest.java b/src/androidTestGoogleplay/java/org/tasks/gtasks/GoogleTaskSyncAdapterTest.java similarity index 94% rename from src/androidTestGoogleplay/java/com/todoroo/astrid/gtasks/sync/GtasksSyncV2ProviderTest.java rename to src/androidTestGoogleplay/java/org/tasks/gtasks/GoogleTaskSyncAdapterTest.java index effad641d..6533b4782 100644 --- a/src/androidTestGoogleplay/java/com/todoroo/astrid/gtasks/sync/GtasksSyncV2ProviderTest.java +++ b/src/androidTestGoogleplay/java/org/tasks/gtasks/GoogleTaskSyncAdapterTest.java @@ -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))); diff --git a/src/generic/java/com/todoroo/astrid/gtasks/sync/GtasksSyncV2Provider.java b/src/generic/java/com/todoroo/astrid/gtasks/sync/GtasksSyncV2Provider.java deleted file mode 100644 index 906563cec..000000000 --- a/src/generic/java/com/todoroo/astrid/gtasks/sync/GtasksSyncV2Provider.java +++ /dev/null @@ -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) { - - } -} diff --git a/src/generic/java/org/tasks/AccountManager.java b/src/generic/java/org/tasks/AccountManager.java index 46249d50b..7bd55acfd 100644 --- a/src/generic/java/org/tasks/AccountManager.java +++ b/src/generic/java/org/tasks/AccountManager.java @@ -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; } } diff --git a/src/generic/java/org/tasks/injection/ApplicationComponent.java b/src/generic/java/org/tasks/injection/ApplicationComponent.java new file mode 100644 index 000000000..8abff6051 --- /dev/null +++ b/src/generic/java/org/tasks/injection/ApplicationComponent.java @@ -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 { +} diff --git a/src/generic/java/org/tasks/scheduling/GtasksBackgroundService.java b/src/generic/java/org/tasks/scheduling/GtasksBackgroundService.java deleted file mode 100644 index e6ef38aa2..000000000 --- a/src/generic/java/org/tasks/scheduling/GtasksBackgroundService.java +++ /dev/null @@ -1,10 +0,0 @@ -package org.tasks.scheduling; - -import javax.inject.Inject; - -public class GtasksBackgroundService { - @Inject - public GtasksBackgroundService() { - - } -} diff --git a/src/googleplay/AndroidManifest.xml b/src/googleplay/AndroidManifest.xml index 19673bc48..a2ba48e22 100644 --- a/src/googleplay/AndroidManifest.xml +++ b/src/googleplay/AndroidManifest.xml @@ -26,6 +26,9 @@ + + + @@ -147,6 +150,19 @@ android:label="@string/app_name" android:exported="true" /> + + + + + + + + + diff --git a/src/googleplay/java/com/todoroo/astrid/gtasks/GtasksFilterExposer.java b/src/googleplay/java/com/todoroo/astrid/gtasks/GtasksFilterExposer.java index 7625c6f17..e6b73a28f 100644 --- a/src/googleplay/java/com/todoroo/astrid/gtasks/GtasksFilterExposer.java +++ b/src/googleplay/java/com/todoroo/astrid/gtasks/GtasksFilterExposer.java @@ -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 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); diff --git a/src/googleplay/java/com/todoroo/astrid/gtasks/GtasksListFragment.java b/src/googleplay/java/com/todoroo/astrid/gtasks/GtasksListFragment.java index ab294b150..9a3996efa 100644 --- a/src/googleplay/java/com/todoroo/astrid/gtasks/GtasksListFragment.java +++ b/src/googleplay/java/com/todoroo/astrid/gtasks/GtasksListFragment.java @@ -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(); } }); } diff --git a/src/googleplay/java/com/todoroo/astrid/gtasks/GtasksPreferences.java b/src/googleplay/java/com/todoroo/astrid/gtasks/GtasksPreferences.java index bf8acc6b6..3a7a653e1 100644 --- a/src/googleplay/java/com/todoroo/astrid/gtasks/GtasksPreferences.java +++ b/src/googleplay/java/com/todoroo/astrid/gtasks/GtasksPreferences.java @@ -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); diff --git a/src/googleplay/java/com/todoroo/astrid/gtasks/sync/GtasksSyncService.java b/src/googleplay/java/com/todoroo/astrid/gtasks/sync/GtasksSyncService.java index 89d284b01..816f3f116 100644 --- a/src/googleplay/java/com/todoroo/astrid/gtasks/sync/GtasksSyncService.java +++ b/src/googleplay/java/com/todoroo/astrid/gtasks/sync/GtasksSyncService.java @@ -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 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(); - } } diff --git a/src/googleplay/java/com/todoroo/astrid/gtasks/sync/GtasksSyncV2Provider.java b/src/googleplay/java/com/todoroo/astrid/gtasks/sync/GtasksSyncV2Provider.java deleted file mode 100644 index f022e85f3..000000000 --- a/src/googleplay/java/com/todoroo/astrid/gtasks/sync/GtasksSyncV2Provider.java +++ /dev/null @@ -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 gtaskLists = new ArrayList<>(); - String nextPageToken = null; - do { - remoteLists = gtasksInvoker.allGtaskLists(nextPageToken); - if (remoteLists == null) { - break; - } - List 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 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 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 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 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 tasks = new ArrayList<>(); - String nextPageToken = null; - do { - Tasks taskList = invoker.getAllGtasksFromListId(listId, includeDeletedAndHidden, - includeDeletedAndHidden, lastSyncDate + 1000L, nextPageToken); - if (taskList == null) { - break; - } - List 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()); - } -} diff --git a/src/googleplay/java/com/todoroo/astrid/service/SyncV2Service.java b/src/googleplay/java/com/todoroo/astrid/service/SyncV2Service.java new file mode 100644 index 000000000..18728a597 --- /dev/null +++ b/src/googleplay/java/com/todoroo/astrid/service/SyncV2Service.java @@ -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 + * + */ +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(); + } + } + }); + } + } +} diff --git a/src/googleplay/java/org/tasks/FlavorSetup.java b/src/googleplay/java/org/tasks/FlavorSetup.java index aa785df37..789150508 100644 --- a/src/googleplay/java/org/tasks/FlavorSetup.java +++ b/src/googleplay/java/org/tasks/FlavorSetup.java @@ -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()); } } diff --git a/src/googleplay/java/org/tasks/activities/ClearGtaskDataActivity.java b/src/googleplay/java/org/tasks/activities/ClearGtaskDataActivity.java index 94ba6ef5a..8ef802e96 100644 --- a/src/googleplay/java/org/tasks/activities/ClearGtaskDataActivity.java +++ b/src/googleplay/java/org/tasks/activities/ClearGtaskDataActivity.java @@ -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); } }) diff --git a/src/googleplay/java/org/tasks/gtasks/GoogleTaskSyncAdapter.java b/src/googleplay/java/org/tasks/gtasks/GoogleTaskSyncAdapter.java new file mode 100644 index 000000000..bd0e25fe4 --- /dev/null +++ b/src/googleplay/java/org/tasks/gtasks/GoogleTaskSyncAdapter.java @@ -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. + * + *

This class is instantiated in {@link GoogleTaskSyncService}, which also binds SyncAdapter to the system. + * SyncAdapter should only be initialized in SyncService, never anywhere else. + * + *

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 in situ, and you don't have to set up a separate thread for them. + . + * + *

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. + * + *

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 gtaskLists = new ArrayList<>(); + String nextPageToken = null; + do { + remoteLists = gtasksInvoker.allGtaskLists(nextPageToken); + if (remoteLists == null) { + break; + } + List 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 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 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 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 tasks = new ArrayList<>(); + String nextPageToken = null; + do { + Tasks taskList = invoker.getAllGtasksFromListId(listId, includeDeletedAndHidden, + includeDeletedAndHidden, lastSyncDate + 1000L, nextPageToken); + if (taskList == null) { + break; + } + List 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); + } +} diff --git a/src/googleplay/java/org/tasks/gtasks/GoogleTaskSyncService.java b/src/googleplay/java/org/tasks/gtasks/GoogleTaskSyncService.java new file mode 100644 index 000000000..18b41b868 --- /dev/null +++ b/src/googleplay/java/org/tasks/gtasks/GoogleTaskSyncService.java @@ -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. + * + *

This service is invoked in response to Intents with action android.content.SyncAdapter, and + * returns a Binder connection to SyncAdapter. + * + *

For performance, only one sync adapter will be initialized within this application's context. + * + *

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}. + * + *

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(); + } +} diff --git a/src/googleplay/java/org/tasks/injection/ApplicationComponent.java b/src/googleplay/java/org/tasks/injection/ApplicationComponent.java new file mode 100644 index 000000000..67139d230 --- /dev/null +++ b/src/googleplay/java/org/tasks/injection/ApplicationComponent.java @@ -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); +} diff --git a/src/googleplay/java/org/tasks/injection/InjectingAbstractThreadedSyncAdapter.java b/src/googleplay/java/org/tasks/injection/InjectingAbstractThreadedSyncAdapter.java new file mode 100644 index 000000000..ee6498f4d --- /dev/null +++ b/src/googleplay/java/org/tasks/injection/InjectingAbstractThreadedSyncAdapter.java @@ -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); +} diff --git a/src/googleplay/java/org/tasks/injection/SyncAdapterComponent.java b/src/googleplay/java/org/tasks/injection/SyncAdapterComponent.java new file mode 100644 index 000000000..ad2775991 --- /dev/null +++ b/src/googleplay/java/org/tasks/injection/SyncAdapterComponent.java @@ -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); +} diff --git a/src/googleplay/java/org/tasks/injection/SyncAdapterModule.java b/src/googleplay/java/org/tasks/injection/SyncAdapterModule.java new file mode 100644 index 000000000..8ff2ffea0 --- /dev/null +++ b/src/googleplay/java/org/tasks/injection/SyncAdapterModule.java @@ -0,0 +1,7 @@ +package org.tasks.injection; + +import dagger.Module; + +@Module +public class SyncAdapterModule { +} diff --git a/src/googleplay/java/org/tasks/receivers/GoogleTaskPushReceiver.java b/src/googleplay/java/org/tasks/receivers/GoogleTaskPushReceiver.java index 67fc0e054..088e60de4 100644 --- a/src/googleplay/java/org/tasks/receivers/GoogleTaskPushReceiver.java +++ b/src/googleplay/java/org/tasks/receivers/GoogleTaskPushReceiver.java @@ -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; } diff --git a/src/googleplay/java/org/tasks/scheduling/GtasksBackgroundService.java b/src/googleplay/java/org/tasks/scheduling/GtasksBackgroundService.java deleted file mode 100644 index 214c5967a..000000000 --- a/src/googleplay/java/org/tasks/scheduling/GtasksBackgroundService.java +++ /dev/null @@ -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); - } -} diff --git a/src/googleplay/res/values/bools.xml b/src/googleplay/res/values/bools.xml index 0c80a8b43..515c5e1fd 100644 --- a/src/googleplay/res/values/bools.xml +++ b/src/googleplay/res/values/bools.xml @@ -1,6 +1,5 @@ - true true true \ No newline at end of file diff --git a/src/main/java/com/todoroo/astrid/actfm/TagViewFragment.java b/src/main/java/com/todoroo/astrid/actfm/TagViewFragment.java index e1d11e263..35b88fe68 100644 --- a/src/main/java/com/todoroo/astrid/actfm/TagViewFragment.java +++ b/src/main/java/com/todoroo/astrid/actfm/TagViewFragment.java @@ -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; diff --git a/src/main/java/com/todoroo/astrid/activity/TaskListActivity.java b/src/main/java/com/todoroo/astrid/activity/TaskListActivity.java index 52e6c91b5..49368f3d8 100644 --- a/src/main/java/com/todoroo/astrid/activity/TaskListActivity.java +++ b/src/main/java/com/todoroo/astrid/activity/TaskListActivity.java @@ -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 diff --git a/src/main/java/com/todoroo/astrid/activity/TaskListFragment.java b/src/main/java/com/todoroo/astrid/activity/TaskListFragment.java index 1b6581979..b696c5798 100644 --- a/src/main/java/com/todoroo/astrid/activity/TaskListFragment.java +++ b/src/main/java/com/todoroo/astrid/activity/TaskListFragment.java @@ -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 diff --git a/src/main/java/com/todoroo/astrid/gtasks/GtasksPreferenceService.java b/src/main/java/com/todoroo/astrid/gtasks/GtasksPreferenceService.java index a1dbc7e50..a68724441 100644 --- a/src/main/java/com/todoroo/astrid/gtasks/GtasksPreferenceService.java +++ b/src/main/java/com/todoroo/astrid/gtasks/GtasksPreferenceService.java @@ -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); + } } diff --git a/src/main/java/com/todoroo/astrid/helper/SyncActionHelper.java b/src/main/java/com/todoroo/astrid/helper/SyncActionHelper.java deleted file mode 100644 index 15a1ba049..000000000 --- a/src/main/java/com/todoroo/astrid/helper/SyncActionHelper.java +++ /dev/null @@ -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. - *

- * In order to make this work you need to call register() and unregister() in - * onResume and onPause, respectively. - * - * @author Tim Su - */ -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; - } - } -} - - diff --git a/src/main/java/com/todoroo/astrid/service/SyncV2Service.java b/src/main/java/com/todoroo/astrid/service/SyncV2Service.java deleted file mode 100644 index 206834be8..000000000 --- a/src/main/java/com/todoroo/astrid/service/SyncV2Service.java +++ /dev/null @@ -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 - * - */ -@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); - } - } -} diff --git a/src/main/java/org/tasks/Tasks.java b/src/main/java/org/tasks/Tasks.java index 3c7dacfa3..2009ee692 100644 --- a/src/main/java/org/tasks/Tasks.java +++ b/src/main/java/org/tasks/Tasks.java @@ -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; diff --git a/src/main/java/org/tasks/filters/FilterCriteriaProvider.java b/src/main/java/org/tasks/filters/FilterCriteriaProvider.java index 3870af445..ef54264bc 100644 --- a/src/main/java/org/tasks/filters/FilterCriteriaProvider.java +++ b/src/main/java/org/tasks/filters/FilterCriteriaProvider.java @@ -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()); } diff --git a/src/main/java/org/tasks/fragments/TaskEditControlSetFragmentManager.java b/src/main/java/org/tasks/fragments/TaskEditControlSetFragmentManager.java index 4dd075222..87a0708c6 100644 --- a/src/main/java/org/tasks/fragments/TaskEditControlSetFragmentManager.java +++ b/src/main/java/org/tasks/fragments/TaskEditControlSetFragmentManager.java @@ -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 controlSetFragments = new LinkedHashMap<>(); private final List 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: diff --git a/src/main/java/org/tasks/gtasks/SyncAdapterHelper.java b/src/main/java/org/tasks/gtasks/SyncAdapterHelper.java new file mode 100644 index 000000000..a65135876 --- /dev/null +++ b/src/main/java/org/tasks/gtasks/SyncAdapterHelper.java @@ -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"). + * + *

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(); + } +} diff --git a/src/main/java/org/tasks/injection/ApplicationComponent.java b/src/main/java/org/tasks/injection/BaseApplicationComponent.java similarity index 79% rename from src/main/java/org/tasks/injection/ApplicationComponent.java rename to src/main/java/org/tasks/injection/BaseApplicationComponent.java index d0c690963..718581f6c 100644 --- a/src/main/java/org/tasks/injection/ApplicationComponent.java +++ b/src/main/java/org/tasks/injection/BaseApplicationComponent.java @@ -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); diff --git a/src/main/java/org/tasks/injection/IntentServiceComponent.java b/src/main/java/org/tasks/injection/IntentServiceComponent.java index dd6fcaa90..3400b6a1f 100644 --- a/src/main/java/org/tasks/injection/IntentServiceComponent.java +++ b/src/main/java/org/tasks/injection/IntentServiceComponent.java @@ -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); } diff --git a/src/main/java/org/tasks/receivers/RepeatConfirmationReceiver.java b/src/main/java/org/tasks/receivers/RepeatConfirmationReceiver.java index 4f8d0cd06..c463e9c83 100644 --- a/src/main/java/org/tasks/receivers/RepeatConfirmationReceiver.java +++ b/src/main/java/org/tasks/receivers/RepeatConfirmationReceiver.java @@ -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) { diff --git a/src/main/java/org/tasks/scheduling/BackgroundScheduler.java b/src/main/java/org/tasks/scheduling/BackgroundScheduler.java index d4ce1dbb8..987a0c383 100644 --- a/src/main/java/org/tasks/scheduling/BackgroundScheduler.java +++ b/src/main/java/org/tasks/scheduling/BackgroundScheduler.java @@ -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)); } diff --git a/src/main/java/org/tasks/sync/IndeterminateProgressBarSyncResultCallback.java b/src/main/java/org/tasks/sync/IndeterminateProgressBarSyncResultCallback.java deleted file mode 100644 index 0d515b954..000000000 --- a/src/main/java/org/tasks/sync/IndeterminateProgressBarSyncResultCallback.java +++ /dev/null @@ -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()); - } -} diff --git a/src/main/java/org/tasks/sync/RecordSyncStatusCallback.java b/src/main/java/org/tasks/sync/RecordSyncStatusCallback.java index 2dd28e7a0..266df4e15 100644 --- a/src/main/java/org/tasks/sync/RecordSyncStatusCallback.java +++ b/src/main/java/org/tasks/sync/RecordSyncStatusCallback.java @@ -18,6 +18,7 @@ public class RecordSyncStatusCallback implements SyncResultCallback { @Override public void started() { gtasksPreferenceService.recordSyncStart(); + broadcaster.refresh(); } @Override diff --git a/src/main/java/org/tasks/sync/SyncExecutor.java b/src/main/java/org/tasks/sync/SyncExecutor.java index 2dfe8e244..7f5776b86 100644 --- a/src/main/java/org/tasks/sync/SyncExecutor.java +++ b/src/main/java/org/tasks/sync/SyncExecutor.java @@ -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( diff --git a/src/main/java/org/tasks/sync/SyncThrottle.java b/src/main/java/org/tasks/sync/SyncThrottle.java deleted file mode 100644 index 949ac1a40..000000000 --- a/src/main/java/org/tasks/sync/SyncThrottle.java +++ /dev/null @@ -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 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)); - } -} diff --git a/src/main/res/values-ar/strings.xml b/src/main/res/values-ar/strings.xml index 3a9517edf..d56a8f680 100644 --- a/src/main/res/values-ar/strings.xml +++ b/src/main/res/values-ar/strings.xml @@ -185,16 +185,12 @@ عشوائياً اختر تاريخ و وقت أختر الموقع - - تعطيل - كل 15 دقيقة - كل ثلاثين دقيقة - كل ساعة - كل ثلاث ساعات - كل ست ساعات - كل 12 ساعة - كل يوم - كل ثلاث أيام - كل أسبوع - + تعطيل + كل ساعة + كل ثلاث ساعات + كل ست ساعات + كل 12 ساعة + كل يوم + كل ثلاث أيام + كل أسبوع \ No newline at end of file diff --git a/src/main/res/values-bg-rBG/strings.xml b/src/main/res/values-bg-rBG/strings.xml index 7fcd8b3e8..c6e501da0 100644 --- a/src/main/res/values-bg-rBG/strings.xml +++ b/src/main/res/values-bg-rBG/strings.xml @@ -414,16 +414,12 @@ Светла Тъмна По подразбиране - - изключено - на всеки 15 минути - на всеки 30 минути - на всеки час - на всеки три часа - на всеки шест часа - на всеки дванадесет часа - всеки ден - на всеки три дена - всяка седмица - + изключено + на всеки час + на всеки три часа + на всеки шест часа + на всеки дванадесет часа + всеки ден + на всеки три дена + всяка седмица \ No newline at end of file diff --git a/src/main/res/values-ca/strings.xml b/src/main/res/values-ca/strings.xml index e5cc56196..a742d8ff3 100644 --- a/src/main/res/values-ca/strings.xml +++ b/src/main/res/values-ca/strings.xml @@ -145,16 +145,12 @@ Surt Esborra tota la informació de sincronització Tancar sessió / esborra la informació de sincronització? - - desactivat - cada quince minuts - cada trenta minuts - cada hora - cada tres hores - cada sis hores - cada dotze hores - diàriament - cada tres dies - setmanalment - + desactivat + cada hora + cada tres hores + cada sis hores + cada dotze hores + diàriament + cada tres dies + setmanalment \ No newline at end of file diff --git a/src/main/res/values-cs/strings.xml b/src/main/res/values-cs/strings.xml index b7d6467f6..939ca21ef 100644 --- a/src/main/res/values-cs/strings.xml +++ b/src/main/res/values-cs/strings.xml @@ -249,16 +249,12 @@ Výchozí Nízká Priorita oznámení - - zakázat - každých patnáct minut - každých třicet minut - každou hodinu - každé tři hodiny - každých šest hodin - každých dvanáct hodin - každý den - každé tři dny - každý týden - + zakázat + každou hodinu + každé tři hodiny + každých šest hodin + každých dvanáct hodin + každý den + každé tři dny + každý týden \ No newline at end of file diff --git a/src/main/res/values-da/strings.xml b/src/main/res/values-da/strings.xml index 1f4ffe9b8..7fc1f81c6 100644 --- a/src/main/res/values-da/strings.xml +++ b/src/main/res/values-da/strings.xml @@ -132,16 +132,11 @@ Baggrunds Synk Log af Sletter al synkroniserings data - - disable - every fifteen minutes - every thirty minutes - hver time - hver 3. time - hver 6. time - hver 12. time - hver dag - hver 3. dag - hver uge - + hver time + hver 3. time + hver 6. time + hver 12. time + hver dag + hver 3. dag + hver uge \ No newline at end of file diff --git a/src/main/res/values-de/strings.xml b/src/main/res/values-de/strings.xml index 054ec0139..d849bdbc2 100644 --- a/src/main/res/values-de/strings.xml +++ b/src/main/res/values-de/strings.xml @@ -398,16 +398,12 @@ Hell Dunkel Standard - - deaktivieren - alle 15 Minuten - alle 30 Minuten - stündlich - alle 3 Stunden - alle 6 Stunden - alle 12 Stunden - täglich - jeden dritten Tag - wöchentlich - + deaktivieren + stündlich + alle 3 Stunden + alle 6 Stunden + alle 12 Stunden + täglich + jeden dritten Tag + wöchentlich \ No newline at end of file diff --git a/src/main/res/values-el/strings.xml b/src/main/res/values-el/strings.xml index 83a40ec05..35f3c332f 100644 --- a/src/main/res/values-el/strings.xml +++ b/src/main/res/values-el/strings.xml @@ -238,16 +238,11 @@ Συγχρονισμός παρασκηνίου Αποσύνδεση Δωρίστε - - disable - every fifteen minutes - κάθε τριάντα λεπτά - κάθε ώρα - κάθε τρεις ώρες - κάθε έξι ώρες - κάθε δώδεκα ώρες - κάθε μέρα - κάθε τρεις ημέρες - κάθε εβδομάδα - + κάθε ώρα + κάθε τρεις ώρες + κάθε έξι ώρες + κάθε δώδεκα ώρες + κάθε μέρα + κάθε τρεις ημέρες + κάθε εβδομάδα \ No newline at end of file diff --git a/src/main/res/values-es/strings.xml b/src/main/res/values-es/strings.xml index d157c8535..097278c47 100644 --- a/src/main/res/values-es/strings.xml +++ b/src/main/res/values-es/strings.xml @@ -408,16 +408,12 @@ Claro Oscuro Predeterminado - - deshabilitar - cada quince minutos - cada treinta minutos - cada hora - cada tres horas - cada seis horas - cada doce horas - cada día - cada tres días - cada semana - + deshabilitar + cada hora + cada tres horas + cada seis horas + cada doce horas + cada día + cada tres días + cada semana \ No newline at end of file diff --git a/src/main/res/values-fa/strings.xml b/src/main/res/values-fa/strings.xml index ca3268596..71f10bd21 100644 --- a/src/main/res/values-fa/strings.xml +++ b/src/main/res/values-fa/strings.xml @@ -272,16 +272,12 @@ خاموش کردن چراغ اعلان (بدون عنوان) لیست پیش فرض - - غیرفعال - هر پانزده دقیقه - هر سی دقیقه - هر ساعت - هر سه ساعت - هر شش ساعت - هر دوازده ساعت - هر روز - هر سه روز - هر هفته - + غیرفعال + هر ساعت + هر سه ساعت + هر شش ساعت + هر دوازده ساعت + هر روز + هر سه روز + هر هفته \ No newline at end of file diff --git a/src/main/res/values-fr/strings.xml b/src/main/res/values-fr/strings.xml index e240c82ce..8ebba752a 100644 --- a/src/main/res/values-fr/strings.xml +++ b/src/main/res/values-fr/strings.xml @@ -369,16 +369,12 @@ Sélectionner le filtre Filtre Transparence - - désactiver - toutes les quinze minutes - toutes les trente minutes - toutes les heures - toutes les trois heures - toutes les six heures - toutes les douze heures - tous les jours - tous les trois jours - toutes les semaines - + désactiver + toutes les heures + toutes les trois heures + toutes les six heures + toutes les douze heures + tous les jours + tous les trois jours + toutes les semaines \ No newline at end of file diff --git a/src/main/res/values-it/strings.xml b/src/main/res/values-it/strings.xml index c803ca830..ab4a6e579 100644 --- a/src/main/res/values-it/strings.xml +++ b/src/main/res/values-it/strings.xml @@ -411,16 +411,12 @@ Se visualizzi questo errore più volte, ti consigliamo di cancellare tutti i dat Chiaro Scuro Predefinito - - disabilita - ogni quindici minuti - ogni trenta minuti - ogni ora - ogni tre ore - ogni sei ore - ogni dodici ore - ogni giorno - ogni tre giorni - Ogni settimana - + disabilita + ogni ora + ogni tre ore + ogni sei ore + ogni dodici ore + ogni giorno + ogni tre giorni + Ogni settimana \ No newline at end of file diff --git a/src/main/res/values-iw/strings.xml b/src/main/res/values-iw/strings.xml index 7340ea9ec..bd243594c 100644 --- a/src/main/res/values-iw/strings.xml +++ b/src/main/res/values-iw/strings.xml @@ -406,16 +406,12 @@ אפור כחול אפור שחור - - מִנְעִי - כל רבע שעה - כל חצי שעה - כל שעה - כל שלוש שעות - כל שש שעות - כל שתים עשרה שעות - כל יום - כל שלושה ימים - כל שבוע - + מִנְעִי + כל שעה + כל שלוש שעות + כל שש שעות + כל שתים עשרה שעות + כל יום + כל שלושה ימים + כל שבוע \ No newline at end of file diff --git a/src/main/res/values-ja/strings.xml b/src/main/res/values-ja/strings.xml index f51f0046b..14207fb9a 100644 --- a/src/main/res/values-ja/strings.xml +++ b/src/main/res/values-ja/strings.xml @@ -414,16 +414,12 @@ ライト ダーク デフォルト - - 無効 - 15分毎 - 30分毎 - 1時間毎 - 3時間毎 - 6時間毎 - 12時間毎 - 毎日 - 3日に一度 - 毎週 - + 無効 + 1時間毎 + 3時間毎 + 6時間毎 + 12時間毎 + 毎日 + 3日に一度 + 毎週 \ No newline at end of file diff --git a/src/main/res/values-ko/strings.xml b/src/main/res/values-ko/strings.xml index 2350d0da9..31ecd28a1 100644 --- a/src/main/res/values-ko/strings.xml +++ b/src/main/res/values-ko/strings.xml @@ -409,16 +409,12 @@ Tasks의 백업에서 당신의 일정을 복구하시기 바랍니다. 회색 회청색 검정 - - 사용안함 - 15분마다 - 30분마다 - 매시간 - 3시간마다 - 6시간마다 - 12시간마다 - 매일 - 3일마다 - 일주일마다 - + 사용안함 + 매시간 + 3시간마다 + 6시간마다 + 12시간마다 + 매일 + 3일마다 + 일주일마다 \ No newline at end of file diff --git a/src/main/res/values-nb/strings.xml b/src/main/res/values-nb/strings.xml index 07d1b52be..a33b44376 100644 --- a/src/main/res/values-nb/strings.xml +++ b/src/main/res/values-nb/strings.xml @@ -124,16 +124,12 @@ Logg ut Sletter all synkroniseringsdata Logg ut / slett synkroniseringsdata? - - deaktiver - hvert kvarter - hver halvtime - hver time - hver tredje time - hver sjette time - hver tolvte time - daglig - hver tredje dag - hver uke - + deaktiver + hver time + hver tredje time + hver sjette time + hver tolvte time + daglig + hver tredje dag + hver uke \ No newline at end of file diff --git a/src/main/res/values-nl/strings.xml b/src/main/res/values-nl/strings.xml index 9edff064e..e515a34f9 100644 --- a/src/main/res/values-nl/strings.xml +++ b/src/main/res/values-nl/strings.xml @@ -382,16 +382,12 @@ Grijs Blauwgrijs Zwart - - uitschakelen - elke 15 min. - elke 30 min. - elk uur - elke 3 uur - elke 6 uur - elke 12 uur - elke dag - elke 3 dagen - elke week - + uitschakelen + elk uur + elke 3 uur + elke 6 uur + elke 12 uur + elke dag + elke 3 dagen + elke week \ No newline at end of file diff --git a/src/main/res/values-pl/strings.xml b/src/main/res/values-pl/strings.xml index 2ea285285..a16fcd639 100644 --- a/src/main/res/values-pl/strings.xml +++ b/src/main/res/values-pl/strings.xml @@ -276,16 +276,12 @@ i odzyskanie zadań z kopi zapasowej (Settings->Sync and backup->Backup-&g Android będzie pozwalał na ograniczone przerwania kiedy urządzenie jest w trybie drzemki. Tasks jest projektem na licencji open source utrzymywanym przez jednego developera. Płatne funkcje wspierają rozwój aplikacji. Przezroczystość - - Wyłączone - co 15 minut - co 30 minut - co godzinę - co 3 godziny - co 6 godzin - co 12 godzin - raz dziennie - co 3 dni - co tydzień - + Wyłączone + co godzinę + co 3 godziny + co 6 godzin + co 12 godzin + raz dziennie + co 3 dni + co tydzień \ No newline at end of file diff --git a/src/main/res/values-pt-rBR/strings.xml b/src/main/res/values-pt-rBR/strings.xml index a8165d062..f73339de0 100644 --- a/src/main/res/values-pt-rBR/strings.xml +++ b/src/main/res/values-pt-rBR/strings.xml @@ -336,16 +336,12 @@ Azul Acinzentado Claro Escuro - - desabilitar - a cada quinze minutos - a cada trinta minutos - a cada hora - a cada três horas - a cada seis horas - a cada doze horas - diariamente - a cada três dias - semanalmente - + desabilitar + a cada hora + a cada três horas + a cada seis horas + a cada doze horas + diariamente + a cada três dias + semanalmente \ No newline at end of file diff --git a/src/main/res/values-pt/strings.xml b/src/main/res/values-pt/strings.xml index b8127a115..b5ada0f75 100644 --- a/src/main/res/values-pt/strings.xml +++ b/src/main/res/values-pt/strings.xml @@ -328,16 +328,12 @@ das tarefas através de um backup em Definições->Sincronização e backup-& Mostrar terminadas Reverter %s tarefas - - desativar - cada 15 minutos - cada 30 minutos - cada hora - cada 3 horas - cada 6 horas - cada 12 horas - todos os dias - cada 3 dias - todas as semanas - + desativar + cada hora + cada 3 horas + cada 6 horas + cada 12 horas + todos os dias + cada 3 dias + todas as semanas \ No newline at end of file diff --git a/src/main/res/values-ru/strings.xml b/src/main/res/values-ru/strings.xml index 69c1bca01..4e352083b 100644 --- a/src/main/res/values-ru/strings.xml +++ b/src/main/res/values-ru/strings.xml @@ -413,16 +413,12 @@ Светлая Темная По умолчанию - - отключить - каждые 15 минут - каждые 30 минут - каждый час - каждые 3 часа - каждые 6 часов - каждые 12 часов - каждый день - каждые 3 дня - каждую неделю - + отключить + каждый час + каждые 3 часа + каждые 6 часов + каждые 12 часов + каждый день + каждые 3 дня + каждую неделю \ No newline at end of file diff --git a/src/main/res/values-sk/strings.xml b/src/main/res/values-sk/strings.xml index f2742c2de..0fa7d135c 100644 --- a/src/main/res/values-sk/strings.xml +++ b/src/main/res/values-sk/strings.xml @@ -308,16 +308,12 @@ Zobraziť skryté Zobraziť dokončené %s úlohy - - zakázať - každých pätnásť minút - každú tretiu minútu - každú hodinu - každé tri hodiny - každých šesť hodín - každých dvanásť hodín - každý deň - každý tretí deň - každý týždeň - + zakázať + každú hodinu + každé tri hodiny + každých šesť hodín + každých dvanásť hodín + každý deň + každý tretí deň + každý týždeň \ No newline at end of file diff --git a/src/main/res/values-sl-rSI/strings.xml b/src/main/res/values-sl-rSI/strings.xml index d3a748b0a..6f1556b0c 100644 --- a/src/main/res/values-sl-rSI/strings.xml +++ b/src/main/res/values-sl-rSI/strings.xml @@ -247,16 +247,12 @@ Zbriše vse usklajene podatke Odjava/brisanje usklajenih podatkov? Donirajte - - onemogoči - vsakih 15 minut - vsakih 30 minut - vsako uro - vsake 3 ure - vsakih 6 ur - vsakih 12 ur - vsak dan - vsake 3 dni - vsak teden - + onemogoči + vsako uro + vsake 3 ure + vsakih 6 ur + vsakih 12 ur + vsak dan + vsake 3 dni + vsak teden \ No newline at end of file diff --git a/src/main/res/values-sv/strings.xml b/src/main/res/values-sv/strings.xml index 58e4bebff..a16e2f4de 100644 --- a/src/main/res/values-sv/strings.xml +++ b/src/main/res/values-sv/strings.xml @@ -402,16 +402,12 @@ och återställer dina aktuella uppgifter från en backup Grå Blågrå Svart - - inaktivera - varje kvartstimme - varje halvtimme - varje timme - var tredje timme - var sjätte timme - var tolfte timme - varje dag - var tredje dag - varje vecka - + inaktivera + varje timme + var tredje timme + var sjätte timme + var tolfte timme + varje dag + var tredje dag + varje vecka \ No newline at end of file diff --git a/src/main/res/values-tr/strings.xml b/src/main/res/values-tr/strings.xml index 16eb40a85..955b64083 100644 --- a/src/main/res/values-tr/strings.xml +++ b/src/main/res/values-tr/strings.xml @@ -215,16 +215,12 @@ Çıkış Yap Bütün eşleme verilerini temizle Çıkış Yap / Senkron verisini sil? - - devre dışı bırak - her 15 dakika - her 30 dakika - her saat - her 3 saat - her 6 saat - her 12 saat - hergün - her 3 gün - her hafta - + devre dışı bırak + her saat + her 3 saat + her 6 saat + her 12 saat + hergün + her 3 gün + her hafta \ No newline at end of file diff --git a/src/main/res/values-uk/strings.xml b/src/main/res/values-uk/strings.xml index b0fbc4184..d1fb3e8c4 100644 --- a/src/main/res/values-uk/strings.xml +++ b/src/main/res/values-uk/strings.xml @@ -277,16 +277,12 @@ Переривати режим \"Doze\" для повідомленнь Андроїд зазвичай буде значно відкладати повідомлення коли пристрій в режимі \"Doze\" Android дозволить деякі переривання в режимі \"Doze\" - - вимкнути - що 15 хвилин - що 30 хвилин - кожну годину - кожних 3 години - кожних 6 годин - кожних 12 годин - щодня - кожного 3-го дня - кожного тижня - + вимкнути + кожну годину + кожних 3 години + кожних 6 годин + кожних 12 годин + щодня + кожного 3-го дня + кожного тижня \ No newline at end of file diff --git a/src/main/res/values-zh-rCN/strings.xml b/src/main/res/values-zh-rCN/strings.xml index 323bf420c..23e84035f 100644 --- a/src/main/res/values-zh-rCN/strings.xml +++ b/src/main/res/values-zh-rCN/strings.xml @@ -207,16 +207,12 @@ 登出 清除所有同步资料 登出/清除同步资料? - - 停用 - 每15分钟 - 每30分钟 - 每小时 - 每3小时 - 每6小时 - 每12小时 - 每天 - 每3天 - 每周 - + 停用 + 每小时 + 每3小时 + 每6小时 + 每12小时 + 每天 + 每3天 + 每周 \ No newline at end of file diff --git a/src/main/res/values-zh-rTW/strings.xml b/src/main/res/values-zh-rTW/strings.xml index efb0f4716..4b3772afe 100644 --- a/src/main/res/values-zh-rTW/strings.xml +++ b/src/main/res/values-zh-rTW/strings.xml @@ -233,16 +233,12 @@ 最優先 預設 最不急迫 - - 停用 - 每15分鐘 - 每30分鐘 - 每小時 - 每3小時 - 每6小時 - 每12小時 - 每天 - 每3天 - 每週 - + 停用 + 每小時 + 每3小時 + 每6小時 + 每12小時 + 每天 + 每3天 + 每週 \ No newline at end of file diff --git a/src/main/res/values/arrays.xml b/src/main/res/values/arrays.xml index 1dbefc895..9521ed9a1 100644 --- a/src/main/res/values/arrays.xml +++ b/src/main/res/values/arrays.xml @@ -40,6 +40,18 @@ 600 + + + @string/sync_interval_disable + @string/sync_interval_one_hour + @string/sync_interval_three_hours + @string/sync_interval_six_hours + @string/sync_interval_twelve_hours + @string/sync_interval_one_day + @string/sync_interval_three_days + @string/sync_interval_one_week + + @string/due_date @string/due_time diff --git a/src/main/res/values/bools.xml b/src/main/res/values/bools.xml index d9688a3d4..de02aba1a 100644 --- a/src/main/res/values/bools.xml +++ b/src/main/res/values/bools.xml @@ -1,7 +1,6 @@ false - false false false \ No newline at end of file diff --git a/src/main/res/values/keys.xml b/src/main/res/values/keys.xml index a63e90ae9..779a3282b 100644 --- a/src/main/res/values/keys.xml +++ b/src/main/res/values/keys.xml @@ -178,8 +178,6 @@ 0 - 900 - 1800 3600 10800 21600 @@ -306,5 +304,6 @@ theme_color theme_accent default_gtasks_list + sync_warning_shown diff --git a/src/main/res/values/strings.xml b/src/main/res/values/strings.xml index 04b006194..05cbedae7 100644 --- a/src/main/res/values/strings.xml +++ b/src/main/res/values/strings.xml @@ -894,18 +894,14 @@ File %1$s contained %2$s.\n\n Dark Default - - - disable - every fifteen minutes - every thirty minutes - every hour - every three hours - every six hours - every twelve hours - every day - every three days - every week - + disable + every hour + every three hours + every six hours + every twelve hours + every day + every three days + every week + Automatic synchronization is currently disabled by Android diff --git a/src/main/res/xml/preferences_gtasks.xml b/src/main/res/xml/preferences_gtasks.xml index 420f12a50..1f824e0ea 100644 --- a/src/main/res/xml/preferences_gtasks.xml +++ b/src/main/res/xml/preferences_gtasks.xml @@ -19,9 +19,9 @@ android:title="@string/default_list" /> diff --git a/src/main/res/xml/syncadapter.xml b/src/main/res/xml/syncadapter.xml new file mode 100644 index 000000000..f1546ce68 --- /dev/null +++ b/src/main/res/xml/syncadapter.xml @@ -0,0 +1,6 @@ + + \ No newline at end of file