mirror of https://github.com/tasks/tasks
Google Tasks Api integration. Basic tests are passing, various edge cases may still need work. Code for accessing account manager has been factored in to the repurposed GtasksLoginActivity activity class. A one-time migration will occur for legacy users to update remote task ids.
parent
8d3e7f5a24
commit
89dc6d462a
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -0,0 +1,22 @@
|
||||
package com.todoroo.astrid.gtasks.api;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import com.google.api.services.tasks.v1.model.Task;
|
||||
/**
|
||||
* Encapsulates a request to the api to create a task on the remote server
|
||||
* @author Sam Bosley
|
||||
*
|
||||
*/
|
||||
public class CreateRequest extends PushRequest {
|
||||
|
||||
public CreateRequest(GtasksService service, String listId, Task toUpdate) {
|
||||
super(service, listId, toUpdate);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Task executePush() throws IOException {
|
||||
return service.createGtask(listId, toPush);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
package com.todoroo.astrid.gtasks.api;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class GoogleTasksException extends IOException {
|
||||
private static final long serialVersionUID = -5585448790574862510L;
|
||||
|
||||
public GoogleTasksException() {
|
||||
|
||||
}
|
||||
|
||||
public GoogleTasksException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public GoogleTasksException(String message, Throwable cause) {
|
||||
super(message);
|
||||
initCause(cause);
|
||||
}
|
||||
|
||||
public GoogleTasksException(Throwable cause) {
|
||||
super(cause.getMessage());
|
||||
initCause(cause);
|
||||
}
|
||||
}
|
@ -0,0 +1,66 @@
|
||||
package com.todoroo.astrid.gtasks.api;
|
||||
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.Locale;
|
||||
import java.util.TimeZone;
|
||||
|
||||
import com.google.api.client.util.DateTime;
|
||||
import com.google.api.services.tasks.v1.model.Task;
|
||||
|
||||
@SuppressWarnings("nls")
|
||||
public class GtasksApiUtilities {
|
||||
|
||||
private static SimpleDateFormat timeWriter = new SimpleDateFormat("yyyy-MM-dd'T'hh:m:ss.SSSZ", Locale.US);
|
||||
|
||||
//When setting completion date, gtasks api will convert to UTC AND change hours/minutes/seconds to match
|
||||
public static long gtasksCompletedTimeToUnixTime(String gtasksCompletedTime, long defaultValue) {
|
||||
if (gtasksCompletedTime == null) return defaultValue;
|
||||
synchronized(timeWriter) {
|
||||
try {
|
||||
long utcTime = DateTime.parseRfc3339(gtasksCompletedTime).value;
|
||||
return new DateTime(new Date(utcTime), TimeZone.getDefault()).value;
|
||||
} catch (NumberFormatException e) {
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//When setting due date, gtasks api will convert to UTC time without changing hours/minutes/seconds
|
||||
public static long gtasksDueTimeToUnixTime(String gtasksDueTime, long defaultValue) {
|
||||
if (gtasksDueTime == null) return defaultValue;
|
||||
synchronized(timeWriter) {
|
||||
try {
|
||||
long utcTime = DateTime.parseRfc3339(gtasksDueTime).value;
|
||||
return utcTime - TimeZone.getDefault().getOffset(utcTime);
|
||||
} catch (NumberFormatException e) {
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static String unixTimeToGtasksTime(long time) {
|
||||
if (time == 0) return null;
|
||||
synchronized(timeWriter) {
|
||||
return new DateTime(new Date(time), TimeZone.getDefault()).toStringRfc3339();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* The two methods below are useful for testing
|
||||
*/
|
||||
public static String gtasksCompletedTimeStringToLocalTimeString(String gtasksTime) {
|
||||
return GtasksApiUtilities.unixTimeToGtasksTime(GtasksApiUtilities.gtasksCompletedTimeToUnixTime(gtasksTime, 0));
|
||||
}
|
||||
|
||||
public static String gtasksDueTimeStringToLocalTimeString(String gtasksTime) {
|
||||
return GtasksApiUtilities.unixTimeToGtasksTime(GtasksApiUtilities.gtasksDueTimeToUnixTime(gtasksTime, 0));
|
||||
}
|
||||
|
||||
public static String extractListIdFromSelfLink(Task task) {
|
||||
String selfLink = task.selfLink;
|
||||
String [] urlComponents = selfLink.split("/");
|
||||
int listIdIndex = urlComponents.length - 3;
|
||||
return urlComponents[listIdIndex];
|
||||
}
|
||||
}
|
@ -0,0 +1,186 @@
|
||||
package com.todoroo.astrid.gtasks.api;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import com.google.api.client.extensions.android2.AndroidHttp;
|
||||
import com.google.api.client.googleapis.auth.oauth2.draft10.GoogleAccessProtectedResource;
|
||||
import com.google.api.client.json.jackson.JacksonFactory;
|
||||
import com.google.api.services.tasks.v1.Tasks;
|
||||
import com.google.api.services.tasks.v1.Tasks.TasksOperations.List;
|
||||
import com.google.api.services.tasks.v1.Tasks.TasksOperations.Move;
|
||||
import com.google.api.services.tasks.v1.model.Task;
|
||||
import com.google.api.services.tasks.v1.model.TaskList;
|
||||
import com.google.api.services.tasks.v1.model.TaskLists;
|
||||
|
||||
/**
|
||||
* Wrapper around the official Google Tasks API to simplify common operations. In the case
|
||||
* of an exception, each request is tried twice in case of a timeout.
|
||||
* @author Sam Bosley
|
||||
*
|
||||
*/
|
||||
@SuppressWarnings("nls")
|
||||
public class GtasksService {
|
||||
private Tasks service;
|
||||
|
||||
private static final String API_KEY = "AIzaSyCIYZTBo6haRHxmiplZsfYdagFEpaiFnAk"; //Dummy
|
||||
|
||||
public static final String AUTH_TOKEN_TYPE = "oauth2:https://www.googleapis.com/auth/tasks"; //$NON-NLS-1$
|
||||
|
||||
public GtasksService(String authToken) {
|
||||
try {
|
||||
authenticate(authToken);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public void authenticate(String authToken) throws IOException {
|
||||
|
||||
GoogleAccessProtectedResource accessProtectedResource = new GoogleAccessProtectedResource(authToken);
|
||||
|
||||
service = new Tasks(AndroidHttp.newCompatibleTransport(), accessProtectedResource, new JacksonFactory());
|
||||
service.accessKey = API_KEY;
|
||||
service.setApplicationName("Astrid");
|
||||
}
|
||||
|
||||
public TaskLists allGtaskLists() throws IOException {
|
||||
TaskLists toReturn;
|
||||
try {
|
||||
toReturn = service.tasklists.list().execute();
|
||||
} catch (IOException e) {
|
||||
toReturn = service.tasklists.list().execute();
|
||||
}
|
||||
return toReturn;
|
||||
}
|
||||
|
||||
public TaskList getGtaskList(String id) throws IOException {
|
||||
TaskList toReturn;
|
||||
try {
|
||||
toReturn = service.tasklists.get(id).execute();
|
||||
} catch (IOException e) {
|
||||
toReturn = service.tasklists.get(id).execute();
|
||||
}
|
||||
return toReturn;
|
||||
}
|
||||
|
||||
public TaskList createGtaskList(String title) throws IOException {
|
||||
TaskList newList = new TaskList();
|
||||
newList.title = title;
|
||||
TaskList toReturn;
|
||||
try {
|
||||
toReturn = service.tasklists.insert(newList).execute();
|
||||
} catch (IOException e) {
|
||||
toReturn = service.tasklists.insert(newList).execute();
|
||||
}
|
||||
return toReturn;
|
||||
}
|
||||
|
||||
public TaskList updateGtaskList(TaskList list) throws IOException {
|
||||
TaskList toReturn;
|
||||
try {
|
||||
toReturn = service.tasklists.update(list.id, list).execute();
|
||||
} catch (IOException e) {
|
||||
toReturn = service.tasklists.update(list.id, list).execute();
|
||||
}
|
||||
return toReturn;
|
||||
}
|
||||
|
||||
public void deleteGtaskList(String listId) throws IOException {
|
||||
try {
|
||||
service.tasklists.delete(listId).execute();
|
||||
} catch (IOException e) {
|
||||
service.tasks.clear(listId).execute();
|
||||
}
|
||||
}
|
||||
|
||||
public com.google.api.services.tasks.v1.model.Tasks getAllGtasksFromTaskList(TaskList list, boolean includeDeleted) throws IOException {
|
||||
com.google.api.services.tasks.v1.model.Tasks toReturn;
|
||||
try {
|
||||
toReturn = getAllGtasksFromListId(list.id, includeDeleted);
|
||||
} catch (IOException e) {
|
||||
toReturn = getAllGtasksFromListId(list.id, includeDeleted);
|
||||
}
|
||||
return toReturn;
|
||||
}
|
||||
|
||||
public com.google.api.services.tasks.v1.model.Tasks getAllGtasksFromListId(String listId, boolean includeDeleted) throws IOException {
|
||||
com.google.api.services.tasks.v1.model.Tasks toReturn;
|
||||
List request = service.tasks.list(listId);
|
||||
request.showDeleted = includeDeleted;
|
||||
try {
|
||||
toReturn = request.execute();
|
||||
} catch (IOException e) {
|
||||
toReturn = request.execute();
|
||||
}
|
||||
return toReturn;
|
||||
}
|
||||
|
||||
public Task getGtask(String listId, String taskId) throws IOException {
|
||||
Task toReturn;
|
||||
try {
|
||||
toReturn = service.tasks.get(listId, taskId).execute();
|
||||
} catch (IOException e) {
|
||||
toReturn = service.tasks.get(listId, taskId).execute();
|
||||
}
|
||||
return toReturn;
|
||||
}
|
||||
|
||||
public Task createGtask(String listId, String title, String notes, String due) throws IOException {
|
||||
Task newGtask = new Task();
|
||||
newGtask.title = title;
|
||||
newGtask.notes = notes;
|
||||
newGtask.due = due;
|
||||
|
||||
return createGtask(listId, newGtask);
|
||||
}
|
||||
|
||||
public Task createGtask(String listId, Task task) throws IOException {
|
||||
Task toReturn;
|
||||
try {
|
||||
toReturn = service.tasks.insert(listId, task).execute();
|
||||
} catch (IOException e) {
|
||||
toReturn = service.tasks.insert(listId, task).execute();
|
||||
}
|
||||
return toReturn;
|
||||
}
|
||||
|
||||
public Task updateGtask(String listId, Task task) throws IOException {
|
||||
Task toReturn;
|
||||
try {
|
||||
toReturn = service.tasks.update(listId, task.id, task).execute();
|
||||
} catch (IOException e) {
|
||||
toReturn = service.tasks.update(listId, task.id, task).execute();
|
||||
}
|
||||
return toReturn;
|
||||
}
|
||||
|
||||
public Task moveGtask(String listId, String taskId, String parentId, String previousId) throws IOException {
|
||||
Move move = service.tasks.move(listId, taskId);
|
||||
move.parent = parentId;
|
||||
move.previous = previousId;
|
||||
|
||||
Task toReturn;
|
||||
try {
|
||||
toReturn = move.execute();
|
||||
} catch (IOException e) {
|
||||
toReturn = move.execute();
|
||||
}
|
||||
return toReturn;
|
||||
}
|
||||
|
||||
public void deleteGtask(String listId, String taskId) throws IOException {
|
||||
try {
|
||||
service.tasks.delete(listId, taskId).execute();
|
||||
} catch (IOException e) {
|
||||
service.tasks.delete(listId, taskId).execute();
|
||||
}
|
||||
}
|
||||
|
||||
public void clearCompletedTasks(String listId) throws IOException {
|
||||
try {
|
||||
service.tasks.clear(listId).execute();
|
||||
} catch (IOException e) {
|
||||
service.tasks.clear(listId).execute();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,61 @@
|
||||
package com.todoroo.astrid.gtasks.api;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import com.google.api.services.tasks.v1.model.Task;
|
||||
/**
|
||||
* Encapsulates a request to the api to move a task from one list to another
|
||||
* @author Sam Bosley
|
||||
*
|
||||
*/
|
||||
public class MoveListRequest extends PushRequest {
|
||||
|
||||
private String idTaskToMove;
|
||||
private String dstList;
|
||||
private final String newParent;
|
||||
|
||||
public MoveListRequest(GtasksService service, String idTask, String srcList, String dstList, String newParent) {
|
||||
super(service, srcList, new Task());
|
||||
this.idTaskToMove = idTask;
|
||||
this.dstList = dstList;
|
||||
this.newParent = newParent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Task executePush() throws IOException {
|
||||
Task localSave = service.getGtask(super.getListId(), idTaskToMove);
|
||||
service.deleteGtask(super.getListId(), idTaskToMove);
|
||||
transferProperties(localSave);
|
||||
return service.createGtask(dstList, toPush);
|
||||
}
|
||||
|
||||
private void transferProperties(Task local) {
|
||||
toPush.completed = local.completed;
|
||||
toPush.deleted = local.deleted;
|
||||
toPush.due = local.due;
|
||||
toPush.hidden = local.hidden;
|
||||
toPush.notes = local.notes;
|
||||
toPush.status = local.status;
|
||||
toPush.title = local.title;
|
||||
|
||||
toPush.parent = newParent;
|
||||
}
|
||||
|
||||
public String getIdTaskToMove() {
|
||||
return idTaskToMove;
|
||||
}
|
||||
|
||||
public void setIdTaskToMove(String idTaskToMove) {
|
||||
this.idTaskToMove = idTaskToMove;
|
||||
}
|
||||
|
||||
public String getDstList() {
|
||||
return dstList;
|
||||
}
|
||||
|
||||
public void setDstList(String dstList) {
|
||||
this.dstList = dstList;
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,52 @@
|
||||
package com.todoroo.astrid.gtasks.api;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import com.google.api.services.tasks.v1.model.Task;
|
||||
/**
|
||||
* Encapsulates a request to the api to change the ordering on the given task
|
||||
* @author Sam Bosley
|
||||
*
|
||||
*/
|
||||
public class MoveRequest extends PushRequest {
|
||||
|
||||
private String taskId;
|
||||
private String parentId;
|
||||
private String priorSiblingId;
|
||||
|
||||
public MoveRequest(GtasksService service, String taskId, String destinationList, String parentId, String priorSiblingId) {
|
||||
super(service, destinationList, null);
|
||||
this.taskId = taskId;
|
||||
this.parentId = parentId;
|
||||
this.priorSiblingId = priorSiblingId;
|
||||
}
|
||||
|
||||
public String getTaskId() {
|
||||
return taskId;
|
||||
}
|
||||
|
||||
public void setTaskId(String taskId) {
|
||||
this.taskId = taskId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Task executePush() throws IOException {
|
||||
return service.moveGtask(super.listId, taskId, parentId, priorSiblingId);
|
||||
}
|
||||
|
||||
public String getParentId() {
|
||||
return parentId;
|
||||
}
|
||||
|
||||
public void setParentId(String parentId) {
|
||||
this.parentId = parentId;
|
||||
}
|
||||
|
||||
public String getPriorSiblingId() {
|
||||
return priorSiblingId;
|
||||
}
|
||||
|
||||
public void setPriorSiblingId(String priorSiblingId) {
|
||||
this.priorSiblingId = priorSiblingId;
|
||||
}
|
||||
}
|
@ -0,0 +1,45 @@
|
||||
package com.todoroo.astrid.gtasks.api;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import com.google.api.services.tasks.v1.model.Task;
|
||||
|
||||
/**
|
||||
* Abstract class that encapsulates some push request to the server
|
||||
* @author Sam Bosley
|
||||
*
|
||||
*/
|
||||
public abstract class PushRequest {
|
||||
protected String listId;
|
||||
protected Task toPush;
|
||||
protected GtasksService service;
|
||||
|
||||
public PushRequest(GtasksService service, String listId, Task toPush) {
|
||||
this.service = service;
|
||||
this.listId = listId;
|
||||
this.toPush = toPush;
|
||||
}
|
||||
|
||||
public String getListId() {
|
||||
return listId;
|
||||
}
|
||||
public void setListId(String listId) {
|
||||
this.listId = listId;
|
||||
}
|
||||
|
||||
public Task getToPush() {
|
||||
return toPush;
|
||||
}
|
||||
public void setToPush(Task toPush) {
|
||||
this.toPush = toPush;
|
||||
}
|
||||
|
||||
public GtasksService getService() {
|
||||
return service;
|
||||
}
|
||||
public void setService(GtasksService service) {
|
||||
this.service = service;
|
||||
}
|
||||
|
||||
public abstract Task executePush() throws IOException;
|
||||
}
|
@ -0,0 +1,23 @@
|
||||
package com.todoroo.astrid.gtasks.api;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import com.google.api.services.tasks.v1.model.Task;
|
||||
|
||||
/**
|
||||
* Encapsulates a request to the api to update a task on the remote server
|
||||
* @author Sam Bosley
|
||||
*
|
||||
*/
|
||||
public class UpdateRequest extends PushRequest {
|
||||
|
||||
public UpdateRequest(GtasksService service, String listId, Task toUpdate) {
|
||||
super(service, listId, toUpdate);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Task executePush() throws IOException {
|
||||
return service.updateGtask(listId, toPush);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,42 @@
|
||||
package com.todoroo.astrid.gtasks.auth;
|
||||
|
||||
import android.accounts.Account;
|
||||
import android.accounts.AccountManager;
|
||||
import android.accounts.AccountManagerFuture;
|
||||
import android.os.Bundle;
|
||||
|
||||
import com.google.api.client.googleapis.extensions.android2.auth.GoogleAccountManager;
|
||||
import com.todoroo.andlib.service.ContextManager;
|
||||
import com.todoroo.andlib.utility.Preferences;
|
||||
import com.todoroo.astrid.gtasks.GtasksPreferenceService;
|
||||
import com.todoroo.astrid.gtasks.api.GtasksService;
|
||||
|
||||
public class GtasksTokenValidator {
|
||||
|
||||
private static GoogleAccountManager accountManager = new GoogleAccountManager(ContextManager.getContext());
|
||||
|
||||
/**
|
||||
* Invalidates and then revalidates the auth token for the currently logged in user
|
||||
* @param token
|
||||
* @return
|
||||
*/
|
||||
public static String validateAuthToken(String token) {
|
||||
Account a = accountManager.getAccountByName(Preferences.getStringValue(GtasksPreferenceService.PREF_USER_NAME));
|
||||
if (a == null) return null;
|
||||
|
||||
accountManager.invalidateAuthToken(token);
|
||||
AccountManagerFuture<Bundle> future = accountManager.manager.getAuthToken(a, GtasksService.AUTH_TOKEN_TYPE, true, null, null);
|
||||
|
||||
try {
|
||||
if (future.getResult().containsKey(AccountManager.KEY_AUTHTOKEN)) {
|
||||
Bundle result = future.getResult();
|
||||
return result.getString(AccountManager.KEY_AUTHTOKEN);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,129 @@
|
||||
package com.todoroo.astrid.gtasks.sync;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
|
||||
import com.google.api.services.tasks.v1.model.TaskLists;
|
||||
import com.google.api.services.tasks.v1.model.Tasks;
|
||||
import com.todoroo.andlib.data.TodorooCursor;
|
||||
import com.todoroo.andlib.service.Autowired;
|
||||
import com.todoroo.andlib.service.DependencyInjectionService;
|
||||
import com.todoroo.andlib.sql.Query;
|
||||
import com.todoroo.andlib.utility.Preferences;
|
||||
import com.todoroo.astrid.data.Metadata;
|
||||
import com.todoroo.astrid.data.Task;
|
||||
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.api.GtasksService;
|
||||
import com.todoroo.astrid.service.AstridDependencyInjector;
|
||||
import com.todoroo.astrid.service.MetadataService;
|
||||
import com.todoroo.astrid.service.TaskService;
|
||||
|
||||
/**
|
||||
* Class to handle migration of legacy metadata (old remote ids) to new
|
||||
* metadata based on the official remote ids returned by the api.
|
||||
* @author Sam Bosley
|
||||
*
|
||||
*/
|
||||
public class GtasksLegacyMigrator {
|
||||
|
||||
@Autowired GtasksMetadataService gtasksMetadataService;
|
||||
@Autowired TaskService taskService;
|
||||
@Autowired MetadataService metadataService;
|
||||
@Autowired GtasksListService gtasksListService;
|
||||
|
||||
private static final String MIGRATION_HAS_OCCURRED = "gtasksLegacySyncMigrated"; //$NON-NLS-1$
|
||||
|
||||
private final GtasksService gtasksService;
|
||||
private final GtasksListService listService;
|
||||
private final TaskLists allLists;
|
||||
|
||||
static {
|
||||
AstridDependencyInjector.initialize();
|
||||
}
|
||||
|
||||
public GtasksLegacyMigrator(GtasksService service,GtasksListService listService, TaskLists allLists) {
|
||||
DependencyInjectionService.getInstance().inject(this);
|
||||
this.gtasksService = service;
|
||||
this.listService = listService;
|
||||
this.allLists = allLists;
|
||||
}
|
||||
|
||||
public boolean checkAndMigrateLegacy() {
|
||||
if (!Preferences.getBoolean(MIGRATION_HAS_OCCURRED, false)) {
|
||||
|
||||
listService.migrateListIds(allLists);
|
||||
|
||||
//Fetch all tasks that have associated gtask metadata
|
||||
String defaultListTitle = gtasksListService.getListName(Preferences.getStringValue(GtasksPreferenceService.PREF_DEFAULT_LIST));
|
||||
String defaultListId = null;
|
||||
|
||||
TodorooCursor<Task> allTasksWithGtaskData = taskService.query(Query.select(Task.PROPERTIES).
|
||||
where(Task.ID.in(
|
||||
Query.select(Metadata.TASK).from(Metadata.TABLE).
|
||||
where(Metadata.KEY.eq(GtasksMetadata.METADATA_KEY)))));
|
||||
|
||||
|
||||
|
||||
try {
|
||||
if (allTasksWithGtaskData.getCount() > 0) {
|
||||
//Fetch all remote tasks from all remote lists (this may be an expensive operation)
|
||||
//and map their titles to their real remote ids
|
||||
HashMap<String, String> taskAndListTitlesToRemoteTaskIds = new HashMap<String, String>();
|
||||
|
||||
for (com.google.api.services.tasks.v1.model.TaskList list : allLists.items) {
|
||||
if (list.title.equals(defaultListTitle)) {
|
||||
defaultListId = list.id;
|
||||
}
|
||||
|
||||
Tasks allTasks = gtasksService.getAllGtasksFromListId(list.id, true);
|
||||
|
||||
if (allTasks.items != null) {
|
||||
for (com.google.api.services.tasks.v1.model.Task t : allTasks.items) {
|
||||
String key = constructKeyFromTitles(t.title, list.title);
|
||||
taskAndListTitlesToRemoteTaskIds.put(key, t.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//For each local task, check to see if its title paired with any list title has a match in the map
|
||||
while (!allTasksWithGtaskData.isLast()) {
|
||||
allTasksWithGtaskData.moveToNext();
|
||||
GtasksTaskContainer container = gtasksMetadataService.readTaskAndMetadata(allTasksWithGtaskData);
|
||||
|
||||
//Search through lists to see if one of them has match
|
||||
String taskTitle = container.task.getValue(Task.TITLE);
|
||||
for (com.google.api.services.tasks.v1.model.TaskList list : allLists.items) {
|
||||
String expectedKey = constructKeyFromTitles(taskTitle, list.title);
|
||||
|
||||
if (taskAndListTitlesToRemoteTaskIds.containsKey(expectedKey)) {
|
||||
String newRemoteTaskId = taskAndListTitlesToRemoteTaskIds.get(expectedKey);
|
||||
String newRemoteListId = list.id;
|
||||
|
||||
container.gtaskMetadata.setValue(GtasksMetadata.ID, newRemoteTaskId);
|
||||
container.gtaskMetadata.setValue(GtasksMetadata.LIST_ID, newRemoteListId);
|
||||
gtasksMetadataService.saveTaskAndMetadata(container);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (defaultListId == null) defaultListId = "@default"; //$NON-NLS-1$
|
||||
Preferences.setString(GtasksPreferenceService.PREF_DEFAULT_LIST, defaultListId);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
return false;
|
||||
} finally {
|
||||
allTasksWithGtaskData.close();
|
||||
}
|
||||
Preferences.setBoolean(MIGRATION_HAS_OCCURRED, true); //Record successful migration
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private String constructKeyFromTitles(String taskTitle, String listTitle) {
|
||||
return taskTitle + "//" + listTitle; //$NON-NLS-1$
|
||||
}
|
||||
|
||||
}
|
@ -1,67 +1,14 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<ScrollView
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent"
|
||||
android:background="#ffffff">
|
||||
<LinearLayout
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
<ImageView
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingTop="5dip"
|
||||
android:paddingBottom="5dip"
|
||||
android:scaleType="fitCenter"
|
||||
android:src="@drawable/gtasks_logo" />
|
||||
<TextView
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center"
|
||||
android:paddingBottom="10dip"
|
||||
android:textSize="16sp"
|
||||
android:text="@string/gtasks_GLA_body" />
|
||||
<TextView
|
||||
android:id="@+id/error"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center_horizontal"
|
||||
android:paddingBottom="20dip"
|
||||
android:textColor="#ff0000"
|
||||
android:textSize="16sp"
|
||||
android:textStyle="bold"
|
||||
android:visibility="gone" />
|
||||
<EditText
|
||||
android:id="@+id/email"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="fill_parent"
|
||||
android:hint="@string/gtasks_GLA_email"
|
||||
android:inputType="textEmailAddress" />
|
||||
<EditText
|
||||
android:id="@+id/password"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="fill_parent"
|
||||
android:hint="@string/gtasks_GLA_password"
|
||||
android:inputType="textPassword" />
|
||||
<CheckBox
|
||||
android:id="@+id/isDomain"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="fill_parent"
|
||||
android:text="@string/gtasks_GLA_domain"
|
||||
android:visibility="gone" />
|
||||
<Button
|
||||
android:id="@+id/signIn"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:text="@string/gtasks_GLA_signIn" />
|
||||
<TextView
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center"
|
||||
android:paddingTop="10dip"
|
||||
android:textSize="14sp"
|
||||
android:text="@string/gtasks_GLA_further_help" />
|
||||
</LinearLayout>
|
||||
</ScrollView>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<ListView android:id="@android:id/list"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"/>
|
||||
<TextView android:id="@android:id/empty"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/gtasks_GLA_noaccounts"/>
|
||||
|
||||
</LinearLayout>
|
@ -0,0 +1,265 @@
|
||||
package com.todoroo.astrid.gtasks;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
import android.accounts.Account;
|
||||
import android.accounts.AccountManager;
|
||||
import android.accounts.AccountManagerFuture;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
|
||||
import com.google.api.client.googleapis.extensions.android2.auth.GoogleAccountManager;
|
||||
import com.google.api.services.tasks.v1.model.Task;
|
||||
import com.google.api.services.tasks.v1.model.TaskList;
|
||||
import com.google.api.services.tasks.v1.model.TaskLists;
|
||||
import com.google.api.services.tasks.v1.model.Tasks;
|
||||
import com.todoroo.andlib.service.ContextManager;
|
||||
import com.todoroo.andlib.utility.Preferences;
|
||||
import com.todoroo.astrid.gtasks.api.GtasksApiUtilities;
|
||||
import com.todoroo.astrid.gtasks.api.GtasksService;
|
||||
import com.todoroo.astrid.gtasks.api.MoveListRequest;
|
||||
import com.todoroo.astrid.gtasks.auth.GtasksTokenValidator;
|
||||
import com.todoroo.astrid.test.DatabaseTestCase;
|
||||
|
||||
@SuppressWarnings("nls")
|
||||
public class GtasksApiTest extends DatabaseTestCase {
|
||||
|
||||
private static final String DEFAULT_LIST = "@default";
|
||||
private static final String TEST_ACCOUNT = "sync_tester@astrid.com";
|
||||
private static GtasksService service;
|
||||
private static boolean initialized = false;
|
||||
|
||||
public void testCreateTask() throws Exception {
|
||||
Task newTask = new Task();
|
||||
String title = newTask.title = "New task";
|
||||
|
||||
service.createGtask(DEFAULT_LIST, newTask);
|
||||
assertTrue(taskWithTitleExists(title));
|
||||
}
|
||||
|
||||
public void testUpdateTaskProperties() throws Exception {
|
||||
Task newTask = new Task();
|
||||
String title = newTask.title = "This title will change";
|
||||
|
||||
newTask = service.createGtask(DEFAULT_LIST, newTask);
|
||||
assertTrue(taskWithTitleExists(title));
|
||||
|
||||
String title2 = newTask.title = "Changed Title";
|
||||
service.updateGtask(DEFAULT_LIST, newTask);
|
||||
assertTrue(taskWithTitleExists(title2));
|
||||
assertFalse(taskWithTitleExists(title));
|
||||
}
|
||||
|
||||
public void testTaskDateFormatting() throws Exception {
|
||||
Task newTask = new Task();
|
||||
String title = newTask.title = "Due date will change";
|
||||
|
||||
newTask = service.createGtask(DEFAULT_LIST, newTask);
|
||||
assertTrue(taskWithTitleExists(title));
|
||||
|
||||
long dueTime = new Date(114, 1, 13).getTime();
|
||||
String dueTimeString = GtasksApiUtilities.unixTimeToGtasksTime(dueTime);
|
||||
newTask.due = dueTimeString;
|
||||
newTask = service.updateGtask(DEFAULT_LIST, newTask);
|
||||
assertEquals(dueTimeString, GtasksApiUtilities.gtasksDueTimeStringToLocalTimeString(newTask.due));
|
||||
assertEquals(dueTime, GtasksApiUtilities.gtasksDueTimeToUnixTime(newTask.due, 0));
|
||||
|
||||
long compTime = new Date(115, 2, 14).getTime();
|
||||
String compTimeString = GtasksApiUtilities.unixTimeToGtasksTime(compTime);
|
||||
newTask.completed = compTimeString;
|
||||
newTask.status = "completed";
|
||||
newTask = service.updateGtask(DEFAULT_LIST, newTask);
|
||||
assertEquals(compTimeString, GtasksApiUtilities.gtasksCompletedTimeStringToLocalTimeString(newTask.completed));
|
||||
assertEquals(compTime, GtasksApiUtilities.gtasksCompletedTimeToUnixTime(newTask.completed, 0));
|
||||
}
|
||||
|
||||
public void testTaskDeleted() throws Exception {
|
||||
Task newTask = new Task();
|
||||
String title = newTask.title = "This task will be deleted";
|
||||
|
||||
newTask = service.createGtask(DEFAULT_LIST, newTask);
|
||||
assertTrue(taskWithTitleExists(title));
|
||||
|
||||
service.deleteGtask(DEFAULT_LIST, newTask.id);
|
||||
assertFalse(taskWithTitleExists(title));
|
||||
}
|
||||
|
||||
public void testTaskMoved() throws Exception {
|
||||
Task newTask1 = new Task();
|
||||
String title1 = newTask1.title = "Task 1";
|
||||
Task newTask2 = new Task();
|
||||
String title2 = newTask2.title = "Task 2";
|
||||
|
||||
newTask1 = service.createGtask(DEFAULT_LIST, newTask1);
|
||||
newTask2 = service.createGtask(DEFAULT_LIST, newTask2);
|
||||
|
||||
assertTrue(taskWithTitleExists(title1));
|
||||
assertTrue(taskWithTitleExists(title2));
|
||||
|
||||
System.err.println("Task 1 id: " + newTask1.id);
|
||||
System.err.println("Task 2 id: " + newTask2.id);
|
||||
|
||||
service.moveGtask(DEFAULT_LIST, newTask1.id, newTask2.id, null);
|
||||
newTask1 = service.getGtask(DEFAULT_LIST, newTask1.id);
|
||||
newTask2 = service.getGtask(DEFAULT_LIST, newTask2.id);
|
||||
|
||||
assertEquals(newTask1.parent, newTask2.id);
|
||||
|
||||
service.moveGtask(DEFAULT_LIST, newTask1.id, null, newTask2.id);
|
||||
newTask1 = service.getGtask(DEFAULT_LIST, newTask1.id);
|
||||
newTask2 = service.getGtask(DEFAULT_LIST, newTask2.id);
|
||||
|
||||
assertNull(newTask1.parent);
|
||||
assertTrue(newTask2.position.compareTo(newTask1.position) < 0);
|
||||
}
|
||||
|
||||
public void testMoveBetweenLists() throws Exception {
|
||||
Task newTask = new Task();
|
||||
String title = newTask.title = "This task will move lists";
|
||||
|
||||
newTask = service.createGtask(DEFAULT_LIST, newTask);
|
||||
assertTrue(taskWithTitleExists(title));
|
||||
|
||||
String listTitle = "New list";
|
||||
service.createGtaskList(listTitle);
|
||||
TaskList newList;
|
||||
|
||||
assertNotNull(newList = listWithTitle(listTitle));
|
||||
|
||||
MoveListRequest moveTask = new MoveListRequest(service, newTask.id, DEFAULT_LIST, newList.id, null);
|
||||
moveTask.executePush();
|
||||
|
||||
assertFalse(taskWithTitleExists(title));
|
||||
assertTrue(listHasTaskWithTitle(newList.id, title));
|
||||
}
|
||||
|
||||
private boolean listHasTaskWithTitle(String listId, String title) throws Exception {
|
||||
com.google.api.services.tasks.v1.model.Tasks newListTasks = service.getAllGtasksFromListId(listId, false);
|
||||
if (newListTasks.items != null) {
|
||||
for (Task t : newListTasks.items) {
|
||||
if (t.title.equals(title)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean taskWithTitleExists(String title) throws Exception {
|
||||
Tasks defaultList = service.getAllGtasksFromListId(DEFAULT_LIST, false);
|
||||
if (defaultList.items != null) {
|
||||
for (Task t : defaultList.items) {
|
||||
if (t.title.equals(title))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public void testCreateList() throws Exception {
|
||||
String title1 = "My new list!";
|
||||
service.createGtaskList(title1);
|
||||
assertNotNull(listWithTitle(title1));
|
||||
|
||||
String title2 = "Another new list!";
|
||||
service.createGtaskList("Another new list!");
|
||||
assertNotNull(listWithTitle(title2));
|
||||
assertNotNull(listWithTitle(title1));
|
||||
}
|
||||
|
||||
public void testDeleteList() throws Exception {
|
||||
String title = "This list will be deleted";
|
||||
TaskList t = service.createGtaskList(title);
|
||||
assertNotNull(listWithTitle(title));
|
||||
|
||||
service.deleteGtaskList(t.id);
|
||||
assertNull(listWithTitle(title));
|
||||
}
|
||||
|
||||
public void testUpdateListProperties() throws Exception {
|
||||
String title1 = "This title will change";
|
||||
TaskList t = service.createGtaskList(title1);
|
||||
assertNotNull(listWithTitle(title1));
|
||||
|
||||
String title2 = t.title = "New title";
|
||||
service.updateGtaskList(t);
|
||||
assertNotNull(listWithTitle(title2));
|
||||
assertNull(listWithTitle(title1));
|
||||
}
|
||||
|
||||
private TaskList listWithTitle(String title) throws Exception {
|
||||
TaskLists allLists = service.allGtaskLists();
|
||||
for (TaskList t : allLists.items) {
|
||||
if (t.title.equals(title))
|
||||
return t;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setUp() throws Exception {
|
||||
super.setUp();
|
||||
if (!initialized) {
|
||||
GoogleAccountManager manager = new GoogleAccountManager(ContextManager.getContext());
|
||||
Account[] accounts = manager.getAccounts();
|
||||
Account toUse = null;
|
||||
for (Account a : accounts) {
|
||||
if (a.name.equals(TEST_ACCOUNT)) {
|
||||
toUse = a;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (toUse == null) {
|
||||
toUse = accounts[0];
|
||||
}
|
||||
|
||||
Preferences.setString(GtasksPreferenceService.PREF_USER_NAME, toUse.name);
|
||||
AccountManagerFuture<Bundle> accountManagerFuture = manager.manager.getAuthToken(toUse, GtasksService.AUTH_TOKEN_TYPE, true, null, null);
|
||||
|
||||
Bundle authTokenBundle = accountManagerFuture.getResult();
|
||||
if (authTokenBundle.containsKey(AccountManager.KEY_INTENT)) {
|
||||
Intent i = (Intent) authTokenBundle.get(AccountManager.KEY_INTENT);
|
||||
ContextManager.getContext().startActivity(i);
|
||||
return;
|
||||
}
|
||||
|
||||
String authToken = authTokenBundle.getString(AccountManager.KEY_AUTHTOKEN);
|
||||
authToken = GtasksTokenValidator.validateAuthToken(authToken);
|
||||
|
||||
service = new GtasksService(authToken);
|
||||
|
||||
initialized = true;
|
||||
}
|
||||
deleteAllLists();
|
||||
clearDefaultList();
|
||||
}
|
||||
|
||||
private void deleteAllLists() {
|
||||
try {
|
||||
TaskLists allLists = service.allGtaskLists();
|
||||
for (TaskList t : allLists.items) {
|
||||
if (!t.title.equals("Default List"))
|
||||
service.deleteGtaskList(t.id);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
fail("Failed to clear lists");
|
||||
}
|
||||
}
|
||||
|
||||
private void clearDefaultList() {
|
||||
try {
|
||||
Tasks tasks = service.getAllGtasksFromListId(DEFAULT_LIST, false);
|
||||
if (tasks.items != null) {
|
||||
for (Task t : tasks.items) {
|
||||
service.deleteGtask(DEFAULT_LIST, t.id);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
fail("Failed to clear default list");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,515 @@
|
||||
package com.todoroo.astrid.gtasks;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
import android.accounts.Account;
|
||||
import android.accounts.AccountManager;
|
||||
import android.accounts.AccountManagerFuture;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
|
||||
import com.google.api.client.googleapis.extensions.android2.auth.GoogleAccountManager;
|
||||
import com.google.api.services.tasks.v1.model.Tasks;
|
||||
import com.todoroo.andlib.data.TodorooCursor;
|
||||
import com.todoroo.andlib.service.Autowired;
|
||||
import com.todoroo.andlib.service.ContextManager;
|
||||
import com.todoroo.andlib.sql.Criterion;
|
||||
import com.todoroo.andlib.sql.Query;
|
||||
import com.todoroo.andlib.utility.AndroidUtilities;
|
||||
import com.todoroo.andlib.utility.DateUtilities;
|
||||
import com.todoroo.andlib.utility.Preferences;
|
||||
import com.todoroo.astrid.data.Metadata;
|
||||
import com.todoroo.astrid.data.Task;
|
||||
import com.todoroo.astrid.gtasks.api.GtasksApiUtilities;
|
||||
import com.todoroo.astrid.gtasks.api.GtasksService;
|
||||
import com.todoroo.astrid.gtasks.auth.GtasksTokenValidator;
|
||||
import com.todoroo.astrid.gtasks.sync.GtasksSyncProvider;
|
||||
import com.todoroo.astrid.service.MetadataService;
|
||||
import com.todoroo.astrid.service.TaskService;
|
||||
import com.todoroo.astrid.test.DatabaseTestCase;
|
||||
|
||||
@SuppressWarnings("nls")
|
||||
public class GtasksNewSyncTest extends DatabaseTestCase {
|
||||
|
||||
private static GtasksService gtasksService;
|
||||
private GtasksSyncProvider syncProvider;
|
||||
private static boolean initialized = false;
|
||||
|
||||
private static String DEFAULT_LIST = "@default";
|
||||
private static final String TEST_ACCOUNT = "sync_tester@astrid.com";
|
||||
private static final long TIME_BETWEEN_SYNCS = 3000l;
|
||||
|
||||
@Autowired TaskService taskService;
|
||||
@Autowired MetadataService metadataService;
|
||||
@Autowired GtasksMetadataService gtasksMetadataService;
|
||||
@Autowired GtasksPreferenceService gtasksPreferenceService;
|
||||
|
||||
/*
|
||||
* Basic creation tests
|
||||
*/
|
||||
public void testTaskCreatedLocally() {
|
||||
String title = "Astrid task 1";
|
||||
Task localTask = createNewLocalTask(title);
|
||||
|
||||
whenInvokeSync();
|
||||
|
||||
assertTaskExistsRemotely(localTask, title);
|
||||
}
|
||||
|
||||
public void testTaskCreatedRemotely() throws Exception {
|
||||
String title = "Gtasks task 1";
|
||||
com.google.api.services.tasks.v1.model.Task remoteTask = new com.google.api.services.tasks.v1.model.Task();
|
||||
remoteTask.title = title;
|
||||
remoteTask = gtasksService.createGtask(DEFAULT_LIST, remoteTask);
|
||||
|
||||
whenInvokeSync();
|
||||
|
||||
assertTaskExistsLocally(remoteTask, title);
|
||||
}
|
||||
|
||||
/*
|
||||
* Title editing tests
|
||||
*/
|
||||
public void testTitleChangedLocally() throws Exception {
|
||||
String title = "Astrid task 2";
|
||||
Task localTask = createNewLocalTask(title);
|
||||
|
||||
whenInvokeSync();
|
||||
|
||||
com.google.api.services.tasks.v1.model.Task remoteTask = assertTaskExistsRemotely(localTask, title);
|
||||
AndroidUtilities.sleepDeep(TIME_BETWEEN_SYNCS);
|
||||
|
||||
//Set new title on local task
|
||||
String newTitle = "Astrid task 2 edited";
|
||||
localTask.setValue(Task.TITLE, newTitle);
|
||||
taskService.save(localTask);
|
||||
|
||||
whenInvokeSync();
|
||||
|
||||
//Refetch remote task and assert that both local and remote titles match expected
|
||||
localTask = refetchLocalTask(localTask);
|
||||
remoteTask = refetchRemoteTask(remoteTask);
|
||||
assertEquals(newTitle, localTask.getValue(Task.TITLE));
|
||||
assertEquals(newTitle, remoteTask.title);
|
||||
}
|
||||
|
||||
public void testTitleChangedRemotely() throws Exception {
|
||||
String title = "Astrid task 3";
|
||||
Task localTask = createNewLocalTask(title);
|
||||
|
||||
whenInvokeSync();
|
||||
|
||||
com.google.api.services.tasks.v1.model.Task remoteTask = assertTaskExistsRemotely(localTask, title);
|
||||
AndroidUtilities.sleepDeep(TIME_BETWEEN_SYNCS);
|
||||
|
||||
//Set new title on remote task
|
||||
String newRemoteTitle = "Task 3 edited on gtasks";
|
||||
remoteTask.title = newRemoteTitle;
|
||||
gtasksService.updateGtask(DEFAULT_LIST, remoteTask);
|
||||
|
||||
whenInvokeSync();
|
||||
|
||||
//Refetch local/remote tasks, assert that both titles match expected
|
||||
remoteTask = refetchRemoteTask(remoteTask);
|
||||
localTask = refetchLocalTask(localTask);
|
||||
assertEquals(newRemoteTitle, remoteTask.title);
|
||||
assertEquals(newRemoteTitle, localTask.getValue(Task.TITLE));
|
||||
}
|
||||
|
||||
public void testDateChangedLocally() throws Exception {
|
||||
Task localTask = createLocalTaskForDateTests(" locally");
|
||||
String title = localTask.getValue(Task.TITLE);
|
||||
long startDate = localTask.getValue(Task.DUE_DATE);
|
||||
|
||||
whenInvokeSync();
|
||||
com.google.api.services.tasks.v1.model.Task remoteTask = assertTaskExistsRemotely(localTask, title);
|
||||
localTask = refetchLocalTask(localTask);
|
||||
assertEquals(startDate, localTask.getValue(Task.DUE_DATE).longValue());
|
||||
assertEquals(startDate, GtasksApiUtilities.gtasksDueTimeToUnixTime(remoteTask.due, 0));
|
||||
AndroidUtilities.sleepDeep(TIME_BETWEEN_SYNCS);
|
||||
|
||||
//Set new due date on local task
|
||||
long newDueDate = new Date(116, 1, 8).getTime();
|
||||
localTask.setValue(Task.DUE_DATE, newDueDate);
|
||||
taskService.save(localTask);
|
||||
|
||||
whenInvokeSync();
|
||||
|
||||
//Refetch remote task and assert that both tasks match expected due date
|
||||
localTask = refetchLocalTask(localTask);
|
||||
remoteTask = refetchRemoteTask(remoteTask);
|
||||
assertEquals(newDueDate, localTask.getValue(Task.DUE_DATE).longValue());
|
||||
assertEquals(newDueDate, GtasksApiUtilities.gtasksDueTimeToUnixTime(remoteTask.due, 0));
|
||||
}
|
||||
|
||||
public void testDateChangedRemotely() throws Exception {
|
||||
Task localTask = createLocalTaskForDateTests(" remotely");
|
||||
String title = localTask.getValue(Task.TITLE);
|
||||
long startDate = localTask.getValue(Task.DUE_DATE);
|
||||
|
||||
whenInvokeSync();
|
||||
com.google.api.services.tasks.v1.model.Task remoteTask = assertTaskExistsRemotely(localTask, title);
|
||||
localTask = refetchLocalTask(localTask);
|
||||
assertEquals(startDate, localTask.getValue(Task.DUE_DATE).longValue());
|
||||
assertEquals(startDate, GtasksApiUtilities.gtasksDueTimeToUnixTime(remoteTask.due, 0));
|
||||
AndroidUtilities.sleepDeep(TIME_BETWEEN_SYNCS);
|
||||
|
||||
//Set new due date on remote task
|
||||
long newDueDate = new Date(116, 1, 8).getTime();
|
||||
remoteTask.due = GtasksApiUtilities.unixTimeToGtasksTime(newDueDate);
|
||||
gtasksService.updateGtask(DEFAULT_LIST, remoteTask);
|
||||
|
||||
whenInvokeSync();
|
||||
|
||||
//Refetch remote task and assert that both tasks match expected due date
|
||||
localTask = refetchLocalTask(localTask);
|
||||
remoteTask = refetchRemoteTask(remoteTask);
|
||||
assertEquals(newDueDate, localTask.getValue(Task.DUE_DATE).longValue());
|
||||
assertEquals(newDueDate, GtasksApiUtilities.gtasksDueTimeToUnixTime(remoteTask.due, 0));
|
||||
|
||||
}
|
||||
|
||||
public void testDateChangedBoth_ChooseLocal() throws Exception {
|
||||
Task localTask = createLocalTaskForDateTests(" remotely");
|
||||
String title = localTask.getValue(Task.TITLE);
|
||||
long startDate = localTask.getValue(Task.DUE_DATE);
|
||||
|
||||
whenInvokeSync();
|
||||
com.google.api.services.tasks.v1.model.Task remoteTask = assertTaskExistsRemotely(localTask, title);
|
||||
localTask = refetchLocalTask(localTask);
|
||||
assertEquals(startDate, localTask.getValue(Task.DUE_DATE).longValue());
|
||||
assertEquals(startDate, GtasksApiUtilities.gtasksDueTimeToUnixTime(remoteTask.due, 0));
|
||||
AndroidUtilities.sleepDeep(TIME_BETWEEN_SYNCS);
|
||||
|
||||
//Set new due date on remote task first
|
||||
long newLocalDate = new Date(128, 5, 11).getTime();
|
||||
long newRemoteDate = new Date(121, 5, 25).getTime();
|
||||
|
||||
remoteTask.due = GtasksApiUtilities.unixTimeToGtasksTime(newRemoteDate);
|
||||
gtasksService.updateGtask(DEFAULT_LIST, remoteTask);
|
||||
|
||||
AndroidUtilities.sleepDeep(TIME_BETWEEN_SYNCS);
|
||||
|
||||
localTask.setValue(Task.DUE_DATE, newLocalDate);
|
||||
taskService.save(localTask);
|
||||
|
||||
whenInvokeSync();
|
||||
|
||||
//Refetch both and assert that due dates match the one we set to local (more recent)
|
||||
localTask = refetchLocalTask(localTask);
|
||||
remoteTask = refetchRemoteTask(remoteTask);
|
||||
assertEquals(newLocalDate, localTask.getValue(Task.DUE_DATE).longValue());
|
||||
assertEquals(newLocalDate, GtasksApiUtilities.gtasksDueTimeToUnixTime(remoteTask.due, 0));
|
||||
}
|
||||
|
||||
public void DISABLED_testDateChangedBoth_ChooseRemote() throws Exception {
|
||||
Task localTask = createLocalTaskForDateTests(" remotely");
|
||||
String title = localTask.getValue(Task.TITLE);
|
||||
long startDate = localTask.getValue(Task.DUE_DATE);
|
||||
|
||||
whenInvokeSync();
|
||||
com.google.api.services.tasks.v1.model.Task remoteTask = assertTaskExistsRemotely(localTask, title);
|
||||
localTask = refetchLocalTask(localTask);
|
||||
assertEquals(startDate, localTask.getValue(Task.DUE_DATE).longValue());
|
||||
assertEquals(startDate, GtasksApiUtilities.gtasksDueTimeToUnixTime(remoteTask.due, 0));
|
||||
AndroidUtilities.sleepDeep(TIME_BETWEEN_SYNCS);
|
||||
|
||||
|
||||
//Set new due date on local task first
|
||||
long newLocalDate = new Date(128, 5, 11).getTime();
|
||||
long newRemoteDate = new Date(121, 5, 25).getTime();
|
||||
|
||||
localTask.setValue(Task.DUE_DATE, newLocalDate);
|
||||
taskService.save(localTask);
|
||||
|
||||
AndroidUtilities.sleepDeep(TIME_BETWEEN_SYNCS);
|
||||
|
||||
remoteTask.due = GtasksApiUtilities.unixTimeToGtasksTime(newRemoteDate);
|
||||
gtasksService.updateGtask(DEFAULT_LIST, remoteTask);
|
||||
|
||||
whenInvokeSync();
|
||||
|
||||
//Refetch both and assert that due dates match the one we set to local (more recent)
|
||||
localTask = refetchLocalTask(localTask);
|
||||
remoteTask = refetchRemoteTask(remoteTask);
|
||||
assertEquals(newLocalDate, localTask.getValue(Task.DUE_DATE).longValue());
|
||||
assertEquals(newLocalDate, GtasksApiUtilities.gtasksDueTimeToUnixTime(remoteTask.due, 0));
|
||||
}
|
||||
|
||||
/*
|
||||
* Helper method for due date tests
|
||||
*/
|
||||
private Task createLocalTaskForDateTests(String addToTitle) {
|
||||
Task localTask = createNewLocalTask("Due date will change" + addToTitle);
|
||||
long dueDate = new Date(115, 2, 14).getTime();
|
||||
localTask.setValue(Task.DUE_DATE, dueDate);
|
||||
taskService.save(localTask);
|
||||
|
||||
return localTask;
|
||||
}
|
||||
|
||||
public void testNoteEditedLocally() throws Exception {
|
||||
Task localTask = createLocalTaskForNoteTests(" locally");
|
||||
String title = localTask.getValue(Task.TITLE);
|
||||
String originalNote = localTask.getValue(Task.NOTES);
|
||||
|
||||
whenInvokeSync();
|
||||
|
||||
com.google.api.services.tasks.v1.model.Task remoteTask = assertTaskExistsRemotely(localTask, title);
|
||||
assertEquals(originalNote, localTask.getValue(Task.NOTES));
|
||||
assertEquals(originalNote, remoteTask.notes);
|
||||
|
||||
AndroidUtilities.sleepDeep(TIME_BETWEEN_SYNCS);
|
||||
|
||||
String newNote = "New local note";
|
||||
localTask.setValue(Task.NOTES, newNote);
|
||||
taskService.save(localTask);
|
||||
|
||||
whenInvokeSync();
|
||||
|
||||
localTask = refetchLocalTask(localTask);
|
||||
remoteTask = refetchRemoteTask(remoteTask);
|
||||
assertEquals(newNote, localTask.getValue(Task.NOTES));
|
||||
assertEquals(newNote, remoteTask.notes);
|
||||
}
|
||||
|
||||
public void testNoteEditedRemotely() throws Exception {
|
||||
Task localTask = createLocalTaskForNoteTests(" remotely");
|
||||
String title = localTask.getValue(Task.TITLE);
|
||||
String originalNote = localTask.getValue(Task.NOTES);
|
||||
|
||||
whenInvokeSync();
|
||||
|
||||
com.google.api.services.tasks.v1.model.Task remoteTask = assertTaskExistsRemotely(localTask, title);
|
||||
assertEquals(originalNote, localTask.getValue(Task.NOTES));
|
||||
assertEquals(originalNote, remoteTask.notes);
|
||||
|
||||
AndroidUtilities.sleepDeep(TIME_BETWEEN_SYNCS);
|
||||
|
||||
String newNote = "New remote note";
|
||||
remoteTask.notes = newNote;
|
||||
gtasksService.updateGtask(DEFAULT_LIST, remoteTask);
|
||||
|
||||
whenInvokeSync();
|
||||
|
||||
localTask = refetchLocalTask(localTask);
|
||||
remoteTask = refetchRemoteTask(remoteTask);
|
||||
assertEquals(newNote, localTask.getValue(Task.NOTES));
|
||||
assertEquals(newNote, remoteTask.notes);
|
||||
}
|
||||
|
||||
public void DISABLED_testNoteEditedBoth() throws Exception {
|
||||
Task localTask = createLocalTaskForNoteTests(" remotely");
|
||||
String title = localTask.getValue(Task.TITLE);
|
||||
String originalNote = localTask.getValue(Task.NOTES);
|
||||
|
||||
whenInvokeSync();
|
||||
|
||||
com.google.api.services.tasks.v1.model.Task remoteTask = assertTaskExistsRemotely(localTask, title);
|
||||
assertEquals(originalNote, localTask.getValue(Task.NOTES));
|
||||
assertEquals(originalNote, remoteTask.notes);
|
||||
|
||||
AndroidUtilities.sleepDeep(TIME_BETWEEN_SYNCS);
|
||||
|
||||
String newLocalNote = "New local note";
|
||||
String newRemoteNote = "New remote note";
|
||||
|
||||
localTask.setValue(Task.NOTES, newLocalNote);
|
||||
taskService.save(localTask);
|
||||
|
||||
AndroidUtilities.sleepDeep(TIME_BETWEEN_SYNCS);
|
||||
|
||||
remoteTask.notes = newRemoteNote;
|
||||
gtasksService.updateGtask(DEFAULT_LIST, remoteTask);
|
||||
|
||||
whenInvokeSync();
|
||||
|
||||
localTask = refetchLocalTask(localTask);
|
||||
remoteTask = refetchRemoteTask(remoteTask);
|
||||
System.err.println("Local note: " + localTask.getValue(Task.NOTES));
|
||||
System.err.println("Remote note: " + remoteTask.notes);
|
||||
}
|
||||
|
||||
private Task createLocalTaskForNoteTests(String addToTitle) {
|
||||
Task localTask = createNewLocalTask("Note will change" + addToTitle);
|
||||
String note = "Original note";
|
||||
localTask.setValue(Task.NOTES, note);
|
||||
taskService.save(localTask);
|
||||
|
||||
return localTask;
|
||||
}
|
||||
|
||||
/*
|
||||
* Completion tests
|
||||
*/
|
||||
|
||||
public void testTaskCompletedLocally() throws Exception {
|
||||
String title = "Will complete locally";
|
||||
Task localTask = createNewLocalTask(title);
|
||||
|
||||
whenInvokeSync();
|
||||
com.google.api.services.tasks.v1.model.Task remoteTask = assertTaskExistsRemotely(localTask, title);
|
||||
|
||||
AndroidUtilities.sleepDeep(TIME_BETWEEN_SYNCS);
|
||||
|
||||
long completion = DateUtilities.now();
|
||||
localTask.setValue(Task.COMPLETION_DATE, completion);
|
||||
taskService.save(localTask);
|
||||
|
||||
whenInvokeSync();
|
||||
|
||||
localTask = refetchLocalTask(localTask);
|
||||
remoteTask = refetchRemoteTask(remoteTask);
|
||||
assertEquals(completion, localTask.getValue(Task.COMPLETION_DATE).longValue());
|
||||
assertEquals(GtasksApiUtilities.unixTimeToGtasksTime(completion), GtasksApiUtilities.gtasksCompletedTimeStringToLocalTimeString(remoteTask.completed));
|
||||
assertEquals("completed", remoteTask.status);
|
||||
}
|
||||
|
||||
public void testTaskCompletedRemotely() throws Exception {
|
||||
String title = "Will complete remotely";
|
||||
Task localTask = createNewLocalTask(title);
|
||||
|
||||
whenInvokeSync();
|
||||
com.google.api.services.tasks.v1.model.Task remoteTask = assertTaskExistsRemotely(localTask, title);
|
||||
|
||||
AndroidUtilities.sleepDeep(TIME_BETWEEN_SYNCS);
|
||||
|
||||
long completion = DateUtilities.now();
|
||||
remoteTask.status = "completed";
|
||||
remoteTask.completed = GtasksApiUtilities.unixTimeToGtasksTime(completion);
|
||||
gtasksService.updateGtask(DEFAULT_LIST, remoteTask);
|
||||
|
||||
whenInvokeSync();
|
||||
|
||||
localTask = refetchLocalTask(localTask);
|
||||
remoteTask = refetchRemoteTask(remoteTask);
|
||||
assertEquals(completion, localTask.getValue(Task.COMPLETION_DATE).longValue());
|
||||
assertEquals(GtasksApiUtilities.unixTimeToGtasksTime(completion), GtasksApiUtilities.gtasksCompletedTimeStringToLocalTimeString(remoteTask.completed));
|
||||
assertEquals("completed", remoteTask.status);
|
||||
}
|
||||
|
||||
private com.google.api.services.tasks.v1.model.Task assertTaskExistsRemotely(Task localTask, String title) {
|
||||
//Get the corresponding remote id for a local task
|
||||
Metadata metadata = gtasksMetadataService.getTaskMetadata(localTask.getId());
|
||||
String taskId = metadata.getValue(GtasksMetadata.ID);
|
||||
String listId = metadata.getValue(GtasksMetadata.LIST_ID);
|
||||
|
||||
//Fetch the remote task belonging to that id
|
||||
com.google.api.services.tasks.v1.model.Task remoteTask = null;
|
||||
try {
|
||||
remoteTask = gtasksService.getGtask(listId, taskId);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
fail("Failed to find remote task " + taskId);
|
||||
}
|
||||
|
||||
//Do a basic title match
|
||||
assertNotNull(remoteTask);
|
||||
assertEquals(title, localTask.getValue(Task.TITLE));
|
||||
assertEquals(title, remoteTask.title);
|
||||
return remoteTask;
|
||||
}
|
||||
|
||||
private Task assertTaskExistsLocally(com.google.api.services.tasks.v1.model.Task remoteTask, String title) {
|
||||
long localId = localIdForTask(remoteTask);
|
||||
|
||||
//Fetch the local task from the database
|
||||
Task localTask = taskService.fetchById(localId, Task.PROPERTIES);
|
||||
|
||||
assertNotNull(localTask);
|
||||
assertEquals(title, remoteTask.title);
|
||||
assertEquals(title, localTask.getValue(Task.TITLE));
|
||||
return localTask;
|
||||
}
|
||||
|
||||
private Task refetchLocalTask(Task localTask) {
|
||||
return taskService.fetchById(localTask.getValue(Task.ID), Task.PROPERTIES);
|
||||
}
|
||||
|
||||
private com.google.api.services.tasks.v1.model.Task refetchRemoteTask(com.google.api.services.tasks.v1.model.Task remoteTask) throws Exception {
|
||||
return gtasksService.getGtask(GtasksApiUtilities.extractListIdFromSelfLink(remoteTask), remoteTask.id);
|
||||
}
|
||||
|
||||
private long localIdForTask(com.google.api.services.tasks.v1.model.Task remoteTask) {
|
||||
TodorooCursor<Metadata> cursor = metadataService.query(Query.select(Metadata.TASK).
|
||||
where(Criterion.and(Metadata.KEY.eq(GtasksMetadata.METADATA_KEY), GtasksMetadata.ID.eq(remoteTask.id))));
|
||||
try {
|
||||
assertEquals(1, cursor.getCount());
|
||||
|
||||
cursor.moveToFirst();
|
||||
return cursor.get(Metadata.TASK);
|
||||
} finally {
|
||||
cursor.close();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//Create a new Astrid task and save it to the database
|
||||
private Task createNewLocalTask(String title) {
|
||||
Task task = new Task();
|
||||
task.setValue(Task.TITLE, title);
|
||||
taskService.save(task);
|
||||
return task;
|
||||
}
|
||||
|
||||
//Perform a synchronization
|
||||
private void whenInvokeSync() {
|
||||
syncProvider.synchronize(getContext());
|
||||
gtasksService = syncProvider.getGtasksService(); //This is to prevent token mismatch; the sync provider invalidates the old one
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setUp() throws Exception {
|
||||
super.setUp();
|
||||
|
||||
if (!initialized) {
|
||||
initializeTestService();
|
||||
}
|
||||
|
||||
setupTestList();
|
||||
|
||||
syncProvider = new GtasksSyncProvider();
|
||||
}
|
||||
|
||||
private void initializeTestService() throws Exception {
|
||||
GoogleAccountManager manager = new GoogleAccountManager(ContextManager.getContext());
|
||||
Account[] accounts = manager.getAccounts();
|
||||
|
||||
Account toUse = null;
|
||||
for (Account a : accounts) {
|
||||
if (a.name.equals(TEST_ACCOUNT)) {
|
||||
toUse = a;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (toUse == null) {
|
||||
toUse = accounts[0];
|
||||
}
|
||||
|
||||
Preferences.setString(GtasksPreferenceService.PREF_USER_NAME, toUse.name);
|
||||
AccountManagerFuture<Bundle> accountManagerFuture = manager.manager.getAuthToken(toUse, "oauth2:https://www.googleapis.com/auth/tasks", true, null, null);
|
||||
|
||||
Bundle authTokenBundle = accountManagerFuture.getResult();
|
||||
if (authTokenBundle.containsKey(AccountManager.KEY_INTENT)) {
|
||||
Intent i = (Intent) authTokenBundle.get(AccountManager.KEY_INTENT);
|
||||
ContextManager.getContext().startActivity(i);
|
||||
return;
|
||||
}
|
||||
String authToken = authTokenBundle.getString(AccountManager.KEY_AUTHTOKEN);
|
||||
authToken = GtasksTokenValidator.validateAuthToken(authToken);
|
||||
|
||||
gtasksService = new GtasksService(authToken);
|
||||
|
||||
initialized = true;
|
||||
}
|
||||
|
||||
private void setupTestList() throws Exception {
|
||||
Tasks defaultListTasks = gtasksService.getAllGtasksFromListId(DEFAULT_LIST, false);
|
||||
if (defaultListTasks.items != null) {
|
||||
for (com.google.api.services.tasks.v1.model.Task t : defaultListTasks.items) {
|
||||
gtasksService.deleteGtask(DEFAULT_LIST, t.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue