Refactored Task.REMOTE_ID to be null when unset rather than 0. Also catch SQLiteConsistency exceptions and recover when inserting a task that already has that remote id.

pull/14/head
Sam Bosley 13 years ago
parent 339b3f8062
commit 46f2ee2c2e

@ -10,6 +10,7 @@ import java.util.ArrayList;
import android.content.ContentValues; import android.content.ContentValues;
import android.content.Context; import android.content.Context;
import android.database.Cursor; import android.database.Cursor;
import android.database.sqlite.SQLiteConstraintException;
import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteDatabase.CursorFactory; import android.database.sqlite.SQLiteDatabase.CursorFactory;
import android.database.sqlite.SQLiteOpenHelper; import android.database.sqlite.SQLiteOpenHelper;
@ -230,7 +231,15 @@ abstract public class AbstractDatabase {
* @see android.database.sqlite.SQLiteDatabase#insert(String table, String nullColumnHack, ContentValues values) * @see android.database.sqlite.SQLiteDatabase#insert(String table, String nullColumnHack, ContentValues values)
*/ */
public synchronized long insert(String table, String nullColumnHack, ContentValues values) { public synchronized long insert(String table, String nullColumnHack, ContentValues values) {
long result = getDatabase().insert(table, nullColumnHack, values); long result = -1;
try {
getDatabase().insertOrThrow(table, nullColumnHack, values);
} catch (SQLiteConstraintException e) { // Throw these exceptions
throw e;
} catch (Exception e) { // Suppress others
Log.e("SQLiteDatabase", "Error inserting " + values, e);
result = -1;
}
onDatabaseUpdated(); onDatabaseUpdated();
return result; return result;
} }

@ -96,7 +96,10 @@ public class TodorooCursor<TYPE extends AbstractModel> extends CursorWrapper {
} }
public Object visitLong(Property<Long> property, TodorooCursor<?> cursor) { public Object visitLong(Property<Long> property, TodorooCursor<?> cursor) {
return cursor.getLong(cursor.getColumnIndexFromCache(property.name)); int column = columnIndex(property, cursor);
if(cursor.isNull(column))
return null;
return cursor.getLong(column);
} }
public Object visitString(Property<String> property, public Object visitString(Property<String> property,
@ -104,6 +107,9 @@ public class TodorooCursor<TYPE extends AbstractModel> extends CursorWrapper {
return cursor.getString(cursor.getColumnIndexFromCache(property.name)); return cursor.getString(cursor.getColumnIndexFromCache(property.name));
} }
private int columnIndex(Property<Long> property, TodorooCursor<?> cursor) {
return cursor.getColumnIndexFromCache(property.name);
}
} }
} }

@ -252,7 +252,7 @@ public final class Task extends RemoteModel {
defaultValues.put(DETAILS_DATE.name, 0); defaultValues.put(DETAILS_DATE.name, 0);
defaultValues.put(LAST_SYNC.name, 0); defaultValues.put(LAST_SYNC.name, 0);
defaultValues.put(REMOTE_ID.name, 0); defaultValues.putNull(REMOTE_ID.name);
defaultValues.put(USER_ID.name, 0); defaultValues.put(USER_ID.name, 0);
defaultValues.put(CREATOR_ID.name, 0); defaultValues.put(CREATOR_ID.name, 0);
defaultValues.put(USER.name, "{}"); defaultValues.put(USER.name, "{}");

@ -3,7 +3,7 @@
<classpathentry excluding="javax/xml/validation/|javax/xml/transform/dom/|javax/xml/parsers/|org/jaxp/transform/dom/|org/jaxp/transform/sax/|org/jaxp/transform/stax/|org/jaxp/transform/stream/|org/jaxp/stream/events/|org/jaxp/stream/util/|org/jaxp/parsers/|org/jaxp/stream/|org/jaxp/validation/" kind="src" path="src"/> <classpathentry excluding="javax/xml/validation/|javax/xml/transform/dom/|javax/xml/parsers/|org/jaxp/transform/dom/|org/jaxp/transform/sax/|org/jaxp/transform/stax/|org/jaxp/transform/stream/|org/jaxp/stream/events/|org/jaxp/stream/util/|org/jaxp/parsers/|org/jaxp/stream/|org/jaxp/validation/" kind="src" path="src"/>
<classpathentry kind="src" path="src-legacy"/> <classpathentry kind="src" path="src-legacy"/>
<classpathentry kind="src" path="common-src"/> <classpathentry kind="src" path="common-src"/>
<classpathentry excluding="com/todoroo/astrid/rmilk/EditOperationExposer.java|com/todoroo/astrid/rmilk/MilkEditActivity.java|com/todoroo/astrid/actfm/TaskFields.java|com/todoroo/astrid/actfm/ShowProjectExposer.java|com/todoroo/astrid/actfm/ProjectDetailExposer.java|com/todoroo/astrid/actfm/ProjectListActivity.java" kind="src" path="plugin-src"/> <classpathentry excluding="com/todoroo/astrid/rmilk/EditOperationExposer.java|com/todoroo/astrid/rmilk/MilkEditActivity.java|com/todoroo/astrid/actfm/TaskFields.java|com/todoroo/astrid/actfm/ShowProjectExposer.java|com/todoroo/astrid/actfm/ProjectDetailExposer.java|com/todoroo/astrid/actfm/ProjectListActivity.java|com/todoroo/astrid/actfm/sync/ActFmSyncProvider.java" kind="src" path="plugin-src"/>
<classpathentry kind="src" path="gen"/> <classpathentry kind="src" path="gen"/>
<classpathentry kind="src" path="rmilk-src"/> <classpathentry kind="src" path="rmilk-src"/>
<classpathentry exported="true" kind="con" path="com.android.ide.eclipse.adt.LIBRARIES"/> <classpathentry exported="true" kind="con" path="com.android.ide.eclipse.adt.LIBRARIES"/>

@ -33,4 +33,4 @@ import android.util.Log;
} }
} }
} }
} }

