Got rid of taskview, now handling that function by expanding the list row. Appropriate hacks to notification. Also, new pink chat icon.

pull/14/head
Tim Su 16 years ago
parent 4e256262bc
commit c0cbd60444

@ -1,8 +1,8 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" <manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.timsu.astrid" package="com.timsu.astrid"
android:versionCode="74" android:versionCode="75"
android:versionName="2.2.0"> android:versionName="2.2.5">
<meta-data android:name="com.a0soft.gphone.aTrackDog.webURL" <meta-data android:name="com.a0soft.gphone.aTrackDog.webURL"
android:value="http://www.weloveastrid.com" /> android:value="http://www.weloveastrid.com" />
@ -14,22 +14,15 @@
<application android:icon="@drawable/icon" android:label="@string/app_name"> <application android:icon="@drawable/icon" android:label="@string/app_name">
<activity android:name=".activities.TaskList" <activity android:name=".activities.TaskList">
android:label="@string/app_name">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.MAIN" /> <action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" /> <category android:name="android.intent.category.LAUNCHER" />
</intent-filter> </intent-filter>
</activity> </activity>
<activity android:name=".activities.TaskView" <activity android:name=".activities.TaskListNotify"
android:theme="@android:style/Theme.Dialog"> android:launchMode="singleTop" />
</activity>
<activity android:name=".activities.TaskViewNotifier"
android:excludeFromRecents="true"
android:theme="@android:style/Theme.Dialog"
android:launchMode="singleTask" />
<activity android:name=".activities.TaskEdit" <activity android:name=".activities.TaskEdit"
android:label="@string/taskEdit_label"/> android:label="@string/taskEdit_label"/>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

@ -79,39 +79,48 @@
color="@color/taskList_dueDate" color="@color/taskList_dueDate"
android:singleLine="true"/> android:singleLine="true"/>
<!-- elapsed / estimated time --> <!-- other details (gray) -->
<TextView android:id="@+id/text_times" <TextView android:id="@+id/details"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_width="wrap_content" android:layout_width="wrap_content"
style="@style/TextAppearance.TaskList_Detail" style="@style/TextAppearance.TaskList_Detail"/>
android:singleLine="true"/>
<!-- repeats -->
<TextView android:id="@+id/text_repeats"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
style="@style/TextAppearance.TaskList_Detail"
android:singleLine="true"/>
<!-- reminders -->
<TextView android:id="@+id/text_reminders"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
style="@style/TextAppearance.TaskList_Detail"
android:singleLine="true"/>
<!-- tags --> <LinearLayout android:id="@+id/expanded_layout"
<TextView android:id="@+id/text_tags" android:orientation="vertical"
android:layout_height="wrap_content" android:paddingTop="5dip"
android:layout_width="wrap_content" android:layout_width="fill_parent"
style="@style/TextAppearance.TaskList_Detail" android:layout_height="wrap_content">
android:singleLine="true"/>
<!-- notes --> <View android:background="@android:drawable/divider_horizontal_dark"
<TextView android:id="@+id/text_notes" android:layout_width="fill_parent"
android:layout_height="wrap_content" android:layout_height="1dip"/>
android:layout_width="wrap_content"
style="@style/TextAppearance.TaskList_Detail"/> <TextView android:id="@+id/expanded_details"
android:paddingTop="5dip"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
style="@style/TextAppearance.TaskList_Detail"/>
<LinearLayout android:id="@+id/expanded_buttons"
android:orientation="horizontal"
android:paddingTop="5dip"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<Button android:id="@+id/timer"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_weight="0.5"/>
<Button android:id="@+id/edit"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_weight="0.5"
android:text="@string/edit_label"/>
</LinearLayout>
</LinearLayout>
</LinearLayout> </LinearLayout>

@ -1,220 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
ASTRID: Android's Simple Task Recording Dashboard
Copyright (c) 2009 Tim Su
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-->
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/scroll_view"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<LinearLayout android:id="@+id/view_layout"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<TextView android:id="@+id/name"
android:layout_height="wrap_content"
android:layout_width="fill_parent"
android:textAppearance="?android:attr/textAppearanceLargeInverse"
android:gravity="center_horizontal"
android:padding="5dip"/>
<!-- Button Row -->
<LinearLayout android:id="@+id/button_layout"
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:paddingTop="15dip">
<Button android:id="@+id/timerButton"
android:gravity="center"
android:layout_weight="0.5"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/startTimer_label"/>
<Button android:id="@+id/progress"
android:gravity="center"
android:layout_weight="0.5"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</LinearLayout>
<!-- Elapsed Time -->
<LinearLayout
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:paddingLeft="5px"
android:paddingTop="5px"
android:text="@string/taskView_elapsed"
android:textAppearance="?android:attr/textAppearanceLarge" />
<TextView android:id="@+id/cell_elapsed"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:paddingLeft="5px"
android:textAppearance="?android:attr/textAppearanceSmall"
android:colorForeground="@color/view_table_values" />
</LinearLayout>
<!-- Estimated Time -->
<LinearLayout
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<ImageView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:padding="5px"
android:src="@android:drawable/divider_horizontal_bright" />
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:paddingLeft="5px"
android:text="@string/taskView_estimated"
android:textAppearance="?android:attr/textAppearanceLarge" />
<TextView android:id="@+id/cell_estimated"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:paddingLeft="5px"
android:textAppearance="?android:attr/textAppearanceSmall"
android:colorForeground="@color/view_table_values" />
</LinearLayout>
<!-- Notes -->
<LinearLayout
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<ImageView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:padding="5px"
android:src="@android:drawable/divider_horizontal_bright" />
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:paddingLeft="5px"
android:text="@string/taskView_notes"
android:textAppearance="?android:attr/textAppearanceLarge" />
<TextView android:id="@+id/cell_notes"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:paddingLeft="5px"
android:autoLink="all"
android:textAppearance="?android:attr/textAppearanceSmall"
android:colorForeground="@color/view_table_values" />
</LinearLayout>
<!-- Absolute Deadline -->
<LinearLayout
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<ImageView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:padding="5px"
android:src="@android:drawable/divider_horizontal_bright" />
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:paddingLeft="5px"
android:text="@string/taskView_definiteDueDate"
android:textAppearance="?android:attr/textAppearanceLarge" />
<TextView android:id="@+id/cell_definiteDueDate"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:paddingLeft="5px"
android:textAppearance="?android:attr/textAppearanceSmall"
android:colorForeground="@color/view_table_values" />
</LinearLayout>
<!-- Goal Deadline -->
<LinearLayout
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<ImageView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:padding="5px"
android:src="@android:drawable/divider_horizontal_bright" />
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:paddingLeft="5px"
android:text="@string/taskView_preferredDueDate"
android:textAppearance="?android:attr/textAppearanceLarge" />
<TextView android:id="@+id/cell_preferredDueDate"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:paddingLeft="5px"
android:textAppearance="?android:attr/textAppearanceSmall"
android:colorForeground="@color/view_table_values" />
</LinearLayout>
<!-- Creation Date -->
<LinearLayout
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<ImageView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:padding="5px"
android:src="@android:drawable/divider_horizontal_bright" />
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:paddingLeft="5px"
android:text="@string/taskView_creationDate"
android:textAppearance="?android:attr/textAppearanceLarge" />
<TextView android:id="@+id/cell_creationDate"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:paddingLeft="5px"
android:textAppearance="?android:attr/textAppearanceSmall"
android:colorForeground="@color/view_table_values" />
</LinearLayout>
<ImageView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:padding="5px" />
<Button android:id="@+id/edit"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/edit_label"/>
</LinearLayout>
</ScrollView>

@ -56,16 +56,16 @@
<item quantity="other">%d Tags</item> <item quantity="other">%d Tags</item>
</plurals> </plurals>
<!-- Time Constants --> <!-- Time Constants -->
<string name="daysVertical">D\na\ny\ns</string>
<string name="hoursVertical">H\no\nu\nr\ns</string>
<plurals name="Ndays"> <plurals name="Ndays">
<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="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="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>
@ -74,6 +74,19 @@
<item quantity="one">1 Second</item> <item quantity="one">1 Second</item>
<item quantity="other">%d Seconds</item> <item quantity="other">%d Seconds</item>
</plurals> </plurals>
<plurals name="NhoursShort">
<item quantity="one">1 Hr</item>
<item quantity="other">%d Hrs</item>
</plurals>
<plurals name="NminutesShort">
<item quantity="one">1 Min</item>
<item quantity="other">%d Min</item>
</plurals>
<plurals name="NsecondsShort">
<item quantity="one">1 Sec</item>
<item quantity="other">%d Sec</item>
</plurals>
<string name="ago_suffix">Ago</string>
<!-- TaskList --> <!-- TaskList -->
<skip /> <skip />
@ -91,9 +104,10 @@
<string name="taskList_elapsedTimePrefix">Spent:</string> <string name="taskList_elapsedTimePrefix">Spent:</string>
<string name="taskList_periodicReminderPrefix">Reminder Every</string> <string name="taskList_periodicReminderPrefix">Reminder Every</string>
<string name="taskList_repeatPrefix">Repeats Every</string> <string name="taskList_repeatPrefix">Repeats Every</string>
<string name="taskList_alarmSuffix">Set</string> <string name="taskList_alarmPrefix">Next Alarm:</string>
<string name="taskList_tagsPrefix">Tags:</string> <string name="taskList_tagsPrefix">Tags:</string>
<string name="taskList_notesPrefix">Notes:</string> <string name="taskList_notesPrefix">Notes:</string>
<string name="taskList_createdPrefix">Created:</string>
<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>
@ -175,30 +189,18 @@ If you don\'t want to see the new task right after you complete the old one, you
<!-- buttons --> <!-- buttons -->
<string name="save_label">Save</string> <string name="save_label">Save</string>
<string name="discard_label">Discard</string> <string name="discard_label">Discard</string>
<string name="edit_label">Edit</string>
<string name="delete_label">Delete</string> <string name="delete_label">Delete</string>
<string name="blank_button_title">Click to Set</string> <string name="blank_button_title">Click to Set</string>
<string name="startTimer_label">Start Timer</string>
<string name="stopTimer_label">Stop Timer</string>
<string name="taskEdit_menu_save">Save</string> <string name="taskEdit_menu_save">Save</string>
<!-- TaskView --> <!-- TaskView -->
<skip /> <skip />
<string name="taskView_title">Astrid: Task Properties</string>
<string name="taskView_notifyTitle">Astrid says...</string> <string name="taskView_notifyTitle">Astrid says...</string>
<string name="startTimer_label">Start Timer</string>
<string name="stopTimer_label">Stop Timer</string>
<string name="progress_suffix">% Done</string>
<string name="edit_label">Edit Task</string>
<string name="taskView_elapsed">Elapsed Time</string>
<string name="taskView_estimated">Estimated Time</string>
<string name="taskView_definiteDueDate">Absolute Deadline</string>
<string name="taskView_preferredDueDate">Goal Deadline</string>
<string name="taskView_creationDate">Creation Date</string>
<string name="taskView_tags">Tags</string>
<string name="taskView_notes">Task Notes</string>
<string name="overdue_suffix"> Overdue</string>
<string name="ago_suffix"> Ago</string>
<string name="progress_dialog">% of Task Finished</string>
<!-- Tag List --> <!-- Tag List -->
<skip /> <skip />
@ -257,9 +259,10 @@ Wish me luck!\n
<skip /> <skip />
<string name="information_title">Information</string> <string name="information_title">Information</string>
<string name="question_title">Question</string> <string name="question_title">Question</string>
<string name="notify_yes">Let\'s do it!</string> <string name="notify_yes">View Task</string>
<string name="notify_snooze">Snooze!</string> <string name="notify_done">Already Done!</string>
<string name="notify_no">No, quit.</string> <string name="notify_snooze">Snooze</string>
<string name="notify_no">Quit</string>
<string name="notify_snooze_title">Hours/minutes to snooze?</string> <string name="notify_snooze_title">Hours/minutes to snooze?</string>
<string name="delete_title">Delete</string> <string name="delete_title">Delete</string>

