Upgrade for repeats, and repeat tests. also reorganized some imports on accident

pull/14/head
Tim Su 14 years ago
parent 21ea97ed34
commit 82e734d986

@ -7,10 +7,10 @@
<classpathentry kind="src" path="plugin-src"/>
<classpathentry kind="src" path="gen"/>
<classpathentry kind="con" path="com.android.ide.eclipse.adt.ANDROID_FRAMEWORK"/>
<classpathentry kind="lib" path="lib/commons-codec-1.3.jar"/>
<classpathentry kind="lib" path="lib/locale_platform.jar"/>
<classpathentry kind="lib" path="lib/FlurryAgent.jar"/>
<classpathentry kind="lib" path="lib/rfc2445-no-joda.jar" sourcepath="lib/rfc2445-source.zip">
<classpathentry exported="true" kind="lib" path="lib/commons-codec-1.3.jar"/>
<classpathentry exported="true" kind="lib" path="lib/locale_platform.jar"/>
<classpathentry exported="true" kind="lib" path="lib/FlurryAgent.jar"/>
<classpathentry exported="true" kind="lib" path="lib/rfc2445-no-joda.jar" sourcepath="lib/rfc2445-source.zip">
<attributes>
<attribute name="javadoc_location" value="http://google-rfc-2445.googlecode.com/svn/trunk/snapshot/docs"/>
</attributes>

@ -11,7 +11,7 @@
<intAttribute key="com.android.ide.eclipse.adt.delay" value="0"/>
<booleanAttribute key="com.android.ide.eclipse.adt.nobootanim" value="true"/>
<intAttribute key="com.android.ide.eclipse.adt.speed" value="0"/>
<booleanAttribute key="com.android.ide.eclipse.adt.target" value="false"/>
<booleanAttribute key="com.android.ide.eclipse.adt.target" value="true"/>
<booleanAttribute key="com.android.ide.eclipse.adt.wipedata" value="false"/>
<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_PATHS">
<listEntry value="/astrid"/>

@ -9,9 +9,9 @@ import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteDatabase.CursorFactory;
import android.database.sqlite.SQLiteException;
import android.database.sqlite.SQLiteOpenHelper;
import android.database.sqlite.SQLiteDatabase.CursorFactory;
import android.util.Log;
import com.todoroo.andlib.data.Property.PropertyVisitor;