@ -106,4 +106,4 @@ public final class ReflectionUtils
throw new RuntimeException(e); throw new RuntimeException(e);
} }
} }
} }

@ -40,4 +40,4 @@ public class Api4GestureDetector implements OnGesturePerformedListener {
} }
} }
} }
} }

@ -138,4 +138,4 @@ public class DateControlSet implements OnTimeSetListener,
}); });
} }
} }
} }

@ -66,4 +66,4 @@ public class DateWithNullControlSet extends DateControlSet {
super.setDate(newDate); super.setDate(newDate);
} }
} }

@ -132,7 +132,7 @@ public class C2DMReceiver extends BroadcastReceiver {
try { try {
TagData tagData = new TagData(); TagData tagData = new TagData();
if(cursor.getCount() == 0) { if(cursor.getCount() == 0) {
tagData.setValue(Task.REMOTE_ID, Long.parseLong(intent.getStringExtra("tag_id"))); tagData.setValue(TagData.REMOTE_ID, Long.parseLong(intent.getStringExtra("tag_id")));
Flags.set(Flags.ACTFM_SUPPRESS_SYNC); Flags.set(Flags.ACTFM_SUPPRESS_SYNC);
tagDataService.save(tagData); tagDataService.save(tagData);
} else { } else {

@ -191,4 +191,4 @@ public class ActFmGoogleAuthActivity extends ListActivity {
} }
} }
} }

@ -76,4 +76,4 @@ public class ActFmPreferences extends SyncProviderPreferences {
} }
} }
} }

