private synchronized void pushUpdated(GtasksInvoker invoker, SyncResultCallback callback) {
   TodorooCursor<Task> queued =
       taskService.query(
           Query.select(Task.PROPERTIES)
               .join(
                   Join.left(
                       Metadata.TABLE,
                       Criterion.and(
                           MetadataCriteria.withKey(GtasksMetadata.METADATA_KEY),
                           Task.ID.eq(Metadata.TASK))))
               .where(
                   Criterion.or(
                       Task.MODIFICATION_DATE.gt(GtasksMetadata.LAST_SYNC),
                       Criterion.and(
                           Task.USER_ID.neq(Task.USER_ID_SELF), GtasksMetadata.ID.isNotNull()),
                       Metadata.KEY.isNull())));
   callback.incrementMax(queued.getCount() * 10);
   try {
     Task task = new Task();
     for (queued.moveToFirst(); !queued.isAfterLast(); queued.moveToNext()) {
       task.readFromCursor(queued);
       try {
         gtasksSyncService.pushTaskOnSave(task, task.getMergedValues(), invoker, false);
       } catch (GoogleTasksException e) {
         handler.handleException("gtasks-sync-io", e, e.getType()); // $NON-NLS-1$
       } catch (IOException e) {
         handler.handleException("gtasks-sync-io", e, e.toString()); // $NON-NLS-1$
       } finally {
         callback.incrementProgress(10);
       }
     }
   } finally {
     queued.close();
   }
 }
  @Override
  public void synchronizeList(
      Object list, final boolean manual, final SyncResultCallback callback) {
    if (!(list instanceof StoreObject)) return;
    final StoreObject gtasksList = (StoreObject) list;
    if (!GtasksList.TYPE.equals(gtasksList.getValue(StoreObject.TYPE))) return;

    callback.started();
    callback.incrementMax(100);

    new Thread(
            new Runnable() {
              public void run() {
                callback.incrementProgress(50);
                try {
                  String authToken = getValidatedAuthToken();
                  callback.incrementProgress(12);
                  gtasksSyncService.waitUntilEmpty();
                  callback.incrementProgress(13);
                  final GtasksInvoker service = new GtasksInvoker(authToken);
                  synchronizeListHelper(gtasksList, service, manual, null, callback);
                } finally {
                  callback.incrementProgress(25);
                  callback.finished();
                }
              }
            })
        .start();
  }
  private synchronized void synchronizeListHelper(
      StoreObject list,
      GtasksInvoker invoker,
      boolean manual,
      SyncExceptionHandler errorHandler,
      SyncResultCallback callback) {
    String listId = list.getValue(GtasksList.REMOTE_ID);
    long lastSyncDate;
    if (!manual && list.containsNonNullValue(GtasksList.LAST_SYNC)) {
      lastSyncDate = list.getValue(GtasksList.LAST_SYNC);
    } else {
      lastSyncDate = 0;
    }
    boolean includeDeletedAndHidden = lastSyncDate != 0;
    try {
      Tasks taskList =
          invoker.getAllGtasksFromListId(
              listId, includeDeletedAndHidden, includeDeletedAndHidden, lastSyncDate);
      List<com.google.api.services.tasks.model.Task> tasks = taskList.getItems();
      if (tasks != null) {
        callback.incrementMax(tasks.size() * 10);
        HashSet<Long> localIds = new HashSet<Long>(tasks.size());
        for (com.google.api.services.tasks.model.Task t : tasks) {
          GtasksTaskContainer container = parseRemoteTask(t, listId);
          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);
          localIds.add(container.task.getId());
          callback.incrementProgress(10);
        }
        list.setValue(GtasksList.LAST_SYNC, DateUtilities.now());
        storeObjectDao.persist(list);

        if (lastSyncDate == 0) {
          Long[] localIdArray = localIds.toArray(new Long[localIds.size()]);
          Criterion delete =
              Criterion.and(
                  Metadata.KEY.eq(GtasksMetadata.METADATA_KEY),
                  GtasksMetadata.LIST_ID.eq(listId),
                  Criterion.not(Metadata.TASK.in(localIdArray)));
          taskService.deleteWhere(
              Task.ID.in(Query.select(Metadata.TASK).from(Metadata.TABLE).where(delete)));
          metadataService.deleteWhere(delete);
        }

        gtasksTaskListUpdater.correctOrderAndIndentForList(listId);
      }
    } catch (GoogleTasksException e) {
      if (errorHandler != null)
        errorHandler.handleException("gtasks-sync-io", e, e.getType()); // $NON-NLS-1$
    } catch (IOException e) {
      if (errorHandler != null)
        errorHandler.handleException("gtasks-sync-io", e, e.toString()); // $NON-NLS-1$
    }
  }
  @Override
  public void synchronizeActiveTasks(final boolean manual, final SyncResultCallback callback) {
    callback.started();
    callback.incrementMax(100);

    gtasksPreferenceService.recordSyncStart();

    new Thread(
            new Runnable() {
              public void run() {
                callback.incrementProgress(50);
                String authToken = getValidatedAuthToken();
                final GtasksInvoker invoker = new GtasksInvoker(authToken);
                try {
                  gtasksListService.updateLists(invoker.allGtaskLists());
                } catch (GoogleTasksException e) {
                  handler.handleException("gtasks-sync=io", e, e.getType()); // $NON-NLS-1$
                } catch (IOException e) {
                  handler.handleException("gtasks-sync=io", e, e.toString()); // $NON-NLS-1$
                }

                StoreObject[] lists = gtasksListService.getLists();
                if (lists.length == 0) {
                  finishSync(callback);
                  return;
                }
                callback.incrementMax(25 * lists.length);
                final AtomicInteger finisher = new AtomicInteger(lists.length);

                for (final StoreObject list : lists) {
                  new Thread(
                          new Runnable() {
                            @Override
                            public void run() {
                              synchronizeListHelper(list, invoker, manual, handler, callback);
                              callback.incrementProgress(25);
                              if (finisher.decrementAndGet() == 0) {
                                pushUpdated(invoker, callback);
                                finishSync(callback);
                              }
                            }
                          })
                      .start();
                }
              }
            })
        .start();
  }