@ -262,8 +262,12 @@ public abstract class AbstractModel implements Parcelable {
* @param property
*/
public synchronized void clearValue(Property<?> property) {
if(setValues != null)
if(setValues != null && setValues.containsKey(property.name))
setValues.remove(property.name);
else if(values != null && values.containsKey(property.name))
values.remove(property.name);
else if(getDefaultValues().containsKey(property.name))
throw new IllegalArgumentException("Property has a default value"); //$NON-NLS-1$
}
// --- property management

@ -3,6 +3,12 @@ package com.todoroo.andlib.service;
import java.lang.reflect.Field;
import java.util.Arrays;
import android.util.Log;
import com.todoroo.astrid.utility.Constants;
/**
* Simple Dependency Injection Service for Android.
* <p>
@ -33,6 +39,10 @@ public class DependencyInjectionService {
@SuppressWarnings("nls")
public void inject(Object caller) {
if(Constants.DEBUG) {
Log.d("INJECTOR", "Invoked Injector on " + caller, new Throwable());
}
// Traverse through class and all parent classes, looking for
// fields declared with the @Autowired annotation and using
// dependency injection to set them as appropriate
@ -92,6 +102,8 @@ public class DependencyInjectionService {
for (AbstractDependencyInjector injector : injectors) {
Object injection = injector.getInjection(caller, field);
if (injection != null) {
if(Constants.DEBUG)
Log.e("INJECTOR", injector + ":" + caller + "." + field.getName() + " => " + injection);
field.set(caller, injection);
return;
}

@ -1,7 +1,7 @@
package com.todoroo.andlib.sql;
import java.util.List;
import java.util.ArrayList;
import java.util.List;
public class GroupBy {
private List<Field> fields = new ArrayList<Field>();

@ -23,8 +23,8 @@ import java.text.Format;
import java.util.Date;
import android.app.DatePickerDialog;
import android.app.TimePickerDialog;
import android.app.DatePickerDialog.OnDateSetListener;
import android.app.TimePickerDialog;
import android.app.TimePickerDialog.OnTimeSetListener;
import android.content.Context;
import android.text.format.DateFormat;

@ -29,9 +29,9 @@ import android.content.DialogInterface.OnClickListener;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.FrameLayout.LayoutParams;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.FrameLayout.LayoutParams;
import com.todoroo.andlib.service.Autowired;
import com.todoroo.andlib.service.DependencyInjectionService;

@ -8,11 +8,11 @@ import android.preference.CheckBoxPreference;
import android.preference.EditTextPreference;
import android.preference.ListPreference;
import android.preference.Preference;
import android.preference.Preference.OnPreferenceChangeListener;
import android.preference.PreferenceActivity;
import android.preference.PreferenceGroup;
import android.preference.PreferenceScreen;
import android.preference.RingtonePreference;
import android.preference.Preference.OnPreferenceChangeListener;
/**
* Displays a preference screen for users to edit their preferences. Override

@ -8,11 +8,11 @@ import android.content.ContentValues;
import com.todoroo.andlib.data.AbstractModel;
import com.todoroo.andlib.data.Property;
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.andlib.data.Property.StringProperty;
import com.todoroo.andlib.data.Table;
import com.todoroo.andlib.data.TodorooCursor;
import com.todoroo.astrid.model.Task;
/**

@ -4,8 +4,8 @@ import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import com.todoroo.astrid.api.AstridApiConstants;
import com.todoroo.astrid.api.Addon;
import com.todoroo.astrid.api.AstridApiConstants;
public class CorePlugin extends BroadcastReceiver {

@ -4,8 +4,8 @@ import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import com.todoroo.astrid.api.AstridApiConstants;
import com.todoroo.astrid.api.Addon;
import com.todoroo.astrid.api.AstridApiConstants;
public class ExtendedPlugin extends BroadcastReceiver {

@ -4,8 +4,8 @@ import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import com.todoroo.astrid.api.AstridApiConstants;
import com.todoroo.astrid.api.Addon;
import com.todoroo.astrid.api.AstridApiConstants;
public class NotesPlugin extends BroadcastReceiver {

@ -27,6 +27,9 @@ import com.timsu.astrid.R;
import com.timsu.astrid.widget.NumberPicker;
import com.timsu.astrid.widget.NumberPickerDialog;
import com.timsu.astrid.widget.NumberPickerDialog.OnNumberPickedListener;
import com.todoroo.andlib.service.Autowired;
import com.todoroo.andlib.service.DependencyInjectionService;
import com.todoroo.andlib.service.ExceptionService;
import com.todoroo.astrid.activity.TaskEditActivity.TaskEditControlSet;
import com.todoroo.astrid.model.Task;
@ -43,7 +46,7 @@ public class RepeatControlSet implements TaskEditControlSet {
private static final int INTERVAL_DAYS = 0;
private static final int INTERVAL_WEEKS = 1;
private static final int INTERVAL_MONTHS = 2;
private static final int INTERVAL_YEARS = 3;
private static final int INTERVAL_HOURS = 3;
private static final int TYPE_DUE_DATE = 0;
private static final int TYPE_COMPLETION_DATE = 1;
@ -59,9 +62,14 @@ public class RepeatControlSet implements TaskEditControlSet {
private final LinearLayout daysOfWeekContainer;
private final CompoundButton[] daysOfWeek = new CompoundButton[7];
@Autowired
ExceptionService exceptionService;
// --- implementation
public RepeatControlSet(final Activity activity, ViewGroup parent) {
DependencyInjectionService.getInstance().inject(this);
this.activity = activity;
LayoutInflater.from(activity).inflate(R.layout.repeat_control, parent, true);
@ -176,12 +184,17 @@ public class RepeatControlSet implements TaskEditControlSet {
case MONTHLY:
interval.setSelection(INTERVAL_MONTHS);
break;
case YEARLY:
interval.setSelection(INTERVAL_YEARS);
case HOURLY:
interval.setSelection(INTERVAL_HOURS);
break;
default:
// an unhandled recurrence
exceptionService.reportError("repeat-unhandled-rule", //$NON-NLS-1$
new Exception("Unhandled rrule frequency: " + recurrence)); //$NON-NLS-1$
}
} catch (ParseException e) {
recurrence = ""; //$NON-NLS-1$
exceptionService.reportError("repeat-parse-exception", e); //$NON-NLS-1$
}
}
enabled.setChecked(recurrence.length() > 0);
@ -219,8 +232,8 @@ public class RepeatControlSet implements TaskEditControlSet {
case INTERVAL_MONTHS:
rrule.setFreq(Frequency.MONTHLY);
break;
case INTERVAL_YEARS:
rrule.setFreq(Frequency.YEARLY);
case INTERVAL_HOURS:
rrule.setFreq(Frequency.HOURLY);
}
result = rrule.toIcal();
}

@ -13,9 +13,12 @@ import com.google.ical.iter.RecurrenceIteratorFactory;
import com.google.ical.values.DateTimeValueImpl;
import com.google.ical.values.DateValue;
import com.google.ical.values.DateValueImpl;
import com.google.ical.values.Frequency;
import com.google.ical.values.RRule;
import com.todoroo.andlib.service.Autowired;
import com.todoroo.andlib.service.DependencyInjectionService;
import com.todoroo.andlib.service.ExceptionService;
import com.todoroo.andlib.utility.DateUtilities;
import com.todoroo.astrid.api.AstridApiConstants;
import com.todoroo.astrid.model.Task;
import com.todoroo.astrid.service.TaskService;
@ -43,53 +46,62 @@ public class RepeatTaskCompleteListener extends BroadcastReceiver {
String recurrence = task.getValue(Task.RECURRENCE);
if(recurrence.length() > 0) {
Date date = new Date();
DateValue today = new DateValueImpl(date.getYear() + 1900,
date.getMonth() + 1, date.getDate());
DateValue repeatFrom;
Date repeatFromDate = new Date();
DateValue today = new DateValueImpl(repeatFromDate.getYear() + 1900,
repeatFromDate.getMonth() + 1, repeatFromDate.getDate());
if(task.hasDueDate() && !task.getFlag(Task.FLAGS, Task.FLAG_REPEAT_AFTER_COMPLETION)) {
date = new Date(task.getValue(Task.DUE_DATE));
repeatFrom = new DateTimeValueImpl(date.getYear() + 1900,
date.getMonth() + 1, date.getDate(),
date.getHours(), date.getMinutes(), date.getSeconds());
repeatFromDate = new Date(task.getValue(Task.DUE_DATE));
if(task.hasDueTime()) {
repeatFrom = new DateTimeValueImpl(repeatFromDate.getYear() + 1900,
repeatFromDate.getMonth() + 1, repeatFromDate.getDate(),
repeatFromDate.getHours(), repeatFromDate.getMinutes(), repeatFromDate.getSeconds());
} else {
repeatFrom = new DateValueImpl(repeatFromDate.getYear() + 1900,
repeatFromDate.getMonth() + 1, repeatFromDate.getDate());
}
} else {
repeatFrom = today;
}
// invoke the recurrence iterator
try {
RecurrenceIterator iterator = RecurrenceIteratorFactory.createRecurrenceIterator(recurrence,
repeatFrom, TimeZone.getDefault());
if(repeatFrom.compareTo(today) < 0)
repeatFrom = today;
// go to the latest value and advance one more
iterator.advanceTo(repeatFrom);
if(!iterator.hasNext())
return;
DateValue nextDate = iterator.next();
if(nextDate.equals(repeatFrom)) {
if(!iterator.hasNext())
return;
nextDate = iterator.next();
}
long newDueDate;
if(nextDate instanceof DateTimeValueImpl) {
DateTimeValueImpl newDateTime = (DateTimeValueImpl)nextDate;
RRule rrule = new RRule(recurrence);
if(rrule.getFreq() == Frequency.HOURLY) {
newDueDate = task.createDueDate(Task.URGENCY_SPECIFIC_DAY_TIME,
new Date(newDateTime.year() - 1900, newDateTime.month() - 1,
newDateTime.day(), newDateTime.hour(),
newDateTime.minute(), newDateTime.second()).getTime());
repeatFromDate.getTime() + DateUtilities.ONE_HOUR * rrule.getInterval());
} else {
newDueDate = task.createDueDate(Task.URGENCY_SPECIFIC_DAY,
new Date(nextDate.year() - 1900, nextDate.month() - 1,
nextDate.day()).getTime());
RecurrenceIterator iterator = RecurrenceIteratorFactory.createRecurrenceIterator(rrule,
repeatFrom, TimeZone.getDefault());
if(repeatFrom.compareTo(today) < 0)
repeatFrom = today;
// go to the latest value and advance one more if needed
iterator.advanceTo(repeatFrom);
if(!iterator.hasNext())
return;
DateValue nextDate = iterator.next();
if(nextDate.compareTo(today) == 0)
nextDate = iterator.next();
if(nextDate instanceof DateTimeValueImpl) {
DateTimeValueImpl newDateTime = (DateTimeValueImpl)nextDate;
newDueDate = task.createDueDate(Task.URGENCY_SPECIFIC_DAY_TIME,
new Date(newDateTime.year() - 1900, newDateTime.month() - 1,
newDateTime.day(), newDateTime.hour(),
newDateTime.minute(), newDateTime.second()).getTime());
} else {
newDueDate = task.createDueDate(Task.URGENCY_SPECIFIC_DAY,
new Date(nextDate.year() - 1900, nextDate.month() - 1,
nextDate.day()).getTime());
}
}
Task newTask = taskService.clone(task);
task = taskService.clone(task);
task.setValue(Task.DUE_DATE, newDueDate);
taskService.save(newTask, false);
task.setValue(Task.COMPLETION_DATE, 0L);
taskService.save(task, false);
} catch (ParseException e) {
exceptionService.reportError("recurrence-rule: " + recurrence, e); //$NON-NLS-1$
}

@ -5,8 +5,8 @@ import android.content.Context;
import android.content.Intent;
import com.timsu.astrid.R;
import com.todoroo.astrid.api.AstridApiConstants;
import com.todoroo.astrid.api.Addon;
import com.todoroo.astrid.api.AstridApiConstants;
public class RepeatsPlugin extends BroadcastReceiver {

@ -2,8 +2,8 @@ package com.todoroo.astrid.tags;
import java.util.ArrayList;
import com.todoroo.andlib.data.TodorooCursor;
import com.todoroo.andlib.data.Property.CountProperty;
import com.todoroo.andlib.data.TodorooCursor;
import com.todoroo.andlib.service.Autowired;
import com.todoroo.andlib.service.DependencyInjectionService;
import com.todoroo.andlib.sql.Criterion;

@ -4,8 +4,8 @@ import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import com.todoroo.astrid.api.AstridApiConstants;
import com.todoroo.astrid.api.Addon;
import com.todoroo.astrid.api.AstridApiConstants;
public class TagsPlugin extends BroadcastReceiver {

@ -24,7 +24,7 @@
<item>Day(s)</item>
<item>Week(s)</item>
<item>Month(s)</item>
<item>Year(s)</item>
<item>Hour(s)</item>
</string-array>
<string-array name="repeat_type">

@ -28,10 +28,10 @@ import com.mdt.rtm.data.RtmFrob;
import com.mdt.rtm.data.RtmList;
import com.mdt.rtm.data.RtmLists;
import com.mdt.rtm.data.RtmLocation;
import com.mdt.rtm.data.RtmTask.Priority;
import com.mdt.rtm.data.RtmTaskNote;
import com.mdt.rtm.data.RtmTaskSeries;
import com.mdt.rtm.data.RtmTasks;
import com.mdt.rtm.data.RtmTask.Priority;
/**
* Represents the Remember the Milk service API.

@ -23,8 +23,8 @@ import java.util.ArrayList;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.Map.Entry;
import java.util.Set;
import org.w3c.dom.Element;
@ -35,12 +35,12 @@ import com.mdt.rtm.data.RtmList;
import com.mdt.rtm.data.RtmLists;
import com.mdt.rtm.data.RtmLocation;
import com.mdt.rtm.data.RtmTask;
import com.mdt.rtm.data.RtmTask.Priority;
import com.mdt.rtm.data.RtmTaskList;
import com.mdt.rtm.data.RtmTaskNote;
import com.mdt.rtm.data.RtmTaskSeries;
import com.mdt.rtm.data.RtmTasks;
import com.mdt.rtm.data.RtmTimeline;
import com.mdt.rtm.data.RtmTask.Priority;
/**
* A major part of the RTM API implementation is here.

@ -22,6 +22,7 @@ package com.mdt.rtm.data;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.w3c.dom.Element;
/**

@ -10,10 +10,10 @@ import android.view.MenuItem;
import android.view.View;
import android.view.Window;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemSelectedListener;
import android.widget.ArrayAdapter;
import android.widget.Spinner;
import android.widget.TextView;
import android.widget.AdapterView.OnItemSelectedListener;
import com.flurry.android.FlurryAgent;
import com.timsu.astrid.R;

@ -27,8 +27,8 @@ import android.content.DialogInterface;
import android.content.res.Resources;
import android.os.Bundle;
import android.preference.Preference;
import android.preference.PreferenceActivity;
import android.preference.Preference.OnPreferenceChangeListener;
import android.preference.PreferenceActivity;
import android.view.KeyEvent;
import android.view.View;
import android.widget.Button;

@ -32,8 +32,8 @@ import android.database.sqlite.SQLiteDatabase;
import com.timsu.astrid.data.AbstractController;
import com.timsu.astrid.data.tag.AbstractTagModel.TagModelDatabaseHelper;
import com.timsu.astrid.data.tag.TagToTaskMapping.TagToTaskMappingDatabaseHelper;
import com.timsu.astrid.data.task.TaskIdentifier;
import com.timsu.astrid.data.task.AbstractTaskModel.TaskModelDatabaseHelper;
import com.timsu.astrid.data.task.TaskIdentifier;
import com.timsu.astrid.provider.TasksProvider;
/** Controller for Tag-related operations */

@ -1,16 +1,17 @@
package com.timsu.astrid.data.task;
import java.util.Date;
import java.util.HashMap;
import android.database.Cursor;
import android.util.Log;
import com.timsu.astrid.R;
import com.timsu.astrid.data.AbstractController;
import com.timsu.astrid.data.enums.Importance;
import com.timsu.astrid.data.enums.RepeatInterval;
import com.timsu.astrid.utilities.DateUtilities;
import java.util.Date;
import java.util.HashMap;
public class TaskModelForXml extends AbstractTaskModel {
static String[] FIELD_LIST = new String[] {

@ -24,8 +24,8 @@ import java.util.Date;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Map;
import java.util.StringTokenizer;
import java.util.Map.Entry;
import java.util.StringTokenizer;
import android.content.Context;
import android.content.Intent;
@ -38,15 +38,15 @@ import com.mdt.rtm.ApplicationInfo;
import com.mdt.rtm.ServiceException;
import com.mdt.rtm.ServiceImpl;
import com.mdt.rtm.ServiceInternalException;
import com.mdt.rtm.data.RtmAuth.Perms;
import com.mdt.rtm.data.RtmList;
import com.mdt.rtm.data.RtmLists;
import com.mdt.rtm.data.RtmTask;
import com.mdt.rtm.data.RtmTask.Priority;
import com.mdt.rtm.data.RtmTaskList;
import com.mdt.rtm.data.RtmTaskNote;
import com.mdt.rtm.data.RtmTaskSeries;
import com.mdt.rtm.data.RtmTasks;
import com.mdt.rtm.data.RtmAuth.Perms;
import com.mdt.rtm.data.RtmTask.Priority;
import com.timsu.astrid.R;
import com.timsu.astrid.activities.SyncLoginActivity;
import com.timsu.astrid.activities.SyncLoginActivity.SyncLoginCallback;

@ -28,9 +28,9 @@ import com.timsu.astrid.data.enums.RepeatInterval;
import com.timsu.astrid.data.tag.TagController;
import com.timsu.astrid.data.tag.TagIdentifier;
import com.timsu.astrid.data.tag.TagModelForView;
import com.timsu.astrid.data.task.AbstractTaskModel.RepeatInfo;
import com.timsu.astrid.data.task.TaskIdentifier;
import com.timsu.astrid.data.task.TaskModelForSync;
import com.timsu.astrid.data.task.AbstractTaskModel.RepeatInfo;
/** Representation of a task on a remote server. Your synchronization
* service should instantiate these, filling out every field (use null

@ -3,6 +3,7 @@ package com.timsu.astrid.utilities;
import android.app.Activity;
/** Astrid constants */
@Deprecated
public class Constants {
// application constants

@ -1,16 +1,17 @@
package com.timsu.astrid.utilities;
import java.io.File;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.res.Resources;
import com.timsu.astrid.R;
import com.timsu.astrid.widget.FilePickerBuilder;
import com.timsu.astrid.widget.NNumberPickerDialog;
import com.timsu.astrid.widget.NNumberPickerDialog.OnNNumberPickedListener;
import java.io.File;
public class DialogUtilities {
/**

@ -1,5 +1,13 @@
package com.timsu.astrid.utilities;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.util.Date;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlPullParserFactory;
import android.app.AlertDialog;
import android.app.ProgressDialog;
import android.content.Context;
@ -7,6 +15,7 @@ import android.content.DialogInterface;
import android.os.Handler;
import android.os.Looper;
import android.util.Log;
import com.timsu.astrid.R;
import com.timsu.astrid.data.AbstractController;
import com.timsu.astrid.data.alerts.AlertController;
@ -18,13 +27,6 @@ import com.timsu.astrid.data.tag.TagModelForView;
import com.timsu.astrid.data.task.TaskController;
import com.timsu.astrid.data.task.TaskIdentifier;
import com.timsu.astrid.data.task.TaskModelForXml;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlPullParserFactory;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.util.Date;
public class TasksXmlImporter {
public static final String TAG = "TasksXmlImporter";

@ -24,8 +24,8 @@ import java.util.Date;
import android.app.Activity;
import android.app.DatePickerDialog;
import android.app.TimePickerDialog;
import android.app.DatePickerDialog.OnDateSetListener;
import android.app.TimePickerDialog;
import android.app.TimePickerDialog.OnTimeSetListener;
import android.view.View;
import android.widget.Button;

@ -1,17 +1,18 @@
package com.timsu.astrid.widget;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.util.Log;
import com.timsu.astrid.R;
import java.io.File;
import java.io.FilenameFilter;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.util.Log;
import com.timsu.astrid.R;
public class FilePickerBuilder extends AlertDialog.Builder implements DialogInterface.OnClickListener {
public interface OnFilePickedListener {

@ -29,9 +29,9 @@ import android.content.DialogInterface.OnClickListener;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.FrameLayout.LayoutParams;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.FrameLayout.LayoutParams;
import com.timsu.astrid.R;

@ -13,9 +13,9 @@ import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.os.Bundle;
import android.preference.Preference;
import android.preference.Preference.OnPreferenceClickListener;
import android.preference.PreferenceCategory;
import android.preference.PreferenceScreen;
import android.preference.Preference.OnPreferenceClickListener;
import com.timsu.astrid.R;
import com.todoroo.andlib.service.Autowired;

@ -18,15 +18,15 @@ import android.graphics.drawable.BitmapDrawable;
import android.os.Bundle;
import android.os.Parcelable;
import android.view.ContextMenu;
import android.view.ContextMenu.ContextMenuInfo;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ContextMenu.ContextMenuInfo;
import android.widget.EditText;
import android.widget.ExpandableListView;
import android.widget.ExpandableListView.ExpandableListContextMenuInfo;
import android.widget.FrameLayout;
import android.widget.Toast;
import android.widget.ExpandableListView.ExpandableListContextMenuInfo;
import com.flurry.android.FlurryAgent;
import com.timsu.astrid.R;

@ -11,11 +11,11 @@ import android.content.res.Resources;
import android.database.Cursor;
import android.graphics.Paint;
import android.view.ContextMenu;
import android.view.ContextMenu.ContextMenuInfo;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.ContextMenu.ContextMenuInfo;
import android.view.View.OnCreateContextMenuListener;
import android.view.ViewGroup;
import android.widget.CheckBox;
import android.widget.CursorAdapter;
import android.widget.LinearLayout;

@ -134,9 +134,12 @@ public class TaskDao extends GenericDao<Task> {
* Saves the given task to the database.getDatabase(). Task must already
* exist. Returns true on success.
*
* @param duringSync whether this save occurs as part of a sync
* @param task
* @param skipHooks
* Whether pre and post hooks should run. This should be set
* to true if tasks are created as part of synchronization
*/
public boolean save(Task task, boolean duringSync) {
public boolean save(Task task, boolean skipHooks) {
boolean saveSuccessful;
if (task.getId() == Task.NO_ID) {
@ -145,9 +148,9 @@ public class TaskDao extends GenericDao<Task> {
ContentValues values = task.getSetValues();
if(values.size() == 0)
return true;
beforeSave(task, values, duringSync);
beforeSave(task, values, skipHooks);
saveSuccessful = saveExisting(task);
afterSave(task, values, duringSync);
afterSave(task, values, skipHooks);
}
if(saveSuccessful)

@ -8,10 +8,10 @@ import android.content.ContentValues;
import com.todoroo.andlib.data.AbstractModel;
import com.todoroo.andlib.data.Property;
import com.todoroo.andlib.data.Table;
import com.todoroo.andlib.data.TodorooCursor;
import com.todoroo.andlib.data.Property.LongProperty;
import com.todoroo.andlib.data.Property.StringProperty;
import com.todoroo.andlib.data.Table;
import com.todoroo.andlib.data.TodorooCursor;
/**
* Data Model which represents a piece of metadata associated with a task

@ -16,9 +16,12 @@ import android.database.sqlite.SQLiteDatabase.CursorFactory;
import android.database.sqlite.SQLiteOpenHelper;
import android.util.Log;
import com.google.ical.values.Frequency;
import com.google.ical.values.RRule;
import com.timsu.astrid.R;
import com.timsu.astrid.data.AbstractController;
import com.timsu.astrid.data.alerts.Alert;
import com.timsu.astrid.data.enums.RepeatInterval;
import com.timsu.astrid.data.task.AbstractTaskModel;
import com.timsu.astrid.utilities.TasksXmlExporter;
import com.todoroo.andlib.data.AbstractModel;
@ -141,7 +144,7 @@ public class Astrid2To3UpgradeHelper {
propertyMap.put(AbstractTaskModel.NOTIFICATIONS, Task.REMINDER_PERIOD);
propertyMap.put(AbstractTaskModel.NOTIFICATION_FLAGS, Task.REMINDER_FLAGS);
propertyMap.put(AbstractTaskModel.LAST_NOTIFIED, Task.REMINDER_LAST);
// propertyMap.put(AbstractTaskModel.REPEAT, Task.REPEAT); // TODO
propertyMap.put(AbstractTaskModel.REPEAT, Task.RECURRENCE);
propertyMap.put(AbstractTaskModel.CREATION_DATE, Task.CREATION_DATE);
propertyMap.put(AbstractTaskModel.COMPLETION_DATE, Task.COMPLETION_DATE);
propertyMap.put(AbstractTaskModel.CALENDAR_URI, Task.CALENDAR_URI);
@ -193,6 +196,38 @@ public class Astrid2To3UpgradeHelper {
public StringBuilder upgradeNotes;
}
/** Legacy repeatInfo class */
private static class RepeatInfo {
public static final int REPEAT_VALUE_OFFSET = 3;
private final RepeatInterval interval;
private final int value;
public RepeatInfo(RepeatInterval repeatInterval, int value) {
this.interval = repeatInterval;
this.value = value;
}
public RepeatInterval getInterval() {
return interval;
}
public int getValue() {
return value;
}
public static RepeatInfo fromSingleField(int 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);
}
}
/**
* Visitor that reads from a visitor container and writes to the model
* @author Tim Su <tim@todoroo.com>
@ -247,7 +282,29 @@ public class Astrid2To3UpgradeHelper {
@Override
public Void visitString(Property<String> property, UpgradeVisitorContainer<?> data) {
String value = data.cursor.getString(data.columnIndex);
data.model.setValue(property, value);
if(property == Task.RECURRENCE) {
RepeatInfo repeatInfo = RepeatInfo.fromSingleField(data.cursor.getInt(data.columnIndex));
RRule rrule = new RRule();
rrule.setInterval(repeatInfo.getValue());
switch(repeatInfo.getInterval()) {
case DAYS:
rrule.setFreq(Frequency.DAILY);
break;
case WEEKS:
rrule.setFreq(Frequency.WEEKLY);
break;
case MONTHS:
rrule.setFreq(Frequency.MONTHLY);
break;
case HOURS:
rrule.setFreq(Frequency.HOURLY);
}
data.model.setValue(property, rrule.toIcal());
} else {
data.model.setValue(property, value);
}
Log.d("upgrade", "wrote " + value + " to -> " + property + " of model id " + data.cursor.getLong(1));
return null;
}

@ -8,8 +8,8 @@ import android.app.AlertDialog;
import android.app.PendingIntent;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.DialogInterface.OnClickListener;
import android.content.Intent;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;

@ -63,12 +63,12 @@ public class TaskService {
* Create or save the given action item
*
* @param item
* @param isDuringSync
* Whether this operation is invoked from synchronizer. This
* determines which pre and post save hooks get run
* @param skipHooks
* Whether pre and post hooks should run. This should be set
* to true if tasks are created as part of synchronization
*/
public boolean save(Task item, boolean isDuringSync) {
return taskDao.save(item, isDuringSync);
public boolean save(Task item, boolean runHooks) {
return taskDao.save(item, runHooks);
}
/**
@ -84,13 +84,15 @@ public class TaskService {
TodorooCursor<Metadata> cursor = metadataDao.query(
Query.select(Metadata.PROPERTIES).where(MetadataCriteria.byTask(task.getId())));
try {
Metadata metadata = new Metadata();
long newId = newTask.getId();
for(cursor.moveToFirst(); !cursor.isAfterLast(); cursor.moveToNext()) {
metadata.readFromCursor(cursor);
metadata.setValue(Metadata.TASK, newId);
metadata.clearValue(Metadata.ID);
metadataDao.createNew(metadata);
if(cursor.getCount() > 0) {
Metadata metadata = new Metadata();
long newId = newTask.getId();
for(cursor.moveToFirst(); !cursor.isAfterLast(); cursor.moveToNext()) {
metadata.readFromCursor(cursor);
metadata.setValue(Metadata.TASK, newId);
metadata.clearValue(Metadata.ID);
metadataDao.createNew(metadata);
}
}
} finally {
cursor.close();

@ -31,5 +31,5 @@ public final class Constants {
/**
* Whether to turn on debugging logging and UI
*/
public static final boolean DEBUG = false;
public static final boolean DEBUG = true;
}

@ -31,6 +31,11 @@ public class TestDependencyInjector implements AbstractDependencyInjector {
return null;
}
@Override
public String toString() {
return "TestDI:" + name;
}
// --- static stuff
/**

@ -18,14 +18,11 @@ public class TodorooTestCase extends AndroidTestCase {
AstridDependencyInjector.initialize();
}
public TodorooTestCase() {
DependencyInjectionService.getInstance().inject(this);
}
@Override
protected void setUp() throws Exception {
super.setUp();
ContextManager.setContext(this.getContext());
DependencyInjectionService.getInstance().inject(this);
}
}

@ -0,0 +1,256 @@
package com.todoroo.astrid.repeats;
import java.util.Date;
import com.google.ical.values.Frequency;
import com.google.ical.values.RRule;
import com.timsu.astrid.R;
import com.todoroo.andlib.data.TodorooCursor;
import com.todoroo.andlib.service.Autowired;
import com.todoroo.andlib.sql.Query;
import com.todoroo.andlib.utility.DateUtilities;
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.test.DatabaseTestCase;
import com.todoroo.astrid.utility.Preferences;
public class RepeatTests extends DatabaseTestCase {
private static final int REPEAT_WAIT = 1000;
@Autowired
TaskDao taskDao;
@Autowired
MetadataDao metadataDao;
@Override
protected void setUp() throws Exception {
super.setUp();
Preferences.setStringFromInteger(R.string.p_default_urgency_key, 0);
}
/** test that completing a task w/ no repeats does nothing */
public void xtestNoRepeats() throws Exception{
Task task = new Task();
task.setValue(Task.TITLE, "nothing");
taskDao.save(task, false);
task.setValue(Task.COMPLETION_DATE, DateUtilities.now());
taskDao.save(task, false);
// wait for repeat handler
Thread.sleep(REPEAT_WAIT);
TodorooCursor<Task> cursor = taskDao.query(Query.select(Task.ID));
try {
assertEquals(1, cursor.getCount());
} finally {
cursor.close();
}
}
/** test daily repeat from due date, but with no due date set */
public void testDailyWithNoDueDate() throws Exception {
Task task = new Task();
task.setValue(Task.TITLE, "daily");
RRule rrule = new RRule();
rrule.setInterval(5);
rrule.setFreq(Frequency.DAILY);
task.setValue(Task.RECURRENCE, rrule.toIcal());
taskDao.save(task, false);
task.setValue(Task.COMPLETION_DATE, DateUtilities.now());
taskDao.save(task, false);
// wait for repeat handler
Thread.sleep(REPEAT_WAIT);
TodorooCursor<Task> cursor = taskDao.query(Query.select(Task.PROPERTIES));
try {
assertEquals(2, cursor.getCount());
cursor.moveToFirst();
task.readFromCursor(cursor);
assertEquals("daily", task.getValue(Task.TITLE));
assertFalse(task.hasDueDate());
assertTrue(task.isCompleted());
cursor.moveToNext();
task.readFromCursor(cursor);
assertEquals("daily", task.getValue(Task.TITLE));
assertFalse(task.isCompleted());
long dueDate = task.getValue(Task.DUE_DATE);
assertFalse(task.hasDueTime());
assertTrue("Due date is '" + new Date(dueDate) + "', expected more like '" +
new Date(DateUtilities.now() + 5 * DateUtilities.ONE_DAY) + "'",
Math.abs(dueDate - DateUtilities.now() - 5 * DateUtilities.ONE_DAY) < DateUtilities.ONE_DAY);
} finally {
cursor.close();
}
}
/** test weekly repeat from due date, with due date & time set */
public void testWeeklyWithDueDate() throws Exception {
Task task = new Task();
task.setValue(Task.TITLE, "weekly");
RRule rrule = new RRule();
rrule.setInterval(1);
rrule.setFreq(Frequency.WEEKLY);
task.setValue(Task.RECURRENCE, rrule.toIcal());
long originalDueDate = (DateUtilities.now() - DateUtilities.ONE_DAY) / 1000L * 1000L;
task.setValue(Task.DUE_DATE, task.createDueDate(Task.URGENCY_SPECIFIC_DAY_TIME, originalDueDate));
taskDao.save(task, false);
task.setValue(Task.COMPLETION_DATE, DateUtilities.now());
taskDao.save(task, false);
// wait for repeat handler
Thread.sleep(REPEAT_WAIT);
TodorooCursor<Task> cursor = taskDao.query(Query.select(Task.PROPERTIES));
try {
assertEquals(2, cursor.getCount());
cursor.moveToFirst();
task.readFromCursor(cursor);
assertEquals("weekly", task.getValue(Task.TITLE));
assertEquals(originalDueDate, (long)task.getValue(Task.DUE_DATE));
assertTrue(task.isCompleted());
cursor.moveToNext();
task.readFromCursor(cursor);
assertEquals("weekly", task.getValue(Task.TITLE));
assertFalse(task.isCompleted());
long dueDate = task.getValue(Task.DUE_DATE);
assertTrue(task.hasDueTime());
assertEquals("Due date is '" + new Date(dueDate) + "', expected exactly '" +
new Date(originalDueDate + DateUtilities.ONE_WEEK) + "': ",
originalDueDate + DateUtilities.ONE_WEEK, dueDate);
} finally {
cursor.close();
}
}
/** test hourly repeat from due date, with due date but no time */
public void testHourlyFromDueDate() throws Exception {
Task task = new Task();
task.setValue(Task.TITLE, "hourly");
RRule rrule = new RRule();
rrule.setInterval(4);
rrule.setFreq(Frequency.HOURLY);
task.setValue(Task.RECURRENCE, rrule.toIcal());
long originalDueDate = (DateUtilities.now() + DateUtilities.ONE_DAY) / 1000L * 1000L;
task.setValue(Task.DUE_DATE, task.createDueDate(Task.URGENCY_SPECIFIC_DAY, originalDueDate));
taskDao.save(task, false);
task.setValue(Task.COMPLETION_DATE, DateUtilities.now());
taskDao.save(task, false);
// wait for repeat handler
Thread.sleep(REPEAT_WAIT);
TodorooCursor<Task> cursor = taskDao.query(Query.select(Task.PROPERTIES));
try {
assertEquals(2, cursor.getCount());
cursor.moveToFirst();
task.readFromCursor(cursor);
assertEquals(originalDueDate, (long)task.getValue(Task.DUE_DATE));
assertTrue(task.isCompleted());
cursor.moveToNext();
task.readFromCursor(cursor);
assertFalse(task.isCompleted());
long dueDate = task.getValue(Task.DUE_DATE);
assertTrue(task.hasDueTime());
assertEquals("Due date is '" + new Date(dueDate) + "', expected exactly '" +
new Date(originalDueDate + 4 * DateUtilities.ONE_HOUR) + "'",
originalDueDate + 4 * DateUtilities.ONE_HOUR, dueDate);
} finally {
cursor.close();
}
}
/** test after completion flag */
public void testRepeatAfterComplete() throws Exception {
Task task = new Task();
task.setValue(Task.TITLE, "afterComplete");
RRule rrule = new RRule();
rrule.setInterval(1);
rrule.setFreq(Frequency.WEEKLY);
task.setValue(Task.RECURRENCE, rrule.toIcal());
long originalDueDate = DateUtilities.now() - 3 * DateUtilities.ONE_DAY;
task.setValue(Task.DUE_DATE, task.createDueDate(Task.URGENCY_SPECIFIC_DAY, originalDueDate));
taskDao.save(task, false);
task.setValue(Task.COMPLETION_DATE, DateUtilities.now());
taskDao.save(task, false);
// wait for repeat handler
Thread.sleep(REPEAT_WAIT);
TodorooCursor<Task> cursor = taskDao.query(Query.select(Task.PROPERTIES));
try {
assertEquals(2, cursor.getCount());
cursor.moveToFirst();
task.readFromCursor(cursor);
assertEquals(originalDueDate, (long)task.getValue(Task.DUE_DATE));
assertTrue(task.isCompleted());
cursor.moveToNext();
task.readFromCursor(cursor);
assertFalse(task.isCompleted());
long dueDate = task.getValue(Task.DUE_DATE);
assertFalse(task.hasDueTime());
assertTrue("Due date is '" + new Date(dueDate) + "', expected more like '" +
new Date(DateUtilities.now() + DateUtilities.ONE_WEEK) + "'",
Math.abs(dueDate - DateUtilities.ONE_WEEK) < DateUtilities.ONE_DAY);
} finally {
cursor.close();
}
}
/** test that metadata is transferred to new task */
public void testMetadataIsCopied() throws Exception {
Task task = new Task();
task.setValue(Task.TITLE, "meta data test");
RRule rrule = new RRule();
rrule.setInterval(5);
rrule.setFreq(Frequency.DAILY);
task.setValue(Task.RECURRENCE, rrule.toIcal());
taskDao.save(task, false);
Metadata metadata = new Metadata();
metadata.setValue(Metadata.KEY, "special");
metadata.setValue(Metadata.VALUE, "sauce");
metadata.setValue(Metadata.TASK, task.getId());
metadataDao.persist(metadata);
task.setValue(Task.COMPLETION_DATE, DateUtilities.now());
taskDao.save(task, false);
// wait for repeat handler
Thread.sleep(REPEAT_WAIT);
TodorooCursor<Metadata> cursor = metadataDao.query(Query.select(Metadata.TASK));
try {
assertEquals(2, cursor.getCount());
cursor.moveToFirst();
metadata.readFromCursor(cursor);
assertSame(task.getId(), (long)metadata.getValue(Metadata.TASK));
cursor.moveToNext();
metadata.readFromCursor(cursor);
assertNotSame(task.getId(), (long)metadata.getValue(Metadata.TASK));
} finally {
cursor.close();
}
}
}

@ -2,7 +2,6 @@ package com.todoroo.astrid.test;
import java.io.File;
import com.todoroo.andlib.service.DependencyInjectionService;
import com.todoroo.andlib.service.TestDependencyInjector;
import com.todoroo.andlib.test.TodorooTestCase;
import com.todoroo.astrid.alarms.AlarmDatabase;
@ -25,17 +24,15 @@ public class DatabaseTestCase extends TodorooTestCase {
@Override
protected void setUp() throws Exception {
super.setUp();
// initialize test dependency injector
TestDependencyInjector injector = TestDependencyInjector.initialize("db");
injector.addInjectable("database", database);
DependencyInjectionService.getInstance().inject(this);
// call upstream setup, which invokes dependency injector
super.setUp();
// empty out test databases
database.clear();
database.openForWriting();
}

@ -2,6 +2,8 @@ package com.todoroo.astrid.upgrade;
import java.util.Date;
import com.google.ical.values.Frequency;
import com.google.ical.values.RRule;
import com.todoroo.andlib.data.TodorooCursor;
import com.todoroo.andlib.service.Autowired;
import com.todoroo.andlib.service.TestDependencyInjector;
@ -14,10 +16,10 @@ 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.AbstractTaskModel.RepeatInfo;
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.Astrid2To3UpgradeHelper;
import com.todoroo.astrid.tags.TagService;
@ -105,7 +107,7 @@ public class Astrid2To3UpgradeTests extends DatabaseTestCase {
/**
* Test various parameters of tasks table
*/
public void testTaskTableUpgrade() {
public void testTaskTableUpgrade() throws Exception {
TaskController taskController = new TaskController(getContext());
taskController.open();
@ -147,6 +149,7 @@ public class Astrid2To3UpgradeTests extends DatabaseTestCase {
assertEquals(Task.IMPORTANCE_NONE, (int)task.getValue(Task.IMPORTANCE));
assertEquals(griffey.getEstimatedSeconds(), task.getValue(Task.ESTIMATED_SECONDS));
assertEquals(griffey.getNotes(), task.getValue(Task.NOTES));
assertEquals("", task.getValue(Task.RECURRENCE));
assertEquals(0, (long)task.getValue(Task.REMINDER_LAST));
assertEquals(0, (long)task.getValue(Task.HIDE_UNTIL));
@ -156,8 +159,9 @@ public class Astrid2To3UpgradeTests extends DatabaseTestCase {
assertDatesEqual(guti.getPreferredDueDate(), task.getValue(Task.DUE_DATE));
assertDatesEqual(guti.getHiddenUntil(), task.getValue(Task.HIDE_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.getRepeat().getValue(), new RRule(task.getValue(Task.RECURRENCE)).getInterval());
assertEquals(Frequency.DAILY,
new RRule(task.getValue(Task.RECURRENCE)).getFreq());
assertEquals(guti.getElapsedSeconds(), task.getValue(Task.ELAPSED_SECONDS));
assertEquals(guti.getNotificationIntervalSeconds() * 1000L, (long)task.getValue(Task.REMINDER_PERIOD));
assertDatesEqual(createdDate, task.getValue(Task.CREATION_DATE));

Loading…
Cancel
Save