Merge remote branch 'origin/master'

pull/14/head
Tim Su 14 years ago
commit 726bb52df6

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.timsu.astrid"
android:versionName="2.14.3" android:versionCode="134">
android:versionName="2.14.4" android:versionCode="135">
<!-- ============================ Metadata ============================ -->

@ -132,8 +132,9 @@ abstract public class AbstractDatabase {
* Close the database if it has been opened previously
*/
public synchronized final void close() {
if(database != null)
if(database != null) {
database.close();
}
database = null;
}
@ -155,6 +156,14 @@ abstract public class AbstractDatabase {
return database;
}
/**
* @return human-readable database name for debugging
*/
@Override
public String toString() {
return "DB:" + getName();
}
// --- helper classes
/**

@ -152,7 +152,8 @@ public abstract class AbstractModel implements Parcelable {
/**
* Utility method to get the identifier of the model, if it exists.
* Returns 0
*
* @return {@value #NO_ID} if this model was not added to the database
*/
abstract public long getId();
@ -175,6 +176,18 @@ public abstract class AbstractModel implements Parcelable {
setValues.put(ID_PROPERTY_NAME, id);
}
/**
* @param property
* @return true if setValues or values contains this property
*/
public boolean containsValue(Property<?> property) {
if(setValues != null && setValues.containsKey(property.name))
return true;
if(values != null && values.containsKey(property.name))
return true;
return false;
}
// --- data storage
/**

@ -184,14 +184,16 @@ public abstract class Property<TYPE> extends Field implements Cloneable {
/** Runs a SQL function and returns the result as a string */
public static class StringFunctionProperty extends StringProperty {
public StringFunctionProperty(String function, String columnName) {
super(null, columnName, function + " AS " + columnName);
super(null, columnName, function);
alias = columnName;
}
}
/** Runs a SQL function and returns the result as a string */
public static class IntegerFunctionProperty extends IntegerProperty {
public IntegerFunctionProperty(String function, String columnName) {
super(null, columnName, function + " AS " + columnName);
super(null, columnName, function);
alias = columnName;
}
}

@ -3,7 +3,7 @@ package com.todoroo.andlib.data.sql;
import static com.todoroo.andlib.data.sql.Constants.AS;
import static com.todoroo.andlib.data.sql.Constants.SPACE;
public abstract class DBObject<T extends DBObject<?>> {
public abstract class DBObject<T extends DBObject<?>> implements Cloneable {
protected String alias;
protected final String expression;
@ -12,8 +12,13 @@ public abstract class DBObject<T extends DBObject<?>> {
}
public T as(String newAlias) {
this.alias = newAlias;
return (T) this;
try {
T clone = (T) clone();
clone.alias = newAlias;
return clone;
} catch (CloneNotSupportedException e) {
throw new RuntimeException(e);
}
}
public boolean hasAlias() {
@ -43,6 +48,13 @@ public abstract class DBObject<T extends DBObject<?>> {
@Override
public final String toString() {
if (hasAlias()) {
return alias;
}
return expression;
}
public final String toStringInSelect() {
StringBuilder sb = new StringBuilder(expression);
if (hasAlias()) {
sb.append(SPACE).append(AS).append(SPACE).append(alias);

@ -22,17 +22,17 @@ public class Query {
private Table table;
private List<Criterion> criterions = new ArrayList<Criterion>();
private List<Property<?>> fields = new ArrayList<Property<?>>();
private List<Field> fields = new ArrayList<Field>();
private List<Join> joins = new ArrayList<Join>();
private List<Field> groupBies = new ArrayList<Field>();
private List<Order> orders = new ArrayList<Order>();
private List<Criterion> havings = new ArrayList<Criterion>();
private Query(Property<?>... fields) {
private Query(Field... fields) {
this.fields.addAll(asList(fields));
}
public static Query select(Property<?>... fields) {
public static Query select(Field... fields) {
return new Query(fields);
}
@ -149,7 +149,7 @@ public class Query {
return;
}
for (Field field : fields) {
sql.append(field).append(COMMA);
sql.append(field.toStringInSelect()).append(COMMA);
}
sql.deleteCharAt(sql.length() - 1).append(SPACE);
}

@ -1,6 +1,9 @@
package com.todoroo.andlib.service;
import java.lang.reflect.Field;
import java.util.Arrays;
import android.util.Log;
/**
* Simple Dependency Injection Service for Android.
@ -97,8 +100,10 @@ public class DependencyInjectionService {
}
throw new IllegalStateException(
String.format("No dependency injector found for autowired field '%s' in class '%s'",
field.getName(), caller.getClass().getName()));
String.format("No dependency injector found for autowired " +
"field '%s' in class '%s'. Injectors: %s",
field.getName(), caller.getClass().getName(),
Arrays.asList(getInjectors())));
}
// --- static methods
@ -133,5 +138,6 @@ public class DependencyInjectionService {
*/
public synchronized void setInjectors(AbstractDependencyInjector[] injectors) {
this.injectors = injectors;
Log.e("INJECTION SETTING", "Set Injector List to: " + Arrays.asList(injectors)); // (debug)
}
}

@ -12,7 +12,7 @@ import com.todoroo.andlib.data.Table;
import com.todoroo.andlib.data.TodorooCursor;
import com.todoroo.andlib.data.Property.IntegerProperty;
import com.todoroo.andlib.data.Property.LongProperty;
import com.todoroo.astrid.model.Metadata;
import com.todoroo.andlib.data.Property.StringProperty;
import com.todoroo.astrid.model.Task;
/**
@ -38,14 +38,18 @@ public class Alarm extends AbstractModel {
public static final LongProperty TASK = new LongProperty(
TABLE, "task");
/** Associated Task */
/** Alarm Time (seconds since epoch) */
public static final IntegerProperty TIME = new IntegerProperty(
TABLE, "time");
/** Associated Task */
/** Alarm Type (see constants) */
public static final IntegerProperty TYPE = new IntegerProperty(
TABLE, "type");
/** Alarm Ringtone */
public static final StringProperty RINGTONE = new StringProperty(
TABLE, "ringtone");
/** List of all properties for this model */
public static final Property<?>[] PROPERTIES = generateProperties(Alarm.class);
@ -67,6 +71,7 @@ public class Alarm extends AbstractModel {
static {
defaultValues.put(TYPE.name, TYPE_SINGLE);
defaultValues.put(RINGTONE.name, "");
}
@Override
@ -80,12 +85,12 @@ public class Alarm extends AbstractModel {
super();
}
public Alarm(TodorooCursor<Metadata> cursor) {
public Alarm(TodorooCursor<Alarm> cursor) {
this();
readPropertiesFromCursor(cursor);
}
public void readFromCursor(TodorooCursor<Metadata> cursor) {
public void readFromCursor(TodorooCursor<Alarm> cursor) {
super.readPropertiesFromCursor(cursor);
}

@ -2,10 +2,7 @@ package com.todoroo.astrid.alarms;
import android.content.Context;
import com.todoroo.andlib.service.Autowired;
import com.todoroo.andlib.service.DependencyInjectionService;
import com.todoroo.astrid.dao.MetadataDao;
import com.todoroo.astrid.service.MetadataService;
/**
* Provides operations for working with alerts
@ -21,12 +18,6 @@ public class AlarmService {
*/
public static final String ALARM_COUNT = "alarms-count";
@Autowired
private MetadataDao metadataDao;
@Autowired
private MetadataService metadataService;
public AlarmService(@SuppressWarnings("unused") Context context) {
DependencyInjectionService.getInstance().inject(this);
}

@ -6,6 +6,7 @@
package com.todoroo.astrid.alarms;
import com.todoroo.andlib.data.AbstractDatabase;
import com.todoroo.andlib.data.GenericDao;
import com.todoroo.andlib.data.Table;
/**
@ -40,6 +41,8 @@ public class AlarmsDatabase extends AbstractDatabase {
// --- implementation
private GenericDao<Alarm> dao = new GenericDao<Alarm>(Alarm.class, this);
@Override
protected String getName() {
return NAME;
@ -55,6 +58,10 @@ public class AlarmsDatabase extends AbstractDatabase {
return TABLES;
}
public GenericDao<Alarm> getDao() {
return dao;
}
@Override
protected void onCreateTables() {
StringBuilder sql = new StringBuilder();
@ -65,9 +72,8 @@ public class AlarmsDatabase extends AbstractDatabase {
database.execSQL(sql.toString());
sql.setLength(0);
sql.append("CREATE INDEX IF NOT EXISTS a_timetype ON ").
sql.append("CREATE INDEX IF NOT EXISTS a_type ON ").
append(Alarm.TABLE).append('(').
append(Alarm.TIME.name).append(',').
append(Alarm.TYPE.name).
append(')');
database.execSQL(sql.toString());

@ -26,7 +26,7 @@ import com.todoroo.astrid.service.MetadataService;
*
*/
@SuppressWarnings("nls")
public class DataService {
public class TagService {
/**
* Metadata key for tag data
@ -39,7 +39,7 @@ public class DataService {
@Autowired
private MetadataService metadataService;
public DataService(@SuppressWarnings("unused") Context context) {
public TagService(@SuppressWarnings("unused") Context context) {
DependencyInjectionService.getInstance().inject(this);
}
@ -75,7 +75,7 @@ public class DataService {
*/
public Tag[] getGroupedTags(Order order) {
TodorooCursor<Metadata> cursor = metadataService.fetchWithCount(
COUNT, MetadataCriteria.withKey(KEY), order, true);
COUNT, MetadataCriteria.withKey(KEY), order);
try {
Tag[] array = new Tag[cursor.getCount()];
for (int i = 0; i < array.length; i++) {

@ -62,10 +62,6 @@
<!-- Time Constants -->
<!-- used for long due dates. c.f. Java's SimpleDateFormat -->
<string name="dateFormatter">d MMM</string>
<!-- used for alarms -->
<string name="alarmDateFormatter">HH:mm dd/M</string>
<!-- vertical labels are used in dialog boxes -->
<string name="daysVertical">D\ni\ne\ns</string>
<string name="hoursVertical">H\no\nr\ne\ns</string>

@ -62,10 +62,6 @@
<!-- Time Constants -->
<!-- used for long due dates. c.f. Java's SimpleDateFormat -->
<string name="dateFormatter">d. MMM</string>
<!-- used for alarms -->
<string name="alarmDateFormatter">dd.M HH:mm</string>
<!-- vertical labels are used in dialog boxes -->
<string name="daysVertical">D\na\ny\ns</string>
<string name="hoursVertical">H\no\nu\nr\ns</string>

@ -62,10 +62,6 @@
<!-- Time Constants -->
<!-- used for long due dates. c.f. Java's SimpleDateFormat -->
<string name="dateFormatter">d. MMM</string>
<!-- used for alarms -->
<string name="alarmDateFormatter">dd/M HH:mm</string>
<!-- vertical labels are used in dialog boxes -->
<string name="daysVertical">T
a

@ -62,10 +62,6 @@
<!-- Time Constants -->
<!-- used for long due dates. c.f. Java's SimpleDateFormat -->
<string name="dateFormatter">d MMM</string>
<!-- used for alarms -->
<string name="alarmDateFormatter">HH:mm dd/M</string>
<!-- vertical labels are used in dialog boxes -->
<string name="daysVertical">D\ní\na\ns</string>
<string name="hoursVertical">H\no\nr\na\ns</string>

@ -62,10 +62,6 @@
<!-- Time Constants -->
<!-- used for long due dates. c.f. Java's SimpleDateFormat -->
<string name="dateFormatter">dd MMM</string>
<!-- used for alarms -->
<string name="alarmDateFormatter">dd/MM HH:mm</string>
<!-- vertical labels are used in dialog boxes -->
<string name="daysVertical">j\no\nu\nr\ns</string>
<string name="hoursVertical">h\ne\nu\nr\ne\ns</string>

@ -62,10 +62,6 @@
<!-- Time Constants -->
<!-- used for long due dates. c.f. Java's SimpleDateFormat -->
<string name="dateFormatter">MMM d</string>
<!-- used for alarms -->
<string name="alarmDateFormatter">M/dd HH:mm</string>
<!-- vertical labels are used in dialog boxes -->
<string name="daysVertical">D\na\ny\ns</string>
<string name="hoursVertical">H\no\nu\nr\ns</string>

@ -62,10 +62,6 @@
<!-- Time Constants -->
<!-- used for long due dates. c.f. Java's SimpleDateFormat -->
<string name="dateFormatter">d MMM</string>
<!-- used for alarms -->
<string name="alarmDateFormatter">dd/M HH:mm</string>
<!-- vertical labels are used in dialog boxes -->
<string name="daysVertical">D\na\ny\ns</string>
<string name="hoursVertical">H\no\nu\nr\ns</string>

@ -62,10 +62,6 @@
<!-- Time Constants -->
<!-- used for long due dates. c.f. Java's SimpleDateFormat -->
<string name="dateFormatter">MMM d</string>
<!-- used for alarms -->
<string name="alarmDateFormatter">M/dd HH:mm</string>
<!-- vertical labels are used in dialog boxes -->
<string name="daysVertical"></string>
<string name="hoursVertical">時\n間</string>

@ -62,10 +62,6 @@
<!-- Time Constants -->
<!-- used for long due dates. c.f. Java's SimpleDateFormat -->
<string name="dateFormatter">MMM d</string>
<!-- used for alarms -->
<string name="alarmDateFormatter">M/dd HH:mm</string>
<!-- vertical labels are used in dialog boxes -->
<string name="daysVertical"></string>
<string name="hoursVertical"></string>

@ -62,10 +62,6 @@
<!-- Time Constants -->
<!-- used for long due dates. c.f. Java's SimpleDateFormat -->
<string name="dateFormatter">d MMM</string>
<!-- used for alarms -->
<string name="alarmDateFormatter">dd/M HH:mm</string>
<!-- vertical labels are used in dialog boxes -->
<string name="daysVertical">d\na\ng\ne\nr</string>
<string name="hoursVertical">t\ni\nm\ne\nr</string>
@ -493,4 +489,4 @@ Hvis du ikke ønsker å se den nye oppgaven rett etter at du fullfører den gaml
<string name="read_tasks_permission">Les Astrid-gjøremål</string>
</resources>

@ -62,10 +62,6 @@
<!-- Time Constants -->
<!-- used for long due dates. c.f. Java's SimpleDateFormat -->
<string name="dateFormatter">d MMM</string>
<!-- used for alarms -->
<string name="alarmDateFormatter">dd-M HH:mm</string>
<!-- vertical labels are used in dialog boxes -->
<string name="daysVertical">D\na\ng\ne\nn</string>
<string name="hoursVertical">U\nr\ne\nn</string>

@ -62,10 +62,6 @@
<!-- Time Constants -->
<!-- used for long due dates. c.f. Java's SimpleDateFormat -->
<string name="dateFormatter">d MMMM</string>
<!-- used for alarms -->
<string name="alarmDateFormatter">M/dd HH:mm</string>
<!-- vertical labels are used in dialog boxes -->
<string name="daysVertical">D\na\ny\ns</string>
<string name="hoursVertical">H\no\nu\nr\ns</string>

@ -62,10 +62,6 @@
<!-- Time Constants -->
<!-- used for long due dates. c.f. Java's SimpleDateFormat -->
<string name="dateFormatter">MMM d</string>
<!-- used for alarms -->
<string name="alarmDateFormatter">M/dd HH:mm</string>
<!-- vertical labels are used in dialog boxes -->
<string name="daysVertical">D\ni\na\ns</string>
<string name="hoursVertical">H\no\nr\na\ns</string>

@ -62,10 +62,6 @@
<!-- Time Constants -->
<!-- used for long due dates. c.f. Java's SimpleDateFormat -->
<string name="dateFormatter">d MMM</string>
<!-- used for alarms -->
<string name="alarmDateFormatter">dd/M HH:mm</string>
<!-- vertical labels are used in dialog boxes -->
<string name="daysVertical">D\ny\na\ns</string>
<string name="hoursVertical">H\no\nu\nr\ns</string>

@ -62,10 +62,6 @@
<!-- Time Constants -->
<!-- used for long due dates. c.f. Java's SimpleDateFormat -->
<string name="dateFormatter">d MMM</string>
<!-- used for alarms -->
<string name="alarmDateFormatter">M/dd HH:mm</string>
<!-- vertical labels are used in dialog boxes -->
<string name="daysVertical">D\na\ng\na\nr</string>
<string name="hoursVertical">H\no\nu\nr\ns</string>

@ -62,10 +62,6 @@
<!-- Time Constants -->
<!-- used for long due dates. c.f. Java's SimpleDateFormat -->
<string name="dateFormatter">d MMM</string>
<!-- used for alarms -->
<string name="alarmDateFormatter">dd-MM HH:mm</string>
<!-- vertical labels are used in dialog boxes -->
<string name="daysVertical">D\na\ny\ns</string>
<string name="hoursVertical">H\no\nu\nr\ns</string>

@ -62,10 +62,6 @@
<!-- Time Constants -->
<!-- used for long due dates. c.f. Java's SimpleDateFormat -->
<string name="dateFormatter">MMM d</string>
<!-- used for alarms -->
<string name="alarmDateFormatter">M/dd HH:mm</string>
<!-- vertical labels are used in dialog boxes -->
<string name="daysVertical"></string>
<string name="hoursVertical">小\n时</string>

@ -62,10 +62,6 @@
<!-- Time Constants -->
<!-- used for long due dates. c.f. Java's SimpleDateFormat -->
<string name="dateFormatter">MMM d</string>
<!-- used for alarms -->
<string name="alarmDateFormatter">M/dd HH:mm</string>
<!-- vertical labels are used in dialog boxes -->
<string name="daysVertical"></string>
<string name="hoursVertical">小\n時</string>

@ -62,10 +62,6 @@
<!-- Time Constants -->
<!-- used for long due dates. c.f. Java's SimpleDateFormat -->
<string name="dateFormatter">MMM d</string>
<!-- used for alarms -->
<string name="alarmDateFormatter">M/dd HH:mm</string>
<!-- vertical labels are used in dialog boxes -->
<string name="daysVertical">D\na\ny\ns</string>
<string name="hoursVertical">H\no\nu\nr\ns</string>

@ -18,7 +18,6 @@
package com.timsu.astrid.activities;
import java.text.Format;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
@ -333,7 +332,7 @@ public class TaskListAdapter extends ArrayAdapter<TaskModelForList> {
DateUtilities.getDurationString(r,
Math.abs(secondsLeft), 1));
else
finishedTime = DateUtilities.getFormattedDate(r,
finishedTime = DateUtilities.getFormattedDate(activity,
task.getCompletionDate());
label.append(r.getString(R.string.taskList_completedPrefix,
finishedTime));
@ -358,7 +357,7 @@ public class TaskListAdapter extends ArrayAdapter<TaskModelForList> {
label.append(DateUtilities.getDurationString(r,
(int)Math.abs(timeLeft), 1, true));
else
label.append(DateUtilities.getFormattedDate(r,
label.append(DateUtilities.getFormattedDate(activity,
task.getDefiniteDueDate()));
}
if(!taskOverdue && task.getPreferredDueDate() != null) {
@ -381,7 +380,7 @@ public class TaskListAdapter extends ArrayAdapter<TaskModelForList> {
label.append(DateUtilities.getDurationString(r,
(int)Math.abs(timeLeft), 1, true));
else
label.append(DateUtilities.getFormattedDate(r,
label.append(DateUtilities.getFormattedDate(activity,
task.getPreferredDueDate()));
}
}
@ -458,8 +457,7 @@ public class TaskListAdapter extends ArrayAdapter<TaskModelForList> {
if(label.length() > 0)
label.append(". ");
if(alarmFormat == null)
alarmFormat = new SimpleDateFormat(
r.getString(R.string.alarmDateFormatter));
alarmFormat = Preferences.getDateWithTimeFormat(activity);
String alarmString = alarmFormat.format(nextAlarm);
label.append(r.getString(R.string.taskList_alarmPrefix) +
" " + alarmString);

@ -42,8 +42,8 @@ public class Alert extends AbstractModel {
// field names
static final String TASK = "task";
static final String DATE = "date";
public static final String TASK = "task";
public static final String DATE = "date";
/** Default values container */
private static final ContentValues defaultValues = new ContentValues();

@ -73,7 +73,7 @@ public class BackupService extends Service {
exporter.exportTasks(backupDirectorySetting.getBackupDirectory());
Preferences.setBackupSummary(ctx,
ctx.getString(R.string.prefs_backup_desc_success,
DateUtilities.getFormattedDate(ctx.getResources(), new Date())));
DateUtilities.getFormattedDate(ctx, new Date())));
} catch (Exception e) {
// unable to backup.
if (e == null || e.getMessage() == null) {

@ -19,25 +19,27 @@
*/
package com.timsu.astrid.utilities;
import android.content.res.Resources;
import android.util.Log;
import com.timsu.astrid.R;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import android.content.Context;
import android.content.res.Resources;
import android.util.Log;
import com.timsu.astrid.R;
public class DateUtilities {
private static SimpleDateFormat format = null;
private static final String ISO_8601_FORMAT = "yyyy-MM-dd'T'HH:mm:ssz";
/** Format a time into a medium length absolute format */
public static String getFormattedDate(Resources r, Date date) {
public static String getFormattedDate(Context context, Date date) {
if(format == null)
format = new SimpleDateFormat(r.getString(R.string.dateFormatter));
format = Preferences.getDateFormat(context);
return format.format(date);
}

@ -148,33 +148,84 @@ public class Preferences {
editor.commit();
}
// --- date time strings and formatters
@SuppressWarnings("nls")
public static boolean is24HourFormat(Context context) {
String value = android.provider.Settings.System.getString(context.getContentResolver(),
android.provider.Settings.System.TIME_12_24);
android.provider.Settings.System.TIME_12_24);
boolean b24 = !(value == null || value.equals("12"));
return b24;
}
/**
* @return time format (hours and minutes)
*/
public static SimpleDateFormat getTimeFormat(Context context) {
String value = getTimeFormatString(context);
return new SimpleDateFormat(value);
}
/**
* @return string used for time formatting
*/
@SuppressWarnings("nls")
private static String getTimeFormatString(Context context) {
String value;
if (is24HourFormat(context)) {
value = "H:mm";
} else {
value = "h:mm a";
}
return value;
}
return new SimpleDateFormat(value);
/**
* @return string used for date formatting
*/
@SuppressWarnings("nls")
private static String getDateFormatString(Context context) {
String value = android.provider.Settings.System.getString(context.getContentResolver(),
android.provider.Settings.System.DATE_FORMAT);
if (value == null) {
// united states, you are special
if (Locale.US.equals(Locale.getDefault())
|| Locale.CANADA.equals(Locale.getDefault()))
value = "MMM d yyyy";
else
value = "d MMM yyyy";
}
return value;
}
/**
* @return date format (month, day, year)
*/
public static SimpleDateFormat getDateFormat(Context context) {
// united states, you are special
if(Locale.US.equals(Locale.getDefault()) ||
Locale.CANADA.equals(Locale.getDefault()))
return new SimpleDateFormat("EEE, MMM d yyyy");
else
return new SimpleDateFormat("EEE, d MMM yyyy");
return new SimpleDateFormat(getDateFormatString(context));
}
/**
* @return date format as getDateFormat with weekday
*/
@SuppressWarnings("nls")
public static SimpleDateFormat getDateFormatWithWeekday(Context context) {
return new SimpleDateFormat("EEE, " + getDateFormatString(context));
}
/**
* @return date with time at the end
*/
@SuppressWarnings("nls")
public static SimpleDateFormat getDateWithTimeFormat(Context context) {
return new SimpleDateFormat(getDateFormatString(context) + " " +
getTimeFormatString(context));
}
// --- notification settings
/** returns hour at which quiet hours start, or null if not set */

@ -76,7 +76,7 @@ public class StartupReceiver extends BroadcastReceiver {
}
Preferences.setCurrentVersion(context, finalVersion);
new UpgradeService().performUpgrade(latestSetVersion, version);
new UpgradeService().performUpgrade(latestSetVersion);
}

@ -47,7 +47,7 @@ public class DateControlSet implements OnTimeSetListener,
protected DateControlSet(Activity activity) {
this.activity = activity;
this.dateFormatter = Preferences.getDateFormat(this.activity);
this.dateFormatter = Preferences.getDateFormatWithWeekday(this.activity);
this.timeFormatter = Preferences.getTimeFormat(this.activity);
}

@ -9,6 +9,7 @@ import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
import com.todoroo.andlib.data.AbstractModel;
import com.todoroo.andlib.data.GenericDao;
import com.todoroo.andlib.data.sql.Criterion;
import com.todoroo.andlib.service.Autowired;
@ -126,14 +127,11 @@ public class TaskDao extends GenericDao<Task> {
boolean saveSuccessful;
if (task.getId() == Task.NO_ID) {
task.setValue(Task.CREATION_DATE, DateUtilities.now());
task.setValue(Task.MODIFICATION_DATE, DateUtilities.now());
saveSuccessful = createItem(task);
} else {
ContentValues values = task.getSetValues();
if(values.size() == 0)
return true;
task.setValue(Task.MODIFICATION_DATE, DateUtilities.now());
beforeSave(task, values, duringSync);
saveSuccessful = saveItem(task);
afterSave(task, values, duringSync);
@ -142,6 +140,20 @@ public class TaskDao extends GenericDao<Task> {
return saveSuccessful;
}
@Override
public boolean createItem(AbstractModel item) {
if(!item.containsValue(Task.CREATION_DATE))
item.setValue(Task.CREATION_DATE, DateUtilities.now());
item.setValue(Task.MODIFICATION_DATE, DateUtilities.now());
return super.createItem(item);
}
@Override
public boolean saveItem(AbstractModel item) {
item.setValue(Task.MODIFICATION_DATE, DateUtilities.now());
return super.saveItem(item);
}
/**
* Called before the task is saved.
* <ul>

@ -0,0 +1,305 @@
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 android.util.Log;
import com.timsu.astrid.data.AbstractController;
import com.timsu.astrid.data.alerts.Alert;
import com.timsu.astrid.data.task.AbstractTaskModel;
import com.todoroo.andlib.data.AbstractModel;
import com.todoroo.andlib.data.GenericDao;
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.andlib.service.DependencyInjectionService;
import com.todoroo.astrid.alarms.Alarm;
import com.todoroo.astrid.alarms.AlarmsDatabase;
import com.todoroo.astrid.dao.Database;
import com.todoroo.astrid.dao.MetadataDao;
import com.todoroo.astrid.dao.TaskDao;
import com.todoroo.astrid.model.Metadata;
import com.todoroo.astrid.model.Task;
import com.todoroo.astrid.tags.TagService;
public class Astrid2To3UpgradeHelper {
@Autowired
private TaskDao taskDao;
@Autowired
private MetadataDao metadataDao;
@Autowired
private MetadataService metadataService;
@Autowired
private Database database;
@Autowired
private String tasksTable;
@Autowired
private String tagsTable;
@Autowired
private String tagTaskTable;
@Autowired
private String alertsTable;
// --- implementation
public Astrid2To3UpgradeHelper() {
DependencyInjectionService.getInstance().inject(this);
}
/**
* Upgrade helper class that reads a database
*/
private static class Astrid2UpgradeHelper extends SQLiteOpenHelper {
public Astrid2UpgradeHelper(Context context, String name,
CursorFactory factory, int version) {
super(context, name, factory, version);
}
@Override
public void onCreate(SQLiteDatabase db) {
// do nothing
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
// do nothing
}
}
/**
* Perform the upgrade from Astrid 2 to Astrid 3
*/
public void upgrade2To3() {
Context context = ContextManager.getContext();
database.openForWriting();
// --- upgrade tasks table
HashMap<String, Property<?>> propertyMap =
new HashMap<String, Property<?>>();
propertyMap.put(AbstractController.KEY_ROWID, Task.ID);
propertyMap.put(AbstractTaskModel.NAME, Task.TITLE);
propertyMap.put(AbstractTaskModel.NOTES, Task.NOTES);
// 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);
upgradeTable(context, tasksTable,
propertyMap, new Task(), taskDao);
// --- upgrade tags tables
migrateTagsToMetadata();
// --- upgrade alerts
AlarmsDatabase alarmsDatabase = new AlarmsDatabase();
alarmsDatabase.openForWriting();
propertyMap.clear();
propertyMap.put(AbstractController.KEY_ROWID, Alarm.ID);
propertyMap.put(Alert.TASK, Alarm.TASK);
propertyMap.put(Alert.DATE, Alarm.TIME);
upgradeTable(context, alertsTable, propertyMap, new Alarm(),
alarmsDatabase.getDao());
alarmsDatabase.close();
// --- upgrade RTM sync mappings (?)
// --- clean up database
metadataService.cleanup();
database.close();
}
// --- database upgrade helpers
protected static final 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 <tim@todoroo.com>
*
*/
@SuppressWarnings("nls")
protected static final class ColumnUpgradeVisitor implements PropertyVisitor<Void, UpgradeVisitorContainer> {
@Override
public Void visitDouble(Property<Double> property, UpgradeVisitorContainer data) {
double value = data.cursor.getDouble(data.columnIndex);
data.model.setValue(property, value);
Log.d("upgrade", "wrote " + value + " to -> " + property + " of model id " + data.cursor.getLong(1));
return null;
}
@Override
public Void visitInteger(Property<Integer> 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 ||
property == Alarm.TIME)
value = (int) (data.cursor.getLong(data.columnIndex) / 1000L);
else
value = data.cursor.getInt(data.columnIndex);
data.model.setValue(property, value);
Log.d("upgrade", "wrote " + value + " to -> " + property + " of model id " + data.cursor.getLong(1));
return null;
}
@Override
public Void visitLong(Property<Long> property, UpgradeVisitorContainer data) {
long value = data.cursor.getLong(data.columnIndex);
data.model.setValue(property, value);
Log.d("upgrade", "wrote " + value + " to -> " + property + " of model id " + data.cursor.getLong(1));
return null;
}
@Override
public Void visitString(Property<String> property, UpgradeVisitorContainer data) {
String value = data.cursor.getString(data.columnIndex);
data.model.setValue(property, value);
Log.d("upgrade", "wrote " + value + " to -> " + property + " of model id " + data.cursor.getLong(1));
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 static final <TYPE extends AbstractModel> void upgradeTable(Context context, String legacyTable,
HashMap<String, Property<?>> propertyMap, TYPE model,
GenericDao<TYPE> dao) {
if(!checkIfDatabaseExists(context, legacyTable))
return;
SQLiteDatabase upgradeDb = new Astrid2UpgradeHelper(context, legacyTable,
null, 1).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<String, Property<?>> entry : propertyMap.entrySet()) {
container.columnIndex = cursor.getColumnIndex(entry.getKey());
entry.getValue().accept(visitor, container);
}
dao.createItem(container.model);
}
upgradeDb.close();
}
private static boolean checkIfDatabaseExists(Context context, String legacyTable) {
return context.getDatabasePath(legacyTable).exists();
}
/**
* Move data from tags tables into metadata table. We do this by looping
* through both the tags and tagTaskMap databases, reading data from
* both and adding to the Metadata table. This way, we are able to
* do everything in one pass without loading too much into memory
*/
@SuppressWarnings("nls")
private void migrateTagsToMetadata() {
Context context = ContextManager.getContext();
if(!checkIfDatabaseExists(context, tagsTable) ||
!checkIfDatabaseExists(context, tagTaskTable))
return;
SQLiteDatabase tagsDb = new Astrid2UpgradeHelper(context, tagsTable,
null, 1).getReadableDatabase();
SQLiteDatabase tagTaskDb = new Astrid2UpgradeHelper(context, tagTaskTable,
null, 1).getReadableDatabase();
Cursor tagCursor = tagsDb.rawQuery("SELECT _id, name FROM " + tagsTable +
" ORDER BY _id ASC", null);
Cursor mapCursor = tagTaskDb.rawQuery("SELECT tag, task FROM " + tagTaskTable +
" ORDER BY tag ASC", null);
if(tagCursor.getCount() == 0)
return;
Metadata metadata = new Metadata();
metadata.setValue(Metadata.KEY, TagService.KEY);
long tagId = -1;
String tag = null;
for(mapCursor.moveToFirst(); !mapCursor.isAfterLast(); mapCursor.moveToNext()) {
long mapTagId = mapCursor.getLong(0);
while(mapTagId > tagId && !tagCursor.isLast()) {
tagCursor.moveToNext();
tagId = tagCursor.getLong(0);
tag = null;
}
if(mapTagId == tagId) {
if(tag == null)
tag = tagCursor.getString(1);
long task = mapCursor.getLong(1);
metadata.setValue(Metadata.TASK, task);
metadata.setValue(Metadata.KEY, TagService.KEY);
metadata.setValue(Metadata.VALUE, tag);
metadataDao.createItem(metadata);
metadata.clearValue(Metadata.ID);
}
}
tagCursor.close();
mapCursor.close();
}
}

@ -68,11 +68,9 @@ public class MetadataService {
* @param onlyCountsGreaterThanZero only include items where count > 0
*/
public TodorooCursor<Metadata> fetchWithCount(CountProperty count,
Criterion where, Order order, boolean onlyCountsGreaterThanZero) {
Query query = Query.select(Metadata.VALUE, count).
where(where).orderBy(order);
if(onlyCountsGreaterThanZero)
query.having(count.gt(0));
Criterion where, Order order) {
Query query = Query.select(Metadata.VALUE.as(Metadata.VALUE.name), count).
where(where).orderBy(order).groupBy(Metadata.VALUE);
TodorooCursor<Metadata> cursor = metadataDao.query(query);
return cursor;
}

@ -1,61 +1,15 @@
package com.todoroo.astrid.service;
import java.util.HashMap;
import java.util.Map.Entry;
import android.app.Activity;
import android.app.AlertDialog;
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 android.util.Log;
import android.webkit.WebView;
import com.timsu.astrid.R;
import com.timsu.astrid.data.AbstractController;
import com.timsu.astrid.data.task.AbstractTaskModel;
import com.todoroo.andlib.data.AbstractModel;
import com.todoroo.andlib.data.GenericDao;
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.andlib.service.DependencyInjectionService;
import com.todoroo.astrid.dao.Database;
import com.todoroo.astrid.dao.MetadataDao;
import com.todoroo.astrid.dao.TaskDao;
import com.todoroo.astrid.model.Metadata;
import com.todoroo.astrid.model.Task;
import com.todoroo.astrid.tags.DataService;
public final class UpgradeService {
@Autowired
private TaskDao taskDao;
@Autowired
private MetadataDao metadataDao;
@Autowired
private Database database;
@Autowired
private String tasksTable;
@Autowired
private String tagsTable;
@Autowired
private String tagTaskTable;
// --- implementation
public UpgradeService() {
DependencyInjectionService.getInstance().inject(this);
}
/**
* 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
@ -64,12 +18,12 @@ public final class UpgradeService {
* @param from
* @param to
*/
public void performUpgrade(int from, int to) {
if(from >= to || from < 1)
public void performUpgrade(int from) {
if(from < 1)
return;
// display changelog
showChangeLog(from, to);
showChangeLog(from);
}
/**
@ -81,7 +35,10 @@ public final class UpgradeService {
* @return
*/
@SuppressWarnings("nls")
public void showChangeLog(int from, int to) {
public void showChangeLog(int from) {
if(!(ContextManager.getContext() instanceof Activity))
return;
StringBuilder changeLog = new StringBuilder();
if(from <= 130)
@ -138,224 +95,4 @@ public final class UpgradeService {
// --- database upgrade logic
/**
* Upgrade helper class that reads a database
*/
private static class Astrid2UpgradeHelper extends SQLiteOpenHelper {
public Astrid2UpgradeHelper(Context context, String name,
CursorFactory factory, int version) {
super(context, name, factory, version);
}
@Override
public void onCreate(SQLiteDatabase db) {
// do nothing
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
// do nothing
}
}
/**
* Perform the upgrade from Astrid 2 to Astrid 3
*/
public void upgrade2To3() {
Context context = ContextManager.getContext();
database.openForWriting();
// --- upgrade tasks table
HashMap<String, Property<?>> propertyMap =
new HashMap<String, Property<?>>();
propertyMap.put(AbstractController.KEY_ROWID, Task.ID);
propertyMap.put(AbstractTaskModel.NAME, Task.TITLE);
propertyMap.put(AbstractTaskModel.NOTES, Task.NOTES);
// 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);
upgradeTable(context, tasksTable,
propertyMap, new Task(), taskDao);
// --- upgrade tags tables
migrateTagsToMetadata();
database.close();
}
// --- database upgrade helpers
protected static final 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 <tim@todoroo.com>
*
*/
@SuppressWarnings("nls")
protected static final class ColumnUpgradeVisitor implements PropertyVisitor<Void, UpgradeVisitorContainer> {
@Override
public Void visitDouble(Property<Double> property, UpgradeVisitorContainer data) {
double value = data.cursor.getDouble(data.columnIndex);
data.model.setValue(property, value);
Log.d("upgrade", "wrote " + value + " to -> " + property + " of model id " + data.cursor.getLong(1));
return null;
}
@Override
public Void visitInteger(Property<Integer> 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);
Log.d("upgrade", "wrote " + value + " to -> " + property + " of model id " + data.cursor.getLong(1));
return null;
}
@Override
public Void visitLong(Property<Long> property, UpgradeVisitorContainer data) {
long value = data.cursor.getLong(data.columnIndex);
data.model.setValue(property, value);
Log.d("upgrade", "wrote " + value + " to -> " + property + " of model id " + data.cursor.getLong(1));
return null;
}
@Override
public Void visitString(Property<String> property, UpgradeVisitorContainer data) {
String value = data.cursor.getString(data.columnIndex);
data.model.setValue(property, value);
Log.d("upgrade", "wrote " + value + " to -> " + property + " of model id " + data.cursor.getLong(1));
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 static final <TYPE extends AbstractModel> void upgradeTable(Context context, String legacyTable,
HashMap<String, Property<?>> propertyMap, TYPE model,
GenericDao<TYPE> dao) {
if(!checkIfDatabaseExists(context, legacyTable))
return;
SQLiteDatabase upgradeDb = new Astrid2UpgradeHelper(context, legacyTable,
null, 1).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<String, Property<?>> entry : propertyMap.entrySet()) {
container.columnIndex = cursor.getColumnIndex(entry.getKey());
entry.getValue().accept(visitor, container);
}
dao.createItem(container.model);
}
upgradeDb.close();
}
private static boolean checkIfDatabaseExists(Context context, String legacyTable) {
return context.getDatabasePath(legacyTable).exists();
}
/**
* Move data from tags tables into metadata table. We do this by looping
* through both the tags and tagTaskMap databases, reading data from
* both and adding to the Metadata table. This way, we are able to
* do everything in one pass without loading too much into memory
*/
@SuppressWarnings("nls")
private void migrateTagsToMetadata() {
Context context = ContextManager.getContext();
if(!checkIfDatabaseExists(context, tagsTable) ||
!checkIfDatabaseExists(context, tagTaskTable))
return;
SQLiteDatabase tagsDb = new Astrid2UpgradeHelper(context, tagsTable,
null, 1).getReadableDatabase();
SQLiteDatabase tagTaskDb = new Astrid2UpgradeHelper(context, tagTaskTable,
null, 1).getReadableDatabase();
Cursor tagCursor = tagsDb.rawQuery("SELECT _id, name FROM " + tagsTable +
" ORDER BY _id ASC", null);
Cursor mapCursor = tagTaskDb.rawQuery("SELECT tag, task FROM " + tagTaskTable +
" ORDER BY tag ASC", null);
if(tagCursor.getCount() == 0)
return;
Metadata metadata = new Metadata();
metadata.setValue(Metadata.KEY, DataService.KEY);
long tagId = -1;
String tag = null;
for(mapCursor.moveToFirst(); !mapCursor.isAfterLast(); mapCursor.moveToNext()) {
long mapTagId = mapCursor.getLong(1);
while(mapTagId > tagId && !tagCursor.isLast()) {
tagCursor.moveToNext();
tagId = tagCursor.getLong(1);
}
if(mapTagId == tagId) {
if(tag == null)
tag = tagCursor.getString(2);
long task = mapCursor.getLong(2);
metadata.setValue(Metadata.TASK, task);
metadata.setValue(Metadata.VALUE, tag);
metadataDao.createItem(metadata);
}
}
tagCursor.close();
mapCursor.close();
}
}

@ -4,6 +4,7 @@ import android.test.AndroidTestCase;
import com.todoroo.andlib.service.ContextManager;
import com.todoroo.andlib.service.DependencyInjectionService;
import com.todoroo.astrid.service.AstridDependencyInjector;
/**
* Base test case for Astrid tests
@ -13,6 +14,10 @@ import com.todoroo.andlib.service.DependencyInjectionService;
*/
public class TodorooTestCase extends AndroidTestCase {
static {
AstridDependencyInjector.initialize();
}
public TodorooTestCase() {
DependencyInjectionService.getInstance().inject(this);
}

@ -20,8 +20,7 @@ public class AstridTranslationTests extends TranslationTests {
@Override
public int[] getDateFormatStrings() {
return new int[] {
R.string.dateFormatter,
R.string.alarmDateFormatter,
//
};
}

@ -2,10 +2,10 @@ package com.todoroo.astrid.test;
import java.io.File;
import com.todoroo.andlib.service.Autowired;
import com.todoroo.andlib.service.DependencyInjectionService;
import com.todoroo.andlib.service.TestDependencyInjector;
import com.todoroo.andlib.test.TodorooTestCase;
import com.todoroo.astrid.alarms.AlarmsDatabase;
import com.todoroo.astrid.dao.Database;
import com.todoroo.astrid.service.AstridDependencyInjector;
@ -23,8 +23,9 @@ public class DatabaseTestCase extends TodorooTestCase {
private static final String TAGS_TEST = "tagstest";
private static final String TASKS_TEST = "taskstest";
@Autowired
public Database database;
public static Database database = new TestDatabase();
public AlarmsDatabase alarmsDatabase;
static {
AstridDependencyInjector.initialize();
@ -36,7 +37,7 @@ public class DatabaseTestCase extends TodorooTestCase {
injector.addInjectable("tagTaskTable", TAG_TASK_TEST);
injector.addInjectable("alertsTable", ALERTS_TEST);
injector.addInjectable("syncTable", SYNC_TEST);
injector.addInjectable("database", new TestDatabase());
injector.addInjectable("database", database);
}
@Override
@ -46,12 +47,15 @@ public class DatabaseTestCase extends TodorooTestCase {
DependencyInjectionService.getInstance().inject(this);
// empty out test databases
database.clear();
database.clear();
deleteDatabase(TASKS_TEST);
deleteDatabase(TAGS_TEST);
deleteDatabase(TAG_TASK_TEST);
deleteDatabase(ALERTS_TEST);
deleteDatabase(SYNC_TEST);
alarmsDatabase = new AlarmsDatabase();
alarmsDatabase.clear();
database.openForWriting();
}
@ -68,11 +72,16 @@ public class DatabaseTestCase extends TodorooTestCase {
}
public static class TestDatabase extends Database {
private static final String NAME = "databasetest";
@Override
protected String getName() {
return NAME;
return "databasetest";
}
}
public static class TestAlarmsDatabase extends AlarmsDatabase {
@Override
protected String getName() {
return "alarmstest";
}
}
}

@ -5,18 +5,21 @@ import java.util.Date;
import com.todoroo.andlib.data.TodorooCursor;
import com.todoroo.andlib.data.sql.Query;
import com.todoroo.andlib.service.Autowired;
import com.todoroo.astrid.alarms.Alarm;
import com.todoroo.astrid.dao.TaskDao;
import com.todoroo.astrid.legacy.data.alerts.AlertController;
import com.todoroo.astrid.legacy.data.enums.Importance;
import com.todoroo.astrid.legacy.data.enums.RepeatInterval;
import com.todoroo.astrid.legacy.data.tag.TagController;
import com.todoroo.astrid.legacy.data.tag.TagIdentifier;
import com.todoroo.astrid.legacy.data.task.TaskController;
import com.todoroo.astrid.legacy.data.task.TaskIdentifier;
import com.todoroo.astrid.legacy.data.task.TaskModelForEdit;
import com.todoroo.astrid.legacy.data.task.AbstractTaskModel.RepeatInfo;
import com.todoroo.astrid.model.Task;
import com.todoroo.astrid.service.UpgradeService;
import com.todoroo.astrid.tags.DataService;
import com.todoroo.astrid.tags.DataService.Tag;
import com.todoroo.astrid.service.Astrid2To3UpgradeHelper;
import com.todoroo.astrid.tags.TagService;
import com.todoroo.astrid.tags.TagService.Tag;
import com.todoroo.astrid.test.DatabaseTestCase;
public class Astrid2To3UpgradeTests extends DatabaseTestCase {
@ -25,7 +28,7 @@ public class Astrid2To3UpgradeTests extends DatabaseTestCase {
TaskDao taskDao;
public void upgrade2To3() {
new UpgradeService().performUpgrade(130, 150);
new Astrid2To3UpgradeHelper().upgrade2To3();
}
public static void assertDatesEqual(Date old, int newDate) {
@ -35,6 +38,9 @@ public class Astrid2To3UpgradeTests extends DatabaseTestCase {
assertEquals(old.getTime() / 1000L, newDate);
}
/**
* Test upgrade doesn't crash and burn when there is nothing
*/
public void testEmptyUpgrade() {
TaskController taskController = new TaskController(getContext());
taskController.open();
@ -44,10 +50,14 @@ public class Astrid2To3UpgradeTests extends DatabaseTestCase {
taskController.close();
upgrade2To3();
database.openForReading();
TodorooCursor<Task> tasks = taskDao.query(Query.select(Task.PROPERTIES));
assertEquals(0, tasks.getCount());
}
/**
* Test various parameters of tasks table
*/
public void testTaskTableUpgrade() {
TaskController taskController = new TaskController(getContext());
taskController.open();
@ -106,7 +116,9 @@ public class Astrid2To3UpgradeTests extends DatabaseTestCase {
assertDatesEqual(createdDate, task.getValue(Task.MODIFICATION_DATE));
}
/**
* Test basic upgrading of tags
*/
public void testTagTableUpgrade() {
TaskController taskController = new TaskController(getContext());
taskController.open();
@ -135,27 +147,113 @@ public class Astrid2To3UpgradeTests extends DatabaseTestCase {
// verify that data exists in our new table
database.openForReading();
DataService tagService = new DataService(getContext());
Tag[] tags = tagService.getGroupedTags(DataService.GROUPED_TAGS_BY_ALPHA);
TagService tagService = new TagService(getContext());
Tag[] tags = tagService.getGroupedTags(TagService.GROUPED_TAGS_BY_ALPHA);
assertEquals(2, tags.length);
assertEquals("salty", tags[0].tag);
assertEquals("tasty", tags[0].tag);
assertEquals("tasty", tags[1].tag);
// verify that tags are applied correctly
TodorooCursor<Task> tasks = taskDao.query(Query.select(Task.PROPERTIES));
assertEquals(3, tasks.getCount());
tasks.moveToFirst();
Task task = new Task(tasks);
assertEquals("tasty,salty", tagService.getTagsAsString(task.getId()));
assertEquals("tasty, salty", tagService.getTagsAsString(task.getId()));
tasks.moveToNext();
task.readFromCursor(tasks);
assertEquals("tasty", tagService.getTagsAsString(task.getId()));
tasks.moveToNext();
task.readFromCursor(tasks);
assertEquals("", tagService.getTagsAsString(task.getId()));
}
/**
* Test basic upgrading when tags point to deleted tasks
*/
public void testDanglingTagsUpgrade() {
TaskController taskController = new TaskController(getContext());
taskController.open();
TagController tagController = new TagController(getContext());
tagController.open();
// create some ish
TagIdentifier tag1 = tagController.createTag("dangling");
TagIdentifier tag2 = tagController.createTag("hanging");
TagIdentifier tag3 = tagController.createTag("attached");
TaskModelForEdit cliff = new TaskModelForEdit();
cliff.setName("cliff");
taskController.saveTask(cliff, false);
TaskModelForEdit water = new TaskModelForEdit();
water.setName("water");
taskController.saveTask(water, false);
// fake task identifiers
tagController.addTag(new TaskIdentifier(10), tag1);
tagController.addTag(new TaskIdentifier(15), tag2);
tagController.addTag(cliff.getTaskIdentifier(), tag3);
// assert created
assertEquals(3, tagController.getAllTags().size());
// upgrade
upgrade2To3();
// verify that data exists in our new table
database.openForReading();
TagService tagService = new TagService(getContext());
Tag[] tags = tagService.getGroupedTags(TagService.GROUPED_TAGS_BY_ALPHA);
assertEquals(1, tags.length);
assertEquals("attached", tags[0].tag);
// verify that tags are applied correctly
TodorooCursor<Task> tasks = taskDao.query(Query.select(Task.PROPERTIES));
assertEquals(2, tasks.getCount());
tasks.moveToFirst();
Task task = new Task(tasks);
assertEquals("attached", tagService.getTagsAsString(task.getId()));
tasks.moveToNext();
task.readFromCursor(tasks);
assertEquals("", tagService.getTagsAsString(task.getId()));
}
/**
* Test basic upgrading of alerts table
*/
public void testAlertTableUpgrade() {
TaskController taskController = new TaskController(getContext());
taskController.open();
AlertController alertController = new AlertController(getContext());
alertController.open();
// create some ish
TaskModelForEdit christmas = new TaskModelForEdit();
taskController.saveTask(christmas, false);
Date x1 = new Date(90,11,25);
Date x2 = new Date(91,11,25);
alertController.addAlert(christmas.getTaskIdentifier(), x1);
alertController.addAlert(christmas.getTaskIdentifier(), x2);
// assert created
assertEquals(2, alertController.getTaskAlerts(christmas.getTaskIdentifier()).size());
// upgradeia32-sun-java6-bin
upgrade2To3();
// verify that data exists in our new table
database.openForReading();
alarmsDatabase.openForReading();
TodorooCursor<Alarm> cursor = alarmsDatabase.getDao().query(Query.select(Alarm.TIME));
assertEquals(2, cursor.getCount());
cursor.moveToFirst();
Alarm alarm = new Alarm(cursor);
assertDatesEqual(x1, alarm.getValue(Alarm.TIME));
cursor.moveToNext();
alarm.readFromCursor(cursor);
assertDatesEqual(x2, alarm.getValue(Alarm.TIME));
}
}

Loading…
Cancel
Save