@ -42,11 +42,6 @@
<PreferenceScreen <PreferenceScreen
android:title="@string/displayedFields_PrefScreen_Title" android:title="@string/displayedFields_PrefScreen_Title"
android:summary="@string/displayedFields_PrefScreen_Desc"> android:summary="@string/displayedFields_PrefScreen_Desc">
<CheckBoxPreference
android:key="@string/prefs_titleVisible"
android:title="@string/prefs_titleVisible_title"
android:summary="@string/prefs_titleVisible_desc"
android:defaultValue="@string/prefs_titleVisible_default" />
<CheckBoxPreference <CheckBoxPreference
android:key="@string/prefs_deadlineVisible" android:key="@string/prefs_deadlineVisible"
android:title="@string/prefs_deadlineVisible_title" android:title="@string/prefs_deadlineVisible_title"

@ -8,14 +8,12 @@ import android.content.res.Resources;
import android.database.Cursor; import android.database.Cursor;
import android.os.Bundle; import android.os.Bundle;
import android.util.Log; import android.util.Log;
import android.view.ContextMenu;
import android.view.GestureDetector; import android.view.GestureDetector;
import android.view.KeyEvent; import android.view.KeyEvent;
import android.view.Menu; import android.view.Menu;
import android.view.MenuItem; import android.view.MenuItem;
import android.view.MotionEvent; import android.view.MotionEvent;
import android.view.View; import android.view.View;
import android.view.ContextMenu.ContextMenuInfo;
import android.view.GestureDetector.SimpleOnGestureListener; import android.view.GestureDetector.SimpleOnGestureListener;
import android.view.animation.Animation; import android.view.animation.Animation;
import android.view.animation.AnimationUtils; import android.view.animation.AnimationUtils;
@ -31,21 +29,21 @@ import com.timsu.astrid.utilities.StartupReceiver;
/** /**
* Main activity uses a ViewFlipper to flip between child views. * Main activity uses a ViewFlipper to flip between child views.
* *
* @author Tim Su (timsu@stanfordalumni.org) * @author Tim Su (timsu@stanfordalumni.org)
*/ */
public class TaskList extends Activity { public class TaskList extends Activity {
/** /**
* Interface for views that are displayed from the main view page * Interface for views that are displayed from the main view page
* *
* @author timsu * @author timsu
*/ */
abstract public static class SubActivity { abstract public static class SubActivity {
private TaskList parent; private TaskList parent;
private ActivityCode code; private ActivityCode code;
private View view; private View view;
public SubActivity(TaskList parent, ActivityCode code, View view) { public SubActivity(TaskList parent, ActivityCode code, View view) {
this.parent = parent; this.parent = parent;
this.code = code; this.code = code;
@ -54,105 +52,107 @@ public class TaskList extends Activity {
} }
// --- pass-through to activity listeners // --- pass-through to activity listeners
/** Called when this subactivity is displayed to the user */ /** Called when this subactivity is displayed to the user */
void onDisplay(Bundle variables) { void onDisplay(Bundle variables) {
// //
} }
boolean onPrepareOptionsMenu(Menu menu) { boolean onPrepareOptionsMenu(Menu menu) {
return false; return false;
} }
void onActivityResult(int requestCode, int resultCode, Intent data) { void onActivityResult(int requestCode, int resultCode, Intent data) {
// //
} }
boolean onMenuItemSelected(int featureId, MenuItem item) { boolean onMenuItemSelected(int featureId, MenuItem item) {
return false; return false;
} }
void onWindowFocusChanged(boolean hasFocus) { void onWindowFocusChanged(boolean hasFocus) {
// //
} }
boolean onKeyDown(int keyCode, KeyEvent event) { boolean onKeyDown(int keyCode, KeyEvent event) {
return false; return false;
} }
// --- pass-through to activity methods // --- pass-through to activity methods
public Resources getResources() { public Resources getResources() {
return parent.getResources(); return parent.getResources();
} }
public View findViewById(int id) { public View findViewById(int id) {
return view.findViewById(id); return view.findViewById(id);
} }
public void startManagingCursor(Cursor c) { public void startManagingCursor(Cursor c) {
parent.startManagingCursor(c); parent.startManagingCursor(c);
} }
public void setTitle(CharSequence title) { public void setTitle(CharSequence title) {
parent.setTitle(title); parent.setTitle(title);
} }
public void closeActivity() { public void closeActivity() {
parent.finish(); parent.finish();
} }
public void launchActivity(Intent intent, int requestCode) { public void launchActivity(Intent intent, int requestCode) {
parent.startActivityForResult(intent, requestCode); parent.startActivityForResult(intent, requestCode);
} }
// --- helper methods // --- helper methods
public Activity getParent() { public Activity getParent() {
return parent; return parent;
} }
public TaskController getTaskController() { public TaskController getTaskController() {
return parent.taskController; return parent.taskController;
} }
public TagController getTagController() { public TagController getTagController() {
return parent.tagController; return parent.tagController;
} }
public View.OnTouchListener getGestureListener() { public View.OnTouchListener getGestureListener() {
return parent.gestureListener; return parent.gestureListener;
} }
public void switchToActivity(ActivityCode activity, Bundle state) { public void switchToActivity(ActivityCode activity, Bundle state) {
parent.switchToActivity(activity, state); parent.switchToActivity(activity, state);
} }
// --- internal methods // --- internal methods
protected ActivityCode getActivityCode() { protected ActivityCode getActivityCode() {
return code; return code;
} }
protected View getView() { protected View getView() {
return view; return view;
} }
} }
/* ====================================================================== /* ======================================================================
* ======================================================= internal stuff * ======================================================= internal stuff
* ====================================================================== */ * ====================================================================== */
public enum ActivityCode { public enum ActivityCode {
TASK_LIST, TASK_LIST,
TAG_LIST, TAG_LIST,
TASK_LIST_W_TAG TASK_LIST_W_TAG
}; };
private static final String TAG_LAST_ACTIVITY = "l"; private static final String LAST_ACTIVITY_TAG = "l";
private static final String TAG_LAST_BUNDLE = "b"; private static final String LAST_BUNDLE_TAG = "b";
public static final String VARIABLES_TAG = "v";
private static final int FLING_DIST_THRESHOLD = 100; private static final int FLING_DIST_THRESHOLD = 100;
private static final int FLING_VEL_THRESHOLD = 300; private static final int FLING_VEL_THRESHOLD = 300;
@ -164,20 +164,20 @@ public class TaskList extends Activity {
private SubActivity tagList; private SubActivity tagList;
private SubActivity taskListWTag; private SubActivity taskListWTag;
private Bundle lastActivityBundle; private Bundle lastActivityBundle;
// animations // animations
private Animation mInAnimationForward; private Animation mInAnimationForward;
private Animation mOutAnimationForward; private Animation mOutAnimationForward;
private Animation mInAnimationBackward; private Animation mInAnimationBackward;
private Animation mOutAnimationBackward; private Animation mOutAnimationBackward;
// data controllers // data controllers
private TaskController taskController; private TaskController taskController;
private TagController tagController; private TagController tagController;
// static variables // static variables
static boolean shouldCloseInstance = false; static boolean shouldCloseInstance = false;
@Override @Override
/** Called when loading up the activity for the first time */ /** Called when loading up the activity for the first time */
public void onCreate(Bundle savedInstanceState) { public void onCreate(Bundle savedInstanceState) {
@ -195,13 +195,16 @@ public class TaskList extends Activity {
Synchronizer.setTaskController(taskController); Synchronizer.setTaskController(taskController);
setupUIComponents(); setupUIComponents();
if(savedInstanceState != null && savedInstanceState.containsKey(TAG_LAST_ACTIVITY)) { if(savedInstanceState != null && savedInstanceState.containsKey(LAST_ACTIVITY_TAG)) {
viewFlipper.setDisplayedChild(savedInstanceState.getInt(TAG_LAST_ACTIVITY)); viewFlipper.setDisplayedChild(savedInstanceState.getInt(LAST_ACTIVITY_TAG));
Bundle variables = savedInstanceState.getBundle(TAG_LAST_BUNDLE); Bundle variables = savedInstanceState.getBundle(LAST_BUNDLE_TAG);
getCurrentSubActivity().onDisplay(variables); getCurrentSubActivity().onDisplay(variables);
} else { } else {
getCurrentSubActivity().onDisplay(null); Bundle variables = null;
if(getIntent().hasExtra(VARIABLES_TAG))
variables = getIntent().getBundleExtra(VARIABLES_TAG);
getCurrentSubActivity().onDisplay(variables);
} }
// auto sync if requested // auto sync if requested
@ -215,23 +218,23 @@ public class TaskList extends Activity {
} }
} }
} }
/** Set up user interface components */ /** Set up user interface components */
private void setupUIComponents() { private void setupUIComponents() {
gestureDetector = new GestureDetector(new AstridGestureDetector()); gestureDetector = new GestureDetector(new AstridGestureDetector());
viewFlipper = (ViewFlipper)findViewById(R.id.main); viewFlipper = (ViewFlipper)findViewById(R.id.main);
taskList = new TaskListSubActivity(this, ActivityCode.TASK_LIST, taskList = new TaskListSubActivity(this, ActivityCode.TASK_LIST,
findViewById(R.id.tasklist_layout)); findViewById(R.id.tasklist_layout));
tagList = new TagListSubActivity(this, ActivityCode.TAG_LIST, tagList = new TagListSubActivity(this, ActivityCode.TAG_LIST,
findViewById(R.id.taglist_layout)); findViewById(R.id.taglist_layout));
taskListWTag = new TaskListSubActivity(this, ActivityCode.TASK_LIST_W_TAG, taskListWTag = new TaskListSubActivity(this, ActivityCode.TASK_LIST_W_TAG,
findViewById(R.id.tasklistwtag_layout)); findViewById(R.id.tasklistwtag_layout));
mInAnimationForward = AnimationUtils.loadAnimation(this, R.anim.slide_left_in); mInAnimationForward = AnimationUtils.loadAnimation(this, R.anim.slide_left_in);
mOutAnimationForward = AnimationUtils.loadAnimation(this, R.anim.slide_left_out); mOutAnimationForward = AnimationUtils.loadAnimation(this, R.anim.slide_left_out);
mInAnimationBackward = AnimationUtils.loadAnimation(this, R.anim.slide_right_in); mInAnimationBackward = AnimationUtils.loadAnimation(this, R.anim.slide_right_in);
mOutAnimationBackward = AnimationUtils.loadAnimation(this, R.anim.slide_right_out); mOutAnimationBackward = AnimationUtils.loadAnimation(this, R.anim.slide_right_out);
gestureListener = new View.OnTouchListener() { gestureListener = new View.OnTouchListener() {
public boolean onTouch(View v, MotionEvent event) { public boolean onTouch(View v, MotionEvent event) {
if (gestureDetector.onTouchEvent(event)) { if (gestureDetector.onTouchEvent(event)) {
@ -241,7 +244,7 @@ public class TaskList extends Activity {
} }
}; };
} }
private class AstridGestureDetector extends SimpleOnGestureListener { private class AstridGestureDetector extends SimpleOnGestureListener {
@Override @Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
@ -252,7 +255,7 @@ public class TaskList extends Activity {
// flick R to L // flick R to L
if(e1.getX() - e2.getX() > FLING_DIST_THRESHOLD && if(e1.getX() - e2.getX() > FLING_DIST_THRESHOLD &&
Math.abs(velocityX) > FLING_VEL_THRESHOLD) { Math.abs(velocityX) > FLING_VEL_THRESHOLD) {
switch(getCurrentSubActivity().getActivityCode()) { switch(getCurrentSubActivity().getActivityCode()) {
case TASK_LIST: case TASK_LIST:
switchToActivity(ActivityCode.TAG_LIST, null); switchToActivity(ActivityCode.TAG_LIST, null);
@ -288,10 +291,10 @@ public class TaskList extends Activity {
/* ====================================================================== /* ======================================================================
* ==================================================== subactivity stuff * ==================================================== subactivity stuff
* ====================================================================== */ * ====================================================================== */
private void switchToActivity(ActivityCode activity, Bundle variables) { private void switchToActivity(ActivityCode activity, Bundle variables) {
closeOptionsMenu(); closeOptionsMenu();
// and flip to them // and flip to them
switch(getCurrentSubActivity().getActivityCode()) { switch(getCurrentSubActivity().getActivityCode()) {
case TASK_LIST: case TASK_LIST:
@ -305,7 +308,7 @@ public class TaskList extends Activity {
viewFlipper.setDisplayedChild(taskListWTag.code.ordinal()); viewFlipper.setDisplayedChild(taskListWTag.code.ordinal());
} }
break; break;
case TAG_LIST: case TAG_LIST:
switch(activity) { switch(activity) {
case TASK_LIST: case TASK_LIST:
@ -320,7 +323,7 @@ public class TaskList extends Activity {
break; break;
} }
break; break;
case TASK_LIST_W_TAG: case TASK_LIST_W_TAG:
viewFlipper.setInAnimation(mInAnimationBackward); viewFlipper.setInAnimation(mInAnimationBackward);
viewFlipper.setOutAnimation(mOutAnimationBackward); viewFlipper.setOutAnimation(mOutAnimationBackward);
@ -333,7 +336,7 @@ public class TaskList extends Activity {
} }
break; break;
} }
// initialize the components // initialize the components
switch(activity) { switch(activity) {
case TASK_LIST: case TASK_LIST:
@ -345,25 +348,25 @@ public class TaskList extends Activity {
case TASK_LIST_W_TAG: case TASK_LIST_W_TAG:
taskListWTag.onDisplay(variables); taskListWTag.onDisplay(variables);
} }
lastActivityBundle = variables; lastActivityBundle = variables;
} }
private SubActivity getCurrentSubActivity() { private SubActivity getCurrentSubActivity() {
return (SubActivity)viewFlipper.getCurrentView().getTag(); return (SubActivity)viewFlipper.getCurrentView().getTag();
} }
/* ====================================================================== /* ======================================================================
* ======================================================= event handling * ======================================================= event handling
* ====================================================================== */ * ====================================================================== */
@Override @Override
protected void onSaveInstanceState(Bundle outState) { protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState); super.onSaveInstanceState(outState);
outState.putInt(TAG_LAST_ACTIVITY, getCurrentSubActivity().code.ordinal()); outState.putInt(LAST_ACTIVITY_TAG, getCurrentSubActivity().code.ordinal());
outState.putBundle(TAG_LAST_BUNDLE, lastActivityBundle); outState.putBundle(LAST_BUNDLE_TAG, lastActivityBundle);
} }
@Override @Override
public boolean onKeyDown(int keyCode, KeyEvent event) { public boolean onKeyDown(int keyCode, KeyEvent event) {
if(getCurrentSubActivity().onKeyDown(keyCode, event)) if(getCurrentSubActivity().onKeyDown(keyCode, event))
@ -371,7 +374,7 @@ public class TaskList extends Activity {
else else
return super.onKeyDown(keyCode, event); return super.onKeyDown(keyCode, event);
} }
@Override @Override
public boolean onPrepareOptionsMenu(Menu menu) { public boolean onPrepareOptionsMenu(Menu menu) {
menu.clear(); menu.clear();
@ -381,7 +384,7 @@ public class TaskList extends Activity {
@Override @Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) { protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data); super.onActivityResult(requestCode, resultCode, data);
if(resultCode == Constants.RESULT_GO_HOME) { if(resultCode == Constants.RESULT_GO_HOME) {
switchToActivity(ActivityCode.TASK_LIST, null); switchToActivity(ActivityCode.TASK_LIST, null);
} else } else
@ -391,13 +394,13 @@ public class TaskList extends Activity {
@Override @Override
public void onWindowFocusChanged(boolean hasFocus) { public void onWindowFocusChanged(boolean hasFocus) {
super.onWindowFocusChanged(hasFocus); super.onWindowFocusChanged(hasFocus);
if(hasFocus && shouldCloseInstance) { // user wants to quit if(hasFocus && shouldCloseInstance) { // user wants to quit
finish(); finish();
} else } else
getCurrentSubActivity().onWindowFocusChanged(hasFocus); getCurrentSubActivity().onWindowFocusChanged(hasFocus);
} }
@Override @Override
public boolean onMenuItemSelected(int featureId, MenuItem item) { public boolean onMenuItemSelected(int featureId, MenuItem item) {
if(getCurrentSubActivity().onMenuItemSelected(featureId, item)) if(getCurrentSubActivity().onMenuItemSelected(featureId, item))
@ -405,7 +408,7 @@ public class TaskList extends Activity {
else else
return super.onMenuItemSelected(featureId, item); return super.onMenuItemSelected(featureId, item);
} }
@Override @Override
public boolean onTouchEvent(MotionEvent event) { public boolean onTouchEvent(MotionEvent event) {
if (gestureDetector.onTouchEvent(event)) if (gestureDetector.onTouchEvent(event))

@ -17,6 +17,8 @@
*/ */
package com.timsu.astrid.activities; package com.timsu.astrid.activities;
import java.text.Format;
import java.text.SimpleDateFormat;
import java.util.Date; import java.util.Date;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
@ -38,9 +40,11 @@ import android.view.ContextMenu.ContextMenuInfo;
import android.view.View.OnCreateContextMenuListener; import android.view.View.OnCreateContextMenuListener;
import android.view.View.OnKeyListener; import android.view.View.OnKeyListener;
import android.widget.ArrayAdapter; import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.CheckBox; import android.widget.CheckBox;
import android.widget.CompoundButton; import android.widget.CompoundButton;
import android.widget.ImageView; import android.widget.ImageView;
import android.widget.ListView;
import android.widget.TextView; import android.widget.TextView;
import android.widget.CompoundButton.OnCheckedChangeListener; import android.widget.CompoundButton.OnCheckedChangeListener;
@ -50,13 +54,14 @@ import com.timsu.astrid.data.enums.Importance;
import com.timsu.astrid.data.tag.TagController; import com.timsu.astrid.data.tag.TagController;
import com.timsu.astrid.data.tag.TagModelForView; import com.timsu.astrid.data.tag.TagModelForView;
import com.timsu.astrid.data.task.TaskController; 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.data.task.TaskModelForList;
import com.timsu.astrid.data.task.AbstractTaskModel.RepeatInfo; import com.timsu.astrid.data.task.AbstractTaskModel.RepeatInfo;
import com.timsu.astrid.utilities.DateUtilities; import com.timsu.astrid.utilities.DateUtilities;
import com.timsu.astrid.utilities.Preferences; import com.timsu.astrid.utilities.Preferences;
import com.timsu.astrid.utilities.TaskFieldsVisibility; import com.timsu.astrid.utilities.TaskFieldsVisibility;
/** /**
* Adapter for displaying a list of TaskModelForList entities * Adapter for displaying a list of TaskModelForList entities
* *
* @author timsu * @author timsu
@ -78,9 +83,15 @@ public class TaskListAdapter extends ArrayAdapter<TaskModelForList> {
private static final int KEY_TIMES = 5; private static final int KEY_TIMES = 5;
private static final int KEY_TAGS = 6; private static final int KEY_TAGS = 6;
private static final int KEY_HIDDEN = 7; private static final int KEY_HIDDEN = 7;
private static final int KEY_EXPANDED = 8;
private static final int KEY_CREATION = 9;
private static final String CACHE_TRUE = "y"; private static final String CACHE_TRUE = "y";
// alarm date formatter
private static final Format alarmFormat = new SimpleDateFormat(
"MM/dd hh:mm");
/** Threshold under which to display a task as red, in millis */ /** Threshold under which to display a task as red, in millis */
private static final long TASK_OVERDUE_THRESHOLD = 30 * 60 * 1000L; private static final long TASK_OVERDUE_THRESHOLD = 30 * 60 * 1000L;
@ -96,8 +107,8 @@ public class TaskListAdapter extends ArrayAdapter<TaskModelForList> {
private TaskModelForList recentlyCompleted = null; private TaskModelForList recentlyCompleted = null;
/** /**
* Callback interface for interacting with parent activity * Call-back interface for interacting with parent activity
* *
* @author timsu * @author timsu
* *
*/ */
@ -108,11 +119,15 @@ public class TaskListAdapter extends ArrayAdapter<TaskModelForList> {
TagController tagController(); TagController tagController();
void performItemClick(View v, int position); void performItemClick(View v, int position);
void onCreatedTaskListView(View v, TaskModelForList task); void onCreatedTaskListView(View v, TaskModelForList task);
void editItem(TaskModelForList task);
void toggleTimerOnItem(TaskModelForList task);
void setSelectedItem(TaskIdentifier taskId);
} }
/** /**
* Constructor * Constructor
* *
* @param activity * @param activity
* @param context * @param context
* @param resource * @param resource
@ -134,6 +149,20 @@ public class TaskListAdapter extends ArrayAdapter<TaskModelForList> {
alarmController = new AlertController(activity); alarmController = new AlertController(activity);
} }
/** Toggle the expanded state of this task */
public void toggleExpanded(View view, TaskModelForList task) {
if(CACHE_TRUE.equals(task.getCachedLabel(KEY_EXPANDED))) {
task.putCachedLabel(KEY_EXPANDED, null);
hooks.setSelectedItem(null);
} else {
task.putCachedLabel(KEY_EXPANDED, CACHE_TRUE);
hooks.setSelectedItem(task.getTaskIdentifier());
}
setFieldContentsAndVisibility(view, task);
((ListView)view.getParent()).setSelection(objects.indexOf(task));
}
// ---------------------------------------------------------------------- // ----------------------------------------------------------------------
// --- code for setting up each view // --- code for setting up each view
// ---------------------------------------------------------------------- // ----------------------------------------------------------------------
@ -145,16 +174,17 @@ public class TaskListAdapter extends ArrayAdapter<TaskModelForList> {
if(view == null) { if(view == null) {
view = inflater.inflate(resource, parent, false); view = inflater.inflate(resource, parent, false);
initializeView(view); initializeView(view);
addListeners(view);
} }
setupView(view, objects.get(position)); setupView(view, objects.get(position));
addListeners(position, view);
return view; return view;
} }
/** /**
* Perform initial setup on the row * Perform initial setup on the row, stuff is constant for every row
* *
* @param view * @param view
*/ */
private void initializeView(View view) { private void initializeView(View view) {
@ -165,28 +195,31 @@ public class TaskListAdapter extends ArrayAdapter<TaskModelForList> {
/** /**
* Setup the given view for the specified task * Setup the given view for the specified task
* *
* @param view * @param view
* @param task * @param task
*/ */
private void setupView(View view, final TaskModelForList task) { private void setupView(View view, final TaskModelForList task) {
Resources r = activity.getResources(); Resources r = activity.getResources();
final CheckBox progress = ((CheckBox)view.findViewById(R.id.cb1));
final ImageView timer = ((ImageView)view.findViewById(R.id.imageLeft));
final TextView name = ((TextView)view.findViewById(R.id.task_name));
view.setTag(task); view.setTag(task);
progress.setTag(task); setFieldContentsAndVisibility(view, task);
if(task.getTimerStart() != null) final CheckBox progress = ((CheckBox)view.findViewById(R.id.cb1));
progress.setChecked(task.isTaskCompleted());
final ImageView timer = ((ImageView)view.findViewById(R.id.imageLeft));
if(task.getTimerStart() != null) {
timer.setImageDrawable(r.getDrawable(R.drawable.icon_timer)); timer.setImageDrawable(r.getDrawable(R.drawable.icon_timer));
else view.setMinimumHeight(80);
} else {
timer.setImageDrawable(null); timer.setImageDrawable(null);
progress.setChecked(task.isTaskCompleted()); view.setMinimumHeight(45);
}
setFieldContentsAndVisibility(view, task); final TextView name = ((TextView)view.findViewById(R.id.task_name));
setTaskAppearance(task, name, progress); setTaskAppearance(task, name, progress);
hooks.onCreatedTaskListView(view, task); hooks.onCreatedTaskListView(view, task);
} }
@ -198,14 +231,30 @@ public class TaskListAdapter extends ArrayAdapter<TaskModelForList> {
v.setVisibility(View.GONE); v.setVisibility(View.GONE);
} }
/** Helper method to add a line and maybe a newline */
private static void appendLine(StringBuilder sb, String line) {
if(line.length() == 0)
return;
if(sb.length() > 0)
sb.append("\n");
sb.append(line);
}
/** Helper method to set the contents and visibility of each field */ /** Helper method to set the contents and visibility of each field */
private void setFieldContentsAndVisibility(View view, TaskModelForList task) { private void setFieldContentsAndVisibility(View view, TaskModelForList task) {
TaskFieldsVisibility visibleFields = Preferences.getTaskFieldsVisibility(activity);
Resources r = getContext().getResources(); Resources r = getContext().getResources();
TaskFieldsVisibility visibleFields = Preferences.getTaskFieldsVisibility(activity);
boolean isExpanded = CACHE_TRUE.equals(task.getCachedLabel(KEY_EXPANDED));
StringBuilder details = new StringBuilder();
StringBuilder expandedDetails = new StringBuilder();
// expanded container
final View expandedContainer = view.findViewById(R.id.expanded_layout);
expandedContainer.setVisibility(isExpanded ? View.VISIBLE : View.GONE);
// 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) {
String cachedResult = task.getCachedLabel(KEY_NAME); String cachedResult = task.getCachedLabel(KEY_NAME);
if(cachedResult == null) { if(cachedResult == null) {
String nameValue = task.getName(); String nameValue = task.getName();
@ -222,8 +271,6 @@ public class TaskListAdapter extends ArrayAdapter<TaskModelForList> {
else else
name.setTypeface(Typeface.DEFAULT_BOLD); name.setTypeface(Typeface.DEFAULT_BOLD);
} }
setVisibility(name);
// importance // importance
final View importance = (View)view.findViewById(R.id.importance); final View importance = (View)view.findViewById(R.id.importance);
@ -235,7 +282,7 @@ 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 || isExpanded) {
String cachedResult = task.getCachedLabel(KEY_DEADLINE); String cachedResult = task.getCachedLabel(KEY_DEADLINE);
if(cachedResult == null) { if(cachedResult == null) {
StringBuilder label = new StringBuilder(); StringBuilder label = new StringBuilder();
@ -282,17 +329,21 @@ public class TaskListAdapter extends ArrayAdapter<TaskModelForList> {
cachedResult = label.toString(); cachedResult = label.toString();
task.putCachedLabel(KEY_DEADLINE, cachedResult); task.putCachedLabel(KEY_DEADLINE, cachedResult);
} }
deadlines.setText(cachedResult);
if(CACHE_TRUE.equals(task.getCachedLabel(KEY_OVERDUE))) if(visibleFields.DEADLINE) {
deadlines.setTextColor(r.getColor(R.color.taskList_dueDateOverdue)); deadlines.setText(cachedResult);
else if(CACHE_TRUE.equals(task.getCachedLabel(KEY_OVERDUE)))
deadlines.setTextColor(r.getColor(R.color.taskList_details)); deadlines.setTextColor(r.getColor(R.color.taskList_dueDateOverdue));
else
deadlines.setTextColor(r.getColor(R.color.taskList_details));
} else {
expandedDetails.append(cachedResult);
}
} }
setVisibility(deadlines); setVisibility(deadlines);
// estimated / elapsed time // estimated / elapsed time
final TextView times = ((TextView)view.findViewById(R.id.text_times)); if(visibleFields.TIMES || isExpanded) {
if(visibleFields.TIMES) {
String cachedResult = task.getCachedLabel(KEY_TIMES); String cachedResult = task.getCachedLabel(KEY_TIMES);
if(cachedResult == null) { if(cachedResult == null) {
Integer elapsed = task.getElapsedSeconds(); Integer elapsed = task.getElapsedSeconds();
@ -310,18 +361,19 @@ public class TaskListAdapter extends ArrayAdapter<TaskModelForList> {
if(elapsed > 0) { if(elapsed > 0) {
label.append(r.getString(R.string.taskList_elapsedTimePrefix)). label.append(r.getString(R.string.taskList_elapsedTimePrefix)).
append(" "). append(" ").
append(DateUtilities.getDurationString(r, elapsed, 2)); append(DateUtilities.getAbbreviatedDurationString(r, elapsed, 2));
} }
cachedResult = label.toString(); cachedResult = label.toString();
task.putCachedLabel(KEY_TIMES, cachedResult); task.putCachedLabel(KEY_TIMES, cachedResult);
} }
times.setText(cachedResult); if(visibleFields.TIMES)
appendLine(details, cachedResult);
else
appendLine(expandedDetails, cachedResult);
} }
setVisibility(times);
// reminders // reminders
final TextView reminders = ((TextView)view.findViewById(R.id.text_reminders)); if(visibleFields.REMINDERS || isExpanded) {
if(visibleFields.REMINDERS) {
String cachedResult = task.getCachedLabel(KEY_REMINDERS); String cachedResult = task.getCachedLabel(KEY_REMINDERS);
if(cachedResult == null) { if(cachedResult == null) {
Integer notifyEvery = task.getNotificationIntervalSeconds(); Integer notifyEvery = task.getNotificationIntervalSeconds();
@ -335,10 +387,21 @@ public class TaskListAdapter extends ArrayAdapter<TaskModelForList> {
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) Date nextAlarm = null;
label.append(". "); Date now = new Date();
label.append(r.getQuantityString(R.plurals.Nalarms, alerts.size(), for(Date alert : alerts) {
alerts.size())).append(" ").append(r.getString(R.string.taskList_alarmSuffix)); if(alert.after(now) && (nextAlarm == null ||
alert.before(nextAlarm)))
nextAlarm = alert;
}
if(nextAlarm != null) {
if(label.length() > 0)
label.append(". ");
String alarmString = alarmFormat.format(nextAlarm);
label.append(r.getString(R.string.taskList_alarmPrefix) +
" " + alarmString);
}
} }
} finally { } finally {
alarmController.close(); alarmController.close();
@ -346,13 +409,14 @@ public class TaskListAdapter extends ArrayAdapter<TaskModelForList> {
cachedResult = label.toString(); cachedResult = label.toString();
task.putCachedLabel(KEY_REMINDERS, cachedResult); task.putCachedLabel(KEY_REMINDERS, cachedResult);
} }
reminders.setText(cachedResult); if(visibleFields.REMINDERS)
appendLine(details, cachedResult);
else
appendLine(expandedDetails, cachedResult);
} }
setVisibility(reminders);
// repeats // repeats
final TextView repeats = ((TextView)view.findViewById(R.id.text_repeats)); if(visibleFields.REPEATS || isExpanded) {
if(visibleFields.REPEATS) {
String cachedResult = task.getCachedLabel(KEY_REPEAT); String cachedResult = task.getCachedLabel(KEY_REPEAT);
if(cachedResult == null) { if(cachedResult == null) {
RepeatInfo repeatInfo = task.getRepeat(); RepeatInfo repeatInfo = task.getRepeat();
@ -364,13 +428,14 @@ public class TaskListAdapter extends ArrayAdapter<TaskModelForList> {
cachedResult = ""; cachedResult = "";
task.putCachedLabel(KEY_REPEAT, cachedResult); task.putCachedLabel(KEY_REPEAT, cachedResult);
} }
repeats.setText(cachedResult); if(visibleFields.REPEATS)
appendLine(details, cachedResult);
else
appendLine(expandedDetails, cachedResult);
} }
setVisibility(repeats);
// tags // tags
final TextView tags = ((TextView)view.findViewById(R.id.text_tags)); if(visibleFields.TAGS || isExpanded) {
if(visibleFields.TAGS) {
String cachedResult = task.getCachedLabel(KEY_TAGS); String cachedResult = task.getCachedLabel(KEY_TAGS);
if(cachedResult == null) { if(cachedResult == null) {
List<TagModelForView> alltags = hooks.getTagsFor(task); List<TagModelForView> alltags = hooks.getTagsFor(task);
@ -382,24 +447,66 @@ public class TaskListAdapter extends ArrayAdapter<TaskModelForList> {
tagString.append(", "); tagString.append(", ");
} }
if(alltags.size() > 0) if(alltags.size() > 0)
cachedResult = r.getString(R.string.taskList_tagsPrefix) + " " + tagString; cachedResult = r.getString(R.string.taskList_tagsPrefix) +
" " + tagString;
else else
cachedResult = ""; cachedResult = "";
task.putCachedLabel(KEY_TAGS, cachedResult); task.putCachedLabel(KEY_TAGS, cachedResult);
} }
tags.setText(cachedResult); if(visibleFields.TAGS)
appendLine(details, cachedResult);
else
appendLine(expandedDetails, cachedResult);
} }
setVisibility(tags);
// notes // notes
final TextView notes = ((TextView)view.findViewById(R.id.text_notes)); if(visibleFields.NOTES || isExpanded) {
if(visibleFields.NOTES) { if(task.getNotes() != null && task.getNotes().length() > 0) {
notes.setText(r.getString(R.string.taskList_notesPrefix) + " " + task.getNotes()); String notes = r.getString(R.string.taskList_notesPrefix) +
" " + task.getNotes();
if(visibleFields.NOTES)
appendLine(details, notes);
else
appendLine(expandedDetails, notes);
}
} }
setVisibility(notes);
final TextView detailsView = ((TextView)view.findViewById(R.id.details));
detailsView.setText(details.toString());
setVisibility(detailsView);
// expanded-only fields: creation date, ...
if(isExpanded) {
if(task.getCreationDate() != null) {
String cachedResult = task.getCachedLabel(KEY_CREATION);
if(cachedResult == null) {
int secondsAgo = (int) ((System.currentTimeMillis() -
task.getCreationDate().getTime())/1000);
cachedResult = r.getString(R.string.taskList_createdPrefix) + " " +
DateUtilities.getDurationString(r, Math.abs(secondsAgo), 1) +
" " + r.getString(R.string.ago_suffix);
task.putCachedLabel(KEY_CREATION, cachedResult);
}
appendLine(expandedDetails, cachedResult);
}
final Button timerButton =
((Button)view.findViewById(R.id.timer));
if(task.getTimerStart() == null)
timerButton.setText(r.getString(R.string.startTimer_label));
else
timerButton.setText(r.getString(R.string.stopTimer_label));
final TextView expandedDetailsView =
((TextView)view.findViewById(R.id.expanded_details));
expandedDetailsView.setText(expandedDetails.toString());
}
} }
private void addListeners(final int position, final View view) { /** Set listeners for this view. This is called once total */
private void addListeners(View view) {
final CheckBox progress = ((CheckBox)view.findViewById(R.id.cb1)); final CheckBox progress = ((CheckBox)view.findViewById(R.id.cb1));
// clicking the check box // clicking the check box
@ -407,7 +514,8 @@ public class TaskListAdapter extends ArrayAdapter<TaskModelForList> {
@Override @Override
public void onCheckedChanged(CompoundButton buttonView, public void onCheckedChanged(CompoundButton buttonView,
boolean isChecked) { boolean isChecked) {
TaskModelForList task = (TaskModelForList)buttonView.getTag(); View parent = (View)buttonView.getParent();
TaskModelForList task = (TaskModelForList)parent.getTag();
int newProgressPercentage; int newProgressPercentage;
if(isChecked) if(isChecked)
@ -417,8 +525,8 @@ public class TaskListAdapter extends ArrayAdapter<TaskModelForList> {
newProgressPercentage = 0; newProgressPercentage = 0;
if(newProgressPercentage != task.getProgressPercentage()) { if(newProgressPercentage != task.getProgressPercentage()) {
setTaskProgress(task, view, newProgressPercentage); setTaskProgress(task, parent, newProgressPercentage);
setupView(view, task); setupView(parent, task);
} }
} }
}); });
@ -427,16 +535,18 @@ public class TaskListAdapter extends ArrayAdapter<TaskModelForList> {
view.setOnClickListener(new View.OnClickListener() { view.setOnClickListener(new View.OnClickListener() {
@Override @Override
public void onClick(View v) { public void onClick(View v) {
hooks.performItemClick(view, position); TaskModelForList task = (TaskModelForList)v.getTag();
toggleExpanded(v, task);
} }
}); });
// typing while selected something
view.setOnKeyListener(new OnKeyListener() { view.setOnKeyListener(new OnKeyListener() {
@Override @Override
public boolean onKey(View v, int keyCode, KeyEvent event) { public boolean onKey(View v, int keyCode, KeyEvent event) {
if(event.getAction() != KeyEvent.ACTION_UP) if(event.getAction() != KeyEvent.ACTION_UP)
return false; return false;
// hotkey to set task priority // hot-key to set task priority
if(keyCode >= KeyEvent.KEYCODE_1 && keyCode <= KeyEvent.KEYCODE_4) { if(keyCode >= KeyEvent.KEYCODE_1 && keyCode <= KeyEvent.KEYCODE_4) {
Importance i = Importance.values()[keyCode - KeyEvent.KEYCODE_1]; Importance i = Importance.values()[keyCode - KeyEvent.KEYCODE_1];
TaskModelForList task = (TaskModelForList)v.getTag(); TaskModelForList task = (TaskModelForList)v.getTag();
@ -455,6 +565,7 @@ public class TaskListAdapter extends ArrayAdapter<TaskModelForList> {
public void onCreateContextMenu(ContextMenu menu, View v, public void onCreateContextMenu(ContextMenu menu, View v,
ContextMenuInfo menuInfo) { ContextMenuInfo menuInfo) {
TaskModelForList task = (TaskModelForList)v.getTag(); TaskModelForList task = (TaskModelForList)v.getTag();
int position = objects.indexOf(task);
menu.add(position, CONTEXT_EDIT_ID, Menu.NONE, menu.add(position, CONTEXT_EDIT_ID, Menu.NONE,
R.string.taskList_context_edit); R.string.taskList_context_edit);
@ -476,6 +587,27 @@ public class TaskListAdapter extends ArrayAdapter<TaskModelForList> {
menu.setHeaderTitle(task.getName()); menu.setHeaderTitle(task.getName());
} }
}); });
// clicking one of the expanded buttons
Button editButton = (Button)view.findViewById(R.id.edit);
editButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
View parent = (View)v.getParent().getParent().getParent().getParent();
TaskModelForList task = (TaskModelForList)parent.getTag();
hooks.editItem(task);
}
});
Button deleteButton = (Button)view.findViewById(R.id.timer);
deleteButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
View parent = (View)v.getParent().getParent().getParent().getParent();
TaskModelForList task = (TaskModelForList)parent.getTag();
hooks.toggleTimerOnItem(task);
}
});
} }
private void setTaskProgress(final TaskModelForList task, View view, int progress) { private void setTaskProgress(final TaskModelForList task, View view, int progress) {
@ -521,14 +653,20 @@ public class TaskListAdapter extends ArrayAdapter<TaskModelForList> {
name.setPaintFlags(name.getPaintFlags() & ~Paint.STRIKE_THRU_TEXT_FLAG); 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) float completedPercentage = 0;
progress.setButtonDrawable(R.drawable.btn_check75); if(task.getEstimatedSeconds() > 0) {
else if(task.getProgressPercentage() >= 50) completedPercentage = task.getElapsedSeconds() /
progress.setButtonDrawable(R.drawable.btn_check50); task.getEstimatedSeconds();
else if(task.getProgressPercentage() >= 25) }
if(completedPercentage < 0.25f)
progress.setButtonDrawable(R.drawable.btn_check0);
else if(completedPercentage < 0.5f)
progress.setButtonDrawable(R.drawable.btn_check25); progress.setButtonDrawable(R.drawable.btn_check25);
else if(completedPercentage < 0.75f)
progress.setButtonDrawable(R.drawable.btn_check50);
else else
progress.setButtonDrawable(R.drawable.btn_check0); progress.setButtonDrawable(R.drawable.btn_check75);
} }
} }

@ -0,0 +1,19 @@
package com.timsu.astrid.activities;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
public class TaskListNotify extends Activity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Intent intent = new Intent(this, TaskList.class);
intent.putExtra(TaskList.VARIABLES_TAG, getIntent().getExtras());
startActivity(intent);
finish();
}
}

@ -27,6 +27,7 @@ import java.util.Iterator;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Random;
import android.app.AlertDialog; import android.app.AlertDialog;
import android.content.DialogInterface; import android.content.DialogInterface;
@ -35,6 +36,7 @@ import android.content.res.Resources;
import android.database.Cursor; import android.database.Cursor;
import android.net.Uri; import android.net.Uri;
import android.os.Bundle; import android.os.Bundle;
import android.util.Log;
import android.view.ContextMenu; import android.view.ContextMenu;
import android.view.KeyEvent; import android.view.KeyEvent;
import android.view.Menu; import android.view.Menu;
@ -42,10 +44,8 @@ import android.view.MenuItem;
import android.view.View; import android.view.View;
import android.view.ContextMenu.ContextMenuInfo; import android.view.ContextMenu.ContextMenuInfo;
import android.view.View.OnCreateContextMenuListener; import android.view.View.OnCreateContextMenuListener;
import android.widget.AdapterView;
import android.widget.Button; import android.widget.Button;
import android.widget.ListView; import android.widget.ListView;
import android.widget.AdapterView.OnItemClickListener;
import com.timsu.astrid.R; import com.timsu.astrid.R;
import com.timsu.astrid.activities.TaskList.ActivityCode; import com.timsu.astrid.activities.TaskList.ActivityCode;
@ -61,11 +61,12 @@ import com.timsu.astrid.sync.Synchronizer;
import com.timsu.astrid.sync.Synchronizer.SynchronizerListener; import com.timsu.astrid.sync.Synchronizer.SynchronizerListener;
import com.timsu.astrid.utilities.Constants; import com.timsu.astrid.utilities.Constants;
import com.timsu.astrid.utilities.DialogUtilities; import com.timsu.astrid.utilities.DialogUtilities;
import com.timsu.astrid.utilities.Notifications;
import com.timsu.astrid.utilities.Preferences; import com.timsu.astrid.utilities.Preferences;
import com.timsu.astrid.widget.NNumberPickerDialog.OnNNumberPickedListener; 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
* system, and allows users to edit them. * system, and allows users to edit them.
* *
@ -75,14 +76,17 @@ import com.timsu.astrid.widget.NNumberPickerDialog.OnNNumberPickedListener;
public class TaskListSubActivity extends SubActivity { public class TaskListSubActivity extends SubActivity {
// bundle tokens // bundle tokens
public static final String TAG_TOKEN = "tag"; public static final String TAG_TOKEN = "tag";
public static final String FROM_NOTIFICATION_TOKEN = "notify";
public static final String NOTIF_FLAGS_TOKEN = "notif_flags";
public static final String NOTIF_REPEAT_TOKEN = "notif_repeat";
public static final String LOAD_INSTANCE_TOKEN = "id";
// activities // activities
private static final int ACTIVITY_CREATE = 0; private static final int ACTIVITY_CREATE = 0;
private static final int ACTIVITY_VIEW = 1; private static final int ACTIVITY_EDIT = 1;
private static final int ACTIVITY_EDIT = 2; private static final int ACTIVITY_TAGS = 2;
private static final int ACTIVITY_TAGS = 3; private static final int ACTIVITY_SYNCHRONIZE = 3;
private static final int ACTIVITY_SYNCHRONIZE = 4;
// menu codes // menu codes
private static final int INSERT_ID = Menu.FIRST; private static final int INSERT_ID = Menu.FIRST;
@ -116,6 +120,8 @@ public class TaskListSubActivity extends SubActivity {
private Map<TagIdentifier, TagModelForView> tagMap; private Map<TagIdentifier, TagModelForView> tagMap;
private ArrayList<TaskModelForList> taskArray; private ArrayList<TaskModelForList> taskArray;
private HashMap<TaskModelForList, LinkedList<TagModelForView>> taskTags; private HashMap<TaskModelForList, LinkedList<TagModelForView>> taskTags;
private Long selectedTaskId = null;
private TaskModelForList selectedTask = null;
// display filters // display filters
private static boolean filterShowHidden = false; private static boolean filterShowHidden = false;
@ -127,26 +133,42 @@ public class TaskListSubActivity extends SubActivity {
/* ====================================================================== /* ======================================================================
* ======================================================= initialization * ======================================================= initialization
* ====================================================================== */ * ====================================================================== */
public TaskListSubActivity(TaskList parent, ActivityCode code, View view) { public TaskListSubActivity(TaskList parent, ActivityCode code, View view) {
super(parent, code, view); super(parent, code, view);
} }
@Override @Override
/** Called when loading up the activity */ /** Called when loading up the activity */
public void onDisplay(Bundle variables) { public void onDisplay(Bundle variables) {
// load tag map // process tag to filter, if any
tagMap = getTagController().getAllTagsAsMap(getParent());
// process the tag to filter on, if any
if(variables != null && variables.containsKey(TAG_TOKEN)) { if(variables != null && variables.containsKey(TAG_TOKEN)) {
TagIdentifier identifier = new TagIdentifier(variables.getLong(TAG_TOKEN)); TagIdentifier identifier = new TagIdentifier(variables.getLong(TAG_TOKEN));
tagMap = getTagController().getAllTagsAsMap(getParent());
filterTag = tagMap.get(identifier); filterTag = tagMap.get(identifier);
} }
// process task that's selected, if any
if(variables != null && variables.containsKey(LOAD_INSTANCE_TOKEN)) {
selectedTaskId = variables.getLong(LOAD_INSTANCE_TOKEN);
}
setupUIComponents(); setupUIComponents();
loadTaskListSort(); loadTaskListSort();
fillData(); fillData();
if(variables != null && variables.containsKey(NOTIF_FLAGS_TOKEN)) {
long repeatInterval = 0;
int flags = 0;
if(variables.containsKey(NOTIF_REPEAT_TOKEN))
repeatInterval = variables.getLong(NOTIF_REPEAT_TOKEN);
flags = variables.getInt(NOTIF_FLAGS_TOKEN);
if(selectedTask != null) {
showNotificationAlert(selectedTask, repeatInterval, flags);
}
}
} }
/** Initialize UI components */ /** Initialize UI components */
@ -173,7 +195,7 @@ public class TaskListSubActivity extends SubActivity {
} }
}); });
} }
@Override @Override
/** Create options menu (displayed when user presses menu key) */ /** Create options menu (displayed when user presses menu key) */
public boolean onPrepareOptionsMenu(Menu menu) { public boolean onPrepareOptionsMenu(Menu menu) {
@ -265,18 +287,81 @@ public class TaskListSubActivity extends SubActivity {
abstract int compareTo(TaskModelForList arg0, TaskModelForList arg1); abstract int compareTo(TaskModelForList arg0, TaskModelForList arg1);
}; };
/* ======================================================================
* ======================================================== notifications
* ====================================================================== */
/** Called when user clicks on a notification to get here */
private void showNotificationAlert(final TaskModelForList task,
final long repeatInterval, final int flags) {
Resources r = getResources();
// clear notifications
Notifications.clearAllNotifications(getParent(), task.getTaskIdentifier());
String[] responses = r.getStringArray(R.array.reminder_responses);
String response = responses[new Random().nextInt(responses.length)];
new AlertDialog.Builder(getParent())
.setTitle(R.string.taskView_notifyTitle)
.setMessage(task.getName() + "\n\n" + response)
.setIcon(android.R.drawable.ic_dialog_alert)
// yes, i will do it: just closes this dialog
.setPositiveButton(R.string.notify_yes, null)
// no, i will ignore: quits application
.setNegativeButton(R.string.notify_no, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
TaskList.shouldCloseInstance = true;
closeActivity();
}
})
// snooze: sets a new temporary alert, closes application
.setNeutralButton(R.string.notify_snooze, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
snoozeAlert(task, repeatInterval, flags);
}
})
.show();
}
private void snoozeAlert(final TaskModelForList task,
final long repeatInterval, final int flags) {
DialogUtilities.hourMinutePicker(getParent(),
getResources().getString(R.string.notify_snooze_title),
new OnNNumberPickedListener() {
public void onNumbersPicked(int[] values) {
int snoozeSeconds = values[0] * 3600 + values[1] * 60;
Notifications.createSnoozeAlarm(getParent(),
task.getTaskIdentifier(), snoozeSeconds, flags,
repeatInterval);
TaskList.shouldCloseInstance = true;
closeActivity();
}
});
}
/* ====================================================================== /* ======================================================================
* ====================================================== populating list * ====================================================== populating list
* ====================================================================== */ * ====================================================================== */
/** Helper method returns true if the task is considered 'hidden' */ /** Helper method returns true if the task is considered 'hidden' */
private boolean isTaskHidden(TaskModelForList task) { private boolean isTaskHidden(TaskModelForList task) {
if(task == selectedTask)
return false;
if(task.isHidden()) if(task.isHidden())
return true; return true;
if(filterTag == null) { if(filterTag == null) {
for(TagModelForView tags : taskTags.get(task)) { for(TagModelForView tags : taskTags.get(task)) {
if(tags.shouldHideFromMainList()) if(tags != null && tags.shouldHideFromMainList())
return true; return true;
} }
} }
@ -306,7 +391,9 @@ public class TaskListSubActivity extends SubActivity {
// read tags and apply filters // read tags and apply filters
int hiddenTasks = 0; // # of tasks hidden int hiddenTasks = 0; // # of tasks hidden
int completedTasks = 0; // # of tasks on list that are done int completedTasks = 0; // # of tasks on list that are done
tagMap = getTagController().getAllTagsAsMap(getParent());
taskTags = new HashMap<TaskModelForList, LinkedList<TagModelForView>>(); taskTags = new HashMap<TaskModelForList, LinkedList<TagModelForView>>();
for(Iterator<TaskModelForList> i = taskArray.iterator(); i.hasNext();) { for(Iterator<TaskModelForList> i = taskArray.iterator(); i.hasNext();) {
TaskModelForList task = i.next(); TaskModelForList task = i.next();
@ -317,6 +404,10 @@ public class TaskListSubActivity extends SubActivity {
} }
} }
if(selectedTaskId != null && task.getTaskIdentifier().getId() == selectedTaskId) {
selectedTask = task;
}
// get list of tags // get list of tags
LinkedList<TagIdentifier> tagIds = getTagController().getTaskTags(getParent(), LinkedList<TagIdentifier> tagIds = getTagController().getTaskTags(getParent(),
task.getTaskIdentifier()); task.getTaskIdentifier());
@ -414,23 +505,39 @@ public class TaskListSubActivity extends SubActivity {
public void onCreatedTaskListView(View v, TaskModelForList task) { public void onCreatedTaskListView(View v, TaskModelForList task) {
v.setOnTouchListener(getGestureListener()); v.setOnTouchListener(getGestureListener());
} }
@Override
public void editItem(TaskModelForList task) {
editTask(task);
}
@Override
public void toggleTimerOnItem(TaskModelForList task) {
toggleTimer(task);
}
@Override
public void setSelectedItem(TaskIdentifier taskId) {
selectedTask = null;
if(taskId == null)
selectedTaskId = null;
else
selectedTaskId = taskId.getId();
}
}); });
listView.setAdapter(tasks); listView.setAdapter(tasks);
listView.setItemsCanFocus(true); listView.setItemsCanFocus(true);
// list view listener if(selectedTask != null) {
listView.setOnItemClickListener(new OnItemClickListener() { try {
@Override int selectedPosition = tasks.getPosition(selectedTask);
public void onItemClick(AdapterView<?> parent, View view, View v = listView.getChildAt(selectedPosition);
int position, long id) { tasks.toggleExpanded(v, selectedTask);
TaskModelForList task = (TaskModelForList)view.getTag(); listView.setSelection(selectedPosition);
} catch (Exception e) {
Intent intent = new Intent(getParent(), TaskView.class); Log.e("astrid", "error with selected task", e);
intent.putExtra(TaskEdit.LOAD_INSTANCE_TOKEN, task.
getTaskIdentifier().getId());
launchActivity(intent, ACTIVITY_VIEW);
} }
}); }
// filters context menu // filters context menu
listView.setOnCreateContextMenuListener(new OnCreateContextMenuListener() { listView.setOnCreateContextMenuListener(new OnCreateContextMenuListener() {
@ -493,15 +600,15 @@ public class TaskListSubActivity extends SubActivity {
return true; return true;
} }
} }
if(keyCode >= KeyEvent.KEYCODE_A && keyCode <= KeyEvent.KEYCODE_Z) { if(keyCode >= KeyEvent.KEYCODE_A && keyCode <= KeyEvent.KEYCODE_Z) {
createTask((char)('A' + (keyCode - KeyEvent.KEYCODE_A))); createTask((char)('A' + (keyCode - KeyEvent.KEYCODE_A)));
return true; return true;
} }
return false; return false;
} }
@Override @Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) { protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if(resultCode == Constants.RESULT_SYNCHRONIZE) { if(resultCode == Constants.RESULT_SYNCHRONIZE) {
@ -554,6 +661,25 @@ public class TaskListSubActivity extends SubActivity {
.show(); .show();
} }
/** Take you to the task edit page */
private void editTask(TaskModelForList task) {
Intent intent = new Intent(getParent(), TaskEdit.class);
intent.putExtra(TaskEdit.LOAD_INSTANCE_TOKEN,
task.getTaskIdentifier().getId());
launchActivity(intent, ACTIVITY_EDIT);
}
/** Toggle the timer */
private void toggleTimer(TaskModelForList task) {
if(task.getTimerStart() == null)
task.setTimerStart(new Date());
else {
task.stopTimerAndUpdateElapsedTime();
}
getTaskController().saveTask(task);
fillData();
}
/** Show the tags view */ /** Show the tags view */
public void showTagsView() { public void showTagsView() {
switchToActivity(ActivityCode.TAG_LIST, null); switchToActivity(ActivityCode.TAG_LIST, null);
@ -592,7 +718,6 @@ public class TaskListSubActivity extends SubActivity {
@Override @Override
public boolean onMenuItemSelected(int featureId, MenuItem item) { public boolean onMenuItemSelected(int featureId, MenuItem item) {
Intent intent;
final TaskModelForList task; final TaskModelForList task;
Resources r = getResources(); Resources r = getResources();
@ -631,9 +756,7 @@ public class TaskListSubActivity extends SubActivity {
// --- list context menu items // --- list context menu items
case TaskListAdapter.CONTEXT_EDIT_ID: case TaskListAdapter.CONTEXT_EDIT_ID:
task = taskArray.get(item.getGroupId()); task = taskArray.get(item.getGroupId());
intent = new Intent(getParent(), TaskEdit.class); editTask(task);
intent.putExtra(TaskEdit.LOAD_INSTANCE_TOKEN, task.getTaskIdentifier().getId());
launchActivity(intent, ACTIVITY_EDIT);
return true; return true;
case TaskListAdapter.CONTEXT_DELETE_ID: case TaskListAdapter.CONTEXT_DELETE_ID:
task = taskArray.get(item.getGroupId()); task = taskArray.get(item.getGroupId());
@ -641,13 +764,7 @@ public class TaskListSubActivity extends SubActivity {
return true; return true;
case TaskListAdapter.CONTEXT_TIMER_ID: case TaskListAdapter.CONTEXT_TIMER_ID:
task = taskArray.get(item.getGroupId()); task = taskArray.get(item.getGroupId());
if(task.getTimerStart() == null) toggleTimer(task);
task.setTimerStart(new Date());
else {
task.stopTimerAndUpdateElapsedTime();
}
getTaskController().saveTask(task);
fillData();
return true; return true;
case TaskListAdapter.CONTEXT_POSTPONE_ID: case TaskListAdapter.CONTEXT_POSTPONE_ID:
task = taskArray.get(item.getGroupId()); task = taskArray.get(item.getGroupId());

@ -1,366 +0,0 @@
/*
* ASTRID: Android's Simple Task Recording Dashboard
*
* Copyright (c) 2009 Tim Su
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package com.timsu.astrid.activities;
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.res.Resources;
import android.os.Bundle;
import android.os.Handler;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.Window;
import android.widget.Button;
import android.widget.TextView;
import com.timsu.astrid.R;
import com.timsu.astrid.data.task.TaskIdentifier;
import com.timsu.astrid.data.task.TaskModelForView;
import com.timsu.astrid.utilities.Constants;
import com.timsu.astrid.utilities.DateUtilities;
import com.timsu.astrid.widget.NumberPicker;
import com.timsu.astrid.widget.NumberPickerDialog;
/** Task Properties view for the Astrid Application.
*
* @author Tim Su (timsu@stanfordalumni.org)
*
*/
public class TaskView extends TaskModificationActivity<TaskModelForView> {
// activities
private static final int ACTIVITY_EDIT = 0;
// menu codes
private static final int EDIT_ID = Menu.FIRST;
private static final int DELETE_ID = Menu.FIRST + 1;
// UI components
private TextView name;
private TextView elapsed;
private TextView estimated;
private TextView definiteDueDate;
private TextView preferredDueDate;
private TextView creationDate;
private TextView notes;
private Button timerButton;
private Button progress;
private NumberPickerDialog progressDialog;
// other instance variables
private Handler handler;
private Timer updateTimer = null;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.task_view);
handler = new Handler();
setUpUIComponents();
setUpListeners();
populateFields();
}
@Override
protected TaskModelForView getModel(TaskIdentifier identifier) {
if(identifier == null)
throw new IllegalArgumentException("Can't view null task!");
return controller.fetchTaskForView(this, identifier);
}
/* ======================================================================
* =================================================== reading from model
* ====================================================================== */
private void populateFields() {
final Resources r = getResources();
name.setText(model.getName());
estimated.setText(DateUtilities.getDurationString(r,
model.getEstimatedSeconds(), 2));
elapsed.setText(DateUtilities.getDurationString(r,
model.getElapsedSeconds(), 2));
formatDate(model.getCreationDate(), creationDate);
updateTimerButtonText();
updateProgressComponents();
if(model.getNotes().length() == 0)
((View)notes.getParent()).setVisibility(View.GONE);
else
notes.setText(model.getNotes());
}
private void formatDeadline(Date deadline, TextView view) {
Resources r = getResources();
if(deadline == null || model.isTaskCompleted()) {
((View)view.getParent()).setVisibility(View.GONE);
return;
}
int secondsToDeadline = (int) ((deadline.getTime() -
System.currentTimeMillis())/1000);
String text = DateUtilities.getDurationString(r,
Math.abs(secondsToDeadline), 2) + " ";
if(secondsToDeadline < 0) {
view.setTextColor(r.getColor(R.color.view_table_overdue));
view.setText(text + r.getString(R.string.overdue_suffix));
} else
view.setText(text);
}
private void formatDate(Date date, TextView view) {
Resources r = getResources();
if(date == null || model.isTaskCompleted()) {
((View)view.getParent()).setVisibility(View.GONE);
return;
}
int secondsAgo = (int) ((System.currentTimeMillis() - date.getTime())/1000);
String text = DateUtilities.getDurationString(r,
Math.abs(secondsAgo), 2) + " ";
view.setText(text + r.getString(R.string.ago_suffix));
}
/* ======================================================================
* ==================================================== UI initialization
* ====================================================================== */
private void setUpUIComponents() {
Resources r = getResources();
name = (TextView)findViewById(R.id.name);
elapsed = (TextView)findViewById(R.id.cell_elapsed);
estimated = (TextView)findViewById(R.id.cell_estimated);
definiteDueDate = (TextView)findViewById(R.id.cell_definiteDueDate);
preferredDueDate = (TextView)findViewById(R.id.cell_preferredDueDate);
creationDate = (TextView)findViewById(R.id.cell_creationDate);
notes = (TextView)findViewById(R.id.cell_notes);
timerButton = (Button)findViewById(R.id.timerButton);
progress = (Button)findViewById(R.id.progress);
progressDialog = new NumberPickerDialog(this,
new NumberPickerDialog.OnNumberPickedListener() {
@Override
public void onNumberPicked(NumberPicker view, int number) {
model.setProgressPercentage(number);
controller.saveTask(model);
updateProgressComponents();
}
}, r.getString(R.string.progress_dialog), 0, 25, 0, 100);
name.setTextSize(36);
}
private void setUpListeners() {
Button edit = (Button)findViewById(R.id.edit);
edit.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
editButtonClick();
}
});
timerButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if(model.getTimerStart() == null) {
model.setTimerStart(new Date());
controller.saveTask(model);
} else {
model.stopTimerAndUpdateElapsedTime();
controller.saveTask(model);
}
updateTimerButtonText();
}
});
progress.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
progressDialog.show();
}
});
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
super.onCreateOptionsMenu(menu);
MenuItem item;
item = menu.add(Menu.NONE, EDIT_ID, 0, R.string.edit_label);
item.setIcon(android.R.drawable.ic_menu_edit);
item.setAlphabeticShortcut('s');
item = menu.add(Menu.NONE, DELETE_ID, 0, R.string.delete_label);
item.setIcon(android.R.drawable.ic_menu_delete);
item.setAlphabeticShortcut('d');
return true;
}
/* ======================================================================
* ======================================================= event handlers
* ====================================================================== */
@Override
public void onWindowFocusChanged(boolean hasFocus) {
super.onWindowFocusChanged(hasFocus);
if(hasFocus && TaskList.shouldCloseInstance) { // user wants to quit
finish();
}
}
@Override
protected void onActivityResult(int requestCode, int resultCode,
Intent intent) {
switch(resultCode) {
case Constants.RESULT_GO_HOME:
case RESULT_CANCELED:
setResult(resultCode);
finish();
break;
default:
populateFields();
}
}
@Override
protected void onPause() {
super.onPause();
updateTimer.cancel(); // stop the timer
updateTimer = null;
}
@Override
protected void onResume() {
super.onResume();
populateFields();
if(updateTimer != null)
return;
updateTimer = new Timer(); // start timer
updateTimer.scheduleAtFixedRate(new TimerTask() {
@Override
public void run() {
handler.post(new Runnable() {
@Override
public void run() {
updateElapsedTimeText();
}
});
}
}, 0, 1000);
}
private void editButtonClick() {
Intent intent = new Intent(TaskView.this, TaskEdit.class);
intent.putExtra(TaskEdit.LOAD_INSTANCE_TOKEN,
model.getTaskIdentifier().getId());
startActivityForResult(intent, ACTIVITY_EDIT);
}
private void deleteButtonClick() {
new AlertDialog.Builder(this)
.setTitle(R.string.delete_title)
.setMessage(R.string.delete_this_task_title)
.setIcon(android.R.drawable.ic_dialog_alert)
.setPositiveButton(android.R.string.ok,
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
controller.deleteTask(model.getTaskIdentifier());
setResult(RESULT_OK);
finish();
}
})
.setNegativeButton(android.R.string.cancel, null)
.show();
}
@Override
public boolean onMenuItemSelected(int featureId, MenuItem item) {
switch(item.getItemId()) {
case EDIT_ID:
editButtonClick();
return true;
case DELETE_ID:
deleteButtonClick();
return true;
}
return super.onMenuItemSelected(featureId, item);
}
/* ======================================================================
* ================================================ UI component updating
* ====================================================================== */
/** Update components that depend on elapsed time */
private void updateElapsedTimeText() {
Resources r = getResources();
int timeElapsed = model.getElapsedSeconds();
if(model.getTimerStart() != null) {
timeElapsed += (int) (System.currentTimeMillis() -
model.getTimerStart().getTime())/1000;
}
elapsed.setText(DateUtilities.getDurationString(r,
timeElapsed, Integer.MAX_VALUE));
}
/** Update components that depend on timer status */
private void updateTimerButtonText() {
Resources r = getResources();
if(model.getTimerStart() == null)
timerButton.setText(r.getString(R.string.startTimer_label));
else
timerButton.setText(r.getString(R.string.stopTimer_label));
}
/** Update components that depend on task progress */
private void updateProgressComponents() {
Resources r = getResources();
progress.setText(model.getProgressPercentage() +
r.getString(R.string.progress_suffix));
if(model.isTaskCompleted())
name.setBackgroundColor(r.getColor(R.color.view_header_done));
else
name.setBackgroundColor(r.getColor(model.getImportance().getColorResource()));
progressDialog.setInitialValue(model.getProgressPercentage());
formatDeadline(model.getDefiniteDueDate(), definiteDueDate);
formatDeadline(model.getPreferredDueDate(), preferredDueDate);
}
}