@ -695,9 +695,12 @@ public class EditPeopleControlSet extends PopupControlSet {
@SuppressWarnings("nls") @SuppressWarnings("nls")
protected Object[] buildSharingArgs(JSONArray emails) throws JSONException { protected Object[] buildSharingArgs(JSONArray emails) throws JSONException {
ArrayList<Object> values = new ArrayList<Object>(); ArrayList<Object> values = new ArrayList<Object>();
long currentTaskID = task.getValue(Task.REMOTE_ID);
values.add("id"); if(task.containsNonNullValue(Task.REMOTE_ID)) {
values.add(currentTaskID); long currentTaskID = task.getValue(Task.REMOTE_ID);
values.add("id");
values.add(currentTaskID);
}
if(emails != null) { if(emails != null) {
for(int i = 0; i < emails.length(); i++) { for(int i = 0; i < emails.length(); i++) {

@ -126,4 +126,4 @@ public class OAuthLoginActivity extends FragmentActivity {
webView.loadUrl(urlParam); webView.loadUrl(urlParam);
} }
} }

@ -41,7 +41,6 @@ import com.todoroo.astrid.activity.TaskListActivity;
import com.todoroo.astrid.adapter.UpdateAdapter; import com.todoroo.astrid.adapter.UpdateAdapter;
import com.todoroo.astrid.dao.UpdateDao; import com.todoroo.astrid.dao.UpdateDao;
import com.todoroo.astrid.data.TagData; import com.todoroo.astrid.data.TagData;
import com.todoroo.astrid.data.Task;
import com.todoroo.astrid.data.Update; import com.todoroo.astrid.data.Update;
import com.todoroo.astrid.helper.ImageDiskCache; import com.todoroo.astrid.helper.ImageDiskCache;
import com.todoroo.astrid.helper.ProgressBarSyncResultCallback; import com.todoroo.astrid.helper.ProgressBarSyncResultCallback;
@ -205,7 +204,7 @@ public class TagUpdatesFragment extends ListFragment {
} }
public void setLastViewed() { public void setLastViewed() {
if(tagData != null && tagData.getValue(Task.REMOTE_ID) > 0) { if(tagData != null && tagData.getValue(TagData.REMOTE_ID) > 0) {
Preferences.setLong(UPDATES_LAST_VIEWED + tagData.getValue(TagData.REMOTE_ID), DateUtilities.now()); Preferences.setLong(UPDATES_LAST_VIEWED + tagData.getValue(TagData.REMOTE_ID), DateUtilities.now());
Activity activity = getActivity(); Activity activity = getActivity();
if (activity instanceof TaskListActivity) if (activity instanceof TaskListActivity)

@ -64,7 +64,7 @@ public final class ActFmDataService {
*/ */
public void clearMetadata() { public void clearMetadata() {
ContentValues values = new ContentValues(); ContentValues values = new ContentValues();
values.put(Task.REMOTE_ID.name, 0); values.putNull(Task.REMOTE_ID.name);
taskDao.updateMultiple(values, Criterion.all); taskDao.updateMultiple(values, Criterion.all);
} }
@ -75,7 +75,7 @@ public final class ActFmDataService {
*/ */
public TodorooCursor<Task> getLocallyCreated(Property<?>[] properties) { public TodorooCursor<Task> getLocallyCreated(Property<?>[] properties) {
return taskDao.query(Query.select(properties).where(Criterion.and(TaskCriteria.isActive(), return taskDao.query(Query.select(properties).where(Criterion.and(TaskCriteria.isActive(),
Task.REMOTE_ID.eq(0)))); Task.REMOTE_ID.isNull())));
} }
/** /**
@ -89,7 +89,7 @@ public final class ActFmDataService {
return taskDao.query(Query.select(properties).where(Criterion.none)); return taskDao.query(Query.select(properties).where(Criterion.none));
return return
taskDao.query(Query.select(properties). taskDao.query(Query.select(properties).
where(Criterion.and(Task.REMOTE_ID.gt(0), where(Criterion.and(Task.REMOTE_ID.isNotNull(),
Task.MODIFICATION_DATE.gt(lastSyncDate), Task.MODIFICATION_DATE.gt(lastSyncDate),
Task.MODIFICATION_DATE.gt(Task.LAST_SYNC))).groupBy(Task.ID)); Task.MODIFICATION_DATE.gt(Task.LAST_SYNC))).groupBy(Task.ID));
} }

@ -123,4 +123,4 @@ public class ActFmPreferenceService extends SyncProviderUtilities {
return Preferences.getStringValue(PREF_NAME); return Preferences.getStringValue(PREF_NAME);
} }
} }

@ -1,363 +0,0 @@
/**
* See the file "LICENSE" for the full license governing this code.
*/
package com.todoroo.astrid.actfm.sync;
import java.io.IOException;
import java.util.ArrayList;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import android.app.Activity;
import android.app.Notification;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.text.TextUtils;
import com.timsu.astrid.C2DMReceiver;
import com.timsu.astrid.R;
import com.todoroo.andlib.data.TodorooCursor;
import com.todoroo.andlib.service.Autowired;
import com.todoroo.andlib.service.ContextManager;
import com.todoroo.andlib.sql.Criterion;
import com.todoroo.andlib.sql.Query;
import com.todoroo.andlib.utility.DateUtilities;
import com.todoroo.andlib.utility.Preferences;
import com.todoroo.astrid.actfm.ActFmBackgroundService;
import com.todoroo.astrid.actfm.ActFmLoginActivity;
import com.todoroo.astrid.actfm.ActFmPreferences;
import com.todoroo.astrid.actfm.sync.ActFmSyncService.JsonHelper;
import com.todoroo.astrid.api.AstridApiConstants;
import com.todoroo.astrid.core.PluginServices;
import com.todoroo.astrid.dao.TagDataDao;
import com.todoroo.astrid.dao.TaskDao;
import com.todoroo.astrid.data.Metadata;
import com.todoroo.astrid.data.TagData;
import com.todoroo.astrid.data.Task;
import com.todoroo.astrid.notes.NoteMetadata;
import com.todoroo.astrid.service.AstridDependencyInjector;
import com.todoroo.astrid.service.StatisticsConstants;
import com.todoroo.astrid.service.StatisticsService;
import com.todoroo.astrid.sync.SyncProvider;
import com.todoroo.astrid.sync.SyncProviderUtilities;
import com.todoroo.astrid.utility.Constants;
@SuppressWarnings("nls")
public class ActFmSyncProvider extends SyncProvider<ActFmTaskContainer> {
private ActFmInvoker invoker = null;
@Autowired ActFmDataService actFmDataService;
@Autowired ActFmSyncService actFmSyncService;
@Autowired ActFmPreferenceService actFmPreferenceService;
@Autowired TagDataDao tagDataDao;
static {
AstridDependencyInjector.initialize();
}
// ----------------------------------------------------------------------
// ------------------------------------------------------ utility methods
// ----------------------------------------------------------------------
@Override
protected SyncProviderUtilities getUtilities() {
return actFmPreferenceService;
}
/**
* Sign out of service, deleting all synchronization metadata
*/
public void signOut() {
actFmPreferenceService.setToken(null);
actFmPreferenceService.clearLastSyncDate();
C2DMReceiver.unregister();
}
// ----------------------------------------------------------------------
// ------------------------------------------------------ initiating sync
// ----------------------------------------------------------------------
/**
* initiate sync in background
*/
@Override
protected void initiateBackground() {
try {
C2DMReceiver.register();
String authToken = actFmPreferenceService.getToken();
invoker = new ActFmInvoker(authToken);
// check if we have a token & it works
if(authToken != null) {
performSync();
}
} catch (IllegalStateException e) {
// occurs when application was closed
} catch (Exception e) {
handleException("actfm-authenticate", e, false);
} finally {
actFmPreferenceService.stopOngoing();
}
}
/**
* If user isn't already signed in, show sign in dialog. Else perform sync.
*/
@Override
protected void initiateManual(Activity activity) {
String authToken = actFmPreferenceService.getToken();
actFmPreferenceService.stopOngoing();
// check if we have a token & it works
if(authToken == null) {
// display login-activity
Intent intent = new Intent(activity, ActFmLoginActivity.class);
activity.startActivityForResult(intent, 0);
} else {
activity.startService(new Intent(null, null,
activity, ActFmBackgroundService.class));
}
}
// ----------------------------------------------------------------------
// ----------------------------------------------------- synchronization!
// ----------------------------------------------------------------------
protected void performSync() {
actFmPreferenceService.recordSyncStart();
String syncSuccess = "failed";
try {
int serverTime = Preferences.getInt(ActFmPreferenceService.PREF_SERVER_TIME, 0);
ArrayList<ActFmTaskContainer> remoteTasks = new ArrayList<ActFmTaskContainer>();
// int newServerTime = fetchRemoteTasks(serverTime, remoteTasks);
if (serverTime == 0) { // If we've never synced, we may lose some empty tags
pushUnsavedTagData();
}
// fetchRemoteTagData(serverTime);
/* SyncData<ActFmTaskContainer> syncData = populateSyncData(remoteTasks);
try {
synchronizeTasks(syncData);
} finally {
syncData.localCreated.close();
syncData.localUpdated.close();
}
Preferences.setInt(ActFmPreferenceService.PREF_SERVER_TIME, newServerTime); */
actFmPreferenceService.recordSuccessfulSync();
syncSuccess = getFinalSyncStatus();
Intent broadcastIntent = new Intent(AstridApiConstants.BROADCAST_EVENT_REFRESH);
ContextManager.getContext().sendBroadcast(broadcastIntent, AstridApiConstants.PERMISSION_READ);
} catch (IllegalStateException e) {
// occurs when application was closed
} catch (Exception e) {
handleException("actfm-sync", e, false); //$NON-NLS-1$
} finally {
StatisticsService.reportEvent(StatisticsConstants.ACTFM_SYNC_FINISHED,
"success", syncSuccess); //$NON-NLS-1$
}
}
//Pushes unsaved, empty tag data, which will otherwise be deleted by the fetch tags call
private void pushUnsavedTagData() {
TodorooCursor<TagData> unsavedTagData = tagDataDao.query(Query.select(TagData.ID, TagData.TASK_COUNT).where(Criterion.and(TagData.TASK_COUNT.eq(0), TagData.REMOTE_ID.eq(0))));
TagData data = new TagData();
for (unsavedTagData.moveToFirst(); !unsavedTagData.isAfterLast(); unsavedTagData.moveToNext()) {
data.readFromCursor(unsavedTagData);
actFmSyncService.pushTag(data.getId());
}
}
/**
* Read remote tag data and merge with local
* @param serverTime last sync time
*/
private void fetchRemoteTagData(int serverTime) throws ActFmServiceException, IOException, JSONException {
actFmSyncService.fetchTags(serverTime);
}
/**
* Read remote task data into remote task array
* @param serverTime last sync time
*/
private int fetchRemoteTasks(int serverTime,
ArrayList<ActFmTaskContainer> remoteTasks) throws IOException,
ActFmServiceException, JSONException {
JSONObject result;
if(serverTime == 0)
result = invoker.invoke("task_list", "active", 1);
else
result = invoker.invoke("task_list", "modified_after", serverTime);
JSONArray taskList = result.getJSONArray("list");
for(int i = 0; i < taskList.length(); i++) {
ActFmTaskContainer remote = parseRemoteTask(taskList.getJSONObject(i));
// update reminder flags for incoming remote tasks to prevent annoying
if(remote.task.hasDueDate() && remote.task.getValue(Task.DUE_DATE) < DateUtilities.now())
remote.task.setFlag(Task.REMINDER_FLAGS, Task.NOTIFY_AFTER_DEADLINE, false);
actFmDataService.findLocalMatch(remote);
remoteTasks.add(remote);
}
return result.optInt("time", 0);
}
// ----------------------------------------------------------------------
// ------------------------------------------------------------ sync data
// ----------------------------------------------------------------------
/**
* Populate SyncData data structure
* @throws JSONException
*/
private SyncData<ActFmTaskContainer> populateSyncData(ArrayList<ActFmTaskContainer> remoteTasks) throws JSONException {
// fetch locally created tasks
TodorooCursor<Task> localCreated = actFmDataService.getLocallyCreated(Task.PROPERTIES);
// fetch locally updated tasks
TodorooCursor<Task> localUpdated = actFmDataService.getLocallyUpdated(Task.PROPERTIES);
return new SyncData<ActFmTaskContainer>(remoteTasks, localCreated, localUpdated);
}
// ----------------------------------------------------------------------
// ------------------------------------------------- create / push / pull
// ----------------------------------------------------------------------
@Override
protected ActFmTaskContainer create(ActFmTaskContainer local) throws IOException {
return push(local, null);
}
/** Create a task container for the given remote task
* @throws JSONException */
private ActFmTaskContainer parseRemoteTask(JSONObject remoteTask) throws JSONException {
Task task = new Task();
ArrayList<Metadata> metadata = new ArrayList<Metadata>();
JsonHelper.taskFromJson(remoteTask, task, metadata);
ActFmTaskContainer container = new ActFmTaskContainer(task, metadata, remoteTask);
return container;
}
@Override
protected ActFmTaskContainer pull(ActFmTaskContainer task) throws IOException {
if(task.task.getValue(Task.REMOTE_ID) == 0)
throw new ActFmServiceException("Tried to read an invalid task"); //$NON-NLS-1$
JSONObject remote = invoker.invoke("task_show", "id", task.task.getValue(Task.REMOTE_ID));
try {
return parseRemoteTask(remote);
} catch (JSONException e) {
throw new ActFmServiceException(e);
}
}
/**
* Send changes for the given Task across the wire.
*/
@Override
protected ActFmTaskContainer push(ActFmTaskContainer local, ActFmTaskContainer remote) throws IOException {
long id = local.task.getValue(Task.REMOTE_ID);
actFmSyncService.pushTaskOnSave(local.task, local.task.getDatabaseValues());
// push unsaved comments
for(Metadata item : local.metadata) {
if(NoteMetadata.METADATA_KEY.equals(item.getValue(Metadata.KEY)))
if(TextUtils.isEmpty(item.getValue(NoteMetadata.EXT_ID))) {
JSONObject comment = invoker.invoke("comment_add",
"task_id", id,
"message", item.getValue(NoteMetadata.BODY));
item.setValue(NoteMetadata.EXT_ID, comment.optString("id"));
}
}
return local;
}
@Override
protected void readRemotelyUpdated(SyncData<ActFmTaskContainer> data) throws IOException {
int serverTime = Preferences.getInt(ActFmPreferenceService.PREF_SERVER_TIME, 0);
ArrayList<ActFmTaskContainer> remoteTasks = new ArrayList<ActFmTaskContainer>();
try {
fetchRemoteTasks(serverTime, remoteTasks);
data.remoteUpdated = remoteTasks;
} catch (JSONException e) {
// Ingnored
}
super.readRemotelyUpdated(data);
}
// ----------------------------------------------------------------------
// --------------------------------------------------------- read / write
// ----------------------------------------------------------------------
@Override
protected ActFmTaskContainer read(TodorooCursor<Task> cursor) throws IOException {
return actFmDataService.readTaskAndMetadata(cursor);
}
@Override
protected void write(ActFmTaskContainer task) throws IOException {
if(task.task.isSaved()) {
Task local = PluginServices.getTaskService().fetchById(task.task.getId(), Task.COMPLETION_DATE);
if(task.task.isCompleted() && !local.isCompleted())
StatisticsService.reportEvent(StatisticsConstants.ACTFM_TASK_COMPLETED);
} else { // Set default reminders for remotely created tasks
TaskDao.setDefaultReminders(task.task);
}
task.task.setValue(Task.LAST_SYNC, DateUtilities.now() + 1000);
actFmDataService.saveTaskAndMetadata(task);
}
// ----------------------------------------------------------------------
// --------------------------------------------------------- misc helpers
// ----------------------------------------------------------------------
@Override
protected int matchTask(ArrayList<ActFmTaskContainer> tasks, ActFmTaskContainer target) {
int length = tasks.size();
for(int i = 0; i < length; i++) {
ActFmTaskContainer task = tasks.get(i);
if (task.task.getValue(Task.REMOTE_ID) == target.task.getValue(Task.REMOTE_ID))
return i;
}
return -1;
}
@Override
protected int updateNotification(Context context, Notification notification) {
String notificationTitle = context.getString(R.string.actfm_notification_title);
Intent intent = new Intent(context, ActFmPreferences.class);
PendingIntent notificationIntent = PendingIntent.getActivity(context, 0,
intent, 0);
notification.setLatestEventInfo(context,
notificationTitle, context.getString(R.string.SyP_progress),
notificationIntent);
return Constants.NOTIFICATION_SYNC;
}
@Override
protected void transferIdentifiers(ActFmTaskContainer source,
ActFmTaskContainer destination) {
destination.task.setValue(Task.REMOTE_ID, source.task.getValue(Task.REMOTE_ID));
}
}

@ -298,13 +298,14 @@ public final class ActFmSyncService {
public void pushTaskOnSave(Task task, ContentValues values) { public void pushTaskOnSave(Task task, ContentValues values) {
Task taskForRemote = taskService.fetchById(task.getId(), Task.REMOTE_ID, Task.CREATION_DATE); Task taskForRemote = taskService.fetchById(task.getId(), Task.REMOTE_ID, Task.CREATION_DATE);
long remoteId; long remoteId = 0;
if(task.containsValue(Task.REMOTE_ID)) { if(task.containsNonNullValue(Task.REMOTE_ID)) {
remoteId = task.getValue(Task.REMOTE_ID); remoteId = task.getValue(Task.REMOTE_ID);
} else { } else {
if(taskForRemote == null) if(taskForRemote == null)
return; return;
remoteId = taskForRemote.getValue(Task.REMOTE_ID); if(taskForRemote.containsNonNullValue(Task.REMOTE_ID))
remoteId = taskForRemote.getValue(Task.REMOTE_ID);
} }
long creationDate; long creationDate;
@ -876,22 +877,15 @@ public final class ActFmSyncService {
TodorooCursor<Update> cursor = updateDao.query(Query.select(Update.ID, Update.PICTURE).where(criterion)); TodorooCursor<Update> cursor = updateDao.query(Query.select(Update.ID, Update.PICTURE).where(criterion));
pushQueuedUpdates(cursor); pushQueuedUpdates(cursor);
Log.d("ActFmSyncService", "Push queued updates for tag");
} }
private void pushQueuedUpdates(Task task) { private void pushQueuedUpdates(Task task) {
Criterion criterion = null; Criterion criterion = null;
if (task.getValue(Task.REMOTE_ID) < 1) { if (task.containsNonNullValue(Task.REMOTE_ID)) {
criterion = Criterion.and(Update.REMOTE_ID.eq(0),
Update.TASK_LOCAL.eq(task.getId()));
}
else {
criterion = Criterion.and(Update.REMOTE_ID.eq(0), criterion = Criterion.and(Update.REMOTE_ID.eq(0),
Criterion.or(Update.TASK.eq(task.getValue(Task.REMOTE_ID)), Update.TASK_LOCAL.eq(task.getId()))); Criterion.or(Update.TASK.eq(task.getValue(Task.REMOTE_ID)), Update.TASK_LOCAL.eq(task.getId())));
} } else
return;
Update template = new Update(); Update template = new Update();
template.setValue(Update.TASK, task.getValue(Task.REMOTE_ID)); //$NON-NLS-1$ template.setValue(Update.TASK, task.getValue(Task.REMOTE_ID)); //$NON-NLS-1$
@ -899,8 +893,6 @@ public final class ActFmSyncService {
TodorooCursor<Update> cursor = updateDao.query(Query.select(Update.ID, Update.PICTURE).where(criterion)); TodorooCursor<Update> cursor = updateDao.query(Query.select(Update.ID, Update.PICTURE).where(criterion));
pushQueuedUpdates(cursor); pushQueuedUpdates(cursor);
Log.d("ActFmSyncService", "Push queued updates for task");
} }
private void pushQueuedUpdates( TodorooCursor<Update> cursor) { private void pushQueuedUpdates( TodorooCursor<Update> cursor) {

@ -213,8 +213,8 @@ public class ActFmSyncV2Provider extends SyncV2Provider {
TodorooCursor<Task> taskCursor = taskService.query(Query.select(Task.PROPERTIES). TodorooCursor<Task> taskCursor = taskService.query(Query.select(Task.PROPERTIES).
where(Criterion.or( where(Criterion.or(
Criterion.and(TaskCriteria.isActive(), Criterion.and(TaskCriteria.isActive(),
Task.REMOTE_ID.eq(0)), Task.REMOTE_ID.isNull()),
Criterion.and(Task.REMOTE_ID.gt(0), Criterion.and(Task.REMOTE_ID.isNotNull(),
Task.MODIFICATION_DATE.gt(Task.LAST_SYNC))))); Task.MODIFICATION_DATE.gt(Task.LAST_SYNC)))));
pushQueued(callback, finisher, taskCursor, false, taskPusher); pushQueued(callback, finisher, taskCursor, false, taskPusher);

@ -128,4 +128,4 @@ public class BackupPreferences extends TodorooPreferenceActivity {
} }
} }

@ -103,4 +103,4 @@ public class DefaultsPreferences extends TodorooPreferenceActivity {
} }
} }
} }

@ -291,4 +291,4 @@ public class OldTaskPreferences extends TodorooPreferenceActivity {
// :) // :)
} }
} }

@ -233,4 +233,4 @@ public class Calendars {
return Preferences.getStringValue(R.string.gcal_p_default); return Preferences.getStringValue(R.string.gcal_p_default);
} }
} }

@ -71,4 +71,4 @@ public class GtasksPreferences extends SyncProviderPreferences {
super.onPause(); super.onPause();
new GtasksBackgroundService().scheduleService(); new GtasksBackgroundService().scheduleService();
} }
} }

