Bugfixes Galore:

- couldn't dismiss initial RTM welcome box
   - duplicated sync notes
   - space between elapsed time & "ago" in view page
   - performance improvements: caching and not inflating on view list
   - tasks due "today" are due at midnight

  New features:
   - postpone button
   - filters persisted
   - timer icon made vertical
   - added a sync shortcut
   - improved cosmetics of the strikeout
pull/14/head
Tim Su 17 years ago
parent 1ccdab86a6
commit 8a247fce08

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 179 B

@ -25,23 +25,34 @@
android:focusable="true" android:focusable="true"
android:background="@android:drawable/list_selector_background" android:background="@android:drawable/list_selector_background"
android:paddingLeft="6dip" android:paddingLeft="6dip"
android:paddingTop="2px"
android:paddingBottom="2px"
android:layout_width="fill_parent" android:layout_width="fill_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:minHeight="45dip" > android:minHeight="45dip" >
<CheckBox android:id="@+id/cb1" <!-- icons on the left -->
<LinearLayout android:id="@+id/icon_layout"
android:orientation="vertical"
android:minWidth="41px"
android:gravity="center_vertical" android:gravity="center_vertical"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="fill_parent" android:layout_height="fill_parent">
android:scaleType="center"
android:minWidth="41px" />
<ImageView android:id="@+id/imageLeft" <CheckBox android:id="@+id/cb1"
android:gravity="center_vertical" android:paddingBottom="5px"
android:layout_width="wrap_content" android:layout_gravity="center_vertical"
android:layout_height="fill_parent" android:layout_width="wrap_content"
android:scaleType="center" android:layout_height="wrap_content"
android:paddingLeft="5dip"/> android:scaleType="center" />
<ImageView android:id="@+id/imageLeft"
android:layout_gravity="center_vertical"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:scaleType="center"/>
</LinearLayout>
<!-- all text --> <!-- all text -->
<LinearLayout android:id="@+id/text_layout" <LinearLayout android:id="@+id/text_layout"
@ -105,7 +116,7 @@
</LinearLayout> </LinearLayout>
<!-- importance --> <!-- importance -->
<ImageView android:id="@+id/importance" <View android:id="@+id/importance"
android:layout_width="12px" android:layout_width="12px"
android:layout_height="fill_parent" android:layout_height="fill_parent"
android:paddingLeft="6dip"/> android:paddingLeft="6dip"/>

@ -60,12 +60,12 @@
<item quantity="one">1 Day</item> <item quantity="one">1 Day</item>
<item quantity="other">%d Days</item> <item quantity="other">%d Days</item>
</plurals> </plurals>
<string name="days">D\na\ny\ns</string> <string name="daysVertical">D\na\ny\ns</string>
<plurals name="Nhours"> <plurals name="Nhours">
<item quantity="one">1 Hour</item> <item quantity="one">1 Hour</item>
<item quantity="other">%d Hours</item> <item quantity="other">%d Hours</item>
</plurals> </plurals>
<string name="hours">H\no\nu\nr\ns</string> <string name="hoursVertical">H\no\nu\nr\ns</string>
<plurals name="Nminutes"> <plurals name="Nminutes">
<item quantity="one">1 Minute</item> <item quantity="one">1 Minute</item>
<item quantity="other">%d Minutes</item> <item quantity="other">%d Minutes</item>
@ -98,6 +98,7 @@
<string name="taskList_menu_insert">Add</string> <string name="taskList_menu_insert">Add</string>
<string name="taskList_menu_tags">Tags</string> <string name="taskList_menu_tags">Tags</string>
<string name="taskList_menu_filters">Display</string> <string name="taskList_menu_filters">Display</string>
<string name="taskList_menu_syncshortcut">Sync</string>
<string name="taskList_menu_more">More</string> <string name="taskList_menu_more">More</string>
<string name="taskList_menu_sync">Synchronization</string> <string name="taskList_menu_sync">Synchronization</string>
<string name="taskList_menu_settings">Settings</string> <string name="taskList_menu_settings">Settings</string>
@ -107,6 +108,7 @@
<string name="taskList_context_delete">Delete Task</string> <string name="taskList_context_delete">Delete Task</string>
<string name="taskList_context_startTimer">Start Timer</string> <string name="taskList_context_startTimer">Start Timer</string>
<string name="taskList_context_stopTimer">Stop Timer</string> <string name="taskList_context_stopTimer">Stop Timer</string>
<string name="taskList_context_postpone">Postpone</string>
<string name="taskList_filter_title">Sort/Filters</string> <string name="taskList_filter_title">Sort/Filters</string>
<string name="taskList_filter_hidden">Hidden/Blocked Tasks</string> <string name="taskList_filter_hidden">Hidden/Blocked Tasks</string>
@ -117,6 +119,8 @@
<string name="taskList_sort_duedate">Sort By Due Date</string> <string name="taskList_sort_duedate">Sort By Due Date</string>
<string name="taskList_sort_reverse">Sort Reverse</string> <string name="taskList_sort_reverse">Sort Reverse</string>
<string name="taskList_postpone_dialog">Postpone for how long?</string>
<!-- TaskEdit --> <!-- TaskEdit -->
<skip /> <skip />
@ -209,15 +213,21 @@ If you don\'t want to see the new task right after you complete the old one, you
<!-- Synchronization --> <!-- Synchronization -->
<skip /> <skip />
<string name="p_sync_rtm">sync_rtm</string>
<string name="p_sync_every">sync_every</string>
<string name="sync_pref_group">Synchronization Services</string> <string name="sync_pref_group">Synchronization Services</string>
<string name="sync_pref_group_actions">Actions</string> <string name="sync_pref_group_actions">Actions</string>
<string name="sync_pref_group_options">Options</string> <string name="sync_pref_group_options">Options</string>
<string name="p_sync_rtm">sync_rtm</string>
<string name="sync_rtm_title">Remember The Milk</string> <string name="sync_rtm_title">Remember The Milk</string>
<string name="sync_rtm_desc">http://www.rememberthemilk.com</string> <string name="sync_rtm_desc">http://www.rememberthemilk.com</string>
<string name="p_sync_every">sync_every</string>
<string name="sync_every_title">Synchronize Frequency</string> <string name="sync_every_title">Synchronize Frequency</string>
<string name="sync_every_desc">If set, sync every # hours when Astrid starts</string> <string name="sync_every_desc">If set, perform sync every # hours</string>
<string name="p_sync_button">sync_button</string>
<string name="sync_button_title">Main Menu Shortcut</string>
<string name="sync_button_desc">Show \"Synchronize\" in Astrid\'s menu</string>
<string name="p_sync_background">sync_background</string>
<string name="sync_background_title">In Background</string>
<string name="sync_background_desc">Synchronize without bothering you</string>
<string name="sync_error">Sync Error! Sorry for the inconvenience! Error:</string> <string name="sync_error">Sync Error! Sorry for the inconvenience! Error:</string>
<string name="sync_auth_request"> <string name="sync_auth_request">
In order to synchronize, please log in to your %s account and authorize Astrid to read your data. In order to synchronize, please log in to your %s account and authorize Astrid to read your data.