@ -1,114 +0,0 @@
/*
* ASTRID: Android's Simple Task Recording Dashboard
*
* Copyright (c) 2009 Tim Su
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package com.timsu.astrid.activities;
import java.util.Random;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.res.Resources;
import android.os.Bundle;
import com.timsu.astrid.R;
import com.timsu.astrid.utilities.Constants;
import com.timsu.astrid.utilities.DialogUtilities;
import com.timsu.astrid.utilities.Notifications;
import com.timsu.astrid.widget.NNumberPickerDialog.OnNNumberPickedListener;
public class TaskViewNotifier extends TaskView {
// bundle tokens
public static final String FROM_NOTIFICATION_TOKEN = "notify";
public static final String NOTIF_FLAGS_TOKEN = "notif_flags";
public static final String NOTIF_REPEAT_TOKEN = "notif_repeat";
// properties of the alarm that was triggered
private long repeatInterval = 0;
private int flags = 0;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Bundle extras = getIntent().getExtras();
if(extras != null && extras.containsKey(FROM_NOTIFICATION_TOKEN)) {
if(extras.containsKey(NOTIF_REPEAT_TOKEN))
repeatInterval = extras.getLong(NOTIF_REPEAT_TOKEN);
if(extras.containsKey(NOTIF_FLAGS_TOKEN))
flags = extras.getInt(NOTIF_FLAGS_TOKEN);
showNotificationAlert();
}
}
/** Called when user clicks on a notification to get here */
private void showNotificationAlert() {
Resources r = getResources();
// clear notifications
Notifications.clearAllNotifications(this, model.getTaskIdentifier());
String[] responses = r.getStringArray(R.array.reminder_responses);
String response = responses[new Random().nextInt(responses.length)];
new AlertDialog.Builder(this)
.setTitle(R.string.taskView_notifyTitle)
.setMessage(response)
.setIcon(android.R.drawable.ic_dialog_alert)
// yes, i will do it: just closes this dialog
.setPositiveButton(R.string.notify_yes, null)
// no, i will ignore: quits application
.setNegativeButton(R.string.notify_no, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
setResult(Constants.RESULT_GO_HOME);
TaskList.shouldCloseInstance = true;
finish();
}
})
// snooze: sets a new temporary alert, closes application
.setNeutralButton(R.string.notify_snooze, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
snoozeAlert();
}
})
.show();
}
private void snoozeAlert() {
DialogUtilities.hourMinutePicker(this,
getResources().getString(R.string.notify_snooze_title),
new OnNNumberPickedListener() {
public void onNumbersPicked(int[] values) {
int snoozeSeconds = values[0] * 3600 + values[1] * 60;
Notifications.createSnoozeAlarm(TaskViewNotifier.this,
model.getTaskIdentifier(), snoozeSeconds, flags,
repeatInterval);
setResult(Constants.RESULT_GO_HOME);
TaskList.shouldCloseInstance = true;
finish();
}
});
}
}