@ -233,4 +233,4 @@ public class GtasksLoginActivity extends ListActivity {
} }
} }
} }

@ -53,4 +53,4 @@ public class GtasksTaskContainer extends SyncContainer {
super.prepareForSaving(); super.prepareForSaving();
metadata.add(gtaskMetadata); metadata.add(gtaskMetadata);
} }
} }

@ -337,4 +337,4 @@ public final class LocaleEditAlerts extends ListActivity {
return super.onOptionsItemSelected(item); return super.onOptionsItemSelected(item);
} }
} }

@ -121,7 +121,6 @@ public class EditNoteActivity extends LinearLayout implements TimerActionListene
} }
public void loadViewForTaskID(long t){ public void loadViewForTaskID(long t){
task = PluginServices.getTaskService().fetchById(t, Task.NOTES, Task.ID, Task.REMOTE_ID, Task.TITLE); task = PluginServices.getTaskService().fetchById(t, Task.NOTES, Task.ID, Task.REMOTE_ID, Task.TITLE);
if(task == null) { if(task == null) {
return; return;
@ -130,7 +129,7 @@ public class EditNoteActivity extends LinearLayout implements TimerActionListene
setUpListAdapter(); setUpListAdapter();
if(actFmPreferenceService.isLoggedIn()) { if(actFmPreferenceService.isLoggedIn()) {
if(task.getValue(Task.REMOTE_ID) == 0) if(!task.containsNonNullValue(Task.REMOTE_ID))
refreshData(true, null); refreshData(true, null);
else { else {
String fetchKey = LAST_FETCH_KEY + task.getId(); String fetchKey = LAST_FETCH_KEY + task.getId();
@ -257,7 +256,7 @@ public class EditNoteActivity extends LinearLayout implements TimerActionListene
TodorooCursor<Update> updates; TodorooCursor<Update> updates;
if (task.getValue(Task.REMOTE_ID) < 1) { if (!task.containsNonNullValue(Task.REMOTE_ID)) {
updates = updateDao.query(Query.select(Update.PROPERTIES).where(Update.TASK_LOCAL.eq(task.getId()))); updates = updateDao.query(Query.select(Update.PROPERTIES).where(Update.TASK_LOCAL.eq(task.getId())));
} }
else { else {
@ -406,7 +405,7 @@ public class EditNoteActivity extends LinearLayout implements TimerActionListene
} }
// push task if it hasn't been pushed // push task if it hasn't been pushed
if(task.getValue(Task.REMOTE_ID) == 0 && !TextUtils.isEmpty(task.getValue(Task.TITLE))) { if(!task.containsNonNullValue(Task.REMOTE_ID) && !TextUtils.isEmpty(task.getValue(Task.TITLE))) {
new Thread(new Runnable() { new Thread(new Runnable() {
@Override @Override
public void run() { public void run() {
@ -446,7 +445,8 @@ public class EditNoteActivity extends LinearLayout implements TimerActionListene
update.setValue(Update.MESSAGE, message); update.setValue(Update.MESSAGE, message);
update.setValue(Update.ACTION_CODE, actionCode); update.setValue(Update.ACTION_CODE, actionCode);
update.setValue(Update.USER_ID, 0L); update.setValue(Update.USER_ID, 0L);
update.setValue(Update.TASK, task.getValue(Task.REMOTE_ID)); if(task.containsNonNullValue(Task.REMOTE_ID))
update.setValue(Update.TASK, task.getValue(Task.REMOTE_ID));
update.setValue(Update.TASK_LOCAL, task.getId()); update.setValue(Update.TASK_LOCAL, task.getId());
update.setValue(Update.CREATION_DATE, DateUtilities.now()); update.setValue(Update.CREATION_DATE, DateUtilities.now());

@ -405,4 +405,4 @@ public class OpencrxControlSet extends PopupControlSet {
// Nothing to do // Nothing to do
} }
} }

@ -277,4 +277,4 @@ public class ProducteevControlSet extends PopupControlSet {
// TODO Auto-generated method stub // TODO Auto-generated method stub
} }
} }

@ -94,4 +94,4 @@ public class ProducteevPreferences extends SyncProviderPreferences {
((ListPreference)preference).getEntries()[index])); ((ListPreference)preference).getEntries()[index]));
} }
} }
} }

@ -76,4 +76,4 @@ public class ProducteevUtilities extends SyncProviderUtilities {
return Preferences.getStringValue(R.string.producteev_PPr_email); return Preferences.getStringValue(R.string.producteev_PPr_email);
} }
} }

