mirror of https://github.com/tasks/tasks
Merge branch 'gtasks'
Conflicts: astrid/src/com/todoroo/astrid/service/AstridDependencyInjector.javapull/14/head
commit
bfc7655d6d
Binary file not shown.
@ -0,0 +1,162 @@
|
||||
package com.todoroo.astrid.gtasks;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Stack;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
import android.text.TextUtils;
|
||||
|
||||
import com.todoroo.andlib.data.TodorooCursor;
|
||||
import com.todoroo.andlib.service.Autowired;
|
||||
import com.todoroo.andlib.service.DependencyInjectionService;
|
||||
import com.todoroo.astrid.api.Filter;
|
||||
import com.todoroo.astrid.core.PluginServices;
|
||||
import com.todoroo.astrid.data.Metadata;
|
||||
import com.todoroo.astrid.data.StoreObject;
|
||||
import com.todoroo.astrid.data.Task;
|
||||
import com.todoroo.astrid.gtasks.sync.GtasksTaskContainer;
|
||||
|
||||
public class GtasksTaskListUpdater {
|
||||
|
||||
@Autowired private GtasksListService gtasksListService;
|
||||
@Autowired private GtasksMetadataService gtasksMetadataService;
|
||||
|
||||
final HashMap<Long, Long> parents = new HashMap<Long, Long>();
|
||||
final HashMap<Long, Long> siblings = new HashMap<Long, Long>();
|
||||
final HashMap<Long, String> localToRemoteIdMap =
|
||||
new HashMap<Long, String>();
|
||||
|
||||
public GtasksTaskListUpdater() {
|
||||
DependencyInjectionService.getInstance().inject(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update order and parent fields for all tasks in the given list
|
||||
* @param listId
|
||||
*/
|
||||
public void updateMetadataForList(String listId) {
|
||||
StoreObject list = gtasksListService.getList(listId);
|
||||
if(list == GtasksListService.LIST_NOT_FOUND_OBJECT)
|
||||
return;
|
||||
|
||||
final ArrayList<Long> ids = new ArrayList<Long>();
|
||||
final Stack<Long> taskHierarchyStack = new Stack<Long>();
|
||||
|
||||
final AtomicInteger order = new AtomicInteger(0);
|
||||
final AtomicInteger previousIndent = new AtomicInteger(-1);
|
||||
|
||||
iterateThroughList(list, new ListIterator() {
|
||||
@Override
|
||||
public void processTask(long taskId) {
|
||||
ids.add(taskId);
|
||||
Metadata metadata = gtasksMetadataService.getTaskMetadata(taskId);
|
||||
if(metadata == null)
|
||||
return;
|
||||
|
||||
metadata.setValue(GtasksMetadata.ORDER, order.getAndAdd(1));
|
||||
int indent = metadata.getValue(GtasksMetadata.INDENT);
|
||||
|
||||
for(int i = indent; i <= previousIndent.get(); i++) {
|
||||
if(!taskHierarchyStack.isEmpty())
|
||||
taskHierarchyStack.pop();
|
||||
}
|
||||
|
||||
if(indent > 0) {
|
||||
if(taskHierarchyStack.isEmpty()) {
|
||||
metadata.setValue(GtasksMetadata.PARENT_TASK, 0L);
|
||||
metadata.setValue(GtasksMetadata.INDENT, 0);
|
||||
} else
|
||||
metadata.setValue(GtasksMetadata.PARENT_TASK, taskHierarchyStack.peek());
|
||||
} else {
|
||||
metadata.setValue(GtasksMetadata.PARENT_TASK, 0L);
|
||||
}
|
||||
|
||||
PluginServices.getMetadataService().save(metadata);
|
||||
taskHierarchyStack.push(taskId);
|
||||
previousIndent.set(indent);
|
||||
}
|
||||
});
|
||||
|
||||
PluginServices.getTaskService().clearDetails(Task.ID.in(ids.toArray(new Long[ids.size()])));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a local tree of tasks to expedite sibling and parent lookups
|
||||
*/
|
||||
public void createParentSiblingMaps() {
|
||||
for(StoreObject list : gtasksListService.getLists()) {
|
||||
final AtomicLong previousTask = new AtomicLong(-1L);
|
||||
final AtomicInteger previousIndent = new AtomicInteger(-1);
|
||||
|
||||
iterateThroughList(list, new ListIterator() {
|
||||
@Override
|
||||
public void processTask(long taskId) {
|
||||
Metadata metadata = gtasksMetadataService.getTaskMetadata(taskId);
|
||||
if(metadata == null)
|
||||
return;
|
||||
|
||||
int indent = metadata.getValue(GtasksMetadata.INDENT);
|
||||
|
||||
long parent, sibling;
|
||||
if(indent > previousIndent.get()) {
|
||||
parent = previousTask.get();
|
||||
sibling = -1L;
|
||||
} else if(indent == previousIndent.get()) {
|
||||
sibling = previousTask.get();
|
||||
parent = parents.get(sibling);
|
||||
} else {
|
||||
// move up once for each indent
|
||||
sibling = previousTask.get();
|
||||
for(int i = indent; i < previousIndent.get(); i++)
|
||||
sibling = parents.get(sibling);
|
||||
parent = parents.get(sibling);
|
||||
}
|
||||
parents.put(taskId, parent);
|
||||
siblings.put(taskId, sibling);
|
||||
|
||||
previousTask.set(taskId);
|
||||
previousIndent.set(indent);
|
||||
if(!TextUtils.isEmpty(metadata.getValue(GtasksMetadata.ID)))
|
||||
localToRemoteIdMap.put(taskId, metadata.getValue(GtasksMetadata.ID));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public void updateParentAndSibling(GtasksTaskContainer container) {
|
||||
long taskId = container.task.getId();
|
||||
if(parents.containsKey(taskId)) {
|
||||
long parentId = parents.get(taskId);
|
||||
if(localToRemoteIdMap.containsKey(parentId))
|
||||
container.parentId = localToRemoteIdMap.get(parentId);
|
||||
}
|
||||
if(siblings.containsKey(taskId)) {
|
||||
long siblingId = siblings.get(taskId);
|
||||
if(localToRemoteIdMap.containsKey(siblingId))
|
||||
container.priorSiblingId = localToRemoteIdMap.get(siblingId);
|
||||
}
|
||||
}
|
||||
|
||||
// --- private helpers
|
||||
|
||||
private interface ListIterator {
|
||||
public void processTask(long taskId);
|
||||
}
|
||||
|
||||
private void iterateThroughList(StoreObject list, ListIterator iterator) {
|
||||
Filter filter = GtasksFilterExposer.filterFromList(list);
|
||||
TodorooCursor<Task> cursor = PluginServices.getTaskService().fetchFiltered(filter.sqlQuery, null, Task.ID);
|
||||
try {
|
||||
for(cursor.moveToFirst(); !cursor.isAfterLast(); cursor.moveToNext()) {
|
||||
long taskId = cursor.getLong(0);
|
||||
iterator.processTask(taskId);
|
||||
}
|
||||
|
||||
} finally {
|
||||
cursor.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,497 @@
|
||||
/**
|
||||
* See the file "LICENSE" for the full license governing this code.
|
||||
*/
|
||||
package com.todoroo.astrid.gtasks.sync;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
|
||||
import org.json.JSONException;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.Notification;
|
||||
import android.app.PendingIntent;
|
||||
import android.app.Service;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
|
||||
import com.flurry.android.FlurryAgent;
|
||||
import com.timsu.astrid.R;
|
||||
import com.todoroo.andlib.data.AbstractModel;
|
||||
import com.todoroo.andlib.data.Property;
|
||||
import com.todoroo.andlib.data.TodorooCursor;
|
||||
import com.todoroo.andlib.service.Autowired;
|
||||
import com.todoroo.andlib.service.ContextManager;
|
||||
import com.todoroo.andlib.service.DependencyInjectionService;
|
||||
import com.todoroo.andlib.service.ExceptionService;
|
||||
import com.todoroo.andlib.utility.AndroidUtilities;
|
||||
import com.todoroo.andlib.utility.DateUtilities;
|
||||
import com.todoroo.andlib.utility.DialogUtilities;
|
||||
import com.todoroo.astrid.data.Metadata;
|
||||
import com.todoroo.astrid.data.StoreObject;
|
||||
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.GtasksPreferences;
|
||||
import com.todoroo.astrid.gtasks.GtasksTaskListUpdater;
|
||||
import com.todoroo.astrid.producteev.ProducteevBackgroundService;
|
||||
import com.todoroo.astrid.producteev.ProducteevLoginActivity;
|
||||
import com.todoroo.astrid.producteev.ProducteevUtilities;
|
||||
import com.todoroo.astrid.producteev.api.ApiServiceException;
|
||||
import com.todoroo.astrid.service.AstridDependencyInjector;
|
||||
import com.todoroo.astrid.sync.SyncContainer;
|
||||
import com.todoroo.astrid.sync.SyncProvider;
|
||||
import com.todoroo.astrid.utility.Constants;
|
||||
import com.todoroo.astrid.utility.Preferences;
|
||||
import com.todoroo.gtasks.GoogleTaskService;
|
||||
import com.todoroo.gtasks.GoogleTaskService.ConvenientTaskCreator;
|
||||
import com.todoroo.gtasks.GoogleTaskTask;
|
||||
import com.todoroo.gtasks.GoogleTaskView;
|
||||
import com.todoroo.gtasks.GoogleTasksException;
|
||||
import com.todoroo.gtasks.actions.Action;
|
||||
import com.todoroo.gtasks.actions.Actions;
|
||||
import com.todoroo.gtasks.actions.GetTasksAction;
|
||||
import com.todoroo.gtasks.actions.ListAction;
|
||||
import com.todoroo.gtasks.actions.ListActions;
|
||||
import com.todoroo.gtasks.actions.ListActions.TaskBuilder;
|
||||
import com.todoroo.gtasks.actions.ListActions.TaskModifier;
|
||||
|
||||
@SuppressWarnings("nls")
|
||||
public class GtasksSyncProvider extends SyncProvider<GtasksTaskContainer> {
|
||||
|
||||
@Autowired private GtasksListService gtasksListService;
|
||||
@Autowired private GtasksMetadataService gtasksMetadataService;
|
||||
@Autowired private GtasksPreferenceService gtasksPreferenceService;
|
||||
@Autowired private GtasksTaskListUpdater gtasksTaskListUpdater;
|
||||
|
||||
/** google task service fields */
|
||||
private GoogleTaskService taskService = null;
|
||||
private static final Actions a = new Actions();
|
||||
private static final ListActions l = new ListActions();
|
||||
|
||||
/** batched actions to execute */
|
||||
private final ArrayList<Action> actions = new ArrayList<Action>();
|
||||
private final HashMap<String, ArrayList<ListAction>> listActions =
|
||||
new HashMap<String, ArrayList<ListAction>>();
|
||||
|
||||
|
||||
static {
|
||||
AstridDependencyInjector.initialize();
|
||||
}
|
||||
|
||||
@Autowired
|
||||
protected ExceptionService exceptionService;
|
||||
|
||||
public GtasksSyncProvider() {
|
||||
super();
|
||||
DependencyInjectionService.getInstance().inject(this);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
// ------------------------------------------------------ utility methods
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Sign out of service, deleting all synchronization metadata
|
||||
*/
|
||||
public void signOut() {
|
||||
gtasksPreferenceService.clearLastSyncDate();
|
||||
gtasksPreferenceService.setToken(null);
|
||||
|
||||
gtasksMetadataService.clearMetadata();
|
||||
}
|
||||
|
||||
/**
|
||||
* Deal with a synchronization exception. If requested, will show an error
|
||||
* to the user (unless synchronization is happening in background)
|
||||
*
|
||||
* @param context
|
||||
* @param tag
|
||||
* error tag
|
||||
* @param e
|
||||
* exception
|
||||
* @param showError
|
||||
* whether to display a dialog
|
||||
*/
|
||||
@Override
|
||||
protected void handleException(String tag, Exception e, boolean displayError) {
|
||||
final Context context = ContextManager.getContext();
|
||||
gtasksPreferenceService.setLastError(e.toString());
|
||||
|
||||
String message = null;
|
||||
|
||||
// occurs when application was closed
|
||||
if(e instanceof IllegalStateException) {
|
||||
exceptionService.reportError(tag + "-caught", e); //$NON-NLS-1$
|
||||
|
||||
// occurs when network error
|
||||
} else if(!(e instanceof ApiServiceException) && e instanceof IOException) {
|
||||
message = context.getString(R.string.producteev_ioerror);
|
||||
} else {
|
||||
message = context.getString(R.string.DLG_error, e.toString());
|
||||
exceptionService.reportError(tag + "-unhandled", e); //$NON-NLS-1$
|
||||
}
|
||||
|
||||
if(displayError && context instanceof Activity && message != null) {
|
||||
DialogUtilities.okDialog((Activity)context,
|
||||
message, null);
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
// ------------------------------------------------------ initiating sync
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* initiate sync in background
|
||||
*/
|
||||
@Override
|
||||
protected void initiateBackground(Service service) {
|
||||
try {
|
||||
String authToken = gtasksPreferenceService.getToken();
|
||||
|
||||
String email = "tasktest@todoroo.com"; // TODO
|
||||
String password = "tasktest0000";
|
||||
|
||||
taskService = new GoogleTaskService(email, password);
|
||||
|
||||
// check if we have a token & it works
|
||||
if(authToken != null) {
|
||||
|
||||
taskService.getTaskView();
|
||||
performSync();
|
||||
} else {
|
||||
if (email == null && password == null) {
|
||||
// we can't do anything, user is not logged in
|
||||
} else {
|
||||
//authToken = null; // TODO set up auth token
|
||||
performSync();
|
||||
}
|
||||
}
|
||||
} catch (IllegalStateException e) {
|
||||
// occurs when application was closed
|
||||
} catch (Exception e) {
|
||||
handleException("gtasks-authenticate", e, true);
|
||||
} finally {
|
||||
gtasksPreferenceService.stopOngoing();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If user isn't already signed in, show sign in dialog. Else perform sync.
|
||||
*/
|
||||
@Override
|
||||
protected void initiateManual(Activity activity) {
|
||||
String authToken = gtasksPreferenceService.getToken();
|
||||
ProducteevUtilities.INSTANCE.stopOngoing();
|
||||
|
||||
// check if we have a token & it works
|
||||
if(authToken == null) {
|
||||
// display login-activity
|
||||
Intent intent = new Intent(activity, ProducteevLoginActivity.class);
|
||||
activity.startActivityForResult(intent, 0);
|
||||
} else {
|
||||
activity.startService(new Intent(ProducteevBackgroundService.SYNC_ACTION, null,
|
||||
activity, ProducteevBackgroundService.class));
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
// ----------------------------------------------------- synchronization!
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
protected void performSync() {
|
||||
FlurryAgent.onEvent("gtasks-started");
|
||||
gtasksPreferenceService.recordSyncStart();
|
||||
|
||||
try {
|
||||
GoogleTaskView taskView = taskService.getTaskView();
|
||||
Preferences.setString(GtasksPreferenceService.PREF_DEFAULT_LIST,
|
||||
taskView.getActiveTaskList().getInfo().getId());
|
||||
|
||||
gtasksListService.updateLists(taskView.getAllLists());
|
||||
|
||||
gtasksTaskListUpdater.createParentSiblingMaps();
|
||||
|
||||
// batched read tasks for each list
|
||||
ArrayList<GtasksTaskContainer> remoteTasks = new ArrayList<GtasksTaskContainer>();
|
||||
ArrayList<GetTasksAction> getTasksActions = new ArrayList<GetTasksAction>();
|
||||
for(StoreObject dashboard : gtasksListService.getLists()) {
|
||||
String listId = dashboard.getValue(GtasksList.REMOTE_ID);
|
||||
getTasksActions.add(a.getTasks(listId, true));
|
||||
}
|
||||
taskService.executeActions(getTasksActions.toArray(new GetTasksAction[getTasksActions.size()]));
|
||||
for(GetTasksAction action : getTasksActions) {
|
||||
List<GoogleTaskTask> remoteTasksInList = action.getGoogleTasks();
|
||||
for(GoogleTaskTask remoteTask : remoteTasksInList) {
|
||||
GtasksTaskContainer remote = parseRemoteTask(remoteTask);
|
||||
// update reminder flags for incoming remote tasks to prevent annoying
|
||||
if(remote.task.hasDueDate() && remote.task.getValue(Task.DUE_DATE) < DateUtilities.now())
|
||||
remote.task.setFlag(Task.REMINDER_FLAGS, Task.NOTIFY_AFTER_DEADLINE, false);
|
||||
gtasksMetadataService.findLocalMatch(remote);
|
||||
remoteTasks.add(remote);
|
||||
}
|
||||
}
|
||||
|
||||
SyncData<GtasksTaskContainer> syncData = populateSyncData(remoteTasks);
|
||||
try {
|
||||
synchronizeTasks(syncData);
|
||||
} finally {
|
||||
syncData.localCreated.close();
|
||||
syncData.localUpdated.close();
|
||||
}
|
||||
|
||||
gtasksPreferenceService.recordSuccessfulSync();
|
||||
FlurryAgent.onEvent("gtasks-sync-finished"); //$NON-NLS-1$
|
||||
} catch (IllegalStateException e) {
|
||||
// occurs when application was closed
|
||||
} catch (Exception e) {
|
||||
handleException("gtasks-sync", e, true); //$NON-NLS-1$
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
// ------------------------------------------------------------ sync data
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
// all synchronized properties
|
||||
private static final Property<?>[] PROPERTIES = new Property<?>[] {
|
||||
Task.ID,
|
||||
Task.TITLE,
|
||||
Task.IMPORTANCE,
|
||||
Task.DUE_DATE,
|
||||
Task.CREATION_DATE,
|
||||
Task.COMPLETION_DATE,
|
||||
Task.DELETION_DATE,
|
||||
Task.REMINDER_FLAGS,
|
||||
Task.NOTES,
|
||||
};
|
||||
|
||||
/**
|
||||
* Populate SyncData data structure
|
||||
* @throws JSONException
|
||||
*/
|
||||
private SyncData<GtasksTaskContainer> populateSyncData(ArrayList<GtasksTaskContainer> remoteTasks) throws JSONException {
|
||||
// fetch locally created tasks
|
||||
TodorooCursor<Task> localCreated = gtasksMetadataService.getLocallyCreated(PROPERTIES);
|
||||
|
||||
// fetch locally updated tasks
|
||||
TodorooCursor<Task> localUpdated = gtasksMetadataService.getLocallyUpdated(PROPERTIES);
|
||||
|
||||
return new SyncData<GtasksTaskContainer>(remoteTasks, localCreated, localUpdated);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
// ------------------------------------------------- create / push / pull
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
@Override
|
||||
protected GtasksTaskContainer create(GtasksTaskContainer local) throws IOException {
|
||||
String list = Preferences.getStringValue(GtasksPreferenceService.PREF_DEFAULT_LIST);
|
||||
if(local.gtaskMetadata.containsNonNullValue(GtasksMetadata.LIST_ID))
|
||||
list = local.gtaskMetadata.getValue(GtasksMetadata.LIST_ID);
|
||||
|
||||
ConvenientTaskCreator createdTask;
|
||||
try {
|
||||
createdTask = taskService.createTask(list, local.task.getValue(Task.TITLE));
|
||||
} catch (JSONException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
updateTaskHelper(local, null, createdTask);
|
||||
String remoteId;
|
||||
try {
|
||||
remoteId = createdTask.go();
|
||||
} catch (JSONException e) {
|
||||
throw new GoogleTasksException(e);
|
||||
}
|
||||
local.gtaskMetadata.setValue(GtasksMetadata.LIST_ID, remoteId);
|
||||
|
||||
return local;
|
||||
}
|
||||
|
||||
private void updateTaskHelper(GtasksTaskContainer local,
|
||||
GtasksTaskContainer remote, TaskBuilder<?> builder) throws IOException {
|
||||
|
||||
String idTask = local.gtaskMetadata.getValue(GtasksMetadata.ID);
|
||||
String idList = local.gtaskMetadata.getValue(GtasksMetadata.LIST_ID);
|
||||
|
||||
// fetch remote task for comparison
|
||||
if(remote == null && idTask != null)
|
||||
remote = pull(local);
|
||||
|
||||
try {
|
||||
|
||||
// moving between lists
|
||||
if(remote != null && !idList.equals(remote.gtaskMetadata.getValue(GtasksMetadata.LIST_ID))) {
|
||||
a.moveTask(idTask, idList, remote.gtaskMetadata.getValue(GtasksMetadata.LIST_ID), null);
|
||||
}
|
||||
|
||||
// other properties
|
||||
if(shouldTransmit(local, Task.TITLE, remote))
|
||||
((TaskModifier)builder).name(local.task.getValue(Task.TITLE));
|
||||
if(shouldTransmit(local, Task.DUE_DATE, remote))
|
||||
builder.taskDate(local.task.getValue(Task.DUE_DATE));
|
||||
if(shouldTransmit(local, Task.COMPLETION_DATE, remote))
|
||||
builder.completed(local.task.isCompleted());
|
||||
if(shouldTransmit(local, Task.DELETION_DATE, remote))
|
||||
builder.deleted(local.task.isDeleted());
|
||||
if(shouldTransmit(local, Task.NOTES, remote))
|
||||
builder.notes(local.task.getValue(Task.NOTES));
|
||||
|
||||
} catch (JSONException e) {
|
||||
throw new GoogleTasksException(e);
|
||||
}
|
||||
|
||||
// TODO indentation
|
||||
}
|
||||
|
||||
/** Create a task container for the given RtmTaskSeries
|
||||
* @throws JSONException */
|
||||
private GtasksTaskContainer parseRemoteTask(GoogleTaskTask remoteTask) {
|
||||
Task task = new Task();
|
||||
ArrayList<Metadata> metadata = new ArrayList<Metadata>();
|
||||
|
||||
task.setValue(Task.TITLE, remoteTask.getName());
|
||||
task.setValue(Task.CREATION_DATE, DateUtilities.now());
|
||||
task.setValue(Task.COMPLETION_DATE, remoteTask.getCompleted_date());
|
||||
task.setValue(Task.DELETION_DATE, remoteTask.isDeleted() ? DateUtilities.now() : 0);
|
||||
|
||||
long dueDate = remoteTask.getTask_date();
|
||||
task.setValue(Task.DUE_DATE, task.createDueDate(Task.URGENCY_SPECIFIC_DAY_TIME, dueDate));
|
||||
task.setValue(Task.NOTES, remoteTask.getNotes());
|
||||
|
||||
Metadata gtasksMetadata = GtasksMetadata.createEmptyMetadata(AbstractModel.NO_ID);
|
||||
gtasksMetadata.setValue(GtasksMetadata.LIST_ID, remoteTask.getList_id());
|
||||
// TODO gtasksMetadata.setValue(GtasksMetadata.INDENT, remoteTask.???);
|
||||
|
||||
GtasksTaskContainer container = new GtasksTaskContainer(task, metadata,
|
||||
gtasksMetadata);
|
||||
|
||||
return container;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected GtasksTaskContainer pull(GtasksTaskContainer task) throws IOException {
|
||||
if(!task.gtaskMetadata.containsNonNullValue(GtasksMetadata.ID) ||
|
||||
!task.gtaskMetadata.containsNonNullValue(GtasksMetadata.LIST_ID))
|
||||
throw new ApiServiceException("Tried to read an invalid task"); //$NON-NLS-1$
|
||||
|
||||
String idToMatch = task.gtaskMetadata.getValue(GtasksMetadata.ID);
|
||||
List<GoogleTaskTask> tasks;
|
||||
try {
|
||||
tasks = taskService.getTasks(task.gtaskMetadata.getValue(GtasksMetadata.LIST_ID));
|
||||
} catch (JSONException e) {
|
||||
throw new GoogleTasksException(e);
|
||||
}
|
||||
for(GoogleTaskTask remoteTask : tasks) {
|
||||
if(remoteTask.getId().equals(idToMatch)) {
|
||||
return parseRemoteTask(remoteTask);
|
||||
}
|
||||
}
|
||||
|
||||
throw new GoogleTasksException("Could not find remote task to pull.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Send changes for the given Task across the wire. If a remoteTask is
|
||||
* supplied, we attempt to intelligently only transmit the values that
|
||||
* have changed.
|
||||
*/
|
||||
@Override
|
||||
protected void push(GtasksTaskContainer local, GtasksTaskContainer remote) throws IOException {
|
||||
try {
|
||||
TaskModifier modifyTask = l.modifyTask(remote.gtaskMetadata.getValue(GtasksMetadata.ID));
|
||||
updateTaskHelper(local, remote, modifyTask);
|
||||
ListAction action = modifyTask.done();
|
||||
batch(local.gtaskMetadata.getValue(GtasksMetadata.LIST_ID), action);
|
||||
} catch (JSONException e) {
|
||||
throw new GoogleTasksException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/** add action to batch */
|
||||
private void batch(String list, ListAction action) {
|
||||
if(!listActions.containsKey(list))
|
||||
listActions.put(list, new ArrayList<ListAction>());
|
||||
listActions.get(list).add(action);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
// --------------------------------------------------------- read / write
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
@Override
|
||||
protected GtasksTaskContainer read(TodorooCursor<Task> cursor) throws IOException {
|
||||
return gtasksMetadataService.readTaskAndMetadata(cursor);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void write(GtasksTaskContainer task) throws IOException {
|
||||
gtasksMetadataService.saveTaskAndMetadata(task);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
// --------------------------------------------------------- misc helpers
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
@Override
|
||||
protected int matchTask(ArrayList<GtasksTaskContainer> tasks, GtasksTaskContainer target) {
|
||||
int length = tasks.size();
|
||||
for(int i = 0; i < length; i++) {
|
||||
GtasksTaskContainer task = tasks.get(i);
|
||||
if(AndroidUtilities.equals(task.gtaskMetadata, target.gtaskMetadata))
|
||||
return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether this task's property should be transmitted
|
||||
* @param task task to consider
|
||||
* @param property property to consider
|
||||
* @param remoteTask remote task proxy
|
||||
* @return
|
||||
*/
|
||||
private boolean shouldTransmit(SyncContainer task, Property<?> property, SyncContainer remoteTask) {
|
||||
if(!task.task.containsValue(property))
|
||||
return false;
|
||||
|
||||
if(remoteTask == null)
|
||||
return true;
|
||||
if(!remoteTask.task.containsValue(property))
|
||||
return true;
|
||||
|
||||
// special cases - match if they're zero or nonzero
|
||||
if(property == Task.COMPLETION_DATE ||
|
||||
property == Task.DELETION_DATE)
|
||||
return !AndroidUtilities.equals((Long)task.task.getValue(property) == 0,
|
||||
(Long)remoteTask.task.getValue(property) == 0);
|
||||
|
||||
return !AndroidUtilities.equals(task.task.getValue(property),
|
||||
remoteTask.task.getValue(property));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int updateNotification(Context context, Notification notification) {
|
||||
String notificationTitle = context.getString(R.string.gtasks_notification_title);
|
||||
Intent intent = new Intent(context, GtasksPreferences.class);
|
||||
PendingIntent notificationIntent = PendingIntent.getActivity(context, 0,
|
||||
intent, 0);
|
||||
notification.setLatestEventInfo(context,
|
||||
notificationTitle, context.getString(R.string.SyP_progress),
|
||||
notificationIntent);
|
||||
return Constants.NOTIFICATION_SYNC;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void transferIdentifiers(GtasksTaskContainer source,
|
||||
GtasksTaskContainer destination) {
|
||||
destination.gtaskMetadata = source.gtaskMetadata;
|
||||
}
|
||||
}
|
@ -0,0 +1,144 @@
|
||||
package com.todoroo.astrid.gtasks;
|
||||
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
|
||||
import com.todoroo.andlib.service.Autowired;
|
||||
import com.todoroo.astrid.api.AstridApiConstants;
|
||||
import com.todoroo.astrid.core.PluginServices;
|
||||
import com.todoroo.astrid.data.Metadata;
|
||||
import com.todoroo.astrid.data.Task;
|
||||
import com.todoroo.astrid.test.DatabaseTestCase;
|
||||
import com.todoroo.gtasks.GoogleTaskListInfo;
|
||||
|
||||
public class GtasksDetailExposerTest extends DatabaseTestCase {
|
||||
|
||||
@Autowired private GtasksListService gtasksListService;
|
||||
private GtasksTestPreferenceService preferences = new GtasksTestPreferenceService();
|
||||
private DetailListener detailListener = new DetailListener();
|
||||
|
||||
private Task task;
|
||||
private String detail;
|
||||
|
||||
public void testExposeNotLoggedIn() {
|
||||
givenTwoListSetup();
|
||||
givenLoggedInStatus(false);
|
||||
givenTaskWithList("listone-id");
|
||||
|
||||
whenRequestingDetails();
|
||||
|
||||
thenExpectNoDetail();
|
||||
}
|
||||
|
||||
public void testExposeListOne() {
|
||||
givenTwoListSetup();
|
||||
givenLoggedInStatus(true);
|
||||
givenTaskWithList("listone-id");
|
||||
|
||||
whenRequestingDetails();
|
||||
|
||||
thenExpectDetail("List One");
|
||||
}
|
||||
|
||||
public void testExposeListTwo() {
|
||||
givenTwoListSetup();
|
||||
givenLoggedInStatus(true);
|
||||
givenTaskWithList("listtwo-id");
|
||||
|
||||
whenRequestingDetails();
|
||||
|
||||
thenExpectDetail("List Two");
|
||||
}
|
||||
|
||||
public void testExposeListDoesntExist() {
|
||||
givenTwoListSetup();
|
||||
givenLoggedInStatus(true);
|
||||
givenTaskWithList("blah");
|
||||
|
||||
whenRequestingDetails();
|
||||
|
||||
thenExpectNoDetail();
|
||||
}
|
||||
|
||||
public void testExposeListNotSet() {
|
||||
givenTwoListSetup();
|
||||
givenLoggedInStatus(true);
|
||||
givenTaskWithList(null);
|
||||
|
||||
whenRequestingDetails();
|
||||
|
||||
thenExpectNoDetail();
|
||||
}
|
||||
|
||||
// --- helpers
|
||||
|
||||
private void thenExpectNoDetail() {
|
||||
assertNull("no detail", detail);
|
||||
}
|
||||
|
||||
private void thenExpectDetail(String expected) {
|
||||
assertNotNull("detail not null", detail);
|
||||
assertTrue("detail was '" + detail + "', '" + expected + "' expected",
|
||||
detail.contains(expected));
|
||||
}
|
||||
|
||||
private void givenTwoListSetup() {
|
||||
GoogleTaskListInfo[] newLists = new GoogleTaskListInfo[2];
|
||||
GoogleTaskListInfo list = new GoogleTaskListInfo("listone-id", "List One");
|
||||
newLists[0] = list;
|
||||
list = new GoogleTaskListInfo("listtwo-id", "List Two");
|
||||
newLists[1] = list;
|
||||
gtasksListService.updateLists(newLists);
|
||||
}
|
||||
|
||||
private Task givenTaskWithList(String list) {
|
||||
Task newTask = new Task();
|
||||
PluginServices.getTaskService().save(newTask);
|
||||
Metadata metadata = GtasksMetadata.createEmptyMetadata(newTask.getId());
|
||||
if(list != null)
|
||||
metadata.setValue(GtasksMetadata.LIST_ID, list);
|
||||
PluginServices.getMetadataService().save(metadata);
|
||||
return task = newTask;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void addInjectables() {
|
||||
super.addInjectables();
|
||||
testInjector.addInjectable("gtasksPreferenceService", preferences);
|
||||
}
|
||||
|
||||
private void whenRequestingDetails() {
|
||||
Intent intent = new Intent(AstridApiConstants.BROADCAST_REQUEST_DETAILS);
|
||||
intent.putExtra(AstridApiConstants.EXTRAS_EXTENDED, false);
|
||||
intent.putExtra(AstridApiConstants.EXTRAS_TASK_ID, task.getId());
|
||||
detail = null;
|
||||
new GtasksDetailExposer().onReceive(getContext(), intent);
|
||||
}
|
||||
|
||||
private void givenLoggedInStatus(boolean status) {
|
||||
preferences.setLoggedIn(status);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setUp() throws Exception {
|
||||
super.setUp();
|
||||
getContext().registerReceiver(detailListener, new IntentFilter(AstridApiConstants.BROADCAST_SEND_DETAILS));
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void tearDown() throws Exception {
|
||||
super.tearDown();
|
||||
getContext().unregisterReceiver(detailListener);
|
||||
}
|
||||
|
||||
private class DetailListener extends BroadcastReceiver {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
detail = intent.getExtras().getString(AstridApiConstants.EXTRAS_RESPONSE);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,162 @@
|
||||
package com.todoroo.astrid.gtasks;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import com.todoroo.andlib.data.TodorooCursor;
|
||||
import com.todoroo.andlib.service.Autowired;
|
||||
import com.todoroo.andlib.utility.DateUtilities;
|
||||
import com.todoroo.astrid.core.PluginServices;
|
||||
import com.todoroo.astrid.data.Metadata;
|
||||
import com.todoroo.astrid.data.Task;
|
||||
import com.todoroo.astrid.test.DatabaseTestCase;
|
||||
import com.todoroo.astrid.utility.Preferences;
|
||||
|
||||
public class GtasksMetadataServiceTest extends DatabaseTestCase {
|
||||
|
||||
private GtasksTestPreferenceService preferences = new GtasksTestPreferenceService();
|
||||
@Autowired private GtasksMetadataService gtasksMetadataService;
|
||||
|
||||
private Task task;
|
||||
private Metadata metadata;
|
||||
private TodorooCursor<Task> cursor;
|
||||
|
||||
public void testMetadataFound() {
|
||||
givenTask(taskWithMetadata(null));
|
||||
|
||||
whenSearchForMetadata();
|
||||
|
||||
thenExpectMetadataFound();
|
||||
}
|
||||
|
||||
public void testMetadataDoesntExist() {
|
||||
givenTask(taskWithoutMetadata());
|
||||
|
||||
whenSearchForMetadata();
|
||||
|
||||
thenExpectNoMetadataFound();
|
||||
}
|
||||
|
||||
public void testLocallyCreatedHasItem() {
|
||||
taskWithMetadata("ok");
|
||||
givenTask(taskWithoutMetadata());
|
||||
|
||||
whenReadLocalCreated();
|
||||
|
||||
thenExpectCursorEquals(task);
|
||||
}
|
||||
|
||||
public void testLocallyCreatedWhenEmptyMetadata() {
|
||||
givenTask(taskWithMetadata(null));
|
||||
|
||||
whenReadLocalCreated();
|
||||
|
||||
thenExpectCursorEquals(task);
|
||||
}
|
||||
|
||||
public void testLocallyCreatedIsEmpty() {
|
||||
givenTask(taskWithMetadata("ok"));
|
||||
|
||||
whenReadLocalCreated();
|
||||
|
||||
thenExpectCursorIsEmpty();
|
||||
}
|
||||
|
||||
public void testLocallyUpdatedHasItem() {
|
||||
givenTask(taskWithMetadata("ok"));
|
||||
|
||||
whenReadLocalUpdated();
|
||||
|
||||
thenExpectCursorEquals(task);
|
||||
}
|
||||
|
||||
public void testLocallyUpdatedIsEmptyWhenUpToDate() {
|
||||
givenTask(taskWithMetadata("ok"));
|
||||
givenSyncDate(DateUtilities.now());
|
||||
|
||||
whenReadLocalUpdated();
|
||||
|
||||
thenExpectCursorIsEmpty();
|
||||
}
|
||||
|
||||
public void testLocallyUpdatedIsEmptyWhenNoUpdatedTasks() {
|
||||
givenTask(taskWithMetadata(null));
|
||||
|
||||
whenReadLocalUpdated();
|
||||
|
||||
thenExpectCursorIsEmpty();
|
||||
}
|
||||
|
||||
// --- helpers
|
||||
|
||||
private void givenSyncDate(long date) {
|
||||
preferences.setSyncDate(date);
|
||||
}
|
||||
|
||||
private void whenReadLocalUpdated() {
|
||||
cursor = gtasksMetadataService.getLocallyUpdated(Task.ID);
|
||||
}
|
||||
|
||||
private void thenExpectCursorIsEmpty() {
|
||||
assertEquals("cursor is empty", 0, cursor.getCount());
|
||||
}
|
||||
|
||||
private void thenExpectCursorEquals(Task expectedTask) {
|
||||
assertEquals("cursor has one item", 1, cursor.getCount());
|
||||
cursor.moveToFirst();
|
||||
Task receivedTask = new Task(cursor);
|
||||
assertEquals("task equals expected", expectedTask.getId(), receivedTask.getId());
|
||||
}
|
||||
|
||||
private void whenReadLocalCreated() {
|
||||
cursor = gtasksMetadataService.getLocallyCreated(Task.ID);
|
||||
}
|
||||
|
||||
private void thenExpectNoMetadataFound() {
|
||||
assertNull(metadata);
|
||||
}
|
||||
|
||||
private void thenExpectMetadataFound() {
|
||||
assertNotNull(metadata);
|
||||
}
|
||||
|
||||
private void whenSearchForMetadata() {
|
||||
metadata = gtasksMetadataService.getTaskMetadata(task.getId());
|
||||
}
|
||||
|
||||
private Task taskWithMetadata(String id) {
|
||||
Task task = new Task();
|
||||
task.setValue(Task.TITLE, "cats");
|
||||
PluginServices.getTaskService().save(task);
|
||||
Metadata metadata = GtasksMetadata.createEmptyMetadata(task.getId());
|
||||
if(id != null)
|
||||
metadata.setValue(GtasksMetadata.ID, id);
|
||||
metadata.setValue(Metadata.TASK, task.getId());
|
||||
PluginServices.getMetadataService().save(metadata);
|
||||
return task;
|
||||
}
|
||||
|
||||
private void givenTask(Task taskToTest) {
|
||||
task = taskToTest;
|
||||
}
|
||||
|
||||
private Task taskWithoutMetadata() {
|
||||
Task task = new Task();
|
||||
task.setValue(Task.TITLE, "dogs");
|
||||
PluginServices.getTaskService().save(task);
|
||||
return task;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void addInjectables() {
|
||||
super.addInjectables();
|
||||
testInjector.addInjectable("gtasksPreferenceService", preferences);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setContext(Context context) {
|
||||
super.setContext(context);
|
||||
if(!Preferences.isSet(GtasksPreferenceService.PREF_DEFAULT_LIST))
|
||||
Preferences.setString(GtasksPreferenceService.PREF_DEFAULT_LIST, "list");
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,164 @@
|
||||
package com.todoroo.astrid.gtasks;
|
||||
|
||||
import com.todoroo.andlib.service.Autowired;
|
||||
import com.todoroo.astrid.core.PluginServices;
|
||||
import com.todoroo.astrid.data.Metadata;
|
||||
import com.todoroo.astrid.data.StoreObject;
|
||||
import com.todoroo.astrid.data.Task;
|
||||
import com.todoroo.astrid.test.DatabaseTestCase;
|
||||
import com.todoroo.gtasks.GoogleTaskListInfo;
|
||||
|
||||
public class GtasksTaskListUpdaterTest extends DatabaseTestCase {
|
||||
|
||||
@Autowired private GtasksTaskListUpdater gtasksTaskListUpdater;
|
||||
@Autowired private GtasksListService gtasksListService;
|
||||
@Autowired private GtasksMetadataService gtasksMetadataService;
|
||||
|
||||
public void testBasicParentComputation() {
|
||||
Task[] tasks = givenTasksABCDE();
|
||||
|
||||
whenCalculatingParentsAndSiblings();
|
||||
|
||||
thenExpectParent(tasks[0], null);
|
||||
thenExpectParent(tasks[1], tasks[0]);
|
||||
thenExpectParent(tasks[2], tasks[0]);
|
||||
thenExpectParent(tasks[3], tasks[2]);
|
||||
thenExpectParent(tasks[4], null);
|
||||
}
|
||||
|
||||
public void testBasicSiblingComputation() {
|
||||
Task[] tasks = givenTasksABCDE();
|
||||
|
||||
whenCalculatingParentsAndSiblings();
|
||||
|
||||
thenExpectSibling(tasks[0], null);
|
||||
thenExpectSibling(tasks[1], null);
|
||||
thenExpectSibling(tasks[2], tasks[1]);
|
||||
thenExpectSibling(tasks[3], null);
|
||||
thenExpectSibling(tasks[4], tasks[0]);
|
||||
}
|
||||
|
||||
public void testMetadataParentComputation() {
|
||||
Task[] tasks = givenTasksABCDE();
|
||||
|
||||
whenCalculatingOrder();
|
||||
|
||||
thenExpectMetadataParent(tasks[0], null);
|
||||
thenExpectMetadataParent(tasks[1], tasks[0]);
|
||||
thenExpectMetadataParent(tasks[2], tasks[0]);
|
||||
thenExpectMetadataParent(tasks[3], tasks[2]);
|
||||
thenExpectMetadataParent(tasks[4], null);
|
||||
}
|
||||
|
||||
public void testMetadataOrderComputation() {
|
||||
Task[] tasks = givenTasksABCDE();
|
||||
|
||||
whenCalculatingOrder();
|
||||
|
||||
thenExpectMetadataIndentAndOrder(tasks[0], 0, 0);
|
||||
thenExpectMetadataIndentAndOrder(tasks[1], 1, 1);
|
||||
thenExpectMetadataIndentAndOrder(tasks[2], 2, 1);
|
||||
thenExpectMetadataIndentAndOrder(tasks[3], 3, 2);
|
||||
thenExpectMetadataIndentAndOrder(tasks[4], 4, 0);
|
||||
}
|
||||
|
||||
public void testNewTaskOrder() {
|
||||
givenTasksABCDE();
|
||||
|
||||
Task newTask = createTask("F", GtasksMetadata.VALUE_UNSET, 0);
|
||||
whenCalculatingOrder();
|
||||
|
||||
thenExpectMetadataIndentAndOrder(newTask, 5, 0);
|
||||
}
|
||||
|
||||
|
||||
// --- helpers
|
||||
|
||||
private void thenExpectMetadataIndentAndOrder(Task task, int order, int indent) {
|
||||
Metadata metadata = gtasksMetadataService.getTaskMetadata(task.getId());
|
||||
assertNotNull("metadata was found", metadata);
|
||||
assertEquals("order", order, (int)metadata.getValue(GtasksMetadata.ORDER));
|
||||
assertEquals("indentation", indent, (int)metadata.getValue(GtasksMetadata.INDENT));
|
||||
}
|
||||
|
||||
private void thenExpectMetadataParent(Task task, Task expectedParent) {
|
||||
Metadata metadata = gtasksMetadataService.getTaskMetadata(task.getId());
|
||||
long parent = metadata.getValue(GtasksMetadata.PARENT_TASK);
|
||||
if(expectedParent == null)
|
||||
assertEquals("Task " + task.getValue(Task.TITLE) + " parent none", parent, 0);
|
||||
else
|
||||
assertEquals("Task " + task.getValue(Task.TITLE) + " parent " +
|
||||
expectedParent.getValue(Task.TITLE), expectedParent.getId(), parent);
|
||||
}
|
||||
|
||||
private void thenExpectSibling(Task task, Task expectedSibling) {
|
||||
long sibling = gtasksTaskListUpdater.siblings.get(task.getId());
|
||||
if(expectedSibling == null)
|
||||
assertEquals("Task " + task.getValue(Task.TITLE) + " sibling null", -1L, sibling);
|
||||
else
|
||||
assertEquals("Task " + task.getValue(Task.TITLE) + " sibling " +
|
||||
expectedSibling.getValue(Task.TITLE), expectedSibling.getId(), sibling);
|
||||
}
|
||||
|
||||
private void thenExpectParent(Task task, Task expectedParent) {
|
||||
long parent = gtasksTaskListUpdater.parents.get(task.getId());
|
||||
if(expectedParent == null)
|
||||
assertEquals("Task " + task.getValue(Task.TITLE) + " parent null", -1L, parent);
|
||||
else
|
||||
assertEquals("Task " + task.getValue(Task.TITLE) + " parent " +
|
||||
expectedParent.getValue(Task.TITLE), expectedParent.getId(), parent);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setUp() throws Exception {
|
||||
super.setUp();
|
||||
|
||||
GoogleTaskListInfo[] lists = new GoogleTaskListInfo[1];
|
||||
GoogleTaskListInfo list = new GoogleTaskListInfo("1", "Tim's Tasks");
|
||||
lists[0] = list;
|
||||
gtasksListService.updateLists(lists);
|
||||
}
|
||||
|
||||
private void whenCalculatingParentsAndSiblings() {
|
||||
gtasksTaskListUpdater.createParentSiblingMaps();
|
||||
}
|
||||
|
||||
private void whenCalculatingOrder() {
|
||||
for(StoreObject list : gtasksListService.getLists())
|
||||
gtasksTaskListUpdater.updateMetadataForList(list.getValue(GtasksList.REMOTE_ID));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* A
|
||||
* B
|
||||
* C
|
||||
* D
|
||||
* E
|
||||
*/
|
||||
private Task[] givenTasksABCDE() {
|
||||
return new Task[] {
|
||||
createTask("A", 0, 0),
|
||||
createTask("B", 1, 1),
|
||||
createTask("C", 2, 1),
|
||||
createTask("D", 3, 2),
|
||||
createTask("E", 4, 0),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
private Task createTask(String title, int order, int indent) {
|
||||
Task task = new Task();
|
||||
task.setValue(Task.TITLE, title);
|
||||
PluginServices.getTaskService().save(task);
|
||||
Metadata metadata = GtasksMetadata.createEmptyMetadata(task.getId());
|
||||
metadata.setValue(GtasksMetadata.LIST_ID, "1");
|
||||
if(order != GtasksMetadata.VALUE_UNSET)
|
||||
metadata.setValue(GtasksMetadata.ORDER, order);
|
||||
if(indent != GtasksMetadata.VALUE_UNSET)
|
||||
metadata.setValue(GtasksMetadata.INDENT, indent);
|
||||
PluginServices.getMetadataService().save(metadata);
|
||||
return task;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
package com.todoroo.astrid.provider;
|
||||
|
||||
import com.todoroo.andlib.data.AbstractDatabase;
|
||||
import com.todoroo.astrid.test.DatabaseTestCase;
|
||||
|
||||
public class ProviderTestUtilities extends DatabaseTestCase {
|
||||
|
||||
public static final void setDatabaseOverride(AbstractDatabase database) {
|
||||
Astrid3ContentProvider.setDatabaseOverride(database);
|
||||
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue