Repeats. woot

pull/14/head
Tim Su 14 years ago
parent eee441e9e3
commit 8e02edab68

@ -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="3.0.0-beta" android:versionCode="136">
android:versionName="3.0.0-beta" android:versionCode="137">
<!-- ================================================== Used Permissions = -->
@ -45,13 +45,14 @@
<!-- ========================================================== Metadata = -->
<uses-sdk android:targetSdkVersion="4" />
<uses-sdk android:targetSdkVersion="4"
android:minSdkVersion="3" />
<supports-screens />
<application android:icon="@drawable/icon"
android:label="@string/app_name"
android:debuggable="true"
android:installLocation="auto">
android:debuggable="true">
<!-- android:installLocation="auto"> -->
<!-- ====================================================== Activities = -->
@ -147,7 +148,7 @@
<!-- core -->
<receiver android:name="com.todoroo.astrid.core.CorePlugin">
<intent-filter>
<action android:name="com.todoroo.astrid.REQUEST_PLUGINS" />
<action android:name="com.todoroo.astrid.REQUEST_ADDONS" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</receiver>
@ -168,7 +169,7 @@
<!-- extended filters -->
<receiver android:name="com.todoroo.astrid.core.ExtendedPlugin">
<intent-filter>
<action android:name="com.todoroo.astrid.REQUEST_PLUGINS" />
<action android:name="com.todoroo.astrid.REQUEST_ADDONS" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</receiver>
@ -182,7 +183,7 @@
<!-- tags -->
<receiver android:name="com.todoroo.astrid.tags.TagsPlugin">
<intent-filter>
<action android:name="com.todoroo.astrid.REQUEST_PLUGINS" />
<action android:name="com.todoroo.astrid.REQUEST_ADDONS" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</receiver>
@ -198,6 +199,14 @@
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</receiver>
<!-- repeats -->
<receiver android:name="com.todoroo.astrid.repeats.RepeatsPlugin">
<intent-filter>
<action android:name="com.todoroo.astrid.REQUEST_ADDONS" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</receiver>
<!-- notes -->
<receiver android:name="com.todoroo.astrid.notes.NoteDetailExposer">

