From e5826cdb9d121c8e554943a9ef87a61196cdf2a0 Mon Sep 17 00:00:00 2001 From: Tim Su Date: Sun, 28 Dec 2008 04:23:08 +0000 Subject: [PATCH] Fixed startup crashing issue, added "completed task time" display on task listing. --- AndroidManifest.xml | 7 +- res/values/colors.xml | 1 + res/values/strings.xml | 5 +- src/com/timsu/astrid/activities/TaskList.java | 106 +++++++++--------- .../astrid/activities/TaskListAdapter.java | 66 ++++++----- .../astrid/activities/TaskViewNotifier.java | 4 +- src/com/timsu/astrid/data/AbstractModel.java | 56 ++++++++- .../astrid/data/task/AbstractTaskModel.java | 70 +++++++++--- .../astrid/data/task/TaskController.java | 12 +- .../astrid/data/task/TaskModelForList.java | 17 ++- .../astrid/data/task/TaskModelForNotify.java | 5 +- .../timsu/astrid/utilities/Notifications.java | 9 +- 12 files changed, 238 insertions(+), 120 deletions(-) diff --git a/AndroidManifest.xml b/AndroidManifest.xml index dcb1e06e4..7f43b789f 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -1,8 +1,8 @@ + android:versionCode="17" + android:versionName="1.7.8"> @@ -21,8 +21,7 @@ + android:launchMode="singleTask" /> diff --git a/res/values/colors.xml b/res/values/colors.xml index a4baef331..41e3222cc 100644 --- a/res/values/colors.xml +++ b/res/values/colors.xml @@ -25,6 +25,7 @@ #ffffff44 #ffffffaa + #ffccffaa #ff888888 #ff83ffa9 diff --git a/res/values/strings.xml b/res/values/strings.xml index 90e217f8b..d22cf1545 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -68,6 +68,7 @@ hidden Due in Goal + Finished Overdue by New Task Tags: @@ -151,8 +152,8 @@ Information Question - Yes - No + Let\'s do it! + No, quit. Delete Delete this task? diff --git a/src/com/timsu/astrid/activities/TaskList.java b/src/com/timsu/astrid/activities/TaskList.java index 963e2e4ac..d5099560b 100644 --- a/src/com/timsu/astrid/activities/TaskList.java +++ b/src/com/timsu/astrid/activities/TaskList.java @@ -48,6 +48,7 @@ import com.timsu.astrid.data.tag.TagModelForView; import com.timsu.astrid.data.task.TaskController; import com.timsu.astrid.data.task.TaskIdentifier; import com.timsu.astrid.data.task.TaskModelForList; +import com.timsu.astrid.utilities.Notifications; /** Primary view for the Astrid Application. Lists all of the tasks in the @@ -89,8 +90,20 @@ public class TaskList extends Activity { static boolean shouldCloseInstance = false; + /* ====================================================================== + * ======================================================= initialization + * ====================================================================== */ + + @Override /** Called when loading up the activity for the first time */ - private void onLoad() { + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.task_list); + + // if we've never been started, do this + if(Notifications.areAlarmsSet()) + Notifications.scheduleAllAlarms(this); + shouldCloseInstance = false; controller = new TaskController(this); @@ -133,13 +146,43 @@ public class TaskList extends Activity { }); } + @Override + public boolean onCreateOptionsMenu(Menu menu) { + super.onCreateOptionsMenu(menu); + + MenuItem item; + + item = menu.add(Menu.NONE, INSERT_ID, Menu.NONE, + R.string.taskList_menu_insert); + item.setIcon(android.R.drawable.ic_menu_add); + item.setAlphabeticShortcut('n'); + + if(filterTag == null) { + item = menu.add(Menu.NONE, FILTERS_ID, Menu.NONE, + R.string.taskList_menu_filters); + item.setIcon(android.R.drawable.ic_menu_view); + item.setAlphabeticShortcut('f'); + + item = menu.add(Menu.NONE, TAGS_ID, Menu.NONE, + R.string.taskList_menu_tags); + item.setIcon(android.R.drawable.ic_menu_myplaces); + item.setAlphabeticShortcut('t'); + } + + return true; + } + + /* ====================================================================== + * ====================================================== populating list + * ====================================================================== */ + /** Fill in the Task List with our tasks */ private void fillData() { Resources r = getResources(); Cursor tasksCursor; - // load tags (again) + // load tags (they might've changed) tagMap = tagController.getAllTagsAsMap(this); Bundle extras = getIntent().getExtras(); if(extras != null && extras.containsKey(TAG_TOKEN)) { @@ -229,33 +272,32 @@ public class TaskList extends Activity { }); } + /* ====================================================================== + * ======================================================= event handlers + * ====================================================================== */ + @Override protected void onActivityResult(int requestCode, int resultCode, Intent intent) { + super.onActivityResult(requestCode, resultCode, intent); - if(resultCode == TaskView.RESULT_DISMISS) { - finish(); - return; - } - - fillData(); + // we would fill the list, but it is already happening on focus change } @Override public void onWindowFocusChanged(boolean hasFocus) { super.onWindowFocusChanged(hasFocus); - if(hasFocus) { // stuff might have changed... - if(shouldCloseInstance) // user wants to quit + // refresh, since stuff might have changed... + if(hasFocus) { + if(shouldCloseInstance) { // user wants to quit + shouldCloseInstance = false; finish(); - else + } else fillData(); - shouldCloseInstance = false; } } - // --- list adapter - private void createTask() { Intent intent = new Intent(this, TaskEdit.class); if(filterTag != null) @@ -335,42 +377,6 @@ public class TaskList extends Activity { return super.onMenuItemSelected(featureId, item); } - // --- creating stuff - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setContentView(R.layout.task_list); - - onLoad(); - } - - @Override - public boolean onCreateOptionsMenu(Menu menu) { - super.onCreateOptionsMenu(menu); - - MenuItem item; - - item = menu.add(Menu.NONE, INSERT_ID, Menu.NONE, - R.string.taskList_menu_insert); - item.setIcon(android.R.drawable.ic_menu_add); - item.setAlphabeticShortcut('n'); - - if(filterTag == null) { - item = menu.add(Menu.NONE, FILTERS_ID, Menu.NONE, - R.string.taskList_menu_filters); - item.setIcon(android.R.drawable.ic_menu_view); - item.setAlphabeticShortcut('f'); - - item = menu.add(Menu.NONE, TAGS_ID, Menu.NONE, - R.string.taskList_menu_tags); - item.setIcon(android.R.drawable.ic_menu_myplaces); - item.setAlphabeticShortcut('t'); - } - - return true; - } - @Override protected void onDestroy() { super.onDestroy(); diff --git a/src/com/timsu/astrid/activities/TaskListAdapter.java b/src/com/timsu/astrid/activities/TaskListAdapter.java index d5a19c8ff..aedcf77a5 100644 --- a/src/com/timsu/astrid/activities/TaskListAdapter.java +++ b/src/com/timsu/astrid/activities/TaskListAdapter.java @@ -118,33 +118,49 @@ public class TaskListAdapter extends ArrayAdapter { timer.setImageDrawable(r.getDrawable(R.drawable.ic_dialog_time)); progress.setChecked(task.isTaskCompleted()); - // due date - Date dueDate = task.getDefiniteDueDate(); - String dueString = ""; - if(dueDate == null || (task.getPreferredDueDate() != null && - task.getPreferredDueDate().before(dueDate))) { - // only prefix with "goal:" if the real deadline isn't overdue - if(task.getDefiniteDueDate() == null || task.getDefiniteDueDate(). - after(new Date())) - dueString = r.getString(R.string.taskList_goalPrefix) + " "; - dueDate = task.getPreferredDueDate(); - } - if(dueDate != null) { - int secondsLeft = (int)(dueDate.getTime() - - System.currentTimeMillis()) / 1000; - - if(secondsLeft > 0) - dueString += r.getString(R.string.taskList_dueIn) + " "; - else { - dueString += r.getString(R.string.taskList_overdueBy) + " "; - dueDateView.setTextColor(r.getColor(R.color.taskList_dueDateOverdue)); + // due date / completion date + if(task.isTaskCompleted()) { + if(task.getCompletionDate() != null) { + int secondsLeft = (int)(task.getCompletionDate().getTime() - + System.currentTimeMillis()) / 1000; + StringBuilder label = new StringBuilder(). + append(r.getString(R.string.taskList_completedPrefix)). + append(" "). + append(DateUtilities.getDurationString(r, Math.abs(secondsLeft), 1)). + append(r.getString(R.string.ago_suffix)); + dueDateView.setText(label); + dueDateView.setTextColor(r.getColor(R.color.taskList_completedDate)); + hasProperties = true; + } else + dueDateView.setVisibility(View.GONE); + } else { + Date dueDate = task.getDefiniteDueDate(); + String dueString = ""; + if(dueDate == null || (task.getPreferredDueDate() != null && + task.getPreferredDueDate().before(dueDate))) { + // only prefix with "goal:" if the real deadline isn't overdue + if(task.getDefiniteDueDate() == null || task.getDefiniteDueDate(). + after(new Date())) + dueString = r.getString(R.string.taskList_goalPrefix) + " "; + dueDate = task.getPreferredDueDate(); } + if(dueDate != null) { + int secondsLeft = (int)(dueDate.getTime() - + System.currentTimeMillis()) / 1000; + + if(secondsLeft > 0) + dueString += r.getString(R.string.taskList_dueIn) + " "; + else { + dueString += r.getString(R.string.taskList_overdueBy) + " "; + dueDateView.setTextColor(r.getColor(R.color.taskList_dueDateOverdue)); + } - dueString += DateUtilities.getDurationString(r, Math.abs(secondsLeft), 1); - dueDateView.setText(dueString); - hasProperties = true; - } else - dueDateView.setVisibility(View.GONE); + dueString += DateUtilities.getDurationString(r, Math.abs(secondsLeft), 1); + dueDateView.setText(dueString); + hasProperties = true; + } else + dueDateView.setVisibility(View.GONE); + } // tags List tags = hooks.getTagController().getTaskTags( diff --git a/src/com/timsu/astrid/activities/TaskViewNotifier.java b/src/com/timsu/astrid/activities/TaskViewNotifier.java index d05d2952e..0afa5b730 100644 --- a/src/com/timsu/astrid/activities/TaskViewNotifier.java +++ b/src/com/timsu/astrid/activities/TaskViewNotifier.java @@ -39,8 +39,8 @@ public class TaskViewNotifier extends TaskView { .setTitle(R.string.taskView_notifyTitle) .setMessage(response) .setIcon(android.R.drawable.ic_dialog_alert) - .setPositiveButton(R.string.yes, null) - .setNegativeButton(R.string.no, new DialogInterface.OnClickListener() { + .setPositiveButton(R.string.notify_yes, null) + .setNegativeButton(R.string.notify_no, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { setResult(RESULT_CANCELED); diff --git a/src/com/timsu/astrid/data/AbstractModel.java b/src/com/timsu/astrid/data/AbstractModel.java index 85cbc1a62..49173e9e8 100644 --- a/src/com/timsu/astrid/data/AbstractModel.java +++ b/src/com/timsu/astrid/data/AbstractModel.java @@ -36,13 +36,13 @@ public abstract class AbstractModel { */ /** User set values */ - protected ContentValues setValues = new ContentValues(); + protected ContentValues setValues = new ContentValues(); /** Cached values from database */ - private ContentValues values = new ContentValues(); + private ContentValues values = new ContentValues(); /** Cursor into the database */ - private Cursor cursor = null; + private Cursor cursor = null; // --- constructors @@ -82,6 +82,56 @@ public abstract class AbstractModel { return cursor; } + // --- checking against cached values + + protected void putIfChangedFromDatabase(String field, String newValue) { + if(values.containsKey(field)) { + String value = values.getAsString(field); + if(value == null) { + if(newValue == null) + return; + } else if(value.equals(newValue)) + return; + } + setValues.put(field, newValue); + } + + protected void putIfChangedFromDatabase(String field, Long newValue) { + if(values.containsKey(field)) { + Long value = values.getAsLong(field); + if(value == null) { + if(newValue == null) + return; + } else if(value.equals(newValue)) + return; + } + setValues.put(field, newValue); + } + + protected void putIfChangedFromDatabase(String field, Integer newValue) { + if(values.containsKey(field)) { + Integer value = values.getAsInteger(field); + if(value == null) { + if(newValue == null) + return; + } else if(value.equals(newValue)) + return; + } + setValues.put(field, newValue); + } + + protected void putIfChangedFromDatabase(String field, Double newValue) { + if(values.containsKey(field)) { + Double value = values.getAsDouble(field); + if(value == null) { + if(newValue == null) + return; + } else if(value.equals(newValue)) + return; + } + setValues.put(field, newValue); + } + // --- data retrieval for the different object types protected String retrieveString(String field) { diff --git a/src/com/timsu/astrid/data/task/AbstractTaskModel.java b/src/com/timsu/astrid/data/task/AbstractTaskModel.java index a4bcb26a6..a3642ff5a 100644 --- a/src/com/timsu/astrid/data/task/AbstractTaskModel.java +++ b/src/com/timsu/astrid/data/task/AbstractTaskModel.java @@ -166,6 +166,40 @@ public abstract class AbstractTaskModel extends AbstractModel { setElapsedSeconds((int) (getElapsedSeconds() + secondsElapsed)); } + + protected void prefetchData(String[] fields) { + for(String field : fields) { + if(field.equals(NAME)) + getName(); + else if(field.equals(NOTES)) + getNotes(); + else if(field.equals(PROGRESS_PERCENTAGE)) + getProgressPercentage(); + else if(field.equals(IMPORTANCE)) + getImportance(); + else if(field.equals(ESTIMATED_SECONDS)) + getEstimatedSeconds(); + else if(field.equals(ELAPSED_SECONDS)) + getElapsedSeconds(); + else if(field.equals(TIMER_START)) + getTimerStart(); + else if(field.equals(DEFINITE_DUE_DATE)) + getDefiniteDueDate(); + else if(field.equals(PREFERRED_DUE_DATE)) + getPreferredDueDate(); + else if(field.equals(HIDDEN_UNTIL)) + getHiddenUntil(); + else if(field.equals(BLOCKING_ON)) + getBlockingOn(); + else if(field.equals(NOTIFICATIONS)) + getNotificationIntervalSeconds(); + else if(field.equals(CREATION_DATE)) + getCreationDate(); + else if(field.equals(COMPLETION_DATE)) + getCompletionDate(); + } + } + // --- task identifier private TaskIdentifier identifier = null; @@ -266,15 +300,15 @@ public abstract class AbstractTaskModel extends AbstractModel { // --- setters protected void setName(String name) { - setValues.put(NAME, name); + putIfChangedFromDatabase(NAME, name); } protected void setNotes(String notes) { - setValues.put(NOTES, notes); + putIfChangedFromDatabase(NOTES, notes); } protected void setProgressPercentage(int progressPercentage) { - setValues.put(PROGRESS_PERCENTAGE, progressPercentage); + putIfChangedFromDatabase(PROGRESS_PERCENTAGE, progressPercentage); if(getProgressPercentage() != progressPercentage && progressPercentage == COMPLETE_PERCENTAGE) @@ -282,58 +316,58 @@ public abstract class AbstractTaskModel extends AbstractModel { } protected void setImportance(Importance importance) { - setValues.put(IMPORTANCE, importance.ordinal()); + putIfChangedFromDatabase(IMPORTANCE, importance.ordinal()); } protected void setEstimatedSeconds(Integer estimatedSeconds) { - setValues.put(ESTIMATED_SECONDS, estimatedSeconds); + putIfChangedFromDatabase(ESTIMATED_SECONDS, estimatedSeconds); } protected void setElapsedSeconds(int elapsedSeconds) { - setValues.put(ELAPSED_SECONDS, elapsedSeconds); + putIfChangedFromDatabase(ELAPSED_SECONDS, elapsedSeconds); } protected void setTimerStart(Date timerStart) { - putDate(setValues, TIMER_START, timerStart); + putDate(TIMER_START, timerStart); } protected void setDefiniteDueDate(Date definiteDueDate) { - putDate(setValues, DEFINITE_DUE_DATE, definiteDueDate); + putDate(DEFINITE_DUE_DATE, definiteDueDate); } protected void setPreferredDueDate(Date preferredDueDate) { - putDate(setValues, PREFERRED_DUE_DATE, preferredDueDate); + putDate(PREFERRED_DUE_DATE, preferredDueDate); } protected void setHiddenUntil(Date hiddenUntil) { - putDate(setValues, HIDDEN_UNTIL, hiddenUntil); + putDate(HIDDEN_UNTIL, hiddenUntil); } protected void setBlockingOn(TaskIdentifier blockingOn) { if(blockingOn == null || blockingOn.equals(getTaskIdentifier())) - setValues.put(BLOCKING_ON, (Integer)null); + putIfChangedFromDatabase(BLOCKING_ON, (Integer)null); else - setValues.put(BLOCKING_ON, blockingOn.getId()); + putIfChangedFromDatabase(BLOCKING_ON, blockingOn.getId()); } protected void setCreationDate(Date creationDate) { - putDate(setValues, CREATION_DATE, creationDate); + putDate(CREATION_DATE, creationDate); } protected void setCompletionDate(Date completionDate) { - putDate(setValues, COMPLETION_DATE, completionDate); + putDate(COMPLETION_DATE, completionDate); } protected void setNotificationIntervalSeconds(Integer intervalInSeconds) { - setValues.put(NOTIFICATIONS, intervalInSeconds); + putIfChangedFromDatabase(NOTIFICATIONS, intervalInSeconds); } // --- utility methods - static void putDate(ContentValues cv, String fieldName, Date date) { + protected void putDate(String fieldName, Date date) { if(date == null) - cv.put(fieldName, (Long)null); + putIfChangedFromDatabase(fieldName, (Long)null); else - cv.put(fieldName, date.getTime()); + putIfChangedFromDatabase(fieldName, date.getTime()); } } diff --git a/src/com/timsu/astrid/data/task/TaskController.java b/src/com/timsu/astrid/data/task/TaskController.java index 2539ca96b..e108217a5 100644 --- a/src/com/timsu/astrid/data/task/TaskController.java +++ b/src/com/timsu/astrid/data/task/TaskController.java @@ -23,6 +23,7 @@ import java.util.ArrayList; import java.util.List; import android.app.Activity; +import android.content.ContentValues; import android.content.Context; import android.database.Cursor; import android.database.SQLException; @@ -157,6 +158,15 @@ public class TaskController extends AbstractController { saveSucessful = newRow >= 0; } else { long id = task.getTaskIdentifier().getId(); + ContentValues values = task.getSetValues(); + + // set completion date + if(values.containsKey(AbstractTaskModel.PROGRESS_PERCENTAGE) && + values.getAsInteger(AbstractTaskModel.PROGRESS_PERCENTAGE) + == AbstractTaskModel.COMPLETE_PERCENTAGE) { + values.put(AbstractTaskModel.COMPLETION_DATE, System.currentTimeMillis()); + } + saveSucessful = database.update(TASK_TABLE_NAME, task.getSetValues(), KEY_ROWID + "=" + id, null) > 0; } @@ -214,7 +224,7 @@ public class TaskController extends AbstractController { public TaskModelForList fetchTaskForList(TaskIdentifier taskId) throws SQLException { long id = taskId.getId(); Cursor cursor = database.query(true, TASK_TABLE_NAME, - TaskModelForView.FIELD_LIST, + TaskModelForList.FIELD_LIST, KEY_ROWID + "=" + id, null, null, null, null, null); if (cursor != null) { cursor.moveToFirst(); diff --git a/src/com/timsu/astrid/data/task/TaskModelForList.java b/src/com/timsu/astrid/data/task/TaskModelForList.java index 90af5ac9c..32a5aabde 100644 --- a/src/com/timsu/astrid/data/task/TaskModelForList.java +++ b/src/com/timsu/astrid/data/task/TaskModelForList.java @@ -46,6 +46,7 @@ public class TaskModelForList extends AbstractTaskModel { DEFINITE_DUE_DATE, PREFERRED_DUE_DATE, PROGRESS_PERCENTAGE, + COMPLETION_DATE, HIDDEN_UNTIL, }; @@ -123,16 +124,7 @@ public class TaskModelForList extends AbstractTaskModel { public TaskModelForList(Cursor cursor) { super(cursor); - // prefetch every field - we can't lazy load with more than 1 - getElapsedSeconds(); - getDefiniteDueDate(); - getEstimatedSeconds(); - getHiddenUntil(); - getImportance(); - getName(); - getPreferredDueDate(); - getProgressPercentage(); - getTimerStart(); + prefetchData(FIELD_LIST); } // --- exposed getters and setters @@ -196,6 +188,11 @@ public class TaskModelForList extends AbstractTaskModel { return super.getTimerStart(); } + @Override + public Date getCompletionDate() { + return super.getCompletionDate(); + } + @Override public void setProgressPercentage(int progressPercentage) { super.setProgressPercentage(progressPercentage); diff --git a/src/com/timsu/astrid/data/task/TaskModelForNotify.java b/src/com/timsu/astrid/data/task/TaskModelForNotify.java index bf5a20a2b..dd77546b6 100644 --- a/src/com/timsu/astrid/data/task/TaskModelForNotify.java +++ b/src/com/timsu/astrid/data/task/TaskModelForNotify.java @@ -44,9 +44,8 @@ public class TaskModelForNotify extends AbstractTaskModel implements Notifiable public TaskModelForNotify(Cursor cursor) { super(cursor); - getNotificationIntervalSeconds(); - getHiddenUntil(); - isTaskCompleted(); + + prefetchData(FIELD_LIST); } // --- getters and setters diff --git a/src/com/timsu/astrid/utilities/Notifications.java b/src/com/timsu/astrid/utilities/Notifications.java index f00dc09fd..f8b06d945 100644 --- a/src/com/timsu/astrid/utilities/Notifications.java +++ b/src/com/timsu/astrid/utilities/Notifications.java @@ -46,6 +46,7 @@ public class Notifications extends BroadcastReceiver { public static final int FLAG_PREFERRED_DEADLINE = 2; private static Random random = new Random(); + private static boolean alarmsSet = false; /** Something we can create a notification for */ public interface Notifiable { @@ -93,6 +94,10 @@ public class Notifications extends BroadcastReceiver { return false; } + public static boolean areAlarmsSet() { + return alarmsSet; + } + public static void scheduleAllAlarms(Context context) { TaskController controller = new TaskController(context); controller.open(); @@ -101,6 +106,7 @@ public class Notifications extends BroadcastReceiver { for(TaskModelForNotify task : tasks) updateAlarm(context, task, false); + alarmsSet = true; controller.close(); } @@ -268,8 +274,7 @@ public class Notifications extends BroadcastReceiver { Resources r = context.getResources(); Intent notifyIntent = new Intent(context, TaskViewNotifier.class); - notifyIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - notifyIntent.setFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK); + notifyIntent.setFlags(Intent.FLAG_ACTIVITY_NO_HISTORY); notifyIntent.putExtra(TaskViewNotifier.LOAD_INSTANCE_TOKEN, id); notifyIntent.putExtra(TaskViewNotifier.FROM_NOTIFICATION_TOKEN, true); notifyIntent.putExtra(TaskViewNotifier.NOTIF_FLAGS_TOKEN, flags);