@ -46,6 +46,7 @@ public class TaskModelForList extends AbstractTaskModel {
NOTIFICATION_FLAGS, NOTIFICATION_FLAGS,
PROGRESS_PERCENTAGE, PROGRESS_PERCENTAGE,
COMPLETION_DATE, COMPLETION_DATE,
CREATION_DATE,
HIDDEN_UNTIL, HIDDEN_UNTIL,
NOTES, NOTES,
REPEAT, REPEAT,
@ -202,6 +203,11 @@ public class TaskModelForList extends AbstractTaskModel {
return super.getNotificationFlags(); return super.getNotificationFlags();
} }
@Override
public Date getCreationDate() {
return super.getCreationDate();
}
// --- setters // --- setters
@Override @Override
@ -232,7 +238,7 @@ public class TaskModelForList extends AbstractTaskModel {
public void setDefiniteDueDate(Date definiteDueDate) { public void setDefiniteDueDate(Date definiteDueDate) {
super.setDefiniteDueDate(definiteDueDate); super.setDefiniteDueDate(definiteDueDate);
} }
@Override @Override
public void setImportance(Importance importance) { public void setImportance(Importance importance) {
super.setImportance(importance); super.setImportance(importance);

@ -75,6 +75,56 @@ public class DateUtilities {
return result.toString().trim(); return result.toString().trim();
} }
/**
* Format a time into the format: 5 days, 3 hrs, 2 min
*
* @param r Resources to get strings from
* @param timeInSeconds
* @param unitsToShow number of units to show (i.e. if 2, then 5 hours
* 3 minutes 2 seconds is truncated to 5 hours 3 minutes)
* @return
*/
public static String getAbbreviatedDurationString(Resources r, int timeInSeconds,
int unitsToShow) {
short days, hours, minutes, seconds;
short unitsDisplayed = 0;
if(timeInSeconds == 0)
return r.getQuantityString(R.plurals.Nseconds, 0, 0);
days = (short)(timeInSeconds / 24 / 3600);
timeInSeconds -= days*24*3600;
hours = (short)(timeInSeconds / 3600);
timeInSeconds -= hours * 3600;
minutes = (short)(timeInSeconds / 60);
timeInSeconds -= minutes * 60;
seconds = (short)timeInSeconds;
StringBuilder result = new StringBuilder();
if(days > 0) {
result.append(r.getQuantityString(R.plurals.Ndays, days, days)).
append(" ");
unitsDisplayed++;
}
if(unitsDisplayed < unitsToShow && hours > 0) {
result.append(r.getQuantityString(R.plurals.NhoursShort, hours,
hours)).
append(" ");
unitsDisplayed++;
}
if(unitsDisplayed < unitsToShow && minutes > 0) {
result.append(r.getQuantityString(R.plurals.NminutesShort, minutes,
minutes)).append(" ");
unitsDisplayed++;
}
if(unitsDisplayed < unitsToShow && seconds > 0) {
result.append(r.getQuantityString(R.plurals.NsecondsShort, seconds,
seconds)).append(" ");
}
return result.toString().trim();
}
/** /**
* Format a time into the format: 5 d, 3 h, 2 m * Format a time into the format: 5 d, 3 h, 2 m
* *

@ -20,8 +20,8 @@ import android.net.Uri;
import android.util.Log; import android.util.Log;
import com.timsu.astrid.R; import com.timsu.astrid.R;
import com.timsu.astrid.activities.TaskView; import com.timsu.astrid.activities.TaskListNotify;
import com.timsu.astrid.activities.TaskViewNotifier; import com.timsu.astrid.activities.TaskListSubActivity;
import com.timsu.astrid.data.alerts.AlertController; import com.timsu.astrid.data.alerts.AlertController;
import com.timsu.astrid.data.task.TaskController; import com.timsu.astrid.data.task.TaskController;
import com.timsu.astrid.data.task.TaskIdentifier; import com.timsu.astrid.data.task.TaskIdentifier;
@ -136,7 +136,7 @@ public class Notifications extends BroadcastReceiver {
if(task.getTaskIdentifier() == null) if(task.getTaskIdentifier() == null)
return; return;
// return if we don't need to go any further // return if we don't need to go any further
if(shouldDeleteAlarm(task)) { if(shouldDeleteAlarm(task)) {
deleteAlarm(context, task.getTaskIdentifier().getId()); deleteAlarm(context, task.getTaskIdentifier().getId());
return; return;
@ -376,12 +376,11 @@ public class Notifications extends BroadcastReceiver {
.getSystemService(Context.NOTIFICATION_SERVICE); .getSystemService(Context.NOTIFICATION_SERVICE);
Resources r = context.getResources(); Resources r = context.getResources();
Intent notifyIntent = new Intent(context, TaskViewNotifier.class); Intent notifyIntent = new Intent(context, TaskListNotify.class);
notifyIntent.setFlags(Intent.FLAG_ACTIVITY_NO_HISTORY); notifyIntent.putExtra(TaskListSubActivity.LOAD_INSTANCE_TOKEN, id);
notifyIntent.putExtra(TaskViewNotifier.LOAD_INSTANCE_TOKEN, id); notifyIntent.putExtra(TaskListSubActivity.FROM_NOTIFICATION_TOKEN, true);
notifyIntent.putExtra(TaskViewNotifier.FROM_NOTIFICATION_TOKEN, true); notifyIntent.putExtra(TaskListSubActivity.NOTIF_FLAGS_TOKEN, flags);
notifyIntent.putExtra(TaskViewNotifier.NOTIF_FLAGS_TOKEN, flags); notifyIntent.putExtra(TaskListSubActivity.NOTIF_REPEAT_TOKEN, repeatInterval);
notifyIntent.putExtra(TaskViewNotifier.NOTIF_REPEAT_TOKEN, repeatInterval);
PendingIntent pendingIntent = PendingIntent.getActivity(context, PendingIntent pendingIntent = PendingIntent.getActivity(context,
(int)id, notifyIntent, PendingIntent.FLAG_ONE_SHOT); (int)id, notifyIntent, PendingIntent.FLAG_ONE_SHOT);
@ -437,7 +436,7 @@ public class Notifications extends BroadcastReceiver {
} }
/** Show a notification when a user is "on-the-clock" for a given task */ /** Show a notification when a user is "on-the-clock" for a given task */
public static boolean showTimingNotification(Context context, public static boolean showTimingNotification(Context context,
TaskIdentifier taskId, String taskName) { TaskIdentifier taskId, String taskName) {
String text = context.getResources().getString(R.string.notif_timerStarted) + String text = context.getResources().getString(R.string.notif_timerStarted) +
@ -446,10 +445,9 @@ public class Notifications extends BroadcastReceiver {
.getSystemService(Context.NOTIFICATION_SERVICE); .getSystemService(Context.NOTIFICATION_SERVICE);
Resources r = context.getResources(); Resources r = context.getResources();
Intent notifyIntent = new Intent(context, TaskView.class); Intent notifyIntent = new Intent(context, TaskListNotify.class);
notifyIntent.setFlags(Intent.FLAG_ACTIVITY_NO_HISTORY); notifyIntent.putExtra(TaskListSubActivity.LOAD_INSTANCE_TOKEN, taskId.getId());
notifyIntent.putExtra(TaskViewNotifier.LOAD_INSTANCE_TOKEN, taskId.getId()); notifyIntent.putExtra(TaskListSubActivity.FROM_NOTIFICATION_TOKEN, true);
notifyIntent.putExtra(TaskViewNotifier.FROM_NOTIFICATION_TOKEN, true);
PendingIntent pendingIntent = PendingIntent.getActivity(context, PendingIntent pendingIntent = PendingIntent.getActivity(context,
(int)taskId.getId(), notifyIntent, PendingIntent.FLAG_ONE_SHOT); (int)taskId.getId(), notifyIntent, PendingIntent.FLAG_ONE_SHOT);
@ -462,7 +460,9 @@ public class Notifications extends BroadcastReceiver {
appName, appName,
text, text,
pendingIntent); pendingIntent);
notification.flags |= Notification.FLAG_ONGOING_EVENT; notification.flags |= Notification.FLAG_ONGOING_EVENT |
Notification.FLAG_NO_CLEAR;
notification.flags &= ~Notification.FLAG_AUTO_CANCEL;
Log.w("Astrid", "Logging timing notification: " + text); Log.w("Astrid", "Logging timing notification: " + text);
nm.notify((int)taskId.getId(), notification); nm.notify((int)taskId.getId(), notification);
@ -470,5 +470,5 @@ public class Notifications extends BroadcastReceiver {
return true; return true;
} }
} }
Loading…
Cancel
Save