@ -7,18 +7,18 @@ import android.os.Parcel;
import android.os.Parcelable;
/**
* Represents a plug-in for Astrid. Users can enable or disable plug-ins,
* Represents an add-onn for Astrid. Users can enable or disable add-ons,
* which affect all other extension points that share the same identifier.
*
* @author Tim Su <tim@todoroo.com>
*
*/
public class Plugin implements Parcelable {
public class Addon implements Parcelable {
/**
* Plug-in Identifier
* Add-on Identifier
*/
public String plugin = null;
public String addon = null;
/**
* Plug-in Title
@ -38,13 +38,13 @@ public class Plugin implements Parcelable {
/**
* Convenience constructor to generate a plug-in object
*
* @param plugin
* @param addon
* @param title
* @param author
* @param description
*/
public Plugin(String plugin, String title, String author, String description) {
this.plugin = plugin;
public Addon(String addon, String title, String author, String description) {
this.addon = addon;
this.title = title;
this.author = author;
this.description = description;
@ -64,7 +64,7 @@ public class Plugin implements Parcelable {
* {@inheritDoc}
*/
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(plugin);
dest.writeString(addon);
dest.writeString(title);
dest.writeString(author);
dest.writeString(description);
@ -73,20 +73,20 @@ public class Plugin implements Parcelable {
/**
* Parcelable creator
*/
public static final Parcelable.Creator<Plugin> CREATOR = new Parcelable.Creator<Plugin>() {
public static final Parcelable.Creator<Addon> CREATOR = new Parcelable.Creator<Addon>() {
/**
* {@inheritDoc}
*/
public Plugin createFromParcel(Parcel source) {
return new Plugin(source.readString(), source.readString(),
public Addon createFromParcel(Parcel source) {
return new Addon(source.readString(), source.readString(),
source.readString(), source.readString());
}
/**
* {@inheritDoc}
*/
public Plugin[] newArray(int size) {
return new Plugin[size];
public Addon[] newArray(int size) {
return new Addon[size];
};
};

@ -3,6 +3,8 @@
*/
package com.todoroo.astrid.api;
import android.widget.RemoteViews;
/**
* Constants for interfacing with Astrid.
*
@ -28,32 +30,35 @@ public class AstridApiConstants {
*/
public static final String PERMISSION_WRITE = PACKAGE + ".WRITE";
// --- Broadcast Extras
/**
* Extras name for Task id
* Extras name for task id
*/
public static final String EXTRAS_TASK_ID = "task";
/**
* Extras name for an array of response items
* Extras name for a response item broadcast to astrid
*/
public static final String EXTRAS_ITEMS = "items";
public static final String EXTRAS_RESPONSE = "response";
/**
* Extras name for plug-in object
* Extras name for plug-in identifier
*/
public static final String EXTRAS_PLUGIN = "plugin";
public static final String EXTRAS_ADDON = "addon";
// --- Plug-ins API
// --- Add-ons API
/**
* Action name for broadcast intent requesting filters
* Action name for broadcast intent requesting add-ons
*/
public static final String BROADCAST_REQUEST_PLUGINS = PACKAGE + ".REQUEST_PLUGINS";
public static final String BROADCAST_REQUEST_ADDONS = PACKAGE + ".REQUEST_ADDONS";
/**
* Action name for broadcast intent sending filters back to Astrid
* Action name for broadcast intent sending add-ons back to Astrid
* @extra EXTRAS_RESPONSE an {@link Addon} object
*/
public static final String BROADCAST_SEND_PLUGINS = PACKAGE + ".SEND_PLUGINS";
public static final String BROADCAST_SEND_ADDONS = PACKAGE + ".SEND_ADDONS";
// --- Filters API
@ -64,30 +69,39 @@ public class AstridApiConstants {
/**
* Action name for broadcast intent sending filters back to Astrid
* @extra EXTRAS_ADDON your add-on identifier
* @extra EXTRAS_RESPONSE an array of {@link FilterListItem}s
*/
public static final String BROADCAST_SEND_FILTERS = PACKAGE + ".SEND_FILTERS";
// --- Edit Operations API
// --- Edit Controls API
/**
* Action name for broadcast intent requesting task edit operations
* Action name for broadcast intent requesting task edit controls
* @extra EXTRAS_TASK_ID id of the task user is editing
*/
public static final String BROADCAST_REQUEST_EDIT_OPERATIONS = PACKAGE + ".REQUEST_EDIT_OPERATIONS";
public static final String BROADCAST_REQUEST_EDIT_CONTROLS = PACKAGE + ".REQUEST_EDIT_CONTROLS";
/**
* Action name for broadcast intent sending task edit operations back to Astrid
* Action name for broadcast intent sending task edit controls back to Astrid
* @extra EXTRAS_ADDON your add-on identifier
* @extra EXTRAS_RESPONSE a {@link RemoteViews} with your edit controls
*/
public static final String BROADCAST_SEND_EDIT_OPERATIONS = PACKAGE + ".SEND_EDIT_OPERATIONS";
public static final String BROADCAST_SEND_EDIT_CONTROLS = PACKAGE + ".SEND_EDIT_CONTROLS";
// --- Task List Details API
/**
* Action name for broadcast intent requesting task list details for a task
* @extra EXTRAS_TASK_ID id of the task
*/
public static final String BROADCAST_REQUEST_DETAILS = PACKAGE + ".REQUEST_DETAILS";
/**
* Action name for broadcast intent sending details back to Astrid
* @extra EXTRAS_ADDON your add-on identifier
* @extra EXTRAS_TASK_ID id of the task
* @extra EXTRAS_RESPONSE a {@link TaskDetail} object
*/
public static final String BROADCAST_SEND_DETAILS = PACKAGE + ".SEND_DETAILS";
@ -95,14 +109,19 @@ public class AstridApiConstants {
/**
* Action name for intents to be displayed on task context menu
* @extra EXTRAS_TASK_ID id of the task
*/
public static final String ACTION_TASK_CONTEXT_MENU = PACKAGE + ".CONTEXT_MENU";
/**
* Action name for intents to be displayed on Astrid's task list menu
* @extra EXTRAS_ADDON your add-on identifier
* @extra EXTRAS_RESPONSE an array of {@link Intent}s
*/
public static final String ACTION_TASK_LIST_MENU = PACKAGE + ".TASK_LIST_MENU";
// --- Settings API
/**
* Action name for intents to be displayed in Astrid's settings
*/
@ -112,11 +131,13 @@ public class AstridApiConstants {
/**
* Action name for broadcast intent notifying that task was completed
* @extra EXTRAS_TASK_ID id of the task
*/
public static final String BROADCAST_EVENT_TASK_COMPLETED = PACKAGE + ".TASK_COMPLETED";
/**
* Action name for broadcast intent notifying that task was created
* @extra EXTRAS_TASK_ID id of the task
*/
public static final String BROADCAST_EVENT_TASK_CREATED = PACKAGE + ".TASK_CREATED";

@ -34,7 +34,7 @@ public final class EditOperation implements Parcelable {
* Create an EditOperation object
*
* @param plugin
* {@link Plugin} identifier that encompasses object
* {@link Addon} identifier that encompasses object
* @param text
* label to display
* @param intent

@ -64,7 +64,7 @@ public final class Filter extends FilterListItem {
* Utility constructor for creating a TaskList object
*
* @param plugin
* {@link Plugin} identifier that encompasses object
* {@link Addon} identifier that encompasses object
* @param listingTitle
* Title of this item as displayed on the lists page, e.g. Inbox
* @param title
@ -88,7 +88,7 @@ public final class Filter extends FilterListItem {
* Utility constructor
*
* @param plugin
* {@link Plugin} identifier that encompasses object
* {@link Addon} identifier that encompasses object
*/
protected Filter(String plugin) {
this.plugin = plugin;

@ -29,7 +29,7 @@ public class FilterCategory extends FilterListItem {
* Constructor for creating a new FilterCategory
*
* @param plugin
* {@link Plugin} identifier that encompasses object
* {@link Addon} identifier that encompasses object
* @param listingTitle
* Title of this item as displayed on the lists page, e.g. Inbox
* @param children
@ -46,7 +46,7 @@ public class FilterCategory extends FilterListItem {
* Constructor for creating a new FilterCategory
*
* @param plugin
* {@link Plugin} identifier that encompasses object
* {@link Addon} identifier that encompasses object
*/
protected FilterCategory(String plugin) {
this.plugin = plugin;

@ -23,7 +23,7 @@ public class FilterListHeader extends FilterListItem {
* Constructor for creating a new FilterListHeader
*
* @param plugin
* {@link Plugin} identifier that encompasses object
* {@link Addon} identifier that encompasses object
* @param listingTitle
* @param listingIconResource
* @param priority
@ -37,7 +37,7 @@ public class FilterListHeader extends FilterListItem {
* Constructor for creating a new FilterListHeader
*
* @param plugin
* {@link Plugin} identifier that encompasses object
* {@link Addon} identifier that encompasses object
*/
protected FilterListHeader(String plugin) {
this.plugin = plugin;

@ -33,7 +33,7 @@ public final class TaskDetail implements Parcelable {
* Creates a TaskDetail object
*
* @param plugin
* {@link Plugin} identifier that encompasses object
* {@link Addon} identifier that encompasses object
* @param text
* text to display
* @param color

@ -4,20 +4,22 @@
<booleanAttribute key="ch.zork.quicklaunch" value="true"/>
<stringAttribute key="ch.zork.quicklaunch.icon" value="14.gif"/>
<intAttribute key="ch.zork.quicklaunch.index" value="0"/>
<stringAttribute key="ch.zork.quicklaunch.mode" value="debug"/>
<stringAttribute key="ch.zork.quicklaunch.mode" value="run"/>
<intAttribute key="com.android.ide.eclipse.adt.action" value="0"/>
<stringAttribute key="com.android.ide.eclipse.adt.avd" value="android-22"/>
<stringAttribute key="com.android.ide.eclipse.adt.avd" value="mytouch-4"/>
<stringAttribute key="com.android.ide.eclipse.adt.commandline" value="-scale 0.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="true"/>
<booleanAttribute key="com.android.ide.eclipse.adt.target" value="false"/>
<booleanAttribute key="com.android.ide.eclipse.adt.wipedata" value="false"/>
<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_PATHS">
<listEntry value="/astrid"/>
<listEntry value="/astrid/AndroidManifest.xml"/>
</listAttribute>
<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_TYPES">
<listEntry value="4"/>
<listEntry value="1"/>
</listAttribute>
<listAttribute key="org.eclipse.debug.ui.favoriteGroups">
<listEntry value="org.eclipse.debug.ui.launchGroup.debug"/>

@ -10,5 +10,5 @@
# Indicates whether an apk should be generated for each density.
split.density=false
# Project target.
target=Google Inc.:Google APIs:8
target=android-4
apk-configurations=

@ -54,7 +54,7 @@ public final class CoreFilterExposer extends BroadcastReceiver {
list[1] = all;
list[2] = searchFilter;
Intent broadcastIntent = new Intent(AstridApiConstants.BROADCAST_SEND_FILTERS);
broadcastIntent.putExtra(AstridApiConstants.EXTRAS_ITEMS, list);
broadcastIntent.putExtra(AstridApiConstants.EXTRAS_RESPONSE, list);
context.sendBroadcast(broadcastIntent, AstridApiConstants.PERMISSION_READ);
}

@ -5,7 +5,7 @@ import android.content.Context;
import android.content.Intent;
import com.todoroo.astrid.api.AstridApiConstants;
import com.todoroo.astrid.api.Plugin;
import com.todoroo.astrid.api.Addon;
public class CorePlugin extends BroadcastReceiver {
@ -13,11 +13,11 @@ public class CorePlugin extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Plugin plugin = new Plugin(IDENTIFIER, "Core Filters", "Todoroo",
Addon plugin = new Addon(IDENTIFIER, "Core Filters", "Todoroo",
"Provides 'Inbox' and 'All Tasks' Filters");
Intent broadcastIntent = new Intent(AstridApiConstants.BROADCAST_SEND_PLUGINS);
broadcastIntent.putExtra(AstridApiConstants.EXTRAS_PLUGIN, plugin);
Intent broadcastIntent = new Intent(AstridApiConstants.BROADCAST_SEND_ADDONS);
broadcastIntent.putExtra(AstridApiConstants.EXTRAS_ADDON, plugin);
context.sendBroadcast(broadcastIntent, AstridApiConstants.PERMISSION_READ);
}

@ -82,7 +82,7 @@ public final class ExtendedFilterExposer extends BroadcastReceiver {
list[3] = hidden;
list[4] = deleted;
Intent broadcastIntent = new Intent(AstridApiConstants.BROADCAST_SEND_FILTERS);
broadcastIntent.putExtra(AstridApiConstants.EXTRAS_ITEMS, list);
broadcastIntent.putExtra(AstridApiConstants.EXTRAS_RESPONSE, list);
context.sendBroadcast(broadcastIntent, AstridApiConstants.PERMISSION_READ);
}

@ -5,7 +5,7 @@ import android.content.Context;
import android.content.Intent;
import com.todoroo.astrid.api.AstridApiConstants;
import com.todoroo.astrid.api.Plugin;
import com.todoroo.astrid.api.Addon;
public class ExtendedPlugin extends BroadcastReceiver {
@ -13,11 +13,11 @@ public class ExtendedPlugin extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Plugin plugin = new Plugin(IDENTIFIER, "Extended Filters", "Todoroo",
Addon plugin = new Addon(IDENTIFIER, "Extended Filters", "Todoroo",
"Provides extended filters for viewing subsets of your tasks");
Intent broadcastIntent = new Intent(AstridApiConstants.BROADCAST_SEND_PLUGINS);
broadcastIntent.putExtra(AstridApiConstants.EXTRAS_PLUGIN, plugin);
Intent broadcastIntent = new Intent(AstridApiConstants.BROADCAST_SEND_ADDONS);
broadcastIntent.putExtra(AstridApiConstants.EXTRAS_ADDON, plugin);
context.sendBroadcast(broadcastIntent, AstridApiConstants.PERMISSION_READ);
}

@ -51,10 +51,8 @@ public class NoteDetailExposer extends BroadcastReceiver {
TaskDetail taskDetail = new TaskDetail(NotesPlugin.IDENTIFIER, notes);
// transmit
TaskDetail[] details = new TaskDetail[1];
details[0] = taskDetail;
Intent broadcastIntent = new Intent(AstridApiConstants.BROADCAST_SEND_DETAILS);
broadcastIntent.putExtra(AstridApiConstants.EXTRAS_ITEMS, details);
broadcastIntent.putExtra(AstridApiConstants.EXTRAS_RESPONSE, taskDetail);
broadcastIntent.putExtra(AstridApiConstants.EXTRAS_TASK_ID, taskId);
context.sendBroadcast(broadcastIntent, AstridApiConstants.PERMISSION_READ);
}

@ -5,7 +5,7 @@ import android.content.Context;
import android.content.Intent;
import com.todoroo.astrid.api.AstridApiConstants;
import com.todoroo.astrid.api.Plugin;
import com.todoroo.astrid.api.Addon;
public class NotesPlugin extends BroadcastReceiver {
@ -13,11 +13,11 @@ public class NotesPlugin extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Plugin plugin = new Plugin(IDENTIFIER, "Notes", "Todoroo",
Addon plugin = new Addon(IDENTIFIER, "Notes", "Todoroo",
"Lets you add and view notes for a task.");
Intent broadcastIntent = new Intent(AstridApiConstants.BROADCAST_SEND_PLUGINS);
broadcastIntent.putExtra(AstridApiConstants.EXTRAS_PLUGIN, plugin);
Intent broadcastIntent = new Intent(AstridApiConstants.BROADCAST_SEND_ADDONS);
broadcastIntent.putExtra(AstridApiConstants.EXTRAS_ADDON, plugin);
context.sendBroadcast(broadcastIntent, AstridApiConstants.PERMISSION_READ);
}

@ -0,0 +1,214 @@
package com.todoroo.astrid.repeats;
import java.text.DateFormatSymbols;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Calendar;
import android.app.Activity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewGroup.LayoutParams;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemSelectedListener;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.CompoundButton.OnCheckedChangeListener;
import android.widget.LinearLayout;
import android.widget.Spinner;
import com.google.ical.values.Frequency;
import com.google.ical.values.RRule;
import com.google.ical.values.Weekday;
import com.google.ical.values.WeekdayNum;
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.astrid.activity.TaskEditActivity.TaskEditControlSet;
import com.todoroo.astrid.model.Task;
/**
* Control Set for managing repeats
*
* @author Tim Su <tim@todoroo.com>
*
*/
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 final Activity activity;
private final CheckBox enabled;
private final Button value;
private final Spinner interval;
private final Spinner type;
private final LinearLayout repeatContainer;
private final LinearLayout daysOfWeekContainer;
private final CompoundButton[] daysOfWeek = new CompoundButton[7];
public RepeatControlSet(final Activity activity, ViewGroup parent) {
this.activity = activity;
LayoutInflater.from(activity).inflate(R.layout.repeat_control, parent, true);
enabled = (CheckBox) activity.findViewById(R.id.repeatEnabled);
value = (Button) activity.findViewById(R.id.repeatValue);
interval = (Spinner) activity.findViewById(R.id.repeatInterval);
type = (Spinner) activity.findViewById(R.id.repeatType);
repeatContainer = (LinearLayout) activity.findViewById(R.id.repeatContainer);
daysOfWeekContainer = (LinearLayout) activity.findViewById(R.id.repeatDayOfWeekContainer);
setRepeatValue(1);
// set up days of week
DateFormatSymbols dfs = new DateFormatSymbols();
Calendar calendar = Calendar.getInstance();
int currentDayOfWeek = calendar.get(Calendar.DAY_OF_WEEK);
calendar.set(Calendar.DAY_OF_WEEK, calendar.getFirstDayOfWeek());
LayoutParams lp = new LinearLayout.LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT, 1.0f/7);
for(int i = 0; i < 7; i++) {
CheckBox checkBox = new CheckBox(activity);
int dayOfWeek = calendar.get(Calendar.DAY_OF_WEEK);
checkBox.setText(dfs.getShortWeekdays()[dayOfWeek].substring(0, 1));
checkBox.setLayoutParams(lp);
checkBox.setTextSize(10);
checkBox.setTag(Weekday.values()[dayOfWeek - 1]);
if(dayOfWeek == currentDayOfWeek)
checkBox.setChecked(true);
daysOfWeek[i] = checkBox;
calendar.add(Calendar.DATE, 1);
daysOfWeekContainer.addView(checkBox);
}
// set up listeners
enabled.setOnCheckedChangeListener(new OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
repeatContainer.setVisibility(isChecked ? View.VISIBLE : View.GONE);
}
});
value.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
repeatValueClick();
}
});
interval.setOnItemSelectedListener(new OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> parentView, View view, int position, long id) {
daysOfWeekContainer.setVisibility(position == INTERVAL_WEEKS ? View.VISIBLE : View.GONE);
}
@Override
public void onNothingSelected(AdapterView<?> arg0) {
//
}
});
}
/** Set up the repeat value button */
private void setRepeatValue(int newValue) {
value.setText(activity.getString(R.string.repeat_every, newValue));
value.setTag(newValue);
}
protected void repeatValueClick() {
final int tagValue = (Integer)value.getTag();
final Runnable openDialogRunnable = new Runnable() {
public void run() {
int dialogValue = tagValue;
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_picker_title),
dialogValue, 1, 1, 365).show();
}
};
openDialogRunnable.run();
}
@Override
public void readFromTask(Task task) {
String recurrence = task.getValue(Task.RECURRENCE);
if(recurrence.length() > 0) {
try {
RRule rrule = new RRule(recurrence);
setRepeatValue(rrule.getInterval());
switch(rrule.getFreq()) {
case DAILY:
interval.setSelection(INTERVAL_DAYS);
break;
case WEEKLY: {
interval.setSelection(INTERVAL_WEEKS);
for(WeekdayNum day : rrule.getByDay()) {
for(int i = 0; i < 7; i++)
if(daysOfWeek[i].getTag() == day.wday)
daysOfWeek[i].setChecked(true);
}
break;
}
case MONTHLY:
interval.setSelection(INTERVAL_MONTHS);
break;
case YEARLY:
interval.setSelection(INTERVAL_YEARS);
break;
}
} catch (ParseException e) {
recurrence = ""; //$NON-NLS-1$
}
}
enabled.setChecked(recurrence.length() > 0);
repeatContainer.setVisibility(enabled.isChecked() ? View.VISIBLE : View.GONE);
}
@Override
public void writeToModel(Task task) {
String result;
if(!enabled.isChecked())
result = ""; //$NON-NLS-1$
else {
RRule rrule = new RRule();
rrule.setInterval((Integer)value.getTag());
switch(interval.getSelectedItemPosition()) {
case INTERVAL_DAYS:
rrule.setFreq(Frequency.DAILY);
break;
case INTERVAL_WEEKS: {
rrule.setFreq(Frequency.WEEKLY);
ArrayList<WeekdayNum> days = new ArrayList<WeekdayNum>();
for(int i = 0; i < daysOfWeek.length; i++)
if(daysOfWeek[i].isChecked())
days.add(new WeekdayNum(0, (Weekday)daysOfWeek[i].getTag()));
rrule.setByDay(days);
break;
}
case INTERVAL_MONTHS:
rrule.setFreq(Frequency.MONTHLY);
break;
case INTERVAL_YEARS:
rrule.setFreq(Frequency.YEARLY);
}
result = rrule.toIcal();
}
task.setValue(Task.RECURRENCE, result);
}
}

@ -0,0 +1,26 @@
package com.todoroo.astrid.repeats;
import android.content.BroadcastReceiver;
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;
public class RepeatsPlugin extends BroadcastReceiver {
static final String IDENTIFIER = "repeats"; //$NON-NLS-1$
@Override
public void onReceive(Context context, Intent intent) {
Addon plugin = new Addon(IDENTIFIER, context.getString(R.string.repeat_plugin),
context.getString(R.string.AOA_internal_author),
context.getString(R.string.repeat_plugin_desc));
Intent broadcastIntent = new Intent(AstridApiConstants.BROADCAST_SEND_ADDONS);
broadcastIntent.putExtra(AstridApiConstants.EXTRAS_ADDON, plugin);
context.sendBroadcast(broadcastIntent, AstridApiConstants.PERMISSION_READ);
}
}

@ -39,10 +39,8 @@ public class TagDetailExposer extends BroadcastReceiver {
context.getString(R.string.tag_TLA_detail, tagList));
// transmit
TaskDetail[] details = new TaskDetail[1];
details[0] = taskDetail;
Intent broadcastIntent = new Intent(AstridApiConstants.BROADCAST_SEND_DETAILS);
broadcastIntent.putExtra(AstridApiConstants.EXTRAS_ITEMS, details);
broadcastIntent.putExtra(AstridApiConstants.EXTRAS_RESPONSE, taskDetail);
broadcastIntent.putExtra(AstridApiConstants.EXTRAS_TASK_ID, taskId);
context.sendBroadcast(broadcastIntent, AstridApiConstants.PERMISSION_READ);
}

@ -97,7 +97,7 @@ public class TagFilterExposer extends BroadcastReceiver {
list[2] = tagsCategoryBySize;
list[3] = tagsCategoryByAlpha;
Intent broadcastIntent = new Intent(AstridApiConstants.BROADCAST_SEND_FILTERS);
broadcastIntent.putExtra(AstridApiConstants.EXTRAS_ITEMS, list);
broadcastIntent.putExtra(AstridApiConstants.EXTRAS_RESPONSE, list);
context.sendBroadcast(broadcastIntent, AstridApiConstants.PERMISSION_READ);
}

@ -5,7 +5,7 @@ import android.content.Context;
import android.content.Intent;
import com.todoroo.astrid.api.AstridApiConstants;
import com.todoroo.astrid.api.Plugin;
import com.todoroo.astrid.api.Addon;
public class TagsPlugin extends BroadcastReceiver {
@ -13,11 +13,11 @@ public class TagsPlugin extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Plugin plugin = new Plugin(IDENTIFIER, "Tags", "Todoroo",
Addon plugin = new Addon(IDENTIFIER, "Tags", "Todoroo",
"Provides tagging support for tasks.");
Intent broadcastIntent = new Intent(AstridApiConstants.BROADCAST_SEND_PLUGINS);
broadcastIntent.putExtra(AstridApiConstants.EXTRAS_PLUGIN, plugin);
Intent broadcastIntent = new Intent(AstridApiConstants.BROADCAST_SEND_ADDONS);
broadcastIntent.putExtra(AstridApiConstants.EXTRAS_ADDON, plugin);
context.sendBroadcast(broadcastIntent, AstridApiConstants.PERMISSION_READ);
}

@ -0,0 +1,53 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- See the file "LICENSE" for the full license governing this code. -->
<merge xmlns:android="http://schemas.android.com/apk/res/android">
<View
android:layout_width="fill_parent"
android:layout_height="1dip"
android:padding="5dip"
android:background="@android:drawable/divider_horizontal_dark" />
<CheckBox android:id="@+id/repeatEnabled"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/repeat_enabled"/>
<LinearLayout android:id="@+id/repeatContainer"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<LinearLayout
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<Button android:id="@+id/repeatValue"
android:layout_weight="1"
android:layout_width="fill_parent"
android:layout_height="wrap_content" />
<Spinner android:id="@+id/repeatInterval"
android:layout_weight="1"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:prompt="@string/repeat_interval_prompt"
android:entries="@array/repeat_interval"/>
</LinearLayout>
<Spinner android:id="@+id/repeatType"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:entries="@array/repeat_type"/>
<LinearLayout android:id="@+id/repeatDayOfWeekContainer"
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="wrap_content" />
</LinearLayout>
</merge>

@ -196,43 +196,16 @@
android:paddingBottom="5dip"
android:layout_width="fill_parent"
android:layout_height="wrap_content" />
<View
android:layout_width="fill_parent"
android:layout_height="1dip"
android:padding="5dip"
android:background="@android:drawable/divider_horizontal_dark" />
<!-- repeat -->
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/repeat_label"
android:visibility="gone"
style="@style/TextAppearance.GEN_EditLabel" />
<LinearLayout
android:orientation="horizontal"
android:paddingRight="10dip"
android:visibility="gone"
<!-- add-ons -->
<LinearLayout android:id="@+id/tab_extra_addons"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<Button
android:id="@+id/repeat_value"
android:layout_weight="1"
android:layout_width="fill_parent"
android:layout_height="wrap_content" />
<Spinner
android:id="@+id/repeat_interval"
android:layout_weight="1"
android:layout_width="fill_parent"
android:layout_height="wrap_content" />
</LinearLayout>
<!-- separator -->
android:layout_height="wrap_content"
android:orientation="vertical" />
<View
android:layout_width="fill_parent"
android:layout_height="1dip"
android:visibility="gone"
android:padding="5dip"
android:background="@android:drawable/divider_horizontal_dark" />
@ -332,6 +305,12 @@
android:layout_width="fill_parent"
android:layout_height="wrap_content" />
<!-- add-ons -->
<LinearLayout android:id="@+id/tab_addons_addons"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="vertical" />
<!-- separator -->
<View
android:layout_width="fill_parent"

@ -18,7 +18,7 @@
<!-- permission description for READ_TASKS -->
<string name="write_permission_desc">create new tasks, edit existing tasks</string>
<!-- ======================== Generic Plurals ========================== -->
<!-- ==================================================== Generic Units == -->
<plurals name="DUt_years">
<!-- plurals: years -->
@ -397,4 +397,10 @@ If you don\'t want to see the new task right after you complete the old one, you
<!-- Default Hide Until Description (%s => setting) -->
<string name="EPr_default_hideUntil_desc">Currently Set To: %s</string>
<!-- ==================================================== AddOnActivity == -->
<!-- Add Ons: author for internal authors -->
<string name="AOA_internal_author">Astrid Team</string>
</resources>

@ -0,0 +1,36 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- See the file "LICENSE" for the full license governing this code. -->
<resources>
<!-- Resources for built-in repeat plug-in -->
<!-- repeating plugin name -->
<string name="repeat_plugin">Repeating Tasks</string>
<!-- repeating plugin description -->
<string name="repeat_plugin_desc">Allows tasks to repeat</string>
<!-- checkbox for turning on/off repeats -->
<string name="repeat_enabled">Repeats</string>
<!-- button for "every x" part of repeat (%d -> repeat value) -->
<string name="repeat_every">Every %d</string>
<!-- hint when opening repeat interval -->
<string name="repeat_interval_prompt">Repeat Interval</string>
<string-array name="repeat_interval">
<!-- repeat interval (days,weeks,months,hours) -->
<item>Day(s)</item>
<item>Week(s)</item>
<item>Month(s)</item>
<item>Year(s)</item>
</string-array>
<string-array name="repeat_type">
<!-- repeat type (date to repeat from) -->
<item>from due date</item>
<item>from completion date</item>
</string-array>
</resources>

@ -183,14 +183,14 @@ public class FilterListActivity extends ExpandableListActivity {
public void onReceive(Context context, Intent intent) {
try {
Parcelable[] filters = intent.getExtras().
getParcelableArray(AstridApiConstants.EXTRAS_ITEMS);
getParcelableArray(AstridApiConstants.EXTRAS_RESPONSE);
for (Parcelable item : filters) {
adapter.add((FilterListItem)item);
}
adapter.notifyDataSetChanged();
} catch (Exception e) {
exceptionService.reportError("receive-filter-" + //$NON-NLS-1$
intent.getStringExtra(AstridApiConstants.EXTRAS_PLUGIN), e);
intent.getStringExtra(AstridApiConstants.EXTRAS_ADDON), e);
}
}
}

@ -27,13 +27,16 @@ import java.util.List;
import android.app.AlertDialog;
import android.app.DatePickerDialog;
import android.app.DatePickerDialog.OnDateSetListener;
import android.app.TabActivity;
import android.app.TimePickerDialog;
import android.app.DatePickerDialog.OnDateSetListener;
import android.app.TimePickerDialog.OnTimeSetListener;
import android.content.BroadcastReceiver;
import android.content.ContentValues;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.res.Resources;
import android.os.Bundle;
import android.text.Editable;
@ -44,6 +47,7 @@ import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup.LayoutParams;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemSelectedListener;
import android.widget.ArrayAdapter;
import android.widget.AutoCompleteTextView;
import android.widget.Button;
@ -53,35 +57,32 @@ import android.widget.DatePicker;
import android.widget.EditText;
import android.widget.ImageButton;
import android.widget.LinearLayout;
import android.widget.RemoteViews;
import android.widget.Spinner;
import android.widget.TabHost;
import android.widget.TextView;
import android.widget.TimePicker;
import android.widget.Toast;
import android.widget.ToggleButton;
import android.widget.AdapterView.OnItemSelectedListener;
import com.flurry.android.FlurryAgent;
import com.timsu.astrid.R;
import com.timsu.astrid.data.enums.RepeatInterval;
import com.timsu.astrid.data.task.TaskModelForEdit;
import com.timsu.astrid.data.task.AbstractTaskModel.RepeatInfo;
import com.timsu.astrid.utilities.AstridUtilities;
import com.timsu.astrid.widget.NumberPicker;
import com.timsu.astrid.widget.NumberPickerDialog;
import com.timsu.astrid.widget.TimeDurationControlSet;
import com.timsu.astrid.widget.NumberPickerDialog.OnNumberPickedListener;
import com.timsu.astrid.widget.TimeDurationControlSet.TimeDurationType;
import com.todoroo.andlib.data.TodorooCursor;
import com.todoroo.andlib.data.Property.IntegerProperty;
import com.todoroo.andlib.data.Property.StringProperty;
import com.todoroo.andlib.data.TodorooCursor;
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.dao.Database;
import com.todoroo.astrid.model.Metadata;
import com.todoroo.astrid.model.Task;
import com.todoroo.astrid.repeats.RepeatControlSet;
import com.todoroo.astrid.service.StartupService;
import com.todoroo.astrid.service.TaskService;
import com.todoroo.astrid.tags.TagService;
@ -155,6 +156,9 @@ public final class TaskEditActivity extends TabActivity {
/** whether task should be saved when this activity exits */
private boolean shouldSaveState = true;
/** edit control receiver */
private final ControlReceiver controlReceiver = new ControlReceiver();
/* ======================================================================
* ======================================================= initialization
* ====================================================================== */
@ -214,7 +218,6 @@ public final class TaskEditActivity extends TabActivity {
controls.add( new RandomReminderControlSet(R.id.reminder_random,
R.id.reminder_random_interval));
controls.add(new TagsControlSet(R.id.tags_container));
controls.add(new RepeatControlSet(R.id.repeat_value, R.id.repeat_interval));
controls.add(new CalendarControlSet(R.id.add_to_calendar, R.id.view_calendar_event));
controls.add(new TimeDurationTaskEditControlSet(Task.ESTIMATED_SECONDS,
@ -224,9 +227,18 @@ public final class TaskEditActivity extends TabActivity {
0, R.string.hour_minutes_dialog,
TimeDurationType.HOURS_MINUTES));
// internal add-ins
LinearLayout extrasAddons = (LinearLayout) findViewById(R.id.tab_extra_addons);
controls.add(new RepeatControlSet(this, extrasAddons));
// read data
populateFields();
// request add-on controls
Intent broadcastIntent = new Intent(AstridApiConstants.BROADCAST_REQUEST_EDIT_CONTROLS);
broadcastIntent.putExtra(AstridApiConstants.EXTRAS_TASK_ID, model.getId());
sendOrderedBroadcast(broadcastIntent, AstridApiConstants.PERMISSION_READ);
// set up listeners
setUpListeners();
}
@ -324,7 +336,7 @@ public final class TaskEditActivity extends TabActivity {
setTitle(r.getString(R.string.TEA_view_title, model.getValue(Task.TITLE)));
for(TaskEditControlSet controlSet : controls)
controlSet.readFromModel();
controlSet.readFromTask(model);
}
/** Save task model from values in UI components */
@ -338,12 +350,45 @@ public final class TaskEditActivity extends TabActivity {
}
for(TaskEditControlSet controlSet : controls)
controlSet.writeToModel();
controlSet.writeToModel(model);
taskService.save(model, false);
showSaveToast();
}
/* ======================================================================
* ================================================ edit control handling
* ====================================================================== */
/**
* Receiver which receives intents to add items to the filter list
*
* @author Tim Su <tim@todoroo.com>
*
*/
protected class ControlReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
try {
Bundle extras = intent.getExtras();
RemoteViews view = extras.getParcelable(AstridApiConstants.EXTRAS_RESPONSE);
// add a separator
View separator = new View(TaskEditActivity.this);
separator.setPadding(5, 5, 5, 5);
separator.setBackgroundResource(android.R.drawable.divider_horizontal_dark);
LinearLayout dest = (LinearLayout)findViewById(R.id.tab_addons_addons);
dest.addView(separator);
view.apply(TaskEditActivity.this, dest);
} catch (Exception e) {
exceptionService.reportError("receive-detail-" + //$NON-NLS-1$
intent.getStringExtra(AstridApiConstants.EXTRAS_ADDON), e);
}
}
}
/* ======================================================================
* ======================================================= event handlers
* ====================================================================== */
@ -475,13 +520,15 @@ public final class TaskEditActivity extends TabActivity {
protected void onPause() {
if(shouldSaveState)
save();
super.onPause();
unregisterReceiver(controlReceiver);
}
@Override
protected void onResume() {
super.onResume();
registerReceiver(controlReceiver,
new IntentFilter(AstridApiConstants.BROADCAST_SEND_EDIT_CONTROLS));
}
@Override
@ -517,12 +564,12 @@ public final class TaskEditActivity extends TabActivity {
/**
* Read data from model to update the control set
*/
public void readFromModel();
public void readFromTask(Task task);
/**
* Write data from control set to model
*/
public void writeToModel();
public void writeToModel(Task task);
}
// --- EditTextControlSet
@ -542,13 +589,13 @@ public final class TaskEditActivity extends TabActivity {
}
@Override
public void readFromModel() {
editText.setText(model.getValue(property));
public void readFromTask(Task task) {
editText.setText(task.getValue(property));
}
@Override
public void writeToModel() {
model.setValue(property, editText.getText().toString());
public void writeToModel(Task task) {
task.setValue(property, editText.getText().toString());
}
}
@ -571,13 +618,13 @@ public final class TaskEditActivity extends TabActivity {
}
@Override
public void readFromModel() {
controlSet.setTimeDuration(model.getValue(property));
public void readFromTask(Task task) {
controlSet.setTimeDuration(task.getValue(property));
}
@Override
public void writeToModel() {
model.setValue(property, controlSet.getTimeDurationInSeconds());
public void writeToModel(Task task) {
task.setValue(property, controlSet.getTimeDurationInSeconds());
}
}
@ -643,13 +690,13 @@ public final class TaskEditActivity extends TabActivity {
}
@Override
public void readFromModel() {
setImportance(model.getValue(Task.IMPORTANCE));
public void readFromTask(Task task) {
setImportance(task.getValue(Task.IMPORTANCE));
}
@Override
public void writeToModel() {
model.setValue(Task.IMPORTANCE, getImportance());
public void writeToModel(Task task) {
task.setValue(Task.IMPORTANCE, getImportance());
}
}
@ -812,15 +859,15 @@ public final class TaskEditActivity extends TabActivity {
// --- setting up values
@Override
public void readFromModel() {
long dueDate = model.getValue(Task.DUE_DATE);
public void readFromTask(Task task) {
long dueDate = task.getValue(Task.DUE_DATE);
createUrgencyList(dueDate);
}
@Override
public void writeToModel() {
public void writeToModel(Task task) {
UrgencyValue item = urgencyAdapter.getItem(urgency.getSelectedItemPosition());
model.setValue(Task.DUE_DATE, item.dueDate);
task.setValue(Task.DUE_DATE, item.dueDate);
}
}
@ -938,9 +985,9 @@ public final class TaskEditActivity extends TabActivity {
// --- setting up values
@Override
public void readFromModel() {
long date = model.getValue(Task.HIDE_UNTIL);
long dueDate = model.getValue(Task.DUE_DATE);
public void readFromTask(Task task) {
long date = task.getValue(Task.HIDE_UNTIL);
long dueDate = task.getValue(Task.DUE_DATE);
int selection = 0;
if(date == 0) {
@ -968,10 +1015,10 @@ public final class TaskEditActivity extends TabActivity {
}
@Override
public void writeToModel() {
public void writeToModel(Task task) {
HideUntilValue item = adapter.getItem(hideUntil.getSelectedItemPosition());
long value = model.createHideUntil(item.setting, item.date);
model.setValue(Task.HIDE_UNTIL, value);
long value = task.createHideUntil(item.setting, item.date);
task.setValue(Task.HIDE_UNTIL, value);
}
}
@ -1021,13 +1068,13 @@ public final class TaskEditActivity extends TabActivity {
}
@Override
public void readFromModel() {
setValue(model.getValue(Task.REMINDER_FLAGS));
public void readFromTask(Task task) {
setValue(task.getValue(Task.REMINDER_FLAGS));
}
@Override
public void writeToModel() {
model.setValue(Task.REMINDER_FLAGS, getValue());
public void writeToModel(Task task) {
task.setValue(Task.REMINDER_FLAGS, getValue());
}
}
@ -1076,8 +1123,8 @@ public final class TaskEditActivity extends TabActivity {
}
@Override
public void readFromModel() {
long time = model.getValue(Task.REMINDER_PERIOD);
public void readFromTask(Task task) {
long time = task.getValue(Task.REMINDER_PERIOD);
boolean shouldDisable = time <= 0;
if(time <= 0) {
@ -1093,12 +1140,12 @@ public final class TaskEditActivity extends TabActivity {
}
@Override
public void writeToModel() {
public void writeToModel(Task task) {
if(settingCheckbox.isChecked()) {
int hourValue = hours[periodSpinner.getSelectedItemPosition()];
model.setValue(Task.REMINDER_PERIOD, hourValue * DateUtilities.ONE_HOUR);
task.setValue(Task.REMINDER_PERIOD, hourValue * DateUtilities.ONE_HOUR);
} else
model.setValue(Task.REMINDER_PERIOD, 0L);
task.setValue(Task.REMINDER_PERIOD, 0L);
}
}
@ -1121,10 +1168,10 @@ public final class TaskEditActivity extends TabActivity {
@SuppressWarnings("nls")
@Override
public void readFromModel() {
public void readFromTask(Task task) {
// tags (only configure if not already set)
if(tagsContainer.getChildCount() == 0) {
TodorooCursor<Metadata> cursor = tagService.getTags(model.getId());
TodorooCursor<Metadata> cursor = tagService.getTags(task.getId());
try {
for(cursor.moveToFirst(); !cursor.isAfterLast(); cursor.moveToNext())
addTag(cursor.get(Metadata.VALUE));
@ -1136,7 +1183,7 @@ public final class TaskEditActivity extends TabActivity {
}
@Override
public void writeToModel() {
public void writeToModel(Task task) {
ArrayList<String> tags = new ArrayList<String>();
for(int i = 0; i < tagsContainer.getChildCount(); i++) {
@ -1146,7 +1193,7 @@ public final class TaskEditActivity extends TabActivity {
tags.add(tagName.getText().toString());
}
tagService.synchronizeTags(model.getId(), tags);
tagService.synchronizeTags(task.getId(), tags);
}
/** Adds a tag to the tag field */
@ -1180,6 +1227,7 @@ public final class TaskEditActivity extends TabActivity {
//
}
public void beforeTextChanged(CharSequence s, int start, int count,
int after) {
//
@ -1198,85 +1246,6 @@ public final class TaskEditActivity extends TabActivity {
}
}
public class RepeatControlSet implements TaskEditControlSet {
private final Button repeatValue;
private final Spinner repeatInterval;
public RepeatControlSet(int repeatValue, int repeatInterval) {
this.repeatValue = (Button) findViewById(repeatValue);
this.repeatInterval = (Spinner) findViewById(repeatInterval);
ArrayAdapter<String> repeatAdapter = new ArrayAdapter<String>(
TaskEditActivity.this, android.R.layout.simple_spinner_item,
RepeatInterval.getLabels(getResources()));
repeatAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
this.repeatInterval.setAdapter(repeatAdapter);
this.repeatValue.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
repeatValueClick();
}
});
}
/** Set up the repeat value button */
void setRepeatValue(int value) {
if(value == 0)
repeatValue.setText(R.string.repeat_value_unset);
else
repeatValue.setText(Integer.toString(value));
repeatValue.setTag(value);
}
private RepeatInfo getRepeatValue() {
if(repeatValue.getTag() == null || repeatValue.getTag().equals(0))
return null;
return new RepeatInfo(RepeatInterval.values()
[repeatInterval.getSelectedItemPosition()],
(Integer)repeatValue.getTag());
}
protected void repeatValueClick() {
final int tagValue = (Integer)repeatValue.getTag();
final Runnable openDialogRunnable = new Runnable() {
public void run() {
int dialogValue = tagValue;
if(dialogValue == 0)
dialogValue = 1;
new NumberPickerDialog(TaskEditActivity.this, new OnNumberPickedListener() {
public void onNumberPicked(NumberPicker view, int number) {
setRepeatValue(number);
}
}, getResources().getString(R.string.repeat_picker_title),
dialogValue, 1, 0, 31).show();
}
};
openDialogRunnable.run();
}
@Override
public void readFromModel() {
// repeats
RepeatInfo repeatInfo = RepeatInfo.fromSingleField(model.getValue(Task.REPEAT));
if(repeatInfo != null) {
setRepeatValue(repeatInfo.getValue());
repeatInterval.setSelection(repeatInfo.getInterval().ordinal());
} else
setRepeatValue(0);
}
@Override
public void writeToModel() {
RepeatInfo repeatInfo = getRepeatValue();
model.setValue(Task.REPEAT, RepeatInfo.toSingleField(repeatInfo));
}
}
/**
* Calendar Control Set
* @author Tim Su <tim@todoroo.com>
@ -1373,11 +1342,11 @@ public final class TaskEditActivity extends TabActivity {
}
@Override
public void readFromModel() {
public void readFromTask(Task task) {
}
@Override
public void writeToModel() {
public void writeToModel(Task task) {
}
}

@ -18,28 +18,27 @@ import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.database.Cursor;
import android.os.Bundle;
import android.os.Parcelable;
import android.view.ContextMenu;
import android.view.ContextMenu.ContextMenuInfo;
import android.view.KeyEvent;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.Window;
import android.view.ContextMenu.ContextMenuInfo;
import android.view.View.OnClickListener;
import android.view.View.OnKeyListener;
import android.view.Window;
import android.view.inputmethod.EditorInfo;
import android.widget.AbsListView;
import android.widget.AbsListView.OnScrollListener;
import android.widget.AdapterView.AdapterContextMenuInfo;
import android.widget.EditText;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.RemoteViews;
import android.widget.TextView;
import android.widget.Toast;
import android.widget.AbsListView.OnScrollListener;
import android.widget.AdapterView.AdapterContextMenuInfo;
import android.widget.TextView.OnEditorActionListener;
import android.widget.Toast;
import com.flurry.android.FlurryAgent;
import com.timsu.astrid.R;
@ -417,12 +416,11 @@ public class TaskListActivity extends ListActivity implements OnScrollListener {
try {
Bundle extras = intent.getExtras();
long taskId = extras.getLong(AstridApiConstants.EXTRAS_TASK_ID);
Parcelable[] details = extras.getParcelableArray(AstridApiConstants.EXTRAS_ITEMS);
for(Parcelable detail : details)
taskAdapter.addDetails(getListView(), taskId, (TaskDetail)detail);
TaskDetail detail = extras.getParcelable(AstridApiConstants.EXTRAS_RESPONSE);
taskAdapter.addDetails(getListView(), taskId, detail);
} catch (Exception e) {
exceptionService.reportError("receive-detail-" + //$NON-NLS-1$
intent.getStringExtra(AstridApiConstants.EXTRAS_PLUGIN), e);
intent.getStringExtra(AstridApiConstants.EXTRAS_ADDON), e);
}
}
}

@ -20,7 +20,7 @@ public class SearchFilter extends FilterListItem {
* Constructor for creating a new SearchFilter
*
* @param plugin
* {@link Plugin} identifier that encompasses object
* {@link Addon} identifier that encompasses object
* @param listingTitle
* Title of this item as displayed on the lists page, e.g. Inbox
*/
@ -33,7 +33,7 @@ public class SearchFilter extends FilterListItem {
* Constructor for creating a new SearchFilter
*
* @param plugin
* {@link Plugin} identifier that encompasses object
* {@link Addon} identifier that encompasses object
*/
protected SearchFilter(String plugin) {
this.plugin = plugin;

@ -25,7 +25,7 @@ public final 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 = 1;
public static final int VERSION = 2;
/**
* Database name (must be unique)
@ -73,6 +73,17 @@ public final class Database extends AbstractDatabase {
@Override
protected boolean onUpgrade(int oldVersion, int newVersion) {
switch(oldVersion) {
case 1: {
SqlConstructorVisitor visitor = new SqlConstructorVisitor();
String sql = "ALTER TABLE " + Task.TABLE.name + " ADD " +
Task.RECURRENCE.accept(visitor, null);
database.execSQL(sql);
return true;
}
}
return false;
}

@ -5,15 +5,25 @@
*/
package com.todoroo.astrid.dao;
import java.text.ParseException;
import java.util.Date;
import java.util.TimeZone;
import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
import com.google.ical.iter.RecurrenceIterator;
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.timsu.astrid.R;
import com.todoroo.andlib.data.GenericDao;
import com.todoroo.andlib.service.Autowired;
import com.todoroo.andlib.service.ContextManager;
import com.todoroo.andlib.service.DependencyInjectionService;
import com.todoroo.andlib.service.ExceptionService;
import com.todoroo.andlib.sql.Criterion;
import com.todoroo.andlib.utility.DateUtilities;
import com.todoroo.astrid.api.AstridApiConstants;
@ -36,6 +46,9 @@ public class TaskDao extends GenericDao<Task> {
@Autowired
Database database;
@Autowired
ExceptionService exceptionService;
ReminderService reminderService;
public TaskDao() {
@ -273,6 +286,57 @@ public class TaskDao extends GenericDao<Task> {
* @param duringSync
*/
private void afterComplete(Task task, ContentValues values, boolean duringSync) {
task = fetch(task.getId(), Task.ID, Task.RECURRENCE, Task.DUE_DATE);
if(task == null)
return;
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 dateValue;
if(task.hasDueDate() && task.hasDueTime()) {
date = new Date(task.getValue(Task.DUE_DATE));
dateValue = new DateTimeValueImpl(date.getYear() + 1900,
date.getMonth() + 1, date.getDate(),
date.getHours(), date.getMinutes(), date.getSeconds());
} else if(task.hasDueDate()) {
date = new Date(task.getValue(Task.DUE_DATE));
dateValue = new DateValueImpl(date.getYear() + 1900, date.getMonth() + 1, date.getDate());
} else {
dateValue = today;
}
try {
RecurrenceIterator iterator = RecurrenceIteratorFactory.createRecurrenceIterator(recurrence,
dateValue, TimeZone.getDefault());
if(dateValue.compareTo(today) < 0)
dateValue = today;
iterator.advanceTo(dateValue);
iterator.next();
if(iterator.hasNext()) {
long dueDate;
DateValue newDate = iterator.next();
if(newDate instanceof DateTimeValueImpl) {
DateTimeValueImpl newDateTime = (DateTimeValueImpl)newDate;
dueDate = 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 {
dueDate = task.createDueDate(Task.URGENCY_SPECIFIC_DAY,
new Date(newDate.year() - 1900, newDate.month() - 1,
newDate.day()).getTime());
}
task.setValue(Task.DUE_DATE, dueDate);
task.setValue(Task.COMPLETION_DATE, 0L);
persist(task);
}
} catch (ParseException e) {
exceptionService.reportError("recurrence-rule: " + recurrence, e); //$NON-NLS-1$
}
}
/*Cursor cursor = fetchTaskCursor(task.getTaskIdentifier(),
TaskModelForHandlers.FIELD_LIST);
TaskModelForHandlers model = new TaskModelForHandlers(cursor, values);

@ -12,7 +12,6 @@ import android.content.ContentValues;
import android.content.res.Resources;
import com.timsu.astrid.R;
import com.timsu.astrid.data.task.AbstractTaskModel.RepeatInfo;
import com.todoroo.andlib.data.AbstractModel;
import com.todoroo.andlib.data.Property;
import com.todoroo.andlib.data.Property.IntegerProperty;
@ -103,8 +102,8 @@ public final class Task extends AbstractModel {
public static final LongProperty REMINDER_LAST = new LongProperty(
TABLE, "lastNotified");
public static final IntegerProperty REPEAT = new IntegerProperty(
TABLE, "repeat");
public static final StringProperty RECURRENCE = new StringProperty(
TABLE, "recurrence");
public static final IntegerProperty FLAGS = new IntegerProperty(
TABLE, "flags");
@ -164,7 +163,7 @@ public final class Task extends AbstractModel {
defaultValues.put(IMPORTANCE.name, IMPORTANCE_NONE);
defaultValues.put(CALENDAR_URI.name, "");
defaultValues.put(REPEAT.name, 0);
defaultValues.put(RECURRENCE.name, "");
defaultValues.put(REMINDER_PERIOD.name, 0);
defaultValues.put(REMINDER_FLAGS.name, 0);
defaultValues.put(ESTIMATED_SECONDS.name, 0);
@ -244,13 +243,6 @@ public final class Task extends AbstractModel {
return (getValue(property) & flag) > 0;
}
/**
* @return repeat data structure. Requires REPEAT
*/
public RepeatInfo getRepeatInfo() {
return RepeatInfo.fromSingleField(getValue(Task.REPEAT));
}
// --- due and hide until date management
/** urgency array index -> significance */

@ -12,8 +12,8 @@ import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.database.sqlite.SQLiteDatabase.CursorFactory;
import android.database.sqlite.SQLiteOpenHelper;
import android.util.Log;
import com.timsu.astrid.R;
@ -141,7 +141,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);
// propertyMap.put(AbstractTaskModel.REPEAT, Task.REPEAT); // TODO
propertyMap.put(AbstractTaskModel.CREATION_DATE, Task.CREATION_DATE);
propertyMap.put(AbstractTaskModel.COMPLETION_DATE, Task.COMPLETION_DATE);
propertyMap.put(AbstractTaskModel.CALENDAR_URI, Task.CALENDAR_URI);

@ -84,6 +84,15 @@ public class AstridDependencyInjector implements AbstractDependencyInjector {
injectables.put("minutesAbbrevResource", R.plurals.DUt_minutesShort);
injectables.put("secondsAbbrevResource", R.plurals.DUt_secondsShort);
// com.todoroo.android.utility
injectables.put("nNumberPickerLayout", R.layout.n_number_picker_dialog);
injectables.put("numberPickerLayout", R.layout.number_picker);
injectables.put("numberPickerIncrementId", R.id.increment);
injectables.put("numberPickerDecrementId", R.id.decrement);
injectables.put("numberPickerId", R.id.numberPicker);
injectables.put("numberPickerInputId", R.id.timepicker_input);
injectables.put("numberPickerDialogLayout", R.layout.number_picker_dialog);
// com.todoroo.astrid.dao
injectables.put("database", Database.class);
injectables.put("taskDao", TaskDao.class);

@ -24,6 +24,10 @@ public final class UpgradeService {
if(from < 136)
new Astrid2To3UpgradeHelper().upgrade2To3();
else if(from < 137) {
// TODO upgrade recurrence
}
// display changelog
showChangeLog(from);

Loading…
Cancel
Save