Merge branch 'gtasks'

Conflicts:
	astrid/src/com/todoroo/astrid/service/AstridDependencyInjector.java
pull/14/head
Tim Su 14 years ago
commit bfc7655d6d

@ -14,5 +14,6 @@
<classpathentry exported="true" kind="lib" path="libs/jsr305.jar"/>
<classpathentry exported="true" kind="lib" path="libs/rfc2445-no-joda.jar"/>
<classpathentry exported="true" kind="lib" path="libs/locale_platform.jar"/>
<classpathentry exported="true" kind="lib" path="libs/todoroo-g.jar"/>
<classpathentry kind="output" path="ecbuild"/>
</classpath>

@ -214,7 +214,7 @@
</receiver>
<!-- gtasks -->
<!-- receiver android:name="com.todoroo.astrid.gtasks.GtasksFilterExposer">
<receiver android:name="com.todoroo.astrid.gtasks.GtasksFilterExposer">
<intent-filter>
<action android:name="com.todoroo.astrid.REQUEST_FILTERS" />
<category android:name="android.intent.category.DEFAULT" />
@ -268,7 +268,7 @@
<action android:name="com.todoroo.astrid.CONTEXT_MENU" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</receiver -->
</receiver>
<!-- tags -->
<receiver android:name="com.todoroo.astrid.tags.TagsPlugin">

Binary file not shown.

