diff --git a/astrid/.classpath b/astrid/.classpath index d479a46be..9ec68927d 100644 --- a/astrid/.classpath +++ b/astrid/.classpath @@ -4,7 +4,7 @@ - + diff --git a/astrid/api-src/com/todoroo/astrid/api/Filter.java b/astrid/api-src/com/todoroo/astrid/api/Filter.java index 7cb103cbf..404f5c075 100644 --- a/astrid/api-src/com/todoroo/astrid/api/Filter.java +++ b/astrid/api-src/com/todoroo/astrid/api/Filter.java @@ -22,11 +22,6 @@ import com.todoroo.andlib.sql.QueryTemplate; */ public final class Filter extends FilterListItem { - /** - * Plug-in Identifier - */ - public final String plugin; - /** * Expanded title of this filter. This is displayed at the top * of the screen when user is viewing this filter. @@ -62,9 +57,6 @@ public final class Filter extends FilterListItem { /** * Utility constructor for creating a TaskList object - * - * @param plugin - * {@link Addon} identifier that encompasses object * @param listingTitle * Title of this item as displayed on the lists page, e.g. Inbox * @param title @@ -75,9 +67,8 @@ public final class Filter extends FilterListItem { * @param valuesForNewTasks * see {@link sqlForNewTasks} */ - public Filter(String plugin, String listingTitle, - String title, QueryTemplate sqlQuery, ContentValues valuesForNewTasks) { - this.plugin = plugin; + public Filter(String listingTitle, String title, + QueryTemplate sqlQuery, ContentValues valuesForNewTasks) { this.listingTitle = listingTitle; this.title = title; this.sqlQuery = sqlQuery.toString(); @@ -90,8 +81,8 @@ public final class Filter extends FilterListItem { * @param plugin * {@link Addon} identifier that encompasses object */ - protected Filter(String plugin) { - this.plugin = plugin; + protected Filter() { + // do nothing } // --- parcelable @@ -108,7 +99,6 @@ public final class Filter extends FilterListItem { */ @Override public void writeToParcel(Parcel dest, int flags) { - dest.writeString(plugin); super.writeToParcel(dest, flags); dest.writeString(title); dest.writeString(sqlQuery); @@ -124,7 +114,7 @@ public final class Filter extends FilterListItem { * {@inheritDoc} */ public Filter createFromParcel(Parcel source) { - Filter item = new Filter(source.readString()); + Filter item = new Filter(); item.readFromParcel(source); item.title = source.readString(); item.sqlQuery = source.readString(); diff --git a/astrid/api-src/com/todoroo/astrid/api/SynchronizationProvider.java b/astrid/api-src/com/todoroo/astrid/api/SynchronizationProvider.java index 1cae40f09..2ce821401 100644 --- a/astrid/api-src/com/todoroo/astrid/api/SynchronizationProvider.java +++ b/astrid/api-src/com/todoroo/astrid/api/SynchronizationProvider.java @@ -25,14 +25,13 @@ import com.todoroo.andlib.service.DependencyInjectionService; import com.todoroo.andlib.service.ExceptionService; import com.todoroo.andlib.service.NotificationManager; import com.todoroo.astrid.model.Task; -import com.todoroo.astrid.rmilk.MilkPreferences; -import com.todoroo.astrid.service.TaskService; +import com.todoroo.astrid.utility.Constants; /** * A helper class for writing synchronization services for Astrid. This class * contains logic for merging incoming changes and writing outgoing changes. *

- * Use {@link synchronize} as the entry point for your synchronization service, + * Use {@link initiate} as the entry point for your synchronization service, * which should handle authentication and then call {@link synchronizeTasks} to * initiate synchronization. * @@ -41,15 +40,13 @@ import com.todoroo.astrid.service.TaskService; */ public abstract class SynchronizationProvider { - /** Notification Manager id for RMilk notifications */ - private static final int RMILK_NOTIFICATION_ID = -1; - // --- abstract methods - your services should implement these /** - * Synchronize with the service + * Perform authenticate and other pre-synchronization steps, then + * synchronize. */ - abstract public void synchronize(); + abstract protected void initiate(); /** * Push variables from given task to the remote server. @@ -77,6 +74,14 @@ public abstract class SynchronizationProvider { */ abstract protected Task read(Task task) throws IOException; + /** + * Save task. Used to save local tasks that have been updated and remote + * tasks that need to be created locally + * + * @param task + */ + abstract protected void save(Task task) throws IOException; + /** * Finds a task in the list with the same remote identifier(s) as * the task passed in @@ -92,9 +97,6 @@ public abstract class SynchronizationProvider { // --- implementation - @Autowired - private TaskService taskService; - @Autowired private ExceptionService exceptionService; @@ -110,6 +112,27 @@ public abstract class SynchronizationProvider { notification = new Notification(icon, null, when); } + public void synchronize() { + Context context = ContextManager.getContext(); + + // display notification + notificationIntent = PendingIntent.getActivity(context, 0, new Intent(), 0); + postUpdate(context, context.getString(R.string.SyP_progress_starting)); + final NotificationManager nm = new NotificationManager.AndroidNotificationManager(context); + nm.notify(Constants.NOTIFICATION_SYNC, notification); + + // start next step in background thread + new Thread(new Runnable() { + public void run() { + try { + initiate(); + } finally { + nm.cancel(Constants.NOTIFICATION_SYNC); + } + } + }).start(); + } + // --- utilities /** @@ -148,12 +171,6 @@ public abstract class SynchronizationProvider { Context context = ContextManager.getContext(); Resources r = context.getResources(); - // create notification - notificationIntent = PendingIntent.getActivity(context, 0, new Intent(context, MilkPreferences.class), 0); - postUpdate(context, r.getString(R.string.rmilk_progress_starting)); - NotificationManager nm = new NotificationManager.AndroidNotificationManager(context); - nm.notify(RMILK_NOTIFICATION_ID, notification); - // create internal data structures HashMap remoteNewTaskNameMap = new HashMap(); length = data.remoteUpdated.size(); @@ -171,7 +188,7 @@ public abstract class SynchronizationProvider { task.readFromCursor(data.localCreated); String taskTitle = task.getValue(Task.TITLE); - postUpdate(context, r.getString(R.string.rmilk_progress_localtx, + postUpdate(context, r.getString(R.string.SyP_progress_localtx, taskTitle)); /* If there exists an incoming remote task with the same name and no @@ -185,10 +202,14 @@ public abstract class SynchronizationProvider { transferIdentifiers(remote, task); push(task, remote); - read(remote); + + // re-read remote task after merge + Task newRemote = read(remote); + remote.mergeWith(newRemote.getMergedValues()); } else { create(task); } + save(task); } // 2. UPDATE: for each updated local task @@ -196,14 +217,17 @@ public abstract class SynchronizationProvider { for(int i = 0; i < length; i++) { data.localUpdated.moveToNext(); task.readFromCursor(data.localUpdated); - postUpdate(context, r.getString(R.string.rmilk_progress_localtx, + postUpdate(context, r.getString(R.string.SyP_progress_localtx, task.getValue(Task.TITLE))); // if there is a conflict, merge Task remote = matchTask(data.remoteUpdated, task); if(remote != null) { push(task, remote); - read(remote); + + // re-read remote task after merge + Task newRemote = read(remote); + remote.mergeWith(newRemote.getMergedValues()); } else { push(task, null); } @@ -243,11 +267,10 @@ public abstract class SynchronizationProvider { length = data.remoteUpdated.size(); for(int i = 0; i < length; i++) { task = data.remoteUpdated.get(i); - postUpdate(context, r.getString(R.string.rmilk_progress_remotetx, + postUpdate(context, r.getString(R.string.SyP_progress_remotetx, task.getValue(Task.TITLE))); - // save the data (TODO: save metadata) - taskService.save(task, true); + save(task); } } diff --git a/astrid/api-src/com/todoroo/astrid/api/TaskDetail.java b/astrid/api-src/com/todoroo/astrid/api/TaskDetail.java index d413f473a..4d24501e8 100644 --- a/astrid/api-src/com/todoroo/astrid/api/TaskDetail.java +++ b/astrid/api-src/com/todoroo/astrid/api/TaskDetail.java @@ -14,11 +14,6 @@ import android.os.Parcelable; */ public final class TaskDetail implements Parcelable { - /** - * Plug-in Identifier - */ - public final String plugin; - /** * Text of detail */ @@ -31,28 +26,23 @@ public final class TaskDetail implements Parcelable { /** * Creates a TaskDetail object - * - * @param plugin - * {@link Addon} identifier that encompasses object * @param text * text to display * @param color * color to use for text. Use 0 for default color */ - public TaskDetail(String plugin, String text, int color) { - this.plugin = plugin; + public TaskDetail(String text, int color) { this.text = text; this.color = color; } /** * Convenience constructor to make a TaskDetail with default color - * * @param text * text to display */ - public TaskDetail(String plugin, String text) { - this(plugin, text, 0); + public TaskDetail(String text) { + this(text, 0); } // --- parcelable helpers @@ -68,7 +58,6 @@ public final class TaskDetail implements Parcelable { * {@inheritDoc} */ public void writeToParcel(Parcel dest, int flags) { - dest.writeString(plugin); dest.writeString(text); dest.writeInt(color); } @@ -81,7 +70,7 @@ public final class TaskDetail implements Parcelable { * {@inheritDoc} */ public TaskDetail createFromParcel(Parcel source) { - return new TaskDetail(source.readString(), source.readString(), source.readInt()); + return new TaskDetail(source.readString(), source.readInt()); } /** diff --git a/astrid/common-src/com/todoroo/andlib/data/GenericDao.java b/astrid/common-src/com/todoroo/andlib/data/GenericDao.java index b90c2e5d5..eac9a74c8 100644 --- a/astrid/common-src/com/todoroo/andlib/data/GenericDao.java +++ b/astrid/common-src/com/todoroo/andlib/data/GenericDao.java @@ -10,9 +10,11 @@ import java.lang.reflect.InvocationTargetException; import android.content.ContentValues; import android.database.Cursor; +import android.util.Log; import com.todoroo.andlib.sql.Criterion; import com.todoroo.andlib.sql.Query; +import com.todoroo.astrid.utility.Constants; @@ -62,6 +64,8 @@ public class GenericDao { */ public TodorooCursor query(Query query) { query.from(table); + if(Constants.DEBUG) + Log.d("SQL-" + modelClass.getSimpleName(), query.toString()); //$NON-NLS-1$ Cursor cursor = database.rawQuery(query.toString(), null); return new TodorooCursor(cursor, query.getFields()); } diff --git a/astrid/common-src/com/todoroo/andlib/service/DependencyInjectionService.java b/astrid/common-src/com/todoroo/andlib/service/DependencyInjectionService.java index 04991f6a4..a21af3067 100644 --- a/astrid/common-src/com/todoroo/andlib/service/DependencyInjectionService.java +++ b/astrid/common-src/com/todoroo/andlib/service/DependencyInjectionService.java @@ -39,10 +39,6 @@ public class DependencyInjectionService { @SuppressWarnings("nls") public void inject(Object caller) { - if(Constants.DEBUG) { - Log.d("INJECTOR", "Invoked Injector on " + caller, new Throwable()); - } - // Traverse through class and all parent classes, looking for // fields declared with the @Autowired annotation and using // dependency injection to set them as appropriate @@ -103,7 +99,7 @@ public class DependencyInjectionService { Object injection = injector.getInjection(caller, field); if (injection != null) { if(Constants.DEBUG) - Log.e("INJECTOR", injector + ":" + caller + "." + field.getName() + " => " + injection); + Log.d("INJECTOR", injector + ":" + caller + "." + field.getName() + " => " + injection); field.set(caller, injection); return; } diff --git a/astrid/common-src/com/todoroo/andlib/sql/Criterion.java b/astrid/common-src/com/todoroo/andlib/sql/Criterion.java index 76c46d580..32d836a38 100644 --- a/astrid/common-src/com/todoroo/andlib/sql/Criterion.java +++ b/astrid/common-src/com/todoroo/andlib/sql/Criterion.java @@ -22,6 +22,13 @@ public abstract class Criterion { } }; + public static Criterion none = new Criterion(Operator.exists) { + @Override + protected void populate(StringBuilder sb) { + sb.append(0); + } + }; + public static Criterion and(final Criterion criterion, final Criterion... criterions) { return new Criterion(Operator.and) { diff --git a/astrid/common-src/com/todoroo/andlib/sql/Functions.java b/astrid/common-src/com/todoroo/andlib/sql/Functions.java index a0ebac26d..ca56da772 100644 --- a/astrid/common-src/com/todoroo/andlib/sql/Functions.java +++ b/astrid/common-src/com/todoroo/andlib/sql/Functions.java @@ -1,5 +1,6 @@ package com.todoroo.andlib.sql; + @SuppressWarnings("nls") public final class Functions { @@ -13,4 +14,8 @@ public final class Functions { return value.toString(); } + public static Field upper(Field title) { + return new Field("UPPER(" + title.toString() + ")"); + } + } diff --git a/astrid/plugin-src/com/todoroo/astrid/core/CoreFilterExposer.java b/astrid/plugin-src/com/todoroo/astrid/core/CoreFilterExposer.java index df998d898..12653bacd 100644 --- a/astrid/plugin-src/com/todoroo/astrid/core/CoreFilterExposer.java +++ b/astrid/plugin-src/com/todoroo/astrid/core/CoreFilterExposer.java @@ -47,8 +47,7 @@ public final class CoreFilterExposer extends BroadcastReceiver { FilterCategory extended = new FilterCategory(CorePlugin.IDENTIFIER, r.getString(R.string.BFE_Extended), new Filter[5]); - Filter alphabetical = new Filter(CorePlugin.IDENTIFIER, - r.getString(R.string.BFE_Alphabetical), + Filter alphabetical = new Filter(r.getString(R.string.BFE_Alphabetical), r.getString(R.string.BFE_Alphabetical), new QueryTemplate().where(Criterion.and(TaskCriteria.isActive(), TaskCriteria.isVisible(DateUtilities.now()))). @@ -56,8 +55,7 @@ public final class CoreFilterExposer extends BroadcastReceiver { null); alphabetical.listingIcon = ((BitmapDrawable)r.getDrawable(R.drawable.tango_alpha)).getBitmap(); - Filter recent = new Filter(CorePlugin.IDENTIFIER, - r.getString(R.string.BFE_Recent), + Filter recent = new Filter(r.getString(R.string.BFE_Recent), r.getString(R.string.BFE_Recent), new QueryTemplate().orderBy(Order.desc(Task.MODIFICATION_DATE)).limit(15), null); @@ -65,26 +63,23 @@ public final class CoreFilterExposer extends BroadcastReceiver { ContentValues hiddenValues = new ContentValues(); hiddenValues.put(Task.HIDE_UNTIL.name, DateUtilities.now() + DateUtilities.ONE_DAY); - Filter hidden = new Filter(CorePlugin.IDENTIFIER, - r.getString(R.string.BFE_Hidden), + Filter hidden = new Filter(r.getString(R.string.BFE_Hidden), r.getString(R.string.BFE_Hidden), new QueryTemplate().where(Criterion.and(TaskCriteria.isActive(), Criterion.not(TaskCriteria.isVisible(DateUtilities.now())))). orderBy(Order.asc(Task.HIDE_UNTIL)), - hiddenValues); + hiddenValues); hidden.listingIcon = ((BitmapDrawable)r.getDrawable(R.drawable.tango_clouds)).getBitmap(); ContentValues completedValues = new ContentValues(); hiddenValues.put(Task.COMPLETION_DATE.name, DateUtilities.now()); - Filter completed = new Filter(CorePlugin.IDENTIFIER, r.getString(R.string.BFE_Completed), - r.getString(R.string.BFE_Completed), + Filter completed = new Filter(r.getString(R.string.BFE_Completed), r.getString(R.string.BFE_Completed), new QueryTemplate().where(Criterion.and(TaskCriteria.completedBefore(DateUtilities.now()), Criterion.not(TaskCriteria.isDeleted()))). orderBy(Order.desc(Task.COMPLETION_DATE)), completedValues); completed.listingIcon = ((BitmapDrawable)r.getDrawable(R.drawable.tango_check)).getBitmap(); - Filter deleted = new Filter(CorePlugin.IDENTIFIER, - r.getString(R.string.BFE_Deleted), + Filter deleted = new Filter(r.getString(R.string.BFE_Deleted), r.getString(R.string.BFE_Deleted), new QueryTemplate().where(TaskCriteria.isDeleted()). orderBy(Order.desc(Task.DELETION_DATE)), @@ -112,8 +107,7 @@ public final class CoreFilterExposer extends BroadcastReceiver { * @return */ public static Filter buildInboxFilter(Resources r) { - Filter inbox = new Filter(CorePlugin.IDENTIFIER, r.getString(R.string.BFE_Active), - r.getString(R.string.BFE_Active_title), + Filter inbox = new Filter(r.getString(R.string.BFE_Active), r.getString(R.string.BFE_Active_title), new QueryTemplate().where(Criterion.and(TaskCriteria.isActive(), TaskCriteria.isVisible(DateUtilities.now()))), null); diff --git a/astrid/plugin-src/com/todoroo/astrid/notes/NoteDetailExposer.java b/astrid/plugin-src/com/todoroo/astrid/notes/NoteDetailExposer.java index 320279e87..c09009339 100644 --- a/astrid/plugin-src/com/todoroo/astrid/notes/NoteDetailExposer.java +++ b/astrid/plugin-src/com/todoroo/astrid/notes/NoteDetailExposer.java @@ -48,7 +48,7 @@ public class NoteDetailExposer extends BroadcastReceiver { if(notes.length() == 0) return; - TaskDetail taskDetail = new TaskDetail(NotesPlugin.IDENTIFIER, notes); + TaskDetail taskDetail = new TaskDetail(notes); // transmit Intent broadcastIntent = new Intent(AstridApiConstants.BROADCAST_SEND_DETAILS); diff --git a/astrid/plugin-src/com/todoroo/astrid/reminders/NotificationActivity.java b/astrid/plugin-src/com/todoroo/astrid/reminders/NotificationActivity.java index f99c5b662..644e2e7eb 100644 --- a/astrid/plugin-src/com/todoroo/astrid/reminders/NotificationActivity.java +++ b/astrid/plugin-src/com/todoroo/astrid/reminders/NotificationActivity.java @@ -65,8 +65,7 @@ public class NotificationActivity extends Activity { return; Intent taskListIntent = new Intent(this, TaskListActivity.class); - Filter itemFilter = new Filter(ReminderPlugin.IDENTIFIER, - getString(R.string.rmd_NoA_filter), + Filter itemFilter = new Filter(getString(R.string.rmd_NoA_filter), getString(R.string.rmd_NoA_filter), new QueryTemplate().where(TaskCriteria.byId(id)), null); diff --git a/astrid/plugin-src/com/todoroo/astrid/repeats/RepeatDetailExposer.java b/astrid/plugin-src/com/todoroo/astrid/repeats/RepeatDetailExposer.java index 2121bcebc..9d730216b 100644 --- a/astrid/plugin-src/com/todoroo/astrid/repeats/RepeatDetailExposer.java +++ b/astrid/plugin-src/com/todoroo/astrid/repeats/RepeatDetailExposer.java @@ -104,7 +104,7 @@ public class RepeatDetailExposer extends BroadcastReceiver { detail = context.getString(R.string.repeat_detail_duedate, interval); - TaskDetail taskDetail = new TaskDetail(RepeatsPlugin.IDENTIFIER, detail); + TaskDetail taskDetail = new TaskDetail(detail); // transmit Intent broadcastIntent = new Intent(AstridApiConstants.BROADCAST_SEND_DETAILS); diff --git a/astrid/plugin-src/com/todoroo/astrid/rmilk/DetailExposer.java b/astrid/plugin-src/com/todoroo/astrid/rmilk/DetailExposer.java index 62cc801f9..f40df01e0 100644 --- a/astrid/plugin-src/com/todoroo/astrid/rmilk/DetailExposer.java +++ b/astrid/plugin-src/com/todoroo/astrid/rmilk/DetailExposer.java @@ -7,10 +7,10 @@ import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; -import com.todoroo.astrid.R; +import com.timsu.astrid.R; import com.todoroo.astrid.api.AstridApiConstants; import com.todoroo.astrid.api.TaskDetail; -import com.todoroo.astrid.model.Task; +import com.todoroo.astrid.model.Metadata; import com.todoroo.astrid.rmilk.data.MilkDataService; /** @@ -34,22 +34,20 @@ public class DetailExposer extends BroadcastReceiver { if(taskId == -1) return; - MilkDataService service = new MilkDataService(context); - Task task = service.readTask(taskId); - - if(task == null) + Metadata metadata = MilkDataService.getInstance().getTaskMetadata(taskId); + if(metadata == null) return; TaskDetail[] details = new TaskDetail[2]; - String listId = task.getValue(MilkDataService.LIST_ID); - if(listId != null && listId.length() > 0) + long listId = metadata.getValue(MilkDataService.LIST_ID); + if(listId > 0) details[0] = new TaskDetail(context.getString(R.string.rmilk_TLA_list, - service.getList(listId))); + MilkDataService.getInstance().getListName(listId))); else details[0] = null; - int repeat = task.getValue(MilkDataService.REPEAT); + int repeat = metadata.getValue(MilkDataService.REPEATING); if(repeat != 0) details[1] = new TaskDetail(context.getString(R.string.rmilk_TLA_repeat)); else @@ -57,7 +55,8 @@ public class DetailExposer extends BroadcastReceiver { // transmit Intent broadcastIntent = new Intent(AstridApiConstants.BROADCAST_SEND_DETAILS); - broadcastIntent.putExtra(AstridApiConstants.EXTRAS_ITEMS, details); + broadcastIntent.putExtra(AstridApiConstants.EXTRAS_ADDON, Utilities.IDENTIFIER); + broadcastIntent.putExtra(AstridApiConstants.EXTRAS_RESPONSE, details); broadcastIntent.putExtra(AstridApiConstants.EXTRAS_TASK_ID, taskId); context.sendBroadcast(broadcastIntent, AstridApiConstants.PERMISSION_READ); } diff --git a/astrid/plugin-src/com/todoroo/astrid/rmilk/FilterExposer.java b/astrid/plugin-src/com/todoroo/astrid/rmilk/FilterExposer.java index 41f7c81b3..4084ee119 100644 --- a/astrid/plugin-src/com/todoroo/astrid/rmilk/FilterExposer.java +++ b/astrid/plugin-src/com/todoroo/astrid/rmilk/FilterExposer.java @@ -15,9 +15,9 @@ import com.todoroo.astrid.api.Filter; import com.todoroo.astrid.api.FilterCategory; import com.todoroo.astrid.api.FilterListHeader; import com.todoroo.astrid.api.FilterListItem; +import com.todoroo.astrid.model.Metadata; import com.todoroo.astrid.rmilk.Utilities.ListContainer; import com.todoroo.astrid.rmilk.data.MilkDataService; -import com.todoroo.astrid.rmilk.data.MilkTask; /** * Exposes filters based on RTM lists @@ -27,16 +27,16 @@ import com.todoroo.astrid.rmilk.data.MilkTask; */ public class FilterExposer extends BroadcastReceiver { - @SuppressWarnings("nls") private Filter filterFromList(Context context, ListContainer list) { String listTitle = context.getString(R.string.rmilk_FEx_list_item). replace("$N", list.name).replace("$C", Integer.toString(list.count)); String title = context.getString(R.string.rmilk_FEx_list_title, list.name); - ContentValues values = new ContentValues(); // TODO - Filter filter = new Filter(Utilities.IDENTIFIER, listTitle, title, - new QueryTemplate().join(MilkDataService.MILK_JOIN).where(MilkTask.LIST_ID.eq(list.id)), - values); + ContentValues values = new ContentValues(); + values.put(Metadata.KEY.name, MilkDataService.METADATA_KEY); + values.put(MilkDataService.LIST_ID.name, list.id); + Filter filter = new Filter(listTitle, title, new QueryTemplate().join(MilkDataService.METADATA_JOIN).where(MilkDataService.LIST_ID.eq(list.id)), + values); return filter; } @@ -47,8 +47,7 @@ public class FilterExposer extends BroadcastReceiver { if(!Utilities.isLoggedIn()) return; - MilkDataService service = new MilkDataService(context); - ListContainer[] lists = service.getListsWithCounts(); + ListContainer[] lists = MilkDataService.getInstance().getListsWithCounts(); // If user does not have any tags, don't show this section at all if(lists.length == 0) @@ -68,6 +67,7 @@ public class FilterExposer extends BroadcastReceiver { list[0] = rtmHeader; list[1] = rtmLists; Intent broadcastIntent = new Intent(AstridApiConstants.BROADCAST_SEND_FILTERS); + broadcastIntent.putExtra(AstridApiConstants.EXTRAS_ADDON, Utilities.IDENTIFIER); broadcastIntent.putExtra(AstridApiConstants.EXTRAS_RESPONSE, list); context.sendBroadcast(broadcastIntent, AstridApiConstants.PERMISSION_READ); } diff --git a/astrid/plugin-src/com/todoroo/astrid/rmilk/Utilities.java b/astrid/plugin-src/com/todoroo/astrid/rmilk/Utilities.java index ce90bcf2a..ac95da289 100644 --- a/astrid/plugin-src/com/todoroo/astrid/rmilk/Utilities.java +++ b/astrid/plugin-src/com/todoroo/astrid/rmilk/Utilities.java @@ -3,8 +3,6 @@ */ package com.todoroo.astrid.rmilk; -import java.util.Date; - import android.content.SharedPreferences; import android.content.SharedPreferences.Editor; import android.preference.PreferenceManager; @@ -42,7 +40,7 @@ public class Utilities { public ListContainer(long id, String name) { this.id = id; this.name = name; - this.count = -1; + this.count = 0; } @Override @@ -87,22 +85,15 @@ public class Utilities { editor.commit(); } - /** RTM Last Successful Sync Date, or null */ - public static Date getLastSyncDate() { - Long value = getPrefs().getLong(PREF_LAST_SYNC, 0); - if (value == 0) - return null; - return new Date(value); + /** RTM Last Successful Sync Date, or 0 */ + public static long getLastSyncDate() { + return getPrefs().getLong(PREF_LAST_SYNC, 0); } /** Set RTM Last Successful Sync Date */ - public static void setLastSyncDate(Date date) { + public static void setLastSyncDate(long time) { Editor editor = getPrefs().edit(); - if (date == null) { - editor.remove(PREF_LAST_SYNC); - } else { - editor.putLong(PREF_LAST_SYNC, date.getTime()); - } + editor.putLong(PREF_LAST_SYNC, time); editor.commit(); } diff --git a/astrid/plugin-src/com/todoroo/astrid/rmilk/api/ServiceImpl.java b/astrid/plugin-src/com/todoroo/astrid/rmilk/api/ServiceImpl.java index 4662f3b31..d298c269a 100644 --- a/astrid/plugin-src/com/todoroo/astrid/rmilk/api/ServiceImpl.java +++ b/astrid/plugin-src/com/todoroo/astrid/rmilk/api/ServiceImpl.java @@ -50,7 +50,7 @@ import com.todoroo.astrid.rmilk.api.data.RtmTask.Priority; * @author timsu January 2009 */ @SuppressWarnings("nls") -public class ServiceImpl +public class ServiceImpl { public final static String SERVER_HOST_NAME = "api.rememberthemilk.com"; //$NON-NLS-1$ @@ -288,7 +288,7 @@ public class ServiceImpl } /** - * Adds a task, name, to the list specified by list_id. + * Adds a task, name, to the list specified by list_id. * @param timelineId * @param listId can be null to omit this parameter (assumes Inbox) * @param name @@ -303,9 +303,9 @@ public class ServiceImpl response = invoker.invoke(new Param("method", "rtm.tasks.add"), new Param("timeline", timelineId), new Param("list_id", listId), new Param("name", name), new Param("auth_token", currentAuthToken), new Param("api_key", applicationInfo.getApiKey())); else - response = invoker.invoke(new Param("method", "rtm.tasks.add"), new Param("timeline", timelineId), + response = invoker.invoke(new Param("method", "rtm.tasks.add"), new Param("timeline", timelineId), new Param("name", name), new Param("auth_token", currentAuthToken), new Param("api_key", applicationInfo.getApiKey())); - + RtmTaskList rtmTaskList = new RtmTaskList(response); if (rtmTaskList.getSeries().size() == 1) { @@ -409,6 +409,8 @@ public class ServiceImpl public RtmTaskSeries tasks_moveTo(String timelineId, String fromListId, String toListId, String taskSeriesId, String taskId) throws ServiceException { + if(fromListId.equals(toListId)) + return null; Element elt = invoker.invoke(new Param("method", "rtm.tasks.moveTo"), new Param("timeline", timelineId), new Param("from_list_id", fromListId), new Param("to_list_id", toListId), new Param("taskseries_id", taskSeriesId), new Param("task_id", taskId), new Param("auth_token", currentAuthToken), new Param("api_key", applicationInfo.getApiKey())); diff --git a/astrid/plugin-src/com/todoroo/astrid/rmilk/api/data/RtmList.java b/astrid/plugin-src/com/todoroo/astrid/rmilk/api/data/RtmList.java index b81db6628..f208efd98 100644 --- a/astrid/plugin-src/com/todoroo/astrid/rmilk/api/data/RtmList.java +++ b/astrid/plugin-src/com/todoroo/astrid/rmilk/api/data/RtmList.java @@ -27,13 +27,15 @@ public class RtmList extends RtmData { private final String id; private final boolean smart; private final boolean archived; + private final int position; private final String name; - public RtmList(String id, String name, boolean smart) { + public RtmList(String id, String name, boolean smart, boolean archived, int position) { this.id = id; this.name = name; this.smart = smart; - this.archived = false; + this.archived = archived; + this.position = position; } public RtmList(Element elt) { @@ -41,6 +43,7 @@ public class RtmList extends RtmData { name = elt.getAttribute("name"); smart = elt.getAttribute("smart") == "1"; archived = elt.getAttribute("archived") == "1"; + position = Integer.parseInt(elt.getAttribute("position")); } public String getId() { @@ -57,5 +60,13 @@ public class RtmList extends RtmData { public boolean isArchived() { return archived; -} + } + + public int getPosition() { + return position; + } + + public boolean isInbox() { + return position == -1; + } } diff --git a/astrid/plugin-src/com/todoroo/astrid/rmilk/data/MilkDataService.java b/astrid/plugin-src/com/todoroo/astrid/rmilk/data/MilkDataService.java index a48df830d..d52b2f26e 100644 --- a/astrid/plugin-src/com/todoroo/astrid/rmilk/data/MilkDataService.java +++ b/astrid/plugin-src/com/todoroo/astrid/rmilk/data/MilkDataService.java @@ -3,6 +3,7 @@ */ package com.todoroo.astrid.rmilk.data; +import java.util.HashMap; import java.util.Map; import java.util.Random; @@ -12,25 +13,62 @@ import com.todoroo.andlib.data.GenericDao; import com.todoroo.andlib.data.Property; import com.todoroo.andlib.data.TodorooCursor; import com.todoroo.andlib.data.Property.CountProperty; +import com.todoroo.andlib.data.Property.IntegerProperty; +import com.todoroo.andlib.data.Property.LongProperty; 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.Join; import com.todoroo.andlib.sql.Order; import com.todoroo.andlib.sql.Query; +import com.todoroo.andlib.utility.DateUtilities; +import com.todoroo.astrid.dao.MetadataDao; import com.todoroo.astrid.dao.TaskDao; +import com.todoroo.astrid.dao.MetadataDao.MetadataCriteria; +import com.todoroo.astrid.dao.TaskDao.TaskCriteria; +import com.todoroo.astrid.model.Metadata; import com.todoroo.astrid.model.Task; +import com.todoroo.astrid.rmilk.Utilities; import com.todoroo.astrid.rmilk.Utilities.ListContainer; import com.todoroo.astrid.rmilk.api.data.RtmList; import com.todoroo.astrid.rmilk.api.data.RtmLists; -public class MilkDataService { +public final class MilkDataService { - // --- constants + // --- public constants - /** for joining milk task table with task table */ - public static final Join MILK_JOIN = Join.left(MilkTask.TABLE, - Task.ID.eq(MilkTask.TASK)); + /** metadata key */ + public static final String METADATA_KEY = "rmilk"; //$NON-NLS-1$ + + /** {@link MilkList} id */ + public static final LongProperty LIST_ID = new LongProperty(Metadata.TABLE, + Metadata.VALUE1.name); + + /** RTM Task Series Id */ + public static final LongProperty TASK_SERIES_ID = new LongProperty(Metadata.TABLE, + Metadata.VALUE2.name); + + /** RTM Task Id */ + public static final LongProperty TASK_ID = new LongProperty(Metadata.TABLE, + Metadata.VALUE3.name); + + /** Whether task repeats in RTM (1 or 0) */ + public static final IntegerProperty REPEATING = new IntegerProperty(Metadata.TABLE, + Metadata.VALUE4.name); + + /** Utility for joining tasks with metadata */ + public static final Join METADATA_JOIN = Join.left(Metadata.TABLE, Task.ID.eq(Metadata.TASK)); + + // --- singleton + + private static MilkDataService instance = null; + + public static synchronized MilkDataService getInstance() { + if(instance == null) + instance = new MilkDataService(ContextManager.getContext()); + return instance; + } // --- instance variables @@ -39,18 +77,19 @@ public class MilkDataService { private final MilkDatabase milkDatabase = new MilkDatabase(); private final GenericDao milkListDao; - private final GenericDao milkTaskDao; @Autowired private TaskDao taskDao; + @Autowired + private MetadataDao metadataDao; + static final Random random = new Random(); - public MilkDataService(Context context) { + private MilkDataService(Context context) { this.context = context; DependencyInjectionService.getInstance().inject(this); milkListDao = new GenericDao(MilkList.class, milkDatabase); - milkTaskDao = new GenericDao(MilkTask.class, milkDatabase); milkDatabase.openForReading(); } @@ -60,7 +99,7 @@ public class MilkDataService { * Clears RTM metadata information. Used when user logs out of RTM */ public void clearMetadata() { - milkTaskDao.deleteWhere(Criterion.all); + metadataDao.deleteWhere(Metadata.KEY.eq(METADATA_KEY)); } /** @@ -70,20 +109,85 @@ public class MilkDataService { */ public TodorooCursor getLocallyCreated(Property[] properties) { return - taskDao.query(Query.select(properties).join(MILK_JOIN).where( - Criterion.or(MilkTask.UPDATED.eq(0), MilkTask.TASK.isNull()))); + taskDao.query(Query.select(properties).join(METADATA_JOIN).where( + Criterion.or(TASK_ID.eq(0), Metadata.TASK.isNull()))); } /** * Gets tasks that were modified since last sync * @param properties - * @return + * @return null if never sync'd */ public TodorooCursor getLocallyUpdated(Property[] properties) { + long lastSyncDate = Utilities.getLastSyncDate(); + if(lastSyncDate == 0) + return taskDao.query(Query.select(Task.ID).where(Criterion.none)); return - taskDao.query(Query.select(properties).join(MILK_JOIN). - where(Criterion.and(MilkTask.UPDATED.neq(0), - MilkTask.UPDATED.lt(Task.MODIFICATION_DATE)))); + taskDao.query(Query.select(properties).join(METADATA_JOIN). + where(Task.MODIFICATION_DATE.gt(lastSyncDate))); + } + + /** + * Searches for a local task with same remote id, updates this task's id + * @param task + */ + public void updateFromLocalCopy(Task task) { + if(task.getId() != Task.NO_ID) + return; + TodorooCursor cursor = taskDao.query(Query.select(Task.ID). + join(METADATA_JOIN).where(Criterion.and(LIST_ID.eq(task.getValue(LIST_ID)), + TASK_SERIES_ID.eq(task.getValue(TASK_SERIES_ID)), + TASK_ID.eq(task.getValue(TASK_ID))))); + try { + if(cursor.getCount() == 0) + return; + cursor.moveToFirst(); + task.setId(cursor.get(Task.ID)); + } finally { + cursor.close(); + } + } + + /** + * Saves a task and its metadata + * @param task + */ + public void saveTaskAndMetadata(Task task) { + Metadata metadata = new Metadata(); + metadata.setValue(Metadata.KEY, METADATA_KEY); + metadata.setValue(LIST_ID, task.getValue(LIST_ID)); + metadata.setValue(TASK_SERIES_ID, task.getValue(TASK_SERIES_ID)); + metadata.setValue(TASK_ID, task.getValue(TASK_ID)); + metadata.setValue(REPEATING, task.getValue(REPEATING)); + + task.clearValue(LIST_ID); + task.clearValue(TASK_SERIES_ID); + task.clearValue(TASK_ID); + task.clearValue(REPEATING); + + taskDao.save(task, true); + metadata.setValue(Metadata.TASK, task.getId()); + + metadataDao.deleteWhere(MetadataCriteria.byTaskAndwithKey(task.getId(), + METADATA_KEY)); + metadataDao.persist(metadata); + } + + /** + * Reads metadata out of a task + * @return null if no metadata found + */ + public Metadata getTaskMetadata(long taskId) { + TodorooCursor cursor = metadataDao.query(Query.select(Metadata.PROPERTIES).where( + MetadataCriteria.byTaskAndwithKey(taskId, METADATA_KEY))); + try { + if(cursor.getCount() == 0) + return null; + cursor.moveToFirst(); + return new Metadata(cursor); + } finally { + cursor.close(); + } } // --- list methods @@ -93,7 +197,7 @@ public class MilkDataService { * @param listId * @return null if no list by this id exists, otherwise list name */ - public String getList(String listId) { + public String getListName(long listId) { TodorooCursor cursor = milkListDao.query(Query.select( MilkList.NAME).where(MilkList.ID.eq(listId))); try { @@ -113,22 +217,40 @@ public class MilkDataService { public ListContainer[] getListsWithCounts() { CountProperty COUNT = new CountProperty(); + // read list names + TodorooCursor listCursor = milkListDao.query(Query.select(MilkList.ID, + MilkList.NAME).where(MilkList.ARCHIVED.eq(0)).orderBy(Order.asc(MilkList.POSITION))); + ListContainer[] lists = new ListContainer[listCursor.getCount()]; + HashMap listIdToContainerMap; + try { + int length = listCursor.getCount(); + if(length == 0) + return lists; + listIdToContainerMap = new HashMap(length); + MilkList list = new MilkList(); + for(int i = 0; i < length; i++) { + listCursor.moveToNext(); + list.readFromCursor(listCursor); + lists[i] = new ListContainer(list); + listIdToContainerMap.put(list.getId(), lists[i]); + } + } finally { + listCursor.close(); + } + // read all list counts - TodorooCursor cursor = milkTaskDao.query(Query.select(MilkList.ID, MilkList.NAME, COUNT). - join(Join.inner(MilkList.TABLE, MilkTask.LIST_ID.eq(MilkList.ID))). - orderBy(Order.asc(MilkList.POSITION), Order.asc(MilkList.ID)). - groupBy(MilkTask.LIST_ID)); - ListContainer[] containers = new ListContainer[cursor.getCount()]; + TodorooCursor cursor = metadataDao.query(Query.select(LIST_ID, COUNT). + join(Join.inner(Task.TABLE, Metadata.TASK.eq(Task.ID))). + where(Criterion.and(TaskCriteria.isVisible(DateUtilities.now()), TaskCriteria.isActive())). + groupBy(LIST_ID)); try { - for(int i = 0; i < containers.length; i++) { - cursor.moveToNext(); - long id = cursor.get(MilkList.ID); - String name = cursor.get(MilkList.NAME); - int count = cursor.get(COUNT); - containers[i] = new ListContainer(id, name); - containers[i].count = count; + for(cursor.moveToFirst(); !cursor.isAfterLast(); cursor.moveToNext()) { + ListContainer container = listIdToContainerMap.get(cursor.get(LIST_ID)); + if(container != null) { + container.count = cursor.get(COUNT); + } } - return containers; + return lists; } finally { cursor.close(); } @@ -160,18 +282,31 @@ public class MilkDataService { }*/ /** - * Clears current cache of RTM lists and re-populates + * Clears current cache of RTM lists and re-populates. Returns the inbox + * list. + * * @param lists + * @return list with the name "inbox" */ - public void setLists(RtmLists lists) { + public MilkList setLists(RtmLists lists) { milkListDao.deleteWhere(Criterion.all); MilkList model = new MilkList(); + MilkList inbox = null; for(Map.Entry list : lists.getLists().entrySet()) { + if(list.getValue().isSmart()) + continue; model.setValue(MilkList.ID, Long.parseLong(list.getValue().getId())); model.setValue(MilkList.NAME, list.getValue().getName()); + model.setValue(MilkList.POSITION, list.getValue().getPosition()); model.setValue(MilkList.ARCHIVED, list.getValue().isArchived()? 1 : 0); milkListDao.createNew(model); + + if(list.getValue().isInbox()) { + inbox = model; + model = new MilkList(); + } } + return inbox; } } diff --git a/astrid/plugin-src/com/todoroo/astrid/rmilk/data/MilkDatabase.java b/astrid/plugin-src/com/todoroo/astrid/rmilk/data/MilkDatabase.java index 12f134e53..2d077f0fd 100644 --- a/astrid/plugin-src/com/todoroo/astrid/rmilk/data/MilkDatabase.java +++ b/astrid/plugin-src/com/todoroo/astrid/rmilk/data/MilkDatabase.java @@ -37,7 +37,6 @@ public class MilkDatabase extends AbstractDatabase { */ public static final Table[] TABLES = new Table[] { MilkList.TABLE, - MilkTask.TABLE }; // --- implementation diff --git a/astrid/plugin-src/com/todoroo/astrid/rmilk/data/MilkTask.java b/astrid/plugin-src/com/todoroo/astrid/rmilk/data/MilkTask.java deleted file mode 100644 index c79726ac6..000000000 --- a/astrid/plugin-src/com/todoroo/astrid/rmilk/data/MilkTask.java +++ /dev/null @@ -1,104 +0,0 @@ -/** - * See the file "LICENSE" for the full license governing this code. - */ -package com.todoroo.astrid.rmilk.data; - - -import android.content.ContentValues; - -import com.todoroo.andlib.data.AbstractModel; -import com.todoroo.andlib.data.Property; -import com.todoroo.andlib.data.Table; -import com.todoroo.andlib.data.TodorooCursor; -import com.todoroo.andlib.data.Property.IntegerProperty; -import com.todoroo.andlib.data.Property.LongProperty; -import com.todoroo.astrid.model.Task; - -/** - * Data Model which represents a list in RTM - * - * @author Tim Su - * - */ -@SuppressWarnings("nls") -public class MilkTask extends AbstractModel { - - // --- table - - public static final Table TABLE = new Table("tasks", MilkTask.class); - - // --- properties - - /** Task Id */ - public static final LongProperty TASK = new LongProperty( - TABLE, "task"); - - /** {@link MilkList} id */ - public static final LongProperty LIST_ID = new LongProperty( - TABLE, "listId"); - - /** RTM Task Id */ - public static final LongProperty TASK_SERIES_ID = new LongProperty( - TABLE, "taskSeriesId"); - - /** RTM Task Series Id */ - public static final LongProperty TASK_ID = new LongProperty( - TABLE, "taskId"); - - /** Whether task repeats in RTM (1 or 0) */ - public static final IntegerProperty REPEATING = new IntegerProperty( - TABLE, "repeating"); - - /** Unixtime task was last updated in RTM */ - public static final LongProperty UPDATED = new LongProperty( - TABLE, "updated"); - - /** List of all properties for this model */ - public static final Property[] PROPERTIES = generateProperties(MilkTask.class); - - // --- defaults - - /** Default values container */ - private static final ContentValues defaultValues = new ContentValues(); - - static { - defaultValues.put(REPEATING.name, 0); - defaultValues.put(UPDATED.name, 0); - } - - @Override - public ContentValues getDefaultValues() { - return defaultValues; - } - - // --- data access boilerplate - - public MilkTask() { - super(); - } - - public MilkTask(TodorooCursor cursor) { - this(); - readPropertiesFromCursor(cursor); - } - - public void readFromCursor(TodorooCursor cursor) { - super.readPropertiesFromCursor(cursor); - } - - @Override - public long getId() { - return getIdHelper(TASK); - }; - - - // --- parcelable helpers - - private static final Creator CREATOR = new ModelCreator(Task.class); - - @Override - protected Creator getCreator() { - return CREATOR; - } - -} diff --git a/astrid/plugin-src/com/todoroo/astrid/rmilk/sync/RTMSyncProvider.java b/astrid/plugin-src/com/todoroo/astrid/rmilk/sync/RTMSyncProvider.java index 6d3ef24a6..69383baa5 100644 --- a/astrid/plugin-src/com/todoroo/astrid/rmilk/sync/RTMSyncProvider.java +++ b/astrid/plugin-src/com/todoroo/astrid/rmilk/sync/RTMSyncProvider.java @@ -7,14 +7,11 @@ import java.io.IOException; import java.util.ArrayList; import java.util.Date; -import android.app.Activity; -import android.app.ProgressDialog; import android.content.Context; import android.content.Intent; import android.content.res.Resources; import android.os.Handler; import android.text.TextUtils; -import android.util.Log; import com.flurry.android.FlurryAgent; import com.timsu.astrid.R; @@ -44,7 +41,6 @@ import com.todoroo.astrid.rmilk.api.data.RtmTasks; import com.todoroo.astrid.rmilk.api.data.RtmAuth.Perms; import com.todoroo.astrid.rmilk.api.data.RtmTask.Priority; import com.todoroo.astrid.rmilk.data.MilkDataService; -import com.todoroo.astrid.rmilk.data.MilkTask; import com.todoroo.astrid.service.AstridDependencyInjector; public class RTMSyncProvider extends SynchronizationProvider { @@ -52,7 +48,6 @@ public class RTMSyncProvider extends SynchronizationProvider { private ServiceImpl rtmService = null; private String timeline = null; private MilkDataService dataService = null; - private ProgressDialog progressDialog = null; static { AstridDependencyInjector.initialize(); @@ -73,27 +68,12 @@ public class RTMSyncProvider extends SynchronizationProvider { // ------------------------------------------------------- public methods // ---------------------------------------------------------------------- - @Override - public void synchronize() { - Context context = ContextManager.getContext(); - dataService = new MilkDataService(context); - - if(context instanceof Activity) { - progressDialog = dialogUtilities.progressDialog(context, - context.getString(R.string.DLG_communicating_text)); - progressDialog.show(); - } - - // authenticate the user. this will automatically call the next step - authenticate(context); - } - /** * Sign out of RTM, deleting all synchronization metadata */ public void signOut() { Utilities.setToken(null); - Utilities.setLastSyncDate(null); + Utilities.setLastSyncDate(0); dataService.clearMetadata(); } @@ -138,19 +118,22 @@ public class RTMSyncProvider extends SynchronizationProvider { } } - private void authenticate(final Context context) { - new Thread(new Runnable() { - public void run() { - authenticateInNewThread(context); - } - }).start(); + @Override + protected void initiate() { + Context context = ContextManager.getContext(); + dataService = MilkDataService.getInstance(); + + // authenticate the user. this will automatically call the next step + authenticate(context); } + + /** * Perform authentication with RTM. Will open the SyncBrowser if necessary */ @SuppressWarnings("nls") - private void authenticateInNewThread(final Context context) { + private void authenticate(final Context context) { final Resources r = context.getResources(); FlurryAgent.onEvent("rtm-started"); @@ -237,10 +220,10 @@ public class RTMSyncProvider extends SynchronizationProvider { // read all tasks ArrayList remoteChanges = new ArrayList(); - Date lastSyncDate = Utilities.getLastSyncDate(); + Date lastSyncDate = new Date(Utilities.getLastSyncDate()); boolean shouldSyncIndividualLists = false; String filter = null; - if(lastSyncDate == null) + if(lastSyncDate.getTime() == 0) filter = "status:incomplete"; //$NON-NLS-1$ // 1st time sync: get unfinished tasks // try the quick synchronization @@ -273,8 +256,7 @@ public class RTMSyncProvider extends SynchronizationProvider { SyncData syncData = populateSyncData(remoteChanges); synchronizeTasks(syncData); - Date syncTime = new Date(System.currentTimeMillis()); - Utilities.setLastSyncDate(syncTime); + Utilities.setLastSyncDate(DateUtilities.now()); FlurryAgent.onEvent("rtm-sync-finished"); //$NON-NLS-1$ } catch (IllegalStateException e) { @@ -301,10 +283,10 @@ public class RTMSyncProvider extends SynchronizationProvider { Task.CREATION_DATE, Task.COMPLETION_DATE, Task.DELETION_DATE, - MilkTask.LIST_ID, - MilkTask.TASK_SERIES_ID, - MilkTask.TASK_ID, - MilkTask.REPEATING, + MilkDataService.LIST_ID, + MilkDataService.TASK_SERIES_ID, + MilkDataService.TASK_ID, + MilkDataService.REPEATING, // TODO tags }; @@ -350,23 +332,29 @@ public class RTMSyncProvider extends SynchronizationProvider { @Override protected void create(Task task) throws IOException { String listId = null; - if(task.containsValue(MilkTask.LIST_ID)) - listId = Long.toString(task.getValue(MilkTask.LIST_ID)); + if(task.containsValue(MilkDataService.LIST_ID) && task.getValue(MilkDataService.LIST_ID) != null) + listId = Long.toString(task.getValue(MilkDataService.LIST_ID)); + if("0".equals(listId)) //$NON-NLS-1$ + listId = null; RtmTaskSeries rtmTask = rtmService.tasks_add(timeline, listId, task.getValue(Task.TITLE)); - push(task, parseRemoteTask(rtmTask)); + Task newRemoteTask = parseRemoteTask(rtmTask); + task.mergeWith(newRemoteTask.getMergedValues()); + push(task, newRemoteTask); } - /** Send changes for the given TaskProxy across the wire */ + /** + * 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(Task task, Task remoteTask) throws IOException { - RtmId id = new RtmId(task); - // fetch remote task for comparison - if(remoteTask == null) { + if(remoteTask == null) remoteTask = read(task); - } + RtmId id = new RtmId(task); if(shouldTransmit(task, Task.TITLE, remoteTask)) rtmService.tasks_setName(timeline, id.listId, id.taskSeriesId, @@ -386,42 +374,52 @@ public class RTMSyncProvider extends SynchronizationProvider { rtmService.tasks_complete(timeline, id.listId, id.taskSeriesId, id.taskId); } - if(shouldTransmit(task, Task.DELETION_DATE, remoteTask)) + if(shouldTransmit(task, Task.DELETION_DATE, remoteTask) && + task.getValue(Task.DELETION_DATE) > 0) rtmService.tasks_delete(timeline, id.listId, id.taskSeriesId, id.taskId); - if(shouldTransmit(task, MilkTask.LIST_ID, remoteTask) && remoteTask != null) - rtmService.tasks_moveTo(timeline, Long.toString(remoteTask.getValue(MilkTask.LIST_ID)), + // TODO tags, notes, url, ... + + + if(remoteTask != null && shouldTransmit(task, MilkDataService.LIST_ID, remoteTask) && + task.getValue(MilkDataService.LIST_ID) != 0) + rtmService.tasks_moveTo(timeline, Long.toString(remoteTask.getValue(MilkDataService.LIST_ID)), id.listId, id.taskSeriesId, id.taskId); } + @Override + protected void save(Task task) throws IOException { + // if task has no id, try to find a corresponding task + if(task.getId() == Task.NO_ID) { + dataService.updateFromLocalCopy(task); + } + + dataService.saveTaskAndMetadata(task); + } + /** Create a task proxy for the given RtmTaskSeries */ private Task parseRemoteTask(RtmTaskSeries rtmTaskSeries) { Task task = new Task(); - task.setValue(MilkTask.LIST_ID, Long.parseLong(rtmTaskSeries.getList().getId())); - task.setValue(MilkTask.TASK_SERIES_ID, Long.parseLong(rtmTaskSeries.getId())); + task.setValue(MilkDataService.LIST_ID, Long.parseLong(rtmTaskSeries.getList().getId())); + task.setValue(MilkDataService.TASK_SERIES_ID, Long.parseLong(rtmTaskSeries.getId())); task.setValue(Task.TITLE, rtmTaskSeries.getName()); - task.setValue(MilkTask.REPEATING, rtmTaskSeries.hasRecurrence() ? 1 : 0); + task.setValue(MilkDataService.REPEATING, rtmTaskSeries.hasRecurrence() ? 1 : 0); RtmTask rtmTask = rtmTaskSeries.getTask(); - if(rtmTask != null) { - task.setValue(Task.CREATION_DATE, DateUtilities.dateToUnixtime(rtmTask.getAdded())); - task.setValue(Task.COMPLETION_DATE, DateUtilities.dateToUnixtime(rtmTask.getCompleted())); - task.setValue(Task.DELETION_DATE, DateUtilities.dateToUnixtime(rtmTask.getDeleted())); - if(rtmTask.getDue() != null) { - task.setValue(Task.DUE_DATE, - task.createDueDate(rtmTask.getHasDueTime() ? Task.URGENCY_SPECIFIC_DAY_TIME : - Task.URGENCY_SPECIFIC_DAY, DateUtilities.dateToUnixtime(rtmTask.getDue()))); - } else { - task.setValue(Task.DUE_DATE, 0L); - } - task.setValue(Task.IMPORTANCE, rtmTask.getPriority().ordinal()); + task.setValue(MilkDataService.TASK_ID, Long.parseLong(rtmTask.getId())); + task.setValue(Task.CREATION_DATE, DateUtilities.dateToUnixtime(rtmTask.getAdded())); + task.setValue(Task.COMPLETION_DATE, DateUtilities.dateToUnixtime(rtmTask.getCompleted())); + task.setValue(Task.DELETION_DATE, DateUtilities.dateToUnixtime(rtmTask.getDeleted())); + if(rtmTask.getDue() != null) { + task.setValue(Task.DUE_DATE, + task.createDueDate(rtmTask.getHasDueTime() ? Task.URGENCY_SPECIFIC_DAY_TIME : + Task.URGENCY_SPECIFIC_DAY, DateUtilities.dateToUnixtime(rtmTask.getDue()))); } else { - // error in upstream code, try to handle gracefully - Log.e("rtmsync", "Got null task parsing remote task series", //$NON-NLS-1$//$NON-NLS-2$ - new Throwable()); + task.setValue(Task.DUE_DATE, 0L); } + task.setValue(Task.IMPORTANCE, rtmTask.getPriority().ordinal()); return task; } @@ -431,9 +429,9 @@ public class RTMSyncProvider extends SynchronizationProvider { int length = tasks.size(); for(int i = 0; i < length; i++) { Task task = tasks.get(i); - if(task.getValue(MilkTask.LIST_ID).equals(target.getValue(MilkTask.LIST_ID)) && - task.getValue(MilkTask.TASK_SERIES_ID).equals(target.getValue(MilkTask.TASK_SERIES_ID)) && - task.getValue(MilkTask.TASK_ID).equals(target.getValue(MilkTask.TASK_ID))) + if(task.getValue(MilkDataService.LIST_ID).equals(target.getValue(MilkDataService.LIST_ID)) && + task.getValue(MilkDataService.TASK_SERIES_ID).equals(target.getValue(MilkDataService.TASK_SERIES_ID)) && + task.getValue(MilkDataService.TASK_ID).equals(target.getValue(MilkDataService.TASK_ID))) return task; } return null; @@ -441,7 +439,7 @@ public class RTMSyncProvider extends SynchronizationProvider { @Override protected Task read(Task task) throws IOException { - RtmTaskSeries rtmTask = rtmService.tasks_getTask(Long.toString(task.getValue(MilkTask.TASK_SERIES_ID)), + RtmTaskSeries rtmTask = rtmService.tasks_getTask(Long.toString(task.getValue(MilkDataService.TASK_SERIES_ID)), task.getValue(Task.TITLE)); if(rtmTask != null) return parseRemoteTask(rtmTask); @@ -450,9 +448,9 @@ public class RTMSyncProvider extends SynchronizationProvider { @Override protected void transferIdentifiers(Task source, Task destination) { - destination.setValue(MilkTask.LIST_ID, source.getValue(MilkTask.LIST_ID)); - destination.setValue(MilkTask.TASK_SERIES_ID, source.getValue(MilkTask.TASK_SERIES_ID)); - destination.setValue(MilkTask.TASK_ID, source.getValue(MilkTask.TASK_ID)); + destination.setValue(MilkDataService.LIST_ID, source.getValue(MilkDataService.LIST_ID)); + destination.setValue(MilkDataService.TASK_SERIES_ID, source.getValue(MilkDataService.TASK_SERIES_ID)); + destination.setValue(MilkDataService.TASK_ID, source.getValue(MilkDataService.TASK_ID)); } // ---------------------------------------------------------------------- @@ -466,9 +464,9 @@ public class RTMSyncProvider extends SynchronizationProvider { public String listId; public RtmId(Task task) { - taskId = Long.toString(task.getValue(MilkTask.TASK_ID)); - taskSeriesId = Long.toString(task.getValue(MilkTask.TASK_SERIES_ID)); - listId = Long.toString(task.getValue(MilkTask.LIST_ID)); + taskId = Long.toString(task.getValue(MilkDataService.TASK_ID)); + taskSeriesId = Long.toString(task.getValue(MilkDataService.TASK_SERIES_ID)); + listId = Long.toString(task.getValue(MilkDataService.LIST_ID)); } } diff --git a/astrid/plugin-src/com/todoroo/astrid/tags/TagDetailExposer.java b/astrid/plugin-src/com/todoroo/astrid/tags/TagDetailExposer.java index 147d91c70..47bccc86e 100644 --- a/astrid/plugin-src/com/todoroo/astrid/tags/TagDetailExposer.java +++ b/astrid/plugin-src/com/todoroo/astrid/tags/TagDetailExposer.java @@ -35,8 +35,7 @@ public class TagDetailExposer extends BroadcastReceiver { if(tagList.length() == 0) return; - TaskDetail taskDetail = new TaskDetail(TagsPlugin.IDENTIFIER, - context.getString(R.string.tag_TLA_detail, tagList)); + TaskDetail taskDetail = new TaskDetail(context.getString(R.string.tag_TLA_detail, tagList)); // transmit Intent broadcastIntent = new Intent(AstridApiConstants.BROADCAST_SEND_DETAILS); diff --git a/astrid/plugin-src/com/todoroo/astrid/tags/TagFilterExposer.java b/astrid/plugin-src/com/todoroo/astrid/tags/TagFilterExposer.java index 475c2498b..597207390 100644 --- a/astrid/plugin-src/com/todoroo/astrid/tags/TagFilterExposer.java +++ b/astrid/plugin-src/com/todoroo/astrid/tags/TagFilterExposer.java @@ -40,9 +40,8 @@ public class TagFilterExposer extends BroadcastReceiver { contentValues.put(Metadata.KEY.name, TagService.KEY); contentValues.put(TagService.TAG.name, tag.tag); - Filter filter = new Filter(TagsPlugin.IDENTIFIER, - listTitle, title, - tagTemplate, + Filter filter = new Filter(listTitle, + title, tagTemplate, contentValues); // filters[0].contextMenuLabels = new String[] { @@ -79,8 +78,7 @@ public class TagFilterExposer extends BroadcastReceiver { FilterListHeader tagsHeader = new FilterListHeader(TagsPlugin.IDENTIFIER, context.getString(R.string.tag_FEx_header)); - Filter untagged = new Filter(TagsPlugin.IDENTIFIER, - r.getString(R.string.tag_FEx_untagged), + Filter untagged = new Filter(r.getString(R.string.tag_FEx_untagged), r.getString(R.string.tag_FEx_untagged), tagService.untaggedTemplate(), null); diff --git a/astrid/plugin-src/com/todoroo/astrid/tags/TagService.java b/astrid/plugin-src/com/todoroo/astrid/tags/TagService.java index ea72aaa46..1cc4974a3 100644 --- a/astrid/plugin-src/com/todoroo/astrid/tags/TagService.java +++ b/astrid/plugin-src/com/todoroo/astrid/tags/TagService.java @@ -164,6 +164,7 @@ public class TagService { metadata.setValue(Metadata.KEY, KEY); metadata.setValue(Metadata.TASK, taskId); for(String tag : tags) { + metadata.clearValue(Metadata.ID); metadata.setValue(TAG, tag.trim()); metadataDao.createNew(metadata); } diff --git a/astrid/res/values/strings-3.0.xml b/astrid/res/values/strings-3.0.xml index 5f73e5529..502bf0580 100644 --- a/astrid/res/values/strings-3.0.xml +++ b/astrid/res/values/strings-3.0.xml @@ -402,5 +402,15 @@ If you don\'t want to see the new task right after you complete the old one, you Astrid Team + + + + Logging In... + + Transmitting: %s + + + Receiving: %s + diff --git a/astrid/res/values/strings-legacy.xml b/astrid/res/values/strings-legacy.xml index 6d512cb4f..3e9bd9275 100644 --- a/astrid/res/values/strings-legacy.xml +++ b/astrid/res/values/strings-legacy.xml @@ -322,6 +322,7 @@ occur (it is a minor drain on battery). Merged: %d Reading Remote Data + Reading Remote Data Reading List: %s Synchronizing Repeating Task Transmitting: %s diff --git a/astrid/res/values/strings-rmilk.xml b/astrid/res/values/strings-rmilk.xml index 5e4e61b2f..cfd97d89d 100644 --- a/astrid/res/values/strings-rmilk.xml +++ b/astrid/res/values/strings-rmilk.xml @@ -115,17 +115,10 @@ Error Message: %s - - Astrid: Remember the Milk + + Sync Error! Sorry for the inconvenience! Error: - Connection Error! Check your Internet connection, - or maybe RTM servers (status.rememberthemilk.com), for possible solutions. Sync: Up to date! - Clear data for selected services? - No Synchronizers Enabled! - Last Sync Date: %s - Last AutoSync Attempt: %s - never %s Results Summary - Astrid Tasks: Summary - Remote Server: @@ -133,12 +126,16 @@ Error Message: %s Updated: %d Deleted: %d Merged: %d + + + Astrid: Remember the Milk + Log out / clear synchronization data? + Last Sync Date: %s + Last AutoSync Attempt: %s + never - Starting Sync... - Reading List: %s - Transmitting: %s - Deleting: %s - Receiving: %s + Connection Error! Check your Internet connection, + or maybe RTM servers (status.rememberthemilk.com), for possible solutions. diff --git a/astrid/src-legacy/com/timsu/astrid/sync/RTMSyncProvider.java b/astrid/src-legacy/com/timsu/astrid/sync/RTMSyncProvider.java index f720fcc64..3e9b59351 100644 --- a/astrid/src-legacy/com/timsu/astrid/sync/RTMSyncProvider.java +++ b/astrid/src-legacy/com/timsu/astrid/sync/RTMSyncProvider.java @@ -24,8 +24,8 @@ import java.util.Date; import java.util.HashMap; import java.util.LinkedList; import java.util.Map; -import java.util.Map.Entry; import java.util.StringTokenizer; +import java.util.Map.Entry; import android.content.Context; import android.content.Intent; @@ -38,15 +38,15 @@ import com.mdt.rtm.ApplicationInfo; import com.mdt.rtm.ServiceException; import com.mdt.rtm.ServiceImpl; import com.mdt.rtm.ServiceInternalException; -import com.mdt.rtm.data.RtmAuth.Perms; import com.mdt.rtm.data.RtmList; import com.mdt.rtm.data.RtmLists; import com.mdt.rtm.data.RtmTask; -import com.mdt.rtm.data.RtmTask.Priority; import com.mdt.rtm.data.RtmTaskList; import com.mdt.rtm.data.RtmTaskNote; import com.mdt.rtm.data.RtmTaskSeries; import com.mdt.rtm.data.RtmTasks; +import com.mdt.rtm.data.RtmAuth.Perms; +import com.mdt.rtm.data.RtmTask.Priority; import com.timsu.astrid.R; import com.timsu.astrid.activities.SyncLoginActivity; import com.timsu.astrid.activities.SyncLoginActivity.SyncLoginCallback; @@ -537,7 +537,7 @@ public class RTMSyncProvider extends SynchronizationProvider { /** SynchronizeHelper for remember the milk */ class RtmSyncHelper implements SynchronizeHelper { - private String timeline; + private final String timeline; private String lastCreatedTask = null; private Context context; diff --git a/astrid/src/com/todoroo/astrid/activity/FilterListActivity.java b/astrid/src/com/todoroo/astrid/activity/FilterListActivity.java index 5d336c04c..7d0e8fbb5 100644 --- a/astrid/src/com/todoroo/astrid/activity/FilterListActivity.java +++ b/astrid/src/com/todoroo/astrid/activity/FilterListActivity.java @@ -22,7 +22,6 @@ import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.view.ContextMenu.ContextMenuInfo; -import android.view.ViewGroup.OnHierarchyChangeListener; import android.widget.EditText; import android.widget.ExpandableListView; import android.widget.FrameLayout; @@ -34,6 +33,7 @@ import com.timsu.astrid.R; import com.todoroo.andlib.service.Autowired; import com.todoroo.andlib.service.DependencyInjectionService; import com.todoroo.andlib.service.ExceptionService; +import com.todoroo.andlib.sql.Functions; import com.todoroo.andlib.sql.QueryTemplate; import com.todoroo.andlib.utility.DialogUtilities; import com.todoroo.astrid.adapter.FilterAdapter; @@ -111,9 +111,9 @@ public class FilterListActivity extends ExpandableListActivity { final String intentAction = intent.getAction(); if (Intent.ACTION_SEARCH.equals(intentAction)) { String query = intent.getStringExtra(SearchManager.QUERY).trim(); - Filter filter = new Filter(null, null, - getString(R.string.FLA_search_filter, query), - new QueryTemplate().where(Task.TITLE.like("%" + query + "%")), //$NON-NLS-1$ //$NON-NLS-2$ + Filter filter = new Filter(null, getString(R.string.FLA_search_filter, query), + new QueryTemplate().where(Functions.upper(Task.TITLE).like("%" + //$NON-NLS-1$ + query.toUpperCase() + "%")), null); intent = new Intent(FilterListActivity.this, TaskListActivity.class); intent.putExtra(TaskListActivity.TOKEN_FILTER, filter); @@ -208,7 +208,7 @@ public class FilterListActivity extends ExpandableListActivity { registerForContextMenu(getExpandableListView()); getExpandableListView().setGroupIndicator( getResources().getDrawable(R.drawable.expander_group)); - getExpandableListView().setOnHierarchyChangeListener(new OnHierarchyChangeListener() { + /*getExpandableListView().setOnHierarchyChangeListener(new OnHierarchyChangeListener() { @Override public void onChildViewAdded(View parent, View child) { // auto-expand adapter @@ -233,7 +233,7 @@ public class FilterListActivity extends ExpandableListActivity { public void onChildViewRemoved(View parent, View child) { // do nothing } - }); + });*/ } /** diff --git a/astrid/src/com/todoroo/astrid/activity/TaskListActivity.java b/astrid/src/com/todoroo/astrid/activity/TaskListActivity.java index 786538841..3057488bf 100644 --- a/astrid/src/com/todoroo/astrid/activity/TaskListActivity.java +++ b/astrid/src/com/todoroo/astrid/activity/TaskListActivity.java @@ -174,7 +174,7 @@ public class TaskListActivity extends ListActivity implements OnScrollListener { if(extras.containsKey(TOKEN_FILTER_VALUES)) values = AndroidUtilities.contentValuesFromString(extras.getString(TOKEN_FILTER_VALUES)); - filter = new Filter("", "", title, new QueryTemplate(), values); //$NON-NLS-1$ //$NON-NLS-2$ + filter = new Filter("", title, new QueryTemplate(), values); //$NON-NLS-1$ //$NON-NLS-2$ filter.sqlQuery = sql; } else { filter = CoreFilterExposer.buildInboxFilter(getResources()); diff --git a/astrid/src/com/todoroo/astrid/dao/MetadataDao.java b/astrid/src/com/todoroo/astrid/dao/MetadataDao.java index b216e9101..ee272b9ab 100644 --- a/astrid/src/com/todoroo/astrid/dao/MetadataDao.java +++ b/astrid/src/com/todoroo/astrid/dao/MetadataDao.java @@ -52,6 +52,11 @@ public class MetadataDao extends GenericDao { return Metadata.KEY.eq(key); } + /** Returns all metadata associated with a given key */ + public static Criterion byTaskAndwithKey(long taskId, String key) { + return Criterion.and(withKey(key), byTask(taskId)); + } + } diff --git a/astrid/src/com/todoroo/astrid/service/StartupService.java b/astrid/src/com/todoroo/astrid/service/StartupService.java index 23871425c..156d243a2 100644 --- a/astrid/src/com/todoroo/astrid/service/StartupService.java +++ b/astrid/src/com/todoroo/astrid/service/StartupService.java @@ -8,13 +8,12 @@ import android.app.AlertDialog; import android.app.PendingIntent; import android.content.Context; import android.content.DialogInterface; -import android.content.DialogInterface.OnClickListener; import android.content.Intent; +import android.content.DialogInterface.OnClickListener; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import com.timsu.astrid.R; -import com.timsu.astrid.sync.SynchronizationService; import com.timsu.astrid.utilities.BackupService; import com.todoroo.andlib.service.Autowired; import com.todoroo.andlib.service.ContextManager; @@ -105,10 +104,6 @@ public class StartupService { am.setInexactRepeating(AlarmManager.RTC, 0, Constants.WIDGET_UPDATE_INTERVAL, pendingIntent); - // start synchronization service - if(Constants.SYNCHRONIZE) - SynchronizationService.scheduleService(context); - // start backup service BackupService.scheduleService(context); diff --git a/astrid/src/com/todoroo/astrid/utility/Constants.java b/astrid/src/com/todoroo/astrid/utility/Constants.java index 979d82d4a..3f4035cde 100644 --- a/astrid/src/com/todoroo/astrid/utility/Constants.java +++ b/astrid/src/com/todoroo/astrid/utility/Constants.java @@ -2,6 +2,8 @@ package com.todoroo.astrid.utility; public final class Constants { + // --- general application constants + /** * Flurry API Key */ @@ -17,11 +19,6 @@ public final class Constants { */ public static final boolean OEM = false; - /** - * Whether to display synchronization options - */ - public static final boolean SYNCHRONIZE = !OEM; - /** * Interval to update the widget (in order to detect hidden tasks * becoming visible) @@ -31,5 +28,11 @@ public final class Constants { /** * Whether to turn on debugging logging and UI */ - public static final boolean DEBUG = false; + public static final boolean DEBUG = true; + + // --- notification id's + + /** Notification Manager id for RMilk notifications */ + public static final int NOTIFICATION_SYNC = -1; + }