@ -101,4 +101,4 @@ public class ReminderPreferences extends TodorooPreferenceActivity {
} }
} }

@ -420,4 +420,4 @@ public class RepeatControlSet extends PopupControlSet {
return d; return d;
} }
} }

@ -81,4 +81,4 @@ public class TaskRabbitTaskContainer extends SyncContainer {
public boolean isTaskRabbit() { public boolean isTaskRabbit() {
return getTaskID() > 0; return getTaskID() > 0;
} }
} }

@ -104,4 +104,4 @@ public class TimerControlSet extends PopupControlSet implements TimerActionListe
return; return;
} }
} }

@ -36,4 +36,4 @@ public class MilkPreferences extends SyncProviderPreferences {
return MilkUtilities.INSTANCE; return MilkUtilities.INSTANCE;
} }
} }

@ -65,4 +65,4 @@ public class MilkTaskContainer extends SyncContainer {
metadata.add(MilkTaskFields.create(this)); metadata.add(MilkTaskFields.create(this));
} }
} }

@ -65,4 +65,4 @@ class About {
private About() { private About() {
// don't construct me // don't construct me
} }
} }

@ -465,4 +465,4 @@ public class EditPreferences extends TodorooPreferenceActivity {
super.onStop(); super.onStop();
} }
} }