@ -20,6 +20,11 @@
android:title="@string/sync_every_title" android:title="@string/sync_every_title"
android:summary="@string/sync_every_desc" /> android:summary="@string/sync_every_desc" />
<CheckBoxPreference
android:key="@string/p_sync_button"
android:title="@string/sync_button_title"
android:summary="@string/sync_button_desc" />
</PreferenceCategory> </PreferenceCategory>
</PreferenceScreen> </PreferenceScreen>

@ -95,7 +95,7 @@ public class Invoker {
public static final String API_SIG_PARAM = "api_sig"; public static final String API_SIG_PARAM = "api_sig";
public static final long INVOCATION_INTERVAL = 750; public static final long INVOCATION_INTERVAL = 300;
private long lastInvocation; private long lastInvocation;

@ -40,8 +40,8 @@ public abstract class RtmData
private static final DateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss"); private static final DateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
public RtmData() public RtmData() {
{ //
} }
/** /**

@ -64,6 +64,7 @@ import com.timsu.astrid.utilities.Constants;
import com.timsu.astrid.utilities.DialogUtilities; import com.timsu.astrid.utilities.DialogUtilities;
import com.timsu.astrid.utilities.Preferences; import com.timsu.astrid.utilities.Preferences;
import com.timsu.astrid.utilities.StartupReceiver; import com.timsu.astrid.utilities.StartupReceiver;
import com.timsu.astrid.widget.NNumberPickerDialog.OnNNumberPickedListener;
/** Primary view for the Astrid Application. Lists all of the tasks in the /** Primary view for the Astrid Application. Lists all of the tasks in the
@ -88,7 +89,8 @@ public class TaskList extends Activity {
private static final int INSERT_ID = Menu.FIRST; private static final int INSERT_ID = Menu.FIRST;
private static final int FILTERS_ID = Menu.FIRST + 1; private static final int FILTERS_ID = Menu.FIRST + 1;
private static final int TAGS_ID = Menu.FIRST + 2; private static final int TAGS_ID = Menu.FIRST + 2;
private static final int MORE_ID = Menu.FIRST + 3; private static final int SYNC_ID = Menu.FIRST + 3;
private static final int MORE_ID = Menu.FIRST + 4;
private static final int OPTIONS_SYNC_ID = Menu.FIRST + 10; private static final int OPTIONS_SYNC_ID = Menu.FIRST + 10;
private static final int OPTIONS_SETTINGS_ID = Menu.FIRST + 11; private static final int OPTIONS_SETTINGS_ID = Menu.FIRST + 11;
@ -103,8 +105,11 @@ public class TaskList extends Activity {
private static final int CONTEXT_SORT_REVERSE = Menu.FIRST + 26; private static final int CONTEXT_SORT_REVERSE = Menu.FIRST + 26;
private static final int CONTEXT_SORT_GROUP = Menu.FIRST; private static final int CONTEXT_SORT_GROUP = Menu.FIRST;
public static final int FLING_DIST_THRESHOLD = 100; public static final int FLING_DIST_THRESHOLD = 100;
public static final int FLING_VEL_THRESHOLD = 300; public static final int FLING_VEL_THRESHOLD = 300;
private static final int SORTFLAG_FILTERDONE = (1 << 5);
private static final int SORTFLAG_FILTERHIDDEN = (1 << 6);
// UI components // UI components
private ListView listView; private ListView listView;
@ -117,6 +122,7 @@ public class TaskList extends Activity {
private HashMap<TaskModelForList, LinkedList<TagModelForView>> taskTags; private HashMap<TaskModelForList, LinkedList<TagModelForView>> taskTags;
private GestureDetector gestureDetector; private GestureDetector gestureDetector;
private View.OnTouchListener gestureTouchListener; private View.OnTouchListener gestureTouchListener;
private boolean displaySyncShortcut;
// display filters // display filters
private static boolean filterShowHidden = false; private static boolean filterShowHidden = false;
@ -254,6 +260,16 @@ public class TaskList extends Activity {
item.setIcon(android.R.drawable.ic_menu_myplaces); item.setIcon(android.R.drawable.ic_menu_myplaces);
item.setAlphabeticShortcut('t'); item.setAlphabeticShortcut('t');
if(Preferences.shouldDisplaySyncButton(this)){
item = menu.add(Menu.NONE, SYNC_ID, Menu.NONE,
R.string.taskList_menu_syncshortcut);
item.setIcon(android.R.drawable.ic_menu_upload);
item.setAlphabeticShortcut('s');
displaySyncShortcut = true;
} else {
displaySyncShortcut = false;
}
item = menu.add(Menu.NONE, MORE_ID, Menu.NONE, item = menu.add(Menu.NONE, MORE_ID, Menu.NONE,
R.string.taskList_menu_more); R.string.taskList_menu_more);
item.setIcon(android.R.drawable.ic_menu_more); item.setIcon(android.R.drawable.ic_menu_more);
@ -262,6 +278,27 @@ public class TaskList extends Activity {
return true; return true;
} }
@Override
public boolean onPrepareOptionsMenu(Menu menu) {
super.onPrepareOptionsMenu(menu);
boolean shouldDisplaySyncShortcut = Preferences.shouldDisplaySyncButton(this);
if(shouldDisplaySyncShortcut != displaySyncShortcut) {
if(shouldDisplaySyncShortcut) {
MenuItem item = menu.add(Menu.NONE, SYNC_ID, Menu.NONE,
R.string.taskList_menu_syncshortcut);
item.setIcon(android.R.drawable.ic_menu_upload);
item.setAlphabeticShortcut('s');
} else
menu.removeItem(SYNC_ID);
displaySyncShortcut = shouldDisplaySyncShortcut;
}
return true;
}
public boolean onCreateMoreOptionsMenu(Menu menu) { public boolean onCreateMoreOptionsMenu(Menu menu) {
MenuItem item; MenuItem item;
@ -619,8 +656,15 @@ public class TaskList extends Activity {
/** Save the sorting mode to the preferences */ /** Save the sorting mode to the preferences */
private void saveTaskListSort() { private void saveTaskListSort() {
int sortId = sortMode.ordinal() + 1; int sortId = sortMode.ordinal() + 1;
if(filterShowDone)
sortId |= SORTFLAG_FILTERDONE;
if(filterShowHidden)
sortId |= SORTFLAG_FILTERHIDDEN;
if(sortReverse) if(sortReverse)
sortId *= -1; sortId *= -1;
Preferences.setTaskListSort(this, sortId); Preferences.setTaskListSort(this, sortId);
} }
@ -630,14 +674,21 @@ public class TaskList extends Activity {
if(sortId == 0) if(sortId == 0)
return; return;
sortReverse = sortId < 0; sortReverse = sortId < 0;
sortId = Math.abs(sortId);
filterShowDone = (sortId & SORTFLAG_FILTERDONE) > 0;
filterShowHidden = (sortId & SORTFLAG_FILTERHIDDEN) > 0;
sortMode = SortMode.values()[Math.abs(sortId - 1)]; sortId = sortId & ~(SORTFLAG_FILTERDONE | SORTFLAG_FILTERHIDDEN);
sortMode = SortMode.values()[sortId - 1];
} }
@Override @Override
public boolean onMenuItemSelected(int featureId, MenuItem item) { public boolean onMenuItemSelected(int featureId, MenuItem item) {
Intent intent; Intent intent;
TaskModelForList task; final TaskModelForList task;
Resources r = getResources();
switch(item.getItemId()) { switch(item.getItemId()) {
case INSERT_ID: case INSERT_ID:
@ -649,6 +700,9 @@ public class TaskList extends Activity {
case TAGS_ID: case TAGS_ID:
showTagsView(); showTagsView();
return true; return true;
case SYNC_ID:
onActivityResult(ACTIVITY_SYNCHRONIZE, Constants.RESULT_SYNCHRONIZE, null);
return true;
case MORE_ID: case MORE_ID:
layout.showContextMenu(); layout.showContextMenu();
return true; return true;
@ -686,13 +740,40 @@ public class TaskList extends Activity {
taskController.saveTask(task); taskController.saveTask(task);
fillData(); fillData();
return true; return true;
case TaskListAdapter.CONTEXT_POSTPONE_ID:
task = taskArray.get(item.getGroupId());
DialogUtilities.dayHourPicker(this,
r.getString(R.string.taskList_postpone_dialog),
new OnNNumberPickedListener() {
public void onNumbersPicked(int[] values) {
long postponeMillis = (values[0] * 24 + values[1]) *
3600L * 1000;
Date preferred = task.getPreferredDueDate();
Date definite = task.getDefiniteDueDate();
if(preferred != null) {
preferred = new Date(preferred.getTime() +
postponeMillis);
task.setPreferredDueDate(preferred);
}
if(definite != null) {
definite = new Date(definite.getTime() +
postponeMillis);
task.setDefiniteDueDate(definite);
}
taskController.saveTask(task);
fillData();
}
});
return true;
case CONTEXT_FILTER_HIDDEN: case CONTEXT_FILTER_HIDDEN:
TaskList.filterShowHidden = !filterShowHidden; TaskList.filterShowHidden = !filterShowHidden;
saveTaskListSort();
fillData(); fillData();
return true; return true;
case CONTEXT_FILTER_DONE: case CONTEXT_FILTER_DONE:
TaskList.filterShowDone = !filterShowDone; TaskList.filterShowDone = !filterShowDone;
saveTaskListSort();
fillData(); fillData();
return true; return true;
case CONTEXT_FILTER_TAG: case CONTEXT_FILTER_TAG:

@ -26,6 +26,7 @@ import android.app.AlertDialog;
import android.content.Context; import android.content.Context;
import android.content.DialogInterface; import android.content.DialogInterface;
import android.content.res.Resources; import android.content.res.Resources;
import android.graphics.Paint;
import android.graphics.Typeface; import android.graphics.Typeface;
import android.view.ContextMenu; import android.view.ContextMenu;
import android.view.LayoutInflater; import android.view.LayoutInflater;
@ -62,6 +63,16 @@ public class TaskListAdapter extends ArrayAdapter<TaskModelForList> {
public static final int CONTEXT_EDIT_ID = Menu.FIRST + 50; public static final int CONTEXT_EDIT_ID = Menu.FIRST + 50;
public static final int CONTEXT_DELETE_ID = Menu.FIRST + 51; public static final int CONTEXT_DELETE_ID = Menu.FIRST + 51;
public static final int CONTEXT_TIMER_ID = Menu.FIRST + 52; public static final int CONTEXT_TIMER_ID = Menu.FIRST + 52;
public static final int CONTEXT_POSTPONE_ID = Menu.FIRST + 53;
private static final int KEY_NAME = 0;
private static final int KEY_DEADLINE = 1;
private static final int KEY_OVERDUE = 2;
private static final int KEY_REPEAT = 3;
private static final int KEY_REMINDERS = 4;
private static final int KEY_TIMES = 5;
private static final int KEY_TAGS = 6;
private static final int KEY_HIDDEN = 7;
private final Activity activity; private final Activity activity;
private List<TaskModelForList> objects; private List<TaskModelForList> objects;
@ -72,6 +83,8 @@ public class TaskListAdapter extends ArrayAdapter<TaskModelForList> {
private Integer fontSizePreference; private Integer fontSizePreference;
private AlertController alarmController; private AlertController alarmController;
private TaskModelForList recentlyCompleted = null;
public interface TaskListAdapterHooks { public interface TaskListAdapterHooks {
List<TaskModelForList> getTaskArray(); List<TaskModelForList> getTaskArray();
List<TagModelForView> getTagsFor(TaskModelForList task); List<TagModelForView> getTagsFor(TaskModelForList task);
@ -100,9 +113,10 @@ public class TaskListAdapter extends ArrayAdapter<TaskModelForList> {
@Override @Override
public View getView(int position, View convertView, ViewGroup parent) { public View getView(int position, View convertView, ViewGroup parent) {
View view; View view = convertView;
view = inflater.inflate(resource, parent, false); if(view == null)
view = inflater.inflate(resource, parent, false);
setupView(view, objects.get(position)); setupView(view, objects.get(position));
addListeners(position, view); addListeners(position, view);
@ -120,7 +134,7 @@ public class TaskListAdapter extends ArrayAdapter<TaskModelForList> {
progress.setTag(task); progress.setTag(task);
if(task.getTimerStart() != null) if(task.getTimerStart() != null)
timer.setImageDrawable(r.getDrawable(R.drawable.ic_dialog_time)); timer.setImageDrawable(r.getDrawable(R.drawable.icon_timer));
progress.setChecked(task.isTaskCompleted()); progress.setChecked(task.isTaskCompleted());
setFieldContentsAndVisibility(view, task); setFieldContentsAndVisibility(view, task);
@ -144,12 +158,20 @@ public class TaskListAdapter extends ArrayAdapter<TaskModelForList> {
// name // name
final TextView name = ((TextView)view.findViewById(R.id.task_name)); final TextView name = ((TextView)view.findViewById(R.id.task_name));
if(visibleFields.TITLE) { if(visibleFields.TITLE) {
String nameValue = task.getName(); String cachedResult = task.getCachedLabel(KEY_NAME);
if(task.getHiddenUntil() != null && task.getHiddenUntil().after(new Date())) { if(cachedResult == null) {
nameValue = "(" + r.getString(R.string.taskList_hiddenPrefix) + ") " + nameValue; String nameValue = task.getName();
name.setTypeface(Typeface.DEFAULT, Typeface.ITALIC); if(task.getHiddenUntil() != null && task.getHiddenUntil().after(new Date())) {
nameValue = "(" + r.getString(R.string.taskList_hiddenPrefix) + ") " + nameValue;
task.putCachedLabel(KEY_HIDDEN, "");
}
cachedResult = nameValue.toString();
task.putCachedLabel(KEY_NAME, cachedResult);
} }
name.setText(nameValue); name.setText(cachedResult);
if(task.getCachedLabel(KEY_HIDDEN) != null)
name.setTypeface(Typeface.DEFAULT, Typeface.ITALIC);
if(fontSizePreference != null && fontSizePreference > 0) if(fontSizePreference != null && fontSizePreference > 0)
name.setTextSize(fontSizePreference); name.setTextSize(fontSizePreference);
} }
@ -157,7 +179,7 @@ public class TaskListAdapter extends ArrayAdapter<TaskModelForList> {
// importance // importance
final ImageView importance = (ImageView)view.findViewById(R.id.importance); final View importance = (View)view.findViewById(R.id.importance);
if(visibleFields.IMPORTANCE) { if(visibleFields.IMPORTANCE) {
importance.setBackgroundColor(r.getColor( importance.setBackgroundColor(r.getColor(
task.getImportance().getColorResource())); task.getImportance().getColorResource()));
@ -167,126 +189,156 @@ public class TaskListAdapter extends ArrayAdapter<TaskModelForList> {
// due date / completion date // due date / completion date
final TextView deadlines = ((TextView)view.findViewById(R.id.text_deadlines)); final TextView deadlines = ((TextView)view.findViewById(R.id.text_deadlines));
if(visibleFields.DEADLINE) { if(visibleFields.DEADLINE) {
StringBuilder label = new StringBuilder(); String cachedResult = task.getCachedLabel(KEY_DEADLINE);
if(task.isTaskCompleted()) { if(cachedResult == null) {
if(task.getCompletionDate() != null) { StringBuilder label = new StringBuilder();
int secondsLeft = (int)((task.getCompletionDate().getTime() - if(task.isTaskCompleted()) {
System.currentTimeMillis()) / 1000); if(task.getCompletionDate() != null) {
label.append(r.getString(R.string.taskList_completedPrefix)). int secondsLeft = (int)((task.getCompletionDate().getTime() -
append(" "). System.currentTimeMillis()) / 1000);
append(DateUtilities.getDurationString(r, Math.abs(secondsLeft), 1)). label.append(r.getString(R.string.taskList_completedPrefix)).
append(" " + r.getString(R.string.ago_suffix)); append(" ").
} append(DateUtilities.getDurationString(r, Math.abs(secondsLeft), 1)).
} else { append(" " + r.getString(R.string.ago_suffix));
boolean taskOverdue = false;
if(task.getDefiniteDueDate() != null) {
long timeLeft = task.getDefiniteDueDate().getTime() -
System.currentTimeMillis();
if(timeLeft > 0){
label.append(r.getString(R.string.taskList_dueIn)).append(" ");
} else {
taskOverdue = true;
label.append(r.getString(R.string.taskList_overdueBy)).append(" ");
deadlines.setTextColor(r.getColor(R.color.taskList_dueDateOverdue));
} }
label.append(DateUtilities.getDurationString(r, } else {
(int)Math.abs(timeLeft/1000), 1)); boolean taskOverdue = false;
} if(task.getDefiniteDueDate() != null) {
if(!taskOverdue && task.getPreferredDueDate() != null) { long timeLeft = task.getDefiniteDueDate().getTime() -
if(task.getDefiniteDueDate() != null) System.currentTimeMillis();
label.append(" / "); if(timeLeft > 0){
long timeLeft = task.getPreferredDueDate().getTime() - label.append(r.getString(R.string.taskList_dueIn)).append(" ");
System.currentTimeMillis(); } else {
label.append(r.getString(R.string.taskList_goalPrefix)).append(" "); taskOverdue = true;
if(timeLeft > 0){ label.append(r.getString(R.string.taskList_overdueBy)).append(" ");
label.append(r.getString(R.string.taskList_dueIn)).append(" "); task.putCachedLabel(KEY_OVERDUE, "");
} else { }
label.append(r.getString(R.string.taskList_overdueBy)).append(" "); label.append(DateUtilities.getDurationString(r,
deadlines.setTextColor(r.getColor(R.color.taskList_dueDateOverdue)); (int)Math.abs(timeLeft/1000), 1));
}
if(!taskOverdue && task.getPreferredDueDate() != null) {
if(task.getDefiniteDueDate() != null)
label.append(" / ");
long timeLeft = task.getPreferredDueDate().getTime() -
System.currentTimeMillis();
label.append(r.getString(R.string.taskList_goalPrefix)).append(" ");
if(timeLeft > 0){
label.append(r.getString(R.string.taskList_dueIn)).append(" ");
} else {
label.append(r.getString(R.string.taskList_overdueBy)).append(" ");
deadlines.setTextColor(r.getColor(R.color.taskList_dueDateOverdue));
}
label.append(DateUtilities.getDurationString(r,
(int)Math.abs(timeLeft/1000), 1)).append(" ");
} }
label.append(DateUtilities.getDurationString(r,
(int)Math.abs(timeLeft/1000), 1)).append(" ");
} }
cachedResult = label.toString();
task.putCachedLabel(KEY_DEADLINE, cachedResult);
} }
deadlines.setText(label); deadlines.setText(cachedResult);
if(task.getCachedLabel(KEY_OVERDUE) != null)
deadlines.setTextColor(r.getColor(R.color.taskList_dueDateOverdue));
} }
setVisibility(deadlines); setVisibility(deadlines);
// estimated / elapsed time // estimated / elapsed time
final TextView times = ((TextView)view.findViewById(R.id.text_times)); final TextView times = ((TextView)view.findViewById(R.id.text_times));
if(visibleFields.TIMES) { if(visibleFields.TIMES) {
Integer elapsed = task.getElapsedSeconds(); String cachedResult = task.getCachedLabel(KEY_TIMES);
if(task.getTimerStart() != null) if(cachedResult == null) {
elapsed += ((System.currentTimeMillis() - task.getTimerStart().getTime())/1000); Integer elapsed = task.getElapsedSeconds();
Integer estimated = task.getEstimatedSeconds(); if(task.getTimerStart() != null)
StringBuilder label = new StringBuilder(); elapsed += ((System.currentTimeMillis() - task.getTimerStart().getTime())/1000);
if(estimated > 0) { Integer estimated = task.getEstimatedSeconds();
label.append(r.getString(R.string.taskList_estimatedTimePrefix)). StringBuilder label = new StringBuilder();
append(" "). if(estimated > 0) {
append(DateUtilities.getDurationString(r, estimated, 2)); label.append(r.getString(R.string.taskList_estimatedTimePrefix)).
if(elapsed > 0) append(" ").
label.append(" / "); append(DateUtilities.getDurationString(r, estimated, 2));
} if(elapsed > 0)
if(elapsed > 0) { label.append(" / ");
label.append(r.getString(R.string.taskList_elapsedTimePrefix)). }
append(" "). if(elapsed > 0) {
append(DateUtilities.getDurationString(r, elapsed, 2)); label.append(r.getString(R.string.taskList_elapsedTimePrefix)).
append(" ").
append(DateUtilities.getDurationString(r, elapsed, 2));
}
cachedResult = label.toString();
task.putCachedLabel(KEY_TIMES, cachedResult);
} }
times.setText(label); times.setText(cachedResult);
} }
setVisibility(times); setVisibility(times);
// reminders // reminders
final TextView reminders = ((TextView)view.findViewById(R.id.text_reminders)); final TextView reminders = ((TextView)view.findViewById(R.id.text_reminders));
if(visibleFields.REMINDERS) { if(visibleFields.REMINDERS) {
Integer notifyEvery = task.getNotificationIntervalSeconds(); String cachedResult = task.getCachedLabel(KEY_REMINDERS);
StringBuilder label = new StringBuilder(); if(cachedResult == null) {
if(notifyEvery != null && notifyEvery > 0) { Integer notifyEvery = task.getNotificationIntervalSeconds();
label.append(r.getString(R.string.taskList_periodicReminderPrefix)). StringBuilder label = new StringBuilder();
append(" ").append(DateUtilities.getDurationString(r, notifyEvery, 1)); if(notifyEvery != null && notifyEvery > 0) {
} label.append(r.getString(R.string.taskList_periodicReminderPrefix)).
append(" ").append(DateUtilities.getDurationString(r, notifyEvery, 1));
}
try { try {
alarmController.open(); alarmController.open();
List<Date> alerts = alarmController.getTaskAlerts(task.getTaskIdentifier()); List<Date> alerts = alarmController.getTaskAlerts(task.getTaskIdentifier());
if(alerts.size() > 0) { if(alerts.size() > 0) {
if(label.length() > 0) if(label.length() > 0)
label.append(". "); label.append(". ");
label.append(r.getQuantityString(R.plurals.Nalarms, alerts.size(), label.append(r.getQuantityString(R.plurals.Nalarms, alerts.size(),
alerts.size())).append(" ").append(r.getString(R.string.taskList_alarmSuffix)); alerts.size())).append(" ").append(r.getString(R.string.taskList_alarmSuffix));
} }
} finally { } finally {
alarmController.close(); alarmController.close();
}
cachedResult = label.toString();
task.putCachedLabel(KEY_REMINDERS, cachedResult);
} }
reminders.setText(label); reminders.setText(cachedResult);
} }
setVisibility(reminders); setVisibility(reminders);
// repeats // repeats
final TextView repeats = ((TextView)view.findViewById(R.id.text_repeats)); final TextView repeats = ((TextView)view.findViewById(R.id.text_repeats));
if(visibleFields.REPEATS) { if(visibleFields.REPEATS) {
RepeatInfo repeatInfo = task.getRepeat(); String cachedResult = task.getCachedLabel(KEY_REPEAT);
if(repeatInfo != null) { if(cachedResult == null) {
repeats.setText(r.getString(R.string.taskList_repeatPrefix) + RepeatInfo repeatInfo = task.getRepeat();
" " + repeatInfo.getValue() + " " + if(repeatInfo != null) {
r.getString(repeatInfo.getInterval().getLabelResource())); cachedResult = r.getString(R.string.taskList_repeatPrefix) +
" " + repeatInfo.getValue() + " " +
r.getString(repeatInfo.getInterval().getLabelResource());
} else
cachedResult = "";
task.putCachedLabel(KEY_REPEAT, cachedResult);
} }
repeats.setText(cachedResult);
} }
setVisibility(repeats); setVisibility(repeats);
// tags // tags
final TextView tags = ((TextView)view.findViewById(R.id.text_tags)); final TextView tags = ((TextView)view.findViewById(R.id.text_tags));
if(visibleFields.TAGS) { if(visibleFields.TAGS) {
List<TagModelForView> alltags = hooks.getTagsFor(task); String cachedResult = task.getCachedLabel(KEY_TAGS);
StringBuilder tagString = new StringBuilder(); if(cachedResult == null) {
for(Iterator<TagModelForView> i = alltags.iterator(); i.hasNext(); ) { List<TagModelForView> alltags = hooks.getTagsFor(task);
TagModelForView tag = i.next(); StringBuilder tagString = new StringBuilder();
tagString.append(tag.getName()); for(Iterator<TagModelForView> i = alltags.iterator(); i.hasNext(); ) {
if(i.hasNext()) TagModelForView tag = i.next();
tagString.append(", "); tagString.append(tag.getName());
if(i.hasNext())
tagString.append(", ");
}
if(alltags.size() > 0)
cachedResult = r.getString(R.string.taskList_tagsPrefix) + " " + tagString;
else
cachedResult = "";
task.putCachedLabel(KEY_TAGS, cachedResult);
} }
if(alltags.size() > 0) tags.setText(cachedResult);
tags.setText(r.getString(R.string.taskList_tagsPrefix) + " " + tagString);
} }
setVisibility(tags); setVisibility(tags);
@ -349,6 +401,9 @@ public class TaskListAdapter extends ArrayAdapter<TaskModelForList> {
timerTitle = R.string.taskList_context_stopTimer; timerTitle = R.string.taskList_context_stopTimer;
menu.add(position, CONTEXT_TIMER_ID, Menu.NONE, timerTitle); menu.add(position, CONTEXT_TIMER_ID, Menu.NONE, timerTitle);
menu.add(position, CONTEXT_POSTPONE_ID, Menu.NONE,
R.string.taskList_context_postpone);
menu.setHeaderTitle(task.getName()); menu.setHeaderTitle(task.getName());
} }
}); });
@ -359,6 +414,12 @@ public class TaskListAdapter extends ArrayAdapter<TaskModelForList> {
task.setProgressPercentage(progress); task.setProgressPercentage(progress);
hooks.getTaskController().saveTask(task); hooks.getTaskController().saveTask(task);
// show this task as completed even if it has repeats
if(progress == 100)
recentlyCompleted = task;
else
recentlyCompleted = null;
// if our timer is on, ask if we want to stop // if our timer is on, ask if we want to stop
if(progress == 100 && task.getTimerStart() != null) { if(progress == 100 && task.getTimerStart() != null) {
new AlertDialog.Builder(activity) new AlertDialog.Builder(activity)
@ -382,12 +443,13 @@ public class TaskListAdapter extends ArrayAdapter<TaskModelForList> {
private void setTaskAppearance(TaskModelForList task, TextView name, CheckBox progress) { private void setTaskAppearance(TaskModelForList task, TextView name, CheckBox progress) {
Resources r = activity.getResources(); Resources r = activity.getResources();
if(task.isTaskCompleted()) { if(task.isTaskCompleted() || task == recentlyCompleted) {
name.setBackgroundDrawable(r.getDrawable(R.drawable.strikeout)); name.setPaintFlags(name.getPaintFlags() | Paint.STRIKE_THRU_TEXT_FLAG);
name.setTextColor(r.getColor(R.color.task_list_done)); name.setTextColor(r.getColor(R.color.task_list_done));
progress.setButtonDrawable(R.drawable.btn_check0); progress.setButtonDrawable(R.drawable.btn_check0);
progress.setChecked(true);
} else { } else {
name.setBackgroundDrawable(null); name.setPaintFlags(name.getPaintFlags() & ~Paint.STRIKE_THRU_TEXT_FLAG);
name.setTextColor(r.getColor(task.getTaskColorResource(getContext()))); name.setTextColor(r.getColor(task.getTaskColorResource(getContext())));
if(task.getProgressPercentage() >= 75) if(task.getProgressPercentage() >= 75)

@ -142,7 +142,7 @@ public class TaskView extends TaskModificationActivity<TaskModelForView> {
int secondsAgo = (int) ((System.currentTimeMillis() - date.getTime())/1000); int secondsAgo = (int) ((System.currentTimeMillis() - date.getTime())/1000);
String text = DateUtilities.getDurationString(r, String text = DateUtilities.getDurationString(r,
Math.abs(secondsAgo), 2); Math.abs(secondsAgo), 2);
view.setText(text + r.getString(R.string.ago_suffix)); view.setText(text + " " + r.getString(R.string.ago_suffix));
} }
/* ====================================================================== /* ======================================================================

@ -9,8 +9,8 @@ import android.os.Bundle;
import com.timsu.astrid.R; import com.timsu.astrid.R;
import com.timsu.astrid.utilities.Constants; import com.timsu.astrid.utilities.Constants;
import com.timsu.astrid.utilities.DialogUtilities;
import com.timsu.astrid.utilities.Notifications; import com.timsu.astrid.utilities.Notifications;
import com.timsu.astrid.widget.NNumberPickerDialog;
import com.timsu.astrid.widget.NNumberPickerDialog.OnNNumberPickedListener; import com.timsu.astrid.widget.NNumberPickerDialog.OnNNumberPickedListener;
public class TaskViewNotifier extends TaskView { public class TaskViewNotifier extends TaskView {
@ -77,9 +77,9 @@ public class TaskViewNotifier extends TaskView {
} }
private void snoozeAlert() { private void snoozeAlert() {
Resources r = getResources(); DialogUtilities.hourMinutePicker(this,
// ask how long getResources().getString(R.string.notify_snooze_title),
new NNumberPickerDialog(this, new OnNNumberPickedListener() { new OnNNumberPickedListener() {
public void onNumbersPicked(int[] values) { public void onNumbersPicked(int[] values) {
int snoozeSeconds = values[0] * 3600 + values[1] * 60; int snoozeSeconds = values[0] * 3600 + values[1] * 60;
Notifications.createSnoozeAlarm(TaskViewNotifier.this, Notifications.createSnoozeAlarm(TaskViewNotifier.this,
@ -90,8 +90,6 @@ public class TaskViewNotifier extends TaskView {
TaskList.shouldCloseInstance = true; TaskList.shouldCloseInstance = true;
finish(); finish();
} }
}, r.getString(R.string.notify_snooze_title), });
new int[] {0, 0}, new int[] {1, 5}, new int[] {0, 0},
new int[] {99, 59}, new String[] {":", null}).show();
} }
} }

@ -161,8 +161,4 @@ public class SyncMapping extends AbstractModel {
private void setRemoteId(String remoteId) { private void setRemoteId(String remoteId) {
setValues.put(REMOTE_ID, remoteId); setValues.put(REMOTE_ID, remoteId);
} }
private void setUpdated(boolean updated) {
setValues.put(UPDATED, updated ? 1 : 0);
}
} }

@ -20,6 +20,7 @@
package com.timsu.astrid.data.task; package com.timsu.astrid.data.task;
import java.util.Date; import java.util.Date;
import java.util.HashMap;
import android.content.Context; import android.content.Context;
import android.database.Cursor; import android.database.Cursor;
@ -96,6 +97,16 @@ public class TaskModelForList extends AbstractTaskModel {
return super.isHidden(); return super.isHidden();
} }
/** map of cached display labels */
private HashMap<Integer, String> displayLabels = new HashMap<Integer, String>();
public String getCachedLabel(int key) {
return displayLabels.get(key);
}
public void putCachedLabel(int key, String value) {
displayLabels.put(key, value);
}
// --- constructors // --- constructors
public TaskModelForList(Cursor cursor) { public TaskModelForList(Cursor cursor) {
@ -205,4 +216,14 @@ public class TaskModelForList extends AbstractTaskModel {
public static String getNameField() { public static String getNameField() {
return NAME; return NAME;
} }
@Override
public void setPreferredDueDate(Date preferredDueDate) {
super.setPreferredDueDate(preferredDueDate);
}
@Override
public void setDefiniteDueDate(Date definiteDueDate) {
super.setDefiniteDueDate(definiteDueDate);
}
} }

@ -60,12 +60,18 @@ public class RTMSyncService extends SynchronizationService {
Preferences.getSyncRTMToken(activity) == null) { Preferences.getSyncRTMToken(activity) == null) {
DialogUtilities.okCancelDialog(activity, DialogUtilities.okCancelDialog(activity,
activity.getResources().getString(R.string.sync_rtm_notes), activity.getResources().getString(R.string.sync_rtm_notes),
new Dialog.OnClickListener() { new Dialog.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
authenticate(activity);
}
}, new Dialog.OnClickListener() {
@Override @Override
public void onClick(DialogInterface dialog, int which) { public void onClick(DialogInterface dialog, int which) {
authenticate(activity); if(progressDialog != null)
progressDialog.dismiss();
} }
}, null); });
} else } else
authenticate(activity); authenticate(activity);
} }
@ -335,7 +341,7 @@ public class RTMSyncService extends SynchronizationService {
sb.append(note.getText() + "\n"); sb.append(note.getText() + "\n");
} }
if(sb.length() > 0) if(sb.length() > 0)
task.notes = sb.toString(); task.notes = sb.toString().trim();
// list / tags // list / tags
LinkedList<String> tagsList = rtmTaskSeries.getTags(); LinkedList<String> tagsList = rtmTaskSeries.getTags();
@ -355,8 +361,18 @@ public class RTMSyncService extends SynchronizationService {
} }
task.creationDate = rtmTaskSeries.getCreated(); task.creationDate = rtmTaskSeries.getCreated();
task.completionDate = rtmTask.getCompleted(); task.completionDate = rtmTask.getCompleted();
if(rtmTask.getDue() != null) if(rtmTask.getDue() != null) {
task.definiteDueDate = rtmTask.getDue(); Date due = rtmTask.getDue();
// just a day - set it to midnight
if(due.getHours() == 0 && due.getMinutes() == 0 && due.getSeconds() == 0) {
due.setHours(23);
due.setMinutes(59);
}
task.definiteDueDate = due;
}
task.progressPercentage = (rtmTask.getCompleted() == null) ? 0 : 100; task.progressPercentage = (rtmTask.getCompleted() == null) ? 0 : 100;
task.importance = Importance.values()[rtmTask.getPriority().ordinal()]; task.importance = Importance.values()[rtmTask.getPriority().ordinal()];

@ -480,7 +480,7 @@ public abstract class SynchronizationService {
sb.append("\nDeleted: " + localDeletedTasks); sb.append("\nDeleted: " + localDeletedTasks);
if(mergedTasks > 0) if(mergedTasks > 0)
sb.append("\n\nMerged: " + localCreatedTasks); sb.append("\n\nMerged: " + mergedTasks);
if(remoteCreatedTasks + remoteDeletedTasks + remoteUpdatedTasks > 0) if(remoteCreatedTasks + remoteDeletedTasks + remoteUpdatedTasks > 0)
sb.append("\n\nSummary - Remote Server:"); sb.append("\n\nSummary - Remote Server:");

@ -3,11 +3,21 @@ package com.timsu.astrid.utilities;
import android.app.AlertDialog; import android.app.AlertDialog;
import android.content.Context; import android.content.Context;
import android.content.DialogInterface; import android.content.DialogInterface;
import android.content.res.Resources;
import com.timsu.astrid.R; import com.timsu.astrid.R;
import com.timsu.astrid.widget.NNumberPickerDialog;
import com.timsu.astrid.widget.NNumberPickerDialog.OnNNumberPickedListener;
public class DialogUtilities { public class DialogUtilities {
/**
* Displays a dialog box with an OK button
*
* @param context
* @param text
* @param okListener
*/
public static void okDialog(Context context, String text, public static void okDialog(Context context, String text,
DialogInterface.OnClickListener okListener) { DialogInterface.OnClickListener okListener) {
new AlertDialog.Builder(context) new AlertDialog.Builder(context)
@ -18,6 +28,14 @@ public class DialogUtilities {
.show(); .show();
} }
/**
* Displays a dialog box with OK and Cancel buttons
*
* @param context
* @param text
* @param okListener
* @param cancelListener
*/
public static void okCancelDialog(Context context, String text, public static void okCancelDialog(Context context, String text,
DialogInterface.OnClickListener okListener, DialogInterface.OnClickListener okListener,
DialogInterface.OnClickListener cancelListener) { DialogInterface.OnClickListener cancelListener) {
@ -29,4 +47,36 @@ public class DialogUtilities {
.setNegativeButton(android.R.string.cancel, cancelListener) .setNegativeButton(android.R.string.cancel, cancelListener)
.show(); .show();
} }
/**
* Displays a dialog box that lets users pick a day & hour value
*
* @param context
* @param title title of the dialog box
* @param listener what happens when users click ok
*/
public static void dayHourPicker(Context context, String title,
OnNNumberPickedListener listener) {
Resources r = context.getResources();
new NNumberPickerDialog(context, listener, title,
new int[] {0, 0}, new int[] {1, 1}, new int[] {0, 0},
new int[] {31, 23}, new String[] {
r.getString(R.string.daysVertical),
r.getString(R.string.hoursVertical)
}).show();
}
/**
* Displays a dialog box that lets users pick an hour & minute value.
*
* @param context
* @param title title of the dialog box
* @param listener what happens when users click ok
*/
public static void hourMinutePicker(Context context, String title,
OnNNumberPickedListener listener) {
new NNumberPickerDialog(context, listener, title,
new int[] {0, 0}, new int[] {1, 5}, new int[] {0, 0},
new int[] {99, 59}, new String[] {":", null}).show();
}
} }

@ -385,9 +385,13 @@ public class Notifications extends BroadcastReceiver {
appName, appName,
reminder + " " + taskName, reminder + " " + taskName,
pendingIntent); pendingIntent);
if(Preferences.isPersistenceMode(context))
notification.flags |= Notification.FLAG_NO_CLEAR;
notification.defaults = Notification.DEFAULT_LIGHTS; notification.defaults = Notification.DEFAULT_LIGHTS;
notification.flags |= Notification.FLAG_AUTO_CANCEL;
if(Preferences.isPersistenceMode(context)) {
notification.flags |= Notification.FLAG_NO_CLEAR;
notification.ledOffMS = 5000;
notification.ledOnMS = 700;
}
if(quietHours) { if(quietHours) {
notification.vibrate = null; notification.vibrate = null;
notification.sound = null; notification.sound = null;

@ -41,6 +41,9 @@ public class Preferences {
if(!prefs.contains(r.getString(R.string.p_deadlineTime))) { if(!prefs.contains(r.getString(R.string.p_deadlineTime))) {
editor.putString(r.getString(R.string.p_deadlineTime), "7"); editor.putString(r.getString(R.string.p_deadlineTime), "7");
} }
if(!prefs.contains(r.getString(R.string.p_notif_defaultRemind))) {
editor.putString(r.getString(R.string.p_notif_defaultRemind), "7");
}
if(!prefs.contains(r.getString(R.string.p_colorize))) { if(!prefs.contains(r.getString(R.string.p_colorize))) {
editor.putBoolean(r.getString(R.string.p_colorize), DEFAULT_COLORIZE); editor.putBoolean(r.getString(R.string.p_colorize), DEFAULT_COLORIZE);
} }
@ -213,6 +216,13 @@ public class Preferences {
R.string.p_sync_rtm), false); R.string.p_sync_rtm), false);
} }
/** Should display sync shortcut? */
public static boolean shouldDisplaySyncButton(Context context) {
Resources r = context.getResources();
return getPrefs(context).getBoolean(r.getString(
R.string.p_sync_button), false);
}
/** returns the font size user wants on the front page */ /** returns the font size user wants on the front page */
public static Integer autoSyncFrequency(Context context) { public static Integer autoSyncFrequency(Context context) {
return getIntegerValue(context, R.string.p_sync_every); return getIntegerValue(context, R.string.p_sync_every);

@ -1,5 +0,0 @@
package com.timsu.astrid.utilities;
public class VisibilityCalculator {
}

@ -60,8 +60,8 @@ public class TimeDurationControlSet implements OnNNumberPickedListener,
activity.getResources().getString(titleResource), activity.getResources().getString(titleResource),
new int[] {0, 0}, new int[] {1, 1}, new int[] {0, 0}, new int[] {0, 0}, new int[] {1, 1}, new int[] {0, 0},
new int[] {31, 23}, new String[] { new int[] {31, 23}, new String[] {
r.getString(R.string.days), r.getString(R.string.daysVertical),
r.getString(R.string.hours) r.getString(R.string.hoursVertical)
}); });
break; break;
case HOURS_MINUTES: case HOURS_MINUTES:

Loading…
Cancel
Save