Updated synchronization algorithm to handle tasks created in Astrid & also RTM, added time remaining to the dashboard.

pull/14/head
Tim Su 17 years ago
parent 37340b6cb2
commit afaac7d9f9

@ -1,13 +1,13 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" <manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.timsu.astrid" package="com.timsu.astrid"
android:versionCode="59" android:versionCode="60"
android:versionName="2.0.2-beta"> android:versionName="2.0.0-rc1">
<meta-data android:name="com.a0soft.gphone.aTrackDog.webURL" <meta-data android:name="com.a0soft.gphone.aTrackDog.webURL"
android:value="http://www.weloveastrid.com" /> android:value="http://www.weloveastrid.com" />
<meta-data android:name="com.a0soft.gphone.aTrackDog.testVersion" <meta-data android:name="com.a0soft.gphone.aTrackDog.testVersion"
android:value="59" /> android:value="60" />
<uses-permission android:name="android.permission.VIBRATE"/> <uses-permission android:name="android.permission.VIBRATE"/>
<uses-permission android:name="android.permission.INTERNET"/> <uses-permission android:name="android.permission.INTERNET"/>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

@ -69,6 +69,15 @@
android:paddingRight="10dip" android:paddingRight="10dip"
android:singleLine="true"/> android:singleLine="true"/>
<TextView android:id="@+id/text_remainingTime"
android:gravity="top"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
style="@style/TextAppearance.TaskList_Detail"
android:textColor="@color/taskList_remainingTime"
android:paddingRight="10dip"
android:singleLine="true"/>
<TextView android:id="@+id/text_tags" <TextView android:id="@+id/text_tags"
android:gravity="top" android:gravity="top"
android:layout_height="wrap_content" android:layout_height="wrap_content"

@ -30,6 +30,7 @@
<color name="taskList_dueDateOverdue">#FFFB6666</color> <color name="taskList_dueDateOverdue">#FFFB6666</color>
<color name="taskList_dueDate">#ffF0E89E</color> <color name="taskList_dueDate">#ffF0E89E</color>
<color name="taskList_remainingTime">#ff88AAFF</color>
<color name="taskList_completedDate">#ff888888</color> <color name="taskList_completedDate">#ff888888</color>
<color name="taskList_tags">#ff888888</color> <color name="taskList_tags">#ff888888</color>

@ -84,6 +84,10 @@
<string name="tags_prefix">Tags: </string> <string name="tags_prefix">Tags: </string>
<string name="no_tags">No Tags</string> <string name="no_tags">No Tags</string>
<string name="taskList_remaining">Left</string>
<string name="taskList_overtime">Over Time</string>
<string name="taskList_spent">Spent</string>
<string name="taskList_menu_insert">Add</string> <string name="taskList_menu_insert">Add</string>
<string name="taskList_menu_tags">Tags</string> <string name="taskList_menu_tags">Tags</string>
<string name="taskList_menu_filters">Display</string> <string name="taskList_menu_filters">Display</string>
@ -227,6 +231,7 @@ Wish me luck!\n
<string name="sync_now">Synchronize Now!</string> <string name="sync_now">Synchronize Now!</string>
<string name="sync_forget">Clear Personal Data</string> <string name="sync_forget">Clear Personal Data</string>
<string name="sync_forget_confirm">Clear data for selected services?</string> <string name="sync_forget_confirm">Clear data for selected services?</string>
<string name="sync_no_synchronizers">No Synchronizers Enabled!</string>
<!-- Dialog Boxes --> <!-- Dialog Boxes -->
<skip /> <skip />