@ -351,4 +351,4 @@ public class ExpandableListFragment extends Fragment
int groupPosition, int childPosition, long id) { int groupPosition, int childPosition, long id) {
return false; return false;
} }
} }

@ -719,10 +719,9 @@ ViewPager.OnPageChangeListener, EditNoteActivity.UpdatesChangedListener {
if (idParam > -1L) { if (idParam > -1L) {
model = taskService.fetchById(idParam, Task.PROPERTIES); model = taskService.fetchById(idParam, Task.PROPERTIES);
if (model != null) { if (model != null && model.containsNonNullValue(Task.REMOTE_ID)) {
remoteId = model.getValue(Task.REMOTE_ID); remoteId = model.getValue(Task.REMOTE_ID);
model.clearValue(Task.REMOTE_ID); // Having this can screw up model.clearValue(Task.REMOTE_ID); // Having this can screw up autosync
// autosync
} }
} }

@ -74,4 +74,4 @@ public class TaskEditViewPager extends PagerAdapter implements TitleProvider {
return null; return null;
} }
} }

@ -9,6 +9,7 @@ import android.app.Activity;
import android.app.AlertDialog; import android.app.AlertDialog;
import android.app.SearchManager; import android.app.SearchManager;
import android.content.BroadcastReceiver; import android.content.BroadcastReceiver;
import android.content.ContentValues;
import android.content.Context; import android.content.Context;
import android.content.DialogInterface; import android.content.DialogInterface;
import android.content.Intent; import android.content.Intent;
@ -260,6 +261,11 @@ public class TaskListFragment extends ListFragment implements OnScrollListener,
// We have a menu item to show in action bar. // We have a menu item to show in action bar.
setHasOptionsMenu(true); setHasOptionsMenu(true);
ContentValues values = new ContentValues();
values.put(Task.REMOTE_ID.name, 1);
TaskService.createWithValues(values, "task 1", taskService, metadataService);
TaskService.createWithValues(values, "task 2", taskService, metadataService);
setUpUiComponents(); setUpUiComponents();
onNewIntent(getActivity().getIntent()); onNewIntent(getActivity().getIntent());

