diff --git a/api/src/com/todoroo/astrid/api/AstridApiConstants.java b/api/src/com/todoroo/astrid/api/AstridApiConstants.java
index c61601977..663c5012e 100644
--- a/api/src/com/todoroo/astrid/api/AstridApiConstants.java
+++ b/api/src/com/todoroo/astrid/api/AstridApiConstants.java
@@ -246,6 +246,14 @@ public class AstridApiConstants {
*/
public static final String BROADCAST_EVENT_TASK_REPEATED = PACKAGE + ".TASK_REPEATED";
+ /**
+ * Action name for broadcast intent notifying that a repeating task has passed its repeat_until value
+ *
EXTRAS_TASK_ID id of the task
+ *
EXTRAS_OLD_DUE_DATE task old due date (could be 0)
+ *
EXTRAS_NEW_DUE_DATE task new due date (will not be 0)
+ */
+ public static final String BROADCAST_EVENT_TASK_REPEAT_FINISHED = PACKAGE + ".TASK_REPEAT_FINISHED";
+
/**
* Action name for broadcast intent notifying that tag was deleted
*/
diff --git a/api/src/com/todoroo/astrid/data/Task.java b/api/src/com/todoroo/astrid/data/Task.java
index 382643b98..30646a3d2 100644
--- a/api/src/com/todoroo/astrid/data/Task.java
+++ b/api/src/com/todoroo/astrid/data/Task.java
@@ -128,6 +128,9 @@ public final class Task extends RemoteModel {
public static final StringProperty RECURRENCE = new StringProperty(
TABLE, "recurrence");
+ public static final LongProperty REPEAT_UNTIL = new LongProperty(
+ TABLE, "repeatUntil");
+
public static final StringProperty CALENDAR_URI = new StringProperty(
TABLE, "calendarUri");
@@ -241,6 +244,7 @@ public final class Task extends RemoteModel {
defaultValues.put(CALENDAR_URI.name, "");
defaultValues.put(RECURRENCE.name, "");
+ defaultValues.put(REPEAT_UNTIL.name, 0);
defaultValues.put(REMINDER_PERIOD.name, 0);
defaultValues.put(REMINDER_FLAGS.name, 0);
defaultValues.put(REMINDER_LAST.name, 0);
diff --git a/astrid/plugin-src/com/todoroo/astrid/alarms/AlarmControlSet.java b/astrid/plugin-src/com/todoroo/astrid/alarms/AlarmControlSet.java
index 5a03ac905..d03d45a2a 100644
--- a/astrid/plugin-src/com/todoroo/astrid/alarms/AlarmControlSet.java
+++ b/astrid/plugin-src/com/todoroo/astrid/alarms/AlarmControlSet.java
@@ -19,6 +19,7 @@ import com.todoroo.astrid.data.Task;
import com.todoroo.astrid.helper.TaskEditControlSet;
import com.todoroo.astrid.ui.DateAndTimeDialog;
import com.todoroo.astrid.ui.DateAndTimeDialog.DateAndTimeDialogListener;
+import com.todoroo.astrid.ui.DateAndTimePicker;
/**
* Control set to manage adding and removing tags
@@ -100,7 +101,7 @@ public final class AlarmControlSet extends TaskEditControlSet {
}
v.setTag(date);
TextView label = (TextView) v.findViewById(R.id.alarm_string);
- label.setText(pickerDialog.getDisplayString(activity, date));
+ label.setText(DateAndTimePicker.getDisplayString(activity, date));
}
}
@@ -115,7 +116,7 @@ public final class AlarmControlSet extends TaskEditControlSet {
alertItem.setTag(alert.getTime());
TextView display = (TextView) alertItem.findViewById(R.id.alarm_string);
- display.setText(pickerDialog.getDisplayString(activity, alert.getTime()));
+ display.setText(DateAndTimePicker.getDisplayString(activity, alert.getTime()));
ImageButton reminderRemoveButton;
reminderRemoveButton = (ImageButton)alertItem.findViewById(R.id.button1);
diff --git a/astrid/plugin-src/com/todoroo/astrid/repeats/RepeatControlSet.java b/astrid/plugin-src/com/todoroo/astrid/repeats/RepeatControlSet.java
index aea9c78bc..5f8d416ef 100644
--- a/astrid/plugin-src/com/todoroo/astrid/repeats/RepeatControlSet.java
+++ b/astrid/plugin-src/com/todoroo/astrid/repeats/RepeatControlSet.java
@@ -37,6 +37,9 @@ import com.todoroo.astrid.data.Task;
import com.todoroo.astrid.service.StatisticsConstants;
import com.todoroo.astrid.service.StatisticsService;
import com.todoroo.astrid.service.TaskService;
+import com.todoroo.astrid.ui.DateAndTimeDialog;
+import com.todoroo.astrid.ui.DateAndTimeDialog.DateAndTimeDialogListener;
+import com.todoroo.astrid.ui.DateAndTimePicker;
import com.todoroo.astrid.ui.NumberPicker;
import com.todoroo.astrid.ui.NumberPickerDialog;
import com.todoroo.astrid.ui.NumberPickerDialog.OnNumberPickedListener;
@@ -67,12 +70,14 @@ public class RepeatControlSet extends PopupControlSet {
private Button value;
private Spinner interval;
private Spinner type;
+ private Button repeatUntil;
private LinearLayout daysOfWeekContainer;
private final CompoundButton[] daysOfWeek = new CompoundButton[7];
private String recurrence;
private int repeatValue;
private int intervalValue;
+ private long repeatUntilValue;
private final List listeners = new LinkedList();
@@ -99,26 +104,45 @@ public class RepeatControlSet extends PopupControlSet {
value.setText(activity.getString(R.string.repeat_every, newValue));
}
+ private void setRepeatUntilValue(long newValue) {
+ repeatUntilValue = newValue;
+
+ if (newValue == 0)
+ repeatUntil.setText(activity.getString(R.string.repeat_forever));
+ else
+ repeatUntil.setText(activity.getString(R.string.repeat_until, DateAndTimePicker.getDisplayString(activity, newValue)));
+ }
+
protected void repeatValueClick() {
+ int dialogValue = repeatValue;
+ if(dialogValue == 0)
+ dialogValue = 1;
- final Runnable openDialogRunnable = new Runnable() {
- public void run() {
- int dialogValue = repeatValue;
- if(dialogValue == 0)
- dialogValue = 1;
-
- new NumberPickerDialog(activity, new OnNumberPickedListener() {
- @Override
- public void onNumberPicked(NumberPicker view,
- int number) {
- setRepeatValue(number);
- }
- }, activity.getResources().getString(R.string.repeat_interval_prompt),
- dialogValue, 1, 1, 365).show();
+ new NumberPickerDialog(activity, new OnNumberPickedListener() {
+ @Override
+ public void onNumberPicked(NumberPicker view,
+ int number) {
+ setRepeatValue(number);
}
- };
+ }, activity.getResources().getString(R.string.repeat_interval_prompt),
+ dialogValue, 1, 1, 365).show();
+ }
- openDialogRunnable.run();
+ private void repeatUntilClick() {
+ DateAndTimeDialog d = new DateAndTimeDialog(activity, repeatUntilValue,
+ R.layout.repeat_until_dialog, R.string.repeat_until_title);
+ d.setDateAndTimeDialogListener(new DateAndTimeDialogListener() {
+ @Override
+ public void onDateAndTimeSelected(long date) {
+ setRepeatUntilValue(date);
+ }
+
+ @Override
+ public void onDateAndTimeCancelled() {
+ //
+ }
+ });
+ d.show();
}
@@ -139,6 +163,8 @@ public class RepeatControlSet extends PopupControlSet {
if(recurrence == null)
recurrence = "";
+ repeatUntilValue = model.getValue(Task.REPEAT_UNTIL);
+
if(recurrence.length() > 0) {
try {
RRule rrule = new RRule(recurrence);
@@ -196,6 +222,7 @@ public class RepeatControlSet extends PopupControlSet {
RRule rrule = new RRule(recurrence);
setRepeatValue(rrule.getInterval());
+ setRepeatUntilValue(model.getValue(Task.REPEAT_UNTIL));
interval.setSelection(intervalValue);
// clear all day of week checks, then update them
@@ -233,7 +260,9 @@ public class RepeatControlSet extends PopupControlSet {
interval = (Spinner) getView().findViewById(R.id.repeatInterval);
type = (Spinner) getView().findViewById(R.id.repeatType);
daysOfWeekContainer = (LinearLayout) getView().findViewById(R.id.repeatDayOfWeekContainer);
+ repeatUntil = (Button) getView().findViewById(R.id.repeatUntil);
setRepeatValue(1);
+ setRepeatUntilValue(0);
// set up days of week
DateFormatSymbols dfs = new DateFormatSymbols();
@@ -280,6 +309,13 @@ public class RepeatControlSet extends PopupControlSet {
//
}
});
+
+ repeatUntil.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ repeatUntilClick();
+ }
+ });
daysOfWeekContainer.setVisibility(View.GONE);
}
@@ -329,6 +365,7 @@ public class RepeatControlSet extends PopupControlSet {
if (!result.equals(task.getValue(Task.RECURRENCE)))
task.putTransitory(TaskService.TRANS_REPEAT_CHANGED, true);
task.setValue(Task.RECURRENCE, result);
+ task.setValue(Task.REPEAT_UNTIL, repeatUntilValue);
switch(type.getSelectedItemPosition()) {
case TYPE_DUE_DATE:
@@ -385,7 +422,10 @@ public class RepeatControlSet extends PopupControlSet {
String[] dates = activity.getResources().getStringArray(
arrayResource);
String date = String.format("%s %s", repeatValue, dates[intervalValue]); //$NON-NLS-1$
- return String.format(activity.getString(R.string.repeat_detail_duedate), date); // Every freq int
+ if (repeatUntilValue > 0)
+ return activity.getString(R.string.repeat_detail_duedate_until, date, DateAndTimePicker.getDisplayString(activity, repeatUntilValue, false, useAbbrev, useAbbrev));
+ else
+ return activity.getString(R.string.repeat_detail_duedate, date); // Every freq int
}
@Override
diff --git a/astrid/plugin-src/com/todoroo/astrid/repeats/RepeatTaskCompleteListener.java b/astrid/plugin-src/com/todoroo/astrid/repeats/RepeatTaskCompleteListener.java
index 87a92238a..76f3bef57 100644
--- a/astrid/plugin-src/com/todoroo/astrid/repeats/RepeatTaskCompleteListener.java
+++ b/astrid/plugin-src/com/todoroo/astrid/repeats/RepeatTaskCompleteListener.java
@@ -63,15 +63,26 @@ public class RepeatTaskCompleteListener extends BroadcastReceiver {
return;
}
+
StatisticsService.reportEvent(StatisticsConstants.V2_TASK_REPEAT);
+ long oldDueDate = task.getValue(Task.DUE_DATE);
+ long repeatUntil = task.getValue(Task.REPEAT_UNTIL);
+ if (repeatUntil > 0 && newDueDate >= repeatUntil) {
+ Intent repeatFinished = new Intent(AstridApiConstants.BROADCAST_EVENT_TASK_REPEAT_FINISHED);
+ repeatFinished.putExtra(AstridApiConstants.EXTRAS_TASK_ID, task.getId());
+ repeatFinished.putExtra(AstridApiConstants.EXTRAS_OLD_DUE_DATE, oldDueDate);
+ repeatFinished.putExtra(AstridApiConstants.EXTRAS_NEW_DUE_DATE, newDueDate);
+ context.sendOrderedBroadcast(repeatFinished, null);
+ return;
+ }
+
long hideUntil = task.getValue(Task.HIDE_UNTIL);
if(hideUntil > 0 && task.getValue(Task.DUE_DATE) > 0) {
hideUntil += newDueDate - task.getValue(Task.DUE_DATE);
}
// update repeat time when it repeats on the server
- long oldDueDate = task.getValue(Task.DUE_DATE);
task.setValue(Task.COMPLETION_DATE, 0L);
task.setValue(Task.DUE_DATE, newDueDate);
task.setValue(Task.HIDE_UNTIL, hideUntil);
diff --git a/astrid/res/layout/control_set_deadline_display.xml b/astrid/res/layout/control_set_deadline_display.xml
new file mode 100644
index 000000000..4163b6c99
--- /dev/null
+++ b/astrid/res/layout/control_set_deadline_display.xml
@@ -0,0 +1,39 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/astrid/res/layout/control_set_repeat.xml b/astrid/res/layout/control_set_repeat.xml
index 296997105..1a0c57f65 100644
--- a/astrid/res/layout/control_set_repeat.xml
+++ b/astrid/res/layout/control_set_repeat.xml
@@ -44,6 +44,10 @@
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:gravity="center"/>
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/astrid/res/values/attrs.xml b/astrid/res/values/attrs.xml
index ded0e8c8b..6ffa25447 100644
--- a/astrid/res/values/attrs.xml
+++ b/astrid/res/values/attrs.xml
@@ -41,5 +41,9 @@
+
+
+
+
diff --git a/astrid/res/values/strings-repeat.xml b/astrid/res/values/strings-repeat.xml
index 8bd8dc086..4694fb85a 100644
--- a/astrid/res/values/strings-repeat.xml
+++ b/astrid/res/values/strings-repeat.xml
@@ -45,6 +45,19 @@
Year(s)
+
+ Forever
+ Specific Day
+ Today
+ Tomorrow
+ (day after)
+ Next Week
+ In Two Weeks
+ Next Month
+
+
+ Repeat until...
+
from due date
@@ -57,10 +70,19 @@
Every %s
+
+
+ Every %1$s\nuntil %2$s%s after completion
+
+ Repeat forever
+
+
+ Repeat until %s
+
Rescheduling task \"%s\"
diff --git a/astrid/src/com/todoroo/astrid/activity/TaskEditFragment.java b/astrid/src/com/todoroo/astrid/activity/TaskEditFragment.java
index 63c0d5c01..d26b430c8 100755
--- a/astrid/src/com/todoroo/astrid/activity/TaskEditFragment.java
+++ b/astrid/src/com/todoroo/astrid/activity/TaskEditFragment.java
@@ -510,19 +510,19 @@ ViewPager.OnPageChangeListener, EditNoteActivity.UpdatesChangedListener {
// The deadline control set contains the repeat controls and the
// calendar controls.
- // NOTE: we add the gcalControl and repeatControl to the list AFTER the
+ // NOTE: we add the gcalControl AFTER the
// deadline control, because
// otherwise the correct date may not be written to the calendar event.
// Order matters!
DeadlineControlSet deadlineControl = new DeadlineControlSet(
getActivity(), R.layout.control_set_deadline,
- R.layout.control_set_default_display, repeatControls,
+ R.layout.control_set_deadline_display, repeatControls,
repeatControls.getDisplayView(), gcalControl.getDisplayView());
controlSetMap.put(getString(R.string.TEA_ctrl_when_pref),
deadlineControl);
- controls.add(deadlineControl);
controls.add(repeatControls);
repeatControls.addListener(editTitle);
+ controls.add(deadlineControl);
controls.add(gcalControl);
ImportanceControlSet importanceControl = new ImportanceControlSet(
@@ -885,7 +885,6 @@ ViewPager.OnPageChangeListener, EditNoteActivity.UpdatesChangedListener {
tla.refreshTaskList();
}
- Log.e("Not on pause", "not on pause", new Throwable());
getActivity().getIntent().removeExtra(TaskListActivity.OPEN_TASK);
shouldSaveState = false;
getActivity().onBackPressed();
diff --git a/astrid/src/com/todoroo/astrid/dao/Database.java b/astrid/src/com/todoroo/astrid/dao/Database.java
index f8d4e2836..c4dd21336 100644
--- a/astrid/src/com/todoroo/astrid/dao/Database.java
+++ b/astrid/src/com/todoroo/astrid/dao/Database.java
@@ -39,7 +39,7 @@ public class Database extends AbstractDatabase {
* Database version number. This variable must be updated when database
* tables are updated, as it determines whether a database needs updating.
*/
- public static final int VERSION = 24;
+ public static final int VERSION = 25;
/**
* Database name (must be unique)
@@ -318,6 +318,13 @@ public class Database extends AbstractDatabase {
Log.e("astrid", "db-upgrade-" + oldVersion + "-" + newVersion, e);
}
+ case 24: try {
+ database.execSQL("ALTER TABLE " + Task.TABLE.name + " ADD " +
+ Task.REPEAT_UNTIL.accept(visitor, null));
+ } catch (SQLiteException e) {
+ Log.e("astrid", "db-upgrade-" + oldVersion + "-" + newVersion, e);
+ }
+
return true;
}
diff --git a/astrid/src/com/todoroo/astrid/ui/DateAndTimeDialog.java b/astrid/src/com/todoroo/astrid/ui/DateAndTimeDialog.java
index 5177cb256..2ea15e465 100644
--- a/astrid/src/com/todoroo/astrid/ui/DateAndTimeDialog.java
+++ b/astrid/src/com/todoroo/astrid/ui/DateAndTimeDialog.java
@@ -26,12 +26,18 @@ public class DateAndTimeDialog extends Dialog {
private DateAndTimeDialogListener listener;
public DateAndTimeDialog(Context context, long startDate) {
+ this(context, startDate, R.layout.date_time_dialog, 0);
+ }
+
+ public DateAndTimeDialog(Context context, long startDate, int contentView, int title) {
super(context, ThemeService.getEditDialogTheme());
- /** 'Window.FEATURE_NO_TITLE' - Used to hide the title */
- requestWindowFeature(Window.FEATURE_NO_TITLE);
+ if (title == 0)
+ requestWindowFeature(Window.FEATURE_NO_TITLE);
+ else
+ setTitle(title);
/** Design the dialog in main.xml file */
- setContentView(R.layout.date_time_dialog);
+ setContentView(contentView);
LayoutParams params = getWindow().getAttributes();
params.height = LayoutParams.FILL_PARENT;
@@ -95,8 +101,4 @@ public class DateAndTimeDialog extends Dialog {
public String getDisplayString(Context context) {
return dateAndTimePicker.getDisplayString(context, false, false);
}
-
- public String getDisplayString(Context context, long forDate) {
- return DateAndTimePicker.getDisplayString(context, forDate, false, false);
- }
}
diff --git a/astrid/src/com/todoroo/astrid/ui/DateAndTimePicker.java b/astrid/src/com/todoroo/astrid/ui/DateAndTimePicker.java
index 806a4857c..7a69b5e5b 100644
--- a/astrid/src/com/todoroo/astrid/ui/DateAndTimePicker.java
+++ b/astrid/src/com/todoroo/astrid/ui/DateAndTimePicker.java
@@ -5,6 +5,7 @@ import java.util.Date;
import android.content.Context;
import android.content.res.Resources;
+import android.content.res.TypedArray;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.util.TypedValue;
@@ -28,7 +29,7 @@ public class DateAndTimePicker extends LinearLayout {
private static final int SHORTCUT_PADDING = 8;
- ArrayList urgencyValues;
+ private ArrayList urgencyValues;
private class UrgencyValue {
public String label;
@@ -62,7 +63,7 @@ public class DateAndTimePicker extends LinearLayout {
dateShortcuts = (LinearLayout) findViewById(R.id.date_shortcuts);
setUpListeners();
- constructShortcutList(context);
+ constructShortcutList(context, attrs);
}
public void initializeWithDate(long dateValue) {
@@ -119,8 +120,18 @@ public class DateAndTimePicker extends LinearLayout {
}
}
- private void constructShortcutList(Context context) {
- String[] labels = context.getResources().getStringArray(R.array.TEA_urgency);
+ private void constructShortcutList(Context context, AttributeSet attrs) {
+ int arrayResource = R.array.TEA_urgency;
+ TypedArray t = context.obtainStyledAttributes(attrs, R.styleable.DateAndTimePicker);
+ for (int i = 0; i < t.getIndexCount(); i++) {
+ int attr = t.getIndex(i);
+ switch(attr) {
+ case R.styleable.DateAndTimePicker_shortcutLabels:
+ arrayResource = t.getResourceId(attr, R.array.TEA_urgency);
+ }
+ }
+
+ String[] labels = context.getResources().getStringArray(arrayResource);
urgencyValues = new ArrayList();
urgencyValues.add(new UrgencyValue(labels[2],
Task.URGENCY_TODAY));
@@ -228,10 +239,14 @@ public class DateAndTimePicker extends LinearLayout {
public String getDisplayString(Context context, boolean useNewline, boolean hideYear) {
long dueDate = constructDueDate();
- return getDisplayString(context, dueDate, useNewline, hideYear);
+ return getDisplayString(context, dueDate, useNewline, hideYear, false);
+ }
+
+ public static String getDisplayString(Context context, long forDate) {
+ return getDisplayString(context, forDate, false, false, false);
}
- public static String getDisplayString(Context context, long forDate, boolean useNewline, boolean hideYear) {
+ public static String getDisplayString(Context context, long forDate, boolean useNewline, boolean hideYear, boolean hideTime) {
StringBuilder displayString = new StringBuilder();
Date d = new Date(forDate);
if (d.getTime() > 0) {
@@ -239,7 +254,7 @@ public class DateAndTimePicker extends LinearLayout {
displayString.append(DateUtilities.getDateStringHideYear(context, d));
else
displayString.append(DateUtilities.getDateString(context, d));
- if (Task.hasDueTime(forDate)) {
+ if (Task.hasDueTime(forDate) && !hideTime) {
displayString.append(useNewline ? "\n" : ", "); //$NON-NLS-1$ //$NON-NLS-2$
displayString.append(DateUtilities.getTimeString(context, d));
}
diff --git a/astrid/src/com/todoroo/astrid/ui/DeadlineControlSet.java b/astrid/src/com/todoroo/astrid/ui/DeadlineControlSet.java
index 53b87b918..bff7aac04 100644
--- a/astrid/src/com/todoroo/astrid/ui/DeadlineControlSet.java
+++ b/astrid/src/com/todoroo/astrid/ui/DeadlineControlSet.java
@@ -36,7 +36,7 @@ public class DeadlineControlSet extends PopupControlSet {
if (initialized)
displayString.append(dateAndTimePicker.getDisplayString(activity, isQuickadd, isQuickadd));
else
- displayString.append(DateAndTimePicker.getDisplayString(activity, model.getValue(Task.DUE_DATE), isQuickadd, isQuickadd));
+ displayString.append(DateAndTimePicker.getDisplayString(activity, model.getValue(Task.DUE_DATE), isQuickadd, isQuickadd, false));
if (!isQuickadd && repeatControlSet != null) {
String repeatString = repeatControlSet.getStringForExternalDisplay();
diff --git a/astrid/src/com/todoroo/astrid/ui/HideUntilControlSet.java b/astrid/src/com/todoroo/astrid/ui/HideUntilControlSet.java
index b20e4d7c0..360a55ea6 100644
--- a/astrid/src/com/todoroo/astrid/ui/HideUntilControlSet.java
+++ b/astrid/src/com/todoroo/astrid/ui/HideUntilControlSet.java
@@ -254,7 +254,7 @@ public class HideUntilControlSet extends PopupControlSet implements OnItemSelect
task.setValue(Task.HIDE_UNTIL, value);
if (value != 0)
- return activity.getString(R.string.TEA_hideUntil_message, DateAndTimePicker.getDisplayString(activity, value, false, false));
+ return activity.getString(R.string.TEA_hideUntil_message, DateAndTimePicker.getDisplayString(activity, value, false, false, false));
return null;
}