Merge branch 'master' into dev

Conflicts:
	astrid/src/com/todoroo/astrid/adapter/TaskAdapter.java
pull/14/head
Tim Su 16 years ago
commit 44e0f00b38

@ -1,8 +1,10 @@
<?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.7" android:versionCode="146"
android:installLocation="auto">
android:versionName="3.1.0" android:versionCode="146"
android:installLocation="internalOnly">
<!-- widgets, alarms, and services will break if Astrid is installed on SD card -->
<!-- ================================================== Used Permissions = -->
@ -46,7 +48,7 @@
<!-- ========================================================== Metadata = -->
<uses-sdk android:targetSdkVersion="4"
<uses-sdk android:targetSdkVersion="8"
android:minSdkVersion="3" />
<supports-screens />
@ -163,6 +165,15 @@
</intent-filter>
</activity>
<!-- alarms -->
<receiver android:name="com.todoroo.astrid.alarms.AlarmTaskRepeatListener">
<intent-filter>
<action android:name="com.todoroo.astrid.TASK_REPEATED" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</receiver>
<!-- tags -->
<receiver android:name="com.todoroo.astrid.tags.TagsPlugin">
<intent-filter>
@ -314,7 +325,7 @@
<activity android:name="com.todoroo.astrid.rmilk.MilkLoginActivity"
android:theme="@style/Theme" />
<activity android:name="com.todoroo.astrid.rmilk.MilkPreferences"
android:icon="@drawable/ic_menu_rmilk"
android:icon="@drawable/ic_menu_refresh"
android:label="@string/rmilk_MPr_header">
<intent-filter>
<action android:name="com.todoroo.astrid.TASK_LIST_MENU" />