@ -150,13 +150,6 @@ public class Database extends AbstractDatabase {
append(')'); append(')');
database.execSQL(sql.toString()); database.execSQL(sql.toString());
sql.setLength(0); sql.setLength(0);
sql.append("CREATE UNIQUE INDEX IF NOT EXISTS tg_rid ON ").
append(TagData.TABLE).append('(').
append(TagData.REMOTE_ID.name).
append(')');
database.execSQL(sql.toString());
sql.setLength(0);
} }
@Override @Override
@ -281,9 +274,9 @@ public class Database extends AbstractDatabase {
Log.e("astrid", "db-upgrade-" + oldVersion + "-" + newVersion, e); Log.e("astrid", "db-upgrade-" + oldVersion + "-" + newVersion, e);
} }
case 20: try { case 20: try {
String[] tables = new String[] { Task.TABLE.name, TagData.TABLE.name }; String[] tables = new String[] { Task.TABLE.name };
String [] ids = new String[] { Task.ID.name, TagData.ID.name }; String [] ids = new String[] { Task.ID.name };
String[] remoteIds = new String[] { Task.REMOTE_ID.name, TagData.REMOTE_ID.name }; String[] remoteIds = new String[] { Task.REMOTE_ID.name };
for (int i = 0; i < tables.length; i++) { for (int i = 0; i < tables.length; i++) {
String table = tables[i]; String table = tables[i];

@ -6,13 +6,16 @@
package com.todoroo.astrid.dao; package com.todoroo.astrid.dao;
import android.content.ContentValues; import android.content.ContentValues;
import android.database.sqlite.SQLiteConstraintException;
import com.timsu.astrid.R; import com.timsu.astrid.R;
import com.todoroo.andlib.data.DatabaseDao; import com.todoroo.andlib.data.DatabaseDao;
import com.todoroo.andlib.data.TodorooCursor;
import com.todoroo.andlib.service.Autowired; import com.todoroo.andlib.service.Autowired;
import com.todoroo.andlib.service.DependencyInjectionService; import com.todoroo.andlib.service.DependencyInjectionService;
import com.todoroo.andlib.sql.Criterion; import com.todoroo.andlib.sql.Criterion;
import com.todoroo.andlib.sql.Functions; import com.todoroo.andlib.sql.Functions;
import com.todoroo.andlib.sql.Query;
import com.todoroo.andlib.utility.DateUtilities; import com.todoroo.andlib.utility.DateUtilities;
import com.todoroo.andlib.utility.Preferences; import com.todoroo.andlib.utility.Preferences;
import com.todoroo.astrid.dao.MetadataDao.MetadataCriteria; import com.todoroo.astrid.dao.MetadataDao.MetadataCriteria;
@ -166,7 +169,17 @@ public class TaskDao extends DatabaseDao<Task> {
public boolean save(Task task) { public boolean save(Task task) {
boolean saveSuccessful; boolean saveSuccessful;
if (task.getId() == Task.NO_ID) { if (task.getId() == Task.NO_ID) {
saveSuccessful = createNew(task); try {
saveSuccessful = createNew(task);
} catch (SQLiteConstraintException e) { // Tried to create task with remote id that already exists
saveSuccessful = false;
TodorooCursor<Task> cursor = query(Query.select(Task.ID).where(Task.REMOTE_ID.eq(task.getValue(Task.REMOTE_ID))));
if (cursor.getCount() > 0) {
cursor.moveToFirst();
task.setId(cursor.get(Task.ID));
saveSuccessful = saveExisting(task);
}
}
} else { } else {
saveSuccessful = saveExisting(task); saveSuccessful = saveExisting(task);
} }

@ -144,4 +144,4 @@ abstract public class TaskAdapterAddOnManager<TYPE> {
return cache.get(taskId).values(); return cache.get(taskId).values();
} }
} }

@ -79,4 +79,4 @@ public abstract class TaskEditControlSet {
* Called when views need to be inflated * Called when views need to be inflated
*/ */
protected abstract void afterInflate(); protected abstract void afterInflate();
} }

@ -52,4 +52,4 @@ public class TaskListContextMenuExtensionLoader {
} }
} }

