mirror of https://github.com/tasks/tasks
You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
530 lines
20 KiB
Java
530 lines
20 KiB
Java
package org.tasks.gtasks;
|
|
|
|
import static com.google.common.collect.Lists.transform;
|
|
import static org.tasks.date.DateTimeUtils.newDateTime;
|
|
|
|
import android.app.PendingIntent;
|
|
import android.content.Context;
|
|
import android.content.Intent;
|
|
import android.text.TextUtils;
|
|
import androidx.annotation.Nullable;
|
|
import androidx.core.app.NotificationCompat;
|
|
import com.google.api.client.googleapis.extensions.android.gms.auth.UserRecoverableAuthIOException;
|
|
import com.google.api.client.googleapis.json.GoogleJsonResponseException;
|
|
import com.google.api.services.tasks.model.Task.Links;
|
|
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.google.common.base.Strings;
|
|
import com.todoroo.andlib.utility.DateUtilities;
|
|
import com.todoroo.astrid.api.Filter;
|
|
import com.todoroo.astrid.api.GtasksFilter;
|
|
import com.todoroo.astrid.dao.TaskDao;
|
|
import com.todoroo.astrid.data.SyncFlags;
|
|
import com.todoroo.astrid.data.Task;
|
|
import com.todoroo.astrid.gtasks.GtasksListService;
|
|
import com.todoroo.astrid.gtasks.api.GtasksApiUtilities;
|
|
import com.todoroo.astrid.gtasks.api.GtasksInvoker;
|
|
import com.todoroo.astrid.gtasks.api.HttpNotFoundException;
|
|
import com.todoroo.astrid.service.TaskCreator;
|
|
import com.todoroo.astrid.service.TaskDeleter;
|
|
import com.todoroo.astrid.utility.Constants;
|
|
import java.io.EOFException;
|
|
import java.io.IOException;
|
|
import java.net.HttpRetryException;
|
|
import java.net.SocketException;
|
|
import java.net.SocketTimeoutException;
|
|
import java.net.UnknownHostException;
|
|
import java.util.ArrayList;
|
|
import java.util.Collections;
|
|
import java.util.Comparator;
|
|
import java.util.List;
|
|
import javax.inject.Inject;
|
|
import javax.net.ssl.SSLException;
|
|
import org.tasks.LocalBroadcastManager;
|
|
import org.tasks.R;
|
|
import org.tasks.analytics.Tracker;
|
|
import org.tasks.billing.Inventory;
|
|
import org.tasks.data.GoogleTask;
|
|
import org.tasks.data.GoogleTaskAccount;
|
|
import org.tasks.data.GoogleTaskDao;
|
|
import org.tasks.data.GoogleTaskList;
|
|
import org.tasks.data.GoogleTaskListDao;
|
|
import org.tasks.injection.ForApplication;
|
|
import org.tasks.notifications.NotificationManager;
|
|
import org.tasks.preferences.DefaultFilterProvider;
|
|
import org.tasks.preferences.PermissionChecker;
|
|
import org.tasks.preferences.Preferences;
|
|
import org.tasks.time.DateTime;
|
|
import timber.log.Timber;
|
|
|
|
public class GoogleTaskSynchronizer {
|
|
|
|
private static final String DEFAULT_LIST = "@default"; // $NON-NLS-1$
|
|
private static final int MAX_TITLE_LENGTH = 1024;
|
|
private static final int MAX_DESCRIPTION_LENGTH = 8192;
|
|
|
|
private static final Comparator<com.google.api.services.tasks.model.Task> PARENTS_FIRST =
|
|
(o1, o2) -> {
|
|
if (Strings.isNullOrEmpty(o1.getParent())) {
|
|
return Strings.isNullOrEmpty(o2.getParent()) ? 0 : -1;
|
|
} else {
|
|
return Strings.isNullOrEmpty(o2.getParent()) ? 1 : 0;
|
|
}
|
|
};
|
|
|
|
private final Context context;
|
|
private final GoogleTaskListDao googleTaskListDao;
|
|
private final GtasksListService gtasksListService;
|
|
private final Preferences preferences;
|
|
private final TaskDao taskDao;
|
|
private final Tracker tracker;
|
|
private final NotificationManager notificationManager;
|
|
private final GoogleTaskDao googleTaskDao;
|
|
private final TaskCreator taskCreator;
|
|
private final DefaultFilterProvider defaultFilterProvider;
|
|
private final PermissionChecker permissionChecker;
|
|
private final GoogleAccountManager googleAccountManager;
|
|
private final LocalBroadcastManager localBroadcastManager;
|
|
private final Inventory inventory;
|
|
private final TaskDeleter taskDeleter;
|
|
private final GtasksInvoker gtasksInvoker;
|
|
|
|
@Inject
|
|
public GoogleTaskSynchronizer(
|
|
@ForApplication Context context,
|
|
GoogleTaskListDao googleTaskListDao,
|
|
GtasksListService gtasksListService,
|
|
Preferences preferences,
|
|
TaskDao taskDao,
|
|
Tracker tracker,
|
|
NotificationManager notificationManager,
|
|
GoogleTaskDao googleTaskDao,
|
|
TaskCreator taskCreator,
|
|
DefaultFilterProvider defaultFilterProvider,
|
|
PermissionChecker permissionChecker,
|
|
GoogleAccountManager googleAccountManager,
|
|
LocalBroadcastManager localBroadcastManager,
|
|
Inventory inventory,
|
|
TaskDeleter taskDeleter,
|
|
GtasksInvoker gtasksInvoker) {
|
|
this.context = context;
|
|
this.googleTaskListDao = googleTaskListDao;
|
|
this.gtasksListService = gtasksListService;
|
|
this.preferences = preferences;
|
|
this.taskDao = taskDao;
|
|
this.tracker = tracker;
|
|
this.notificationManager = notificationManager;
|
|
this.googleTaskDao = googleTaskDao;
|
|
this.taskCreator = taskCreator;
|
|
this.defaultFilterProvider = defaultFilterProvider;
|
|
this.permissionChecker = permissionChecker;
|
|
this.googleAccountManager = googleAccountManager;
|
|
this.localBroadcastManager = localBroadcastManager;
|
|
this.inventory = inventory;
|
|
this.taskDeleter = taskDeleter;
|
|
this.gtasksInvoker = gtasksInvoker;
|
|
}
|
|
|
|
public static void mergeDates(long remoteDueDate, Task local) {
|
|
if (remoteDueDate > 0 && local.hasDueTime()) {
|
|
DateTime oldDate = newDateTime(local.getDueDate());
|
|
DateTime newDate =
|
|
newDateTime(remoteDueDate)
|
|
.withHourOfDay(oldDate.getHourOfDay())
|
|
.withMinuteOfHour(oldDate.getMinuteOfHour())
|
|
.withSecondOfMinute(oldDate.getSecondOfMinute());
|
|
local.setDueDateAdjustingHideUntil(
|
|
Task.createDueDate(Task.URGENCY_SPECIFIC_DAY_TIME, newDate.getMillis()));
|
|
} else {
|
|
local.setDueDateAdjustingHideUntil(remoteDueDate);
|
|
}
|
|
}
|
|
|
|
public void sync(GoogleTaskAccount account, int i) {
|
|
Timber.d("%s: start sync", account);
|
|
try {
|
|
if (i == 0 || inventory.hasPro()) {
|
|
synchronize(account);
|
|
} else {
|
|
account.setError(context.getString(R.string.requires_pro_subscription));
|
|
}
|
|
} catch (SocketTimeoutException
|
|
| SSLException
|
|
| SocketException
|
|
| UnknownHostException
|
|
| HttpRetryException
|
|
| EOFException e) {
|
|
Timber.e(e);
|
|
account.setError(e.getMessage());
|
|
} catch (GoogleJsonResponseException e) {
|
|
account.setError(e.getMessage());
|
|
if (e.getStatusCode() == 401) {
|
|
Timber.e(e);
|
|
} else {
|
|
tracker.reportException(e);
|
|
}
|
|
} catch (UserRecoverableAuthIOException e) {
|
|
Timber.e(e);
|
|
sendNotification(context, e.getIntent());
|
|
} catch (Exception e) {
|
|
account.setError(e.getMessage());
|
|
tracker.reportException(e);
|
|
} finally {
|
|
googleTaskListDao.update(account);
|
|
localBroadcastManager.broadcastRefreshList();
|
|
Timber.d("%s: end sync", account);
|
|
}
|
|
}
|
|
|
|
private void sendNotification(Context context, Intent intent) {
|
|
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_FROM_BACKGROUND);
|
|
|
|
PendingIntent resolve =
|
|
PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
|
|
NotificationCompat.Builder builder =
|
|
new NotificationCompat.Builder(
|
|
context, NotificationManager.NOTIFICATION_CHANNEL_MISCELLANEOUS)
|
|
.setAutoCancel(true)
|
|
.setContentIntent(resolve)
|
|
.setContentTitle(context.getString(R.string.sync_error_permissions))
|
|
.setContentText(
|
|
context.getString(R.string.common_google_play_services_notification_ticker))
|
|
.setAutoCancel(true)
|
|
.setSmallIcon(R.drawable.ic_warning_white_24dp)
|
|
.setTicker(context.getString(R.string.common_google_play_services_notification_ticker));
|
|
notificationManager.notify(Constants.NOTIFICATION_SYNC_ERROR, builder, true, false, false);
|
|
}
|
|
|
|
private void synchronize(GoogleTaskAccount account) throws IOException {
|
|
if (!permissionChecker.canAccessAccounts()
|
|
|| googleAccountManager.getAccount(account.getAccount()) == null) {
|
|
account.setError(context.getString(R.string.cannot_access_account));
|
|
googleTaskListDao.update(account);
|
|
localBroadcastManager.broadcastRefreshList();
|
|
return;
|
|
}
|
|
|
|
GtasksInvoker gtasksInvoker = this.gtasksInvoker.forAccount(account.getAccount());
|
|
pushLocalChanges(account, gtasksInvoker);
|
|
|
|
List<TaskList> gtaskLists = new ArrayList<>();
|
|
String nextPageToken = null;
|
|
String eTag = null;
|
|
do {
|
|
TaskLists remoteLists = gtasksInvoker.allGtaskLists(nextPageToken);
|
|
if (remoteLists == null) {
|
|
break;
|
|
}
|
|
eTag = remoteLists.getEtag();
|
|
List<TaskList> items = remoteLists.getItems();
|
|
if (items != null) {
|
|
gtaskLists.addAll(items);
|
|
}
|
|
nextPageToken = remoteLists.getNextPageToken();
|
|
} while (!Strings.isNullOrEmpty(nextPageToken));
|
|
gtasksListService.updateLists(account, gtaskLists);
|
|
Filter defaultRemoteList = defaultFilterProvider.getDefaultRemoteList();
|
|
if (defaultRemoteList instanceof GtasksFilter) {
|
|
GoogleTaskList list =
|
|
gtasksListService.getList(((GtasksFilter) defaultRemoteList).getRemoteId());
|
|
if (list == null) {
|
|
preferences.setString(R.string.p_default_remote_list, null);
|
|
}
|
|
}
|
|
for (GoogleTaskList list :
|
|
googleTaskListDao.getByRemoteId(transform(gtaskLists, TaskList::getId))) {
|
|
fetchAndApplyRemoteChanges(gtasksInvoker, list);
|
|
if (!preferences.isPositionHackEnabled()) {
|
|
googleTaskDao.reposition(list.getRemoteId());
|
|
}
|
|
}
|
|
if (preferences.isPositionHackEnabled()) {
|
|
for (TaskList list : gtaskLists) {
|
|
List<com.google.api.services.tasks.model.Task> tasks =
|
|
fetchPositions(gtasksInvoker, list.getId());
|
|
for (com.google.api.services.tasks.model.Task task : tasks) {
|
|
googleTaskDao.updatePosition(task.getId(), task.getParent(), task.getPosition());
|
|
}
|
|
googleTaskDao.reposition(list.getId());
|
|
}
|
|
}
|
|
account.setEtag(eTag);
|
|
account.setError("");
|
|
}
|
|
|
|
private List<com.google.api.services.tasks.model.Task> fetchPositions(
|
|
GtasksInvoker gtasksInvoker, String listId) throws IOException {
|
|
List<com.google.api.services.tasks.model.Task> tasks = new ArrayList<>();
|
|
String nextPageToken = null;
|
|
do {
|
|
Tasks taskList = gtasksInvoker.getAllPositions(listId, nextPageToken);
|
|
if (taskList == null) {
|
|
break;
|
|
}
|
|
List<com.google.api.services.tasks.model.Task> items = taskList.getItems();
|
|
if (items != null) {
|
|
tasks.addAll(items);
|
|
}
|
|
nextPageToken = taskList.getNextPageToken();
|
|
} while (!Strings.isNullOrEmpty(nextPageToken));
|
|
return tasks;
|
|
}
|
|
|
|
private void pushLocalChanges(GoogleTaskAccount account, GtasksInvoker gtasksInvoker)
|
|
throws IOException {
|
|
List<Task> tasks = taskDao.getGoogleTasksToPush(account.getAccount());
|
|
for (Task task : tasks) {
|
|
pushTask(task, gtasksInvoker);
|
|
}
|
|
}
|
|
|
|
private void pushTask(Task task, GtasksInvoker gtasksInvoker) throws IOException {
|
|
for (GoogleTask deleted : googleTaskDao.getDeletedByTaskId(task.getId())) {
|
|
gtasksInvoker.deleteGtask(deleted.getListId(), deleted.getRemoteId());
|
|
googleTaskDao.delete(deleted);
|
|
}
|
|
|
|
GoogleTask gtasksMetadata = googleTaskDao.getByTaskId(task.getId());
|
|
|
|
if (gtasksMetadata == null) {
|
|
return;
|
|
}
|
|
|
|
com.google.api.services.tasks.model.Task remoteModel =
|
|
new com.google.api.services.tasks.model.Task();
|
|
boolean newlyCreated = false;
|
|
|
|
String remoteId;
|
|
Filter defaultRemoteList = defaultFilterProvider.getDefaultRemoteList();
|
|
String listId =
|
|
defaultRemoteList instanceof GtasksFilter
|
|
? ((GtasksFilter) defaultRemoteList).getRemoteId()
|
|
: DEFAULT_LIST;
|
|
|
|
if (Strings.isNullOrEmpty(gtasksMetadata.getRemoteId())) { // Create case
|
|
String selectedList = gtasksMetadata.getListId();
|
|
if (!Strings.isNullOrEmpty(selectedList)) {
|
|
listId = selectedList;
|
|
}
|
|
newlyCreated = true;
|
|
} else { // update case
|
|
remoteId = gtasksMetadata.getRemoteId();
|
|
listId = gtasksMetadata.getListId();
|
|
remoteModel.setId(remoteId);
|
|
}
|
|
|
|
// If task was newly created but without a title, don't sync--we're in the middle of
|
|
// creating a task which may end up being cancelled. Also don't sync new but already
|
|
// deleted tasks
|
|
if (newlyCreated && (TextUtils.isEmpty(task.getTitle()) || task.getDeletionDate() > 0)) {
|
|
return;
|
|
}
|
|
|
|
// Update the remote model's changed properties
|
|
if (task.isDeleted()) {
|
|
remoteModel.setDeleted(true);
|
|
}
|
|
|
|
remoteModel.setTitle(truncate(task.getTitle(), MAX_TITLE_LENGTH));
|
|
remoteModel.setNotes(truncate(task.getNotes(), MAX_DESCRIPTION_LENGTH));
|
|
if (task.hasDueDate()) {
|
|
remoteModel.setDue(GtasksApiUtilities.unixTimeToGtasksDueDate(task.getDueDate()));
|
|
}
|
|
if (task.isCompleted()) {
|
|
remoteModel.setCompleted(
|
|
GtasksApiUtilities.unixTimeToGtasksCompletionTime(task.getCompletionDate()));
|
|
remoteModel.setStatus("completed"); // $NON-NLS-1$
|
|
} else {
|
|
remoteModel.setCompleted(null);
|
|
remoteModel.setStatus("needsAction"); // $NON-NLS-1$
|
|
}
|
|
|
|
if (newlyCreated) {
|
|
long parent = gtasksMetadata.getParent();
|
|
String localParent = parent > 0 ? googleTaskDao.getRemoteId(parent) : null;
|
|
String previous =
|
|
googleTaskDao.getPrevious(
|
|
listId, Strings.isNullOrEmpty(localParent) ? 0 : parent, gtasksMetadata.getOrder());
|
|
|
|
com.google.api.services.tasks.model.Task created;
|
|
try {
|
|
created = gtasksInvoker.createGtask(listId, remoteModel, localParent, previous);
|
|
} catch (HttpNotFoundException e) {
|
|
created = gtasksInvoker.createGtask(listId, remoteModel, null, null);
|
|
}
|
|
|
|
if (created != null) {
|
|
// Update the metadata for the newly created task
|
|
gtasksMetadata.setRemoteId(created.getId());
|
|
gtasksMetadata.setListId(listId);
|
|
|
|
gtasksMetadata.setRemoteOrder(Long.parseLong(created.getPosition()));
|
|
gtasksMetadata.setRemoteParent(created.getParent());
|
|
} else {
|
|
return;
|
|
}
|
|
} else {
|
|
try {
|
|
if (!task.isDeleted() && gtasksMetadata.isMoved()) {
|
|
try {
|
|
long parent = gtasksMetadata.getParent();
|
|
String localParent = parent > 0 ? googleTaskDao.getRemoteId(parent) : null;
|
|
String previous =
|
|
googleTaskDao.getPrevious(
|
|
listId,
|
|
Strings.isNullOrEmpty(localParent) ? 0 : parent,
|
|
gtasksMetadata.getOrder());
|
|
|
|
com.google.api.services.tasks.model.Task result =
|
|
gtasksInvoker.moveGtask(listId, remoteModel.getId(), localParent, previous);
|
|
gtasksMetadata.setRemoteOrder(Long.parseLong(result.getPosition()));
|
|
gtasksMetadata.setRemoteParent(result.getParent());
|
|
gtasksMetadata.setParent(
|
|
Strings.isNullOrEmpty(result.getParent())
|
|
? 0
|
|
: googleTaskDao.getTask(result.getParent()));
|
|
} catch (GoogleJsonResponseException e) {
|
|
if (e.getStatusCode() == 400) {
|
|
Timber.e(e);
|
|
} else {
|
|
throw e;
|
|
}
|
|
}
|
|
}
|
|
// TODO: don't updateGtask if it was only moved
|
|
gtasksInvoker.updateGtask(listId, remoteModel);
|
|
} catch (HttpNotFoundException e) {
|
|
googleTaskDao.delete(gtasksMetadata);
|
|
return;
|
|
}
|
|
}
|
|
|
|
task.setModificationDate(DateUtilities.now());
|
|
gtasksMetadata.setMoved(false);
|
|
gtasksMetadata.setLastSync(DateUtilities.now() + 1000L);
|
|
if (gtasksMetadata.getId() == Task.NO_ID) {
|
|
googleTaskDao.insert(gtasksMetadata);
|
|
} else {
|
|
googleTaskDao.update(gtasksMetadata);
|
|
}
|
|
task.putTransitory(SyncFlags.GTASKS_SUPPRESS_SYNC, true);
|
|
taskDao.save(task);
|
|
}
|
|
|
|
private synchronized void fetchAndApplyRemoteChanges(
|
|
GtasksInvoker gtasksInvoker, GoogleTaskList list) throws IOException {
|
|
String listId = list.getRemoteId();
|
|
long lastSyncDate = list.getLastSync();
|
|
List<com.google.api.services.tasks.model.Task> tasks = new ArrayList<>();
|
|
String nextPageToken = null;
|
|
do {
|
|
Tasks taskList =
|
|
gtasksInvoker.getAllGtasksFromListId(listId, lastSyncDate + 1000L, nextPageToken);
|
|
if (taskList == null) {
|
|
break;
|
|
}
|
|
List<com.google.api.services.tasks.model.Task> items = taskList.getItems();
|
|
if (items != null) {
|
|
tasks.addAll(items);
|
|
}
|
|
nextPageToken = taskList.getNextPageToken();
|
|
} while (!Strings.isNullOrEmpty(nextPageToken));
|
|
|
|
Collections.sort(tasks, PARENTS_FIRST);
|
|
|
|
for (com.google.api.services.tasks.model.Task gtask : tasks) {
|
|
String remoteId = gtask.getId();
|
|
GoogleTask googleTask = googleTaskDao.getByRemoteId(remoteId);
|
|
Task task = null;
|
|
if (googleTask == null) {
|
|
googleTask = new GoogleTask(0, "");
|
|
} else if (googleTask.getTask() > 0) {
|
|
task = taskDao.fetch(googleTask.getTask());
|
|
}
|
|
com.google.api.client.util.DateTime updated = gtask.getUpdated();
|
|
if (updated != null) {
|
|
lastSyncDate = Math.max(lastSyncDate, updated.getValue());
|
|
}
|
|
Boolean isDeleted = gtask.getDeleted();
|
|
Boolean isHidden = gtask.getHidden();
|
|
if (isDeleted != null && isDeleted) {
|
|
if (task != null) {
|
|
taskDeleter.delete(task);
|
|
}
|
|
continue;
|
|
} else if (isHidden != null && isHidden) {
|
|
if (task == null) {
|
|
continue;
|
|
}
|
|
if (task.isRecurring()) {
|
|
googleTask.setRemoteId("");
|
|
} else {
|
|
taskDeleter.delete(task);
|
|
continue;
|
|
}
|
|
} else {
|
|
googleTask.setRemoteOrder(Long.parseLong(gtask.getPosition()));
|
|
googleTask.setRemoteParent(gtask.getParent());
|
|
googleTask.setParent(
|
|
Strings.isNullOrEmpty(gtask.getParent())
|
|
? 0
|
|
: googleTaskDao.getTask(gtask.getParent()));
|
|
googleTask.setRemoteId(gtask.getId());
|
|
}
|
|
|
|
if (task == null) {
|
|
task = taskCreator.createWithValues("");
|
|
}
|
|
List<Links> links = gtask.getLinks();
|
|
if (!links.isEmpty()) {
|
|
Links link = links.get(0);
|
|
googleTask.setEmailDescription(link.getDescription());
|
|
googleTask.setEmailUrl(link.getLink());
|
|
}
|
|
task.setTitle(getTruncatedValue(task.getTitle(), gtask.getTitle(), MAX_TITLE_LENGTH));
|
|
task.setCreationDate(DateUtilities.now());
|
|
task.setCompletionDate(
|
|
GtasksApiUtilities.gtasksCompletedTimeToUnixTime(gtask.getCompleted()));
|
|
long dueDate = GtasksApiUtilities.gtasksDueTimeToUnixTime(gtask.getDue());
|
|
mergeDates(Task.createDueDate(Task.URGENCY_SPECIFIC_DAY, dueDate), task);
|
|
task.setNotes(getTruncatedValue(task.getNotes(), gtask.getNotes(), MAX_DESCRIPTION_LENGTH));
|
|
googleTask.setListId(listId);
|
|
googleTask.setLastSync(DateUtilities.now() + 1000L);
|
|
write(task, googleTask);
|
|
}
|
|
list.setLastSync(lastSyncDate);
|
|
googleTaskListDao.insertOrReplace(list);
|
|
}
|
|
|
|
static String truncate(@Nullable String string, int max) {
|
|
return string == null || string.length() <= max ? string : string.substring(0, max);
|
|
}
|
|
|
|
static String getTruncatedValue(@Nullable String currentValue, @Nullable String newValue, int maxLength) {
|
|
return Strings.isNullOrEmpty(newValue)
|
|
|| newValue.length() < maxLength
|
|
|| Strings.isNullOrEmpty(currentValue)
|
|
|| !currentValue.startsWith(newValue)
|
|
? newValue
|
|
: currentValue;
|
|
}
|
|
|
|
private void write(Task task, GoogleTask googleTask) {
|
|
if (!(TextUtils.isEmpty(task.getTitle()) && TextUtils.isEmpty(task.getNotes()))) {
|
|
task.putTransitory(SyncFlags.GTASKS_SUPPRESS_SYNC, true);
|
|
task.putTransitory(TaskDao.TRANS_SUPPRESS_REFRESH, true);
|
|
if (task.isNew()) {
|
|
taskDao.createNew(task);
|
|
}
|
|
taskDao.save(task);
|
|
googleTask.setTask(task.getId());
|
|
if (googleTask.getId() == 0) {
|
|
googleTaskDao.insert(googleTask);
|
|
} else {
|
|
googleTaskDao.update(googleTask);
|
|
}
|
|
}
|
|
}
|
|
}
|