@ -9,7 +9,7 @@ import android.widget.Button;
import com.timsu.astrid.R; import com.timsu.astrid.R;
import com.timsu.astrid.sync.Synchronizer; import com.timsu.astrid.sync.Synchronizer;
import com.timsu.astrid.sync.Synchronizer.SynchronizerListener; import com.timsu.astrid.utilities.Constants;
import com.timsu.astrid.utilities.DialogUtilities; import com.timsu.astrid.utilities.DialogUtilities;
public class SyncPreferences extends PreferenceActivity { public class SyncPreferences extends PreferenceActivity {
@ -19,7 +19,6 @@ public class SyncPreferences extends PreferenceActivity {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.sync_preferences); addPreferencesFromResource(R.xml.sync_preferences);
syncFinished = true;
getListView().addFooterView(getLayoutInflater().inflate( getListView().addFooterView(getLayoutInflater().inflate(
R.layout.sync_footer, getListView(), false)); R.layout.sync_footer, getListView(), false));
@ -28,7 +27,7 @@ public class SyncPreferences extends PreferenceActivity {
syncButton.setOnClickListener(new View.OnClickListener() { syncButton.setOnClickListener(new View.OnClickListener() {
@Override @Override
public void onClick(View v) { public void onClick(View v) {
Preferences.setSyncLastSync(SyncPreferences.this, null); setResult(Constants.RESULT_SYNCHRONIZE);
finish(); finish();
} }
}); });

@ -59,7 +59,9 @@ import com.timsu.astrid.data.task.TaskController;
import com.timsu.astrid.data.task.TaskIdentifier; import com.timsu.astrid.data.task.TaskIdentifier;
import com.timsu.astrid.data.task.TaskModelForList; import com.timsu.astrid.data.task.TaskModelForList;
import com.timsu.astrid.sync.Synchronizer; import com.timsu.astrid.sync.Synchronizer;
import com.timsu.astrid.sync.Synchronizer.SynchronizerListener;
import com.timsu.astrid.utilities.Constants; import com.timsu.astrid.utilities.Constants;
import com.timsu.astrid.utilities.DialogUtilities;
import com.timsu.astrid.utilities.Preferences; import com.timsu.astrid.utilities.Preferences;
import com.timsu.astrid.utilities.StartupReceiver; import com.timsu.astrid.utilities.StartupReceiver;
@ -105,8 +107,6 @@ public class TaskList extends Activity {
public static final int FLING_VEL_THRESHOLD = 300; public static final int FLING_VEL_THRESHOLD = 300;
// UI components // UI components
private TaskController controller;
private TagController tagController = null;
private ListView listView; private ListView listView;
private Button addButton; private Button addButton;
private View layout; private View layout;
@ -127,6 +127,11 @@ public class TaskList extends Activity {
static boolean shouldCloseInstance = false; static boolean shouldCloseInstance = false;
// database controllers
private TaskController taskController;
private TagController tagController;
/* ====================================================================== /* ======================================================================
* ======================================================= initialization * ======================================================= initialization
* ====================================================================== */ * ====================================================================== */
@ -140,13 +145,15 @@ public class TaskList extends Activity {
StartupReceiver.onStartupApplication(this); StartupReceiver.onStartupApplication(this);
shouldCloseInstance = false; shouldCloseInstance = false;
controller = new TaskController(this); taskController = new TaskController(this);
controller.open(); taskController.open();
tagController = new TagController(this); tagController = new TagController(this);
tagController.open(); tagController.open();
Synchronizer.setTagController(tagController);
Synchronizer.setTaskController(taskController);
setupUIComponents(); setupUIComponents();
fillData();
// auto sync // auto sync
Integer autoSyncHours = Preferences.autoSyncFrequency(this); Integer autoSyncHours = Preferences.autoSyncFrequency(this);
@ -155,11 +162,10 @@ public class TaskList extends Activity {
if(lastSync == null || lastSync.getTime() + if(lastSync == null || lastSync.getTime() +
1000L*3600*autoSyncHours < System.currentTimeMillis()) { 1000L*3600*autoSyncHours < System.currentTimeMillis()) {
Synchronizer.synchronize(this, new SynchronizationListener() { Synchronizer.synchronize(this, true, null);
show dialog!
});
} }
} } else
fillData();
} }
public void setupUIComponents() { public void setupUIComponents() {
@ -345,15 +351,15 @@ public class TaskList extends Activity {
if(filterTag != null) { if(filterTag != null) {
List<TaskIdentifier> tasks = tagController.getTaggedTasks(this, List<TaskIdentifier> tasks = tagController.getTaggedTasks(this,
filterTag.getTagIdentifier()); filterTag.getTagIdentifier());
tasksCursor = controller.getTaskListCursorById(tasks); tasksCursor = taskController.getTaskListCursorById(tasks);
} else { } else {
if(filterShowDone) if(filterShowDone)
tasksCursor = controller.getAllTaskListCursor(); tasksCursor = taskController.getAllTaskListCursor();
else else
tasksCursor = controller.getActiveTaskListCursor(); tasksCursor = taskController.getActiveTaskListCursor();
} }
startManagingCursor(tasksCursor); startManagingCursor(tasksCursor);
taskArray = controller.createTaskListFromCursor(tasksCursor); taskArray = taskController.createTaskListFromCursor(tasksCursor);
// read tags and apply filters // read tags and apply filters
int hiddenTasks = 0; // # of tasks hidden int hiddenTasks = 0; // # of tasks hidden
@ -456,7 +462,7 @@ public class TaskList extends Activity {
@Override @Override
public TaskController getTaskController() { public TaskController getTaskController() {
return controller; return taskController;
} }
@Override @Override
@ -542,7 +548,16 @@ public class TaskList extends Activity {
protected void onActivityResult(int requestCode, int resultCode, Intent data) { protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data); super.onActivityResult(requestCode, resultCode, data);
if(requestCode == ACTIVITY_TAGS && resultCode == RESULT_CANCELED) if(resultCode == Constants.RESULT_SYNCHRONIZE) {
Synchronizer.synchronize(this, false, new SynchronizerListener() {
@Override
public void onSynchronizerFinished(int numServicesSynced) {
if(numServicesSynced == 0)
DialogUtilities.okDialog(TaskList.this, getResources().getString(
R.string.sync_no_synchronizers), null);
}
});
} else if(requestCode == ACTIVITY_TAGS && resultCode == RESULT_CANCELED)
filterTag = null; filterTag = null;
} }
@ -576,7 +591,7 @@ public class TaskList extends Activity {
new DialogInterface.OnClickListener() { new DialogInterface.OnClickListener() {
@Override @Override
public void onClick(DialogInterface dialog, int which) { public void onClick(DialogInterface dialog, int which) {
controller.deleteTask(taskId); taskController.deleteTask(taskId);
fillData(); fillData();
} }
}) })
@ -648,7 +663,7 @@ public class TaskList extends Activity {
else { else {
task.stopTimerAndUpdateElapsedTime(); task.stopTimerAndUpdateElapsedTime();
} }
controller.saveTask(task); taskController.saveTask(task);
fillData(); fillData();
return true; return true;
@ -697,7 +712,7 @@ public class TaskList extends Activity {
@Override @Override
protected void onDestroy() { protected void onDestroy() {
super.onDestroy(); super.onDestroy();
controller.close(); taskController.close();
if(tagController != null) if(tagController != null)
tagController.close(); tagController.close();
} }

@ -105,6 +105,7 @@ public class TaskListAdapter extends ArrayAdapter<TaskModelForList> {
// find UI components // find UI components
final TextView name = ((TextView)view.findViewById(R.id.text1)); final TextView name = ((TextView)view.findViewById(R.id.text1));
final TextView dueDateView = ((TextView)view.findViewById(R.id.text_dueDate)); final TextView dueDateView = ((TextView)view.findViewById(R.id.text_dueDate));
final TextView remainingTimeView = ((TextView)view.findViewById(R.id.text_remainingTime));
final TextView tagsView = ((TextView)view.findViewById(R.id.text_tags)); final TextView tagsView = ((TextView)view.findViewById(R.id.text_tags));
final CheckBox progress = ((CheckBox)view.findViewById(R.id.cb1)); final CheckBox progress = ((CheckBox)view.findViewById(R.id.cb1));
final ImageView timer = ((ImageView)view.findViewById(R.id.imageLeft)); final ImageView timer = ((ImageView)view.findViewById(R.id.imageLeft));
@ -114,6 +115,7 @@ public class TaskListAdapter extends ArrayAdapter<TaskModelForList> {
view.setTag(task); view.setTag(task);
progress.setTag(task); progress.setTag(task);
// name
String nameValue = task.getName(); String nameValue = task.getName();
if(task.getHiddenUntil() != null && task.getHiddenUntil().after(new Date())) if(task.getHiddenUntil() != null && task.getHiddenUntil().after(new Date()))
nameValue = "(" + r.getString(R.string.taskList_hiddenPrefix) + ") " + nameValue; nameValue = "(" + r.getString(R.string.taskList_hiddenPrefix) + ") " + nameValue;
@ -172,6 +174,40 @@ public class TaskListAdapter extends ArrayAdapter<TaskModelForList> {
dueDateView.setVisibility(View.GONE); dueDateView.setVisibility(View.GONE);
} }
// remaining time
if(task.getElapsedSeconds() > 0 || task.getEstimatedSeconds() > 0 ||
task.getTimerStart() != null) {
remainingTimeView.setVisibility(View.VISIBLE);
int elapsed = task.getElapsedSeconds();
if(task.getTimerStart() != null)
elapsed += ((System.currentTimeMillis() - task.getTimerStart().getTime())/1000);
String remainingString = "";
if(!task.isTaskCompleted() && task.getEstimatedSeconds() > 0) {
int remaining = task.getEstimatedSeconds() - elapsed;
remainingString = DateUtilities.getShortDurationString(r,
(int)Math.abs(remaining), 1) + " ";
if(remaining >= 0)
remainingString += r.getString(R.string.taskList_remaining);
else
remainingString += r.getString(R.string.taskList_overtime);
} else if(elapsed > 0) {
remainingString = DateUtilities.getShortDurationString(r,
(int)Math.abs(elapsed), 1) + " " +
r.getString(R.string.taskList_spent);
}
if(remainingString.length() == 0) {
remainingTimeView.setVisibility(View.GONE);
} else {
hasProperties = true;
remainingTimeView.setText(remainingString);
if(task.isTaskCompleted())
remainingTimeView.setTextColor(r.getColor(R.color.taskList_completedDate));
}
} else {
remainingTimeView.setVisibility(View.GONE);
}
// tags // tags
List<TagModelForView> tags = hooks.getTagsFor(task); List<TagModelForView> tags = hooks.getTagsFor(task);
StringBuilder tagString = new StringBuilder(); StringBuilder tagString = new StringBuilder();

@ -34,7 +34,6 @@ abstract public class AbstractController {
// special columns // special columns
public static final String KEY_ROWID = "_id"; public static final String KEY_ROWID = "_id";
// database and table names // database and table names
protected static final String TASK_TABLE_NAME = "tasks"; protected static final String TASK_TABLE_NAME = "tasks";
protected static final String TAG_TABLE_NAME = "tags"; protected static final String TAG_TABLE_NAME = "tags";
@ -42,6 +41,9 @@ abstract public class AbstractController {
protected static final String ALERT_TABLE_NAME = "alerts"; protected static final String ALERT_TABLE_NAME = "alerts";
protected static final String SYNC_TABLE_NAME = "sync"; protected static final String SYNC_TABLE_NAME = "sync";
abstract public void open();
abstract public void close();
// cursor iterator // cursor iterator
public static class CursorIterator<TYPE extends AbstractModel> implements Iterator<TYPE> { public static class CursorIterator<TYPE extends AbstractModel> implements Iterator<TYPE> {

@ -130,14 +130,14 @@ public class AlertController extends AbstractController {
* initialization call) * initialization call)
* @throws SQLException if the database could be neither opened or created * @throws SQLException if the database could be neither opened or created
*/ */
public AlertController open() throws SQLException { @Override
public void open() throws SQLException {
alertDatabase = new AlertDatabaseHelper(context, alertDatabase = new AlertDatabaseHelper(context,
ALERT_TABLE_NAME, ALERT_TABLE_NAME).getWritableDatabase(); ALERT_TABLE_NAME, ALERT_TABLE_NAME).getWritableDatabase();
return this;
} }
/** Closes database resource */ /** Closes database resource */
@Override
public void close() { public void close() {
alertDatabase.close(); alertDatabase.close();
} }

@ -119,15 +119,15 @@ public class SyncDataController extends AbstractController {
* initialization call) * initialization call)
* @throws SQLException if the database could be neither opened or created * @throws SQLException if the database could be neither opened or created
*/ */
public SyncDataController open() throws SQLException { @Override
public void open() throws SQLException {
SQLiteOpenHelper helper = new SyncMappingDatabaseHelper(context, SQLiteOpenHelper helper = new SyncMappingDatabaseHelper(context,
SYNC_TABLE_NAME, SYNC_TABLE_NAME); SYNC_TABLE_NAME, SYNC_TABLE_NAME);
syncDatabase = helper.getWritableDatabase(); syncDatabase = helper.getWritableDatabase();
return this;
} }
/** Closes database resource */ /** Closes database resource */
@Override
public void close() { public void close() {
syncDatabase.close(); syncDatabase.close();
} }

@ -209,15 +209,16 @@ public class TagController extends AbstractController {
* initialization call) * initialization call)
* @throws SQLException if the database could be neither opened or created * @throws SQLException if the database could be neither opened or created
*/ */
public TagController open() throws SQLException { @Override
public void open() throws SQLException {
tagToTaskMapDatabase = new TagToTaskMappingDatabaseHelper(context, tagToTaskMapDatabase = new TagToTaskMappingDatabaseHelper(context,
TAG_TASK_MAP_NAME, TAG_TASK_MAP_NAME).getWritableDatabase(); TAG_TASK_MAP_NAME, TAG_TASK_MAP_NAME).getWritableDatabase();
tagDatabase = new TagModelDatabaseHelper(context, tagDatabase = new TagModelDatabaseHelper(context,
TAG_TABLE_NAME, TAG_TABLE_NAME).getWritableDatabase(); TAG_TABLE_NAME, TAG_TABLE_NAME).getWritableDatabase();
return this;
} }
/** Closes database resource */ /** Closes database resource */
@Override
public void close() { public void close() {
tagDatabase.close(); tagDatabase.close();
tagToTaskMapDatabase.close(); tagToTaskMapDatabase.close();

@ -364,14 +364,15 @@ public class TaskController extends AbstractController {
* initialization call) * initialization call)
* @throws SQLException if the database could be neither opened or created * @throws SQLException if the database could be neither opened or created
*/ */
public TaskController open() throws SQLException { @Override
public void open() throws SQLException {
SQLiteOpenHelper databaseHelper = new TaskModelDatabaseHelper( SQLiteOpenHelper databaseHelper = new TaskModelDatabaseHelper(
context, TASK_TABLE_NAME, TASK_TABLE_NAME); context, TASK_TABLE_NAME, TASK_TABLE_NAME);
database = databaseHelper.getWritableDatabase(); database = databaseHelper.getWritableDatabase();
return this;
} }
/** Closes database resource */ /** Closes database resource */
@Override
public void close() { public void close() {
database.close(); database.close();
} }

@ -105,10 +105,10 @@ public abstract class SynchronizationService {
/** Create a task on the remote server /** Create a task on the remote server
* *
* @return listName list name to create it on. null -> inbox * @return primaryTag primary tag of this task. null if no tags exist.
* @return remote id * @return remote id
*/ */
String createTask(String listName) throws IOException; String createTask(String primaryTag) throws IOException;
/** Fetch remote task. Used to re-read merged tasks /** Fetch remote task. Used to re-read merged tasks
* *
@ -153,7 +153,7 @@ public abstract class SynchronizationService {
TagController tagController = Synchronizer.getTagController(activity); TagController tagController = Synchronizer.getTagController(activity);
AlertController alertController = Synchronizer.getAlertController(activity); AlertController alertController = Synchronizer.getAlertController(activity);
// get data out of the database // 1. get data out of the database
HashSet<SyncMapping> mappings = syncController.getSyncMapping(getId()); HashSet<SyncMapping> mappings = syncController.getSyncMapping(getId());
HashSet<TaskIdentifier> activeTasks = taskController. HashSet<TaskIdentifier> activeTasks = taskController.
getActiveTaskIdentifiers(); getActiveTaskIdentifiers();
@ -162,7 +162,7 @@ public abstract class SynchronizationService {
HashMap<TagIdentifier, TagModelForView> tags = HashMap<TagIdentifier, TagModelForView> tags =
tagController.getAllTagsAsMap(activity); tagController.getAllTagsAsMap(activity);
// build local maps / lists // 2. build helper data structures
HashMap<String, SyncMapping> remoteIdToSyncMapping = HashMap<String, SyncMapping> remoteIdToSyncMapping =
new HashMap<String, SyncMapping>(); new HashMap<String, SyncMapping>();
HashMap<TaskIdentifier, SyncMapping> localIdToSyncMapping = HashMap<TaskIdentifier, SyncMapping> localIdToSyncMapping =
@ -177,34 +177,47 @@ public abstract class SynchronizationService {
mappedTasks.add(mapping.getTask()); mappedTasks.add(mapping.getTask());
} }
// build remote map // 3. build map of remote tasks
HashMap<TaskIdentifier, TaskProxy> remoteChangeMap = HashMap<TaskIdentifier, TaskProxy> remoteChangeMap =
new HashMap<TaskIdentifier, TaskProxy>(); new HashMap<TaskIdentifier, TaskProxy>();
HashMap<String, TaskProxy> newRemoteTasks = new HashMap<String, TaskProxy>();
for(TaskProxy remoteTask : remoteTasks) { for(TaskProxy remoteTask : remoteTasks) {
if(remoteIdToSyncMapping.containsKey(remoteTask.getRemoteId())) { if(remoteIdToSyncMapping.containsKey(remoteTask.getRemoteId())) {
SyncMapping mapping = remoteIdToSyncMapping.get(remoteTask.getRemoteId()); SyncMapping mapping = remoteIdToSyncMapping.get(remoteTask.getRemoteId());
remoteChangeMap.put(mapping.getTask(), remoteTask); remoteChangeMap.put(mapping.getTask(), remoteTask);
} else if(remoteTask.name != null){
newRemoteTasks.put(remoteTask.name, remoteTask);
} }
} }
// grab tasks without a sync mapping and create them remotely // 4. CREATE: grab tasks without a sync mapping and create them remotely
log.append(">> on remote server:\n"); log.append(">> on remote server:\n");
syncHandler.post(new Runnable() { syncHandler.post(new ProgressLabelUpdater("Sending locally created tasks"));
@Override
public void run() {
progressDialog.setMessage("Sending locally created tasks");
progressDialog.setProgress(0);
}
});
HashSet<TaskIdentifier> newlyCreatedTasks = new HashSet<TaskIdentifier>(activeTasks); HashSet<TaskIdentifier> newlyCreatedTasks = new HashSet<TaskIdentifier>(activeTasks);
newlyCreatedTasks.removeAll(mappedTasks); newlyCreatedTasks.removeAll(mappedTasks);
for(TaskIdentifier taskId : newlyCreatedTasks) { for(TaskIdentifier taskId : newlyCreatedTasks) {
TaskModelForSync task = taskController.fetchTaskForSync(taskId);
/* If there exists an incoming remote task with the same name and
* no mapping, we don't want to create this on the remote server.
* Instead, we create a mapping and do an update. */
if(newRemoteTasks.containsKey(task.getName())) {
TaskProxy remoteTask = newRemoteTasks.get(task.getName());
SyncMapping mapping = new SyncMapping(taskId, getId(),
remoteTask.getRemoteId());
syncController.saveSyncMapping(mapping);
localChanges.add(mapping);
remoteChangeMap.put(taskId, remoteTask);
localIdToSyncMapping.put(taskId, mapping);
continue;
}
// grab the primary tag for this task
LinkedList<TagIdentifier> taskTags = LinkedList<TagIdentifier> taskTags =
tagController.getTaskTags(activity, taskId); tagController.getTaskTags(activity, taskId);
String listName = null; String listName = null;
if(taskTags.size() > 0) { if(taskTags.size() > 0) {
listName = tags.get(taskTags.get(0)).getName(); listName = tags.get(taskTags.get(0)).getName();
// strip the underline
if(listName.startsWith(TagModelForView.HIDDEN_FROM_MAIN_LIST_PREFIX)) if(listName.startsWith(TagModelForView.HIDDEN_FROM_MAIN_LIST_PREFIX))
listName = listName.substring(1); listName = listName.substring(1);
} }
@ -212,27 +225,19 @@ public abstract class SynchronizationService {
SyncMapping mapping = new SyncMapping(taskId, getId(), remoteId); SyncMapping mapping = new SyncMapping(taskId, getId(), remoteId);
syncController.saveSyncMapping(mapping); syncController.saveSyncMapping(mapping);
TaskModelForSync task = taskController.fetchTaskForSync(
mapping.getTask());
TaskProxy localTask = new TaskProxy(getId(), remoteId, false); TaskProxy localTask = new TaskProxy(getId(), remoteId, false);
localTask.readFromTaskModel(task); localTask.readFromTaskModel(task);
helper.pushTask(localTask, mapping); helper.pushTask(localTask, mapping);
// update stats // update stats
log.append("add " + task.getName() + "\n"); log.append("added " + task.getName() + "\n");
stats.remoteCreatedTasks++; stats.remoteCreatedTasks++;
syncHandler.post(new ProgressUpdater(stats.remoteCreatedTasks, syncHandler.post(new ProgressUpdater(stats.remoteCreatedTasks,
newlyCreatedTasks.size())); newlyCreatedTasks.size()));
} }
// find deleted tasks and remove them from the list // 5. DELETE: find deleted tasks and remove them from the list
syncHandler.post(new Runnable() { syncHandler.post(new ProgressLabelUpdater("Sending locally deleted tasks"));
@Override
public void run() {
progressDialog.setMessage("Sending locally deleted tasks");
progressDialog.setProgress(0);
}
});
HashSet<TaskIdentifier> deletedTasks = new HashSet<TaskIdentifier>( HashSet<TaskIdentifier> deletedTasks = new HashSet<TaskIdentifier>(
mappedTasks); mappedTasks);
deletedTasks.removeAll(allTasks); deletedTasks.removeAll(allTasks);
@ -247,20 +252,14 @@ public abstract class SynchronizationService {
remoteChangeMap.remove(taskId); remoteChangeMap.remove(taskId);
// update stats // update stats
log.append("del #" + taskId.getId() + "\n"); log.append("deleted id #" + taskId.getId() + "\n");
stats.remoteDeletedTasks++; stats.remoteDeletedTasks++;
syncHandler.post(new ProgressUpdater(stats.remoteDeletedTasks, syncHandler.post(new ProgressUpdater(stats.remoteDeletedTasks,
deletedTasks.size())); deletedTasks.size()));
} }
// for each updated local task // 6. UPDATE: for each updated local task
syncHandler.post(new Runnable() { syncHandler.post(new ProgressLabelUpdater("Sending locally edited tasks"));
@Override
public void run() {
progressDialog.setMessage("Sending locally edited tasks");
progressDialog.setProgress(0);
}
});
for(SyncMapping mapping : localChanges) { for(SyncMapping mapping : localChanges) {
TaskProxy localTask = new TaskProxy(getId(), mapping.getRemoteId(), TaskProxy localTask = new TaskProxy(getId(), mapping.getRemoteId(),
false); false);
@ -274,9 +273,9 @@ public abstract class SynchronizationService {
remoteConflict = remoteChangeMap.get(mapping.getTask()); remoteConflict = remoteChangeMap.get(mapping.getTask());
localTask.mergeWithOther(remoteConflict); localTask.mergeWithOther(remoteConflict);
stats.mergedTasks++; stats.mergedTasks++;
log.append("mrg " + task.getName() + "\n"); log.append("merged " + task.getName() + "\n");
} else { } else {
log.append("upd " + task.getName() + "\n"); log.append("updated " + task.getName() + "\n");
} }
try { try {
@ -297,15 +296,9 @@ public abstract class SynchronizationService {
localChanges.size())); localChanges.size()));
} }
// load remote information // 7. REMOTE SYNC load remote information
log.append(">> on astrid:\n"); log.append(">> on astrid:\n");
syncHandler.post(new Runnable() { syncHandler.post(new ProgressLabelUpdater("Updating local tasks"));
@Override
public void run() {
progressDialog.setMessage("Updating local tasks");
progressDialog.setProgress(0);
}
});
for(TaskProxy remoteTask : remoteTasks) { for(TaskProxy remoteTask : remoteTasks) {
SyncMapping mapping = null; SyncMapping mapping = null;
TaskModelForSync task = null; TaskModelForSync task = null;
@ -321,22 +314,22 @@ public abstract class SynchronizationService {
if(task == null) { if(task == null) {
task = new TaskModelForSync(); task = new TaskModelForSync();
setupTaskDefaults(activity, task); setupTaskDefaults(activity, task);
log.append("add " + remoteTask.name + "\n"); log.append("added " + remoteTask.name + "\n");
} else { } else {
mapping = localIdToSyncMapping.get(task.getTaskIdentifier()); mapping = localIdToSyncMapping.get(task.getTaskIdentifier());
log.append("mov " + remoteTask.name + "\n"); log.append("merged " + remoteTask.name + "\n");
} }
} else { } else {
mapping = remoteIdToSyncMapping.get(remoteTask.getRemoteId()); mapping = remoteIdToSyncMapping.get(remoteTask.getRemoteId());
if(remoteTask.isDeleted()) { if(remoteTask.isDeleted()) {
taskController.deleteTask(mapping.getTask()); taskController.deleteTask(mapping.getTask());
syncController.deleteSyncMapping(mapping); syncController.deleteSyncMapping(mapping);
log.append("del " + remoteTask.name + "\n"); log.append("deleted " + remoteTask.name + "\n");
stats.localDeletedTasks++; stats.localDeletedTasks++;
continue; continue;
} }
log.append("upd " + remoteTask.name + "\n"); log.append("updated " + remoteTask.name + "\n");
task = taskController.fetchTaskForSync( task = taskController.fetchTaskForSync(
mapping.getTask()); mapping.getTask());
} }
@ -424,7 +417,8 @@ public abstract class SynchronizationService {
if(localCreatedTasks + localUpdatedTasks + localDeletedTasks + if(localCreatedTasks + localUpdatedTasks + localDeletedTasks +
mergedTasks + remoteCreatedTasks + remoteDeletedTasks + mergedTasks + remoteCreatedTasks + remoteDeletedTasks +
remoteUpdatedTasks == 0) { remoteUpdatedTasks == 0) {
DialogUtilities.okDialog(activity, "Sync: Up to date!", finishListener); if(!Synchronizer.isAutoSync())
DialogUtilities.okDialog(activity, "Sync: Up to date!", finishListener);
return; return;
} }
@ -467,4 +461,15 @@ public abstract class SynchronizationService {
progressDialog.setProgress(100*step/outOf); progressDialog.setProgress(100*step/outOf);
} }
} }
protected class ProgressLabelUpdater implements Runnable {
String label;
public ProgressLabelUpdater(String label) {
this.label = label;
}
public void run() {
progressDialog.setMessage(label);
progressDialog.setProgress(0);
}
}
} }

@ -1,10 +1,13 @@
package com.timsu.astrid.sync; package com.timsu.astrid.sync;
import java.lang.reflect.InvocationTargetException;
import java.util.Date; import java.util.Date;
import android.app.Activity; import android.app.Activity;
import android.content.Context; import android.content.Context;
import android.util.Log;
import com.timsu.astrid.data.AbstractController;
import com.timsu.astrid.data.alerts.AlertController; import com.timsu.astrid.data.alerts.AlertController;
import com.timsu.astrid.data.sync.SyncDataController; import com.timsu.astrid.data.sync.SyncDataController;
import com.timsu.astrid.data.tag.TagController; import com.timsu.astrid.data.tag.TagController;
@ -22,9 +25,11 @@ public class Synchronizer {
} }
/** Synchronize all activated sync services */ /** Synchronize all activated sync services */
public static void synchronize(Activity activity, SynchronizerListener listener) { public static void synchronize(Activity activity, boolean isAutoSync,
SynchronizerListener listener) {
currentStep = ServiceWrapper._FIRST_SERVICE.ordinal(); currentStep = ServiceWrapper._FIRST_SERVICE.ordinal();
servicesSynced = 0; servicesSynced = 0;
autoSync = isAutoSync;
callback = listener; callback = listener;
continueSynchronization(activity); continueSynchronization(activity);
} }
@ -88,6 +93,9 @@ public class Synchronizer {
/** On finished callback */ /** On finished callback */
private static SynchronizerListener callback; private static SynchronizerListener callback;
/** If this synchronization was automatically initiated */
private static boolean autoSync;
/** Called to do the next step of synchronization. Run me on the UI thread! */ /** Called to do the next step of synchronization. Run me on the UI thread! */
static void continueSynchronization(Activity activity) { static void continueSynchronization(Activity activity) {
@ -119,65 +127,95 @@ public class Synchronizer {
callback.onSynchronizerFinished(servicesSynced); callback.onSynchronizerFinished(servicesSynced);
} }
// --- package helpers /** Was this sync automatically initiated? */
static boolean isAutoSync() {
return autoSync;
}
static SyncDataController getSyncController(Activity activity) { // --- controller stuff
if(syncController == null) {
syncController = new SyncDataController(activity); private static class ControllerWrapper<TYPE extends AbstractController> {
syncController.open(); TYPE controller;
Class<TYPE> typeClass;
boolean override;
public ControllerWrapper(Class<TYPE> cls) {
override = false;
controller = null;
typeClass = cls;
}
public TYPE get(Activity activity) {
if(controller == null) {
try {
controller = typeClass.getConstructors()[0].newInstance(
activity);
} catch (IllegalArgumentException e) {
Log.e(getClass().getSimpleName(), e.toString());
} catch (SecurityException e) {
Log.e(getClass().getSimpleName(), e.toString());
} catch (InstantiationException e) {
Log.e(getClass().getSimpleName(), e.toString());
} catch (IllegalAccessException e) {
Log.e(getClass().getSimpleName(), e.toString());
} catch (InvocationTargetException e) {
Log.e(getClass().getSimpleName(), e.toString());
}
controller.open();
}
return controller;
}
public void set(TYPE newController) {
override = newController != null;
controller = newController;
}
public void close() {
if(controller != null && !override) {
controller.close();
controller = null;
}
} }
return syncController; }
private static ControllerWrapper<SyncDataController> syncController =
new ControllerWrapper<SyncDataController>(SyncDataController.class);
private static ControllerWrapper<TaskController> taskController =
new ControllerWrapper<TaskController>(TaskController.class);
private static ControllerWrapper<TagController> tagController =
new ControllerWrapper<TagController>(TagController.class);
private static ControllerWrapper<AlertController> alertController =
new ControllerWrapper<AlertController>(AlertController.class);
static SyncDataController getSyncController(Activity activity) {
return syncController.get(activity);
} }
static TaskController getTaskController(Activity activity) { static TaskController getTaskController(Activity activity) {
if(taskController == null) { return taskController.get(activity);
taskController = new TaskController(activity);
taskController.open();
}
return taskController;
} }
static TagController getTagController(Activity activity) { static TagController getTagController(Activity activity) {
if(tagController == null) { return tagController.get(activity);
tagController = new TagController(activity);
tagController.open();
}
return tagController;
} }
static AlertController getAlertController(Activity activity) { static AlertController getAlertController(Activity activity) {
if(alertController == null) { return alertController.get(activity);
alertController = new AlertController(activity);
alertController.open();
}
return alertController;
} }
// --- controller stuff public static void setTaskController(TaskController taskController) {
private static SyncDataController syncController = null; Synchronizer.taskController.set(taskController);
private static TaskController taskController = null; }
private static TagController tagController = null;
private static AlertController alertController = null;
private static void closeControllers() {
if(syncController != null) {
syncController.close();
syncController = null;
}
if(taskController != null) {
taskController.close();
taskController = null;
}
if(tagController != null) { public static void setTagController(TagController tagController) {
tagController.close(); Synchronizer.tagController.set(tagController);
tagController = null; }
}
if(alertController != null) { private static void closeControllers() {
alertController.close(); syncController.close();
alertController = null; taskController.close();
} tagController.close();
alertController.close();
} }
} }

@ -14,6 +14,6 @@ public class Constants {
/** Return to the task list view */ /** Return to the task list view */
public static final int RESULT_GO_HOME = Activity.RESULT_FIRST_USER; public static final int RESULT_GO_HOME = Activity.RESULT_FIRST_USER;
/** Callback from synchronization */ /** Callback to force synchronization */
public static final int RESULT_SYNCHRONIZE = Activity.RESULT_FIRST_USER + 1; public static final int RESULT_SYNCHRONIZE = Activity.RESULT_FIRST_USER + 1;
} }

Loading…
Cancel
Save