@ -52,6 +52,16 @@ public class AstridApiConstants {
*/
public static final String EXTRAS_EXTENDED = "extended";
/**
* Extras name for old task due date
*/
public static final String EXTRAS_OLD_DUE_DATE= "oldDueDate";
/**
* Extras name for new task due date
*/
public static final String EXTRAS_NEW_DUE_DATE = "newDueDate";
// --- Add-ons API
/**
@ -177,10 +187,12 @@ public class AstridApiConstants {
public static final String BROADCAST_EVENT_TASK_COMPLETED = PACKAGE + ".TASK_COMPLETED";
/**
* Action name for broadcast intent notifying that task was created
* Action name for broadcast intent notifying that task was created from repeating template
* @extra EXTRAS_TASK_ID id of the task
* @extra EXTRAS_OLD_DUE_DATE task old due date (could be 0)
* @extra EXTRAS_NEW_DUE_DATE task new due date (will not be 0)
*/
public static final String BROADCAST_EVENT_TASK_CREATED = PACKAGE + ".TASK_CREATED";
public static final String BROADCAST_EVENT_TASK_REPEATED = PACKAGE + ".TASK_REPEATED";
// --- SQL Constants

@ -61,6 +61,11 @@ public class Field extends DBObject<Field> {
return UnaryCriterion.like(this, value);
}
public Criterion like(String value, String escape) {
return UnaryCriterion.like(this, value, escape);
}
public <T> Criterion in(final T... value) {
final Field field = this;
return new Criterion(Operator.in) {

@ -89,14 +89,14 @@ public final class Query {
visitSelectClause(sql);
visitFromClause(sql);
visitJoinClause(sql);
if(queryTemplate == null) {
visitJoinClause(sql);
visitWhereClause(sql);
visitGroupByClause(sql);
visitOrderByClause(sql);
visitLimitClause(sql);
} else {
if(joins.size() > 0 || groupBies.size() > 0 || orders.size() > 0 ||
if(groupBies.size() > 0 || orders.size() > 0 ||
havings.size() > 0)
throw new IllegalStateException("Can't have extras AND query template"); //$NON-NLS-1$
sql.append(queryTemplate);

@ -89,4 +89,19 @@ public class UnaryCriterion extends Criterion {
}
};
}
public static Criterion like(Field field, String value, final String escape) {
return new UnaryCriterion(field, Operator.like, value) {
@Override
protected void populateOperator(StringBuilder sb) {
sb.append(SPACE).append(operator).append(SPACE);
}
@SuppressWarnings("nls")
@Override
protected void afterPopulateOperator(StringBuilder sb) {
super.afterPopulateOperator(sb);
sb.append(SPACE).append("ESCAPE").append(" '").append(sanitize(escape)).append("'");
}
};
}
}

@ -193,11 +193,21 @@ public class AndroidUtilities {
if(string == null)
return null;
String[] pairs = string.split(" ");
String[] pairs = string.split("=");
ContentValues result = new ContentValues();
for(String item : pairs) {
String[] keyValue = item.split("=");
result.put(keyValue[0].trim(), keyValue[1].trim());
String key = null;
for(int i = 0; i < pairs.length; i++) {
String newKey = null;
int lastSpace = pairs[i].lastIndexOf(' ');
if(lastSpace != -1) {
newKey = pairs[i].substring(lastSpace + 1);
pairs[i] = pairs[i].substring(0, lastSpace);
} else {
newKey = pairs[i];
}
if(key != null)
result.put(key.trim(), pairs[i].trim());
key = newKey;
}
return result;
}

@ -6,11 +6,13 @@
package com.todoroo.andlib.utility;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.Locale;
import android.content.Context;
import android.content.res.Resources;
import android.text.format.DateUtils;
import com.todoroo.andlib.service.Autowired;
import com.todoroo.andlib.service.ContextManager;
@ -108,101 +110,66 @@ public class DateUtilities {
}
/**
* @return time format (hours and minutes)
*/
public static SimpleDateFormat getTimeFormat(Context context) {
String value = getTimeFormatString(context);
return new SimpleDateFormat(value);
}
/**
* @return string used for time formatting
* @param context android context
* @param date time to format
* @return time, with hours and minutes
*/
@SuppressWarnings("nls")
private static String getTimeFormatString(Context context) {
public static String getTimeString(Context context, Date date) {
String value;
if (is24HourFormat(context)) {
value = "H:mm";
} else {
value = "h:mm a";
}
return value;
return new SimpleDateFormat(value).format(date);
}
/**
* @param context android context
* @return string used for date formatting
* @param date date to format
* @return date, with month, day, and year
*/
@SuppressWarnings("nls")
private static String getDateFormatString(Context context) {
public static String getDateString(Context context, Date date) {
String month = "'" + DateUtils.getMonthString(date.getMonth() +
Calendar.JANUARY, DateUtils.LENGTH_MEDIUM) + "'";
String value;
// united states, you are special
if (Locale.US.equals(Locale.getDefault())
|| Locale.CANADA.equals(Locale.getDefault()))
value = "MMM d yyyy";
value = month + " d yyyy";
else
value = "d MMM yyyy";
return value;
}
/**
* @return date format (month, day, year)
*/
@SuppressWarnings("nls")
public static SimpleDateFormat getDateFormat(Context context) {
try {
return new SimpleDateFormat(getDateFormatString(context));
} catch (Exception e) {
return new SimpleDateFormat("d MMM yyyy");
}
value = "d " + month + " yyyy";
return new SimpleDateFormat(value).format(date);
}
/**
* @return date format as getDateFormat with weekday
*/
@SuppressWarnings("nls")
public static SimpleDateFormat getDateFormatWithWeekday(Context context) {
try {
return new SimpleDateFormat("EEEE, " + getDateFormatString(context));
} catch (Exception e) {
return new SimpleDateFormat("EEEE, d MMM yyyy");
}
public static String getDateStringWithWeekday(Context context, Date date) {
String weekday = DateUtils.getDayOfWeekString(date.getDay() + Calendar.SUNDAY,
DateUtils.LENGTH_LONG);
return weekday + ", " + getDateString(context, date);
}
/**
* @return date format as getDateFormat with weekday
*/
@SuppressWarnings("nls")
public static SimpleDateFormat getDateWithTimeAndWeekday(Context context) {
try {
return new SimpleDateFormat("EEEE, " + getDateFormatString(context)
+ " " + getTimeFormatString(context));
} catch (Exception e) {
return new SimpleDateFormat("EEEE, d MMM yyyy H:mm");
}
public static String getDateStringWithTimeAndWeekday(Context context, Date date) {
return getDateStringWithWeekday(context, date) + " " + getTimeString(context, date);
}
/**
* @return date with time at the end
*/
@SuppressWarnings("nls")
public static SimpleDateFormat getDateWithTimeFormat(Context context) {
try {
return new SimpleDateFormat(getDateFormatString(context) + " " +
getTimeFormatString(context));
} catch (Exception e) {
return new SimpleDateFormat("d MMM yyyy H:mm");
}
public static String getDateStringWithTime(Context context, Date date) {
return getDateString(context, date) + " " + getTimeString(context, date);
}
/**
* @return formatted date (will contain month, day, year)
*/
public static String getFormattedDate(Context context, Date date) {
return getDateFormat(context).format(date);
}
/* ======================================================================
* ============================================================= duration
* ====================================================================== */

@ -0,0 +1,42 @@
package com.todoroo.andlib.widget;
import java.util.ArrayList;
import android.app.Activity;
import android.gesture.Gesture;
import android.gesture.GestureLibraries;
import android.gesture.GestureLibrary;
import android.gesture.GestureOverlayView;
import android.gesture.Prediction;
import android.gesture.GestureOverlayView.OnGesturePerformedListener;
import com.todoroo.andlib.widget.GestureService.GestureInterface;
public class Api4GestureDetector implements OnGesturePerformedListener {
private final GestureLibrary mLibrary;
private final GestureInterface listener;
public Api4GestureDetector(Activity activity, int view, int gestureLibrary, GestureInterface listener) {
this.listener = listener;
mLibrary = GestureLibraries.fromRawResource(activity, gestureLibrary);
if(mLibrary.load()) {
GestureOverlayView gestures = (GestureOverlayView) activity.findViewById(view);
gestures.addOnGesturePerformedListener(this);
}
}
@Override
public void onGesturePerformed(GestureOverlayView overlay, Gesture gesture) {
ArrayList<Prediction> predictions = mLibrary.recognize(gesture);
// We want at least one prediction
if (predictions.size() > 0) {
Prediction prediction = predictions.get(0);
// We want at least some confidence in the result
if (prediction.score > 1.0) {
listener.gesturePerformed(prediction.name);
}
}
}
}

@ -0,0 +1,38 @@
package com.todoroo.andlib.widget;
import android.app.Activity;
import com.todoroo.andlib.utility.AndroidUtilities;
/**
* All API versions-friendly gesture detector. On SDK < 4, nothing happens
* @author Tim Su <tim@todoroo.com>
*
*/
public class GestureService {
public interface GestureInterface {
public void gesturePerformed(String gesture);
}
/**
* Register gesture detector. If android SDK version is not correct,
* a {@link VerifyError} will be throw. Catch this explicitly.
*
* @param activity
* @param view
* @param gestureLibrary
* @param listener
* @throws VerifyError
*/
public static void registerGestureDetector(Activity activity, int view,
int gestureLibrary, GestureInterface listener) throws VerifyError {
if(AndroidUtilities.getSdkVersion() > 3)
new Api4GestureDetector(activity, view, gestureLibrary, listener);
}
}

@ -1,111 +1,34 @@
/**
* See the file "LICENSE" for the full license governing this code.
*/
package com.todoroo.astrid.alarms;
import android.content.ContentValues;
import com.todoroo.andlib.data.AbstractModel;
import com.todoroo.andlib.data.Property;
import com.todoroo.andlib.data.Property.IntegerProperty;
import com.todoroo.andlib.data.Property.LongProperty;
import com.todoroo.andlib.data.Property.StringProperty;
import com.todoroo.andlib.data.Table;
import com.todoroo.andlib.data.TodorooCursor;
import com.todoroo.astrid.model.Task;
import com.todoroo.astrid.model.Metadata;
/**
* Data Model which represents an alarm
* Metadata entry for a task alarm
*
* @author Tim Su <tim@todoroo.com>
*
*/
@SuppressWarnings("nls")
public class Alarm extends AbstractModel {
// --- table
public static final Table TABLE = new Table("alarm", Alarm.class);
public class Alarm {
// --- properties
/** metadata key */
public static final String METADATA_KEY = "alarm"; //$NON-NLS-1$
/** ID */
public static final LongProperty ID = new LongProperty(
TABLE, ID_PROPERTY_NAME);
/** time of alarm */
public static final LongProperty TIME = new LongProperty(Metadata.TABLE,
Metadata.VALUE1.name);
/** Associated Task */
public static final LongProperty TASK = new LongProperty(
TABLE, "task");
/** Alarm Time */
public static final LongProperty TIME = new LongProperty(
TABLE, "time");
/** Alarm Type (see constants) */
public static final IntegerProperty TYPE = new IntegerProperty(
TABLE, "type");
/** Alarm Ringtone */
public static final StringProperty RINGTONE = new StringProperty(
TABLE, "ringtone");
/** List of all properties for this model */
public static final Property<?>[] PROPERTIES = generateProperties(Alarm.class);
/** alarm type */
public static final IntegerProperty TYPE = new IntegerProperty(Metadata.TABLE,
Metadata.VALUE2.name);
// --- constants
/** this alarm was already triggered */
public static final int TYPE_TRIGGERED = 0;
/** this alarm is single-shot */
public static final int TYPE_SINGLE = 1;
/** this alarm repeats itself until turned off */
public static final int TYPE_REPEATING = 2;
// --- defaults
/** Default values container */
private static final ContentValues defaultValues = new ContentValues();
static {
defaultValues.put(TYPE.name, TYPE_SINGLE);
defaultValues.put(RINGTONE.name, "");
}
@Override
public ContentValues getDefaultValues() {
return defaultValues;
}
// --- data access boilerplate
public Alarm() {
super();
}
public Alarm(TodorooCursor<Alarm> cursor) {
this();
readPropertiesFromCursor(cursor);
}
public void readFromCursor(TodorooCursor<Alarm> cursor) {
super.readPropertiesFromCursor(cursor);
}
@Override
public long getId() {
return getIdHelper(ID);
};
// --- parcelable helpers
private static final Creator<Task> CREATOR = new ModelCreator<Task>(Task.class);
@Override
protected Creator<? extends AbstractModel> getCreator() {
return CREATOR;
}
}

@ -0,0 +1,92 @@
package com.todoroo.astrid.alarms;
import java.util.Date;
import java.util.LinkedHashSet;
import android.app.Activity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.ImageButton;
import android.widget.LinearLayout;
import com.timsu.astrid.R;
import com.todoroo.andlib.data.TodorooCursor;
import com.todoroo.andlib.widget.DateControlSet;
import com.todoroo.astrid.activity.TaskEditActivity.TaskEditControlSet;
import com.todoroo.astrid.model.Metadata;
import com.todoroo.astrid.model.Task;
/**
* Control set to manage adding and removing tags
*
* @author Tim Su <tim@todoroo.com>
*
*/
public final class AlarmControlSet implements TaskEditControlSet {
// --- instance variables
private final LinearLayout alertsContainer;
private final Activity activity;
public AlarmControlSet(Activity activity, ViewGroup parent) {
View v = LayoutInflater.from(activity).inflate(R.layout.alarm_control, parent, true);
this.activity = activity;
this.alertsContainer = (LinearLayout) v.findViewById(R.id.alert_container);
v.findViewById(R.id.alarms_add).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View arg0) {
addAlarm(new Date());
}
});
}
@Override
public void readFromTask(Task task) {
alertsContainer.removeAllViews();
TodorooCursor<Metadata> cursor = AlarmService.getInstance().getAlarms(task.getId());
try {
for(cursor.moveToFirst(); !cursor.isAfterLast(); cursor.moveToNext())
addAlarm(new Date(cursor.get(Alarm.TIME)));
} finally {
cursor.close();
}
}
@Override
public void writeToModel(Task task) {
LinkedHashSet<Long> alarms = new LinkedHashSet<Long>();
for(int i = 0; i < alertsContainer.getChildCount(); i++) {
DateControlSet set = (DateControlSet) alertsContainer.getChildAt(i).getTag();
if(set == null)
continue;
Date date = set.getDate();
if(date != null)
alarms.add(set.getDate().getTime());
}
AlarmService.getInstance().synchronizeAlarms(task.getId(), alarms);
}
private boolean addAlarm(Date alert) {
final View alertItem = LayoutInflater.from(activity).inflate(R.layout.alarm_edit_row, null);
alertsContainer.addView(alertItem);
DateControlSet dcs = new DateControlSet(activity, (Button)alertItem.findViewById(R.id.date),
(Button)alertItem.findViewById(R.id.time));
dcs.setDate(alert);
alertItem.setTag(dcs);
ImageButton reminderRemoveButton;
reminderRemoveButton = (ImageButton)alertItem.findViewById(R.id.button1);
reminderRemoveButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
alertsContainer.removeView(alertItem);
}
});
return true;
}
}

@ -0,0 +1,79 @@
/**
* See the file "LICENSE" for the full license governing this code.
*/
package com.todoroo.astrid.alarms;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.text.format.DateUtils;
import com.timsu.astrid.R;
import com.todoroo.andlib.data.TodorooCursor;
import com.todoroo.andlib.utility.DateUtilities;
import com.todoroo.astrid.api.AstridApiConstants;
import com.todoroo.astrid.api.DetailExposer;
import com.todoroo.astrid.model.Metadata;
/**
* Exposes Task Detail for tags, i.e. "Tags: frogs, animals"
*
* @author Tim Su <tim@todoroo.com>
*
*/
public class AlarmDetailExposer extends BroadcastReceiver implements DetailExposer {
@Override
public void onReceive(Context context, Intent intent) {
// get tags associated with this task
long taskId = intent.getLongExtra(AstridApiConstants.EXTRAS_TASK_ID, -1);
if(taskId == -1)
return;
boolean extended = intent.getBooleanExtra(AstridApiConstants.EXTRAS_EXTENDED, false);
String taskDetail = getTaskDetails(context, taskId, extended);
if(taskDetail == null)
return;
// transmit
Intent broadcastIntent = new Intent(AstridApiConstants.BROADCAST_SEND_DETAILS);
broadcastIntent.putExtra(AstridApiConstants.EXTRAS_ADDON, AlarmService.IDENTIFIER);
broadcastIntent.putExtra(AstridApiConstants.EXTRAS_RESPONSE, taskDetail);
broadcastIntent.putExtra(AstridApiConstants.EXTRAS_EXTENDED, extended);
broadcastIntent.putExtra(AstridApiConstants.EXTRAS_TASK_ID, taskId);
context.sendBroadcast(broadcastIntent, AstridApiConstants.PERMISSION_READ);
}
@Override
public String getTaskDetails(Context context, long id, boolean extended) {
if(extended)
return null;
TodorooCursor<Metadata> cursor = AlarmService.getInstance().getAlarms(id);
long nextTime = -1;
try {
for(cursor.moveToFirst(); !cursor.isAfterLast(); cursor.moveToNext()) {
long time = cursor.get(Alarm.TIME);
if(time > DateUtilities.now()) {
nextTime = time;
break;
}
}
if(nextTime == -1)
return null;
CharSequence durationString = DateUtils.getRelativeDateTimeString(context,
nextTime, DateUtils.MINUTE_IN_MILLIS, DateUtils.WEEK_IN_MILLIS,
DateUtils.FORMAT_ABBREV_ALL);
return context.getString(R.string.alarm_ADE_detail, durationString);
} finally {
cursor.close();
}
}
@Override
public String getPluginIdentifier() {
return AlarmService.IDENTIFIER;
}
}

@ -1,11 +1,28 @@
package com.todoroo.astrid.alarms;
import java.util.ArrayList;
import java.util.Date;
import java.util.LinkedHashSet;
import android.app.AlarmManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.util.Log;
import com.todoroo.andlib.data.GenericDao;
import com.todoroo.andlib.data.TodorooCursor;
import com.todoroo.andlib.service.DependencyInjectionService;
import com.todoroo.andlib.service.ContextManager;
import com.todoroo.andlib.sql.Criterion;
import com.todoroo.andlib.sql.Join;
import com.todoroo.andlib.sql.Order;
import com.todoroo.andlib.sql.Query;
import com.todoroo.astrid.core.PluginServices;
import com.todoroo.astrid.dao.MetadataDao.MetadataCriteria;
import com.todoroo.astrid.dao.TaskDao.TaskCriteria;
import com.todoroo.astrid.model.Metadata;
import com.todoroo.astrid.model.Task;
import com.todoroo.astrid.reminders.Notifications;
import com.todoroo.astrid.reminders.ReminderService;
import com.todoroo.astrid.service.MetadataService;
/**
* Provides operations for working with alerts
@ -13,32 +30,31 @@ import com.todoroo.andlib.sql.Query;
* @author Tim Su <tim@todoroo.com>
*
*/
@SuppressWarnings("nls")
public class AlarmService {
AlarmDatabase database = new AlarmDatabase();
GenericDao<Alarm> dao = new GenericDao<Alarm>(Alarm.class, database);
// --- singleton
/**
* Metadata key for # of alarms
*/
public static final String ALARM_COUNT = "alarms-count";
private static AlarmService instance = null;
public AlarmService() {
DependencyInjectionService.getInstance().inject(this);
public static synchronized AlarmService getInstance() {
if(instance == null)
instance = new AlarmService();
return instance;
}
// --- data retrieval
public static final String IDENTIFIER = "alarms"; //$NON-NLS-1$
/**
* Return alarms for the given task
* Return alarms for the given task. PLEASE CLOSE THE CURSOR!
*
* @param taskId
*/
public TodorooCursor<Alarm> getAlarms(long taskId) {
database.openForReading();
Query query = Query.select(Alarm.PROPERTIES).where(Alarm.TASK.eq(taskId));
return dao.query(query);
public TodorooCursor<Metadata> getAlarms(long taskId) {
return PluginServices.getMetadataService().query(Query.select(
Metadata.PROPERTIES).where(MetadataCriteria.byTaskAndwithKey(
taskId, Alarm.METADATA_KEY)).orderBy(Order.asc(Alarm.TIME)));
}
/**
@ -46,14 +62,140 @@ public class AlarmService {
* @param taskId
* @param tags
*/
public void synchronizeAlarms(long taskId, ArrayList<Alarm> alarms) {
database.openForWriting();
dao.deleteWhere(Alarm.TASK.eq(taskId));
for(Alarm alarm : alarms) {
alarm.setId(Alarm.NO_ID);
alarm.setValue(Alarm.TASK, taskId);
dao.saveExisting(alarm);
public void synchronizeAlarms(long taskId, LinkedHashSet<Long> alarms) {
MetadataService service = PluginServices.getMetadataService();
if(alarmsIdentical(taskId, alarms))
return;
service.deleteWhere(Criterion.and(MetadataCriteria.byTask(taskId),
MetadataCriteria.withKey(Alarm.METADATA_KEY)));
Metadata metadata = new Metadata();
metadata.setValue(Metadata.KEY, Alarm.METADATA_KEY);
metadata.setValue(Metadata.TASK, taskId);
for(Long alarm : alarms) {
metadata.clearValue(Metadata.ID);
metadata.setValue(Alarm.TIME, alarm);
metadata.setValue(Alarm.TYPE, Alarm.TYPE_SINGLE);
service.save(metadata);
scheduleAlarm(metadata);
}
}
private boolean alarmsIdentical(long taskId, LinkedHashSet<Long> alarms) {
TodorooCursor<Metadata> cursor = getAlarms(taskId);
try {
if(cursor.getCount() != alarms.size())
return false;
for(Long alarm : alarms) {
cursor.moveToNext();
if(alarm != cursor.get(Alarm.TIME))
return false;
}
} finally {
cursor.close();
}
return true;
}
// --- alarm scheduling
/**
* Gets a listing of all alarms that are active
* @param properties
* @return todoroo cursor. PLEASE CLOSE THIS CURSOR!
*/
private TodorooCursor<Metadata> getActiveAlarms() {
return PluginServices.getMetadataService().query(Query.select(Alarm.TIME).
join(Join.inner(Task.TABLE, Metadata.TASK.eq(Task.ID))).
where(Criterion.and(TaskCriteria.isActive(), MetadataCriteria.withKey(Alarm.METADATA_KEY))));
}
/**
* Gets a listing of alarms by task
* @param properties
* @return todoroo cursor. PLEASE CLOSE THIS CURSOR!
*/
private TodorooCursor<Metadata> getAlarmsForTask(long taskId) {
return PluginServices.getMetadataService().query(Query.select(Alarm.TIME).
join(Join.inner(Task.TABLE, Metadata.TASK.eq(Task.ID))).
where(Criterion.and(TaskCriteria.isActive(),
MetadataCriteria.byTaskAndwithKey(taskId, Alarm.METADATA_KEY))));
}
/**
* Schedules all alarms
*/
public void scheduleAllAlarms() {
TodorooCursor<Metadata> cursor = getActiveAlarms();
try {
Metadata alarm = new Metadata();
for(cursor.moveToFirst(); !cursor.isAfterLast(); cursor.moveToNext()) {
alarm.readFromCursor(cursor);
scheduleAlarm(alarm);
}
} catch (Exception e) {
// suppress
} finally {
cursor.close();
}
}
private static final long NO_ALARM = Long.MAX_VALUE;
/**
* Schedules alarms for a single task
* @param task
*/
public void scheduleAlarms(Task task) {
TodorooCursor<Metadata> cursor = getAlarmsForTask(task.getId());
try {
Metadata alarm = new Metadata();
for(cursor.moveToFirst(); !cursor.isAfterLast(); cursor.moveToNext()) {
alarm.readFromCursor(cursor);
scheduleAlarm(alarm);
}
} catch (Exception e) {
// suppress
} finally {
cursor.close();
}
}
/**
* Schedules alarms for a single task
*
* @param shouldPerformPropertyCheck
* whether to check if task has requisite properties
*/
@SuppressWarnings("nls")
private void scheduleAlarm(Metadata alarm) {
if(alarm == null)
return;
long taskId = alarm.getValue(Metadata.TASK);
int type = ReminderService.TYPE_ALARM;
Context context = ContextManager.getContext();
Intent intent = new Intent(context, Notifications.class);
intent.setType("ALARM" + Long.toString(taskId)); //$NON-NLS-1$
intent.setAction(Integer.toString(type));
intent.putExtra(Notifications.ID_KEY, taskId);
intent.putExtra(Notifications.TYPE_KEY, type);
AlarmManager am = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0,
intent, 0);
long time = alarm.getValue(Alarm.TIME);
if(time == 0 || time == NO_ALARM)
am.cancel(pendingIntent);
else {
Log.e("Astrid", "Alarm (" + taskId + ", " + type +
") set for " + new Date(time));
am.set(AlarmManager.RTC_WAKEUP, time, pendingIntent);
}
}
}

@ -0,0 +1,47 @@
package com.todoroo.astrid.alarms;
import java.util.LinkedHashSet;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import com.todoroo.andlib.data.TodorooCursor;
import com.todoroo.andlib.utility.DateUtilities;
import com.todoroo.astrid.api.AstridApiConstants;
import com.todoroo.astrid.model.Metadata;
public class AlarmTaskRepeatListener extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
long taskId = intent.getLongExtra(AstridApiConstants.EXTRAS_TASK_ID, -1);
if(taskId == -1)
return;
long oldDueDate = intent.getLongExtra(AstridApiConstants.EXTRAS_OLD_DUE_DATE, 0);
if(oldDueDate == 0)
oldDueDate = DateUtilities.now();
long newDueDate = intent.getLongExtra(AstridApiConstants.EXTRAS_NEW_DUE_DATE, -1);
if(newDueDate <= 0 || newDueDate <= oldDueDate)
return;
TodorooCursor<Metadata> cursor = AlarmService.getInstance().getAlarms(taskId);
try {
if(cursor.getCount() == 0)
return;
Metadata metadata = new Metadata();
LinkedHashSet<Long> alarms = new LinkedHashSet<Long>(cursor.getCount());
for(cursor.moveToFirst(); !cursor.isAfterLast(); cursor.moveToNext()) {
metadata.readFromCursor(cursor);
alarms.add(metadata.getValue(Alarm.TIME) + (newDueDate - oldDueDate));
}
AlarmService.getInstance().synchronizeAlarms(taskId, alarms);
} finally {
cursor.close();
}
}
}

@ -121,8 +121,8 @@ public class BackupPreferences extends TodorooPreferences {
});
} else if(last > 0) {
status = r.getString(R.string.backup_status_success,
DateUtilities.getDateWithTimeFormat(BackupPreferences.this).
format(new Date(last)));
DateUtilities.getDateStringWithTime(BackupPreferences.this,
new Date(last)));
statusColor = Color.rgb(0, 100, 0);
preference.setOnPreferenceClickListener(null);
} else {

@ -6,6 +6,7 @@ import java.io.IOException;
import org.xmlpull.v1.XmlSerializer;
import android.app.Activity;
import android.app.ProgressDialog;
import android.content.Context;
import android.os.Handler;
@ -16,8 +17,8 @@ import android.widget.Toast;
import com.timsu.astrid.R;
import com.todoroo.andlib.data.AbstractModel;
import com.todoroo.andlib.data.Property;
import com.todoroo.andlib.data.TodorooCursor;
import com.todoroo.andlib.data.Property.PropertyVisitor;
import com.todoroo.andlib.data.TodorooCursor;
import com.todoroo.andlib.service.ExceptionService;
import com.todoroo.andlib.sql.Order;
import com.todoroo.andlib.sql.Query;
@ -82,6 +83,8 @@ public class TasksXmlExporter {
progressDialog.setCancelable(false);
progressDialog.setIndeterminate(false);
progressDialog.show();
if(context instanceof Activity)
progressDialog.setOwnerActivity((Activity)context);
}
new Thread(new Runnable() {

@ -2,14 +2,15 @@ package com.todoroo.astrid.backup;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Date;
import java.util.LinkedHashSet;
import java.util.StringTokenizer;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlPullParserFactory;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.ProgressDialog;
import android.content.Context;
@ -97,6 +98,8 @@ public class TasksXmlImporter {
progressDialog.setCancelable(false);
progressDialog.setIndeterminate(true);
progressDialog.show();
if(context instanceof Activity)
progressDialog.setOwnerActivity((Activity)context);
new Thread(new Runnable() {
@Override
@ -326,7 +329,7 @@ public class TasksXmlImporter {
private String upgradeNotes = null;
private boolean syncOnComplete = false;
private final ArrayList<String> tags = new ArrayList<String>();
private final LinkedHashSet<String> tags = new LinkedHashSet<String>();
public Format1TaskImporter(XmlPullParser xpp) throws XmlPullParserException, IOException {
this.xpp = xpp;
@ -496,7 +499,7 @@ public class TasksXmlImporter {
if(preferred != null) {
Date preferredDate = BackupDateUtilities.getDateFromIso8601String(value);
upgradeNotes = "Goal Deadline: " +
DateUtilities.getFormattedDate(ContextManager.getContext(),
DateUtilities.getDateString(ContextManager.getContext(),
preferredDate);
}
task.setValue(Task.DUE_DATE,

@ -14,6 +14,7 @@ import com.timsu.astrid.R;
import com.todoroo.andlib.sql.Criterion;
import com.todoroo.andlib.sql.Functions;
import com.todoroo.andlib.sql.Order;
import com.todoroo.andlib.sql.Query;
import com.todoroo.andlib.sql.QueryTemplate;
import com.todoroo.andlib.utility.DateUtilities;
import com.todoroo.astrid.activity.FilterListActivity;
@ -22,8 +23,11 @@ import com.todoroo.astrid.api.Filter;
import com.todoroo.astrid.api.FilterCategory;
import com.todoroo.astrid.api.FilterListItem;
import com.todoroo.astrid.api.SearchFilter;
import com.todoroo.astrid.dao.MetadataDao.MetadataCriteria;
import com.todoroo.astrid.dao.TaskDao.TaskCriteria;
import com.todoroo.astrid.model.Metadata;
import com.todoroo.astrid.model.Task;
import com.todoroo.astrid.tags.TagService;
/**
* Exposes Astrid's built in filters to the {@link FilterListActivity}
@ -86,7 +90,7 @@ public final class CoreFilterExposer extends BroadcastReceiver {
r.getString(R.string.BFE_Alphabetical),
new QueryTemplate().where(Criterion.and(TaskCriteria.isActive(),
TaskCriteria.isVisible())).
orderBy(Order.asc(Task.TITLE)),
orderBy(Order.asc(Functions.upper(Task.TITLE))),
null);
alphabetical.listingIcon = ((BitmapDrawable)r.getDrawable(R.drawable.tango_alpha)).getBitmap();
@ -134,8 +138,11 @@ public final class CoreFilterExposer extends BroadcastReceiver {
*/
public static Filter buildInboxFilter(Resources r) {
Filter inbox = new Filter(r.getString(R.string.BFE_Active), r.getString(R.string.BFE_Active_title),
new QueryTemplate().where(Criterion.and(TaskCriteria.isActive(),
TaskCriteria.isVisible())),
new QueryTemplate().where(
Criterion.and(TaskCriteria.isActive(), TaskCriteria.isVisible(),
Criterion.not(Task.ID.in(Query.select(Metadata.TASK).from(Metadata.TABLE).where(
Criterion.and(MetadataCriteria.withKey(TagService.KEY),
TagService.TAG.like("x_%", "x"))))))), //$NON-NLS-1$ //$NON-NLS-2$
null);
inbox.listingIcon = ((BitmapDrawable)r.getDrawable(R.drawable.tango_home)).getBitmap();
return inbox;

@ -3,9 +3,11 @@ package com.todoroo.astrid.core;
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.andlib.utility.DialogUtilities;
import com.todoroo.astrid.dao.Database;
import com.todoroo.astrid.service.AddOnService;
import com.todoroo.astrid.service.AstridDependencyInjector;
import com.todoroo.astrid.service.MetadataService;
import com.todoroo.astrid.service.TaskService;
@ -32,11 +34,18 @@ public final class PluginServices {
@Autowired
DialogUtilities dialogUtilities;
@Autowired
DateUtilities dateUtilities;
@Autowired
AddOnService addOnService;
private static PluginServices instance;
static {
AstridDependencyInjector.initialize();
}
private PluginServices() {
DependencyInjectionService.getInstance().inject(this);
}
@ -68,4 +77,8 @@ public final class PluginServices {
public static DialogUtilities getDialogUtilities() {
return getInstance().dialogUtilities;
}
public static DateUtilities getDateUtilities() {
return getInstance().dateUtilities;
}
}

@ -16,6 +16,7 @@ import android.widget.Spinner;
import com.flurry.android.FlurryAgent;
import com.timsu.astrid.R;
import com.todoroo.andlib.service.ContextManager;
import com.todoroo.astrid.activity.AddOnActivity;
import com.todoroo.astrid.adapter.FilterAdapter;
import com.todoroo.astrid.api.Filter;
@ -96,6 +97,8 @@ public final class LocaleEditAlerts extends ExpandableListActivity {
super.onCreate(savedInstanceState);
setContentView(R.layout.locale_edit_alerts);
ContextManager.setContext(this);
/*
* Locale guarantees that the breadcrumb string will be present, but checking for null anyway makes your Activity more
* robust and re-usable

@ -9,7 +9,7 @@ import android.util.Log;
import com.timsu.astrid.R;
import com.todoroo.andlib.data.TodorooCursor;
import com.todoroo.andlib.service.Autowired;
import com.todoroo.andlib.service.ContextManager;
import com.todoroo.andlib.service.DependencyInjectionService;
import com.todoroo.andlib.utility.DateUtilities;
import com.todoroo.astrid.activity.ShortcutActivity;
@ -17,7 +17,6 @@ import com.todoroo.astrid.api.Filter;
import com.todoroo.astrid.core.PluginServices;
import com.todoroo.astrid.model.Task;
import com.todoroo.astrid.reminders.Notifications;
import com.todoroo.astrid.service.TaskService;
import com.todoroo.astrid.utility.Constants;
import com.todoroo.astrid.utility.Preferences;
@ -29,9 +28,6 @@ import com.todoroo.astrid.utility.Preferences;
*/
public class LocaleReceiver extends BroadcastReceiver {
@Autowired
private TaskService taskService;
/**
* Create a preference key for storing / retrieving last interval time
* @param filterTitle
@ -46,6 +42,8 @@ public class LocaleReceiver extends BroadcastReceiver {
@Override
/** Called when the system is started up */
public void onReceive(Context context, Intent intent) {
ContextManager.setContext(context);
try {
if (com.twofortyfouram.Intent.ACTION_FIRE_SETTING.equals(intent.getAction())) {
if(!PluginServices.getAddOnService().hasLocalePlugin())
@ -72,7 +70,7 @@ public class LocaleReceiver extends BroadcastReceiver {
DependencyInjectionService.getInstance().inject(this);
Filter filter = new Filter(title, title, null, null);
filter.sqlQuery = sql;
TodorooCursor<Task> cursor = taskService.fetchFiltered(filter, null, Task.ID);
TodorooCursor<Task> cursor = PluginServices.getTaskService().fetchFiltered(filter, null, Task.ID);
try {
if(cursor.getCount() == 0)
return;

@ -32,10 +32,10 @@ public class Notifications extends BroadcastReceiver {
// --- constants
/** task id extra */
static final String ID_KEY = "id"; //$NON-NLS-1$
public static final String ID_KEY = "id"; //$NON-NLS-1$
/** notification type extra */
static final String TYPE_KEY = "type"; //$NON-NLS-1$
public static final String TYPE_KEY = "type"; //$NON-NLS-1$
/** preference values */
public static final int ICON_SET_PINK = 0;
@ -72,15 +72,24 @@ public class Notifications extends BroadcastReceiver {
Resources r = context.getResources();
String reminder;
if(type == ReminderService.TYPE_DUE || type == ReminderService.TYPE_OVERDUE)
reminder = getRandomReminder(r.getStringArray(R.array.reminders_due));
else if(type == ReminderService.TYPE_SNOOZE)
reminder = getRandomReminder(r.getStringArray(R.array.reminders_snooze));
else if(Preferences.getBoolean(R.string.p_rmd_nagging, true))
reminder = getRandomReminder(r.getStringArray(R.array.reminders));
else
if(type == ReminderService.TYPE_ALARM)
reminder = getRandomReminder(r.getStringArray(R.array.reminders_alarm));
else if(Preferences.getBoolean(R.string.p_rmd_nagging, true)) {
if(type == ReminderService.TYPE_DUE || type == ReminderService.TYPE_OVERDUE)
reminder = getRandomReminder(r.getStringArray(R.array.reminders_due));
else if(type == ReminderService.TYPE_SNOOZE)
reminder = getRandomReminder(r.getStringArray(R.array.reminders_snooze));
else
reminder = getRandomReminder(r.getStringArray(R.array.reminders));
} else
reminder = ""; //$NON-NLS-1$
synchronized(Notifications.class) {
if(notificationManager == null)
notificationManager = new AndroidNotificationManager(context);
}
if(!showTaskNotification(id, type, reminder)) {
notificationManager.cancel((int)id);
}

@ -48,13 +48,15 @@ public final class ReminderService {
};
/** flag for due date reminder */
static final int TYPE_DUE = 0;
public static final int TYPE_DUE = 0;
/** flag for overdue reminder */
static final int TYPE_OVERDUE = 1;
public static final int TYPE_OVERDUE = 1;
/** flag for random reminder */
static final int TYPE_RANDOM = 2;
public static final int TYPE_RANDOM = 2;
/** flag for a snoozed reminder */
static final int TYPE_SNOOZE = 3;
public static final int TYPE_SNOOZE = 3;
/** flag for an alarm reminder */
public static final int TYPE_ALARM = 4;
static final Random random = new Random();
@ -179,6 +181,8 @@ public final class ReminderService {
scheduler.createAlarm(task, whenDueDate, TYPE_DUE);
else if(whenOverdue != NO_ALARM)
scheduler.createAlarm(task, whenOverdue, TYPE_OVERDUE);
else
scheduler.createAlarm(task, 0, 0);
}
/**
@ -291,14 +295,6 @@ public final class ReminderService {
*/
@SuppressWarnings("nls")
public void createAlarm(Task task, long time, int type) {
if(time == 0 || time == NO_ALARM)
return;
if(time < DateUtilities.now()) {
time = DateUtilities.now() + (long)((0.5f +
4 * random.nextFloat()) * DateUtilities.ONE_HOUR);
}
Context context = ContextManager.getContext();
Intent intent = new Intent(context, Notifications.class);
intent.setType(Long.toString(task.getId()));
@ -310,9 +306,18 @@ public final class ReminderService {
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0,
intent, 0);
Log.e("Astrid", "Alarm (" + task.getId() + ", " + type +
") set for " + new Date(time));
am.set(AlarmManager.RTC_WAKEUP, time, pendingIntent);
if(time == 0 || time == NO_ALARM)
am.cancel(pendingIntent);
else {
if(time < DateUtilities.now()) {
time = DateUtilities.now() + (long)((0.5f +
4 * random.nextFloat()) * DateUtilities.ONE_HOUR);
}
Log.e("Astrid", "Alarm (" + task.getId() + ", " + type +
") set for " + new Date(time));
am.set(AlarmManager.RTC_WAKEUP, time, pendingIntent);
}
}
}

@ -8,6 +8,7 @@ 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.astrid.alarms.AlarmService;
import com.todoroo.astrid.service.AstridDependencyInjector;
/**
@ -33,6 +34,7 @@ public class ReminderStartupReceiver extends BroadcastReceiver {
ContextManager.setContext(context);
try {
ReminderService.getInstance().scheduleAllAlarms();
AlarmService.getInstance().scheduleAllAlarms();
} catch (Exception e) {
DependencyInjectionService.getInstance().inject(this);
exceptionService.reportError("reminder-startup", e); //$NON-NLS-1$

@ -4,6 +4,7 @@ import java.text.DateFormatSymbols;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import android.app.Activity;
import android.view.LayoutInflater;
@ -11,13 +12,13 @@ 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 android.widget.AdapterView.OnItemSelectedListener;
import android.widget.CompoundButton.OnCheckedChangeListener;
import com.google.ical.values.Frequency;
import com.google.ical.values.RRule;
@ -61,10 +62,13 @@ public class RepeatControlSet implements TaskEditControlSet {
private final LinearLayout repeatContainer;
private final LinearLayout daysOfWeekContainer;
private final CompoundButton[] daysOfWeek = new CompoundButton[7];
private Task model;
@Autowired
ExceptionService exceptionService;
boolean setInterval = false;
// --- implementation
public RepeatControlSet(final Activity activity, ViewGroup parent) {
@ -111,10 +115,22 @@ public class RepeatControlSet implements TaskEditControlSet {
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);
if(position == INTERVAL_WEEKS) {
Date date;
if(model.getValue(Task.DUE_DATE) == 0)
date = new Date();
else
date = new Date(model.getValue(Task.DUE_DATE));
int dayOfWeek = date.getDay();
for(int i = 0; i < 7; i++)
daysOfWeek[i].setChecked(i == dayOfWeek);
}
}
@Override
@ -122,6 +138,7 @@ public class RepeatControlSet implements TaskEditControlSet {
//
}
});
daysOfWeekContainer.setVisibility(View.GONE);
}
/** Set up the repeat value button */
@ -156,6 +173,8 @@ public class RepeatControlSet implements TaskEditControlSet {
@Override
public void readFromTask(Task task) {
model = task;
String recurrence = task.getValue(Task.RECURRENCE);
if(recurrence == null)
recurrence = ""; //$NON-NLS-1$
@ -172,17 +191,6 @@ public class RepeatControlSet implements TaskEditControlSet {
break;
case WEEKLY: {
interval.setSelection(INTERVAL_WEEKS);
// clear all day of week checks, then update them
for(int i = 0; i < 7; i++)
daysOfWeek[i].setChecked(false);
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:
@ -196,6 +204,19 @@ public class RepeatControlSet implements TaskEditControlSet {
exceptionService.reportError("repeat-unhandled-rule", //$NON-NLS-1$
new Exception("Unhandled rrule frequency: " + recurrence)); //$NON-NLS-1$
}
// clear all day of week checks, then update them
for(int i = 0; i < 7; i++)
daysOfWeek[i].setChecked(false);
for(WeekdayNum day : rrule.getByDay()) {
for(int i = 0; i < 7; i++)
if(daysOfWeek[i].getTag() == day.wday)
daysOfWeek[i].setChecked(true);
}
// suppress first call to interval.onItemSelected
setInterval = true;
} catch (ParseException e) {
recurrence = ""; //$NON-NLS-1$
exceptionService.reportError("repeat-parse-exception", e); //$NON-NLS-1$
@ -226,6 +247,7 @@ public class RepeatControlSet implements TaskEditControlSet {
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())
@ -239,6 +261,7 @@ public class RepeatControlSet implements TaskEditControlSet {
case INTERVAL_HOURS:
rrule.setFreq(Frequency.HOURLY);
}
result = rrule.toIcal();
}
task.setValue(Task.RECURRENCE, result);

@ -92,8 +92,8 @@ public class RepeatDetailExposer extends BroadcastReceiver implements DetailExpo
}
interval = "<b>" + interval + "</b>"; //$NON-NLS-1$//$NON-NLS-2$
if(rrule.getFreq() == Frequency.WEEKLY) {
List<WeekdayNum> byDay = rrule.getByDay();
List<WeekdayNum> byDay = rrule.getByDay();
if(rrule.getFreq() == Frequency.WEEKLY || byDay.size() != 7) {
if(byDay.size() > 0) {
StringBuilder byDayString = new StringBuilder();
DateFormatSymbols dfs = new DateFormatSymbols();

@ -1,6 +1,7 @@
package com.todoroo.astrid.repeats;
import java.text.ParseException;
import java.util.Collections;
import java.util.Date;
import java.util.TimeZone;
@ -15,107 +16,134 @@ import com.google.ical.values.DateValue;
import com.google.ical.values.DateValueImpl;
import com.google.ical.values.Frequency;
import com.google.ical.values.RRule;
import com.todoroo.andlib.service.Autowired;
import com.todoroo.andlib.service.DependencyInjectionService;
import com.todoroo.andlib.service.ExceptionService;
import com.todoroo.andlib.utility.DateUtilities;
import com.todoroo.astrid.api.AstridApiConstants;
import com.todoroo.astrid.core.PluginServices;
import com.todoroo.astrid.model.Task;
import com.todoroo.astrid.service.TaskService;
public class RepeatTaskCompleteListener extends BroadcastReceiver {
@Autowired
private TaskService taskService;
@Autowired
private ExceptionService exceptionService;
@Override
public void onReceive(Context context, Intent intent) {
long taskId = intent.getLongExtra(AstridApiConstants.EXTRAS_TASK_ID, -1);
if(taskId == -1)
return;
DependencyInjectionService.getInstance().inject(this);
Task task = taskService.fetchById(taskId, Task.ID, Task.RECURRENCE,
Task task = PluginServices.getTaskService().fetchById(taskId, Task.ID, Task.RECURRENCE,
Task.DUE_DATE, Task.FLAGS, Task.HIDE_UNTIL);
if(task == null)
return;
String recurrence = task.getValue(Task.RECURRENCE);
if(recurrence != null && recurrence.length() > 0) {
DateValue repeatFrom;
Date repeatFromDate = new Date();
DateValue today = new DateValueImpl(repeatFromDate.getYear() + 1900,
repeatFromDate.getMonth() + 1, repeatFromDate.getDate());
if(task.hasDueDate() && !task.getFlag(Task.FLAGS, Task.FLAG_REPEAT_AFTER_COMPLETION)) {
repeatFromDate = new Date(task.getValue(Task.DUE_DATE));
if(task.hasDueTime()) {
repeatFrom = new DateTimeValueImpl(repeatFromDate.getYear() + 1900,
repeatFromDate.getMonth() + 1, repeatFromDate.getDate(),
repeatFromDate.getHours(), repeatFromDate.getMinutes(), repeatFromDate.getSeconds());
} else {
repeatFrom = new DateValueImpl(repeatFromDate.getYear() + 1900,
repeatFromDate.getMonth() + 1, repeatFromDate.getDate());
}
long newDueDate;
try {
newDueDate = computeNextDueDate(task, recurrence);
if(newDueDate == -1)
return;
} catch (ParseException e) {
PluginServices.getExceptionService().reportError("repeat-parse", e); //$NON-NLS-1$
return;
}
long hideUntil = task.getValue(Task.HIDE_UNTIL);
if(hideUntil > 0 && task.getValue(Task.DUE_DATE) > 0) {
hideUntil += newDueDate - task.getValue(Task.DUE_DATE);
}
// clone to create new task
Task clone = PluginServices.getTaskService().clone(task);
clone.setValue(Task.DUE_DATE, newDueDate);
clone.setValue(Task.HIDE_UNTIL, hideUntil);
clone.setValue(Task.COMPLETION_DATE, 0L);
clone.setValue(Task.TIMER_START, 0L);
clone.setValue(Task.ELAPSED_SECONDS, 0);
PluginServices.getTaskService().save(clone, false);
// clear recurrence from completed task so it can be re-completed
task.setValue(Task.RECURRENCE, ""); //$NON-NLS-1$
PluginServices.getTaskService().save(task, false);
// send a broadcast
Intent broadcastIntent = new Intent(AstridApiConstants.BROADCAST_EVENT_TASK_REPEATED);
broadcastIntent.putExtra(AstridApiConstants.EXTRAS_TASK_ID, clone.getId());
broadcastIntent.putExtra(AstridApiConstants.EXTRAS_OLD_DUE_DATE, task.getValue(Task.DUE_DATE));
broadcastIntent.putExtra(AstridApiConstants.EXTRAS_NEW_DUE_DATE, newDueDate);
context.sendOrderedBroadcast(broadcastIntent, null);
}
}
public static long computeNextDueDate(Task task, String recurrence) throws ParseException {
DateValue repeatFrom;
Date repeatFromDate = new Date();
DateValue today = new DateValueImpl(repeatFromDate.getYear() + 1900,
repeatFromDate.getMonth() + 1, repeatFromDate.getDate());
if(task.hasDueDate() && !task.getFlag(Task.FLAGS, Task.FLAG_REPEAT_AFTER_COMPLETION)) {
repeatFromDate = new Date(task.getValue(Task.DUE_DATE));
if(task.hasDueTime()) {
repeatFrom = new DateTimeValueImpl(repeatFromDate.getYear() + 1900,
repeatFromDate.getMonth() + 1, repeatFromDate.getDate(),
repeatFromDate.getHours(), repeatFromDate.getMinutes(), repeatFromDate.getSeconds());
} else {
repeatFrom = today;
repeatFrom = new DateValueImpl(repeatFromDate.getYear() + 1900,
repeatFromDate.getMonth() + 1, repeatFromDate.getDate());
}
} else {
repeatFrom = today;
}
// invoke the recurrence iterator
try {
long newDueDate;
RRule rrule = new RRule(recurrence);
if(rrule.getFreq() == Frequency.HOURLY) {
// invoke the recurrence iterator
long newDueDate = -1;
RRule rrule = new RRule(recurrence);
// handle the iCalendar "byDay" field differently depending on if
// we are weekly or otherwise
if(rrule.getFreq() != Frequency.WEEKLY) {
rrule.setByDay(Collections.EMPTY_LIST);
}
if(rrule.getFreq() == Frequency.HOURLY) {
newDueDate = task.createDueDate(Task.URGENCY_SPECIFIC_DAY_TIME,
repeatFromDate.getTime() + DateUtilities.ONE_HOUR * rrule.getInterval());
} else {
RecurrenceIterator iterator = RecurrenceIteratorFactory.createRecurrenceIterator(rrule,
repeatFrom, TimeZone.getDefault());
DateValue nextDate = repeatFrom;
if(repeatFrom.compareTo(today) < 0)
iterator.advanceTo(today);
for(int i = 0; i < 10; i++) { // ten tries then we give up
if(!iterator.hasNext())
return -1;
nextDate = iterator.next();
if(nextDate.compareTo(repeatFrom) == 0)
continue;
if(nextDate instanceof DateTimeValueImpl) {
DateTimeValueImpl newDateTime = (DateTimeValueImpl)nextDate;
newDueDate = task.createDueDate(Task.URGENCY_SPECIFIC_DAY_TIME,
repeatFromDate.getTime() + DateUtilities.ONE_HOUR * rrule.getInterval());
Date.UTC(newDateTime.year() - 1900, newDateTime.month() - 1,
newDateTime.day(), newDateTime.hour(),
newDateTime.minute(), newDateTime.second()));
} else {
RecurrenceIterator iterator = RecurrenceIteratorFactory.createRecurrenceIterator(rrule,
repeatFrom, TimeZone.getDefault());
DateValue nextDate;
if(repeatFrom.compareTo(today) < 0) {
iterator.advanceTo(today);
if(!iterator.hasNext())
return;
nextDate = iterator.next();
} else {
iterator.advanceTo(repeatFrom);
if(!iterator.hasNext())
return;
nextDate = iterator.next();
nextDate = iterator.next();
}
if(nextDate instanceof DateTimeValueImpl) {
DateTimeValueImpl newDateTime = (DateTimeValueImpl)nextDate;
newDueDate = task.createDueDate(Task.URGENCY_SPECIFIC_DAY_TIME,
Date.UTC(newDateTime.year() - 1900, newDateTime.month() - 1,
newDateTime.day(), newDateTime.hour(),
newDateTime.minute(), newDateTime.second()));
} else {
newDueDate = task.createDueDate(Task.URGENCY_SPECIFIC_DAY,
new Date(nextDate.year() - 1900, nextDate.month() - 1,
nextDate.day()).getTime());
}
newDueDate = task.createDueDate(Task.URGENCY_SPECIFIC_DAY,
new Date(nextDate.year() - 1900, nextDate.month() - 1,
nextDate.day()).getTime());
}
long hideUntil = task.getValue(Task.HIDE_UNTIL);
if(hideUntil > 0 && task.getValue(Task.DUE_DATE) > 0) {
hideUntil += newDueDate - task.getValue(Task.DUE_DATE);
}
if(newDueDate > DateUtilities.now() && newDueDate != repeatFromDate.getTime())
break;
task = taskService.clone(task);
task.setValue(Task.DUE_DATE, newDueDate);
task.setValue(Task.HIDE_UNTIL, hideUntil);
task.setValue(Task.COMPLETION_DATE, 0L);
taskService.save(task, false);
} catch (ParseException e) {
exceptionService.reportError("recurrence-rule: " + recurrence, e); //$NON-NLS-1$
}
}
if(newDueDate == -1)
return -1;
return newDueDate;
}
}

@ -114,12 +114,12 @@ public class MilkPreferences extends TodorooPreferences {
// last sync was error
else if(MilkUtilities.getLastAttemptedSyncDate() != 0) {
status = r.getString(R.string.rmilk_status_failed,
DateUtilities.getDateWithTimeFormat(MilkPreferences.this).
format(new Date(MilkUtilities.getLastAttemptedSyncDate())));
DateUtilities.getDateStringWithTime(MilkPreferences.this,
new Date(MilkUtilities.getLastAttemptedSyncDate())));
if(MilkUtilities.getLastSyncDate() > 0) {
subtitle = r.getString(R.string.rmilk_status_failed_subtitle,
DateUtilities.getDateWithTimeFormat(MilkPreferences.this).
format(new Date(MilkUtilities.getLastSyncDate())));
DateUtilities.getDateStringWithTime(MilkPreferences.this,
new Date(MilkUtilities.getLastSyncDate())));
}
statusColor = Color.rgb(100, 0, 0);
preference.setOnPreferenceClickListener(new OnPreferenceClickListener() {
@ -132,8 +132,8 @@ public class MilkPreferences extends TodorooPreferences {
});
} else if(MilkUtilities.getLastSyncDate() > 0) {
status = r.getString(R.string.rmilk_status_success,
DateUtilities.getDateWithTimeFormat(MilkPreferences.this).
format(new Date(MilkUtilities.getLastSyncDate())));
DateUtilities.getDateStringWithTime(MilkPreferences.this,
new Date(MilkUtilities.getLastSyncDate())));
statusColor = Color.rgb(0, 100, 0);
} else {
status = r.getString(R.string.rmilk_status_never);

@ -11,12 +11,14 @@ import android.content.res.Resources;
import android.graphics.drawable.BitmapDrawable;
import com.timsu.astrid.R;
import com.todoroo.andlib.sql.Criterion;
import com.todoroo.andlib.sql.QueryTemplate;
import com.todoroo.astrid.api.AstridApiConstants;
import com.todoroo.astrid.api.Filter;
import com.todoroo.astrid.api.FilterCategory;
import com.todoroo.astrid.api.FilterListHeader;
import com.todoroo.astrid.api.FilterListItem;
import com.todoroo.astrid.dao.TaskDao.TaskCriteria;
import com.todoroo.astrid.model.Metadata;
import com.todoroo.astrid.tags.TagService.Tag;
@ -31,11 +33,11 @@ public class TagFilterExposer extends BroadcastReceiver {
private TagService tagService;
@SuppressWarnings("nls")
private Filter filterFromTag(Context context, Tag tag) {
private Filter filterFromTag(Context context, Tag tag, Criterion criterion) {
String listTitle = context.getString(R.string.tag_FEx_tag_w_size).
replace("$T", tag.tag).replace("$C", Integer.toString(tag.count));
String title = context.getString(R.string.tag_FEx_name, tag.tag);
QueryTemplate tagTemplate = tag.queryTemplate();
QueryTemplate tagTemplate = tag.queryTemplate(criterion);
ContentValues contentValues = new ContentValues();
contentValues.put(Metadata.KEY.name, TagService.KEY);
contentValues.put(TagService.TAG.name, tag.tag);
@ -59,7 +61,7 @@ public class TagFilterExposer extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
tagService = TagService.getInstance();
Tag[] tagsByAlpha = tagService.getGroupedTags(TagService.GROUPED_TAGS_BY_ALPHA);
Tag[] tagsByAlpha = tagService.getGroupedTags(TagService.GROUPED_TAGS_BY_ALPHA, TaskCriteria.notDeleted());
// If user does not have any tags, don't show this section at all
if(tagsByAlpha.length == 0)
@ -67,34 +69,47 @@ public class TagFilterExposer extends BroadcastReceiver {
Resources r = context.getResources();
Tag[] tagsBySize = tagService.getGroupedTags(TagService.GROUPED_TAGS_BY_SIZE);
Filter[] filtersByAlpha = new Filter[tagsByAlpha.length];
for(int i = 0; i < tagsByAlpha.length; i++)
filtersByAlpha[i] = filterFromTag(context, tagsByAlpha[i]);
filtersByAlpha[i] = filterFromTag(context, tagsByAlpha[i], TaskCriteria.notDeleted());
Tag[] tagsBySize = tagService.getGroupedTags(TagService.GROUPED_TAGS_BY_SIZE, TaskCriteria.isActive());
Filter[] filtersBySize = new Filter[tagsBySize.length];
for(int i = 0; i < tagsBySize.length; i++)
filtersBySize[i] = filterFromTag(context, tagsBySize[i]);
filtersBySize[i] = filterFromTag(context, tagsBySize[i], TaskCriteria.isActive());
Tag[] completed = tagService.getGroupedTags(TagService.GROUPED_TAGS_BY_SIZE, TaskCriteria.completed());
Filter[] filtersCompleted = new Filter[completed.length];
for(int i = 0; i < completed.length; i++)
filtersCompleted[i] = filterFromTag(context, completed[i], TaskCriteria.completed());
FilterListHeader tagsHeader = new FilterListHeader(context.getString(R.string.tag_FEx_header));
Filter untagged = new Filter(r.getString(R.string.tag_FEx_untagged),
r.getString(R.string.tag_FEx_untagged),
tagService.untaggedTemplate(),
null);
untagged.listingIcon = ((BitmapDrawable)r.getDrawable(R.drawable.filter_untagged)).getBitmap();
FilterCategory tagsCategoryBySize = new FilterCategory(context.getString(R.string.tag_FEx_by_size),
filtersBySize);
tagsCategoryBySize.listingIcon = ((BitmapDrawable)r.getDrawable(R.drawable.filter_tags1)).getBitmap();
FilterCategory tagsCategoryByAlpha = new FilterCategory(context.getString(R.string.tag_FEx_alpha),
FilterCategory tagsCategoryAllByAlpha = new FilterCategory(context.getString(R.string.tag_FEx_alpha),
filtersByAlpha);
tagsCategoryByAlpha.listingIcon = ((BitmapDrawable)r.getDrawable(R.drawable.filter_tags2)).getBitmap();
tagsCategoryAllByAlpha.listingIcon = ((BitmapDrawable)r.getDrawable(R.drawable.filter_tags1)).getBitmap();
FilterCategory tagsCategoryCompleted = new FilterCategory(context.getString(R.string.tag_FEx_completed),
filtersCompleted);
tagsCategoryCompleted.listingIcon = ((BitmapDrawable)r.getDrawable(R.drawable.filter_tags2)).getBitmap();
// transmit filter list
FilterListItem[] list = new FilterListItem[4];
FilterListItem[] list = new FilterListItem[5];
list[0] = tagsHeader;
list[1] = untagged;
list[2] = tagsCategoryBySize;
list[3] = tagsCategoryByAlpha;
list[3] = tagsCategoryCompleted;
list[4] = tagsCategoryAllByAlpha;
Intent broadcastIntent = new Intent(AstridApiConstants.BROADCAST_SEND_FILTERS);
broadcastIntent.putExtra(AstridApiConstants.EXTRAS_RESPONSE, list);
context.sendBroadcast(broadcastIntent, AstridApiConstants.PERMISSION_READ);

@ -1,6 +1,6 @@
package com.todoroo.astrid.tags;
import java.util.ArrayList;
import java.util.LinkedHashSet;
import com.todoroo.andlib.data.TodorooCursor;
import com.todoroo.andlib.data.Property.CountProperty;
@ -82,11 +82,11 @@ public final class TagService {
* @param tag
* @return
*/
public QueryTemplate queryTemplate() {
public QueryTemplate queryTemplate(Criterion criterion) {
return new QueryTemplate().join(Join.inner(Metadata.TABLE,
Task.ID.eq(Metadata.TASK))).where(Criterion.and(
MetadataCriteria.withKey(KEY), TAG.eq(tag),
TaskCriteria.isActive()));
criterion));
}
}
@ -100,13 +100,14 @@ public final class TagService {
/**
* Return all tags ordered by given clause
*
* @param taskId
* @param order ordering
* @param activeStatus criterion for specifying completed or uncompleted
* @return empty array if no tags, otherwise array
*/
public Tag[] getGroupedTags(Order order) {
public Tag[] getGroupedTags(Order order, Criterion activeStatus) {
Query query = Query.select(TAG.as(TAG.name), COUNT).
join(Join.inner(Task.TABLE, Metadata.TASK.eq(Task.ID))).
where(Criterion.and(TaskCriteria.isActive(), MetadataCriteria.withKey(KEY))).
where(Criterion.and(activeStatus, MetadataCriteria.withKey(KEY))).
orderBy(order).groupBy(TAG);
TodorooCursor<Metadata> cursor = metadataDao.query(query);
try {
@ -175,7 +176,7 @@ public final class TagService {
* @param taskId
* @param tags
*/
public void synchronizeTags(long taskId, ArrayList<String> tags) {
public void synchronizeTags(long taskId, LinkedHashSet<String> tags) {
metadataDao.deleteWhere(Criterion.and(MetadataCriteria.byTask(taskId),
MetadataCriteria.withKey(KEY)));

@ -1,12 +1,11 @@
package com.todoroo.astrid.tags;
import java.util.ArrayList;
import java.util.LinkedHashSet;
import android.app.Activity;
import android.text.Editable;
import android.text.TextWatcher;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.ArrayAdapter;
import android.widget.AutoCompleteTextView;
import android.widget.ImageButton;
@ -15,6 +14,7 @@ import android.widget.TextView;
import com.timsu.astrid.R;
import com.todoroo.andlib.data.TodorooCursor;
import com.todoroo.andlib.sql.Criterion;
import com.todoroo.astrid.activity.TaskEditActivity.TaskEditControlSet;
import com.todoroo.astrid.model.Metadata;
import com.todoroo.astrid.model.Task;
@ -28,11 +28,6 @@ import com.todoroo.astrid.tags.TagService.Tag;
*/
public final class TagsControlSet implements TaskEditControlSet {
// --- constants
/** Number of tags a task can have */
static final int MAX_TAGS = 5;
// --- instance variables
private final TagService tagService = TagService.getInstance();
@ -41,30 +36,35 @@ public final class TagsControlSet implements TaskEditControlSet {
private final Activity activity;
public TagsControlSet(Activity activity, int tagsContainer) {
allTags = tagService.getGroupedTags(TagService.GROUPED_TAGS_BY_SIZE);
allTags = tagService.getGroupedTags(TagService.GROUPED_TAGS_BY_SIZE, Criterion.all);
this.activity = activity;
this.tagsContainer = (LinearLayout) activity.findViewById(tagsContainer);
}
@SuppressWarnings("nls")
@Override
public void readFromTask(Task task) {
// tags (only configure if not already set)
if(tagsContainer.getChildCount() == 0) {
TodorooCursor<Metadata> cursor = tagService.getTags(task.getId());
try {
for(cursor.moveToFirst(); !cursor.isAfterLast(); cursor.moveToNext())
addTag(cursor.get(TagService.TAG));
} finally {
cursor.close();
}
addTag("");
System.err.println("TAGS loading... old size = " + tagsContainer.getChildCount());
tagsContainer.removeAllViews();
TodorooCursor<Metadata> cursor = tagService.getTags(task.getId());
try {
for(cursor.moveToFirst(); !cursor.isAfterLast(); cursor.moveToNext())
addTag(cursor.get(TagService.TAG));
} finally {
cursor.close();
}
if(tagsContainer.getChildCount() == 0)
addTag(""); //$NON-NLS-1$
System.err.println("TAGS loaded ");
}
@Override
public void writeToModel(Task task) {
ArrayList<String> tags = new ArrayList<String>();
// this is a case where we're asked to save but the UI was not yet populated
if(tagsContainer.getChildCount() == 0)
return;
LinkedHashSet<String> tags = new LinkedHashSet<String>();
for(int i = 0; i < tagsContainer.getChildCount(); i++) {
TextView tagName = (TextView)tagsContainer.getChildAt(i).findViewById(R.id.text1);
@ -74,14 +74,12 @@ public final class TagsControlSet implements TaskEditControlSet {
}
tagService.synchronizeTags(task.getId(), tags);
System.err.println("TAGS saved " + tags);
}
/** Adds a tag to the tag field */
boolean addTag(String tagName) {
if (tagsContainer.getChildCount() >= MAX_TAGS) {
return false;
}
System.err.println("TAG ADDING ui " + tagName);
LayoutInflater inflater = activity.getLayoutInflater();
final View tagItem = inflater.inflate(R.layout.tag_edit_row, null);
tagsContainer.addView(tagItem);
@ -93,33 +91,40 @@ public final class TagsControlSet implements TaskEditControlSet {
new ArrayAdapter<Tag>(activity,
android.R.layout.simple_dropdown_item_1line, allTags);
textView.setAdapter(tagsAdapter);
textView.addTextChangedListener(new TextWatcher() {
@SuppressWarnings("nls")
public void onTextChanged(CharSequence s, int start, int before,
int count) {
if(start == 0 && tagsContainer.getChildAt(
tagsContainer.getChildCount()-1) == tagItem) {
addTag("");
}
}
public void afterTextChanged(Editable s) {
//
textView.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View arg0) {
View lastItem = tagsContainer.getChildAt(tagsContainer.getChildCount()-1);
TextView lastText = (TextView) lastItem.findViewById(R.id.text1);
if(lastText.getText().length() != 0) {
addTag(""); //$NON-NLS-1$
}
}
});
public void beforeTextChanged(CharSequence s, int start, int count,
int after) {
//
/*textView.setOnEditorActionListener(new OnEditorActionListener() {
@Override
public boolean onEditorAction(TextView arg0, int actionId, KeyEvent arg2) {
if(actionId != EditorInfo.IME_NULL)
return false;
View lastItem = tagsContainer.getChildAt(tagsContainer.getChildCount()-1);
TextView lastText = (TextView) lastItem.findViewById(R.id.text1);
if(lastText.getText().length() != 0) {
addTag(""); //$NON-NLS-1$
}
return true;
}
});
});*/
ImageButton reminderRemoveButton;
reminderRemoveButton = (ImageButton)tagItem.findViewById(R.id.button1);
reminderRemoveButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
if(textView.getText().length() > 0)
if(tagsContainer.getChildCount() > 0)
tagsContainer.removeView(tagItem);
else
textView.setText(""); //$NON-NLS-1$
}
});

@ -10,7 +10,6 @@ import com.todoroo.andlib.service.DependencyInjectionService;
import com.todoroo.astrid.activity.TaskEditActivity.TaskEditControlSet;
import com.todoroo.astrid.model.Task;
import com.todoroo.astrid.ui.TimeDurationControlSet;
import com.todoroo.astrid.ui.TimeDurationControlSet.TimeDurationType;
/**
* Control Set for managing repeats
@ -31,11 +30,11 @@ public class TimerControlSet implements TaskEditControlSet {
LayoutInflater.from(activity).inflate(R.layout.timer_control, parent, true);
estimated = new TimeDurationTaskEditControlSet(Task.ESTIMATED_SECONDS,
R.id.estimatedDuration, 0, R.string.DLG_hour_minutes,
TimeDurationType.HOURS_MINUTES);
R.id.estimatedDuration, 0, R.string.DLG_hour_minutes
);
elapsed = new TimeDurationTaskEditControlSet(Task.ELAPSED_SECONDS, R.id.elapsedDuration,
0, R.string.DLG_hour_minutes,
TimeDurationType.HOURS_MINUTES);
0, R.string.DLG_hour_minutes
);
}
@Override
@ -62,10 +61,10 @@ public class TimerControlSet implements TaskEditControlSet {
private final IntegerProperty property;
public TimeDurationTaskEditControlSet(IntegerProperty property, int timeButtonId,
int prefixResource, int titleResource, TimeDurationType type) {
int prefixResource, int titleResource) {
this.property = property;
this.controlSet = new TimeDurationControlSet(activity,
timeButtonId, prefixResource, titleResource, type);
timeButtonId, prefixResource, titleResource);
}
@Override

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

@ -0,0 +1,23 @@
<?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">
<TextView android:id="@+id/alarms_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/alarm_ACS_label"
style="@style/TextAppearance.GEN_EditLabel" />
<LinearLayout android:id="@+id/alert_container"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
</LinearLayout>
<Button android:id="@+id/alarms_add"
android:text="@string/alarm_ACS_button"
android:layout_width="fill_parent"
android:layout_height="wrap_content"/>
</merge>

@ -0,0 +1,42 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2008 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<Button android:id="@+id/date"
android:layout_weight="0.7"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<Button android:id="@+id/time"
android:layout_weight="1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<ImageButton android:id="@+id/button1"
style="?android:attr/buttonStyleInset"
android:src="@android:drawable/ic_delete"
android:layout_width="wrap_content"
android:layout_height="fill_parent"
android:layout_marginTop="2dip"
android:layout_marginRight="2dip"
android:layout_marginBottom="2dip"
android:gravity="center_vertical"
/>
</LinearLayout>

@ -34,6 +34,12 @@
android:visibility="gone" />
</LinearLayout>
<View
android:layout_width="fill_parent"
android:layout_height="1dip"
android:padding="5dip"
android:background="@android:drawable/divider_horizontal_dark" />
</merge>

@ -10,13 +10,19 @@
android:paddingRight="4dip"
android:minHeight="40dip"
android:orientation="vertical">
<LinearLayout android:id="@+id/task_row"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_weight="100"
android:orientation="horizontal">
<!-- importance -->
<View android:id="@+id/importance"
android:layout_width="2dip"
android:layout_height="fill_parent"
android:layout_marginRight="5dip" />
<!-- completion check-box -->
<CheckBox android:id="@+id/completeBox"
android:layout_width="34dip"
@ -29,13 +35,12 @@
android:layout_height="wrap_content"
android:layout_weight="100"
android:paddingLeft="8dip"
android:paddingRight="3dip"
android:orientation="vertical">
<!-- task name -->
<TextView android:id="@+id/title"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_height="fill_parent"
style="@style/TextAppearance.TAd_ItemTitle"
android:gravity="center_vertical"/>
@ -52,11 +57,6 @@
</LinearLayout>
<!-- importance -->
<View android:id="@+id/importance"
android:layout_width="2dip"
android:layout_height="fill_parent" />
</LinearLayout>
<TextView android:id="@+id/extendedDetails"

@ -36,11 +36,15 @@
</LinearLayout>
<!-- Body -->
<FrameLayout
<android.gesture.GestureOverlayView android:id="@+id/gestures"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_weight="100">
android:layout_weight="100"
android:uncertainGestureColor="#00000000"
android:eventsInterceptionEnabled="true"
android:orientation="vertical">
<!-- No Tasks label -->
<TextView android:id="@android:id/empty"
@ -57,7 +61,7 @@
android:layout_width="fill_parent"
android:layout_height="fill_parent"/>
</FrameLayout>
</android.gesture.GestureOverlayView>
<!-- Footer -->
<LinearLayout
@ -95,6 +99,5 @@
android:scaleType="fitCenter"/>
</LinearLayout>
</LinearLayout>

@ -0,0 +1,99 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- See the file "LICENSE" for the full license governing this code. -->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/taskListParent"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="@drawable/background_gradient"
android:orientation="vertical">
<!-- Header -->
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:orientation="horizontal"
android:background="@drawable/edit_header">
<!-- Back Button -->
<ImageView android:id="@+id/back"
android:layout_width="wrap_content"
android:layout_height="fill_parent"
android:layout_weight="1"
android:src="@drawable/tango_previous"
android:paddingLeft="5dip"
android:paddingRight="8dip"/>
<!-- List Label -->
<TextView android:id="@+id/listLabel"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="100"
android:singleLine="true"
android:paddingTop="6dip"
android:paddingRight="50dip"
style="@style/TextAppearance.TLA_Header"/>
</LinearLayout>
<FrameLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_weight="100">
<!-- No Tasks label -->
<TextView android:id="@android:id/empty"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:visibility="gone"
android:text="@string/TLA_no_items"
style="@style/TextAppearance.TLA_NoItems"/>
<!-- Task List -->
<ListView android:id="@android:id/list"
android:scrollbars="vertical"
android:cacheColorHint="#00000000"
android:layout_width="fill_parent"
android:layout_height="fill_parent"/>
</FrameLayout>
<!-- Footer -->
<LinearLayout
android:id="@+id/taskListFooter"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:orientation="horizontal">
<!-- Quick Add Button -->
<ImageButton android:id="@+id/quickAddButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:visibility="gone"
android:src="@drawable/tango_add"
android:scaleType="fitCenter"/>
<!-- Quick Add Task -->
<EditText android:id="@+id/quickAddText"
android:layout_width="wrap_content"
android:layout_height="fill_parent"
android:layout_weight="100"
android:hint="@string/TLA_quick_add_hint"
android:singleLine="true"
android:autoText="true"
android:capitalize="sentences"/>
<!-- Extended Add Button -->
<ImageButton android:id="@+id/extendedAddButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:src="@drawable/tango_edit"
android:scaleType="fitCenter"/>
</LinearLayout>
</LinearLayout>

@ -1,7 +1,7 @@
<?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">
<!-- estimated time -->
<TextView
android:layout_width="wrap_content"
@ -23,6 +23,12 @@
android:id="@+id/elapsedDuration"
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" />
</merge>

Binary file not shown.

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- See the file "LICENSE" for the full license governing this code. -->
<resources xmlns:android="http://schemas.android.com/apk/res/android">
<!-- Resources for built-in timers plug-in -->
<!-- Task Edit Activity: Container Label -->
<string name="alarm_ACS_label">Alarms</string>
<!-- Task Edit Activity: Add New Alarn -->
<string name="alarm_ACS_button">Add an Alarm</string>
<!-- Task Detail for Alarms (%s -> time)-->
<string name="alarm_ADE_detail">Alarm %s</string>
<string-array name="reminders_alarm">
<!-- reminders related to alarm -->
<item>Alarm!</item>
</string-array>
</resources>

@ -23,10 +23,13 @@
<string name="tag_FEx_header">Tags</string>
<!-- filter header for tags, sorted by size -->
<string name="tag_FEx_by_size">By Size</string>
<string name="tag_FEx_by_size">Active</string>
<!-- filter header for tags, sorted by name -->
<string name="tag_FEx_alpha">Alphabetical</string>
<!-- filter header for tags of completed tasks -->
<string name="tag_FEx_completed">Completed</string>
<!-- filter header for all tags, sorted by name -->
<string name="tag_FEx_alpha">All Tags</string>
<!-- filter for untagged tasks -->
<string name="tag_FEx_untagged">Untagged</string>

@ -12,22 +12,23 @@ import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.graphics.drawable.BitmapDrawable;
import android.net.Uri;
import android.os.Bundle;
import android.util.DisplayMetrics;
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.ContextMenu.ContextMenuInfo;
import android.view.inputmethod.EditorInfo;
import android.widget.EditText;
import android.widget.ExpandableListView;
import android.widget.ExpandableListView.ExpandableListContextMenuInfo;
import android.widget.FrameLayout;
import android.widget.TextView;
import android.widget.TextView.OnEditorActionListener;
import android.widget.Toast;
import android.widget.ExpandableListView.ExpandableListContextMenuInfo;
import android.widget.TextView.OnEditorActionListener;
import com.flurry.android.FlurryAgent;
import com.timsu.astrid.R;
@ -137,9 +138,9 @@ public class FilterListActivity extends ExpandableListActivity {
R.string.FLA_menu_search);
item.setIcon(android.R.drawable.ic_menu_search);
/*item = menu.add(Menu.NONE, MENU_HELP_ID, Menu.NONE,
item = menu.add(Menu.NONE, MENU_HELP_ID, Menu.NONE,
R.string.FLA_menu_help);
item.setIcon(android.R.drawable.ic_menu_help);*/
item.setIcon(android.R.drawable.ic_menu_help);
return true;
}
@ -251,7 +252,6 @@ public class FilterListActivity extends ExpandableListActivity {
if(item instanceof Filter) {
Filter filter = (Filter) item;
info.targetView.setTag(filter);
menuItem = menu.add(0, CONTEXT_MENU_SHORTCUT, 0, R.string.FLA_context_shortcut);
menuItem.setIntent(ShortcutActivity.createIntent(filter));
}
@ -295,13 +295,9 @@ public class FilterListActivity extends ExpandableListActivity {
bitmap.getWidth(), bitmap.getHeight()), null);
Intent createShortcutIntent = new Intent();
createShortcutIntent.putExtra(
Intent.EXTRA_SHORTCUT_INTENT,
shortcutIntent);
createShortcutIntent.putExtra(
Intent.EXTRA_SHORTCUT_NAME, label);
createShortcutIntent.putExtra(
Intent.EXTRA_SHORTCUT_ICON, bitmap);
createShortcutIntent.putExtra(Intent.EXTRA_SHORTCUT_INTENT, shortcutIntent);
createShortcutIntent.putExtra(Intent.EXTRA_SHORTCUT_NAME, label);
createShortcutIntent.putExtra(Intent.EXTRA_SHORTCUT_ICON, bitmap);
createShortcutIntent.setAction("com.android.launcher.action.INSTALL_SHORTCUT"); //$NON-NLS-1$
sendBroadcast(createShortcutIntent);
@ -323,7 +319,9 @@ public class FilterListActivity extends ExpandableListActivity {
}
case MENU_HELP_ID: {
// TODO
Intent intent = new Intent(Intent.ACTION_VIEW,
Uri.parse("http://weloveastrid.com/help-user-guide-astrid-v3/filters/")); //$NON-NLS-1$
startActivity(intent);
return true;
}
@ -331,49 +329,9 @@ public class FilterListActivity extends ExpandableListActivity {
ExpandableListContextMenuInfo info = (ExpandableListContextMenuInfo)item.getMenuInfo();
final Intent shortcutIntent = item.getIntent();
final Filter filter = (Filter)info.targetView.getTag();
FrameLayout frameLayout = new FrameLayout(this);
frameLayout.setPadding(10, 0, 10, 0);
final EditText editText = new EditText(this);
if(filter.listingTitle == null)
filter.listingTitle = ""; //$NON-NLS-1$
editText.setText(filter.listingTitle.
replaceAll("\\(\\d+\\)$", "").trim()); //$NON-NLS-1$ //$NON-NLS-2$
frameLayout.addView(editText, new FrameLayout.LayoutParams(
FrameLayout.LayoutParams.FILL_PARENT,
FrameLayout.LayoutParams.WRAP_CONTENT));
final Runnable createShortcut = new Runnable() {
@Override
public void run() {
String label = editText.getText().toString();
createShortcut(filter, shortcutIntent, label);
}
};
editText.setOnEditorActionListener(new OnEditorActionListener() {
@Override
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
if(actionId == EditorInfo.IME_NULL) {
createShortcut.run();
return true;
}
return false;
}
});
new AlertDialog.Builder(this)
.setTitle(R.string.FLA_shortcut_dialog_title)
.setMessage(R.string.FLA_shortcut_dialog)
.setView(frameLayout)
.setIcon(android.R.drawable.ic_dialog_info)
.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
createShortcut.run();
}
})
.setNegativeButton(android.R.string.cancel, null)
.show();
FilterListItem filter = ((FilterAdapter.ViewHolder)info.targetView.getTag()).item;
if(filter instanceof Filter)
showCreateShortcutDialog(shortcutIntent, (Filter)filter);
return true;
}
@ -388,4 +346,49 @@ public class FilterListActivity extends ExpandableListActivity {
return false;
}
private void showCreateShortcutDialog(final Intent shortcutIntent,
final Filter filter) {
FrameLayout frameLayout = new FrameLayout(this);
frameLayout.setPadding(10, 0, 10, 0);
final EditText editText = new EditText(this);
if(filter.listingTitle == null)
filter.listingTitle = ""; //$NON-NLS-1$
editText.setText(filter.listingTitle.
replaceAll("\\(\\d+\\)$", "").trim()); //$NON-NLS-1$ //$NON-NLS-2$
frameLayout.addView(editText, new FrameLayout.LayoutParams(
FrameLayout.LayoutParams.FILL_PARENT,
FrameLayout.LayoutParams.WRAP_CONTENT));
final Runnable createShortcut = new Runnable() {
@Override
public void run() {
String label = editText.getText().toString();
createShortcut(filter, shortcutIntent, label);
}
};
editText.setOnEditorActionListener(new OnEditorActionListener() {
@Override
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
if(actionId == EditorInfo.IME_NULL) {
createShortcut.run();
return true;
}
return false;
}
});
new AlertDialog.Builder(this)
.setTitle(R.string.FLA_shortcut_dialog_title)
.setMessage(R.string.FLA_shortcut_dialog)
.setView(frameLayout)
.setIcon(android.R.drawable.ic_dialog_info)
.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
createShortcut.run();
}
})
.setNegativeButton(android.R.string.cancel, null)
.show();
}
}

@ -19,6 +19,8 @@
*/
package com.todoroo.astrid.activity;
import java.util.Map.Entry;
import android.app.Activity;
import android.content.ContentValues;
import android.content.Intent;
@ -46,14 +48,13 @@ public class ShortcutActivity extends Activity {
/** token for passing a {@link Filter}'s sql through extras */
public static final String TOKEN_FILTER_SQL = "sql"; //$NON-NLS-1$
/** token for passing a {@link Filter}'s values for new tasks through extras */
/** token for passing a {@link Filter}'s values for new tasks through extras as string */
@Deprecated
public static final String TOKEN_FILTER_VALUES = "v4nt"; //$NON-NLS-1$
/** token for passing a {@link Filter}'s values for new tasks through extras (keys) */
public static final String TOKEN_FILTER_VALUES_KEYS = "v4ntk"; //$NON-NLS-1$
/** token for passing a {@link Filter}'s values for new tasks through extras as exploded ContentValues */
public static final String TOKEN_FILTER_VALUES_ITEM = "v4ntp_"; //$NON-NLS-1$
/** token for passing a {@link Filter}'s values for new tasks through extras (values) */
public static final String TOKEN_FILTER_VALUES_VALUES = "v4ntv"; //$NON-NLS-1$
// --- implementation
@ -81,6 +82,28 @@ public class ShortcutActivity extends Activity {
ContentValues values = null;
if(extras.containsKey(TOKEN_FILTER_VALUES))
values = AndroidUtilities.contentValuesFromString(extras.getString(TOKEN_FILTER_VALUES));
else {
values = new ContentValues();
for(String key : extras.keySet()) {
if(!key.startsWith(TOKEN_FILTER_VALUES_ITEM))
continue;
Object value = extras.get(key);
key = key.substring(TOKEN_FILTER_VALUES_ITEM.length());
// assume one of the big 4...
if(value instanceof String)
values.put(key, (String) value);
else if(value instanceof Integer)
values.put(key, (Integer) value);
else if(value instanceof Double)
values.put(key, (Double) value);
else if(value instanceof Long)
values.put(key, (Long) value);
else
throw new IllegalStateException("Unsupported bundle type " + value.getClass()); //$NON-NLS-1$
}
}
Filter filter = new Filter("", title, new QueryTemplate(), values); //$NON-NLS-1$
filter.sqlQuery = sql;
@ -95,12 +118,26 @@ public class ShortcutActivity extends Activity {
public static Intent createIntent(Filter filter) {
Intent shortcutIntent = new Intent(ContextManager.getContext(),
ShortcutActivity.class);
shortcutIntent.setAction(Intent.ACTION_VIEW);
shortcutIntent.setAction(Intent.ACTION_VIEW);
shortcutIntent.putExtra(ShortcutActivity.TOKEN_FILTER_TITLE, filter.title);
shortcutIntent.putExtra(ShortcutActivity.TOKEN_FILTER_SQL, filter.sqlQuery);
if(filter.valuesForNewTasks != null) {
shortcutIntent.putExtra(ShortcutActivity.TOKEN_FILTER_VALUES,
filter.valuesForNewTasks.toString());
for(Entry<String, Object> item : filter.valuesForNewTasks.valueSet()) {
String key = TOKEN_FILTER_VALUES_ITEM + item.getKey();
Object value = item.getValue();
// assume one of the big 4...
if(value instanceof String)
shortcutIntent.putExtra(key, (String) value);
else if(value instanceof Integer)
shortcutIntent.putExtra(key, (Integer) value);
else if(value instanceof Double)
shortcutIntent.putExtra(key, (Double) value);
else if(value instanceof Long)
shortcutIntent.putExtra(key, (Long) value);
else
throw new IllegalStateException("Unsupported bundle type " + value.getClass()); //$NON-NLS-1$
}
}
return shortcutIntent;
}

@ -41,7 +41,6 @@ import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewGroup.LayoutParams;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemSelectedListener;
@ -68,6 +67,7 @@ import com.todoroo.andlib.service.DependencyInjectionService;
import com.todoroo.andlib.service.ExceptionService;
import com.todoroo.andlib.utility.AndroidUtilities;
import com.todoroo.andlib.utility.DateUtilities;
import com.todoroo.astrid.alarms.AlarmControlSet;
import com.todoroo.astrid.api.AstridApiConstants;
import com.todoroo.astrid.dao.Database;
import com.todoroo.astrid.gcal.GCalControlSet;
@ -218,8 +218,8 @@ public final class TaskEditActivity extends TabActivity {
LinearLayout addonsAddons = (LinearLayout) findViewById(R.id.tab_addons_addons);
if(addOnService.hasPowerPack()) {
controls.add(new GCalControlSet(this, addonsAddons));
separator(addonsAddons);
controls.add(new TimerControlSet(this, addonsAddons));
controls.add(new AlarmControlSet(this, addonsAddons));
}
// show add-on help if necessary
@ -246,17 +246,6 @@ public final class TaskEditActivity extends TabActivity {
setUpListeners();
}
/**
* Creates a separator in the view group specified
* @return
*/
private void separator(ViewGroup parent) {
View view = new View(this);
view.setBackgroundResource(R.drawable.black_white_gradient);
view.setPadding(2, 3, 2, 3);
parent.addView(view);
}
/**
* @return true if task is newly created
*/
@ -768,12 +757,10 @@ public final class TaskEditActivity extends TabActivity {
for(int i = 0; i < labels.length; i++)
updated[i+1] = urgencyValues[i];
if(Task.hasDueTime(dueDate)) {
SimpleDateFormat format = DateUtilities.getDateWithTimeFormat(TaskEditActivity.this);
updated[0] = new UrgencyValue(format.format(new Date(dueDate)),
updated[0] = new UrgencyValue(DateUtilities.getDateStringWithTime(TaskEditActivity.this, new Date(dueDate)),
Task.URGENCY_SPECIFIC_DAY_TIME, dueDate);
} else {
SimpleDateFormat format = DateUtilities.getDateFormat(TaskEditActivity.this);
updated[0] = new UrgencyValue(format.format(new Date(dueDate)),
updated[0] = new UrgencyValue(DateUtilities.getDateString(TaskEditActivity.this, new Date(dueDate)),
Task.URGENCY_SPECIFIC_DAY, dueDate);
}
selection = 0;
@ -805,6 +792,7 @@ public final class TaskEditActivity extends TabActivity {
datePicker.show();
} else {
previousSetting = position;
model.setValue(Task.DUE_DATE, item.dueDate);
}
}
@ -852,6 +840,7 @@ public final class TaskEditActivity extends TabActivity {
private void customDateFinished() {
long time = model.createDueDate(customSetting, customDate.getTime());
model.setValue(Task.DUE_DATE, time);
createUrgencyList(time);
}
@ -933,8 +922,7 @@ public final class TaskEditActivity extends TabActivity {
HideUntilValue[] updated = new HideUntilValue[values.length + 1];
for(int i = 0; i < values.length; i++)
updated[i+1] = values[i];
SimpleDateFormat format = DateUtilities.getDateFormat(TaskEditActivity.this);
updated[0] = new HideUntilValue(format.format(new Date(specificDate)),
updated[0] = new HideUntilValue(DateUtilities.getDateString(TaskEditActivity.this, new Date(specificDate)),
Task.HIDE_UNTIL_SPECIFIC_DAY, specificDate);
values = updated;
}

@ -17,6 +17,7 @@ import android.content.ServiceConnection;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.os.IBinder;
import android.text.Editable;
@ -53,6 +54,8 @@ import com.todoroo.andlib.utility.AndroidUtilities;
import com.todoroo.andlib.utility.DateUtilities;
import com.todoroo.andlib.utility.DialogUtilities;
import com.todoroo.andlib.utility.Pair;
import com.todoroo.andlib.widget.GestureService;
import com.todoroo.andlib.widget.GestureService.GestureInterface;
import com.todoroo.astrid.adapter.TaskAdapter;
import com.todoroo.astrid.adapter.TaskAdapter.ViewHolder;
import com.todoroo.astrid.api.AstridApiConstants;
@ -85,7 +88,7 @@ import com.todoroo.astrid.utility.Flags;
* @author Tim Su <tim@todoroo.com>
*
*/
public class TaskListActivity extends ListActivity implements OnScrollListener {
public class TaskListActivity extends ListActivity implements OnScrollListener, GestureInterface {
// --- activities
@ -156,7 +159,10 @@ public class TaskListActivity extends ListActivity implements OnScrollListener {
super.onCreate(savedInstanceState);
new StartupService().onStartupApplication(this);
setContentView(R.layout.task_list_activity);
if(AndroidUtilities.getSdkVersion() > 3)
setContentView(R.layout.task_list_activity);
else
setContentView(R.layout.task_list_activity_api3);
Bundle extras = getIntent().getExtras();
if(extras != null && extras.containsKey(TOKEN_FILTER)) {
@ -207,9 +213,9 @@ public class TaskListActivity extends ListActivity implements OnScrollListener {
R.string.TLA_menu_settings);
item.setIcon(android.R.drawable.ic_menu_preferences);
/*item = menu.add(Menu.NONE, MENU_HELP_ID, Menu.NONE,
item = menu.add(Menu.NONE, MENU_HELP_ID, Menu.NONE,
R.string.TLA_menu_help);
item.setIcon(android.R.drawable.ic_menu_help);*/
item.setIcon(android.R.drawable.ic_menu_help);
// ask about plug-ins
Intent queryIntent = new Intent(AstridApiConstants.ACTION_TASK_LIST_MENU);
@ -261,7 +267,7 @@ public class TaskListActivity extends ListActivity implements OnScrollListener {
View selected = getListView().getSelectedView();
// hot-key to set task priority - 1-4 or ALT + Q-R
if(!filterOn && event.getNumber() >= '1' && event.getNumber() <= '4' && selected != null) {
if(!filterOn && event.getUnicodeChar() >= '1' && event.getUnicodeChar() <= '4' && selected != null) {
int importance = event.getNumber() - '1';
Task task = ((ViewHolder)selected.getTag()).task;
task.setValue(Task.IMPORTANCE, importance);
@ -336,6 +342,12 @@ public class TaskListActivity extends ListActivity implements OnScrollListener {
}
});
// gestures
try {
GestureService.registerGestureDetector(this, R.id.gestures, R.raw.gestures, this);
} catch (VerifyError e) {
// failed check, no gestures :P
}
}
public void bindServices() {
@ -706,7 +718,9 @@ public class TaskListActivity extends ListActivity implements OnScrollListener {
startActivityForResult(intent, ACTIVITY_SETTINGS);
return true;
case MENU_HELP_ID:
// TODO
intent = new Intent(Intent.ACTION_VIEW,
Uri.parse("http://weloveastrid.com/help-user-guide-astrid-v3/active-tasks/")); //$NON-NLS-1$
startActivity(intent);
return true;
case MENU_ADDON_INTENT_ID:
intent = item.getIntent();
@ -780,4 +794,15 @@ public class TaskListActivity extends ListActivity implements OnScrollListener {
return false;
}
@SuppressWarnings("nls")
@Override
public void gesturePerformed(String gesture) {
if("nav_filters".equals(gesture)) {
Intent intent = new Intent(TaskListActivity.this,
FilterListActivity.class);
startActivity(intent);
finish();
}
}
}

@ -95,7 +95,7 @@ public class FilterAdapter extends BaseExpandableListAdapter {
return convertView;
}
private static class ViewHolder {
public static class ViewHolder {
public FilterListItem item;
public ImageView expander;
public ImageView icon;
@ -132,11 +132,7 @@ public class FilterAdapter extends BaseExpandableListAdapter {
convertView = newView(convertView, parent);
ViewHolder viewHolder = (ViewHolder) convertView.getTag();
Object item = getChild(groupPosition, childPosition);
if(!(item instanceof FilterListItem))
return convertView;
viewHolder.item = (FilterListItem) item;
viewHolder.item = (FilterListItem) getChild(groupPosition, childPosition);
populateView(viewHolder, true, false);
return convertView;
@ -162,11 +158,7 @@ public class FilterAdapter extends BaseExpandableListAdapter {
ViewGroup parent) {
convertView = newView(convertView, parent);
ViewHolder viewHolder = (ViewHolder) convertView.getTag();
Object groupItem = getGroup(groupPosition);
if(!(groupItem instanceof FilterListItem))
return convertView;
viewHolder.item = (FilterListItem) groupItem;
viewHolder.item = (FilterListItem) getGroup(groupPosition);
populateView(viewHolder, false, isExpanded);
return convertView;
}

@ -15,12 +15,12 @@ import android.graphics.Paint;
import android.text.Html;
import android.text.util.Linkify;
import android.view.ContextMenu;
import android.view.ContextMenu.ContextMenuInfo;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.ContextMenu.ContextMenuInfo;
import android.view.View.OnClickListener;
import android.view.View.OnCreateContextMenuListener;
import android.view.ViewGroup;
import android.view.ViewGroup.LayoutParams;
import android.widget.Button;
import android.widget.CheckBox;
@ -39,6 +39,7 @@ import com.todoroo.andlib.service.ExceptionService;
import com.todoroo.andlib.utility.DateUtilities;
import com.todoroo.andlib.utility.SoftHashMap;
import com.todoroo.astrid.activity.TaskEditActivity;
import com.todoroo.astrid.alarms.AlarmDetailExposer;
import com.todoroo.astrid.api.AstridApiConstants;
import com.todoroo.astrid.api.DetailExposer;
import com.todoroo.astrid.api.Filter;
@ -87,6 +88,7 @@ public class TaskAdapter extends CursorAdapter implements Filterable {
new NoteDetailExposer(),
new MilkDetailExposer(),
new ProducteevDetailExposer(),
new AlarmDetailExposer(),
};
private static int[] IMPORTANCE_COLORS = null;
@ -265,6 +267,7 @@ public class TaskAdapter extends CursorAdapter implements Filterable {
if(hiddenUntil > DateUtilities.now())
nameValue = r.getString(R.string.TAd_hiddenFormat, nameValue);
nameView.setText(nameValue);
Linkify.addLinks(nameView, Linkify.ALL);
}
// due date / completion date
@ -281,14 +284,15 @@ public class TaskAdapter extends CursorAdapter implements Filterable {
String dateValue;
Date dueDateAsDate = DateUtilities.unixtimeToDate(dueDate);
if (task.hasDueTime()) {
dateValue = DateUtilities.getDateWithTimeAndWeekday(activity).format(dueDateAsDate);
dateValue = DateUtilities.getDateStringWithTimeAndWeekday(activity, dueDateAsDate);
} else {
dateValue = DateUtilities.getDateFormatWithWeekday(activity).format(dueDateAsDate);
dateValue = DateUtilities.getDateStringWithWeekday(activity, dueDateAsDate);
}
dueDateView.setText(dateValue);
setVisibility(dueDateView);
} else if(task.isCompleted()) {
String dateValue = DateUtilities.getDateFormatWithWeekday(activity).format(task.getValue(Task.COMPLETION_DATE));
String dateValue = DateUtilities.getDateStringWithWeekday(activity,
new Date(task.getValue(Task.COMPLETION_DATE)));
dueDateView.setText(r.getString(R.string.TAd_completed, dateValue));
dueDateView.setTextAppearance(activity, R.style.TextAppearance_TAd_ItemDetails);
setVisibility(dueDateView);
@ -310,7 +314,10 @@ public class TaskAdapter extends CursorAdapter implements Filterable {
// importance bar
final View importanceView = viewHolder.importance; {
int value = task.getValue(Task.IMPORTANCE);
importanceView.setBackgroundColor(IMPORTANCE_COLORS[value]);
if(value < IMPORTANCE_COLORS.length)
importanceView.setBackgroundColor(IMPORTANCE_COLORS[value]);
else
importanceView.setBackgroundColor(0);
}
// details and decorations, expanded
@ -483,10 +490,10 @@ public class TaskAdapter extends CursorAdapter implements Filterable {
viewHolder.decorations[i] = view;
switch(decoration.position) {
case TaskDecoration.POSITION_LEFT:
viewHolder.taskRow.addView(view, 1);
viewHolder.taskRow.addView(view, 2);
break;
case TaskDecoration.POSITION_RIGHT:
viewHolder.taskRow.addView(view, viewHolder.taskRow.getChildCount() - 2);
viewHolder.taskRow.addView(view, viewHolder.taskRow.getChildCount() - 1);
}
}
i++;

@ -63,6 +63,11 @@ public class TaskDao extends GenericDao<Task> {
return Task.DELETION_DATE.neq(0);
}
/** @return tasks that were not deleted */
public static Criterion notDeleted() {
return Task.DELETION_DATE.eq(0);
}
/** @return tasks that have not yet been completed or deleted */
public static Criterion isActive() {
return Criterion.and(Task.COMPLETION_DATE.eq(0),
@ -153,7 +158,7 @@ public class TaskDao extends GenericDao<Task> {
if(saveSuccessful) {
task.markSaved();
afterSave(task, values, skipHooks);
afterSave(task, values);
}
return saveSuccessful;
@ -208,19 +213,15 @@ public class TaskDao extends GenericDao<Task> {
* @param values values to be persisted to the database
* @param skipHooks whether this save occurs as part of a sync
*/
private void afterSave(Task task, ContentValues values, boolean skipHooks) {
private void afterSave(Task task, ContentValues values) {
if(values.containsKey(Task.COMPLETION_DATE.name) && task.isCompleted())
afterComplete(task, values, skipHooks);
else {
afterComplete(task, values);
else
ReminderService.getInstance().scheduleAlarm(task);
}
Astrid2TaskProvider.notifyDatabaseModification();
ContextManager.getContext().startService(new Intent(ContextManager.getContext(),
TasksWidget.UpdateService.class));
if(skipHooks)
return;
}
/**
@ -230,14 +231,12 @@ public class TaskDao extends GenericDao<Task> {
* @param values
* @param duringSync
*/
private void afterComplete(Task task, ContentValues values, boolean duringSync) {
private void afterComplete(Task task, ContentValues values) {
// send broadcast
if(!duringSync) {
Context context = ContextManager.getContext();
Intent broadcastIntent = new Intent(AstridApiConstants.BROADCAST_EVENT_TASK_COMPLETED);
broadcastIntent.putExtra(AstridApiConstants.EXTRAS_TASK_ID, task.getId());
context.sendOrderedBroadcast(broadcastIntent, null);
}
Context context = ContextManager.getContext();
Intent broadcastIntent = new Intent(AstridApiConstants.BROADCAST_EVENT_TASK_COMPLETED);
broadcastIntent.putExtra(AstridApiConstants.EXTRAS_TASK_ID, task.getId());
context.sendOrderedBroadcast(broadcastIntent, null);
}
}

@ -3,7 +3,7 @@
* All Rights Reserved
* http://www.todoroo.com
*/
package com.todoroo.astrid.alarms;
package com.todoroo.astrid.legacy;
import com.todoroo.andlib.data.AbstractDatabase;
import com.todoroo.andlib.data.GenericDao;
@ -15,7 +15,7 @@ import com.todoroo.andlib.data.Table;
* @author Tim Su <tim@todoroo.com>
*
*/
@SuppressWarnings("nls")
@SuppressWarnings({"nls","deprecation"})
public class AlarmDatabase extends AbstractDatabase {
// --- constants
@ -29,19 +29,19 @@ public class AlarmDatabase extends AbstractDatabase {
/**
* Database name (must be unique)
*/
private static final String NAME = "alarms";
public static final String NAME = "alarms";
/**
* List of table/ If you're adding a new table, add it to this list and
* also make sure that our SQLite helper does the right thing.
*/
public static final Table[] TABLES = new Table[] {
Alarm.TABLE
TransitionalAlarm.TABLE
};
// --- implementation
private final GenericDao<Alarm> dao = new GenericDao<Alarm>(Alarm.class, this);
private final GenericDao<TransitionalAlarm> dao = new GenericDao<TransitionalAlarm>(TransitionalAlarm.class, this);
@Override
protected String getName() {
@ -58,7 +58,7 @@ public class AlarmDatabase extends AbstractDatabase {
return TABLES;
}
public GenericDao<Alarm> getDao() {
public GenericDao<TransitionalAlarm> getDao() {
return dao;
}
@ -66,15 +66,8 @@ public class AlarmDatabase extends AbstractDatabase {
protected synchronized void onCreateTables() {
StringBuilder sql = new StringBuilder();
sql.append("CREATE INDEX IF NOT EXISTS a_task ON ").
append(Alarm.TABLE).append('(').
append(Alarm.TASK.name).
append(')');
database.execSQL(sql.toString());
sql.setLength(0);
sql.append("CREATE INDEX IF NOT EXISTS a_type ON ").
append(Alarm.TABLE).append('(').
append(Alarm.TYPE.name).
append(TransitionalAlarm.TABLE).append('(').
append(TransitionalAlarm.TASK.name).
append(')');
database.execSQL(sql.toString());
}

@ -0,0 +1,102 @@
/**
* See the file "LICENSE" for the full license governing this code.
*/
package com.todoroo.astrid.legacy;
import android.content.ContentValues;
import com.todoroo.andlib.data.AbstractModel;
import com.todoroo.andlib.data.Property;
import com.todoroo.andlib.data.Property.LongProperty;
import com.todoroo.andlib.data.Table;
import com.todoroo.andlib.data.TodorooCursor;
import com.todoroo.astrid.model.Task;
/**
* Data Model which represents an alarm. This is a transitional class -
* Alarms are moved over to metadata
*
* @author Tim Su <tim@todoroo.com>
*
*/
@SuppressWarnings("nls")
@Deprecated
public class TransitionalAlarm extends AbstractModel {
// --- table
public static final Table TABLE = new Table("alarm", TransitionalAlarm.class);
// --- properties
/** ID */
public static final LongProperty ID = new LongProperty(
TABLE, ID_PROPERTY_NAME);
/** Associated Task */
public static final LongProperty TASK = new LongProperty(
TABLE, "task");
/** Alarm Time */
public static final LongProperty TIME = new LongProperty(
TABLE, "time");
/** List of all properties for this model */
public static final Property<?>[] PROPERTIES = generateProperties(TransitionalAlarm.class);
// --- constants
/** this alarm was already triggered */
public static final int TYPE_TRIGGERED = 0;
/** this alarm is single-shot */
public static final int TYPE_SINGLE = 1;
/** this alarm repeats itself until turned off */
public static final int TYPE_REPEATING = 2;
// --- defaults
/** Default values container */
private static final ContentValues defaultValues = new ContentValues();
static {
//
}
@Override
public ContentValues getDefaultValues() {
return defaultValues;
}
// --- data access boilerplate
public TransitionalAlarm() {
super();
}
public TransitionalAlarm(TodorooCursor<TransitionalAlarm> cursor) {
this();
readPropertiesFromCursor(cursor);
}
public void readFromCursor(TodorooCursor<TransitionalAlarm> cursor) {
super.readPropertiesFromCursor(cursor);
}
@Override
public long getId() {
return getIdHelper(ID);
};
// --- parcelable helpers
private static final Creator<Task> CREATOR = new ModelCreator<Task>(Task.class);
@Override
protected Creator<? extends AbstractModel> getCreator() {
return CREATOR;
}
}

@ -128,7 +128,8 @@ public class Astrid2TaskProvider extends ContentProvider {
*/
public Cursor getTags() {
Tag[] tags = TagService.getInstance().getGroupedTags(TagService.GROUPED_TAGS_BY_SIZE);
Tag[] tags = TagService.getInstance().getGroupedTags(TagService.GROUPED_TAGS_BY_SIZE,
Criterion.all);
MatrixCursor ret = new MatrixCursor(TAGS_FIELD_LIST);

@ -2,18 +2,16 @@ package com.todoroo.astrid.service;
import java.util.Date;
import java.util.HashMap;
import java.util.StringTokenizer;
import java.util.Map.Entry;
import java.util.StringTokenizer;
import android.app.Activity;
import android.app.ProgressDialog;
import android.content.Context;
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.text.TextUtils;
import android.util.Log;
@ -24,28 +22,30 @@ import com.todoroo.andlib.data.AbstractModel;
import com.todoroo.andlib.data.GenericDao;
import com.todoroo.andlib.data.Property;
import com.todoroo.andlib.data.Property.PropertyVisitor;
import com.todoroo.andlib.data.TodorooCursor;
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.Query;
import com.todoroo.andlib.utility.DateUtilities;
import com.todoroo.andlib.utility.DialogUtilities;
import com.todoroo.astrid.activity.TaskListActivity;
import com.todoroo.astrid.alarms.Alarm;
import com.todoroo.astrid.alarms.AlarmDatabase;
import com.todoroo.astrid.backup.TasksXmlImporter;
import com.todoroo.astrid.dao.Database;
import com.todoroo.astrid.dao.MetadataDao;
import com.todoroo.astrid.dao.TaskDao;
import com.todoroo.astrid.legacy.AlarmDatabase;
import com.todoroo.astrid.legacy.LegacyAlertModel;
import com.todoroo.astrid.legacy.LegacyRepeatInfo;
import com.todoroo.astrid.legacy.LegacyTaskModel;
import com.todoroo.astrid.legacy.TransitionalAlarm;
import com.todoroo.astrid.model.Metadata;
import com.todoroo.astrid.model.Task;
import com.todoroo.astrid.rmilk.data.MilkTask;
import com.todoroo.astrid.tags.TagService;
import com.todoroo.astrid.utility.Preferences;
@SuppressWarnings("deprecation")
public class Astrid2To3UpgradeHelper {
@Autowired
@ -75,9 +75,6 @@ public class Astrid2To3UpgradeHelper {
@Autowired
private String syncTable;
@Autowired
private DialogUtilities dialogUtilities;
@Autowired
private ExceptionService exceptionService;
@ -110,11 +107,29 @@ public class Astrid2To3UpgradeHelper {
}
// ---------------------------------------------------------------- 3 => 3.1
/**
* Perform the upgrade from Astrid 3 to 3.1
* @param context
* @param upgradeService
* @param from
*/
public void upgrade3To3_1(final Context context, final int from) {
if(!checkIfDatabaseExists(context, alertsTable))
return;
database.openForWriting();
migrateAlarmsToMetadata();
}
// ----------------------------------------------------------------- 2 => 3
/**
* Perform the upgrade from Astrid 2 to Astrid 3
* @param context2
*/
public void upgrade2To3(final Context context, final UpgradeService upgradeService, final int from) {
public void upgrade2To3(final Context context, final int from) {
// if from < 1 (we don't know what version, and database exists, leave it alone)
if(from < 1 && checkIfDatabaseExists(context, database.getName()))
@ -129,98 +144,72 @@ public class Astrid2To3UpgradeHelper {
context.deleteDatabase(database.getName());
database.openForWriting();
// pop up a progress dialog
final ProgressDialog dialog;
if(context instanceof Activity)
dialog = dialogUtilities.progressDialog(context,
context.getString(R.string.DLG_upgrading));
else
dialog = null;
new Thread(new Runnable() {
@Override
public void run() {
// initiate a backup
String backupFile = legacyBackup();
try {
// --- upgrade tasks table
HashMap<String, Property<?>> propertyMap =
new HashMap<String, Property<?>>();
propertyMap.put("_id", Task.ID); //$NON-NLS-1$
propertyMap.put(LegacyTaskModel.NAME, Task.TITLE);
propertyMap.put(LegacyTaskModel.NOTES, Task.NOTES);
// (don't update progress percentage, we don't use this anymore)
propertyMap.put(LegacyTaskModel.IMPORTANCE, Task.IMPORTANCE);
propertyMap.put(LegacyTaskModel.ESTIMATED_SECONDS, Task.ESTIMATED_SECONDS);
propertyMap.put(LegacyTaskModel.ELAPSED_SECONDS, Task.ELAPSED_SECONDS);
propertyMap.put(LegacyTaskModel.TIMER_START, Task.TIMER_START);
propertyMap.put(LegacyTaskModel.DEFINITE_DUE_DATE, Task.DUE_DATE);
propertyMap.put(LegacyTaskModel.HIDDEN_UNTIL, Task.HIDE_UNTIL);
propertyMap.put(LegacyTaskModel.POSTPONE_COUNT, Task.POSTPONE_COUNT);
propertyMap.put(LegacyTaskModel.NOTIFICATIONS, Task.REMINDER_PERIOD);
propertyMap.put(LegacyTaskModel.NOTIFICATION_FLAGS, Task.REMINDER_FLAGS);
propertyMap.put(LegacyTaskModel.LAST_NOTIFIED, Task.REMINDER_LAST);
propertyMap.put(LegacyTaskModel.REPEAT, Task.RECURRENCE);
propertyMap.put(LegacyTaskModel.CREATION_DATE, Task.CREATION_DATE);
propertyMap.put(LegacyTaskModel.COMPLETION_DATE, Task.COMPLETION_DATE);
propertyMap.put(LegacyTaskModel.CALENDAR_URI, Task.CALENDAR_URI);
propertyMap.put(LegacyTaskModel.FLAGS, Task.FLAGS);
upgradeTable(context, tasksTable,
propertyMap, new Task(), taskDao);
// --- upgrade tags tables
migrateTagsToMetadata();
// --- upgrade alerts
AlarmDatabase alarmsDatabase = new AlarmDatabase();
alarmsDatabase.openForWriting();
propertyMap.clear();
propertyMap.put("_id", Alarm.ID); //$NON-NLS-1$
propertyMap.put(LegacyAlertModel.TASK, Alarm.TASK);
propertyMap.put(LegacyAlertModel.DATE, Alarm.TIME);
upgradeTable(context, alertsTable, propertyMap, new Alarm(),
alarmsDatabase.getDao());
alarmsDatabase.close();
// --- upgrade RTM sync mappings
migrateSyncMappingToMetadata();
// --- clean up database
metadataService.cleanup();
// --- upgrade properties
SharedPreferences prefs = Preferences.getPrefs(context);
Editor editor = prefs.edit();
int random = Preferences.getIntegerFromString(R.string.p_rmd_default_random_hours, -1);
if(random != -1) {
// convert days => hours
editor.putString(context.getString(R.string.p_rmd_default_random_hours),
Integer.toString(random * 24));
}
} catch (Exception e) {
exceptionService.reportError("backup-error", e); //$NON-NLS-1$
if(backupFile != null) {
// try to restore the latest XML
TasksXmlImporter.importTasks(context, backupFile, null);
}
} finally {
if(context instanceof Activity) {
((Activity)context).runOnUiThread(new Runnable() {
public void run() {
if(dialog != null)
dialog.dismiss();
upgradeService.showChangeLog(context, from);
if(context instanceof TaskListActivity)
((TaskListActivity)context).loadTaskListContent(true);
}
});
}
}
}
}).start();
// initiate a backup
String backupFile = legacyBackup();
try {
// --- upgrade tasks table
HashMap<String, Property<?>> propertyMap =
new HashMap<String, Property<?>>();
propertyMap.put("_id", Task.ID); //$NON-NLS-1$
propertyMap.put(LegacyTaskModel.NAME, Task.TITLE);
propertyMap.put(LegacyTaskModel.NOTES, Task.NOTES);
// (don't update progress percentage, we don't use this anymore)
propertyMap.put(LegacyTaskModel.IMPORTANCE, Task.IMPORTANCE);
propertyMap.put(LegacyTaskModel.ESTIMATED_SECONDS, Task.ESTIMATED_SECONDS);
propertyMap.put(LegacyTaskModel.ELAPSED_SECONDS, Task.ELAPSED_SECONDS);
propertyMap.put(LegacyTaskModel.TIMER_START, Task.TIMER_START);
propertyMap.put(LegacyTaskModel.DEFINITE_DUE_DATE, Task.DUE_DATE);
propertyMap.put(LegacyTaskModel.HIDDEN_UNTIL, Task.HIDE_UNTIL);
propertyMap.put(LegacyTaskModel.POSTPONE_COUNT, Task.POSTPONE_COUNT);
propertyMap.put(LegacyTaskModel.NOTIFICATIONS, Task.REMINDER_PERIOD);
propertyMap.put(LegacyTaskModel.NOTIFICATION_FLAGS, Task.REMINDER_FLAGS);
propertyMap.put(LegacyTaskModel.LAST_NOTIFIED, Task.REMINDER_LAST);
propertyMap.put(LegacyTaskModel.REPEAT, Task.RECURRENCE);
propertyMap.put(LegacyTaskModel.CREATION_DATE, Task.CREATION_DATE);
propertyMap.put(LegacyTaskModel.COMPLETION_DATE, Task.COMPLETION_DATE);
propertyMap.put(LegacyTaskModel.CALENDAR_URI, Task.CALENDAR_URI);
propertyMap.put(LegacyTaskModel.FLAGS, Task.FLAGS);
upgradeTable(context, tasksTable,
propertyMap, new Task(), taskDao);
// --- upgrade tags tables
migrateTagsToMetadata();
// --- upgrade alerts
AlarmDatabase alarmsDatabase = new AlarmDatabase();
alarmsDatabase.openForWriting();
propertyMap.clear();
propertyMap.put("_id", TransitionalAlarm.ID); //$NON-NLS-1$
propertyMap.put(LegacyAlertModel.TASK, TransitionalAlarm.TASK);
propertyMap.put(LegacyAlertModel.DATE, TransitionalAlarm.TIME);
upgradeTable(context, alertsTable, propertyMap, new TransitionalAlarm(),
alarmsDatabase.getDao());
alarmsDatabase.close();
// --- upgrade RTM sync mappings
migrateSyncMappingToMetadata();
// --- clean up database
metadataService.cleanup();
// --- upgrade properties
SharedPreferences prefs = Preferences.getPrefs(context);
Editor editor = prefs.edit();
int random = Preferences.getIntegerFromString(R.string.p_rmd_default_random_hours, -1);
if(random != -1) {
// convert days => hours
editor.putString(context.getString(R.string.p_rmd_default_random_hours),
Integer.toString(random * 24));
}
} catch (Exception e) {
exceptionService.reportError("backup-error", e); //$NON-NLS-1$
if(backupFile != null) {
// try to restore the latest XML
TasksXmlImporter.importTasks(context, backupFile, null);
}
}
}
// --- database upgrade helpers
@ -284,7 +273,7 @@ public class Astrid2To3UpgradeHelper {
if(data.upgradeNotes == null)
data.upgradeNotes = new StringBuilder();
data.upgradeNotes.append("Goal Deadline: " +
DateUtilities.getFormattedDate(ContextManager.getContext(),
DateUtilities.getDateString(ContextManager.getContext(),
new Date(preferredDueDate)));
}
} else if(property == Task.REMINDER_PERIOD) {
@ -484,4 +473,41 @@ public class Astrid2To3UpgradeHelper {
}
}
/**
* Move data from alert table into metadata table.
*/
private void migrateAlarmsToMetadata() {
Context context = ContextManager.getContext();
if(!checkIfDatabaseExists(context, AlarmDatabase.NAME))
return;
AlarmDatabase alarmsDatabase = new AlarmDatabase();
GenericDao<TransitionalAlarm> dao = new GenericDao<TransitionalAlarm>(
TransitionalAlarm.class, alarmsDatabase);
TodorooCursor<TransitionalAlarm> cursor = dao.query(Query.select(TransitionalAlarm.PROPERTIES));
try {
if(cursor.getCount() == 0)
return;
Metadata metadata = new Metadata();
metadata.setValue(Metadata.KEY, Alarm.METADATA_KEY);
for(cursor.moveToFirst(); !cursor.isAfterLast(); cursor.moveToNext()) {
long task = cursor.get(TransitionalAlarm.TASK);
long time = cursor.get(TransitionalAlarm.TIME);
metadata.setValue(Metadata.TASK, task);
metadata.setValue(Alarm.TIME, time);
metadata.setValue(Alarm.TYPE, Alarm.TYPE_SINGLE);
metadataDao.createNew(metadata);
metadata.clearValue(Metadata.ID);
}
} finally {
cursor.close();
alarmsDatabase.close();
}
}
}

@ -2,14 +2,26 @@ package com.todoroo.astrid.service;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.ProgressDialog;
import android.content.Context;
import android.webkit.WebView;
import com.timsu.astrid.R;
import com.todoroo.andlib.service.Autowired;
import com.todoroo.andlib.service.DependencyInjectionService;
import com.todoroo.andlib.utility.DialogUtilities;
import com.todoroo.astrid.activity.TaskListActivity;
public final class UpgradeService {
@Autowired
private DialogUtilities dialogUtilities;
public UpgradeService() {
DependencyInjectionService.getInstance().inject(this);
}
/**
* Perform upgrade from one version to the next. Needs to be called
* on the UI thread so it can display a progress bar and then
@ -18,16 +30,45 @@ public final class UpgradeService {
* @param from
* @param to
*/
public void performUpgrade(Context context, int from) {
public void performUpgrade(final Context context, final int from) {
if(from == 135)
AddOnService.recordOem();
if(from < 136) {
new Astrid2To3UpgradeHelper().upgrade2To3(context, this, from);
} else {
// display changelog
showChangeLog(context, from);
}
// pop up a progress dialog
final ProgressDialog dialog;
if(context instanceof Activity)
dialog = dialogUtilities.progressDialog(context,
context.getString(R.string.DLG_upgrading));
else
dialog = null;
new Thread(new Runnable() {
@Override
public void run() {
try {
if(from < 136)
new Astrid2To3UpgradeHelper().upgrade2To3(context, from);
if(from < 146)
new Astrid2To3UpgradeHelper().upgrade3To3_1(context, from);
} finally {
if(context instanceof Activity) {
((Activity)context).runOnUiThread(new Runnable() {
public void run() {
if(dialog != null)
dialog.dismiss();
// display changelog
showChangeLog(context, from);
if(context instanceof TaskListActivity)
((TaskListActivity)context).loadTaskListContent(true);
}
});
}
}
}
}).start();
}
/**
@ -46,15 +87,26 @@ public final class UpgradeService {
StringBuilder changeLog = new StringBuilder();
if(from <= 135)
newVersionString(changeLog, "3.0.6 (8/4/10)", new String[] {
newVersionString(changeLog, "3.1.0 (8/9/10)", new String[] {
"Astrid is brand new inside and out! In addition to a new " +
"look and feel, a new add-on system allows Astrid to become " +
"more powerful, while other improvements have made it faster " +
"and easier to use. Hope you like it!",
"This update contains for free all of the " +
"powerpack's features for evaluation purposes",
"look and feel, a new add-on system allows Astrid to become " +
"more powerful, while other improvements have made it faster " +
"and easier to use. Hope you like it!",
"This update contains for free all of Astrid " +
"Power Pack's features for evaluation purposes",
"If you liked the old version, you can also go back by " +
"<a href='http://bit.ly/oldastrid'>clicking here</a>",
"<a href='http://bit.ly/oldastrid'>clicking here</a>",
});
if(from > 135 && from <= 145)
newVersionString(changeLog, "3.1.0 (8/9/10)", new String[] {
"Linkify phone numbers, e-mails, and web pages",
"Swipe L => R to go from tasks to filters",
"Moved task priority bar to left side",
"Added ability to create fixed alerts for a task",
"Restored tag hiding when tag begins with underscore (_)",
"FROYO: disabled moving app to SD card, it would break alarms and widget",
"Also gone: a couple force closes, bugs with repeating tasks",
"... enjoy! - we ♥ astrid team",
});
if(from > 135 && from <= 144)
newVersionString(changeLog, "3.0.6 (8/4/10)", new String[] {
@ -63,11 +115,6 @@ public final class UpgradeService {
"Fixed widget not updating when tasks are edited",
"Added a setting for displaying task notes in the list",
});
if(from > 135 && from <= 145)
newVersionString(changeLog, "3.0.7 (8/5/10)", new String[] {
"Linkify phone numbers, e-mails, and web pages",
"Made the 'Astrid Notifications' setting in the menu work",
});
if(changeLog.length() == 0)
return;

@ -29,9 +29,9 @@ import android.content.DialogInterface.OnClickListener;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.FrameLayout.LayoutParams;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.FrameLayout.LayoutParams;
import com.timsu.astrid.R;
@ -102,6 +102,10 @@ public class NNumberPickerDialog extends AlertDialog implements OnClickListener
}
}
public NumberPicker getPicker(int index) {
return pickers.get(index);
}
public void setInitialValues(int[] values) {
for(int i = 0; i < pickers.size(); i++)
pickers.get(i).setCurrent(values[i]);

@ -44,7 +44,8 @@ public class NumberPicker extends LinearLayout implements OnClickListener,
OnFocusChangeListener, OnLongClickListener {
public interface OnChangedListener {
void onChanged(NumberPicker picker, int oldVal, int newVal);
/** return new value */
int onChanged(NumberPicker picker, int oldVal, int newVal);
}
public interface Formatter {
@ -105,7 +106,7 @@ public class NumberPicker extends LinearLayout implements OnClickListener,
private int mPrevious;
private OnChangedListener mListener;
private Formatter mFormatter;
private long mSpeed = 500;
private long mSpeed = 300;
private boolean mIncrement;
private boolean mDecrement;
@ -178,6 +179,7 @@ public class NumberPicker extends LinearLayout implements OnClickListener,
public void setFormatter(Formatter formatter) {
mFormatter = formatter;
updateView();
}
/**
@ -253,6 +255,7 @@ public class NumberPicker extends LinearLayout implements OnClickListener,
}
private void changeCurrent(int current, Animation in, Animation out) {
current = notifyChange(current);
// Wrap around the values if we go past the start or end
if (current > mEnd) {
@ -262,14 +265,15 @@ public class NumberPicker extends LinearLayout implements OnClickListener,
}
mPrevious = mCurrent;
mCurrent = current;
notifyChange();
updateView();
}
private void notifyChange() {
private int notifyChange(int current) {
if (mListener != null) {
mListener.onChanged(this, mPrevious, mCurrent);
}
return mListener.onChanged(this, mCurrent, current);
} else
return current;
}
private void updateView() {
@ -291,7 +295,7 @@ public class NumberPicker extends LinearLayout implements OnClickListener,
if ((val >= mStart) && (val <= mEnd)) {
mPrevious = mCurrent;
mCurrent = val;
notifyChange();
notifyChange(mCurrent);
}
updateView();
}

@ -37,47 +37,49 @@ public class TimeDurationControlSet implements OnNNumberPickedListener,
@Autowired
DateUtilities dateUtilities;
public enum TimeDurationType {
DAYS_HOURS,
HOURS_MINUTES;
}
private final Activity activity;
private final Button timeButton;
private final NNumberPickerDialog dialog;
private final int prefixResource;
private final TimeDurationType type;
private int timeDuration;
public TimeDurationControlSet(Activity activity, int timeButtonId,
int prefixResource, int titleResource, TimeDurationType type) {
int prefixResource, int titleResource) {
DependencyInjectionService.getInstance().inject(this);
this.activity = activity;
this.prefixResource = prefixResource;
this.type = type;
timeButton = (Button)activity.findViewById(timeButtonId);
timeButton.setOnClickListener(this);
switch(type) {
case DAYS_HOURS:
dialog = new NNumberPickerDialog(activity, this,
activity.getResources().getString(titleResource),
new int[] {0, 0}, new int[] {1, 1}, new int[] {0, 0},
new int[] {31, 23}, new String[] {
"d\na\ny\ns",
"h\nr\ns"
});
break;
case HOURS_MINUTES:
default:
dialog = new NNumberPickerDialog(activity, this,
activity.getResources().getString(titleResource),
new int[] {0, 0}, new int[] {1, 5}, new int[] {0, 0},
new int[] {99, 59}, new String[] {":", null});
break;
}
dialog = new NNumberPickerDialog(activity, this,
activity.getResources().getString(titleResource),
new int[] {0, 0}, new int[] {1, 5}, new int[] {0, 0},
new int[] {99, 59}, new String[] {":", null});
final NumberPicker hourPicker = dialog.getPicker(0);
final NumberPicker minutePicker = dialog.getPicker(1);
minutePicker.setFormatter(new NumberPicker.Formatter() {
@Override
public String toString(int value) {
return String.format("%02d", value);
}
});
minutePicker.setOnChangeListener(new NumberPicker.OnChangedListener() {
@Override
public int onChanged(NumberPicker picker, int oldVal, int newVal) {
if(newVal < 0) {
if(hourPicker.getCurrent() == 0)
return 0;
hourPicker.setCurrent(hourPicker.getCurrent() - 1);
return 60 + newVal;
} else if(newVal > 59) {
hourPicker.setCurrent(hourPicker.getCurrent() + 1);
return newVal % 60;
}
return newVal;
}
});
}
public int getTimeDurationInSeconds() {
@ -97,33 +99,20 @@ public class TimeDurationControlSet implements OnNNumberPickedListener,
}
String prefix = "";
if(prefixResource != 0)
if (prefixResource != 0)
prefix = r.getString(prefixResource);
timeButton.setText(prefix + " " + dateUtilities.getDurationString(
timeDurationInSeconds * 1000L, 2));
switch(type) {
case DAYS_HOURS:
int days = timeDuration / 24 / 3600;
int hours = timeDuration / 3600 - 24 * days;
dialog.setInitialValues(new int[] {days, hours});
break;
case HOURS_MINUTES:
hours = timeDuration / 3600;
int minutes = timeDuration/60 - 60 * hours;
dialog.setInitialValues(new int[] {hours, minutes});
}
timeButton.setText(prefix
+ " "
+ dateUtilities.getDurationString(
timeDurationInSeconds * 1000L, 2));
int hours = timeDuration / 3600;
int minutes = timeDuration / 60 - 60 * hours;
dialog.setInitialValues(new int[] { hours, minutes });
}
/** Called when NumberPicker activity is completed */
public void onNumbersPicked(int[] values) {
switch(type) {
case DAYS_HOURS:
setTimeDuration(values[0] * 24 * 3600 + values[1] * 3600);
break;
case HOURS_MINUTES:
setTimeDuration(values[0] * 3600 + values[1] * 60);
break;
}
setTimeDuration(values[0] * 3600 + values[1] * 60);
}
/** Called when time button is clicked */

@ -82,6 +82,7 @@ public class Preferences {
/** Get preferences object from the context */
public static SharedPreferences getPrefs(Context context) {
context = context.getApplicationContext();
return PreferenceManager.getDefaultSharedPreferences(context);
}

@ -103,7 +103,7 @@ public class TasksWidget extends AppWidgetProvider {
database.openForReading();
cursor = taskService.fetchFiltered(inboxFilter, null, Task.TITLE, Task.DUE_DATE);
Task task = new Task();
for (int i = 0; i < cursor.getCount(); i++) {
for (int i = 0; i < cursor.getCount() && i < numberOfTasks; i++) {
cursor.moveToPosition(i);
task.readFromCursor(cursor);
@ -115,7 +115,7 @@ public class TasksWidget extends AppWidgetProvider {
textColor = context.getResources().getColor(R.color.task_list_overdue);
if(i > 0)
views.setViewVisibility(separatorIDs[i-1], View.VISIBLE);
views.setViewVisibility(separatorIDs[i-1], View.VISIBLE);
views.setTextViewText(textIDs[i], textContent);
views.setTextColor(textIDs[i], textColor);
}
@ -123,6 +123,8 @@ public class TasksWidget extends AppWidgetProvider {
for(int i = cursor.getCount() - 1; i < separatorIDs.length; i++) {
if(i >= 0)
views.setViewVisibility(separatorIDs[i], View.INVISIBLE);
if(i > cursor.getCount() - 1)
views.setViewVisibility(textIDs[i], View.INVISIBLE);
}
} catch (Exception e) {
// can happen if database is not ready

@ -11,6 +11,7 @@ import com.todoroo.astrid.utility.Preferences;
public class ReminderServiceTests extends DatabaseTestCase {
ReminderService service;
AlarmScheduler original;
@Autowired
TaskDao taskDao;
@ -19,9 +20,16 @@ public class ReminderServiceTests extends DatabaseTestCase {
protected void setUp() throws Exception {
super.setUp();
service = ReminderService.getInstance();
original = service.getScheduler();
Preferences.setPreferenceDefaults();
}
@Override
protected void tearDown() throws Exception {
super.tearDown();
service.setScheduler(original);
}
/** tests with no alarms */
public void testNoReminders() {
service.setScheduler(new NoAlarmExpected());

@ -0,0 +1,134 @@
package com.todoroo.astrid.repeats;
import java.text.ParseException;
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
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.todoroo.andlib.test.TodorooTestCase;
import com.todoroo.andlib.utility.DateUtilities;
import com.todoroo.astrid.model.Task;
public class AdvancedRepeatTests extends TodorooTestCase {
public static void assertDatesEqual(long date, long other) {
assertEquals("Expected: " + new Date(date) + ", Actual: " + new Date(other),
date, other);
}
public void testDailyWithDaysOfWeek() throws ParseException {
RRule rrule = new RRule();
rrule.setInterval(1);
rrule.setFreq(Frequency.DAILY);
rrule.setByDay(Collections.singletonList(new WeekdayNum(0, Weekday.FR)));
Task task = new Task();
long thursday = task.createDueDate(Task.URGENCY_SPECIFIC_DAY, new Date(113, 7, 1).getTime());
task.setValue(Task.DUE_DATE, thursday);
// repeat once => due date should become friday
long friday = thursday + DateUtilities.ONE_DAY;
long nextDueDate = RepeatTaskCompleteListener.computeNextDueDate(task, rrule.toIcal());
assertDatesEqual(friday, nextDueDate);
// repeat again => due date should be one week from friday
long nextFriday = friday + DateUtilities.ONE_WEEK;
task.setValue(Task.DUE_DATE, friday);
nextDueDate = RepeatTaskCompleteListener.computeNextDueDate(task, rrule.toIcal());
assertDatesEqual(nextFriday, nextDueDate);
// now try with thursday, and repeat every 2 days. expect next friday
rrule.setInterval(2);
task.setValue(Task.DUE_DATE, thursday);
nextDueDate = RepeatTaskCompleteListener.computeNextDueDate(task, rrule.toIcal());
assertDatesEqual(nextFriday, nextDueDate);
// again with friday, expect next friday
task.setValue(Task.DUE_DATE, friday);
nextDueDate = RepeatTaskCompleteListener.computeNextDueDate(task, rrule.toIcal());
assertDatesEqual(nextFriday, nextDueDate);
}
public void testMonthlyWithDaysOfWeek() throws ParseException {
RRule rrule = new RRule();
rrule.setInterval(1);
rrule.setFreq(Frequency.MONTHLY);
rrule.setByDay(Arrays.asList(new WeekdayNum[] {
new WeekdayNum(0, Weekday.SU),
new WeekdayNum(0, Weekday.MO),
new WeekdayNum(0, Weekday.TU),
new WeekdayNum(0, Weekday.WE),
new WeekdayNum(0, Weekday.TH),
new WeekdayNum(0, Weekday.FR),
new WeekdayNum(0, Weekday.SA),
}));
Task task = new Task();
long thursday = task.createDueDate(Task.URGENCY_SPECIFIC_DAY, new Date(113, 7, 1).getTime());
task.setValue(Task.DUE_DATE, thursday);
// repeat once => due date should become next month on the first
long nextMonth = task.createDueDate(Task.URGENCY_SPECIFIC_DAY, new Date(113, 8, 1).getTime());
long nextDueDate = RepeatTaskCompleteListener.computeNextDueDate(task, rrule.toIcal());
assertDatesEqual(nextMonth, nextDueDate);
// only allow thursdays
rrule.setByDay(Arrays.asList(new WeekdayNum[] {
new WeekdayNum(0, Weekday.TH),
}));
long nextMonthOnThursday = task.createDueDate(Task.URGENCY_SPECIFIC_DAY, new Date(113, 8, 5).getTime());
nextDueDate = RepeatTaskCompleteListener.computeNextDueDate(task, rrule.toIcal());
assertDatesEqual(nextMonthOnThursday, nextDueDate);
}
public void testDueDateInPast() throws ParseException {
RRule rrule = new RRule();
rrule.setInterval(1);
rrule.setFreq(Frequency.DAILY);
Task task = new Task();
// repeat once => due date should become tomorrow
long past = task.createDueDate(Task.URGENCY_SPECIFIC_DAY, new Date(110, 7, 1).getTime());
task.setValue(Task.DUE_DATE, past);
long today = task.createDueDate(Task.URGENCY_SPECIFIC_DAY, DateUtilities.now());
long nextDueDate = RepeatTaskCompleteListener.computeNextDueDate(task, rrule.toIcal());
assertDatesEqual(today, nextDueDate);
// test specific day & time
long pastWithTime = task.createDueDate(Task.URGENCY_SPECIFIC_DAY_TIME, new Date(110, 7, 1, 10, 4).getTime());
task.setValue(Task.DUE_DATE, pastWithTime);
Date date = new Date(DateUtilities.now() / 1000L * 1000L);
date.setHours(10);
date.setMinutes(4);
date.setSeconds(0);
long todayWithTime = task.createDueDate(Task.URGENCY_SPECIFIC_DAY_TIME, date.getTime()) / 1000L * 1000L;
if(todayWithTime < DateUtilities.now())
todayWithTime += DateUtilities.ONE_DAY;
nextDueDate = RepeatTaskCompleteListener.computeNextDueDate(task, rrule.toIcal());
assertDatesEqual(todayWithTime, nextDueDate);
}
public void testDueDateInPastRepeatMultiple() throws ParseException {
RRule rrule = new RRule();
rrule.setInterval(1);
rrule.setFreq(Frequency.DAILY);
Task task = new Task();
// repeat once => due date should become tomorrow
long past = task.createDueDate(Task.URGENCY_SPECIFIC_DAY_TIME, new Date(110, 7, 1, 0, 0, 0).getTime());
task.setValue(Task.DUE_DATE, past);
long nextDueDate = RepeatTaskCompleteListener.computeNextDueDate(task, rrule.toIcal());
assertTrue(nextDueDate > DateUtilities.now());
task.setValue(Task.DUE_DATE, nextDueDate);
long evenMoreNextDueDate = RepeatTaskCompleteListener.computeNextDueDate(task, rrule.toIcal());
assertNotSame(nextDueDate, evenMoreNextDueDate);
}
}

@ -176,6 +176,8 @@ public class RepeatTests extends DatabaseTestCase {
/** test after completion flag */
public void testRepeatAfterComplete() throws Exception {
// create a weekly task due a couple days in the past, but with the 'after completion'
// specified. should be due 7 days from now
Task task = new Task();
task.setValue(Task.TITLE, "afterComplete");
RRule rrule = new RRule();

@ -4,8 +4,8 @@ import java.io.File;
import com.todoroo.andlib.service.TestDependencyInjector;
import com.todoroo.andlib.test.TodorooTestCase;
import com.todoroo.astrid.alarms.AlarmDatabase;
import com.todoroo.astrid.dao.Database;
import com.todoroo.astrid.legacy.AlarmDatabase;
import com.todoroo.astrid.service.AstridDependencyInjector;
/**

@ -10,11 +10,12 @@ import com.todoroo.andlib.data.TodorooCursor;
import com.todoroo.andlib.service.Autowired;
import com.todoroo.andlib.service.TestDependencyInjector;
import com.todoroo.andlib.sql.Query;
import com.todoroo.astrid.alarms.Alarm;
import com.todoroo.astrid.alarms.AlarmDatabase;
import com.todoroo.astrid.dao.MetadataDao;
import com.todoroo.astrid.dao.TaskDao;
import com.todoroo.astrid.dao.MetadataDao.MetadataCriteria;
import com.todoroo.astrid.dao.TaskDao.TaskCriteria;
import com.todoroo.astrid.legacy.AlarmDatabase;
import com.todoroo.astrid.legacy.TransitionalAlarm;
import com.todoroo.astrid.legacy.data.alerts.AlertController;
import com.todoroo.astrid.legacy.data.enums.Importance;
import com.todoroo.astrid.legacy.data.enums.RepeatInterval;
@ -31,7 +32,6 @@ import com.todoroo.astrid.model.Metadata;
import com.todoroo.astrid.model.Task;
import com.todoroo.astrid.rmilk.data.MilkTask;
import com.todoroo.astrid.service.Astrid2To3UpgradeHelper;
import com.todoroo.astrid.service.UpgradeService;
import com.todoroo.astrid.tags.TagService;
import com.todoroo.astrid.tags.TagService.Tag;
import com.todoroo.astrid.test.DatabaseTestCase;
@ -89,7 +89,7 @@ public class Astrid2To3UpgradeTests extends DatabaseTestCase {
// --- helper methods
public void upgrade2To3() {
new Astrid2To3UpgradeHelper().upgrade2To3(getContext(), new UpgradeService(), 125);
new Astrid2To3UpgradeHelper().upgrade2To3(getContext(), 125);
}
public static void assertDatesEqual(Date old, long newDate) {
@ -267,7 +267,8 @@ public class Astrid2To3UpgradeTests extends DatabaseTestCase {
// verify that data exists in our new table
database.openForReading();
TagService tagService = TagService.getInstance();
Tag[] tags = tagService.getGroupedTags(TagService.GROUPED_TAGS_BY_ALPHA);
Tag[] tags = tagService.getGroupedTags(TagService.GROUPED_TAGS_BY_ALPHA,
TaskCriteria.isActive());
assertEquals(2, tags.length);
assertEquals("salty", tags[0].tag);
assertEquals("tasty", tags[1].tag);
@ -321,7 +322,8 @@ public class Astrid2To3UpgradeTests extends DatabaseTestCase {
// verify that data exists in our new table
database.openForReading();
TagService tagService = TagService.getInstance();
Tag[] tags = tagService.getGroupedTags(TagService.GROUPED_TAGS_BY_ALPHA);
Tag[] tags = tagService.getGroupedTags(TagService.GROUPED_TAGS_BY_ALPHA,
TaskCriteria.isActive());
assertEquals(1, tags.length);
assertEquals("attached", tags[0].tag);
@ -363,14 +365,14 @@ public class Astrid2To3UpgradeTests extends DatabaseTestCase {
database.openForReading();
alarmsDatabase.openForReading();
TodorooCursor<Alarm> cursor = alarmsDatabase.getDao().query(Query.select(Alarm.TIME));
TodorooCursor<TransitionalAlarm> cursor = alarmsDatabase.getDao().query(Query.select(TransitionalAlarm.TIME));
assertEquals(2, cursor.getCount());
cursor.moveToFirst();
Alarm alarm = new Alarm(cursor);
assertDatesEqual(x1, alarm.getValue(Alarm.TIME));
TransitionalAlarm alarm = new TransitionalAlarm(cursor);
assertDatesEqual(x1, alarm.getValue(TransitionalAlarm.TIME));
cursor.moveToNext();
alarm.readFromCursor(cursor);
assertDatesEqual(x2, alarm.getValue(Alarm.TIME));
assertDatesEqual(x2, alarm.getValue(TransitionalAlarm.TIME));
}
/**

Loading…
Cancel
Save