Introduced ability to repeat a task until a specified date

pull/14/head
Sam Bosley 12 years ago
parent 9b21eb1a21
commit ce6812fcf5

@ -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
* <li> EXTRAS_TASK_ID id of the task
* <li> EXTRAS_OLD_DUE_DATE task old due date (could be 0)
* <li> 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
*/

@ -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);

@ -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);

@ -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<RepeatChangedListener> listeners = new LinkedList<RepeatChangedListener>();
@ -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

@ -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);

@ -0,0 +1,39 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<LinearLayout
android:id="@+id/display_row_body"
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:minHeight="50dip"
android:paddingLeft="10dip"
android:paddingRight="10dip"
android:gravity="center_vertical">
<TextView
android:id="@+id/display_row_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginRight="10dip"
style="@style/TextAppearance.GEN_EditLabel" />
<TextView
android:id="@+id/display_row_edit"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="right"
android:paddingLeft="10dip"
style="@style/TextAppearance.EditRowDisplay" />
</LinearLayout>
<View
android:id="@+id/TEA_Separator"
android:layout_width="fill_parent"
android:layout_height="1px"
style="@style/TEA_Separator" />
</LinearLayout>

@ -44,6 +44,10 @@
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:gravity="center"/>
<Button android:id="@+id/repeatUntil"
android:layout_width="fill_parent"
android:layout_height="wrap_content" />
</LinearLayout>
<LinearLayout

@ -0,0 +1,37 @@
<?xml version="1.0" encoding="utf-8"?>
<ScrollView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:astrid="http://schemas.android.com/apk/res/com.timsu.astrid"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<com.todoroo.astrid.ui.DateAndTimePicker
android:id="@+id/date_and_time"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical"
astrid:shortcutLabels="@array/repeat_until_shortcuts"/>
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:paddingLeft="7dip"
android:paddingRight="7dip"
android:orientation="horizontal">
<Button
android:id="@+id/ok"
android:layout_width="fill_parent"
android:layout_height="50dip"
android:layout_weight="1"
android:text="@string/DLG_ok"/>
<Button
android:id="@+id/cancel"
android:layout_width="fill_parent"
android:layout_height="50dip"
android:layout_weight="1"
android:text="@string/DLG_cancel"/>
</LinearLayout>
</LinearLayout>
</ScrollView>

@ -41,5 +41,9 @@
<attr name="allowMultiple" format="boolean"/>
<attr name="completeTags" format="boolean"/>
</declare-styleable>
<declare-styleable name="DateAndTimePicker">
<attr name="shortcutLabels" format="reference"/>
</declare-styleable>
</resources>

@ -45,6 +45,19 @@
<item>Year(s)</item>
</string-array>
<string-array name="repeat_until_shortcuts">
<item>Forever</item>
<item>Specific Day</item>
<item>Today</item>
<item>Tomorrow</item>
<item>(day after)</item>
<item>Next Week</item>
<item>In Two Weeks</item>
<item>Next Month</item>
</string-array>
<string name="repeat_until_title">Repeat until...</string>
<string-array name="repeat_type">
<!-- slide 20c: repeat type (date to repeat from) -->
<item>from due date</item>
@ -57,10 +70,19 @@
<!-- task detail for repeat from due date (%s -> interval) -->
<string name="repeat_detail_duedate">Every %s</string>
<!-- task detail for repeat until a specific date (%1$s -> interval, %2$s -> finish date)-->
<string name="repeat_detail_duedate_until">Every %1$s\nuntil %2$s</string>
<!-- task detail for repeat from completion date (%s -> interval) -->
<string name="repeat_detail_completion">%s after completion</string>
<!-- text for button when repeating task indefinitely -->
<string name="repeat_forever">Repeat forever</string>
<!-- text for button when repeating task until specified date (%s -> date string) -->
<string name="repeat_until">Repeat until %s</string>
<!-- text for confirmation dialog after repeating a task -->
<string name="repeat_rescheduling_dialog_title">Rescheduling task \"%s\"</string>

@ -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();

@ -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;
}

@ -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);
}
}

@ -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<UrgencyValue> urgencyValues;
private ArrayList<UrgencyValue> 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<UrgencyValue>();
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));
}

@ -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();

@ -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;
}

Loading…
Cancel
Save