@ -35,7 +35,7 @@ public class GtasksDecorationExposer implements TaskDecorationExposer {
if(!gtasksPreferenceService.isLoggedIn())
return null;
if(Flags.checkAndClear(Flags.GTASKS))
if(!Flags.check(Flags.GTASKS))
return null;
return createDecoration(task);

@ -13,6 +13,7 @@ import com.todoroo.andlib.service.ContextManager;
import com.todoroo.andlib.service.DependencyInjectionService;
import com.todoroo.astrid.api.AstridApiConstants;
import com.todoroo.astrid.data.Metadata;
import com.todoroo.astrid.utility.Constants;
/**
* Exposes Task Details for Remember the Milk:
@ -31,10 +32,13 @@ public class GtasksDetailExposer extends BroadcastReceiver {
@Autowired private GtasksListService gtasksListService;
@Autowired private GtasksPreferenceService gtasksPreferenceService;
public GtasksDetailExposer() {
DependencyInjectionService.getInstance().inject(this);
}
@Override
public void onReceive(Context context, Intent intent) {
ContextManager.setContext(context);
DependencyInjectionService.getInstance().inject(this);
// if we aren't logged in, don't expose features
if(!gtasksPreferenceService.isLoggedIn())
@ -74,7 +78,7 @@ public class GtasksDetailExposer extends BroadcastReceiver {
builder.append("<img src='silk_folder'/> ").append(listName); //$NON-NLS-1$
if(metadata.getValue(GtasksMetadata.PARENT_TASK) > AbstractModel.NO_ID) {
if(metadata.getValue(GtasksMetadata.PARENT_TASK) > AbstractModel.NO_ID && Constants.DEBUG) {
builder.append(DETAIL_SEPARATOR).append("Parent: ").append(metadata.getValue(GtasksMetadata.PARENT_TASK));
}

@ -16,7 +16,6 @@ import com.todoroo.andlib.service.Autowired;
import com.todoroo.andlib.service.ContextManager;
import com.todoroo.andlib.service.DependencyInjectionService;
import com.todoroo.andlib.sql.Criterion;
import com.todoroo.andlib.sql.Functions;
import com.todoroo.andlib.sql.Join;
import com.todoroo.andlib.sql.Order;
import com.todoroo.andlib.sql.QueryTemplate;
@ -26,9 +25,9 @@ import com.todoroo.astrid.api.FilterCategory;
import com.todoroo.astrid.api.FilterListHeader;
import com.todoroo.astrid.api.FilterListItem;
import com.todoroo.astrid.data.Metadata;
import com.todoroo.astrid.data.MetadataApiDao.MetadataCriteria;
import com.todoroo.astrid.data.StoreObject;
import com.todoroo.astrid.data.Task;
import com.todoroo.astrid.data.MetadataApiDao.MetadataCriteria;
import com.todoroo.astrid.data.TaskApiDao.TaskCriteria;
/**
@ -54,9 +53,7 @@ public class GtasksFilterExposer extends BroadcastReceiver {
Join.left(Metadata.TABLE, Task.ID.eq(Metadata.TASK))).where(Criterion.and(
MetadataCriteria.withKey(GtasksMetadata.METADATA_KEY),
TaskCriteria.activeAndVisible(),
GtasksMetadata.LIST_ID.eq(list.getValue(GtasksList.REMOTE_ID)))).orderBy(
Order.asc(Functions.caseStatement(GtasksMetadata.ORDER.eq(0),
Task.CREATION_DATE, GtasksMetadata.ORDER))),
GtasksMetadata.LIST_ID.eq(list.getValue(GtasksList.REMOTE_ID)))).orderBy(Order.asc(GtasksMetadata.ORDER)),
values);
return filter;

@ -22,6 +22,7 @@ import com.todoroo.astrid.utility.Flags;
abstract public class GtasksIndentAction extends BroadcastReceiver {
@Autowired private GtasksMetadataService gtasksMetadataService;
@Autowired private GtasksTaskListUpdater gtasksTaskListUpdater;
abstract int getDelta();
@ -43,7 +44,7 @@ abstract public class GtasksIndentAction extends BroadcastReceiver {
metadata.setValue(GtasksMetadata.INDENT, newIndent);
PluginServices.getMetadataService().save(metadata);
gtasksMetadataService.updateMetadataForList(metadata.getValue(GtasksMetadata.LIST_ID));
gtasksTaskListUpdater.updateMetadataForList(metadata.getValue(GtasksMetadata.LIST_ID));
Flags.set(Flags.REFRESH);
Toast.makeText(context, context.getString(R.string.gtasks_indent_toast, newIndent),

@ -1,9 +1,5 @@
package com.todoroo.astrid.gtasks;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import com.todoroo.andlib.data.TodorooCursor;
import com.todoroo.andlib.service.Autowired;
import com.todoroo.andlib.service.DependencyInjectionService;
@ -11,6 +7,7 @@ import com.todoroo.andlib.sql.Query;
import com.todoroo.astrid.dao.StoreObjectDao;
import com.todoroo.astrid.dao.StoreObjectDao.StoreObjectCriteria;
import com.todoroo.astrid.data.StoreObject;
import com.todoroo.gtasks.GoogleTaskListInfo;
public class GtasksListService {
@ -62,12 +59,12 @@ public class GtasksListService {
}
@SuppressWarnings("nls")
public void updateLists(JSONArray newLists) throws JSONException {
public void updateLists(GoogleTaskListInfo[] remoteLists) {
readLists();
for(int i = 0; i < newLists.length(); i++) {
JSONObject remote = newLists.getJSONObject(i);
for(int i = 0; i < remoteLists.length; i++) {
GoogleTaskListInfo remote = remoteLists[i];
String id = remote.getString("id");
String id = remote.getId();
StoreObject local = null;
for(StoreObject list : lists) {
if(list.getValue(GtasksList.REMOTE_ID).equals(id)) {
@ -81,7 +78,7 @@ public class GtasksListService {
local.setValue(StoreObject.TYPE, GtasksList.TYPE);
local.setValue(GtasksList.REMOTE_ID, id);
local.setValue(GtasksList.NAME, remote.getString("title"));
local.setValue(GtasksList.NAME, remote.getName());
local.setValue(GtasksList.ORDER, i);
storeObjectDao.persist(local);
}

@ -4,6 +4,7 @@ import com.todoroo.andlib.data.AbstractModel;
import com.todoroo.andlib.data.Property.IntegerProperty;
import com.todoroo.andlib.data.Property.LongProperty;
import com.todoroo.andlib.data.Property.StringProperty;
import com.todoroo.andlib.utility.DateUtilities;
import com.todoroo.astrid.data.Metadata;
import com.todoroo.astrid.utility.Preferences;
@ -14,18 +15,19 @@ import com.todoroo.astrid.utility.Preferences;
*/
public class GtasksMetadata {
private static final int VALUE_UNSET = -1;
static final int VALUE_UNSET = -1;
/** metadata key */
public static final String METADATA_KEY = "gtasks"; //$NON-NLS-1$
/** task id in google */
public static final LongProperty ID = new LongProperty(Metadata.TABLE,
public static final StringProperty ID = new StringProperty(Metadata.TABLE,
Metadata.VALUE1.name);
public static final StringProperty LIST_ID = new StringProperty(Metadata.TABLE,
Metadata.VALUE2.name);
/** parent task id, or 0 if top level task */
public static final LongProperty PARENT_TASK = new LongProperty(Metadata.TABLE,
Metadata.VALUE3.name);
@ -43,11 +45,16 @@ public class GtasksMetadata {
public static Metadata createEmptyMetadata(long taskId) {
Metadata metadata = new Metadata();
metadata.setValue(Metadata.KEY, GtasksMetadata.METADATA_KEY);
metadata.setValue(ID, (long)VALUE_UNSET);
metadata.setValue(LIST_ID, Preferences.getStringValue(GtasksPreferenceService.PREF_DEFAULT_LIST));
metadata.setValue(ID, ""); //$NON-NLS-1$
String defaultList = Preferences.getStringValue(GtasksPreferenceService.PREF_DEFAULT_LIST);
if(defaultList == null)
throw new NullPointerException("No default list has been set."); //$NON-NLS-1$
metadata.setValue(LIST_ID, defaultList);
metadata.setValue(PARENT_TASK, (long)VALUE_UNSET);
metadata.setValue(INDENT, 0);
metadata.setValue(ORDER, VALUE_UNSET);
metadata.setValue(ORDER, (int)(DateUtilities.now() / 1000L));
if(taskId > AbstractModel.NO_ID)
metadata.setValue(Metadata.TASK, taskId);
return metadata;

@ -4,18 +4,13 @@
package com.todoroo.astrid.gtasks;
import java.util.ArrayList;
import java.util.Stack;
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.sql.Criterion;
import com.todoroo.astrid.api.Filter;
import com.todoroo.astrid.core.PluginServices;
import com.todoroo.astrid.dao.MetadataDao.MetadataCriteria;
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;
import com.todoroo.astrid.sync.SyncMetadataService;
@ -30,7 +25,6 @@ import com.todoroo.astrid.sync.SyncProviderUtilities;
public final class GtasksMetadataService extends SyncMetadataService<GtasksTaskContainer> {
@Autowired private GtasksPreferenceService gtasksPreferenceService;
@Autowired private GtasksListService gtasksListService;
public GtasksMetadataService() {
super(ContextManager.getContext());
@ -63,55 +57,9 @@ public final class GtasksMetadataService extends SyncMetadataService<GtasksTaskC
return gtasksPreferenceService;
}
/**
* 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;
Filter filter = GtasksFilterExposer.filterFromList(list);
TodorooCursor<Task> cursor = PluginServices.getTaskService().fetchFiltered(filter.sqlQuery, null, Task.ID);
try {
Long[] ids = new Long[cursor.getCount()];
int order = 0;
int previousIndent = -1;
Stack<Long> taskHierarchyStack = new Stack<Long>();
for(cursor.moveToFirst(); !cursor.isAfterLast(); cursor.moveToNext()) {
long taskId = cursor.getLong(0);
ids[order] = taskId;
Metadata metadata = getTaskMetadata(taskId);
if(metadata == null)
continue;
metadata.setValue(GtasksMetadata.ORDER, order++);
int indent = metadata.getValue(GtasksMetadata.INDENT);
for(int i = indent; i <= previousIndent; 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 = indent;
}
PluginServices.getTaskService().clearDetails(Task.ID.in(ids));
} finally {
cursor.close();
}
@Override
public Criterion getMetadataWithRemoteId() {
return GtasksMetadata.ID.neq(""); //$NON-NLS-1$
}
}

@ -23,6 +23,7 @@ import com.todoroo.astrid.utility.Flags;
abstract public class GtasksOrderAction extends BroadcastReceiver {
@Autowired private GtasksMetadataService gtasksMetadataService;
@Autowired private GtasksTaskListUpdater gtasksTaskListUpdater;
abstract int getDelta();
@ -38,7 +39,7 @@ abstract public class GtasksOrderAction extends BroadcastReceiver {
if(metadata == null)
return;
gtasksMetadataService.updateMetadataForList(metadata.getValue(GtasksMetadata.LIST_ID));
gtasksTaskListUpdater.updateMetadataForList(metadata.getValue(GtasksMetadata.LIST_ID));
metadata = gtasksMetadataService.getTaskMetadata(taskId);
int oldOrder = metadata.getValue(GtasksMetadata.ORDER);

@ -3,10 +3,6 @@
*/
package com.todoroo.astrid.gtasks;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
@ -27,41 +23,17 @@ public class GtasksSyncActionExposer extends BroadcastReceiver {
@Autowired private GtasksPreferenceService gtasksPreferenceService;
@Autowired private GtasksListService gtasksListService;
@Override
public void onReceive(Context context, Intent intent) {
ContextManager.setContext(context);
DependencyInjectionService.getInstance().inject(this);
if(intent.getBooleanExtra("setup", false)) {
gtasksPreferenceService.setToken("haha");
try {
JSONArray newLists = new JSONArray();
JSONObject list = new JSONObject();
list.put("id", "1");
list.put("title", "Tim's Tasks");
newLists.put(list);
list = new JSONObject();
list.put("id", "2");
list.put("title", "Travel");
newLists.put(list);
gtasksListService.updateLists(newLists);
} catch (JSONException e) {
throw new RuntimeException(e);
}
System.err.println("you've ben set up the bomb.");
return;
}
// if we aren't logged in, don't expose sync action
//if(!gtasksPreferenceService.isLoggedIn())
// return;
if(!gtasksPreferenceService.isLoggedIn())
return;
Intent syncIntent = new Intent(intent.getAction(), null,
context, GtasksSyncActionExposer.class);
syncIntent.putExtra("setup", true);
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, syncIntent, 0);
SyncAction syncAction = new SyncAction(context.getString(R.string.gtasks_GPr_header),
pendingIntent);

@ -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;
}
}

@ -19,6 +19,10 @@ public class GtasksTaskContainer extends SyncContainer {
public Metadata gtaskMetadata;
// position information
public String parentId = null;
public String priorSiblingId = null;
public GtasksTaskContainer(Task task, ArrayList<Metadata> metadata, Metadata gtaskMetadata) {
this.task = task;
this.metadata = metadata;

@ -14,8 +14,8 @@ import com.todoroo.andlib.service.ContextManager;
import com.todoroo.andlib.sql.Criterion;
import com.todoroo.andlib.sql.Query;
import com.todoroo.astrid.data.Metadata;
import com.todoroo.astrid.data.Task;
import com.todoroo.astrid.data.MetadataApiDao.MetadataCriteria;
import com.todoroo.astrid.data.Task;
import com.todoroo.astrid.sync.SyncMetadataService;
import com.todoroo.astrid.sync.SyncProviderUtilities;
@ -67,4 +67,9 @@ public final class MilkMetadataService extends SyncMetadataService<MilkTaskConta
return cursor;
}
@Override
public Criterion getMetadataWithRemoteId() {
return MilkTaskFields.TASK_ID.neq(""); //$NON-NLS-1$
}
}

@ -15,6 +15,7 @@ import com.todoroo.astrid.gtasks.GtasksListService;
import com.todoroo.astrid.gtasks.GtasksMetadataService;
import com.todoroo.astrid.gtasks.GtasksPreferenceService;
import com.todoroo.astrid.tags.TagService;
import com.todoroo.astrid.gtasks.GtasksTaskListUpdater;
import com.todoroo.astrid.utility.Constants;
/**
@ -69,6 +70,7 @@ public class AstridDependencyInjector extends AbstractDependencyInjector {
injectables.put("gtasksPreferenceService", GtasksPreferenceService.class);
injectables.put("gtasksListService", GtasksListService.class);
injectables.put("gtasksMetadataService", GtasksMetadataService.class);
injectables.put("gtasksTaskListUpdater", GtasksTaskListUpdater.class);
// com.todoroo.astrid.tags
injectables.put("tagService", TagService.class);

@ -88,6 +88,12 @@ public class Preferences {
return PreferenceManager.getDefaultSharedPreferences(context);
}
/** @return true if given preference is set */
public static boolean isSet(String key) {
Context context = ContextManager.getContext();
return getPrefs(context).contains(key);
}
// --- preference fetching (string)
/** Gets an string value from a string preference. Returns null
@ -276,6 +282,4 @@ public class Preferences {
editor.commit();
}
}

@ -11,6 +11,8 @@ 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.astrid.utility.Flags;
import com.todoroo.astrid.utility.Preferences;
public class GtasksDecorationExposerTest extends DatabaseTestCase {
@ -93,6 +95,7 @@ public class GtasksDecorationExposerTest extends DatabaseTestCase {
StoreObject list = new StoreObject();
list.setValue(GtasksList.REMOTE_ID, "1");
list.setValue(GtasksList.NAME, "lamo");
Flags.set(Flags.GTASKS);
return GtasksFilterExposer.filterFromList(list);
}
@ -106,6 +109,7 @@ public class GtasksDecorationExposerTest extends DatabaseTestCase {
}
private Filter nonGtasksFilter() {
Flags.checkAndClear(Flags.GTASKS);
return CoreFilterExposer.buildInboxFilter(getContext().getResources());
}
@ -127,4 +131,11 @@ public class GtasksDecorationExposerTest extends DatabaseTestCase {
preferences.setLoggedIn(status);
}
@Override
protected void setUp() throws Exception {
super.setUp();
if(!Preferences.isSet(GtasksPreferenceService.PREF_DEFAULT_LIST))
Preferences.setString(GtasksPreferenceService.PREF_DEFAULT_LIST, "list");
}
}

@ -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);
}
}
}

@ -65,6 +65,7 @@ public class GtasksIndentActionTest extends DatabaseTestCase {
PluginServices.getTaskService().save(task);
Metadata metadata = GtasksMetadata.createEmptyMetadata(task.getId());
metadata.setValue(GtasksMetadata.INDENT, indentation);
metadata.setValue(GtasksMetadata.LIST_ID, "list");
metadata.setValue(Metadata.TASK, task.getId());
PluginServices.getMetadataService().save(metadata);
return task;
@ -85,12 +86,10 @@ public class GtasksIndentActionTest extends DatabaseTestCase {
action.onReceive(getContext(), intent);
}
private void givenTask(Task taskToTest) {
task = taskToTest;
}
private Task taskWithoutMetadata() {
Task task = new Task();
PluginServices.getTaskService().save(task);

@ -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;
}
}

@ -3,6 +3,7 @@ package com.todoroo.astrid.gtasks;
public class GtasksTestPreferenceService extends GtasksPreferenceService {
private boolean loggedIn = false;
private long syncDate = 0;
public void setLoggedIn(boolean loggedIn) {
this.loggedIn = loggedIn;
@ -13,4 +14,12 @@ public class GtasksTestPreferenceService extends GtasksPreferenceService {
return loggedIn;
}
public void setSyncDate(long date) {
syncDate = date;
}
@Override
public long getLastSyncDate() {
return syncDate;
}
}

@ -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);
}
}

@ -5,6 +5,7 @@ import java.io.File;
import com.todoroo.andlib.test.TodorooTestCaseWithInjector;
import com.todoroo.astrid.dao.Database;
import com.todoroo.astrid.legacy.AlarmDatabase;
import com.todoroo.astrid.provider.ProviderTestUtilities;
import com.todoroo.astrid.service.AstridDependencyInjector;
/**
@ -34,6 +35,8 @@ public class DatabaseTestCase extends TodorooTestCaseWithInjector {
// empty out test databases
database.clear();
database.openForWriting();
ProviderTestUtilities.setDatabaseOverride(database);
}
/**

Loading…
Cancel
Save