@ -7,4 +7,4 @@ abstract public class LegacyAlertModel {
public static final String TASK = "task"; public static final String TASK = "task";
public static final String DATE = "date"; public static final String DATE = "date";
} }

@ -60,4 +60,4 @@ public class LegacyRepeatInfo {
return rrule; return rrule;
} }
} }

@ -13,7 +13,6 @@ import com.todoroo.astrid.dao.TagDataDao;
import com.todoroo.astrid.dao.TaskDao; import com.todoroo.astrid.dao.TaskDao;
import com.todoroo.astrid.dao.UpdateDao; import com.todoroo.astrid.dao.UpdateDao;
import com.todoroo.astrid.data.TagData; import com.todoroo.astrid.data.TagData;
import com.todoroo.astrid.data.Task;
import com.todoroo.astrid.data.Update; import com.todoroo.astrid.data.Update;
/** /**
@ -141,10 +140,10 @@ public class TagDataService {
return updateDao.query(Query.select(Update.PROPERTIES).where( return updateDao.query(Query.select(Update.PROPERTIES).where(
criterion). criterion).
orderBy(Order.desc(Update.CREATION_DATE))); orderBy(Order.desc(Update.CREATION_DATE)));
if(tagData.getValue(Task.REMOTE_ID) < 1) if(tagData.getValue(TagData.REMOTE_ID) == 0)
return updateDao.query(Query.select(Update.PROPERTIES).where(Update.TAGS_LOCAL.like("%," + tagData.getId() + ",%"))); return updateDao.query(Query.select(Update.PROPERTIES).where(Update.TAGS_LOCAL.like("%," + tagData.getId() + ",%")));
return updateDao.query(Query.select(Update.PROPERTIES).where(Criterion.and(criterion, return updateDao.query(Query.select(Update.PROPERTIES).where(Criterion.and(criterion,
Criterion.or(Update.TAGS.like("%," + tagData.getValue(Task.REMOTE_ID) + ",%"), Criterion.or(Update.TAGS.like("%," + tagData.getValue(TagData.REMOTE_ID) + ",%"),
Update.TAGS_LOCAL.like("%," + tagData.getId() + ",%")))). Update.TAGS_LOCAL.like("%," + tagData.getId() + ",%")))).
orderBy(Order.desc(Update.CREATION_DATE))); orderBy(Order.desc(Update.CREATION_DATE)));
} }
@ -155,12 +154,12 @@ public class TagDataService {
* @return * @return
*/ */
public Update getLatestUpdate(TagData tagData) { public Update getLatestUpdate(TagData tagData) {
if(tagData.getValue(Task.REMOTE_ID) < 1) if(tagData.getValue(TagData.REMOTE_ID) == 0)
return null; return null;
@SuppressWarnings("nls") @SuppressWarnings("nls")
TodorooCursor<Update> updates = updateDao.query(Query.select(Update.PROPERTIES).where( TodorooCursor<Update> updates = updateDao.query(Query.select(Update.PROPERTIES).where(
Update.TAGS.like("%," + tagData.getValue(Task.REMOTE_ID) + ",%")). Update.TAGS.like("%," + tagData.getValue(TagData.REMOTE_ID) + ",%")).
orderBy(Order.desc(Update.CREATION_DATE)).limit(1)); orderBy(Order.desc(Update.CREATION_DATE)).limit(1));
try { try {
if(updates.getCount() == 0) if(updates.getCount() == 0)

@ -62,4 +62,4 @@ public class CalendarDialog extends Dialog implements OnClickListener, OnSelecte
public void setCalendarDate(Date calendarDate) { public void setCalendarDate(Date calendarDate) {
this.calendarDate = calendarDate; this.calendarDate = calendarDate;
} }
} }

@ -189,4 +189,4 @@ public class ContactListAdapter extends CursorAdapter {
} }
} }
} }
} }

@ -69,4 +69,4 @@ public class EditTextControlSet extends TaskEditControlSet {
return null; return null;
} }
} }

@ -40,4 +40,4 @@ public class ErrorCatchingEditText extends EditText {
} }
} }
} }

@ -258,4 +258,4 @@ public class HideUntilControlSet extends PopupControlSet implements OnItemSelect
return null; return null;
} }
} }

@ -44,4 +44,4 @@ public class NestedListView extends ListView {
setMeasuredDimension(getMeasuredWidth(), newHeight); setMeasuredDimension(getMeasuredWidth(), newHeight);
} }
} }

@ -489,4 +489,4 @@ public class NumberPicker extends LinearLayout implements OnClickListener,
validateCurrentView(str, true); validateCurrentView(str, true);
return mCurrent; return mCurrent;
} }
} }

@ -86,4 +86,4 @@ public class NumberPickerButton extends ImageButton {
mNumberPicker.cancelDecrement(); mNumberPicker.cancelDecrement();
} }
} }
} }

@ -95,4 +95,4 @@ public class RandomReminderControlSet extends TaskEditControlSet {
task.setValue(Task.REMINDER_PERIOD, 0L); task.setValue(Task.REMINDER_PERIOD, 0L);
return null; return null;
} }
} }

@ -127,4 +127,4 @@ public class ReminderControlSet extends PopupControlSet {
protected void refreshDisplayView() { protected void refreshDisplayView() {
// Nothing to do here // Nothing to do here
} }
} }

@ -122,4 +122,4 @@ public class TimeDurationControlSet implements OnNNumberPickedListener,
} }
} }

@ -325,4 +325,4 @@ public class VoiceInputAssistant {
} }
} }
} }
} }

@ -142,4 +142,4 @@ public class WelcomePagerAdapter extends PagerAdapter implements TitleProvider
return context.getString(title[position]); return context.getString(title[position]);
} }
} }

Loading…
Cancel
Save