diff --git a/api/src/com/todoroo/andlib/data/AbstractModel.java b/api/src/com/todoroo/andlib/data/AbstractModel.java index 0989dd6c5..4a572d750 100644 --- a/api/src/com/todoroo/andlib/data/AbstractModel.java +++ b/api/src/com/todoroo/andlib/data/AbstractModel.java @@ -139,6 +139,18 @@ public abstract class AbstractModel implements Parcelable, Cloneable { return getMergedValues().hashCode() ^ getClass().hashCode(); } + @Override + @SuppressWarnings("nls") + public String toString() { + StringBuilder builder = new StringBuilder(); + builder.append(getClass().getSimpleName()).append("\n") + .append("set values:\n") + .append(setValues).append("\n") + .append("values:\n") + .append(values).append("\n"); + return builder.toString(); + } + @Override public AbstractModel clone() { AbstractModel clone; diff --git a/api/src/com/todoroo/astrid/api/AstridApiConstants.java b/api/src/com/todoroo/astrid/api/AstridApiConstants.java index 54367876d..5f7a499a9 100644 --- a/api/src/com/todoroo/astrid/api/AstridApiConstants.java +++ b/api/src/com/todoroo/astrid/api/AstridApiConstants.java @@ -266,4 +266,9 @@ public class AstridApiConstants { */ public static final String BROADCAST_EVENT_TAG_DELETED = API_PACKAGE + ".TAG_DELETED"; + /** + * Action name for broadcast intent notifying that tag was renamed + */ + public static final String BROADCAST_EVENT_TAG_RENAMED = API_PACKAGE + ".TAG_RENAMED"; + } diff --git a/api/src/com/todoroo/astrid/data/TagData.java b/api/src/com/todoroo/astrid/data/TagData.java index e03d806e6..797f8b2d6 100644 --- a/api/src/com/todoroo/astrid/data/TagData.java +++ b/api/src/com/todoroo/astrid/data/TagData.java @@ -134,6 +134,7 @@ public final class TagData extends RemoteModel { TABLE, "activities_pushed_at", Property.PROP_FLAG_DATE); /** Tag ordering */ + @Deprecated public static final StringProperty TAG_ORDERING = new StringProperty( TABLE, "tagOrdering"); diff --git a/api/src/com/todoroo/astrid/data/User.java b/api/src/com/todoroo/astrid/data/User.java index 1a92cb25f..c885edf36 100644 --- a/api/src/com/todoroo/astrid/data/User.java +++ b/api/src/com/todoroo/astrid/data/User.java @@ -133,16 +133,20 @@ public final class User extends RemoteModel { return getDisplayName(NAME, FIRST_NAME, LAST_NAME); } + private String getCheckedString(StringProperty stringProperty) { + return containsNonNullValue(stringProperty) ? getValue(stringProperty) : null; + } + public String getDisplayName(StringProperty nameProperty, StringProperty firstNameProperty, StringProperty lastNameProperty) { - String name = getValue(nameProperty); + String name = getCheckedString(nameProperty); if (!(TextUtils.isEmpty(name) || "null".equals(name))) return name; - String firstName = getValue(firstNameProperty); + String firstName = getCheckedString(firstNameProperty); boolean firstNameEmpty = TextUtils.isEmpty(firstName) || "null".equals(firstName); - String lastName = getValue(lastNameProperty); + String lastName = getCheckedString(lastNameProperty); boolean lastNameEmpty = TextUtils.isEmpty(lastName) || "null".equals(lastName); if (firstNameEmpty && lastNameEmpty) - return getValue(EMAIL); + return getCheckedString(EMAIL); StringBuilder nameBuilder = new StringBuilder(); if (!firstNameEmpty) nameBuilder.append(firstName).append(" "); diff --git a/astrid/plugin-src/com/todoroo/astrid/actfm/ActFmLoginActivity.java b/astrid/plugin-src/com/todoroo/astrid/actfm/ActFmLoginActivity.java index c84cd8352..7a526a115 100644 --- a/astrid/plugin-src/com/todoroo/astrid/actfm/ActFmLoginActivity.java +++ b/astrid/plugin-src/com/todoroo/astrid/actfm/ActFmLoginActivity.java @@ -64,7 +64,25 @@ import com.todoroo.astrid.actfm.sync.ActFmInvoker; import com.todoroo.astrid.actfm.sync.ActFmPreferenceService; import com.todoroo.astrid.actfm.sync.ActFmServiceException; import com.todoroo.astrid.actfm.sync.ActFmSyncMonitor; +import com.todoroo.astrid.actfm.sync.messages.ConstructOutstandingTableFromMasterTable; +import com.todoroo.astrid.actfm.sync.messages.NameMaps; import com.todoroo.astrid.activity.Eula; +import com.todoroo.astrid.dao.TagDataDao; +import com.todoroo.astrid.dao.TagOutstandingDao; +import com.todoroo.astrid.dao.TaskDao; +import com.todoroo.astrid.dao.TaskListMetadataDao; +import com.todoroo.astrid.dao.TaskListMetadataOutstandingDao; +import com.todoroo.astrid.dao.TaskOutstandingDao; +import com.todoroo.astrid.dao.UserActivityDao; +import com.todoroo.astrid.dao.UserActivityOutstandingDao; +import com.todoroo.astrid.data.TagData; +import com.todoroo.astrid.data.TagOutstanding; +import com.todoroo.astrid.data.Task; +import com.todoroo.astrid.data.TaskListMetadata; +import com.todoroo.astrid.data.TaskListMetadataOutstanding; +import com.todoroo.astrid.data.TaskOutstanding; +import com.todoroo.astrid.data.UserActivity; +import com.todoroo.astrid.data.UserActivityOutstanding; import com.todoroo.astrid.gtasks.auth.ModernAuthManager; import com.todoroo.astrid.service.AstridDependencyInjector; import com.todoroo.astrid.service.MarketStrategy.AmazonMarketStrategy; @@ -90,6 +108,25 @@ public class ActFmLoginActivity extends FragmentActivity implements AuthListener @Autowired protected ActFmPreferenceService actFmPreferenceService; + @Autowired + private TaskDao taskDao; + @Autowired + private TaskOutstandingDao taskOutstandingDao; + @Autowired + private TagDataDao tagDataDao; + @Autowired + private TagOutstandingDao tagOutstandingDao; + @Autowired + private UserActivityDao userActivityDao; + @Autowired + private UserActivityOutstandingDao userActivityOutstandingDao; + @Autowired + private TaskListMetadataDao taskListMetadataDao; + @Autowired + private TaskListMetadataOutstandingDao taskListMetadataOutstandingDao; + + + @Autowired protected SyncV2Service syncService; private final ActFmInvoker actFmInvoker = new ActFmInvoker(); private Random rand; @@ -527,6 +564,13 @@ public class ActFmLoginActivity extends FragmentActivity implements AuthListener if (result.optBoolean("new")) { // Report new user statistic StatisticsService.reportEvent(StatisticsConstants.ACTFM_NEW_USER, "provider", provider); } + // Successful login, create outstanding entries + if (!TextUtils.isEmpty(token)) { + new ConstructOutstandingTableFromMasterTable(NameMaps.TABLE_ID_TASKS, taskDao, taskOutstandingDao, Task.CREATION_DATE).execute(); + new ConstructOutstandingTableFromMasterTable(NameMaps.TABLE_ID_TAGS, tagDataDao, tagOutstandingDao, TagData.CREATION_DATE).execute(); + new ConstructOutstandingTableFromMasterTable(NameMaps.TABLE_ID_USER_ACTIVITY, userActivityDao, userActivityOutstandingDao, UserActivity.CREATED_AT).execute(); + new ConstructOutstandingTableFromMasterTable(NameMaps.TABLE_ID_TASK_LIST_METADATA, taskListMetadataDao, taskListMetadataOutstandingDao, null).execute(); + } runOnUiThread(new Runnable() { public void run() { DialogUtilities.dismissDialog(ActFmLoginActivity.this, progressDialog); diff --git a/astrid/plugin-src/com/todoroo/astrid/actfm/EditPeopleControlSet.java b/astrid/plugin-src/com/todoroo/astrid/actfm/EditPeopleControlSet.java index 17876b36a..9ca985c9a 100644 --- a/astrid/plugin-src/com/todoroo/astrid/actfm/EditPeopleControlSet.java +++ b/astrid/plugin-src/com/todoroo/astrid/actfm/EditPeopleControlSet.java @@ -370,6 +370,18 @@ public class EditPeopleControlSet extends PopupControlSet { }); } + private String getLongOrStringId(JSONObject obj, String defaultValue) { + try { + long value = obj.getLong("id"); //$NON-NLS-1$ + return Long.toString(value); + } catch (JSONException e) { + String value = obj.optString("id"); //$NON-NLS-1$ + if (TextUtils.isEmpty(value)) + value = defaultValue; + return value; + } + } + @SuppressWarnings("nls") private ArrayList convertJsonUsersToAssignedUsers(ArrayList jsonUsers, HashSet userIds, HashSet emails, HashMap names) { @@ -378,7 +390,7 @@ public class EditPeopleControlSet extends PopupControlSet { JSONObject person = jsonUsers.get(i); if(person == null) continue; - String id = Long.toString(person.optLong("id", -2)); + String id = getLongOrStringId(person, Task.USER_ID_EMAIL); if(ActFmPreferenceService.userId().equals(id) || ((Task.USER_ID_UNASSIGNED.equals(id) || Task.isRealUserId(id)) && userIds.contains(id))) continue; userIds.add(id); @@ -418,11 +430,11 @@ public class EditPeopleControlSet extends PopupControlSet { @SuppressWarnings("nls") private int findAssignedIndex(Task t, ArrayList... userLists) { String assignedStr = t.getValue(Task.USER); - long assignedId = -2; + String assignedId = Task.USER_ID_IGNORE; String assignedEmail = t.getValue(Task.USER_ID); try { JSONObject assigned = new JSONObject(assignedStr); - assignedId = assigned.optLong("id", -2); + assignedId = getLongOrStringId(assigned, Task.USER_ID_EMAIL); assignedEmail = assigned.optString("email"); } catch (JSONException e) { User assignedUser = userDao.fetch(t.getValue(Task.USER_ID), User.PROPERTIES); @@ -430,8 +442,14 @@ public class EditPeopleControlSet extends PopupControlSet { try { if (assignedUser != null) { ActFmSyncService.JsonHelper.jsonFromUser(assigned, assignedUser); - assignedId = assigned.optLong("id", -2); + try { + assignedId = assigned.getString("id"); + } catch (JSONException e2) { + assignedId = getLongOrStringId(assigned, Task.USER_ID_EMAIL); + } assignedEmail = assigned.optString("email"); + } else if (!t.getValue(Task.USER_ID).contains("@")) { + assignedId = t.getValue(Task.USER_ID); } } catch (JSONException e2) { // @@ -443,7 +461,7 @@ public class EditPeopleControlSet extends PopupControlSet { for (int i = 0; i < userList.size(); i++) { JSONObject user = userList.get(i).user; if (user != null) { - if (user.optLong("id") == assignedId || + if (getLongOrStringId(user, Task.USER_ID_EMAIL).equals(assignedId) || (user.optString("email").equals(assignedEmail) && !(TextUtils.isEmpty(assignedEmail)))) return index; @@ -649,23 +667,22 @@ public class EditPeopleControlSet extends PopupControlSet { activity.getString(R.string.actfm_EPA_invalid_email, userJson.optString("email"))); } - if(userJson == null || Task.USER_ID_SELF.equals(Long.toString(userJson.optLong("id", -2)))) { - dirty = task.getValue(Task.USER_ID) == Task.USER_ID_SELF ? dirty : true; + if(userJson == null || Task.USER_ID_SELF.equals(getLongOrStringId(userJson, Task.USER_ID_EMAIL))) { + dirty = Task.USER_ID_SELF.equals(task.getValue(Task.USER_ID)) ? dirty : true; task.setValue(Task.USER_ID, Task.USER_ID_SELF); task.setValue(Task.USER, ""); assignedToMe = true; - } else if(Task.USER_ID_UNASSIGNED.equals(Long.toString(userJson.optLong("id")))) { - dirty = task.getValue(Task.USER_ID) == Task.USER_ID_UNASSIGNED ? dirty : true; + } else if(Task.USER_ID_UNASSIGNED.equals(getLongOrStringId(userJson, Task.USER_ID_SELF))) { + dirty = Task.USER_ID_UNASSIGNED.equals(task.getValue(Task.USER_ID)) ? dirty : true; task.setValue(Task.USER_ID, Task.USER_ID_UNASSIGNED); task.setValue(Task.USER, ""); } else { - String user = userJson.toString(); String taskUserId = Task.USER_ID_EMAIL; String taskUserEmail = ""; try { @SuppressWarnings("deprecation") // For backwards compatibility JSONObject taskUser = new JSONObject(task.getValue(Task.USER)); - taskUserId = Long.toString(taskUser.optLong("id", -2)); + taskUserId = getLongOrStringId(taskUser, Task.USER_ID_EMAIL); taskUserEmail = taskUser.optString("email"); } catch (JSONException e) { // sad times @@ -673,14 +690,14 @@ public class EditPeopleControlSet extends PopupControlSet { if (Task.userIdIsEmail(taskUserId)) taskUserEmail = taskUserId; } - String userId = Long.toString(userJson.optLong("id", -2)); + String userId = getLongOrStringId(userJson, Task.USER_ID_EMAIL); String userEmail = userJson.optString("email"); boolean match = userId.equals(taskUserId); match = match || (userEmail.equals(taskUserEmail) && !TextUtils.isEmpty(userEmail)); dirty = match ? dirty : true; - String willAssignToId = Long.toString(userJson.optLong("id", -2)); + String willAssignToId = getLongOrStringId(userJson, Task.USER_ID_EMAIL); task.setValue(Task.USER_ID, willAssignToId); if (Task.USER_ID_EMAIL.equals(task.getValue(Task.USER_ID))) task.setValue(Task.USER_ID, userEmail); @@ -760,7 +777,7 @@ public class EditPeopleControlSet extends PopupControlSet { userJson = item.user; } - if(userJson == null || Task.USER_ID_SELF.equals(Long.toString(userJson.optLong("id", -2)))) { //$NON-NLS-1$ + if(userJson == null || Task.USER_ID_SELF.equals(getLongOrStringId(userJson, Task.USER_ID_EMAIL))) { return true; } diff --git a/astrid/plugin-src/com/todoroo/astrid/actfm/TagSettingsActivity.java b/astrid/plugin-src/com/todoroo/astrid/actfm/TagSettingsActivity.java index a8645f919..4a4672073 100644 --- a/astrid/plugin-src/com/todoroo/astrid/actfm/TagSettingsActivity.java +++ b/astrid/plugin-src/com/todoroo/astrid/actfm/TagSettingsActivity.java @@ -274,13 +274,13 @@ public class TagSettingsActivity extends FragmentActivity { if (nameChanged) { if (oldName.equalsIgnoreCase(newName)) { // Change the capitalization of a list manually tagData.setValue(TagData.NAME, newName); - service.renameCaseSensitive(oldName, newName); + service.rename(tagData.getUuid(), newName); } else { // Rename list--check for existing name newName = service.getTagWithCase(newName); tagName.setText(newName); if (!newName.equals(oldName)) { tagData.setValue(TagData.NAME, newName); - service.rename(oldName, newName); + service.rename(tagData.getUuid(), newName); } else { nameChanged = false; } @@ -354,14 +354,15 @@ public class TagSettingsActivity extends FragmentActivity { InputMethodManager imm = (InputMethodManager)getSystemService(Context.INPUT_METHOD_SERVICE); imm.hideSoftInputFromWindow(tagName.getWindowToken(), 0); + tagDataService.save(tagData); + tagMetadataDao.synchronizeMembers(tagData.getId(), tagData.getUuid(), members); + if (isNewTag) { setResult(RESULT_OK, new Intent().putExtra(TOKEN_NEW_FILTER, TagFilterExposer.filterFromTagData(TagSettingsActivity.this, tagData))); } else { setResult(RESULT_OK); } - tagDataService.save(tagData); - tagMetadataDao.synchronizeMembers(tagData.getId(), tagData.getUuid(), members); refreshSettingsPage(); finish(); @@ -539,6 +540,15 @@ public class TagSettingsActivity extends FragmentActivity { return super.onCreateOptionsMenu(menu); } + @Override + public void onBackPressed() { + if (tagName.getText().length() == 0) { + finish(); + } else { + saveSettings(); + } + } + @Override public boolean onOptionsItemSelected(MenuItem item) { switch(item.getItemId()) { @@ -579,7 +589,7 @@ public class TagSettingsActivity extends FragmentActivity { } protected boolean deleteTag() { - boolean result = tagService.deleteOrLeaveTag(this, tagData.getValue(TagData.NAME), TagService.SHOW_ACTIVE_TASKS); + boolean result = tagService.deleteOrLeaveTag(this, tagData.getValue(TagData.NAME), tagData.getUuid()); setResult(Activity.RESULT_OK); finish(); return result; diff --git a/astrid/plugin-src/com/todoroo/astrid/actfm/TagViewFragment.java b/astrid/plugin-src/com/todoroo/astrid/actfm/TagViewFragment.java index 98c23bf5a..8d3ccecc1 100644 --- a/astrid/plugin-src/com/todoroo/astrid/actfm/TagViewFragment.java +++ b/astrid/plugin-src/com/todoroo/astrid/actfm/TagViewFragment.java @@ -242,9 +242,13 @@ public class TagViewFragment extends TaskListFragment { if(tag == null && RemoteModel.NO_UUID.equals(uuid)) return; - TodorooCursor cursor = tagDataService.query(Query.select(TagData.PROPERTIES).where( - Criterion.or(TagData.NAME.eqCaseInsensitive(tag), - TagData.UUID.eq(uuid)))); + TodorooCursor cursor; + if (!RemoteModel.isUuidEmpty(uuid)) { + cursor = tagDataService.query(Query.select(TagData.PROPERTIES).where(TagData.UUID.eq(uuid))); + } else { + cursor = tagDataService.query(Query.select(TagData.PROPERTIES).where(TagData.NAME.eqCaseInsensitive(tag))); + } + try { tagData = new TagData(); if(cursor.getCount() == 0) { @@ -329,7 +333,7 @@ public class TagViewFragment extends TaskListFragment { /** refresh the list with latest data from the web */ private void refreshData() { - if (actFmPreferenceService.isLoggedIn()) { + if (actFmPreferenceService.isLoggedIn() && tagData != null && !RemoteModel.isUuidEmpty(tagData.getUuid())) { ((TextView)taskListView.findViewById(android.R.id.empty)).setText(R.string.DLG_loading); Runnable callback = new Runnable() { @@ -468,6 +472,11 @@ public class TagViewFragment extends TaskListFragment { } catch (JSONException e) { e.printStackTrace(); } + } else { + membersView.removeAllViews(); + membersView.setOnClickListener(settingsListener); + TextView textView = (TextView) getActivity().getLayoutInflater().inflate(R.layout.no_members_text_view, null); + membersView.addView(textView); } View filterAssigned = getView().findViewById(R.id.filter_assigned); diff --git a/astrid/plugin-src/com/todoroo/astrid/actfm/sync/ActFmInvoker.java b/astrid/plugin-src/com/todoroo/astrid/actfm/sync/ActFmInvoker.java index 4d878af5f..72d20a464 100644 --- a/astrid/plugin-src/com/todoroo/astrid/actfm/sync/ActFmInvoker.java +++ b/astrid/plugin-src/com/todoroo/astrid/actfm/sync/ActFmInvoker.java @@ -40,7 +40,7 @@ import com.todoroo.astrid.utility.Constants; public class ActFmInvoker { /** NOTE: these values are development values & will not work on production */ - private static final String URL = "//192.168.0.160:3000/api/"; + private static final String URL = "//10.0.2.2:3000/api/"; private static final String APP_ID = "a4732a32859dbcd3e684331acd36432c"; private static final String APP_SECRET = "e389bfc82a0d932332f9a8bd8203735f"; diff --git a/astrid/plugin-src/com/todoroo/astrid/actfm/sync/ActFmPreferenceService.java b/astrid/plugin-src/com/todoroo/astrid/actfm/sync/ActFmPreferenceService.java index 7d99b9509..c32104f63 100644 --- a/astrid/plugin-src/com/todoroo/astrid/actfm/sync/ActFmPreferenceService.java +++ b/astrid/plugin-src/com/todoroo/astrid/actfm/sync/ActFmPreferenceService.java @@ -13,6 +13,7 @@ import android.text.TextUtils; import com.timsu.astrid.R; import com.todoroo.andlib.utility.Preferences; import com.todoroo.astrid.billing.BillingConstants; +import com.todoroo.astrid.dao.RemoteModelDao; import com.todoroo.astrid.service.StatisticsConstants; import com.todoroo.astrid.service.StatisticsService; import com.todoroo.astrid.sync.SyncProviderUtilities; @@ -54,6 +55,15 @@ public class ActFmPreferenceService extends SyncProviderUtilities { // --- user management + @Override + public void setToken(String setting) { + super.setToken(setting); + if (TextUtils.isEmpty(setting)) + RemoteModelDao.outstandingEntryFlag = -1; + else + RemoteModelDao.outstandingEntryFlag = 1; + } + /** * @return get user id */ diff --git a/astrid/plugin-src/com/todoroo/astrid/actfm/sync/ActFmSyncThread.java b/astrid/plugin-src/com/todoroo/astrid/actfm/sync/ActFmSyncThread.java index cad1d135d..8fc756ad2 100644 --- a/astrid/plugin-src/com/todoroo/astrid/actfm/sync/ActFmSyncThread.java +++ b/astrid/plugin-src/com/todoroo/astrid/actfm/sync/ActFmSyncThread.java @@ -25,7 +25,9 @@ import com.todoroo.astrid.actfm.sync.messages.ChangesHappened; import com.todoroo.astrid.actfm.sync.messages.ClientToServerMessage; import com.todoroo.astrid.actfm.sync.messages.NameMaps; import com.todoroo.astrid.actfm.sync.messages.ReplayOutstandingEntries; +import com.todoroo.astrid.actfm.sync.messages.ReplayTaskListMetadataOutstanding; import com.todoroo.astrid.actfm.sync.messages.ServerToClientMessage; +import com.todoroo.astrid.actfm.sync.messages.TaskListMetadataChangesHappened; import com.todoroo.astrid.api.AstridApiConstants; import com.todoroo.astrid.core.PluginServices; import com.todoroo.astrid.dao.OutstandingEntryDao; @@ -181,7 +183,7 @@ public class ActFmSyncThread { } } - private final Runnable defaultRefreshRunnable = new Runnable() { + public static final Runnable DEFAULT_REFRESH_RUNNABLE = new Runnable() { @Override public void run() { Intent refresh = new Intent(AstridApiConstants.BROADCAST_EVENT_REFRESH); @@ -211,10 +213,10 @@ public class ActFmSyncThread { if (timeForBackgroundSync()) { repopulateQueueFromOutstandingTables(); - enqueueMessage(BriefMe.instantiateBriefMeForClass(TaskListMetadata.class, NameMaps.PUSHED_AT_TASK_LIST_METADATA), defaultRefreshRunnable); - enqueueMessage(BriefMe.instantiateBriefMeForClass(Task.class, NameMaps.PUSHED_AT_TASKS), defaultRefreshRunnable); - enqueueMessage(BriefMe.instantiateBriefMeForClass(TagData.class, NameMaps.PUSHED_AT_TAGS), defaultRefreshRunnable); - enqueueMessage(BriefMe.instantiateBriefMeForClass(User.class, NameMaps.PUSHED_AT_USERS), defaultRefreshRunnable); + enqueueMessage(BriefMe.instantiateBriefMeForClass(TaskListMetadata.class, NameMaps.PUSHED_AT_TASK_LIST_METADATA), DEFAULT_REFRESH_RUNNABLE); + enqueueMessage(BriefMe.instantiateBriefMeForClass(Task.class, NameMaps.PUSHED_AT_TASKS), DEFAULT_REFRESH_RUNNABLE); + enqueueMessage(BriefMe.instantiateBriefMeForClass(TagData.class, NameMaps.PUSHED_AT_TAGS), DEFAULT_REFRESH_RUNNABLE); + enqueueMessage(BriefMe.instantiateBriefMeForClass(User.class, NameMaps.PUSHED_AT_USERS), DEFAULT_REFRESH_RUNNABLE); setTimeForBackgroundSync(false); } @@ -270,7 +272,7 @@ public class ActFmSyncThread { try { Runnable r = pendingCallbacks.remove(message); if (r != null) { - if (r == defaultRefreshRunnable) { + if (r == DEFAULT_REFRESH_RUNNABLE) { if (didDefaultRefreshThisLoop) continue; didDefaultRefreshThisLoop = true; @@ -298,9 +300,9 @@ public class ActFmSyncThread { // Called after a batch has finished processing private void replayOutstandingChanges(boolean afterErrors) { syncLog("Replaying outstanding changes"); //$NON-NLS-1$ - new ReplayOutstandingEntries(Task.class, NameMaps.TABLE_ID_TASKS, taskDao, taskOutstandingDao, this, afterErrors).execute(); - new ReplayOutstandingEntries(TagData.class, NameMaps.TABLE_ID_TAGS, tagDataDao, tagOutstandingDao, this, afterErrors).execute(); - new ReplayOutstandingEntries(TaskListMetadata.class, NameMaps.TABLE_ID_TASK_LIST_METADATA, taskListMetadataDao, taskListMetadataOutstandingDao, this, afterErrors).execute(); + new ReplayOutstandingEntries(Task.class, NameMaps.TABLE_ID_TASKS, taskDao, taskOutstandingDao, afterErrors).execute(); + new ReplayOutstandingEntries(TagData.class, NameMaps.TABLE_ID_TAGS, tagDataDao, tagOutstandingDao, afterErrors).execute(); + new ReplayTaskListMetadataOutstanding(taskListMetadataDao, taskListMetadataOutstandingDao, afterErrors).execute(); } private boolean timeForBackgroundSync() { @@ -308,9 +310,11 @@ public class ActFmSyncThread { } public void repopulateQueueFromOutstandingTables() { + syncLog("Constructing queue from outstanding tables"); //$NON-NLS-1$ constructChangesHappenedFromOutstandingTable(Task.class, taskDao, taskOutstandingDao); constructChangesHappenedFromOutstandingTable(TagData.class, tagDataDao, tagOutstandingDao); constructChangesHappenedFromOutstandingTable(UserActivity.class, userActivityDao, userActivityOutstandingDao); + constructChangesHappenedForTaskListMetadata(taskListMetadataDao, taskListMetadataOutstandingDao); } private > void constructChangesHappenedFromOutstandingTable(Class modelClass, RemoteModelDao modelDao, OutstandingEntryDao oustandingDao) { @@ -318,8 +322,19 @@ public class ActFmSyncThread { try { for (outstanding.moveToFirst(); !outstanding.isAfterLast(); outstanding.moveToNext()) { Long id = outstanding.get(OutstandingEntry.ENTITY_ID_PROPERTY); - ChangesHappened ch = new ChangesHappened(id, modelClass, modelDao, oustandingDao); - enqueueMessage(ch, null); + enqueueMessage(new ChangesHappened(id, modelClass, modelDao, oustandingDao), null); + } + } finally { + outstanding.close(); + } + } + + private void constructChangesHappenedForTaskListMetadata(TaskListMetadataDao dao, TaskListMetadataOutstandingDao outstandingDao) { + TodorooCursor outstanding = outstandingDao.query(Query.select(OutstandingEntry.ENTITY_ID_PROPERTY).groupBy(OutstandingEntry.ENTITY_ID_PROPERTY)); + try { + for (outstanding.moveToFirst(); !outstanding.isAfterLast(); outstanding.moveToNext()) { + Long id = outstanding.get(OutstandingEntry.ENTITY_ID_PROPERTY); + ActFmSyncWaitingPool.getInstance().enqueueMessage(new TaskListMetadataChangesHappened(id, TaskListMetadata.class, dao, outstandingDao)); } } finally { outstanding.close(); diff --git a/astrid/plugin-src/com/todoroo/astrid/actfm/sync/ActFmSyncWaitingPool.java b/astrid/plugin-src/com/todoroo/astrid/actfm/sync/ActFmSyncWaitingPool.java index c0e6dca36..37e93b1ec 100644 --- a/astrid/plugin-src/com/todoroo/astrid/actfm/sync/ActFmSyncWaitingPool.java +++ b/astrid/plugin-src/com/todoroo/astrid/actfm/sync/ActFmSyncWaitingPool.java @@ -35,7 +35,7 @@ public class ActFmSyncWaitingPool { return; AndroidUtilities.sleepDeep(WAIT_TIME); while (!pendingMessages.isEmpty()) { - ActFmSyncThread.getInstance().enqueueMessage(pendingMessages.remove(0), null); + ActFmSyncThread.getInstance().enqueueMessage(pendingMessages.remove(0), ActFmSyncThread.DEFAULT_REFRESH_RUNNABLE); } } }; diff --git a/astrid/plugin-src/com/todoroo/astrid/actfm/sync/AstridNewSyncMigrator.java b/astrid/plugin-src/com/todoroo/astrid/actfm/sync/AstridNewSyncMigrator.java index eaea97034..aa8dc1c65 100644 --- a/astrid/plugin-src/com/todoroo/astrid/actfm/sync/AstridNewSyncMigrator.java +++ b/astrid/plugin-src/com/todoroo/astrid/actfm/sync/AstridNewSyncMigrator.java @@ -20,6 +20,7 @@ import com.todoroo.andlib.utility.Preferences; import com.todoroo.astrid.actfm.sync.messages.NameMaps; import com.todoroo.astrid.dao.MetadataDao.MetadataCriteria; import com.todoroo.astrid.dao.OutstandingEntryDao; +import com.todoroo.astrid.dao.RemoteModelDao; import com.todoroo.astrid.dao.TagDataDao; import com.todoroo.astrid.dao.TagOutstandingDao; import com.todoroo.astrid.dao.TaskAttachmentDao; @@ -133,7 +134,6 @@ public class AstridNewSyncMigrator { // -------------- // Then ensure that every remote model has a remote id, by generating one using the uuid generator for all those without one // -------------- - final Set tagsThatNeedOrderingSync = new HashSet(); final Set tasksThatNeedTagSync = new HashSet(); try { Query tagsQuery = Query.select(TagData.ID, TagData.UUID, TagData.MODIFICATION_DATE).where(Criterion.or(TagData.UUID.eq(RemoteModel.NO_UUID), TagData.UUID.isNull())); @@ -146,14 +146,12 @@ public class AstridNewSyncMigrator { @Override public boolean shouldCreateOutstandingEntries(TagData instance) { - return lastFetchTime == 0 || (instance.containsNonNullValue(TagData.MODIFICATION_DATE) && instance.getValue(TagData.MODIFICATION_DATE) > lastFetchTime); + boolean result = lastFetchTime == 0 || (instance.containsNonNullValue(TagData.MODIFICATION_DATE) && instance.getValue(TagData.MODIFICATION_DATE) > lastFetchTime); + return result && RemoteModelDao.getOutstandingEntryFlag(); } @Override - public void afterSave(TagData instance, boolean createdOutstanding) { - if (createdOutstanding) - tagsThatNeedOrderingSync.add(instance.getId()); - } + public void afterSave(TagData instance, boolean createdOutstanding) {/**/} }); Query tasksQuery = Query.select(Task.ID, Task.UUID, Task.RECURRENCE, Task.FLAGS, Task.MODIFICATION_DATE, Task.LAST_SYNC).where(Criterion.all); @@ -185,8 +183,9 @@ public class AstridNewSyncMigrator { @Override public boolean shouldCreateOutstandingEntries(Task instance) { if (!instance.containsNonNullValue(Task.MODIFICATION_DATE) || instance.getValue(Task.LAST_SYNC) == 0) - return true; - return instance.getValue(Task.LAST_SYNC) < instance.getValue(Task.MODIFICATION_DATE); + return RemoteModelDao.getOutstandingEntryFlag(); + + return (instance.getValue(Task.LAST_SYNC) < instance.getValue(Task.MODIFICATION_DATE)) && RemoteModelDao.getOutstandingEntryFlag(); } @Override @@ -350,8 +349,6 @@ public class AstridNewSyncMigrator { tlm.setValue(TaskListMetadata.TASK_IDS, tagOrdering); tlm.setValue(TaskListMetadata.TAG_UUID, td.getUuid()); - if (!tagsThatNeedOrderingSync.contains(td.getId())) - tlm.putTransitory(SyncFlags.ACTFM_SUPPRESS_OUTSTANDING_ENTRIES, true); taskListMetadataDao.createNew(tlm); } } finally { @@ -515,7 +512,7 @@ public class AstridNewSyncMigrator { private void updateTagUuid(Metadata m) { String tag = m.getValue(TaskToTagMetadata.TAG_NAME); - TagData tagData = tagDataService.getTag(tag, TagData.UUID); + TagData tagData = tagDataService.getTagByName(tag, TagData.UUID); if (tagData != null) { if (ActFmInvoker.SYNC_DEBUG) Log.w(LOG_TAG, "Linking with tag uuid " + tagData.getValue(TagData.UUID)); diff --git a/astrid/plugin-src/com/todoroo/astrid/actfm/sync/messages/ConstructOutstandingTableFromMasterTable.java b/astrid/plugin-src/com/todoroo/astrid/actfm/sync/messages/ConstructOutstandingTableFromMasterTable.java new file mode 100644 index 000000000..fcdf9f2ea --- /dev/null +++ b/astrid/plugin-src/com/todoroo/astrid/actfm/sync/messages/ConstructOutstandingTableFromMasterTable.java @@ -0,0 +1,61 @@ +package com.todoroo.astrid.actfm.sync.messages; + +import android.util.Log; + +import com.todoroo.andlib.data.AbstractModel; +import com.todoroo.andlib.data.Property; +import com.todoroo.andlib.data.Property.LongProperty; +import com.todoroo.andlib.data.TodorooCursor; +import com.todoroo.andlib.sql.Query; +import com.todoroo.andlib.utility.AndroidUtilities; +import com.todoroo.andlib.utility.DateUtilities; +import com.todoroo.astrid.dao.OutstandingEntryDao; +import com.todoroo.astrid.dao.RemoteModelDao; +import com.todoroo.astrid.data.OutstandingEntry; +import com.todoroo.astrid.data.RemoteModel; + +@SuppressWarnings("nls") +public class ConstructOutstandingTableFromMasterTable> { + + private final String table; + private final RemoteModelDao dao; + private final OutstandingEntryDao outstandingDao; + private final LongProperty createdAtProperty; + + public ConstructOutstandingTableFromMasterTable(String table, RemoteModelDao dao, + OutstandingEntryDao outstandingDao, LongProperty createdAtProperty) { + this.table = table; + this.dao = dao; + this.outstandingDao = outstandingDao; + this.createdAtProperty = createdAtProperty; + } + + public void execute() { + Property[] syncableProperties = NameMaps.syncableProperties(table); + TodorooCursor items = dao.query(Query.select(AndroidUtilities.addToArray(syncableProperties, AbstractModel.ID_PROPERTY, RemoteModel.UUID_PROPERTY))); + try { + OE oe = outstandingDao.getModelClass().newInstance(); + for (items.moveToFirst(); !items.isAfterLast(); items.moveToNext()) { + long itemId = items.get(AbstractModel.ID_PROPERTY); + for (Property p : syncableProperties) { + oe.clear(); + oe.setValue(OutstandingEntry.ENTITY_ID_PROPERTY, itemId); + oe.setValue(OutstandingEntry.COLUMN_STRING_PROPERTY, p.name); + oe.setValue(OutstandingEntry.VALUE_STRING_PROPERTY, items.get(p).toString()); + if (createdAtProperty != null) + oe.setValue(OutstandingEntry.CREATED_AT_PROPERTY, items.get(createdAtProperty)); + else + oe.setValue(OutstandingEntry.CREATED_AT_PROPERTY, DateUtilities.now()); + outstandingDao.createNew(oe); + } + } + } catch (IllegalAccessException e) { + Log.e("ConstructOutstanding", "Error instantiating outstanding model class", e); + } catch (InstantiationException e2) { + Log.e("ConstructOutstanding", "Error instantiating outstanding model class", e2); + } finally { + items.close(); + } + } + +} diff --git a/astrid/plugin-src/com/todoroo/astrid/actfm/sync/messages/MakeChanges.java b/astrid/plugin-src/com/todoroo/astrid/actfm/sync/messages/MakeChanges.java index 2b7041efd..30474f285 100644 --- a/astrid/plugin-src/com/todoroo/astrid/actfm/sync/messages/MakeChanges.java +++ b/astrid/plugin-src/com/todoroo/astrid/actfm/sync/messages/MakeChanges.java @@ -118,8 +118,11 @@ public class MakeChanges extends ServerToClientMessage } private Criterion getMatchCriterion(TYPE model) { - if (NameMaps.TABLE_ID_TASK_LIST_METADATA.equals(table) && model.getSetValues().containsKey(TaskListMetadata.FILTER.name)) { - return TaskListMetadata.FILTER.eq(model.getSetValues().getAsString(TaskListMetadata.FILTER.name)); + if (NameMaps.TABLE_ID_TASK_LIST_METADATA.equals(table)) { + if (model.getSetValues().containsKey(TaskListMetadata.FILTER.name)) + return TaskListMetadata.FILTER.eq(model.getSetValues().getAsString(TaskListMetadata.FILTER.name)); + else if (model.getSetValues().containsKey(TaskListMetadata.TAG_UUID.name)) + return TaskListMetadata.TAG_UUID.eq(model.getSetValues().getAsString(TaskListMetadata.TAG_UUID.name)); } return null; } diff --git a/astrid/plugin-src/com/todoroo/astrid/actfm/sync/messages/ReplayOutstandingEntries.java b/astrid/plugin-src/com/todoroo/astrid/actfm/sync/messages/ReplayOutstandingEntries.java index c2b5d21f0..260f624ac 100644 --- a/astrid/plugin-src/com/todoroo/astrid/actfm/sync/messages/ReplayOutstandingEntries.java +++ b/astrid/plugin-src/com/todoroo/astrid/actfm/sync/messages/ReplayOutstandingEntries.java @@ -20,22 +20,19 @@ public class ReplayOutstandingEntries modelClass; + protected final Class modelClass; private final Class outstandingClass; private final String table; - private final RemoteModelDao dao; - private final OutstandingEntryDao outstandingDao; - private final ActFmSyncThread actFmSyncThread; + protected final RemoteModelDao dao; + protected final OutstandingEntryDao outstandingDao; private final boolean afterErrors; - public ReplayOutstandingEntries(Class modelClass, String table, RemoteModelDao dao, OutstandingEntryDao outstandingDao, - ActFmSyncThread actFmSyncThread, boolean afterErrors) { + public ReplayOutstandingEntries(Class modelClass, String table, RemoteModelDao dao, OutstandingEntryDao outstandingDao, boolean afterErrors) { this.modelClass = modelClass; this.outstandingClass = DaoReflectionHelpers.getOutstandingClass(modelClass); this.table = table; this.dao = dao; this.outstandingDao = outstandingDao; - this.actFmSyncThread = actFmSyncThread; this.afterErrors = afterErrors; } @@ -58,6 +55,14 @@ public class ReplayOutstandingEntries(id, modelClass, dao, outstandingDao), null); + } + + protected boolean shouldSaveModel(T model) { + return true; + } + private void processItem(long id, OE instance, TodorooCursor outstanding) { try { T model = modelClass.newInstance(); @@ -78,11 +83,12 @@ public class ReplayOutstandingEntries 0 && !afterErrors && actFmSyncThread != null) { - ChangesHappened ch = new ChangesHappened(id, modelClass, dao, outstandingDao); - actFmSyncThread.enqueueMessage(ch, null); + if (count > 0 && !afterErrors) { + enqueueChangesHappenedMessage(id); + } } outstanding.moveToPrevious(); // Move back one to undo the last iteration of the for loop diff --git a/astrid/plugin-src/com/todoroo/astrid/actfm/sync/messages/ReplayTaskListMetadataOutstanding.java b/astrid/plugin-src/com/todoroo/astrid/actfm/sync/messages/ReplayTaskListMetadataOutstanding.java new file mode 100644 index 000000000..7f3f96b70 --- /dev/null +++ b/astrid/plugin-src/com/todoroo/astrid/actfm/sync/messages/ReplayTaskListMetadataOutstanding.java @@ -0,0 +1,27 @@ +package com.todoroo.astrid.actfm.sync.messages; + +import com.todoroo.astrid.dao.TaskListMetadataDao; +import com.todoroo.astrid.dao.TaskListMetadataOutstandingDao; +import com.todoroo.astrid.data.TaskListMetadata; +import com.todoroo.astrid.data.TaskListMetadataOutstanding; + +public class ReplayTaskListMetadataOutstanding extends ReplayOutstandingEntries { + + public ReplayTaskListMetadataOutstanding(TaskListMetadataDao dao, TaskListMetadataOutstandingDao outstandingDao, boolean afterErrors) { + super(TaskListMetadata.class, NameMaps.TABLE_ID_TASK_LIST_METADATA, dao, outstandingDao, afterErrors); + } + + @Override + protected boolean shouldSaveModel(TaskListMetadata model) { + if (model.containsNonNullValue(TaskListMetadata.TASK_IDS) && + TaskListMetadata.taskIdsIsEmpty(model.getValue(TaskListMetadata.TASK_IDS))) + return false; + return true; + } + + @Override + protected void enqueueChangesHappenedMessage(long id) { + // Do nothing + } + +} diff --git a/astrid/plugin-src/com/todoroo/astrid/actfm/sync/messages/TaskListMetadataChangesHappened.java b/astrid/plugin-src/com/todoroo/astrid/actfm/sync/messages/TaskListMetadataChangesHappened.java index bc512f24d..cfe419484 100644 --- a/astrid/plugin-src/com/todoroo/astrid/actfm/sync/messages/TaskListMetadataChangesHappened.java +++ b/astrid/plugin-src/com/todoroo/astrid/actfm/sync/messages/TaskListMetadataChangesHappened.java @@ -5,13 +5,17 @@ import java.util.Set; import com.todoroo.astrid.dao.TaskListMetadataDao; import com.todoroo.astrid.dao.TaskListMetadataOutstandingDao; +import com.todoroo.astrid.data.RemoteModel; import com.todoroo.astrid.data.TaskListMetadata; import com.todoroo.astrid.data.TaskListMetadataOutstanding; public class TaskListMetadataChangesHappened extends ChangesHappened { + private final Throwable throwable; + public TaskListMetadataChangesHappened(long id, Class modelClass, TaskListMetadataDao modelDao, TaskListMetadataOutstandingDao outstandingDao) { super(id, modelClass, modelDao, outstandingDao); + throwable = new Throwable(); } @Override @@ -23,13 +27,19 @@ public class TaskListMetadataChangesHappened extends ChangesHappened= 0; i--) { TaskListMetadataOutstanding oe = changes.get(i); - if (TaskListMetadata.TASK_IDS.name.equals(oe.getValue(TaskListMetadataOutstanding.COLUMN_STRING))) { - if (foundOrderChange) { + String column = oe.getValue(TaskListMetadataOutstanding.COLUMN_STRING); + if (TaskListMetadata.TASK_IDS.name.equals(column)) { + if (foundOrderChange || TaskListMetadata.taskIdsIsEmpty(oe.getValue(TaskListMetadataOutstanding.VALUE_STRING))) { changes.remove(i); removedChanges.add(oe.getId()); } else { foundOrderChange = true; } + } else if (TaskListMetadata.FILTER.name.equals(column) || TaskListMetadata.TAG_UUID.name.equals(column)) { + if (RemoteModel.isUuidEmpty(oe.getValue(TaskListMetadataOutstanding.VALUE_STRING))) { + changes.remove(i); + removedChanges.add(oe.getId()); + } } } diff --git a/astrid/plugin-src/com/todoroo/astrid/core/PluginServices.java b/astrid/plugin-src/com/todoroo/astrid/core/PluginServices.java index 2a88a09f1..075786c6f 100644 --- a/astrid/plugin-src/com/todoroo/astrid/core/PluginServices.java +++ b/astrid/plugin-src/com/todoroo/astrid/core/PluginServices.java @@ -10,6 +10,7 @@ import com.todoroo.andlib.service.Autowired; import com.todoroo.andlib.service.DependencyInjectionService; import com.todoroo.andlib.service.ExceptionService; import com.todoroo.andlib.sql.Query; +import com.todoroo.astrid.actfm.sync.ActFmPreferenceService; import com.todoroo.astrid.dao.Database; import com.todoroo.astrid.dao.HistoryDao; import com.todoroo.astrid.dao.MetadataDao.MetadataCriteria; @@ -101,6 +102,9 @@ public final class PluginServices { @Autowired TaskListMetadataOutstandingDao taskListMetadataOutstandingDao; + @Autowired + ActFmPreferenceService actFmPreferenceService; + private static volatile PluginServices instance; static { @@ -204,6 +208,10 @@ public final class PluginServices { return getInstance().taskListMetadataOutstandingDao; } + public static ActFmPreferenceService getActFmPreferenceService() { + return getInstance().actFmPreferenceService; + } + // -- helpers /** diff --git a/astrid/plugin-src/com/todoroo/astrid/gcal/CalendarReminderActivity.java b/astrid/plugin-src/com/todoroo/astrid/gcal/CalendarReminderActivity.java index d7e6493e1..a3c23391e 100644 --- a/astrid/plugin-src/com/todoroo/astrid/gcal/CalendarReminderActivity.java +++ b/astrid/plugin-src/com/todoroo/astrid/gcal/CalendarReminderActivity.java @@ -178,7 +178,7 @@ public class CalendarReminderActivity extends Activity { @Override public void onClick(View v) { String listName = getString(R.string.CRA_default_list_name, eventName); - TagData existing = tagDataService.getTag(listName, TagData.PROPERTIES); + TagData existing = tagDataService.getTagByName(listName, TagData.PROPERTIES); if (existing != null) { listExists(existing); } else { diff --git a/astrid/plugin-src/com/todoroo/astrid/notes/EditNoteActivity.java b/astrid/plugin-src/com/todoroo/astrid/notes/EditNoteActivity.java index 30550fb10..f0aa259ff 100644 --- a/astrid/plugin-src/com/todoroo/astrid/notes/EditNoteActivity.java +++ b/astrid/plugin-src/com/todoroo/astrid/notes/EditNoteActivity.java @@ -300,6 +300,7 @@ public class EditNoteActivity extends LinearLayout implements TimerActionListene String type = updates.getString(UpdateAdapter.TYPE_PROPERTY_INDEX); NoteOrUpdate noa; + UpdateAdapter.readUserProperties(updates, user); if (NameMaps.TABLE_ID_USER_ACTIVITY.equals(type)) { UpdateAdapter.readUserActivityProperties(updates, update); noa = NoteOrUpdate.fromUpdateOrHistory(activity, update, null, user, linkColor); @@ -307,7 +308,6 @@ public class EditNoteActivity extends LinearLayout implements TimerActionListene UpdateAdapter.readHistoryProperties(updates, history); noa = NoteOrUpdate.fromUpdateOrHistory(activity, null, history, user, linkColor); } - UpdateAdapter.readUserProperties(updates, user); if(noa != null) items.add(noa); } diff --git a/astrid/plugin-src/com/todoroo/astrid/people/PeopleFilterMode.java b/astrid/plugin-src/com/todoroo/astrid/people/PeopleFilterMode.java index aebe6df10..bd16cc85e 100644 --- a/astrid/plugin-src/com/todoroo/astrid/people/PeopleFilterMode.java +++ b/astrid/plugin-src/com/todoroo/astrid/people/PeopleFilterMode.java @@ -12,8 +12,6 @@ import com.todoroo.astrid.ui.MainMenuPopover; public class PeopleFilterMode implements FilterModeSpec { -// private AsyncImageView imageView; - @Override public Filter getDefaultFilter(Context context) { Filter defaultFilter = PeopleFilterExposer.mySharedTasks(context); @@ -26,18 +24,7 @@ public class PeopleFilterMode implements FilterModeSpec { } @Override - public void onFilterItemClickedCallback(FilterListItem item) { -// if (imageView == null) -// return; -// if (item instanceof FilterWithUpdate) -// imageView.setUrl(((FilterWithUpdate) item).imageUrl); -// else -// imageView.setUrl(null); - } -// -// public void setImageView(AsyncImageView imageView) { -// this.imageView = imageView; -// } + public void onFilterItemClickedCallback(FilterListItem item) {/**/} @Override public int[] getForbiddenMenuItems() { diff --git a/astrid/plugin-src/com/todoroo/astrid/subtasks/AstridOrderedListUpdater.java b/astrid/plugin-src/com/todoroo/astrid/subtasks/AstridOrderedListUpdater.java index 9c713340c..655e911dd 100644 --- a/astrid/plugin-src/com/todoroo/astrid/subtasks/AstridOrderedListUpdater.java +++ b/astrid/plugin-src/com/todoroo/astrid/subtasks/AstridOrderedListUpdater.java @@ -83,7 +83,9 @@ public abstract class AstridOrderedListUpdater { currentIds.add(id); } Set idsInQuery = new HashSet(); - TodorooCursor tasks = taskService.fetchFiltered(filter.getSqlQuery(), null, Task.UUID); + String sql = filter.getSqlQuery().replaceAll("ORDER BY .*", ""); //$NON-NLS-1$//$NON-NLS-2$ + sql = sql + String.format(" ORDER BY %s", Task.CREATION_DATE); //$NON-NLS-1$ + TodorooCursor tasks = taskService.fetchFiltered(sql, null, Task.UUID); try { for (tasks.moveToFirst(); !tasks.isAfterLast(); tasks.moveToNext()) { String id = tasks.getString(0); @@ -93,7 +95,7 @@ public abstract class AstridOrderedListUpdater { changedThings = true; Node newNode = new Node(id, treeRoot, 0); - treeRoot.children.add(newNode); + treeRoot.children.add(0, newNode); idToNode.put(id, newNode); } @@ -331,7 +333,7 @@ public abstract class AstridOrderedListUpdater { return; Node newNode = new Node(uuid, treeRoot, 0); - treeRoot.children.add(newNode); + treeRoot.children.add(0, newNode); idToNode.put(uuid, newNode); writeSerialization(list, serializeTree(), true); applyToFilter(filter); diff --git a/astrid/plugin-src/com/todoroo/astrid/subtasks/SubtasksHelper.java b/astrid/plugin-src/com/todoroo/astrid/subtasks/SubtasksHelper.java index 15c0c4661..681d18170 100644 --- a/astrid/plugin-src/com/todoroo/astrid/subtasks/SubtasksHelper.java +++ b/astrid/plugin-src/com/todoroo/astrid/subtasks/SubtasksHelper.java @@ -12,7 +12,6 @@ import com.todoroo.andlib.data.TodorooCursor; import com.todoroo.andlib.service.ContextManager; import com.todoroo.andlib.sql.Criterion; import com.todoroo.andlib.sql.Query; -import com.todoroo.andlib.utility.Preferences; import com.todoroo.astrid.actfm.TagViewFragment; import com.todoroo.astrid.api.Filter; import com.todoroo.astrid.api.FilterWithCustomIntent; @@ -21,9 +20,11 @@ import com.todoroo.astrid.core.CustomFilterExposer; import com.todoroo.astrid.core.PluginServices; import com.todoroo.astrid.core.SortHelper; import com.todoroo.astrid.dao.TaskDao.TaskCriteria; +import com.todoroo.astrid.dao.TaskListMetadataDao; import com.todoroo.astrid.data.RemoteModel; import com.todoroo.astrid.data.TagData; import com.todoroo.astrid.data.Task; +import com.todoroo.astrid.data.TaskListMetadata; import com.todoroo.astrid.subtasks.AstridOrderedListUpdater.Node; import com.todoroo.astrid.utility.AstridPreferences; @@ -55,25 +56,25 @@ public class SubtasksHelper { return false; } - @SuppressWarnings("nls") - public static String serverFilterOrderId(String localFilterOrderId) { - if (SubtasksUpdater.ACTIVE_TASKS_ORDER.equals(localFilterOrderId)) - return "all"; - else if (SubtasksUpdater.TODAY_TASKS_ORDER.equals(localFilterOrderId)) - return "today"; - return null; - } - @SuppressWarnings("nls") public static String applySubtasksToWidgetFilter(Filter filter, String query, String tagName, int limit) { if (SubtasksHelper.shouldUseSubtasksFragmentForFilter(filter)) { // care for manual ordering - TagData tagData = PluginServices.getTagDataService().getTag(tagName, TagData.TAG_ORDERING); + TagData tagData = PluginServices.getTagDataService().getTagByName(tagName, TagData.UUID, TagData.TAG_ORDERING); + TaskListMetadataDao tlmd = PluginServices.getTaskListMetadataDao(); + TaskListMetadata tlm = null; + if (tagData != null) { + tlm = tlmd.fetchByTagId(tagData.getUuid(), TaskListMetadata.TASK_IDS); + } else if (CoreFilterExposer.isInbox(filter)) { + tlm = tlmd.fetchByTagId(TaskListMetadata.FILTER_ID_ALL, TaskListMetadata.TASK_IDS); + } else if (CustomFilterExposer.isTodayFilter(filter)) { + tlm = tlmd.fetchByTagId(TaskListMetadata.FILTER_ID_TODAY, TaskListMetadata.TASK_IDS); + } query = query.replaceAll("ORDER BY .*", ""); query = query + String.format(" ORDER BY %s, %s, %s, %s", Task.DELETION_DATE, Task.COMPLETION_DATE, - getOrderString(tagData), Task.CREATION_DATE); + getOrderString(tagData, tlm), Task.CREATION_DATE); if (limit > 0) query = query + " LIMIT " + limit; query = query.replace(TaskCriteria.isVisible().toString(), @@ -84,12 +85,14 @@ public class SubtasksHelper { return query; } - private static String getOrderString(TagData tagData) { + private static String getOrderString(TagData tagData, TaskListMetadata tlm) { String serialized; - if (tagData != null) - serialized = tagData.getValue(TagData.TAG_ORDERING); + if (tlm != null) + serialized = tlm.getValue(TaskListMetadata.TASK_IDS); + else if (tagData != null) + serialized = convertTreeToRemoteIds(tagData.getValue(TagData.TAG_ORDERING)); else - serialized = Preferences.getStringValue(SubtasksUpdater.ACTIVE_TASKS_ORDER); + serialized = "[]"; //$NON-NLS-1$ return AstridOrderedListUpdater.buildOrderString(getStringIdArray(serialized)); } @@ -113,7 +116,7 @@ public class SubtasksHelper { @SuppressWarnings("nls") public static String[] getStringIdArray(String serializedTree) { ArrayList ids = new ArrayList(); - String[] values = serializedTree.split("[\\[\\],\\s]"); // Split on [ ] , or whitespace chars + String[] values = serializedTree.split("[\\[\\],\"\\s]"); // Split on [ ] , or whitespace chars for (String idString : values) { if (!TextUtils.isEmpty(idString)) ids.add(idString); diff --git a/astrid/plugin-src/com/todoroo/astrid/subtasks/SubtasksTagListFragment.java b/astrid/plugin-src/com/todoroo/astrid/subtasks/SubtasksTagListFragment.java index 0a10b03f3..91f68910b 100644 --- a/astrid/plugin-src/com/todoroo/astrid/subtasks/SubtasksTagListFragment.java +++ b/astrid/plugin-src/com/todoroo/astrid/subtasks/SubtasksTagListFragment.java @@ -53,6 +53,7 @@ public class SubtasksTagListFragment extends TagViewFragment { helper.beforeSetUpTaskList(filter); super.setUpTaskList(); + setUpMembersGallery(); unregisterForContextMenu(getListView()); } diff --git a/astrid/plugin-src/com/todoroo/astrid/tags/TagCaseMigrator.java b/astrid/plugin-src/com/todoroo/astrid/tags/TagCaseMigrator.java index b31369863..7e852cd8c 100644 --- a/astrid/plugin-src/com/todoroo/astrid/tags/TagCaseMigrator.java +++ b/astrid/plugin-src/com/todoroo/astrid/tags/TagCaseMigrator.java @@ -13,6 +13,7 @@ import com.todoroo.andlib.data.TodorooCursor; import com.todoroo.andlib.service.Autowired; import com.todoroo.andlib.service.DependencyInjectionService; import com.todoroo.andlib.sql.Criterion; +import com.todoroo.andlib.sql.Field; import com.todoroo.andlib.sql.Join; import com.todoroo.andlib.sql.Query; import com.todoroo.andlib.utility.Preferences; @@ -23,6 +24,7 @@ import com.todoroo.astrid.data.Task; import com.todoroo.astrid.service.MetadataService; import com.todoroo.astrid.service.TagDataService; import com.todoroo.astrid.service.TaskService; +import com.todoroo.astrid.utility.Flags; public class TagCaseMigrator { @@ -59,7 +61,7 @@ public class TagCaseMigrator { } for (String key : renameMap.keySet()) { - TagService.getInstance().renameCaseSensitive(key, renameMap.get(key)); + renameCaseSensitive(key, renameMap.get(key)); updateTagData(key); } @@ -138,6 +140,40 @@ public class TagCaseMigrator { } finally { tasks.close(); } + } + + @Deprecated + private int renameCaseSensitive(String oldTag, String newTag) { // Need this for tag case migration process + return renameHelper(oldTag, newTag, true); + } + + @Deprecated + private int renameHelper(String oldTag, String newTag, boolean caseSensitive) { + // First remove newTag from all tasks that have both oldTag and newTag. + metadataService.deleteWhere( + Criterion.and( + Metadata.VALUE1.eq(newTag), + Metadata.TASK.in(rowsWithTag(oldTag, Metadata.TASK)))); + + // Then rename all instances of oldTag to newTag. + Metadata metadata = new Metadata(); + metadata.setValue(TaskToTagMetadata.TAG_NAME, newTag); + int ret; + if (caseSensitive) + ret = metadataService.update(tagEq(oldTag, Criterion.all), metadata); + else + ret = metadataService.update(TagService.tagEqIgnoreCase(oldTag, Criterion.all), metadata); + invalidateTaskCache(newTag); + return ret; + } + + + private Query rowsWithTag(String tag, Field... projections) { + return Query.select(projections).from(Metadata.TABLE).where(Metadata.VALUE1.eq(tag)); + } + private void invalidateTaskCache(String tag) { + taskService.clearDetails(Task.ID.in(rowsWithTag(tag, Task.ID))); + Flags.set(Flags.REFRESH); } } diff --git a/astrid/plugin-src/com/todoroo/astrid/tags/TagFilterExposer.java b/astrid/plugin-src/com/todoroo/astrid/tags/TagFilterExposer.java index 05508e551..e79639118 100644 --- a/astrid/plugin-src/com/todoroo/astrid/tags/TagFilterExposer.java +++ b/astrid/plugin-src/com/todoroo/astrid/tags/TagFilterExposer.java @@ -38,6 +38,8 @@ import com.todoroo.astrid.api.FilterCategory; import com.todoroo.astrid.api.FilterListItem; import com.todoroo.astrid.api.FilterWithCustomIntent; import com.todoroo.astrid.api.FilterWithUpdate; +import com.todoroo.astrid.dao.TagDataDao; +import com.todoroo.astrid.dao.TagMetadataDao; import com.todoroo.astrid.dao.TaskDao.TaskCriteria; import com.todoroo.astrid.data.Metadata; import com.todoroo.astrid.data.RemoteModel; @@ -74,7 +76,7 @@ public class TagFilterExposer extends BroadcastReceiver implements AstridFilterE ContentValues contentValues = new ContentValues(); contentValues.put(Metadata.KEY.name, TaskToTagMetadata.KEY); contentValues.put(TaskToTagMetadata.TAG_NAME.name, tag.tag); - contentValues.put(TaskToTagMetadata.TAG_UUID.name, tag.uuid.toString()); + contentValues.put(TaskToTagMetadata.TAG_UUID.name, tag.uuid); FilterWithUpdate filter = new FilterWithUpdate(tag.tag, title, tagTemplate, @@ -94,8 +96,8 @@ public class TagFilterExposer extends BroadcastReceiver implements AstridFilterE context.getString(deleteIntentLabel) }; filter.contextMenuIntents = new Intent[] { - newTagIntent(context, RenameTagActivity.class, tag, tagTemplate.toString()), - newTagIntent(context, DeleteTagActivity.class, tag, tagTemplate.toString()) + newTagIntent(context, RenameTagActivity.class, tag, tag.uuid), + newTagIntent(context, DeleteTagActivity.class, tag, tag.uuid) }; filter.customTaskList = new ComponentName(ContextManager.getContext(), TagViewFragment.class); @@ -115,10 +117,10 @@ public class TagFilterExposer extends BroadcastReceiver implements AstridFilterE return filterFromTag(context, tag, TaskCriteria.activeAndVisible()); } - private static Intent newTagIntent(Context context, Class activity, Tag tag, String sql) { + private static Intent newTagIntent(Context context, Class activity, Tag tag, String uuid) { Intent ret = new Intent(context, activity); ret.putExtra(TAG, tag.tag); - ret.putExtra(TagService.TOKEN_TAG_SQL, sql); + ret.putExtra(TagViewFragment.EXTRA_TAG_UUID, uuid); return ret; } @@ -197,10 +199,11 @@ public class TagFilterExposer extends BroadcastReceiver implements AstridFilterE public abstract static class TagActivity extends Activity { protected String tag; - protected String sql; + protected String uuid; @Autowired public TagService tagService; - @Autowired public TagDataService tagDataService; + @Autowired public TagDataDao tagDataDao; + @Autowired public TagMetadataDao tagMetadataDao; static { AstridDependencyInjector.initialize(); @@ -211,19 +214,16 @@ public class TagFilterExposer extends BroadcastReceiver implements AstridFilterE super.onCreate(savedInstanceState); tag = getIntent().getStringExtra(TAG); - sql = getIntent().getStringExtra(TagService.TOKEN_TAG_SQL); - if(tag == null) { + uuid = getIntent().getStringExtra(TagViewFragment.EXTRA_TAG_UUID); + + if(tag == null || RemoteModel.isUuidEmpty(uuid)) { finish(); return; } DependencyInjectionService.getInstance().inject(this); - TagData tagData = tagDataService.getTag(tag, TagData.MEMBER_COUNT, TagData.USER_ID); - if(tagData != null && tagData.getValue(TagData.MEMBER_COUNT) > 0 && Task.USER_ID_SELF.equals(tagData.getValue(TagData.USER_ID))) { - DialogUtilities.okCancelDialog(this, getString(R.string.actfm_tag_operation_owner_delete), getOkListener(), getCancelListener()); - return; - } + TagData tagData = tagDataDao.fetch(uuid, TagData.MEMBER_COUNT, TagData.USER_ID); showDialog(tagData); } @@ -275,8 +275,12 @@ public class TagFilterExposer extends BroadcastReceiver implements AstridFilterE @Override protected void showDialog(TagData tagData) { int string; - if (tagData != null && tagData.getValue(TagData.MEMBER_COUNT) > 0) - string = R.string.DLG_leave_this_shared_tag_question; + if (tagData != null && (tagMetadataDao.tagHasMembers(uuid) || tagData.getValue(TagData.MEMBER_COUNT) > 0)) { + if (Task.USER_ID_SELF.equals(tagData.getValue(TagData.USER_ID))) + string = R.string.actfm_tag_operation_owner_delete; + else + string = R.string.DLG_leave_this_shared_tag_question; + } else string = R.string.DLG_delete_this_tag_question; DialogUtilities.okCancelDialog(this, getString(string, tag), getOkListener(), getCancelListener()); @@ -284,7 +288,7 @@ public class TagFilterExposer extends BroadcastReceiver implements AstridFilterE @Override protected boolean ok() { - return tagService.deleteOrLeaveTag(this, tag, sql); + return tagService.deleteOrLeaveTag(this, tag, uuid); } } @@ -308,12 +312,7 @@ public class TagFilterExposer extends BroadcastReceiver implements AstridFilterE if (text == null || text.length() == 0) { return false; } else { - int renamed = tagService.rename(tag, text); - TagData tagData = tagDataService.getTag(tag, TagData.ID, TagData.NAME); - if (tagData != null) { - tagData.setValue(TagData.NAME, text); - tagDataService.save(tagData); - } + int renamed = tagService.rename(uuid, text); Toast.makeText(this, getString(R.string.TEA_tags_renamed, tag, text, renamed), Toast.LENGTH_SHORT).show(); return true; diff --git a/astrid/plugin-src/com/todoroo/astrid/tags/TagService.java b/astrid/plugin-src/com/todoroo/astrid/tags/TagService.java index 94014adae..a12397f0d 100644 --- a/astrid/plugin-src/com/todoroo/astrid/tags/TagService.java +++ b/astrid/plugin-src/com/todoroo/astrid/tags/TagService.java @@ -22,6 +22,7 @@ import com.todoroo.andlib.data.Property; import com.todoroo.andlib.data.Property.CountProperty; import com.todoroo.andlib.data.TodorooCursor; import com.todoroo.andlib.service.Autowired; +import com.todoroo.andlib.service.ContextManager; import com.todoroo.andlib.service.DependencyInjectionService; import com.todoroo.andlib.sql.Criterion; import com.todoroo.andlib.sql.Field; @@ -36,6 +37,7 @@ import com.todoroo.astrid.api.AstridApiConstants; import com.todoroo.astrid.core.PluginServices; import com.todoroo.astrid.dao.MetadataDao; import com.todoroo.astrid.dao.MetadataDao.MetadataCriteria; +import com.todoroo.astrid.dao.TagDataDao; import com.todoroo.astrid.dao.TaskDao.TaskCriteria; import com.todoroo.astrid.data.Metadata; import com.todoroo.astrid.data.RemoteModel; @@ -45,7 +47,6 @@ import com.todoroo.astrid.data.Task; import com.todoroo.astrid.service.MetadataService; import com.todoroo.astrid.service.TagDataService; import com.todoroo.astrid.service.TaskService; -import com.todoroo.astrid.utility.Flags; /** * Provides operations for working with tags @@ -84,6 +85,8 @@ public final class TagService { @Autowired TagDataService tagDataService; + @Autowired TagDataDao tagDataDao; + public TagService() { DependencyInjectionService.getInstance().inject(this); } @@ -187,7 +190,6 @@ public final class TagService { * @param activeStatus criterion for specifying completed or uncompleted * @return empty array if no tags, otherwise array */ - @Deprecated public Tag[] getGroupedTags(Order order, Criterion activeStatus) { Criterion criterion = Criterion.and(activeStatus, MetadataCriteria.withKey(TaskToTagMetadata.KEY)); Query query = Query.select(TaskToTagMetadata.TAG_NAME, TaskToTagMetadata.TAG_UUID, COUNT). @@ -372,21 +374,20 @@ public final class TagService { return tagBuilder.toString(); } - public boolean deleteOrLeaveTag(Context context, String tag, String sql) { - int deleted = deleteTagMetadata(tag); - TagData tagData = PluginServices.getTagDataService().getTag(tag, TagData.ID, TagData.DELETION_DATE, TagData.MEMBER_COUNT, TagData.USER_ID); + public boolean deleteOrLeaveTag(Context context, String tag, String uuid) { + int deleted = deleteTagMetadata(uuid); + TagData tagData = tagDataDao.fetch(uuid, TagData.ID, TagData.UUID, TagData.DELETION_DATE, TagData.MEMBER_COUNT, TagData.USER_ID); boolean shared = false; + Intent tagDeleted = new Intent(AstridApiConstants.BROADCAST_EVENT_TAG_DELETED); if(tagData != null) { tagData.setValue(TagData.DELETION_DATE, DateUtilities.now()); PluginServices.getTagDataService().save(tagData); + tagDeleted.putExtra(TagViewFragment.EXTRA_TAG_UUID, tagData.getUuid()); shared = tagData.getValue(TagData.MEMBER_COUNT) > 0 && !Task.USER_ID_SELF.equals(tagData.getValue(TagData.USER_ID)); // Was I a list member and NOT owner? } Toast.makeText(context, context.getString(shared ? R.string.TEA_tags_left : R.string.TEA_tags_deleted, tag, deleted), Toast.LENGTH_SHORT).show(); - Intent tagDeleted = new Intent(AstridApiConstants.BROADCAST_EVENT_TAG_DELETED); - tagDeleted.putExtra(TagViewFragment.EXTRA_TAG_NAME, tag); - tagDeleted.putExtra(TOKEN_TAG_SQL, sql); context.sendBroadcast(tagDeleted); return true; } @@ -531,51 +532,38 @@ public final class TagService { return null; } - private int deleteTagMetadata(String tag) { - invalidateTaskCache(tag); + private int deleteTagMetadata(String uuid) { Metadata deleted = new Metadata(); deleted.setValue(Metadata.DELETION_DATE, DateUtilities.now()); - return metadataDao.update(tagEqIgnoreCase(tag, Criterion.all), deleted); + return metadataDao.update(Criterion.and(MetadataCriteria.withKey(TaskToTagMetadata.KEY), TaskToTagMetadata.TAG_UUID.eq(uuid)), deleted); } - public int rename(String oldTag, String newTag) { - return renameHelper(oldTag, newTag, false); + public int rename(String uuid, String newName) { + return rename(uuid, newName, false); } - public int renameCaseSensitive(String oldTag, String newTag) { // Need this for tag case migration process - return renameHelper(oldTag, newTag, true); - } + public int rename(String uuid, String newName, boolean suppressSync) { + TagData template = new TagData(); + template.setValue(TagData.NAME, newName); + if (suppressSync) + template.putTransitory(SyncFlags.ACTFM_SUPPRESS_OUTSTANDING_ENTRIES, true); + int result = tagDataDao.update(TagData.UUID.eq(uuid), template); - @Deprecated - private int renameHelper(String oldTag, String newTag, boolean caseSensitive) { - // First remove newTag from all tasks that have both oldTag and newTag. - MetadataService metadataService = PluginServices.getMetadataService(); - metadataService.deleteWhere( - Criterion.and( - Metadata.VALUE1.eq(newTag), - Metadata.TASK.in(rowsWithTag(oldTag, Metadata.TASK)))); - - // Then rename all instances of oldTag to newTag. - Metadata metadata = new Metadata(); - metadata.setValue(TaskToTagMetadata.TAG_NAME, newTag); - int ret; - if (caseSensitive) - ret = metadataService.update(tagEq(oldTag, Criterion.all), metadata); - else - ret = metadataService.update(tagEqIgnoreCase(oldTag, Criterion.all), metadata); - invalidateTaskCache(newTag); - return ret; - } + boolean tagRenamed = result > 0; + Metadata metadataTemplate = new Metadata(); + metadataTemplate.setValue(TaskToTagMetadata.TAG_NAME, newName); + result = metadataDao.update(Criterion.and(MetadataCriteria.withKey(TaskToTagMetadata.KEY), TaskToTagMetadata.TAG_UUID.eq(uuid)), metadataTemplate); + tagRenamed = tagRenamed || result > 0; - private Query rowsWithTag(String tag, Field... projections) { - return Query.select(projections).from(Metadata.TABLE).where(Metadata.VALUE1.eq(tag)); - } + if (tagRenamed) { + Intent intent = new Intent(AstridApiConstants.BROADCAST_EVENT_TAG_RENAMED); + intent.putExtra(TagViewFragment.EXTRA_TAG_UUID, uuid); + ContextManager.getContext().sendBroadcast(intent); + } - private void invalidateTaskCache(String tag) { - taskService.clearDetails(Task.ID.in(rowsWithTag(tag, Task.ID))); - Flags.set(Flags.REFRESH); + return result; } public static int getDefaultImageIDForTag(String nameOrUUID) { diff --git a/astrid/plugin-src/com/todoroo/astrid/tags/reusable/FeaturedTaskListFragment.java b/astrid/plugin-src/com/todoroo/astrid/tags/reusable/FeaturedTaskListFragment.java index c35340cea..bcfd28eef 100644 --- a/astrid/plugin-src/com/todoroo/astrid/tags/reusable/FeaturedTaskListFragment.java +++ b/astrid/plugin-src/com/todoroo/astrid/tags/reusable/FeaturedTaskListFragment.java @@ -140,6 +140,7 @@ public class FeaturedTaskListFragment extends TagViewFragment { } else { clone = new TagData(); clone.setValue(TagData.NAME, localName); + tagDataService.save(clone); } } finally { diff --git a/astrid/res/layout/no_members_text_view.xml b/astrid/res/layout/no_members_text_view.xml new file mode 100644 index 000000000..35571a8b0 --- /dev/null +++ b/astrid/res/layout/no_members_text_view.xml @@ -0,0 +1,11 @@ + + \ No newline at end of file diff --git a/astrid/res/layout/tag_settings_activity.xml b/astrid/res/layout/tag_settings_activity.xml index a2c670116..74ec18416 100644 --- a/astrid/res/layout/tag_settings_activity.xml +++ b/astrid/res/layout/tag_settings_activity.xml @@ -74,7 +74,6 @@ android:layout_alignParentTop="true" android:layout_marginLeft="10dip" android:layout_toRightOf="@id/picture" - android:capitalize="sentences" android:text="@string/actfm_TVA_tag_label" /> diff --git a/astrid/res/layout/task_list_body_tag.xml b/astrid/res/layout/task_list_body_tag.xml index 039a2e86d..88b8876d4 100644 --- a/astrid/res/layout/task_list_body_tag.xml +++ b/astrid/res/layout/task_list_body_tag.xml @@ -31,16 +31,8 @@ android:id="@+id/shared_with" android:layout_width="wrap_content" android:paddingLeft="6dip" - android:layout_height="fill_parent"> - + android:layout_height="fill_parent" + android:gravity="center_vertical"> unionCursor, History history) { @@ -232,6 +235,7 @@ public class UpdateAdapter extends CursorAdapter { history.setValue(History.OLD_VALUE, unionCursor.getString(4)); history.setValue(History.NEW_VALUE, unionCursor.getString(5)); history.setValue(History.TASK, unionCursor.getString(6)); + history.setValue(History.USER_UUID, unionCursor.getString(7)); } public static void readUserProperties(TodorooCursor joinCursor, User user) { diff --git a/astrid/src/com/todoroo/astrid/dao/MetadataDao.java b/astrid/src/com/todoroo/astrid/dao/MetadataDao.java index b2e720024..08a5aae96 100644 --- a/astrid/src/com/todoroo/astrid/dao/MetadataDao.java +++ b/astrid/src/com/todoroo/astrid/dao/MetadataDao.java @@ -86,7 +86,8 @@ public class MetadataDao extends DatabaseDao { ((cv.containsKey(Metadata.KEY.name) && TaskToTagMetadata.KEY.equals(item.getValue(Metadata.KEY))) || (cv.containsKey(Metadata.DELETION_DATE.name) && - item.getValue(Metadata.DELETION_DATE) > 0)); + item.getValue(Metadata.DELETION_DATE) > 0)) && + RemoteModelDao.getOutstandingEntryFlag(); } @Override diff --git a/astrid/src/com/todoroo/astrid/dao/RemoteModelDao.java b/astrid/src/com/todoroo/astrid/dao/RemoteModelDao.java index c014a9ddc..f197999cc 100644 --- a/astrid/src/com/todoroo/astrid/dao/RemoteModelDao.java +++ b/astrid/src/com/todoroo/astrid/dao/RemoteModelDao.java @@ -5,6 +5,7 @@ import com.todoroo.andlib.data.DatabaseDao; import com.todoroo.andlib.data.Property; import com.todoroo.andlib.data.TodorooCursor; import com.todoroo.andlib.sql.Query; +import com.todoroo.astrid.core.PluginServices; import com.todoroo.astrid.data.RemoteModel; import com.todoroo.astrid.helper.UUIDHelper; @@ -29,7 +30,26 @@ public class RemoteModelDao extends DatabaseDao 0; + } + + @Override + protected boolean shouldRecordOutstanding(RTYPE item) { + return super.shouldRecordOutstanding(item) && getOutstandingEntryFlag(); + } /** * Fetch a model object by UUID diff --git a/astrid/src/com/todoroo/astrid/dao/TagMetadataDao.java b/astrid/src/com/todoroo/astrid/dao/TagMetadataDao.java index 3d82297ec..d8503fac5 100644 --- a/astrid/src/com/todoroo/astrid/dao/TagMetadataDao.java +++ b/astrid/src/com/todoroo/astrid/dao/TagMetadataDao.java @@ -77,7 +77,8 @@ public class TagMetadataDao extends DatabaseDao { ((cv.containsKey(TagMetadata.KEY.name) && TagMemberMetadata.KEY.equals(item.getValue(TagMetadata.KEY))) || (cv.containsKey(TagMetadata.DELETION_DATE.name) && - item.getValue(TagMetadata.DELETION_DATE) > 0)); + item.getValue(TagMetadata.DELETION_DATE) > 0)) && + RemoteModelDao.getOutstandingEntryFlag(); } @Override @@ -188,19 +189,28 @@ public class TagMetadataDao extends DatabaseDao { } } - public boolean memberOfTagData(String email, String id) { + public boolean tagHasMembers(String uuid) { + TodorooCursor metadata = query(Query.select(TagMetadata.ID).where(Criterion.and(TagMetadataCriteria.byTagAndWithKey(uuid, TagMemberMetadata.KEY), TagMetadata.DELETION_DATE.eq(0)))); + try { + return metadata.getCount() > 0; + } finally { + metadata.close(); + } + } + + public boolean memberOfTagData(String email, String tagId, String memberId) { Criterion criterion; - if (!RemoteModel.isUuidEmpty(id) && !TextUtils.isEmpty(email)) - criterion = Criterion.or(TagMemberMetadata.USER_UUID.eq(email), TagMemberMetadata.USER_UUID.eq(id)); - else if (!RemoteModel.isUuidEmpty(id)) - criterion = TagMemberMetadata.USER_UUID.eq(id); + if (!RemoteModel.isUuidEmpty(memberId) && !TextUtils.isEmpty(email)) + criterion = Criterion.or(TagMemberMetadata.USER_UUID.eq(email), TagMemberMetadata.USER_UUID.eq(memberId)); + else if (!RemoteModel.isUuidEmpty(memberId)) + criterion = TagMemberMetadata.USER_UUID.eq(memberId); else if (!TextUtils.isEmpty(email)) criterion = TagMemberMetadata.USER_UUID.eq(email); else return false; TodorooCursor count = query(Query.select(TagMetadata.ID).where( - Criterion.and(TagMetadataCriteria.withKey(TagMemberMetadata.KEY), criterion))); + Criterion.and(TagMetadataCriteria.withKey(TagMemberMetadata.KEY), TagMetadata.TAG_UUID.eq(tagId), criterion))); try { return count.getCount() > 0; } finally { diff --git a/astrid/src/com/todoroo/astrid/provider/SqlContentProvider.java b/astrid/src/com/todoroo/astrid/provider/SqlContentProvider.java index 8cea5d916..1477b7f23 100644 --- a/astrid/src/com/todoroo/astrid/provider/SqlContentProvider.java +++ b/astrid/src/com/todoroo/astrid/provider/SqlContentProvider.java @@ -31,13 +31,18 @@ import com.todoroo.astrid.service.AstridDependencyInjector; @SuppressWarnings("nls") public class SqlContentProvider extends ContentProvider { + // --- instance variables + + private static UriMatcher uriMatcher; + static { AstridDependencyInjector.initialize(); - } - // --- instance variables + uriMatcher = new UriMatcher(UriMatcher.NO_MATCH); - private static UriMatcher uriMatcher; + uriMatcher.addURI(AstridApiConstants.API_PACKAGE + ".private", + "sql", 0); + } @Autowired private Database database; @@ -56,12 +61,6 @@ public class SqlContentProvider extends ContentProvider { } } - static { - uriMatcher = new UriMatcher(UriMatcher.NO_MATCH); - - uriMatcher.addURI(AstridApiConstants.API_PACKAGE + ".private", - "sql", 0); - } public SqlContentProvider() { DependencyInjectionService.getInstance().inject(this); diff --git a/astrid/src/com/todoroo/astrid/service/TagDataService.java b/astrid/src/com/todoroo/astrid/service/TagDataService.java index e4713d2cc..8dab16665 100644 --- a/astrid/src/com/todoroo/astrid/service/TagDataService.java +++ b/astrid/src/com/todoroo/astrid/service/TagDataService.java @@ -89,7 +89,7 @@ public class TagDataService { * Find a tag by name * @return null if doesn't exist */ - public TagData getTag(String name, Property... properties) { + public TagData getTagByName(String name, Property... properties) { TodorooCursor cursor = tagDataDao.query(Query.select(properties).where(TagData.NAME.eqCaseInsensitive(name))); try { if(cursor.getCount() == 0) @@ -197,7 +197,7 @@ public class TagDataService { if (!cursor.isAfterLast()) { tagData.readFromCursor(cursor); if(!tagData.getValue(TagData.NAME).equals(featObject.getString("name"))) - TagService.getInstance().rename(tagData.getValue(TagData.NAME), featObject.getString("name")); + TagService.getInstance().rename(tagData.getUuid(), featObject.getString("name"), true); cursor.moveToNext(); } ActFmSyncService.JsonHelper.featuredListFromJson(featObject, tagData); diff --git a/astrid/src/com/todoroo/astrid/service/TaskService.java b/astrid/src/com/todoroo/astrid/service/TaskService.java index 68afd31f7..a632757f4 100644 --- a/astrid/src/com/todoroo/astrid/service/TaskService.java +++ b/astrid/src/com/todoroo/astrid/service/TaskService.java @@ -229,11 +229,13 @@ public class TaskService { newTask.clearValue(Task.UUID); newTask.clearValue(Task.USER); newTask.clearValue(Task.USER_ID); + newTask.clearValue(Task.IS_READONLY); + newTask.clearValue(Task.IS_PUBLIC); taskDao.save(newTask); - if (!RemoteModel.NO_UUID.equals(tagUuid)) { - TagService.getInstance().createLink(task, tagName, tagUuid); + if (!RemoteModel.isUuidEmpty(tagUuid)) { + TagService.getInstance().createLink(newTask, tagName, tagUuid); } return newTask; } diff --git a/astrid/src/com/todoroo/astrid/widget/TasksWidget.java b/astrid/src/com/todoroo/astrid/widget/TasksWidget.java index f177fec9c..fab7d1489 100644 --- a/astrid/src/com/todoroo/astrid/widget/TasksWidget.java +++ b/astrid/src/com/todoroo/astrid/widget/TasksWidget.java @@ -10,6 +10,7 @@ import android.app.Service; import android.appwidget.AppWidgetManager; import android.appwidget.AppWidgetProvider; import android.content.ComponentName; +import android.content.ContentValues; import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; @@ -42,12 +43,14 @@ import com.todoroo.astrid.api.PermaSql; import com.todoroo.astrid.core.CoreFilterExposer; import com.todoroo.astrid.core.SortHelper; import com.todoroo.astrid.dao.Database; +import com.todoroo.astrid.data.TagData; import com.todoroo.astrid.data.Task; import com.todoroo.astrid.service.AstridDependencyInjector; import com.todoroo.astrid.service.TagDataService; import com.todoroo.astrid.service.TaskService; import com.todoroo.astrid.service.ThemeService; import com.todoroo.astrid.subtasks.SubtasksHelper; +import com.todoroo.astrid.tags.TagFilterExposer; import com.todoroo.astrid.utility.AstridPreferences; import com.todoroo.astrid.utility.Constants; @@ -175,7 +178,7 @@ public class TasksWidget extends AppWidgetProvider { TodorooCursor cursor = null; Filter filter = null; try { - filter = getFilter(widgetId); + filter = getFilter(context, widgetId); if (SubtasksHelper.isTagFilter(filter)) ((FilterWithCustomIntent) filter).customTaskList = new ComponentName(context, TagViewFragment.class); // In case legacy widget was created with subtasks fragment views.setTextViewText(R.id.widget_title, filter.title); @@ -377,7 +380,7 @@ public class TasksWidget extends AppWidgetProvider { return 5; } - private Filter getFilter(int widgetId) { + private Filter getFilter(Context context, int widgetId) { // base our filter off the inbox filter, replace stuff if we have it Filter filter = CoreFilterExposer.buildInboxFilter(getResources()); @@ -403,6 +406,33 @@ public class TasksWidget extends AppWidgetProvider { ((FilterWithCustomIntent) filter).customExtras = extras; } + // Validate tagData + long id = Preferences.getLong(WidgetConfigActivity.PREF_TAG_ID + widgetId, 0); + TagData tagData = null; + if (id > 0) { + tagData = tagDataService.fetchById(id, TagData.ID, TagData.NAME, TagData.TASK_COUNT, TagData.UUID, TagData.PICTURE, TagData.USER_ID, TagData.MEMBER_COUNT); + if (tagData != null && !tagData.getValue(TagData.NAME).equals(filter.title)) { // Tag has been renamed; rebuild filter + filter = TagFilterExposer.filterFromTagData(context, tagData); + Preferences.setString(WidgetConfigActivity.PREF_SQL + widgetId, filter.getSqlQuery()); + Preferences.setString(WidgetConfigActivity.PREF_TITLE + widgetId, filter.title); + ContentValues newTaskValues = filter.valuesForNewTasks; + String contentValuesString = null; + if(newTaskValues != null) + contentValuesString = AndroidUtilities.contentValuesToSerializedString(newTaskValues); + Preferences.setString(WidgetConfigActivity.PREF_VALUES + widgetId, contentValuesString); + if (filter instanceof FilterWithCustomIntent) { + String flattenedExtras = AndroidUtilities.bundleToSerializedString(((FilterWithCustomIntent) filter).customExtras); + if (flattenedExtras != null) + Preferences.setString(WidgetConfigActivity.PREF_CUSTOM_EXTRAS + widgetId, + flattenedExtras); + } + } + } else { + tagData = tagDataService.getTagByName(filter.title, TagData.ID); + if (tagData != null) + Preferences.setLong(WidgetConfigActivity.PREF_TAG_ID + widgetId, tagData.getId()); + } + return filter; } diff --git a/astrid/src/com/todoroo/astrid/widget/WidgetConfigActivity.java b/astrid/src/com/todoroo/astrid/widget/WidgetConfigActivity.java index 884808422..43321fd57 100644 --- a/astrid/src/com/todoroo/astrid/widget/WidgetConfigActivity.java +++ b/astrid/src/com/todoroo/astrid/widget/WidgetConfigActivity.java @@ -34,6 +34,7 @@ abstract public class WidgetConfigActivity extends ListActivity { static final String PREF_VALUES = "widget-values-"; static final String PREF_CUSTOM_INTENT = "widget-intent-"; static final String PREF_CUSTOM_EXTRAS = "widget-extras-"; + static final String PREF_TAG_ID = "widget-tag-id-"; int mAppWidgetId = AppWidgetManager.INVALID_APPWIDGET_ID; diff --git a/tests-sync/src/com/todoroo/astrid/sync/ConstructOutstandingFromMasterTest.java b/tests-sync/src/com/todoroo/astrid/sync/ConstructOutstandingFromMasterTest.java new file mode 100644 index 000000000..fb26377ff --- /dev/null +++ b/tests-sync/src/com/todoroo/astrid/sync/ConstructOutstandingFromMasterTest.java @@ -0,0 +1,38 @@ +package com.todoroo.astrid.sync; + +import com.todoroo.andlib.data.Property; +import com.todoroo.andlib.data.TodorooCursor; +import com.todoroo.andlib.sql.Query; +import com.todoroo.astrid.actfm.sync.messages.ConstructOutstandingTableFromMasterTable; +import com.todoroo.astrid.actfm.sync.messages.NameMaps; +import com.todoroo.astrid.data.Task; +import com.todoroo.astrid.data.TaskOutstanding; + +public class ConstructOutstandingFromMasterTest extends NewSyncTestCase { + + public void testConstructOutstandingConstructsOutstanding() { + Task t = createTask(true); + TodorooCursor to = taskOutstandingDao.query(Query.select(TaskOutstanding.PROPERTIES)); + try { + assertEquals(0, to.getCount()); + } finally { + to.close(); + } + + new ConstructOutstandingTableFromMasterTable(NameMaps.TABLE_ID_TASKS, taskDao, taskOutstandingDao, Task.CREATION_DATE).execute(); + + Property[] syncable = NameMaps.syncableProperties(NameMaps.TABLE_ID_TASKS); + for (Property p : syncable) { + to = taskOutstandingDao.query(Query.select(TaskOutstanding.PROPERTIES).where(TaskOutstanding.COLUMN_STRING.eq(p.name))); + try { + assertEquals(1, to.getCount()); + to.moveToFirst(); + String value = t.getValue(p).toString(); + assertEquals(value, to.get(TaskOutstanding.VALUE_STRING)); + } finally { + to.close(); + } + } + } + +} diff --git a/tests-sync/src/com/todoroo/astrid/sync/NewSyncTestCase.java b/tests-sync/src/com/todoroo/astrid/sync/NewSyncTestCase.java index c12a0f174..78b501f38 100644 --- a/tests-sync/src/com/todoroo/astrid/sync/NewSyncTestCase.java +++ b/tests-sync/src/com/todoroo/astrid/sync/NewSyncTestCase.java @@ -1,6 +1,7 @@ package com.todoroo.astrid.sync; import com.todoroo.andlib.service.Autowired; +import com.todoroo.astrid.dao.RemoteModelDao; import com.todoroo.astrid.dao.TagDataDao; import com.todoroo.astrid.dao.TagOutstandingDao; import com.todoroo.astrid.dao.TaskDao; @@ -22,6 +23,12 @@ public class NewSyncTestCase extends DatabaseTestCase { protected TagOutstandingDao tagOutstandingDao; + @Override + protected void setUp() throws Exception { + super.setUp(); + RemoteModelDao.outstandingEntryFlag = 1; + } + protected Task createTask(String title, boolean suppress) { Task task = new Task(); task.setValue(Task.TITLE, title); diff --git a/tests-sync/src/com/todoroo/astrid/sync/SyncMessageTest.java b/tests-sync/src/com/todoroo/astrid/sync/SyncMessageTest.java index 33221b2f0..583effdab 100644 --- a/tests-sync/src/com/todoroo/astrid/sync/SyncMessageTest.java +++ b/tests-sync/src/com/todoroo/astrid/sync/SyncMessageTest.java @@ -133,7 +133,7 @@ public class SyncMessageTest extends NewSyncTestCase { t.putTransitory(SyncFlags.ACTFM_SUPPRESS_OUTSTANDING_ENTRIES, true); taskDao.save(t); - new ReplayOutstandingEntries(Task.class, NameMaps.TABLE_ID_TASKS, taskDao, taskOutstandingDao, null, false).execute(); + new ReplayOutstandingEntries(Task.class, NameMaps.TABLE_ID_TASKS, taskDao, taskOutstandingDao, false).execute(); t = taskDao.fetch(t.getId(), Task.TITLE, Task.IMPORTANCE); assertEquals(SYNC_TASK_TITLE, t.getValue(Task.TITLE)); diff --git a/tests/src/com/todoroo/astrid/service/TitleParserTest.java b/tests/src/com/todoroo/astrid/service/TitleParserTest.java index 7356a4f42..c925cceb6 100644 --- a/tests/src/com/todoroo/astrid/service/TitleParserTest.java +++ b/tests/src/com/todoroo/astrid/service/TitleParserTest.java @@ -15,7 +15,6 @@ import com.google.ical.values.RRule; import com.timsu.astrid.R; import com.todoroo.andlib.utility.Preferences; import com.todoroo.astrid.data.Task; -import com.todoroo.astrid.service.TaskService; import com.todoroo.astrid.test.DatabaseTestCase; import com.todoroo.astrid.utility.TitleParser; @@ -484,6 +483,7 @@ public class TitleParserTest extends DatabaseTestCase { assertTrue(task.hasDueDate()); task.clearValue(Task.ID); + task.clearValue(Task.UUID); title = "Jog every day starting from today"; task.setValue(Task.TITLE, title); TaskService.createWithValues(task, null, title); @@ -513,6 +513,7 @@ public class TitleParserTest extends DatabaseTestCase { assertTrue(task.hasDueDate()); task.clearValue(Task.ID); + task.clearValue(Task.UUID); title = "Jog every week starting from today"; task.setValue(Task.TITLE, title); TaskService.createWithValues(task, null, title);