diff --git a/api-src/com/todoroo/astrid/api/AstridContentProvider.java b/api-src/com/todoroo/astrid/api/AstridContentProvider.java
index e975c018a..61b4bf35f 100644
--- a/api-src/com/todoroo/astrid/api/AstridContentProvider.java
+++ b/api-src/com/todoroo/astrid/api/AstridContentProvider.java
@@ -106,47 +106,34 @@ public class AstridContentProvider {
// --- columns
/** long: Task id */
- public static final String ID = AstridApiConstants.TASK_TABLE + "._id";
+ public static final String ID = "_id";
/** String: name of Task */
- public static final String TITLE = AstridApiConstants.TASK_TABLE
- + ".title";
-
- /**
- * int: Task Urgency setting (see Task.URGENCY_* for
- * possible values)
- */
- public static final String URGENCY = AstridApiConstants.TASK_TABLE
- + ".urgency";
-
- /**
- * int: Task Importance setting (see Task.IMPORTANCE_* for
- * possible values)
- */
- public static final String IMPORTANCE = AstridApiConstants.TASK_TABLE
- + ".importance";
+ public static final String TITLE = "title";
+
+ /** int: Task Urgency setting (see Task.URGENCY_*) */
+ public static final String URGENCY = "urgency";
+
+ /** int: Task Importance setting (see Task.IMPORTANCE_*) */
+ public static final String IMPORTANCE = "importance";
/** int: unixtime Task is due, 0 if not set */
- public static final String DUE_DATE = AstridApiConstants.TASK_TABLE
- + ".dueDate";
+ public static final String DUE_DATE = "dueDate";
/** int: unixtime Task should be hidden until, 0 if not set */
- public static final String HIDDEN_UNTIL = AstridApiConstants.TASK_TABLE
- + ".hiddenUntil";
+ public static final String HIDDEN_UNTIL = "hiddenUntil";
/** int: unixtime Task was created */
- public static final String CREATION_DATE = AstridApiConstants.TASK_TABLE
- + ".creationDate";
+ public static final String CREATION_DATE = "creationDate";
/** int: unixtime Task was completed, 0 if task not completed */
- public static final String COMPLETION_DATE = AstridApiConstants.TASK_TABLE
- + ".completionDate";
+ public static final String COMPLETION_DATE = "completionDate";
/** int: unixtime Task was deleted, 0 if task not deleted */
- public static final String DELETION_DATE = AstridApiConstants.TASK_TABLE
- + ".deletionDate";
+ public static final String DELETION_DATE = "deletionDate";
- public static final String MODIFICATION_DATE = null;
+ /** int: unixtime Task was modified */
+ public static final String MODIFICATION_DATE = "modificationDate";
// --- urgency settings
diff --git a/src-legacy/com/timsu/astrid/data/AbstractController.java b/src-legacy/com/timsu/astrid/data/AbstractController.java
index 2747ecf9c..e132c54bc 100644
--- a/src-legacy/com/timsu/astrid/data/AbstractController.java
+++ b/src-legacy/com/timsu/astrid/data/AbstractController.java
@@ -35,11 +35,11 @@ abstract public class AbstractController {
public static final String KEY_ROWID = "_id";
// database and table names
- protected static final String TASK_TABLE_NAME = "tasks";
- protected static final String TAG_TABLE_NAME = "tags";
- protected static final String TAG_TASK_MAP_NAME = "tagTaskMap";
- protected static final String ALERT_TABLE_NAME = "alerts";
- protected static final String SYNC_TABLE_NAME = "sync";
+ public static final String TASK_TABLE_NAME = "tasks";
+ public static final String TAG_TABLE_NAME = "tags";
+ public static final String TAG_TASK_MAP_NAME = "tagTaskMap";
+ public static final String ALERT_TABLE_NAME = "alerts";
+ public static final String SYNC_TABLE_NAME = "sync";
abstract public void open();
abstract public void close();
diff --git a/src/com/todoroo/astrid/dao/Database.java b/src/com/todoroo/astrid/dao/Database.java
index 1f389ec5e..d5e31386c 100644
--- a/src/com/todoroo/astrid/dao/Database.java
+++ b/src/com/todoroo/astrid/dao/Database.java
@@ -76,7 +76,7 @@ public final class Database extends AbstractDatabase {
* Default implementation of Astrid database helper
*/
@SuppressWarnings("nls")
- public static class AstridSQLiteOpenHelper extends SQLiteOpenHelper {
+ private static class AstridSQLiteOpenHelper extends SQLiteOpenHelper {
public AstridSQLiteOpenHelper(Context context, String name,
CursorFactory factory, int version) {
@@ -98,6 +98,7 @@ public final class Database extends AbstractDatabase {
for(Property> property : table.getProperties()) {
if(AbstractModel.ID_PROPERTY.equals(property.name))
continue;
+ Log.e("haha", table.name + "'s " + property.name);
sql.append(',').append(property.accept(sqlVisitor, null));
}
sql.append(')');
diff --git a/src/com/todoroo/astrid/model/Metadata.java b/src/com/todoroo/astrid/model/Metadata.java
index b3384aaba..d668d6ce1 100644
--- a/src/com/todoroo/astrid/model/Metadata.java
+++ b/src/com/todoroo/astrid/model/Metadata.java
@@ -45,16 +45,7 @@ public class Metadata extends AbstractModel {
TABLE, "value");
/** List of all properties for this model */
- public static final Property>[] PROPERTIES = new Property>[] {
- ID,
- TASK,
- KEY,
- VALUE,
- };
-
- static {
- TABLE.setProperties(PROPERTIES);
- }
+ public static final Property>[] PROPERTIES = generateProperties(Metadata.class);
// --- defaults
diff --git a/src/com/todoroo/astrid/model/Task.java b/src/com/todoroo/astrid/model/Task.java
index 009d6d20a..7cb95a32a 100644
--- a/src/com/todoroo/astrid/model/Task.java
+++ b/src/com/todoroo/astrid/model/Task.java
@@ -8,6 +8,8 @@ package com.todoroo.astrid.model;
import android.content.ContentValues;
+import com.timsu.astrid.data.enums.RepeatInterval;
+import com.timsu.astrid.data.task.AbstractTaskModel.RepeatInfo;
import com.todoroo.andlib.data.AbstractModel;
import com.todoroo.andlib.data.Property;
import com.todoroo.andlib.data.Table;
@@ -73,22 +75,47 @@ public class Task extends AbstractModel {
public static final IntegerProperty DELETION_DATE = new IntegerProperty(
TABLE, AstridTask.DELETION_DATE);
- /** List of all properties for this model */
- public static final Property>[] PROPERTIES = new Property>[] {
- ID,
- TITLE,
- URGENCY,
- IMPORTANCE,
- DUE_DATE,
- HIDDEN_UNTIL,
- CREATION_DATE,
- COMPLETION_DATE,
- DELETION_DATE
- };
+ // --- for migration purposes from astrid 2 (eventually we will want to
+ // move these into the metadata table and treat them as plug-ins
- static {
- TABLE.setProperties(PROPERTIES);
- }
+ public static final StringProperty NOTES = new StringProperty(
+ TABLE, "notes");
+
+ public static final IntegerProperty ESTIMATED_SECONDS = new IntegerProperty(
+ TABLE, "estimatedSeconds");
+
+ public static final IntegerProperty ELAPSED_SECONDS = new IntegerProperty(
+ TABLE, "elapsedSeconds");
+
+ public static final IntegerProperty TIMER_START = new IntegerProperty(
+ TABLE, "timerStart");
+
+ public static final IntegerProperty PREFERRED_DUE_DATE = new IntegerProperty(
+ TABLE, "preferredDueDate");
+
+ public static final IntegerProperty POSTPONE_COUNT = new IntegerProperty(
+ TABLE, "postponeCount");
+
+ public static final IntegerProperty NOTIFICATIONS = new IntegerProperty(
+ TABLE, "notifications");
+
+ public static final IntegerProperty NOTIFICATION_FLAGS = new IntegerProperty(
+ TABLE, "notificationFlags");
+
+ public static final IntegerProperty LAST_NOTIFIED = new IntegerProperty(
+ TABLE, "lastNotified");
+
+ public static final IntegerProperty REPEAT = new IntegerProperty(
+ TABLE, "repeat");
+
+ public static final IntegerProperty FLAGS = new IntegerProperty(
+ TABLE, "flags");
+
+ public static final StringProperty CALENDAR_URI = new StringProperty(
+ TABLE, "calendarUri");
+
+ /** List of all properties for this model */
+ public static final Property>[] PROPERTIES = generateProperties(Task.class);
// --- urgency flags
@@ -197,4 +224,23 @@ public class Task extends AbstractModel {
return getValue(DUE_DATE) > 0;
}
+ // --- data access methods for migration properties
+
+ /** Number of bits to shift repeat value by */
+ public static final int REPEAT_VALUE_OFFSET = 3;
+
+ /**
+ * @return RepeatInfo corresponding to
+ */
+ public RepeatInfo getRepeatInfo() {
+ int repeat = getValue(REPEAT);
+ if(repeat == 0)
+ return null;
+ int value = repeat >> REPEAT_VALUE_OFFSET;
+ RepeatInterval interval = RepeatInterval.values()
+ [repeat - (value << REPEAT_VALUE_OFFSET)];
+
+ return new RepeatInfo(interval, value);
+ }
+
}
\ No newline at end of file
diff --git a/src/com/todoroo/astrid/service/UpgradeService.java b/src/com/todoroo/astrid/service/UpgradeService.java
new file mode 100644
index 000000000..0274d94ba
--- /dev/null
+++ b/src/com/todoroo/astrid/service/UpgradeService.java
@@ -0,0 +1,208 @@
+package com.todoroo.astrid.service;
+
+import java.util.HashMap;
+import java.util.Map.Entry;
+
+import android.content.Context;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteOpenHelper;
+import android.database.sqlite.SQLiteDatabase.CursorFactory;
+
+import com.timsu.astrid.data.AbstractController;
+import com.timsu.astrid.data.task.AbstractTaskModel;
+import com.todoroo.andlib.data.AbstractDao;
+import com.todoroo.andlib.data.AbstractModel;
+import com.todoroo.andlib.data.Property;
+import com.todoroo.andlib.data.Property.PropertyVisitor;
+import com.todoroo.andlib.service.Autowired;
+import com.todoroo.andlib.service.ContextManager;
+import com.todoroo.astrid.dao.Database;
+import com.todoroo.astrid.dao.TaskDao;
+import com.todoroo.astrid.model.Task;
+
+
+public final class UpgradeService {
+
+ @Autowired
+ private Database database;
+
+ @Autowired
+ private TaskDao taskDao;
+
+ /**
+ * Perform upgrade from one version to the next. Needs to be called
+ * on the UI thread so it can display a progress bar and then
+ * show users a change log.
+ *
+ * @param from
+ * @param to
+ */
+ public void performUpgrade(int from, int to) {
+ if(from == to)
+ return;
+
+ if(from < 150) {
+ upgrade2To3();
+ }
+ }
+
+ // --- database upgrade logic
+
+ /**
+ * Upgrade helper class that reads a database
+ */
+ private class Astrid2UpgradeHelper extends SQLiteOpenHelper {
+
+ private String name;
+
+ public Astrid2UpgradeHelper(Context context, String name,
+ CursorFactory factory, int version) {
+ super(context, name, factory, version);
+ this.name = name;
+ }
+
+ @Override
+ @SuppressWarnings("nls")
+ public void onCreate(SQLiteDatabase db) {
+ // create empty table with nothing in it
+ String sql = "CREATE TABLE IF NOT EXISTS " + name + " (" +
+ AbstractModel.ID_PROPERTY + " INTEGER);";
+ db.execSQL(sql);
+ }
+
+ @Override
+ public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
+ // do nothing
+ }
+
+ }
+
+ /**
+ * Perform the upgrade from Astrid 2 to Astrid 3
+ */
+ private void upgrade2To3() {
+ Context context = ContextManager.getContext();
+
+ // --- upgrade tasks table
+ HashMap> propertyMap =
+ new HashMap>();
+ propertyMap.put(AbstractController.KEY_ROWID, Task.ID);
+ propertyMap.put(AbstractTaskModel.NAME, Task.TITLE);
+ propertyMap.put(AbstractTaskModel.NOTES, Task.TITLE);
+ // don't update progress percentage, we don't use this anymore
+ propertyMap.put(AbstractTaskModel.IMPORTANCE, Task.IMPORTANCE);
+ propertyMap.put(AbstractTaskModel.ESTIMATED_SECONDS, Task.ESTIMATED_SECONDS);
+ propertyMap.put(AbstractTaskModel.ELAPSED_SECONDS, Task.ELAPSED_SECONDS);
+ propertyMap.put(AbstractTaskModel.TIMER_START, Task.TIMER_START);
+ propertyMap.put(AbstractTaskModel.DEFINITE_DUE_DATE, Task.DUE_DATE);
+ propertyMap.put(AbstractTaskModel.PREFERRED_DUE_DATE, Task.PREFERRED_DUE_DATE);
+ propertyMap.put(AbstractTaskModel.HIDDEN_UNTIL, Task.HIDDEN_UNTIL);
+ propertyMap.put(AbstractTaskModel.POSTPONE_COUNT, Task.POSTPONE_COUNT);
+ propertyMap.put(AbstractTaskModel.NOTIFICATIONS, Task.NOTIFICATIONS);
+ propertyMap.put(AbstractTaskModel.NOTIFICATION_FLAGS, Task.NOTIFICATION_FLAGS);
+ propertyMap.put(AbstractTaskModel.LAST_NOTIFIED, Task.LAST_NOTIFIED);
+ propertyMap.put(AbstractTaskModel.REPEAT, Task.REPEAT);
+ propertyMap.put(AbstractTaskModel.CREATION_DATE, Task.CREATION_DATE);
+ propertyMap.put(AbstractTaskModel.COMPLETION_DATE, Task.COMPLETION_DATE);
+ propertyMap.put(AbstractTaskModel.CALENDAR_URI, Task.CALENDAR_URI);
+ propertyMap.put(AbstractTaskModel.FLAGS, Task.FLAGS);
+ upgradeTasksTable(context, AbstractController.TASK_TABLE_NAME,
+ propertyMap, new Task(), taskDao);
+
+ // --- upgrade tags table
+
+ }
+
+ protected static class UpgradeVisitorContainer {
+ public int columnIndex;
+ public Cursor cursor;
+ public AbstractModel model;
+ }
+
+ /**
+ * Visitor that reads from a visitor container and writes to the model
+ * @author Tim Su
+ *
+ */
+ protected class ColumnUpgradeVisitor implements PropertyVisitor {
+ @Override
+ public Void visitDouble(Property property, UpgradeVisitorContainer data) {
+ double value = data.cursor.getDouble(data.columnIndex);
+ data.model.setValue(property, value);
+ return null;
+ }
+
+ @Override
+ public Void visitInteger(Property property, UpgradeVisitorContainer data) {
+ int value;
+
+ // convert long date -> integer
+ if(property == Task.COMPLETION_DATE ||
+ property == Task.CREATION_DATE ||
+ property == Task.DELETION_DATE ||
+ property == Task.DUE_DATE ||
+ property == Task.HIDDEN_UNTIL ||
+ property == Task.LAST_NOTIFIED ||
+ property == Task.MODIFICATION_DATE ||
+ property == Task.PREFERRED_DUE_DATE)
+ value = (int) (data.cursor.getLong(data.columnIndex) / 1000L);
+ else
+ value = data.cursor.getInt(data.columnIndex);
+
+ data.model.setValue(property, value);
+ return null;
+ }
+
+ @Override
+ public Void visitLong(Property property, UpgradeVisitorContainer data) {
+ long value = data.cursor.getLong(data.columnIndex);
+ data.model.setValue(property, value);
+ return null;
+ }
+
+ @Override
+ public Void visitString(Property property, UpgradeVisitorContainer data) {
+ String value = data.cursor.getString(data.columnIndex);
+ data.model.setValue(property, value);
+ return null;
+ }
+ }
+
+ /**
+ * Helper that reads entries from legacy database and row-by-row
+ * creates new models and saves them.
+ *
+ * @param context
+ * @param legacyTable
+ * @param propertyMap
+ * @param model
+ * @param dao
+ */
+ @SuppressWarnings("nls")
+ private void upgradeTasksTable(Context context, String legacyTable,
+ HashMap> propertyMap, TYPE model,
+ AbstractDao dao) {
+
+ SQLiteDatabase upgradeDb = new Astrid2UpgradeHelper(context, legacyTable,
+ null, 0).getReadableDatabase();
+
+ Cursor cursor = upgradeDb.rawQuery("SELECT * FROM " + legacyTable, null);
+ UpgradeVisitorContainer container = new UpgradeVisitorContainer();
+ container.cursor = cursor;
+ container.model = model;
+ ColumnUpgradeVisitor visitor = new ColumnUpgradeVisitor();
+ for(cursor.moveToFirst(); !cursor.isAfterLast(); cursor.moveToNext()) {
+ model.clear();
+ for(Entry> entry : propertyMap.entrySet()) {
+ container.columnIndex = cursor.getColumnIndex(entry.getKey());
+ entry.getValue().accept(visitor, container);
+ }
+ dao.save(database, container.model);
+ }
+
+ upgradeDb.close();
+ context.deleteDatabase(legacyTable);
+ }
+
+}
diff --git a/tests/AndroidManifest.xml b/tests/AndroidManifest.xml
index 24af24bfa..78e3d25bd 100644
--- a/tests/AndroidManifest.xml
+++ b/tests/AndroidManifest.xml
@@ -20,6 +20,6 @@
"adb shell am instrument -w com.xxx.xxx.tests/android.test.InstrumentationTestRunner"
-->
diff --git a/tests/src/com/todoroo/astrid/test/DatabaseTestCase.java b/tests/src/com/todoroo/astrid/test/DatabaseTestCase.java
index 8c87a0ae2..f7a29e9bf 100644
--- a/tests/src/com/todoroo/astrid/test/DatabaseTestCase.java
+++ b/tests/src/com/todoroo/astrid/test/DatabaseTestCase.java
@@ -22,11 +22,11 @@ public class DatabaseTestCase extends TodorooTestCase {
@Override
protected void setUp() throws Exception {
+ super.setUp();
+
// create new test database
- database.clear();
+ database.clear();
database.openForWriting();
-
- super.setUp();
}
@Override
diff --git a/tests/src/com/todoroo/astrid/upgrade/Astrid2To3UpgradeTests.java b/tests/src/com/todoroo/astrid/upgrade/Astrid2To3UpgradeTests.java
index b4de9b2ef..b9930cd40 100644
--- a/tests/src/com/todoroo/astrid/upgrade/Astrid2To3UpgradeTests.java
+++ b/tests/src/com/todoroo/astrid/upgrade/Astrid2To3UpgradeTests.java
@@ -25,6 +25,8 @@ public class Astrid2To3UpgradeTests extends DatabaseTestCase {
}
public static void assertDatesEqual(Date old, int newDate) {
+ if(old == null)
+ assertEquals(0, newDate);
assertEquals(old.getTime() / 1000L, newDate);
}
@@ -44,10 +46,12 @@ public class Astrid2To3UpgradeTests extends DatabaseTestCase {
TaskModelForEdit guti = new com.todoroo.astrid.legacy.data.task.TaskModelForEdit();
guti.setName("franklin gutierrez");
guti.setPreferredDueDate(new Date(System.currentTimeMillis() + 5000000L));
+ guti.setImportance(Importance.LEVEL_4);
guti.setHiddenUntil(new Date());
guti.setRepeat(new RepeatInfo(RepeatInterval.DAYS, 10));
guti.setElapsedSeconds(500);
taskController.saveTask(guti, false);
+ Date createdDate = new Date();
// assert created
assertEquals(2, taskController.getAllTaskIdentifiers());
@@ -62,12 +66,30 @@ public class Astrid2To3UpgradeTests extends DatabaseTestCase {
// verify that data exists in our new table
TodorooCursor tasks = taskDao.query(database, Query.select(Task.PROPERTIES));
+ assertEquals(2, tasks.getCount());
+
tasks.moveToFirst();
Task task = new Task(tasks, Task.PROPERTIES);
assertEquals(griffey.getName(), task.getValue(Task.TITLE));
assertDatesEqual(griffey.getDefiniteDueDate(), task.getValue(Task.DUE_DATE));
assertEquals((Integer)Task.IMPORTANCE_SHOULD_DO, task.getValue(Task.IMPORTANCE));
-
+ assertEquals(griffey.getEstimatedSeconds(), task.getValue(Task.ESTIMATED_SECONDS));
+ assertEquals(griffey.getNotes(), task.getValue(Task.NOTES));
+ assertEquals((Integer)0, task.getValue(Task.LAST_NOTIFIED));
+ assertEquals((Integer)0, task.getValue(Task.HIDDEN_UNTIL));
+
+ tasks.moveToNext();
+ task = new Task(tasks, Task.PROPERTIES);
+ assertEquals(guti.getName(), task.getValue(Task.TITLE));
+ assertDatesEqual(guti.getDefiniteDueDate(), task.getValue(Task.DUE_DATE));
+ assertDatesEqual(guti.getPreferredDueDate(), task.getValue(Task.DUE_DATE));
+ assertDatesEqual(guti.getHiddenUntil(), task.getValue(Task.HIDDEN_UNTIL));
+ assertEquals((Integer)Task.IMPORTANCE_DO_OR_DIE, task.getValue(Task.IMPORTANCE));
+ assertEquals(guti.getRepeat().getValue(), task.getRepeatInfo().getValue());
+ assertEquals(guti.getRepeat().getInterval().ordinal(), task.getRepeatInfo().getInterval().ordinal());
+ assertEquals(guti.getElapsedSeconds(), task.getValue(Task.ELAPSED_SECONDS));
+ assertEquals(createdDate, task.getValue(Task.CREATION_DATE));
+ assertEquals(createdDate, task.getValue(Task.MODIFICATION_DATE));
}