diff --git a/astrid/api-src/com/todoroo/astrid/api/SynchronizationProvider.java b/astrid/api-src/com/todoroo/astrid/api/SynchronizationProvider.java index bc7db9f1c..763307caf 100644 --- a/astrid/api-src/com/todoroo/astrid/api/SynchronizationProvider.java +++ b/astrid/api-src/com/todoroo/astrid/api/SynchronizationProvider.java @@ -131,7 +131,13 @@ public abstract class SynchronizationProvider { public void synchronize(final Context context) { // display toast if(context instanceof Activity) { - Toast.makeText(context, R.string.SyP_progress_toast, Toast.LENGTH_LONG).show(); + ((Activity) context).runOnUiThread(new Runnable() { + @Override + public void run() { + Toast.makeText(context, R.string.SyP_progress_toast, + Toast.LENGTH_LONG).show(); + } + }); } // display notification @@ -184,14 +190,14 @@ public abstract class SynchronizationProvider { * * @param data synchronization data structure */ - protected void synchronizeTasks(SyncData data) throws IOException { + protected void synchronizeTasks(SyncData data) throws IOException { int length; // create internal data structures HashMap remoteNewTaskNameMap = new HashMap(); length = data.remoteUpdated.size(); for(int i = 0; i < length; i++) { - TaskContainer remote = data.remoteUpdated.get(i); + TYPE remote = data.remoteUpdated.get(i); if(remote.task.getId() != Task.NO_ID) continue; remoteNewTaskNameMap.put(remote.task.getValue(Task.TITLE), i); @@ -201,7 +207,7 @@ public abstract class SynchronizationProvider { length = data.localCreated.getCount(); for(int i = 0; i < length; i++) { data.localCreated.moveToNext(); - TaskContainer local = read(data.localCreated); + TYPE local = read(data.localCreated); String taskTitle = local.task.getValue(Task.TITLE); @@ -212,36 +218,39 @@ public abstract class SynchronizationProvider { */ if (remoteNewTaskNameMap.containsKey(taskTitle)) { int remoteIndex = remoteNewTaskNameMap.remove(taskTitle); - TaskContainer remote = data.remoteUpdated.get(remoteIndex); - remote.task.setId(local.task.getId()); + TYPE remote = data.remoteUpdated.get(remoteIndex); - transferIdentifiers((TYPE)remote, (TYPE)local); - push((TYPE)local, (TYPE)remote); + transferIdentifiers(remote, local); + push(local, remote); - // re-read remote task after merge - data.remoteUpdated.set(remoteIndex, pull((TYPE)remote)); + // re-read remote task after merge, update remote task list + remote = pull(remote); + remote.task.setId(local.task.getId()); + data.remoteUpdated.set(remoteIndex, remote); } else { - create((TYPE)local); + create(local); } - write((TYPE)local); + write(local); } // 2. UPDATE: for each updated local task length = data.localUpdated.getCount(); for(int i = 0; i < length; i++) { data.localUpdated.moveToNext(); - TaskContainer local = read(data.localUpdated); + TYPE local = read(data.localUpdated); // if there is a conflict, merge - int remoteIndex = matchTask((ArrayList)data.remoteUpdated, (TYPE)local); + int remoteIndex = matchTask((ArrayList)data.remoteUpdated, local); if(remoteIndex != -1) { - TaskContainer remote = data.remoteUpdated.get(remoteIndex); - push((TYPE)local, (TYPE)remote); + TYPE remote = data.remoteUpdated.get(remoteIndex); + push(local, remote); // re-read remote task after merge - data.remoteUpdated.set(remoteIndex, pull((TYPE)remote)); + remote = pull(remote); + remote.task.setId(local.task.getId()); + data.remoteUpdated.set(remoteIndex, remote); } else { - push((TYPE)local, null); + push(local, null); } } @@ -252,9 +261,9 @@ public abstract class SynchronizationProvider { // the wire, the new version and the completed old version. The new // version would get merged, then completed, if done in the wrong order. - Collections.sort(data.remoteUpdated, new Comparator() { + Collections.sort(data.remoteUpdated, new Comparator() { private static final int SENTINEL = -2; - private final int check(TaskContainer o1, TaskContainer o2, LongProperty property) { + private final int check(TYPE o1, TYPE o2, LongProperty property) { long o1Property = o1.task.getValue(property); long o2Property = o2.task.getValue(property); if(o1Property != 0 && o2Property != 0) @@ -265,7 +274,7 @@ public abstract class SynchronizationProvider { return 1; return SENTINEL; } - public int compare(TaskContainer o1, TaskContainer o2) { + public int compare(TYPE o1, TYPE o2) { int comparison = check(o1, o2, Task.DELETION_DATE); if(comparison != SENTINEL) return comparison; @@ -278,21 +287,21 @@ public abstract class SynchronizationProvider { length = data.remoteUpdated.size(); for(int i = 0; i < length; i++) { - TaskContainer remote = data.remoteUpdated.get(i); - write((TYPE)remote); + TYPE remote = data.remoteUpdated.get(i); + write(remote); } } // --- helper classes /** data structure builder */ - protected static class SyncData { - public final ArrayList remoteUpdated; + protected static class SyncData { + public final ArrayList remoteUpdated; public final TodorooCursor localCreated; public final TodorooCursor localUpdated; - public SyncData(ArrayList remoteUpdated, + public SyncData(ArrayList remoteUpdated, TodorooCursor localCreated, TodorooCursor localUpdated) { super(); diff --git a/astrid/astrid.launch b/astrid/astrid.launch index e1e654a56..72aa24a68 100644 --- a/astrid/astrid.launch +++ b/astrid/astrid.launch @@ -6,7 +6,7 @@ - + diff --git a/astrid/common-src/com/todoroo/andlib/data/GenericDao.java b/astrid/common-src/com/todoroo/andlib/data/GenericDao.java index eac9a74c8..fccbfadcc 100644 --- a/astrid/common-src/com/todoroo/andlib/data/GenericDao.java +++ b/astrid/common-src/com/todoroo/andlib/data/GenericDao.java @@ -65,7 +65,7 @@ public class GenericDao { public TodorooCursor query(Query query) { query.from(table); if(Constants.DEBUG) - Log.d("SQL-" + modelClass.getSimpleName(), query.toString()); //$NON-NLS-1$ + Log.i("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/plugin-src/com/todoroo/astrid/rmilk/data/MilkDataService.java b/astrid/plugin-src/com/todoroo/astrid/rmilk/data/MilkDataService.java index 0ed300606..ed68b339c 100644 --- a/astrid/plugin-src/com/todoroo/astrid/rmilk/data/MilkDataService.java +++ b/astrid/plugin-src/com/todoroo/astrid/rmilk/data/MilkDataService.java @@ -142,8 +142,11 @@ public final class MilkDataService { Log.e("SAV", "saving " + task.task.getSetValues()); taskDao.save(task.task, true); - metadataDao.deleteWhere(MetadataCriteria.byTaskAndwithKey(task.task.getId(), - MilkTask.METADATA_KEY)); + metadataDao.deleteWhere(Criterion.and(MetadataCriteria.byTask(task.task.getId()), + Criterion.or(MetadataCriteria.withKey(MilkTask.METADATA_KEY), + MetadataCriteria.withKey(MilkNote.METADATA_KEY), + MetadataCriteria.withKey(TagService.KEY)))); + task.metadata.add(MilkTask.create(task)); for(Metadata metadata : task.metadata) { metadata.setValue(Metadata.TASK, task.task.getId()); metadataDao.persist(metadata); @@ -166,8 +169,6 @@ public final class MilkDataService { MetadataCriteria.withKey(MilkTask.METADATA_KEY), MetadataCriteria.withKey(MilkNote.METADATA_KEY))))); try { - if(metadataCursor.getCount() == 0) - return null; for(metadataCursor.moveToFirst(); !metadataCursor.isAfterLast(); metadataCursor.moveToNext()) { metadata.add(new Metadata(metadataCursor)); } diff --git a/astrid/plugin-src/com/todoroo/astrid/rmilk/data/MilkTask.java b/astrid/plugin-src/com/todoroo/astrid/rmilk/data/MilkTask.java index e63a350e1..45cd7af46 100644 --- a/astrid/plugin-src/com/todoroo/astrid/rmilk/data/MilkTask.java +++ b/astrid/plugin-src/com/todoroo/astrid/rmilk/data/MilkTask.java @@ -3,6 +3,7 @@ package com.todoroo.astrid.rmilk.data; import com.todoroo.andlib.data.Property.IntegerProperty; import com.todoroo.andlib.data.Property.LongProperty; import com.todoroo.astrid.model.Metadata; +import com.todoroo.astrid.rmilk.sync.RTMTaskContainer; /** * Metadata entries for a Remember The Milk Task @@ -30,4 +31,20 @@ public class MilkTask { public static final IntegerProperty REPEATING = new IntegerProperty(Metadata.TABLE, Metadata.VALUE4.name); + /** + * Creates a piece of metadata from a remote task + * @param rtmTaskSeries + * @return + */ + public static Metadata create(RTMTaskContainer container) { + Metadata metadata = new Metadata(); + metadata.setValue(Metadata.KEY, METADATA_KEY); + metadata.setValue(MilkTask.LIST_ID, container.listId); + metadata.setValue(MilkTask.TASK_SERIES_ID, container.taskSeriesId); + metadata.setValue(MilkTask.TASK_ID, container.taskId); + metadata.setValue(MilkTask.REPEATING, container.repeating ? 1 : 0); + + return metadata; + } + } 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 8e4e980e3..05d24a4dd 100644 --- a/astrid/plugin-src/com/todoroo/astrid/rmilk/sync/RTMSyncProvider.java +++ b/astrid/plugin-src/com/todoroo/astrid/rmilk/sync/RTMSyncProvider.java @@ -47,7 +47,6 @@ import com.todoroo.astrid.rmilk.api.data.RtmTaskSeries; import com.todoroo.astrid.rmilk.api.data.RtmTasks; import com.todoroo.astrid.rmilk.data.MilkDataService; import com.todoroo.astrid.rmilk.data.MilkNote; -import com.todoroo.astrid.rmilk.data.MilkTask; import com.todoroo.astrid.service.AstridDependencyInjector; import com.todoroo.astrid.tags.TagService; @@ -234,7 +233,7 @@ public class RTMSyncProvider extends SynchronizationProvider { dataService.setLists(lists); // read all tasks - ArrayList remoteChanges = new ArrayList(); + ArrayList remoteChanges = new ArrayList(); Date lastSyncDate = new Date(Utilities.getLastSyncDate()); boolean shouldSyncIndividualLists = false; String filter = null; @@ -268,7 +267,7 @@ public class RTMSyncProvider extends SynchronizationProvider { } } - SyncData syncData = populateSyncData(remoteChanges); + SyncData syncData = populateSyncData(remoteChanges); try { synchronizeTasks(syncData); } finally { @@ -305,20 +304,20 @@ public class RTMSyncProvider extends SynchronizationProvider { /** * Populate SyncData data structure */ - private SyncData populateSyncData(ArrayList remoteTasks) { + private SyncData populateSyncData(ArrayList remoteTasks) { // fetch locally created tasks TodorooCursor localCreated = dataService.getLocallyCreated(PROPERTIES); // fetch locally updated tasks TodorooCursor localUpdated = dataService.getLocallyUpdated(PROPERTIES); - return new SyncData(remoteTasks, localCreated, localUpdated); + return new SyncData(remoteTasks, localCreated, localUpdated); } /** * Add the tasks read from RTM to the given list */ - private void addTasksToList(RtmTasks tasks, ArrayList list) { + private void addTasksToList(RtmTasks tasks, ArrayList list) { for (RtmTaskList taskList : tasks.getLists()) { for (RtmTaskSeries taskSeries : taskList.getSeries()) { RTMTaskContainer remoteTask = parseRemoteTask(taskSeries); @@ -408,13 +407,13 @@ public class RTMSyncProvider extends SynchronizationProvider { // tags HashSet localTags = new HashSet(); HashSet remoteTags = new HashSet(); - for(Metadata metadatum : local.metadata) - if(TagService.KEY.equals(metadatum.getValue(Metadata.KEY))) - localTags.add(metadatum.getValue(TagService.TAG)); - if(remote != null) { - for(Metadata metadatum : remote.metadata) - if(TagService.KEY.equals(metadatum.getValue(Metadata.KEY))) - remoteTags.add(metadatum.getValue(TagService.TAG)); + for(Metadata item : local.metadata) + if(TagService.KEY.equals(item.getValue(Metadata.KEY))) + localTags.add(item.getValue(TagService.TAG)); + if(remote != null && remote.metadata != null) { + for(Metadata item : remote.metadata) + if(TagService.KEY.equals(item.getValue(Metadata.KEY))) + remoteTags.add(item.getValue(TagService.TAG)); } if(!localTags.equals(remoteTags)) { String[] tags = localTags.toArray(new String[localTags.size()]); @@ -426,7 +425,7 @@ public class RTMSyncProvider extends SynchronizationProvider { if(shouldTransmit(local, Task.NOTES, remote)) { String[] titleAndText = MilkNote.fromNoteField(local.task.getValue(Task.NOTES)); List notes = null; - if(remote != null) + if(remote != null && remote.remote.getNotes() != null) notes = remote.remote.getNotes().getNotes(); if(notes != null && notes.size() > 0) { String remoteNoteId = notes.get(0).getId(); @@ -440,19 +439,12 @@ public class RTMSyncProvider extends SynchronizationProvider { } } - /** Create a task proxy for the given RtmTaskSeries */ + /** Create a task container for the given RtmTaskSeries */ private RTMTaskContainer parseRemoteTask(RtmTaskSeries rtmTaskSeries) { Task task = new Task(); RtmTask rtmTask = rtmTaskSeries.getTask(); ArrayList metadata = new ArrayList(); - Metadata rtmMetadata = new Metadata(); - metadata.add(rtmMetadata); - rtmMetadata.setValue(MilkTask.LIST_ID, Long.parseLong(rtmTaskSeries.getList().getId())); - rtmMetadata.setValue(MilkTask.TASK_SERIES_ID, Long.parseLong(rtmTaskSeries.getId())); - rtmMetadata.setValue(MilkTask.REPEATING, rtmTaskSeries.hasRecurrence() ? 1 : 0); - rtmMetadata.setValue(MilkTask.TASK_ID, Long.parseLong(rtmTask.getId())); - task.setValue(Task.TITLE, rtmTaskSeries.getName()); task.setValue(Task.CREATION_DATE, DateUtilities.dateToUnixtime(rtmTask.getAdded())); task.setValue(Task.COMPLETION_DATE, DateUtilities.dateToUnixtime(rtmTask.getCompleted())); @@ -466,20 +458,25 @@ public class RTMSyncProvider extends SynchronizationProvider { } task.setValue(Task.IMPORTANCE, rtmTask.getPriority().ordinal()); - for(String tag : rtmTaskSeries.getTags()) { - Metadata tagData = new Metadata(); - tagData.setValue(Metadata.KEY, TagService.KEY); - tagData.setValue(TagService.TAG, tag); - metadata.add(tagData); + if(rtmTaskSeries.getTags() != null) { + for(String tag : rtmTaskSeries.getTags()) { + Metadata tagData = new Metadata(); + tagData.setValue(Metadata.KEY, TagService.KEY); + tagData.setValue(TagService.TAG, tag); + metadata.add(tagData); + } } - boolean firstNote = true; - for(RtmTaskNote note : rtmTaskSeries.getNotes().getNotes()) { - if(firstNote) { - firstNote = false; - task.setValue(Task.NOTES, MilkNote.toNoteField(note)); - } else - metadata.add(MilkNote.create(note)); + task.setValue(Task.NOTES, ""); //$NON-NLS-1$ + if(rtmTaskSeries.getNotes() != null) { + boolean firstNote = true; + for(RtmTaskNote note : rtmTaskSeries.getNotes().getNotes()) { + if(firstNote) { + firstNote = false; + task.setValue(Task.NOTES, MilkNote.toNoteField(note)); + } else + metadata.add(MilkNote.create(note)); + } } RTMTaskContainer container = new RTMTaskContainer(task, metadata, rtmTaskSeries); diff --git a/astrid/plugin-src/com/todoroo/astrid/rmilk/sync/RTMTaskContainer.java b/astrid/plugin-src/com/todoroo/astrid/rmilk/sync/RTMTaskContainer.java index accbb5e90..857ca0039 100644 --- a/astrid/plugin-src/com/todoroo/astrid/rmilk/sync/RTMTaskContainer.java +++ b/astrid/plugin-src/com/todoroo/astrid/rmilk/sync/RTMTaskContainer.java @@ -1,6 +1,7 @@ package com.todoroo.astrid.rmilk.sync; import java.util.ArrayList; +import java.util.Iterator; import com.todoroo.astrid.api.TaskContainer; import com.todoroo.astrid.model.Metadata; @@ -16,31 +17,38 @@ import com.todoroo.astrid.rmilk.data.MilkTask; */ public class RTMTaskContainer extends TaskContainer { public long listId, taskSeriesId, taskId; - public RtmTaskSeries remote = null; + public boolean repeating; + public RtmTaskSeries remote; public RTMTaskContainer(Task task, ArrayList metadata, - long listId, long taskSeriesId, long taskId) { + long listId, long taskSeriesId, long taskId, boolean repeating, + RtmTaskSeries remote) { this.task = task; this.metadata = metadata; this.listId = listId; this.taskSeriesId = taskSeriesId; this.taskId = taskId; + this.repeating = repeating; + this.remote = remote; } public RTMTaskContainer(Task task, ArrayList metadata, RtmTaskSeries rtmTaskSeries) { this(task, metadata, Long.parseLong(rtmTaskSeries.getList().getId()), - Long.parseLong(rtmTaskSeries.getId()), Long.parseLong(rtmTaskSeries.getTask().getId())); - remote = rtmTaskSeries; + Long.parseLong(rtmTaskSeries.getId()), Long.parseLong(rtmTaskSeries.getTask().getId()), + rtmTaskSeries.hasRecurrence(), rtmTaskSeries); } public RTMTaskContainer(Task task, ArrayList metadata) { - this(task, metadata, 0, 0, 0); - for(Metadata metadatum : metadata) { - if(MilkTask.METADATA_KEY.equals(metadatum.getValue(Metadata.KEY))) { - listId = metadatum.getValue(MilkTask.LIST_ID); - taskSeriesId = metadatum.getValue(MilkTask.TASK_SERIES_ID); - taskId = metadatum.getValue(MilkTask.TASK_ID); + this(task, metadata, 0, 0, 0, false, null); + for(Iterator iterator = metadata.iterator(); iterator.hasNext(); ) { + Metadata item = iterator.next(); + if(MilkTask.METADATA_KEY.equals(item.getValue(Metadata.KEY))) { + listId = item.getValue(MilkTask.LIST_ID); + taskSeriesId = item.getValue(MilkTask.TASK_SERIES_ID); + taskId = item.getValue(MilkTask.TASK_ID); + repeating = item.getValue(MilkTask.REPEATING) == 1; + iterator.remove(); break; } } diff --git a/astrid/src/com/todoroo/astrid/activity/TaskListActivity.java b/astrid/src/com/todoroo/astrid/activity/TaskListActivity.java index 77d9e654f..e14bca8b8 100644 --- a/astrid/src/com/todoroo/astrid/activity/TaskListActivity.java +++ b/astrid/src/com/todoroo/astrid/activity/TaskListActivity.java @@ -127,6 +127,7 @@ public class TaskListActivity extends ListActivity implements OnScrollListener { protected TaskAdapter taskAdapter = null; protected DetailReceiver detailReceiver = new DetailReceiver(); + ImageButton quickAddButton; EditText quickAddBox; Filter filter; @@ -261,7 +262,7 @@ public class TaskListActivity extends ListActivity implements OnScrollListener { } }); - final ImageButton quickAddButton = ((ImageButton)findViewById(R.id.quickAddButton)); + quickAddButton = ((ImageButton)findViewById(R.id.quickAddButton)); quickAddBox.setOnKeyListener(new OnKeyListener() { @Override public boolean onKey(View v, int keyCode, KeyEvent event) { @@ -327,6 +328,7 @@ public class TaskListActivity extends ListActivity implements OnScrollListener { TextView quickAdd = (TextView)findViewById(R.id.quickAddText); quickAdd.setText(""); //$NON-NLS-1$ + quickAddButton.setVisibility(View.GONE); if(selectNewTask) { loadTaskListContent(true); diff --git a/astrid/src/com/todoroo/astrid/dao/TaskDao.java b/astrid/src/com/todoroo/astrid/dao/TaskDao.java index 342ab7612..64f59d969 100644 --- a/astrid/src/com/todoroo/astrid/dao/TaskDao.java +++ b/astrid/src/com/todoroo/astrid/dao/TaskDao.java @@ -146,7 +146,7 @@ public class TaskDao extends GenericDao { saveSuccessful = createNew(task); } else { ContentValues values = task.getSetValues(); - if(values.size() == 0) + if(values == null || values.size() == 0) return true; beforeSave(task, values, skipHooks); saveSuccessful = saveExisting(task); diff --git a/astrid/src/com/todoroo/astrid/widget/DeadlineTimePickerDialog.java b/astrid/src/com/todoroo/astrid/widget/DeadlineTimePickerDialog.java index 78b03b911..c29abe5e0 100644 --- a/astrid/src/com/todoroo/astrid/widget/DeadlineTimePickerDialog.java +++ b/astrid/src/com/todoroo/astrid/widget/DeadlineTimePickerDialog.java @@ -29,8 +29,8 @@ import android.view.LayoutInflater; import android.view.View; import android.widget.CheckBox; import android.widget.CompoundButton; -import android.widget.TimePicker; import android.widget.CompoundButton.OnCheckedChangeListener; +import android.widget.TimePicker; import android.widget.TimePicker.OnTimeChangedListener; import com.timsu.astrid.R; @@ -107,7 +107,6 @@ public class DeadlineTimePickerDialog extends AlertDialog implements OnClickList mDateFormat = DateFormat.getTimeFormat(context); mCalendar = Calendar.getInstance(); - updateTitle(mInitialHourOfDay, mInitialMinute); setButton(context.getText(android.R.string.ok), this); setButton2(context.getText(android.R.string.cancel), (OnClickListener) null); @@ -125,7 +124,7 @@ public class DeadlineTimePickerDialog extends AlertDialog implements OnClickList boolean isChecked) { mTimePicker.setEnabled(isChecked); if(isChecked) - updateTitle(mTimePicker.getCurrentHour(), mTimePicker.getCurrentMinute()); + updateTitle(); else setTitle(R.string.TEA_urgency_time_none); } @@ -137,6 +136,8 @@ public class DeadlineTimePickerDialog extends AlertDialog implements OnClickList mTimePicker.setCurrentMinute(mInitialMinute); mTimePicker.setIs24HourView(mIs24HourView); mTimePicker.setOnTimeChangedListener(this); + updateTitle(); + } public void onClick(DialogInterface dialog, int which) { @@ -150,7 +151,7 @@ public class DeadlineTimePickerDialog extends AlertDialog implements OnClickList } public void onTimeChanged(TimePicker view, int hourOfDay, int minute) { - updateTitle(hourOfDay, minute); + updateTitle(); } public void updateTime(int hourOfDay, int minutOfHour) { @@ -158,7 +159,9 @@ public class DeadlineTimePickerDialog extends AlertDialog implements OnClickList mTimePicker.setCurrentMinute(minutOfHour); } - private void updateTitle(int hour, int minute) { + private void updateTitle() { + int hour = mTimePicker.getCurrentHour(); + int minute = mTimePicker.getCurrentMinute(); mCalendar.set(Calendar.HOUR_OF_DAY, hour); mCalendar.set(Calendar.MINUTE, minute); setTitle(mDateFormat.format(mCalendar.getTime())); @@ -182,6 +185,6 @@ public class DeadlineTimePickerDialog extends AlertDialog implements OnClickList mTimePicker.setCurrentMinute(minute); mTimePicker.setIs24HourView(savedInstanceState.getBoolean(IS_24_HOUR)); mTimePicker.setOnTimeChangedListener(this); - updateTitle(hour, minute); + updateTitle(); } }