Bug fixes to the chagnes made in Astrid.

pull/14/head
Tim Su 17 years ago
parent 753ab8a23e
commit 5aa45b1dc6

@ -1,7 +1,7 @@
<?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="73" android:versionCode="74"
android:versionName="2.2.0"> android:versionName="2.2.0">
<meta-data android:name="com.a0soft.gphone.aTrackDog.webURL" <meta-data android:name="com.a0soft.gphone.aTrackDog.webURL"
@ -14,7 +14,7 @@
<application android:icon="@drawable/icon" android:label="@string/app_name"> <application android:icon="@drawable/icon" android:label="@string/app_name">
<activity android:name=".activities.MainActivity" <activity android:name=".activities.TaskList"
android:label="@string/app_name"> android:label="@string/app_name">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.MAIN" /> <action android:name="android.intent.action.MAIN" />

Binary file not shown.

Before

Width:  |  Height:  |  Size: 882 B

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 579 B

After

Width:  |  Height:  |  Size: 1.4 KiB

@ -1,366 +0,0 @@
package com.timsu.astrid.activities;
import java.util.Date;
import android.app.Activity;
import android.content.Intent;
import android.content.res.Resources;
import android.database.Cursor;
import android.os.Bundle;
import android.util.Log;
import android.view.GestureDetector;
import android.view.Menu;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.View;
import android.view.GestureDetector.SimpleOnGestureListener;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.widget.ViewFlipper;
import com.timsu.astrid.R;
import com.timsu.astrid.data.tag.TagController;
import com.timsu.astrid.data.task.TaskController;
import com.timsu.astrid.sync.Synchronizer;
import com.timsu.astrid.utilities.Constants;
import com.timsu.astrid.utilities.Preferences;
import com.timsu.astrid.utilities.StartupReceiver;
/**
* Main activity uses a ViewFlipper to flip between child views.
*
* @author Tim Su (timsu@stanfordalumni.org)
*/
public class MainActivity extends Activity {
/**
* Interface for views that are displayed from the main view page
*
* @author timsu
*/
abstract public static class SubActivity {
private MainActivity parent;
private SubActivities code;
private View view;
public SubActivity(MainActivity parent, SubActivities code, View view) {
this.parent = parent;
this.code = code;
this.view = view;
view.setTag(this);
}
// --- pass-through to activity listeners
abstract void onDisplay(Bundle variables);
abstract boolean onPrepareOptionsMenu(Menu menu);
abstract void onActivityResult(int requestCode, int resultCode, Intent data);
abstract boolean onOptionsItemSelected(MenuItem item);
void onWindowFocusChanged(boolean hasFocus) {
//
}
// --- pass-through to activity methods
public Resources getResources() {
return parent.getResources();
}
public View findViewById(int id) {
return view.findViewById(id);
}
public void startManagingCursor(Cursor c) {
parent.startManagingCursor(c);
}
public void setTitle(CharSequence title) {
parent.setTitle(title);
}
public void closeActivity() {
parent.finish();
}
public void launchActivity(Intent intent, int requestCode) {
parent.startActivityForResult(intent, requestCode);
}
// --- helper methods
public Activity getParent() {
return parent;
}
public TaskController getTaskController() {
return parent.taskController;
}
public TagController getTagController() {
return parent.tagController;
}
public View.OnTouchListener getGestureListener() {
return parent.gestureListener;
}
public void switchToActivity(SubActivities activity, Bundle state) {
parent.switchToActivity(activity, state);
}
// --- internal methods
public SubActivities getActivityCode() {
return code;
}
}
/* ======================================================================
* ======================================================= internal stuff
* ====================================================================== */
public enum SubActivities {
TASK_LIST,
TAG_LIST,
TASK_LIST_W_TAG
};
public static final int FLING_DIST_THRESHOLD = 100;
public static final int FLING_VEL_THRESHOLD = 300;
// view components
private ViewFlipper viewFlipper;
private GestureDetector gestureDetector;
private View.OnTouchListener gestureListener;
private SubActivity taskList;
private SubActivity tagList;
private SubActivity taskListWTag;
// animations
private Animation mInAnimationForward;
private Animation mOutAnimationForward;
private Animation mInAnimationBackward;
private Animation mOutAnimationBackward;
// data controllers
private TaskController taskController;
private TagController tagController;
// static variables
static boolean shouldCloseInstance = false;
@Override
/** Called when loading up the activity for the first time */
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
// open controllers & perform application startup rituals
StartupReceiver.onStartupApplication(this);
shouldCloseInstance = false;
taskController = new TaskController(this);
taskController.open();
tagController = new TagController(this);
tagController.open();
Synchronizer.setTagController(tagController);
Synchronizer.setTaskController(taskController);
setupUIComponents();
getCurrentSubActivity().onDisplay(savedInstanceState);
// auto sync if requested
Integer autoSyncHours = Preferences.autoSyncFrequency(this);
if(autoSyncHours != null) {
final Date lastSync = Preferences.getSyncLastSync(this);
if(lastSync == null || lastSync.getTime() +
1000L*3600*autoSyncHours < System.currentTimeMillis()) {
Synchronizer.synchronize(this, true, null);
}
}
}
/** Set up user interface components */
private void setupUIComponents() {
gestureDetector = new GestureDetector(new AstridGestureDetector());
viewFlipper = (ViewFlipper)findViewById(R.id.main);
taskList = new TaskList(this, SubActivities.TASK_LIST,
findViewById(R.id.tasklist_layout));
tagList = new TagList(this, SubActivities.TAG_LIST,
findViewById(R.id.taglist_layout));
taskListWTag = new TaskList(this, SubActivities.TASK_LIST_W_TAG,
findViewById(R.id.tasklistwtag_layout));
mInAnimationForward = AnimationUtils.loadAnimation(this, R.anim.slide_left_in);
mOutAnimationForward = AnimationUtils.loadAnimation(this, R.anim.slide_left_out);
mInAnimationBackward = AnimationUtils.loadAnimation(this, R.anim.slide_right_in);
mOutAnimationBackward = AnimationUtils.loadAnimation(this, R.anim.slide_right_out);
gestureListener = new View.OnTouchListener() {
public boolean onTouch(View v, MotionEvent event) {
if (gestureDetector.onTouchEvent(event)) {
return true;
}
return false;
}
};
}
private class AstridGestureDetector extends SimpleOnGestureListener {
@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
try {
Log.i("astrid", "Got fling. X: " + (e2.getX() - e1.getX()) +
", vel: " + velocityX);
// flick R to L
if(e1.getX() - e2.getX() > FLING_DIST_THRESHOLD &&
Math.abs(velocityX) > FLING_VEL_THRESHOLD) {
switch(getCurrentSubActivity().getActivityCode()) {
case TASK_LIST:
switchToActivity(SubActivities.TAG_LIST, null);
return true;
default:
return false;
}
}
// flick L to R
else if(e2.getX() - e1.getX() > FLING_DIST_THRESHOLD &&
Math.abs(velocityX) > FLING_VEL_THRESHOLD) {
switch(getCurrentSubActivity().getActivityCode()) {
case TASK_LIST_W_TAG:
switchToActivity(SubActivities.TAG_LIST, null);
return true;
case TAG_LIST:
switchToActivity(SubActivities.TASK_LIST, null);
return true;
default:
return false;
}
}
} catch (Exception e) {
// ignore!
}
return false;
}
}
/* ======================================================================
* ==================================================== subactivity stuff
* ====================================================================== */
private void switchToActivity(SubActivities activity, Bundle variables) {
// initialize the components
switch(activity) {
case TASK_LIST:
taskList.onDisplay(variables);
break;
case TAG_LIST:
tagList.onDisplay(variables);
break;
case TASK_LIST_W_TAG:
taskListWTag.onDisplay(variables);
}
// and flip to them
switch(getCurrentSubActivity().getActivityCode()) {
case TASK_LIST:
viewFlipper.setInAnimation(mInAnimationForward);
viewFlipper.setOutAnimation(mOutAnimationForward);
viewFlipper.showNext();
if(activity == SubActivities.TASK_LIST_W_TAG)
viewFlipper.showNext();
break;
case TAG_LIST:
switch(activity) {
case TASK_LIST:
viewFlipper.setInAnimation(mInAnimationBackward);
viewFlipper.setOutAnimation(mOutAnimationBackward);
viewFlipper.showPrevious();
break;
case TASK_LIST_W_TAG:
viewFlipper.setInAnimation(mInAnimationForward);
viewFlipper.setOutAnimation(mOutAnimationForward);
viewFlipper.showNext();
break;
}
break;
case TASK_LIST_W_TAG:
viewFlipper.setInAnimation(mInAnimationBackward);
viewFlipper.setOutAnimation(mOutAnimationBackward);
viewFlipper.showPrevious();
if(activity == SubActivities.TASK_LIST_W_TAG)
viewFlipper.showPrevious();
break;
}
viewFlipper.getCurrentView().requestFocus();
}
private SubActivity getCurrentSubActivity() {
return (SubActivity)viewFlipper.getCurrentView().getTag();
}
/* ======================================================================
* ======================================================= event handling
* ====================================================================== */
@Override
public boolean onPrepareOptionsMenu(Menu menu) {
return getCurrentSubActivity().onPrepareOptionsMenu(menu);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if(resultCode == Constants.RESULT_GO_HOME) {
switchToActivity(SubActivities.TASK_LIST, null);
} else
getCurrentSubActivity().onActivityResult(requestCode, resultCode, data);
}
@Override
public void onWindowFocusChanged(boolean hasFocus) {
super.onWindowFocusChanged(hasFocus);
if(hasFocus && shouldCloseInstance) { // user wants to quit
finish();
} else
getCurrentSubActivity().onWindowFocusChanged(hasFocus);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
if(getCurrentSubActivity().onOptionsItemSelected(item) == true)
return true;
else
return super.onOptionsItemSelected(item);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
if (gestureDetector.onTouchEvent(event))
return true;
else
return false;
}
@Override
protected void onDestroy() {
super.onDestroy();
if(taskController != null)
taskController.close();
if(tagController != null)
tagController.close();
Synchronizer.setTagController(null);
Synchronizer.setTaskController(null);
}
}

@ -32,6 +32,7 @@ import android.content.res.Resources;
import android.database.Cursor; import android.database.Cursor;
import android.os.Bundle; import android.os.Bundle;
import android.view.ContextMenu; import android.view.ContextMenu;
import android.view.KeyEvent;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.Menu; import android.view.Menu;
import android.view.MenuItem; import android.view.MenuItem;
@ -47,8 +48,8 @@ import android.widget.AdapterView.AdapterContextMenuInfo;
import android.widget.AdapterView.OnItemClickListener; import android.widget.AdapterView.OnItemClickListener;
import com.timsu.astrid.R; import com.timsu.astrid.R;
import com.timsu.astrid.activities.MainActivity.SubActivities; import com.timsu.astrid.activities.TaskList.ActivityCode;
import com.timsu.astrid.activities.MainActivity.SubActivity; import com.timsu.astrid.activities.TaskList.SubActivity;
import com.timsu.astrid.data.tag.TagIdentifier; import com.timsu.astrid.data.tag.TagIdentifier;
import com.timsu.astrid.data.tag.TagModelForView; import com.timsu.astrid.data.tag.TagModelForView;
import com.timsu.astrid.data.task.TaskIdentifier; import com.timsu.astrid.data.task.TaskIdentifier;
@ -61,7 +62,7 @@ import com.timsu.astrid.data.task.TaskModelForList;
* @author Tim Su (timsu@stanfordalumni.org) * @author Tim Su (timsu@stanfordalumni.org)
* *
*/ */
public class TagList extends SubActivity { public class TagListSubActivity extends SubActivity {
private static final int ACTIVITY_CREATE = 0; private static final int ACTIVITY_CREATE = 0;
private static final int MENU_SORT_ALPHA_ID = Menu.FIRST; private static final int MENU_SORT_ALPHA_ID = Menu.FIRST;
@ -78,7 +79,7 @@ public class TagList extends SubActivity {
private static SortMode sortMode = SortMode.SIZE; private static SortMode sortMode = SortMode.SIZE;
private static boolean sortReverse = false; private static boolean sortReverse = false;
public TagList(MainActivity parent, SubActivities code, View view) { public TagListSubActivity(TaskList parent, ActivityCode code, View view) {
super(parent, code, view); super(parent, code, view);
} }
@ -93,18 +94,18 @@ public class TagList extends SubActivity {
private enum SortMode { private enum SortMode {
ALPHA { ALPHA {
@Override @Override
int compareTo(TagList self, TagModelForView arg0, TagModelForView arg1) { int compareTo(TagListSubActivity self, TagModelForView arg0, TagModelForView arg1) {
return arg0.getName().compareTo(arg1.getName()); return arg0.getName().compareTo(arg1.getName());
} }
}, },
SIZE { SIZE {
@Override @Override
int compareTo(TagList self, TagModelForView arg0, TagModelForView arg1) { int compareTo(TagListSubActivity self, TagModelForView arg0, TagModelForView arg1) {
return self.tagToTaskCount.get(arg1) - self.tagToTaskCount.get(arg0); return self.tagToTaskCount.get(arg1) - self.tagToTaskCount.get(arg0);
} }
}; };
abstract int compareTo(TagList self, TagModelForView arg0, TagModelForView arg1); abstract int compareTo(TagListSubActivity self, TagModelForView arg0, TagModelForView arg1);
}; };
private void sortTagArray() { private void sortTagArray() {
@ -137,7 +138,7 @@ public class TagList extends SubActivity {
Collections.sort(tagArray, new Comparator<TagModelForView>() { Collections.sort(tagArray, new Comparator<TagModelForView>() {
@Override @Override
public int compare(TagModelForView arg0, TagModelForView arg1) { public int compare(TagModelForView arg0, TagModelForView arg1) {
return sortMode.compareTo(TagList.this, arg0, arg1); return sortMode.compareTo(TagListSubActivity.this, arg0, arg1);
} }
}); });
if(sortReverse) if(sortReverse)
@ -175,8 +176,8 @@ public class TagList extends SubActivity {
TagModelForView tag = (TagModelForView)view.getTag(); TagModelForView tag = (TagModelForView)view.getTag();
Bundle bundle = new Bundle(); Bundle bundle = new Bundle();
bundle.putLong(TaskList.TAG_TOKEN, tag.getTagIdentifier().getId()); bundle.putLong(TaskListSubActivity.TAG_TOKEN, tag.getTagIdentifier().getId());
switchToActivity(SubActivities.TASK_LIST_W_TAG, bundle); switchToActivity(ActivityCode.TASK_LIST_W_TAG, bundle);
} }
}); });
@ -239,7 +240,19 @@ public class TagList extends SubActivity {
} }
@Override @Override
public boolean onOptionsItemSelected(MenuItem item) { /** Handle back button by moving to task list */
protected boolean onKeyDown(int keyCode, KeyEvent event) {
if(keyCode == KeyEvent.KEYCODE_BACK) {
switchToActivity(ActivityCode.TASK_LIST, null);
return true;
}
return false;
}
@Override
/** Picked item in the options list */
public boolean onMenuItemSelected(int featureId, MenuItem item) {
switch(item.getItemId()) { switch(item.getItemId()) {
case MENU_SORT_ALPHA_ID: case MENU_SORT_ALPHA_ID:
if(sortMode == SortMode.ALPHA) if(sortMode == SortMode.ALPHA)
@ -336,6 +349,8 @@ public class TagList extends SubActivity {
if(tagToTaskCount.get(tag) == 0) if(tagToTaskCount.get(tag) == 0)
name.setTextColor(r.getColor(R.color.task_list_done)); name.setTextColor(r.getColor(R.color.task_list_done));
else
name.setTextColor(r.getColor(android.R.color.white));
} }
} }

@ -80,7 +80,8 @@ import com.timsu.astrid.widget.TimeDurationControlSet.TimeDurationType;
public class TaskEdit extends TaskModificationTabbedActivity<TaskModelForEdit> { public class TaskEdit extends TaskModificationTabbedActivity<TaskModelForEdit> {
// bundle arguments // bundle arguments
public static final String TAG_NAME_TOKEN = "tag"; public static final String TAG_NAME_TOKEN = "t";
public static final String START_CHAR_TOKEN = "s";
// menu items // menu items
private static final int SAVE_ID = Menu.FIRST; private static final int SAVE_ID = Menu.FIRST;
@ -169,6 +170,11 @@ public class TaskEdit extends TaskModificationTabbedActivity<TaskModelForEdit> {
// set UI components based on model variables // set UI components based on model variables
if(model.getCursor() != null) if(model.getCursor() != null)
startManagingCursor(model.getCursor()); startManagingCursor(model.getCursor());
if(model.getTaskIdentifier() == null) {
Bundle extras = getIntent().getExtras();
if(extras != null && extras.containsKey(START_CHAR_TOKEN))
name.setText("" + extras.getChar(START_CHAR_TOKEN));
} else
name.setText(model.getName()); name.setText(model.getName());
if(model.getName().length() > 0) if(model.getName().length() > 0)
setTitle(new StringBuilder(). setTitle(new StringBuilder().
@ -507,7 +513,7 @@ public class TaskEdit extends TaskModificationTabbedActivity<TaskModelForEdit> {
public void onWindowFocusChanged(boolean hasFocus) { public void onWindowFocusChanged(boolean hasFocus) {
super.onWindowFocusChanged(hasFocus); super.onWindowFocusChanged(hasFocus);
if(hasFocus && MainActivity.shouldCloseInstance) { // user wants to quit if(hasFocus && TaskList.shouldCloseInstance) { // user wants to quit
finish(); finish();
} }
} }

@ -1,694 +1,417 @@
/*
* 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; package com.timsu.astrid.activities;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date; import java.util.Date;
import java.util.HashMap;
import java.util.Iterator; import android.app.Activity;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.Intent; import android.content.Intent;
import android.content.res.Resources; import android.content.res.Resources;
import android.database.Cursor; import android.database.Cursor;
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.GestureDetector;
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.View; import android.view.View;
import android.view.ContextMenu.ContextMenuInfo; import android.view.ContextMenu.ContextMenuInfo;
import android.view.View.OnCreateContextMenuListener; import android.view.GestureDetector.SimpleOnGestureListener;
import android.widget.AdapterView; import android.view.animation.Animation;
import android.widget.Button; import android.view.animation.AnimationUtils;
import android.widget.ListView; import android.widget.ViewFlipper;
import android.widget.AdapterView.OnItemClickListener;
import com.timsu.astrid.R; import com.timsu.astrid.R;
import com.timsu.astrid.activities.MainActivity.SubActivities;
import com.timsu.astrid.activities.MainActivity.SubActivity;
import com.timsu.astrid.activities.TaskListAdapter.TaskListAdapterHooks;
import com.timsu.astrid.data.tag.TagController; import com.timsu.astrid.data.tag.TagController;
import com.timsu.astrid.data.tag.TagIdentifier;
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.sync.Synchronizer; import com.timsu.astrid.sync.Synchronizer;
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.Preferences; import com.timsu.astrid.utilities.Preferences;
import com.timsu.astrid.widget.NNumberPickerDialog.OnNNumberPickedListener; import com.timsu.astrid.utilities.StartupReceiver;
/** /**
* Primary view for the Astrid Application. Lists all of the tasks in the * Main activity uses a ViewFlipper to flip between child views.
* system, and allows users to edit them.
* *
* @author Tim Su (timsu@stanfordalumni.org) * @author Tim Su (timsu@stanfordalumni.org)
*
*/ */
public class TaskList extends SubActivity { public class TaskList extends Activity {
// bundle tokens
public static final String TAG_TOKEN = "tag";
// activities
private static final int ACTIVITY_CREATE = 0;
private static final int ACTIVITY_VIEW = 1;
private static final int ACTIVITY_EDIT = 2;
private static final int ACTIVITY_TAGS = 3;
private static final int ACTIVITY_SYNCHRONIZE = 4;
// menu codes
private static final int INSERT_ID = Menu.FIRST;
private static final int FILTERS_ID = Menu.FIRST + 1;
private static final int TAGS_ID = Menu.FIRST + 2;
private static final int SYNC_ID = Menu.FIRST + 3;
private static final int MORE_ID = Menu.FIRST + 4;
private static final int OPTIONS_SYNC_ID = Menu.FIRST + 10;
private static final int OPTIONS_SETTINGS_ID = Menu.FIRST + 11;
private static final int OPTIONS_HELP_ID = Menu.FIRST + 12;
private static final int CONTEXT_FILTER_HIDDEN = Menu.FIRST + 20;
private static final int CONTEXT_FILTER_DONE = Menu.FIRST + 21;
private static final int CONTEXT_FILTER_TAG = Menu.FIRST + 22;
private static final int CONTEXT_SORT_AUTO = Menu.FIRST + 23;
private static final int CONTEXT_SORT_ALPHA = Menu.FIRST + 24;
private static final int CONTEXT_SORT_DUEDATE = Menu.FIRST + 25;
private static final int CONTEXT_SORT_REVERSE = Menu.FIRST + 26;
private static final int CONTEXT_SORT_GROUP = Menu.FIRST;
private static final int SORTFLAG_FILTERDONE = (1 << 5);
private static final int SORTFLAG_FILTERHIDDEN = (1 << 6);
// UI components
private ListView listView;
private Button addButton;
private View layout;
// other instance variables
private Map<TagIdentifier, TagModelForView> tagMap;
private ArrayList<TaskModelForList> taskArray;
private HashMap<TaskModelForList, LinkedList<TagModelForView>> taskTags;
// display filters
private static boolean filterShowHidden = false;
private static boolean filterShowDone = false;
private static TagModelForView filterTag = null;
private static SortMode sortMode = SortMode.AUTO;
private static boolean sortReverse = false;
/* ====================================================================== /**
* ======================================================= initialization * Interface for views that are displayed from the main view page
* ====================================================================== */ *
* @author timsu
*/
abstract public static class SubActivity {
private TaskList parent;
private ActivityCode code;
private View view;
public TaskList(MainActivity parent, SubActivities code, View view) { public SubActivity(TaskList parent, ActivityCode code, View view) {
super(parent, code, view); this.parent = parent;
this.code = code;
this.view = view;
view.setTag(this);
} }
@Override // --- pass-through to activity listeners
/** Called when loading up the activity */
public void onDisplay(Bundle variables) {
// load tag map
tagMap = getTagController().getAllTagsAsMap(getParent());
// process the tag to filter on, if any /** Called when this subactivity is displayed to the user */
if(variables != null && variables.containsKey(TAG_TOKEN)) { void onDisplay(Bundle variables) {
TagIdentifier identifier = new TagIdentifier(variables.getLong(TAG_TOKEN)); //
filterTag = tagMap.get(identifier);
} }
setupUIComponents(); boolean onPrepareOptionsMenu(Menu menu) {
loadTaskListSort(); return false;
fillData();
} }
public void setupUIComponents() { void onActivityResult(int requestCode, int resultCode, Intent data) {
listView = (ListView)findViewById(R.id.tasklist); //
addButton = (Button)findViewById(R.id.addtask);
addButton.setOnClickListener(new
View.OnClickListener() {
@Override
public void onClick(View v) {
createTask();
} }
});
layout = findViewById(R.id.tasklist_layout); boolean onMenuItemSelected(int featureId, MenuItem item) {
layout.setOnCreateContextMenuListener( return false;
new OnCreateContextMenuListener() {
@Override
public void onCreateContextMenu(ContextMenu menu, View v,
ContextMenuInfo menuInfo) {
if(menu.hasVisibleItems())
return;
onCreateMoreOptionsMenu(menu);
} }
});
void onWindowFocusChanged(boolean hasFocus) {
//
} }
@Override boolean onKeyDown(int keyCode, KeyEvent event) {
public boolean onPrepareOptionsMenu(Menu menu) { return false;
MenuItem item; }
item = menu.add(Menu.NONE, INSERT_ID, Menu.NONE, // --- pass-through to activity methods
R.string.taskList_menu_insert);
item.setIcon(android.R.drawable.ic_menu_add);
item.setAlphabeticShortcut('n');
item = menu.add(Menu.NONE, FILTERS_ID, Menu.NONE, public Resources getResources() {
R.string.taskList_menu_filters); return parent.getResources();
item.setIcon(android.R.drawable.ic_menu_view); }
item.setAlphabeticShortcut('f');
item = menu.add(Menu.NONE, TAGS_ID, Menu.NONE, public View findViewById(int id) {
R.string.taskList_menu_tags); return view.findViewById(id);
item.setIcon(android.R.drawable.ic_menu_myplaces); }
item.setAlphabeticShortcut('t');
if(Preferences.shouldDisplaySyncButton(getParent())){ public void startManagingCursor(Cursor c) {
item = menu.add(Menu.NONE, SYNC_ID, Menu.NONE, parent.startManagingCursor(c);
R.string.taskList_menu_syncshortcut);
item.setIcon(android.R.drawable.ic_menu_upload);
item.setAlphabeticShortcut('s');
} }
item = menu.add(Menu.NONE, MORE_ID, Menu.NONE, public void setTitle(CharSequence title) {
R.string.taskList_menu_more); parent.setTitle(title);
item.setIcon(android.R.drawable.ic_menu_more); }
item.setAlphabeticShortcut('m');
return true; public void closeActivity() {
parent.finish();
} }
public boolean onCreateMoreOptionsMenu(Menu menu) { public void launchActivity(Intent intent, int requestCode) {
MenuItem item; parent.startActivityForResult(intent, requestCode);
}
item = menu.add(Menu.NONE, OPTIONS_SYNC_ID, Menu.NONE, // --- helper methods
R.string.taskList_menu_sync);
item.setAlphabeticShortcut('s');
item = menu.add(Menu.NONE, OPTIONS_SETTINGS_ID, Menu.NONE, public Activity getParent() {
R.string.taskList_menu_settings); return parent;
item.setAlphabeticShortcut('p'); }
item = menu.add(Menu.NONE, OPTIONS_HELP_ID, Menu.NONE, public TaskController getTaskController() {
R.string.taskList_menu_help); return parent.taskController;
item.setAlphabeticShortcut('h'); }
return true; public TagController getTagController() {
return parent.tagController;
} }
private enum SortMode { public View.OnTouchListener getGestureListener() {
ALPHA { return parent.gestureListener;
@Override
int compareTo(TaskModelForList arg0, TaskModelForList arg1) {
return arg0.getName().compareTo(arg1.getName());
}
},
DUEDATE {
long getDueDate(TaskModelForList task) {
Date definite = task.getDefiniteDueDate();
Date preferred = task.getPreferredDueDate();
if(definite != null && preferred != null) {
if(preferred.before(new Date()))
return definite.getTime();
return preferred.getTime();
} else if(definite != null)
return definite.getTime();
else if(preferred != null)
return preferred.getTime();
else
return new Date(2020,1,1).getTime();
} }
@Override
int compareTo(TaskModelForList arg0, TaskModelForList arg1) { public void switchToActivity(ActivityCode activity, Bundle state) {
return (int)((getDueDate(arg0) - getDueDate(arg1))/1000); parent.switchToActivity(activity, state);
} }
},
AUTO { // --- internal methods
@Override
int compareTo(TaskModelForList arg0, TaskModelForList arg1) { protected ActivityCode getActivityCode() {
return arg0.getTaskWeight() - arg1.getTaskWeight(); return code;
} }
};
abstract int compareTo(TaskModelForList arg0, TaskModelForList arg1); protected View getView() {
}; return view;
}
}
/* ====================================================================== /* ======================================================================
* ====================================================== populating list * ======================================================= internal stuff
* ====================================================================== */ * ====================================================================== */
private boolean isTaskHidden(TaskModelForList task) {
if(task.isHidden())
return true;
if(filterTag == null) { public enum ActivityCode {
for(TagModelForView tags : taskTags.get(task)) { TASK_LIST,
if(tags.shouldHideFromMainList()) TAG_LIST,
return true; TASK_LIST_W_TAG
} };
}
return false; private static final String TAG_LAST_ACTIVITY = "l";
} private static final String TAG_LAST_BUNDLE = "b";
private static final int FLING_DIST_THRESHOLD = 100;
private static final int FLING_VEL_THRESHOLD = 300;
// view components
private ViewFlipper viewFlipper;
private GestureDetector gestureDetector;
private View.OnTouchListener gestureListener;
private SubActivity taskList;
private SubActivity tagList;
private SubActivity taskListWTag;
private Bundle lastActivityBundle;
// animations
private Animation mInAnimationForward;
private Animation mOutAnimationForward;
private Animation mInAnimationBackward;
private Animation mOutAnimationBackward;
// data controllers
private TaskController taskController;
private TagController tagController;
// static variables
static boolean shouldCloseInstance = false;
@Override
/** Called when loading up the activity for the first time */
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
// open controllers & perform application startup rituals
StartupReceiver.onStartupApplication(this);
shouldCloseInstance = false;
taskController = new TaskController(this);
taskController.open();
tagController = new TagController(this);
tagController.open();
Synchronizer.setTagController(tagController);
Synchronizer.setTaskController(taskController);
/** Fill in the Task List with our tasks */ setupUIComponents();
private void fillData() {
Resources r = getResources();
// get a cursor to the task list if(savedInstanceState != null && savedInstanceState.containsKey(TAG_LAST_ACTIVITY)) {
Cursor tasksCursor; viewFlipper.setDisplayedChild(savedInstanceState.getInt(TAG_LAST_ACTIVITY));
if(filterTag != null) { Bundle variables = savedInstanceState.getBundle(TAG_LAST_BUNDLE);
List<TaskIdentifier> tasks = getTagController().getTaggedTasks(getParent(), getCurrentSubActivity().onDisplay(variables);
filterTag.getTagIdentifier());
tasksCursor = getTaskController().getTaskListCursorById(tasks);
} else { } else {
if(filterShowDone) getCurrentSubActivity().onDisplay(null);
tasksCursor = getTaskController().getAllTaskListCursor();
else
tasksCursor = getTaskController().getActiveTaskListCursor();
} }
startManagingCursor(tasksCursor);
taskArray = getTaskController().createTaskListFromCursor(tasksCursor);
// read tags and apply filters // auto sync if requested
int hiddenTasks = 0; // # of tasks hidden Integer autoSyncHours = Preferences.autoSyncFrequency(this);
int completedTasks = 0; // # of tasks on list that are done if(autoSyncHours != null) {
taskTags = new HashMap<TaskModelForList, LinkedList<TagModelForView>>(); final Date lastSync = Preferences.getSyncLastSync(this);
for(Iterator<TaskModelForList> i = taskArray.iterator(); i.hasNext();) {
TaskModelForList task = i.next();
if(task.isTaskCompleted()) { if(lastSync == null || lastSync.getTime() +
if(!filterShowDone) { 1000L*3600*autoSyncHours < System.currentTimeMillis()) {
i.remove(); Synchronizer.synchronize(this, true, null);
continue;
} }
} }
// get list of tags
LinkedList<TagIdentifier> tagIds = getTagController().getTaskTags(getParent(),
task.getTaskIdentifier());
LinkedList<TagModelForView> tags = new LinkedList<TagModelForView>();
for(TagIdentifier tagId : tagIds) {
TagModelForView tag = tagMap.get(tagId);
tags.add(tag);
} }
taskTags.put(task, tags);
// hide hidden /** Set up user interface components */
if(!filterShowHidden) { private void setupUIComponents() {
if(isTaskHidden(task)) { gestureDetector = new GestureDetector(new AstridGestureDetector());
hiddenTasks++; viewFlipper = (ViewFlipper)findViewById(R.id.main);
i.remove(); taskList = new TaskListSubActivity(this, ActivityCode.TASK_LIST,
continue; findViewById(R.id.tasklist_layout));
} tagList = new TagListSubActivity(this, ActivityCode.TAG_LIST,
} findViewById(R.id.taglist_layout));
taskListWTag = new TaskListSubActivity(this, ActivityCode.TASK_LIST_W_TAG,
findViewById(R.id.tasklistwtag_layout));
if(task.isTaskCompleted()) mInAnimationForward = AnimationUtils.loadAnimation(this, R.anim.slide_left_in);
completedTasks++; mOutAnimationForward = AnimationUtils.loadAnimation(this, R.anim.slide_left_out);
} mInAnimationBackward = AnimationUtils.loadAnimation(this, R.anim.slide_right_in);
int activeTasks = taskArray.size() - completedTasks; mOutAnimationBackward = AnimationUtils.loadAnimation(this, R.anim.slide_right_out);
// sort task list gestureListener = new View.OnTouchListener() {
// do sort public boolean onTouch(View v, MotionEvent event) {
Collections.sort(taskArray, new Comparator<TaskModelForList>() { if (gestureDetector.onTouchEvent(event)) {
@Override return true;
public int compare(TaskModelForList arg0, TaskModelForList arg1) {
return sortMode.compareTo(arg0, arg1);
} }
}); return false;
if(sortReverse)
Collections.reverse(taskArray);
// hide "add" button if we have a few tasks
if(taskArray.size() > 4)
addButton.setVisibility(View.GONE);
else
addButton.setVisibility(View.VISIBLE);
// set up the title
StringBuilder title = new StringBuilder().
append(r.getString(R.string.taskList_titlePrefix)).append(" ");
if(filterTag != null) {
title.append(r.getString(R.string.taskList_titleTagPrefix,
filterTag.getName())).append(" ");
} }
};
if(completedTasks > 0)
title.append(r.getQuantityString(R.plurals.NactiveTasks,
activeTasks, activeTasks, taskArray.size()));
else
title.append(r.getQuantityString(R.plurals.Ntasks,
taskArray.size(), taskArray.size()));
if(hiddenTasks > 0)
title.append(" (+").append(hiddenTasks).append(" ").
append(r.getString(R.string.taskList_hiddenSuffix)).append(")");
setTitle(title);
setUpListUI();
} }
private void setUpListUI() { private class AstridGestureDetector extends SimpleOnGestureListener {
// set up our adapter
TaskListAdapter tasks = new TaskListAdapter(getParent(),
R.layout.task_list_row, taskArray, new TaskListAdapterHooks() {
@Override @Override
public TagController getTagController() { public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
return getTagController(); try {
Log.i("astrid", "Got fling. X: " + (e2.getX() - e1.getX()) +
", vel: " + velocityX);
// flick R to L
if(e1.getX() - e2.getX() > FLING_DIST_THRESHOLD &&
Math.abs(velocityX) > FLING_VEL_THRESHOLD) {
switch(getCurrentSubActivity().getActivityCode()) {
case TASK_LIST:
switchToActivity(ActivityCode.TAG_LIST, null);
return true;
default:
return false;
} }
@Override
public List<TagModelForView> getTagsFor(
TaskModelForList task) {
return taskTags.get(task);
} }
@Override // flick L to R
public List<TaskModelForList> getTaskArray() { else if(e2.getX() - e1.getX() > FLING_DIST_THRESHOLD &&
return taskArray; Math.abs(velocityX) > FLING_VEL_THRESHOLD) {
}
@Override switch(getCurrentSubActivity().getActivityCode()) {
public TaskController getTaskController() { case TASK_LIST_W_TAG:
return getTaskController(); switchToActivity(ActivityCode.TAG_LIST, null);
return true;
case TAG_LIST:
switchToActivity(ActivityCode.TASK_LIST, null);
return true;
default:
return false;
} }
@Override
public void performItemClick(View v, int position) {
listView.performItemClick(v, position, 0);
} }
} catch (Exception e) {
public void onCreatedTaskListView(View v, TaskModelForList task) { // ignore!
v.setOnTouchListener(getGestureListener());
} }
});
listView.setAdapter(tasks);
listView.setItemsCanFocus(true);
// list view listener return false;
listView.setOnItemClickListener(new OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view,
int position, long id) {
TaskModelForList task = (TaskModelForList)view.getTag();
Intent intent = new Intent(getParent(), TaskView.class);
intent.putExtra(TaskEdit.LOAD_INSTANCE_TOKEN, task.
getTaskIdentifier().getId());
launchActivity(intent, ACTIVITY_VIEW);
} }
});
// filters context menu
listView.setOnCreateContextMenuListener(new OnCreateContextMenuListener() {
@Override
public void onCreateContextMenu(ContextMenu menu, View v,
ContextMenuInfo menuInfo) {
if(menu.hasVisibleItems())
return;
Resources r = getResources();
menu.setHeaderTitle(R.string.taskList_filter_title);
MenuItem item = menu.add(Menu.NONE, CONTEXT_FILTER_HIDDEN,
Menu.NONE, R.string.taskList_filter_hidden);
item.setCheckable(true);
item.setChecked(filterShowHidden);
item = menu.add(Menu.NONE, CONTEXT_FILTER_DONE, Menu.NONE,
R.string.taskList_filter_done);
item.setCheckable(true);
item.setChecked(filterShowDone);
if(filterTag != null) {
item = menu.add(Menu.NONE, CONTEXT_FILTER_TAG, Menu.NONE,
r.getString(R.string.taskList_filter_tagged,
filterTag.getName()));
item.setCheckable(true);
item.setChecked(true);
}
item = menu.add(CONTEXT_SORT_GROUP, CONTEXT_SORT_AUTO, Menu.NONE,
R.string.taskList_sort_auto);
item.setChecked(sortMode == SortMode.AUTO);
item = menu.add(CONTEXT_SORT_GROUP, CONTEXT_SORT_ALPHA, Menu.NONE,
R.string.taskList_sort_alpha);
item.setChecked(sortMode == SortMode.ALPHA);
item = menu.add(CONTEXT_SORT_GROUP, CONTEXT_SORT_DUEDATE, Menu.NONE,
R.string.taskList_sort_duedate);
item.setChecked(sortMode == SortMode.DUEDATE);
menu.setGroupCheckable(CONTEXT_SORT_GROUP, true, true);
item = menu.add(CONTEXT_SORT_GROUP, CONTEXT_SORT_REVERSE, Menu.NONE,
R.string.taskList_sort_reverse);
item.setCheckable(true);
item.setChecked(sortReverse);
}
});
listView.setOnTouchListener(getGestureListener());
} }
/* ====================================================================== /* ======================================================================
* ======================================================= event handlers * ==================================================== subactivity stuff
* ====================================================================== */ * ====================================================================== */
@Override private void switchToActivity(ActivityCode activity, Bundle variables) {
protected void onActivityResult(int requestCode, int resultCode, Intent data) { closeOptionsMenu();
if(resultCode == Constants.RESULT_SYNCHRONIZE) {
Synchronizer.synchronize(getParent(), false, new SynchronizerListener() { // and flip to them
@Override switch(getCurrentSubActivity().getActivityCode()) {
public void onSynchronizerFinished(int numServicesSynced) { case TASK_LIST:
if(numServicesSynced == 0) viewFlipper.setInAnimation(mInAnimationForward);
DialogUtilities.okDialog(getParent(), getResources().getString( viewFlipper.setOutAnimation(mOutAnimationForward);
R.string.sync_no_synchronizers), null); viewFlipper.showNext();
fillData(); if(activity == ActivityCode.TASK_LIST_W_TAG)
} viewFlipper.showNext();
}); break;
} else if(requestCode == ACTIVITY_TAGS)
switchToActivity(SubActivities.TAG_LIST, null); case TAG_LIST:
switch(activity) {
case TASK_LIST:
viewFlipper.setInAnimation(mInAnimationBackward);
viewFlipper.setOutAnimation(mOutAnimationBackward);
viewFlipper.showPrevious();
break;
case TASK_LIST_W_TAG:
viewFlipper.setInAnimation(mInAnimationForward);
viewFlipper.setOutAnimation(mOutAnimationForward);
viewFlipper.showNext();
break;
}
break;
case TASK_LIST_W_TAG:
viewFlipper.setInAnimation(mInAnimationBackward);
viewFlipper.setOutAnimation(mOutAnimationBackward);
viewFlipper.showPrevious();
if(activity == ActivityCode.TASK_LIST_W_TAG)
viewFlipper.showPrevious();
break;
}
// initialize the components
switch(activity) {
case TASK_LIST:
taskList.onDisplay(variables);
break;
case TAG_LIST:
tagList.onDisplay(variables);
break;
case TASK_LIST_W_TAG:
taskListWTag.onDisplay(variables);
}
lastActivityBundle = variables;
}
private SubActivity getCurrentSubActivity() {
return (SubActivity)viewFlipper.getCurrentView().getTag();
} }
@Override /* ======================================================================
public void onWindowFocusChanged(boolean hasFocus) { * ======================================================= event handling
// refresh, since stuff might have changed... * ====================================================================== */
if(hasFocus) {
fillData();
}
}
private void createTask() { @Override
Intent intent = new Intent(getParent(), TaskEdit.class); protected void onSaveInstanceState(Bundle outState) {
if(filterTag != null) super.onSaveInstanceState(outState);
intent.putExtra(TaskEdit.TAG_NAME_TOKEN, filterTag.getName()); outState.putInt(TAG_LAST_ACTIVITY, getCurrentSubActivity().code.ordinal());
launchActivity(intent, ACTIVITY_CREATE); outState.putBundle(TAG_LAST_BUNDLE, lastActivityBundle);
} }
private void deleteTask(final TaskIdentifier taskId) {
new AlertDialog.Builder(getParent())
.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 @Override
public void onClick(DialogInterface dialog, int which) { public boolean onKeyDown(int keyCode, KeyEvent event) {
getTaskController().deleteTask(taskId); if(getCurrentSubActivity().onKeyDown(keyCode, event))
fillData(); return true;
} else
}) return super.onKeyDown(keyCode, event);
.setNegativeButton(android.R.string.cancel, null)
.show();
} }
public void showTagsView() { @Override
switchToActivity(SubActivities.TAG_LIST, null); public boolean onPrepareOptionsMenu(Menu menu) {
menu.clear();
return getCurrentSubActivity().onPrepareOptionsMenu(menu);
} }
/** Save the sorting mode to the preferences */ @Override
private void saveTaskListSort() { protected void onActivityResult(int requestCode, int resultCode, Intent data) {
int sortId = sortMode.ordinal() + 1; super.onActivityResult(requestCode, resultCode, data);
if(filterShowDone)
sortId |= SORTFLAG_FILTERDONE;
if(filterShowHidden)
sortId |= SORTFLAG_FILTERHIDDEN;
if(sortReverse)
sortId *= -1;
Preferences.setTaskListSort(getParent(), sortId); if(resultCode == Constants.RESULT_GO_HOME) {
switchToActivity(ActivityCode.TASK_LIST, null);
} else
getCurrentSubActivity().onActivityResult(requestCode, resultCode, data);
} }
/** Save the sorting mode to the preferences */ @Override
private void loadTaskListSort() { public void onWindowFocusChanged(boolean hasFocus) {
int sortId = Preferences.getTaskListSort(getParent()); super.onWindowFocusChanged(hasFocus);
if(sortId == 0)
return;
sortReverse = sortId < 0;
sortId = Math.abs(sortId);
filterShowDone = (sortId & SORTFLAG_FILTERDONE) > 0;
filterShowHidden = (sortId & SORTFLAG_FILTERHIDDEN) > 0;
sortId = sortId & ~(SORTFLAG_FILTERDONE | SORTFLAG_FILTERHIDDEN);
sortMode = SortMode.values()[sortId - 1]; if(hasFocus && shouldCloseInstance) { // user wants to quit
finish();
} else
getCurrentSubActivity().onWindowFocusChanged(hasFocus);
} }
@Override @Override
public boolean onOptionsItemSelected(MenuItem item) { public boolean onMenuItemSelected(int featureId, MenuItem item) {
Intent intent; if(getCurrentSubActivity().onMenuItemSelected(featureId, item))
final TaskModelForList task;
Resources r = getResources();
switch(item.getItemId()) {
case INSERT_ID:
createTask();
return true;
case FILTERS_ID:
listView.showContextMenu();
return true;
case TAGS_ID:
showTagsView();
return true;
case SYNC_ID:
onActivityResult(ACTIVITY_SYNCHRONIZE, Constants.RESULT_SYNCHRONIZE, null);
return true;
case MORE_ID:
layout.showContextMenu();
return true;
case OPTIONS_SYNC_ID:
launchActivity(new Intent(getParent(), SyncPreferences.class),
ACTIVITY_SYNCHRONIZE);
return true;
case OPTIONS_SETTINGS_ID:
launchActivity(new Intent(getParent(), EditPreferences.class), 0);
return true;
case OPTIONS_HELP_ID:
Intent browserIntent = new Intent(Intent.ACTION_VIEW,
Uri.parse(Constants.HELP_URL));
launchActivity(browserIntent, 0);
return true;
case TaskListAdapter.CONTEXT_EDIT_ID:
task = taskArray.get(item.getGroupId());
intent = new Intent(getParent(), TaskEdit.class);
intent.putExtra(TaskEdit.LOAD_INSTANCE_TOKEN, task.getTaskIdentifier().getId());
launchActivity(intent, ACTIVITY_EDIT);
return true;
case TaskListAdapter.CONTEXT_DELETE_ID:
task = taskArray.get(item.getGroupId());
deleteTask(task.getTaskIdentifier());
return true;
case TaskListAdapter.CONTEXT_TIMER_ID:
task = taskArray.get(item.getGroupId());
if(task.getTimerStart() == null)
task.setTimerStart(new Date());
else {
task.stopTimerAndUpdateElapsedTime();
}
getTaskController().saveTask(task);
fillData();
return true;
case TaskListAdapter.CONTEXT_POSTPONE_ID:
task = taskArray.get(item.getGroupId());
DialogUtilities.dayHourPicker(getParent(),
r.getString(R.string.taskList_postpone_dialog),
new OnNNumberPickedListener() {
public void onNumbersPicked(int[] values) {
long postponeMillis = (values[0] * 24 + values[1]) *
3600L * 1000;
Date preferred = task.getPreferredDueDate();
if(preferred != null) {
preferred = new Date(preferred.getTime() +
postponeMillis);
task.setPreferredDueDate(preferred);
}
Date definite = task.getDefiniteDueDate();
if(definite != null) {
definite = new Date(definite.getTime() +
postponeMillis);
task.setDefiniteDueDate(definite);
}
getTaskController().saveTask(task);
fillData();
}
});
return true; return true;
else
return super.onMenuItemSelected(featureId, item);
}
case CONTEXT_FILTER_HIDDEN: @Override
TaskList.filterShowHidden = !filterShowHidden; public boolean onTouchEvent(MotionEvent event) {
saveTaskListSort(); if (gestureDetector.onTouchEvent(event))
fillData();
return true;
case CONTEXT_FILTER_DONE:
TaskList.filterShowDone = !filterShowDone;
saveTaskListSort();
fillData();
return true;
case CONTEXT_FILTER_TAG:
TaskList.filterTag = null;
fillData();
return true;
case CONTEXT_SORT_AUTO:
if(sortMode == SortMode.AUTO)
return true;
TaskList.sortReverse = false;
TaskList.sortMode = SortMode.AUTO;
saveTaskListSort();
fillData();
return true;
case CONTEXT_SORT_ALPHA:
if(sortMode == SortMode.ALPHA)
return true;
TaskList.sortReverse = false;
TaskList.sortMode = SortMode.ALPHA;
saveTaskListSort();
fillData();
return true;
case CONTEXT_SORT_DUEDATE:
if(sortMode == SortMode.DUEDATE)
return true;
TaskList.sortReverse = false;
TaskList.sortMode = SortMode.DUEDATE;
saveTaskListSort();
fillData();
return true;
case CONTEXT_SORT_REVERSE:
TaskList.sortReverse = !sortReverse;
saveTaskListSort();
fillData();
return true; return true;
else
return false;
} }
return false; @Override
protected void onDestroy() {
super.onDestroy();
taskController.close();
tagController.close();
Synchronizer.setTagController(null);
Synchronizer.setTaskController(null);
} }
} }

@ -29,12 +29,14 @@ import android.content.res.Resources;
import android.graphics.Paint; import android.graphics.Paint;
import android.graphics.Typeface; import android.graphics.Typeface;
import android.view.ContextMenu; import android.view.ContextMenu;
import android.view.KeyEvent;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.Menu; import android.view.Menu;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.view.ContextMenu.ContextMenuInfo; import android.view.ContextMenu.ContextMenuInfo;
import android.view.View.OnCreateContextMenuListener; import android.view.View.OnCreateContextMenuListener;
import android.view.View.OnKeyListener;
import android.widget.ArrayAdapter; import android.widget.ArrayAdapter;
import android.widget.CheckBox; import android.widget.CheckBox;
import android.widget.CompoundButton; import android.widget.CompoundButton;
@ -44,6 +46,7 @@ import android.widget.CompoundButton.OnCheckedChangeListener;
import com.timsu.astrid.R; import com.timsu.astrid.R;
import com.timsu.astrid.data.alerts.AlertController; import com.timsu.astrid.data.alerts.AlertController;
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;
@ -101,8 +104,8 @@ public class TaskListAdapter extends ArrayAdapter<TaskModelForList> {
public interface TaskListAdapterHooks { public interface TaskListAdapterHooks {
List<TaskModelForList> getTaskArray(); List<TaskModelForList> getTaskArray();
List<TagModelForView> getTagsFor(TaskModelForList task); List<TagModelForView> getTagsFor(TaskModelForList task);
TaskController getTaskController(); TaskController taskController();
TagController getTagController(); 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);
} }
@ -178,6 +181,8 @@ public class TaskListAdapter extends ArrayAdapter<TaskModelForList> {
if(task.getTimerStart() != null) if(task.getTimerStart() != null)
timer.setImageDrawable(r.getDrawable(R.drawable.icon_timer)); timer.setImageDrawable(r.getDrawable(R.drawable.icon_timer));
else
timer.setImageDrawable(null);
progress.setChecked(task.isTaskCompleted()); progress.setChecked(task.isTaskCompleted());
setFieldContentsAndVisibility(view, task); setFieldContentsAndVisibility(view, task);
@ -426,6 +431,24 @@ public class TaskListAdapter extends ArrayAdapter<TaskModelForList> {
} }
}); });
view.setOnKeyListener(new OnKeyListener() {
@Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
if(event.getAction() != KeyEvent.ACTION_UP)
return false;
// hotkey to set task priority
if(keyCode >= KeyEvent.KEYCODE_1 && keyCode <= KeyEvent.KEYCODE_4) {
Importance i = Importance.values()[keyCode - KeyEvent.KEYCODE_1];
TaskModelForList task = (TaskModelForList)v.getTag();
task.setImportance(i);
hooks.taskController().saveTask(task);
setFieldContentsAndVisibility(v, task);
return true;
}
return false;
}
});
// long-clicking the text field // long-clicking the text field
view.setOnCreateContextMenuListener(new OnCreateContextMenuListener() { view.setOnCreateContextMenuListener(new OnCreateContextMenuListener() {
@Override @Override
@ -458,7 +481,7 @@ public class TaskListAdapter extends ArrayAdapter<TaskModelForList> {
private void setTaskProgress(final TaskModelForList task, View view, int progress) { private void setTaskProgress(final TaskModelForList task, View view, int progress) {
final ImageView timer = ((ImageView)view.findViewById(R.id.imageLeft)); final ImageView timer = ((ImageView)view.findViewById(R.id.imageLeft));
task.setProgressPercentage(progress); task.setProgressPercentage(progress);
hooks.getTaskController().saveTask(task); hooks.taskController().saveTask(task);
// show this task as completed even if it has repeats // show this task as completed even if it has repeats
if(progress == 100) if(progress == 100)
@ -477,7 +500,7 @@ public class TaskListAdapter extends ArrayAdapter<TaskModelForList> {
@Override @Override
public void onClick(DialogInterface dialog, int which) { public void onClick(DialogInterface dialog, int which) {
task.stopTimerAndUpdateElapsedTime(); task.stopTimerAndUpdateElapsedTime();
hooks.getTaskController().saveTask(task); hooks.taskController().saveTask(task);
timer.setVisibility(View.GONE); timer.setVisibility(View.GONE);
} }
}) })

@ -0,0 +1,725 @@
/*
* 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.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.res.Resources;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.view.ContextMenu;
import android.view.KeyEvent;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ContextMenu.ContextMenuInfo;
import android.view.View.OnCreateContextMenuListener;
import android.widget.AdapterView;
import android.widget.Button;
import android.widget.ListView;
import android.widget.AdapterView.OnItemClickListener;
import com.timsu.astrid.R;
import com.timsu.astrid.activities.TaskList.ActivityCode;
import com.timsu.astrid.activities.TaskList.SubActivity;
import com.timsu.astrid.activities.TaskListAdapter.TaskListAdapterHooks;
import com.timsu.astrid.data.tag.TagController;
import com.timsu.astrid.data.tag.TagIdentifier;
import com.timsu.astrid.data.tag.TagModelForView;
import com.timsu.astrid.data.task.TaskController;
import com.timsu.astrid.data.task.TaskIdentifier;
import com.timsu.astrid.data.task.TaskModelForList;
import com.timsu.astrid.sync.Synchronizer;
import com.timsu.astrid.sync.Synchronizer.SynchronizerListener;
import com.timsu.astrid.utilities.Constants;
import com.timsu.astrid.utilities.DialogUtilities;
import com.timsu.astrid.utilities.Preferences;
import com.timsu.astrid.widget.NNumberPickerDialog.OnNNumberPickedListener;
/**
* Primary view for the Astrid Application. Lists all of the tasks in the
* system, and allows users to edit them.
*
* @author Tim Su (timsu@stanfordalumni.org)
*
*/
public class TaskListSubActivity extends SubActivity {
// bundle tokens
public static final String TAG_TOKEN = "tag";
// activities
private static final int ACTIVITY_CREATE = 0;
private static final int ACTIVITY_VIEW = 1;
private static final int ACTIVITY_EDIT = 2;
private static final int ACTIVITY_TAGS = 3;
private static final int ACTIVITY_SYNCHRONIZE = 4;
// menu codes
private static final int INSERT_ID = Menu.FIRST;
private static final int FILTERS_ID = Menu.FIRST + 1;
private static final int TAGS_ID = Menu.FIRST + 2;
private static final int SYNC_ID = Menu.FIRST + 3;
private static final int MORE_ID = Menu.FIRST + 4;
private static final int OPTIONS_SYNC_ID = Menu.FIRST + 10;
private static final int OPTIONS_SETTINGS_ID = Menu.FIRST + 11;
private static final int OPTIONS_HELP_ID = Menu.FIRST + 12;
private static final int CONTEXT_FILTER_HIDDEN = Menu.FIRST + 20;
private static final int CONTEXT_FILTER_DONE = Menu.FIRST + 21;
private static final int CONTEXT_FILTER_TAG = Menu.FIRST + 22;
private static final int CONTEXT_SORT_AUTO = Menu.FIRST + 23;
private static final int CONTEXT_SORT_ALPHA = Menu.FIRST + 24;
private static final int CONTEXT_SORT_DUEDATE = Menu.FIRST + 25;
private static final int CONTEXT_SORT_REVERSE = Menu.FIRST + 26;
private static final int CONTEXT_SORT_GROUP = Menu.FIRST;
private static final int SORTFLAG_FILTERDONE = (1 << 5);
private static final int SORTFLAG_FILTERHIDDEN = (1 << 6);
// UI components
private ListView listView;
private Button addButton;
private View layout;
// other instance variables
private Map<TagIdentifier, TagModelForView> tagMap;
private ArrayList<TaskModelForList> taskArray;
private HashMap<TaskModelForList, LinkedList<TagModelForView>> taskTags;
// display filters
private static boolean filterShowHidden = false;
private static boolean filterShowDone = false;
private static SortMode sortMode = SortMode.AUTO;
private static boolean sortReverse = false;
private TagModelForView filterTag = null;
/* ======================================================================
* ======================================================= initialization
* ====================================================================== */
public TaskListSubActivity(TaskList parent, ActivityCode code, View view) {
super(parent, code, view);
}
@Override
/** Called when loading up the activity */
public void onDisplay(Bundle variables) {
// load tag map
tagMap = getTagController().getAllTagsAsMap(getParent());
// process the tag to filter on, if any
if(variables != null && variables.containsKey(TAG_TOKEN)) {
TagIdentifier identifier = new TagIdentifier(variables.getLong(TAG_TOKEN));
filterTag = tagMap.get(identifier);
}
setupUIComponents();
loadTaskListSort();
fillData();
}
/** Initialize UI components */
public void setupUIComponents() {
listView = (ListView)findViewById(R.id.tasklist);
addButton = (Button)findViewById(R.id.addtask);
addButton.setOnClickListener(new
View.OnClickListener() {
@Override
public void onClick(View v) {
createTask(null);
}
});
layout = getView();
layout.setOnCreateContextMenuListener(
new OnCreateContextMenuListener() {
@Override
public void onCreateContextMenu(ContextMenu menu, View v,
ContextMenuInfo menuInfo) {
if(menu.hasVisibleItems())
return;
onCreateMoreOptionsMenu(menu);
}
});
}
@Override
/** Create options menu (displayed when user presses menu key) */
public boolean onPrepareOptionsMenu(Menu menu) {
MenuItem item;
item = menu.add(Menu.NONE, INSERT_ID, Menu.NONE,
R.string.taskList_menu_insert);
item.setIcon(android.R.drawable.ic_menu_add);
item.setAlphabeticShortcut('n');
item = menu.add(Menu.NONE, FILTERS_ID, Menu.NONE,
R.string.taskList_menu_filters);
item.setIcon(android.R.drawable.ic_menu_view);
item.setAlphabeticShortcut('f');
item = menu.add(Menu.NONE, TAGS_ID, Menu.NONE,
R.string.taskList_menu_tags);
item.setIcon(android.R.drawable.ic_menu_myplaces);
item.setAlphabeticShortcut('t');
if(Preferences.shouldDisplaySyncButton(getParent())){
item = menu.add(Menu.NONE, SYNC_ID, Menu.NONE,
R.string.taskList_menu_syncshortcut);
item.setIcon(android.R.drawable.ic_menu_upload);
item.setAlphabeticShortcut('s');
}
item = menu.add(Menu.NONE, MORE_ID, Menu.NONE,
R.string.taskList_menu_more);
item.setIcon(android.R.drawable.ic_menu_more);
item.setAlphabeticShortcut('m');
return true;
}
/** Create 'more options' menu */
public boolean onCreateMoreOptionsMenu(Menu menu) {
MenuItem item;
item = menu.add(Menu.NONE, OPTIONS_SYNC_ID, Menu.NONE,
R.string.taskList_menu_sync);
item.setAlphabeticShortcut('s');
item = menu.add(Menu.NONE, OPTIONS_SETTINGS_ID, Menu.NONE,
R.string.taskList_menu_settings);
item.setAlphabeticShortcut('p');
item = menu.add(Menu.NONE, OPTIONS_HELP_ID, Menu.NONE,
R.string.taskList_menu_help);
item.setAlphabeticShortcut('h');
return true;
}
private enum SortMode {
ALPHA {
@Override
int compareTo(TaskModelForList arg0, TaskModelForList arg1) {
return arg0.getName().compareTo(arg1.getName());
}
},
DUEDATE {
long getDueDate(TaskModelForList task) {
Date definite = task.getDefiniteDueDate();
Date preferred = task.getPreferredDueDate();
if(definite != null && preferred != null) {
if(preferred.before(new Date()))
return definite.getTime();
return preferred.getTime();
} else if(definite != null)
return definite.getTime();
else if(preferred != null)
return preferred.getTime();
else
return new Date(2020,1,1).getTime();
}
@Override
int compareTo(TaskModelForList arg0, TaskModelForList arg1) {
return (int)((getDueDate(arg0) - getDueDate(arg1))/1000);
}
},
AUTO {
@Override
int compareTo(TaskModelForList arg0, TaskModelForList arg1) {
return arg0.getTaskWeight() - arg1.getTaskWeight();
}
};
abstract int compareTo(TaskModelForList arg0, TaskModelForList arg1);
};
/* ======================================================================
* ====================================================== populating list
* ====================================================================== */
/** Helper method returns true if the task is considered 'hidden' */
private boolean isTaskHidden(TaskModelForList task) {
if(task.isHidden())
return true;
if(filterTag == null) {
for(TagModelForView tags : taskTags.get(task)) {
if(tags.shouldHideFromMainList())
return true;
}
}
return false;
}
/** Fill in the Task List with our tasks */
private void fillData() {
Resources r = getResources();
// get a cursor to the task list
Cursor tasksCursor;
if(filterTag != null) {
List<TaskIdentifier> tasks = getTagController().getTaggedTasks(getParent(),
filterTag.getTagIdentifier());
tasksCursor = getTaskController().getTaskListCursorById(tasks);
} else {
if(filterShowDone)
tasksCursor = getTaskController().getAllTaskListCursor();
else
tasksCursor = getTaskController().getActiveTaskListCursor();
}
startManagingCursor(tasksCursor);
taskArray = getTaskController().createTaskListFromCursor(tasksCursor);
// read tags and apply filters
int hiddenTasks = 0; // # of tasks hidden
int completedTasks = 0; // # of tasks on list that are done
taskTags = new HashMap<TaskModelForList, LinkedList<TagModelForView>>();
for(Iterator<TaskModelForList> i = taskArray.iterator(); i.hasNext();) {
TaskModelForList task = i.next();
if(task.isTaskCompleted()) {
if(!filterShowDone) {
i.remove();
continue;
}
}
// get list of tags
LinkedList<TagIdentifier> tagIds = getTagController().getTaskTags(getParent(),
task.getTaskIdentifier());
LinkedList<TagModelForView> tags = new LinkedList<TagModelForView>();
for(TagIdentifier tagId : tagIds) {
TagModelForView tag = tagMap.get(tagId);
tags.add(tag);
}
taskTags.put(task, tags);
// hide hidden
if(!filterShowHidden) {
if(isTaskHidden(task)) {
hiddenTasks++;
i.remove();
continue;
}
}
if(task.isTaskCompleted())
completedTasks++;
}
int activeTasks = taskArray.size() - completedTasks;
// sort task list
// do sort
Collections.sort(taskArray, new Comparator<TaskModelForList>() {
@Override
public int compare(TaskModelForList arg0, TaskModelForList arg1) {
return sortMode.compareTo(arg0, arg1);
}
});
if(sortReverse)
Collections.reverse(taskArray);
// hide "add" button if we have a few tasks
if(taskArray.size() > 4)
addButton.setVisibility(View.GONE);
else
addButton.setVisibility(View.VISIBLE);
// set up the title
StringBuilder title = new StringBuilder().
append(r.getString(R.string.taskList_titlePrefix)).append(" ");
if(filterTag != null) {
title.append(r.getString(R.string.taskList_titleTagPrefix,
filterTag.getName())).append(" ");
}
if(completedTasks > 0)
title.append(r.getQuantityString(R.plurals.NactiveTasks,
activeTasks, activeTasks, taskArray.size()));
else
title.append(r.getQuantityString(R.plurals.Ntasks,
taskArray.size(), taskArray.size()));
if(hiddenTasks > 0)
title.append(" (+").append(hiddenTasks).append(" ").
append(r.getString(R.string.taskList_hiddenSuffix)).append(")");
setTitle(title);
setUpListUI();
}
/** Set up the adapter for our task list */
private void setUpListUI() {
// set up our adapter
TaskListAdapter tasks = new TaskListAdapter(getParent(),
R.layout.task_list_row, taskArray, new TaskListAdapterHooks() {
@Override
public TagController tagController() {
return getTagController();
}
@Override
public List<TagModelForView> getTagsFor(
TaskModelForList task) {
return taskTags.get(task);
}
@Override
public List<TaskModelForList> getTaskArray() {
return taskArray;
}
@Override
public TaskController taskController() {
return getTaskController();
}
@Override
public void performItemClick(View v, int position) {
listView.performItemClick(v, position, 0);
}
public void onCreatedTaskListView(View v, TaskModelForList task) {
v.setOnTouchListener(getGestureListener());
}
});
listView.setAdapter(tasks);
listView.setItemsCanFocus(true);
// list view listener
listView.setOnItemClickListener(new OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view,
int position, long id) {
TaskModelForList task = (TaskModelForList)view.getTag();
Intent intent = new Intent(getParent(), TaskView.class);
intent.putExtra(TaskEdit.LOAD_INSTANCE_TOKEN, task.
getTaskIdentifier().getId());
launchActivity(intent, ACTIVITY_VIEW);
}
});
// filters context menu
listView.setOnCreateContextMenuListener(new OnCreateContextMenuListener() {
@Override
public void onCreateContextMenu(ContextMenu menu, View v,
ContextMenuInfo menuInfo) {
if(menu.hasVisibleItems())
return;
Resources r = getResources();
menu.setHeaderTitle(R.string.taskList_filter_title);
MenuItem item = menu.add(Menu.NONE, CONTEXT_FILTER_HIDDEN,
Menu.NONE, R.string.taskList_filter_hidden);
item.setCheckable(true);
item.setChecked(filterShowHidden);
item = menu.add(Menu.NONE, CONTEXT_FILTER_DONE, Menu.NONE,
R.string.taskList_filter_done);
item.setCheckable(true);
item.setChecked(filterShowDone);
if(filterTag != null) {
item = menu.add(Menu.NONE, CONTEXT_FILTER_TAG, Menu.NONE,
r.getString(R.string.taskList_filter_tagged,
filterTag.getName()));
item.setCheckable(true);
item.setChecked(true);
}
item = menu.add(CONTEXT_SORT_GROUP, CONTEXT_SORT_AUTO, Menu.NONE,
R.string.taskList_sort_auto);
item.setChecked(sortMode == SortMode.AUTO);
item = menu.add(CONTEXT_SORT_GROUP, CONTEXT_SORT_ALPHA, Menu.NONE,
R.string.taskList_sort_alpha);
item.setChecked(sortMode == SortMode.ALPHA);
item = menu.add(CONTEXT_SORT_GROUP, CONTEXT_SORT_DUEDATE, Menu.NONE,
R.string.taskList_sort_duedate);
item.setChecked(sortMode == SortMode.DUEDATE);
menu.setGroupCheckable(CONTEXT_SORT_GROUP, true, true);
item = menu.add(CONTEXT_SORT_GROUP, CONTEXT_SORT_REVERSE, Menu.NONE,
R.string.taskList_sort_reverse);
item.setCheckable(true);
item.setChecked(sortReverse);
}
});
listView.setOnTouchListener(getGestureListener());
}
/* ======================================================================
* ======================================================= event handlers
* ====================================================================== */
@Override
protected boolean onKeyDown(int keyCode, KeyEvent event) {
if(keyCode == KeyEvent.KEYCODE_BACK) {
if(filterTag != null) {
showTagsView();
return true;
}
}
if(keyCode >= KeyEvent.KEYCODE_A && keyCode <= KeyEvent.KEYCODE_Z) {
createTask((char)('A' + (keyCode - KeyEvent.KEYCODE_A)));
return true;
}
return false;
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if(resultCode == Constants.RESULT_SYNCHRONIZE) {
Synchronizer.synchronize(getParent(), false, new SynchronizerListener() {
@Override
public void onSynchronizerFinished(int numServicesSynced) {
if(numServicesSynced == 0)
DialogUtilities.okDialog(getParent(), getResources().getString(
R.string.sync_no_synchronizers), null);
fillData();
}
});
} else if(requestCode == ACTIVITY_TAGS)
switchToActivity(ActivityCode.TAG_LIST, null);
}
@Override
public void onWindowFocusChanged(boolean hasFocus) {
// refresh, since stuff might have changed...
if(hasFocus) {
fillData();
}
}
/** Call an activity to create the given task */
private void createTask(Character startCharacter) {
Intent intent = new Intent(getParent(), TaskEdit.class);
if(filterTag != null)
intent.putExtra(TaskEdit.TAG_NAME_TOKEN, filterTag.getName());
if(startCharacter != null)
intent.putExtra(TaskEdit.START_CHAR_TOKEN, startCharacter);
launchActivity(intent, ACTIVITY_CREATE);
}
/** Show a dialog box and delete the task specified */
private void deleteTask(final TaskIdentifier taskId) {
new AlertDialog.Builder(getParent())
.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) {
getTaskController().deleteTask(taskId);
fillData();
}
})
.setNegativeButton(android.R.string.cancel, null)
.show();
}
/** Show the tags view */
public void showTagsView() {
switchToActivity(ActivityCode.TAG_LIST, null);
}
/** Save the sorting mode to the preferences */
private void saveTaskListSort() {
int sortId = sortMode.ordinal() + 1;
if(filterShowDone)
sortId |= SORTFLAG_FILTERDONE;
if(filterShowHidden)
sortId |= SORTFLAG_FILTERHIDDEN;
if(sortReverse)
sortId *= -1;
Preferences.setTaskListSort(getParent(), sortId);
}
/** Save the sorting mode to the preferences */
private void loadTaskListSort() {
int sortId = Preferences.getTaskListSort(getParent());
if(sortId == 0)
return;
sortReverse = sortId < 0;
sortId = Math.abs(sortId);
filterShowDone = (sortId & SORTFLAG_FILTERDONE) > 0;
filterShowHidden = (sortId & SORTFLAG_FILTERHIDDEN) > 0;
sortId = sortId & ~(SORTFLAG_FILTERDONE | SORTFLAG_FILTERHIDDEN);
sortMode = SortMode.values()[sortId - 1];
}
@Override
public boolean onMenuItemSelected(int featureId, MenuItem item) {
Intent intent;
final TaskModelForList task;
Resources r = getResources();
switch(item.getItemId()) {
// --- options menu items
case INSERT_ID:
createTask(null);
return true;
case FILTERS_ID:
listView.showContextMenu();
return true;
case TAGS_ID:
showTagsView();
return true;
case SYNC_ID:
onActivityResult(ACTIVITY_SYNCHRONIZE, Constants.RESULT_SYNCHRONIZE, null);
return true;
case MORE_ID:
layout.showContextMenu();
return true;
// --- more options menu items
case OPTIONS_SYNC_ID:
launchActivity(new Intent(getParent(), SyncPreferences.class),
ACTIVITY_SYNCHRONIZE);
return true;
case OPTIONS_SETTINGS_ID:
launchActivity(new Intent(getParent(), EditPreferences.class), 0);
return true;
case OPTIONS_HELP_ID:
Intent browserIntent = new Intent(Intent.ACTION_VIEW,
Uri.parse(Constants.HELP_URL));
launchActivity(browserIntent, 0);
return true;
// --- list context menu items
case TaskListAdapter.CONTEXT_EDIT_ID:
task = taskArray.get(item.getGroupId());
intent = new Intent(getParent(), TaskEdit.class);
intent.putExtra(TaskEdit.LOAD_INSTANCE_TOKEN, task.getTaskIdentifier().getId());
launchActivity(intent, ACTIVITY_EDIT);
return true;
case TaskListAdapter.CONTEXT_DELETE_ID:
task = taskArray.get(item.getGroupId());
deleteTask(task.getTaskIdentifier());
return true;
case TaskListAdapter.CONTEXT_TIMER_ID:
task = taskArray.get(item.getGroupId());
if(task.getTimerStart() == null)
task.setTimerStart(new Date());
else {
task.stopTimerAndUpdateElapsedTime();
}
getTaskController().saveTask(task);
fillData();
return true;
case TaskListAdapter.CONTEXT_POSTPONE_ID:
task = taskArray.get(item.getGroupId());
DialogUtilities.dayHourPicker(getParent(),
r.getString(R.string.taskList_postpone_dialog),
new OnNNumberPickedListener() {
public void onNumbersPicked(int[] values) {
long postponeMillis = (values[0] * 24 + values[1]) *
3600L * 1000;
Date preferred = task.getPreferredDueDate();
if(preferred != null) {
preferred = new Date(preferred.getTime() +
postponeMillis);
task.setPreferredDueDate(preferred);
}
Date definite = task.getDefiniteDueDate();
if(definite != null) {
definite = new Date(definite.getTime() +
postponeMillis);
task.setDefiniteDueDate(definite);
}
getTaskController().saveTask(task);
fillData();
}
});
return true;
// --- display context menu items
case CONTEXT_FILTER_HIDDEN:
TaskListSubActivity.filterShowHidden = !filterShowHidden;
saveTaskListSort();
fillData();
return true;
case CONTEXT_FILTER_DONE:
TaskListSubActivity.filterShowDone = !filterShowDone;
saveTaskListSort();
fillData();
return true;
case CONTEXT_FILTER_TAG:
switchToActivity(ActivityCode.TASK_LIST, null);
return true;
case CONTEXT_SORT_AUTO:
if(sortMode == SortMode.AUTO)
return true;
TaskListSubActivity.sortReverse = false;
TaskListSubActivity.sortMode = SortMode.AUTO;
saveTaskListSort();
fillData();
return true;
case CONTEXT_SORT_ALPHA:
if(sortMode == SortMode.ALPHA)
return true;
TaskListSubActivity.sortReverse = false;
TaskListSubActivity.sortMode = SortMode.ALPHA;
saveTaskListSort();
fillData();
return true;
case CONTEXT_SORT_DUEDATE:
if(sortMode == SortMode.DUEDATE)
return true;
TaskListSubActivity.sortReverse = false;
TaskListSubActivity.sortMode = SortMode.DUEDATE;
saveTaskListSort();
fillData();
return true;
case CONTEXT_SORT_REVERSE:
TaskListSubActivity.sortReverse = !sortReverse;
saveTaskListSort();
fillData();
return true;
}
return false;
}
}

@ -232,7 +232,7 @@ public class TaskView extends TaskModificationActivity<TaskModelForView> {
public void onWindowFocusChanged(boolean hasFocus) { public void onWindowFocusChanged(boolean hasFocus) {
super.onWindowFocusChanged(hasFocus); super.onWindowFocusChanged(hasFocus);
if(hasFocus && MainActivity.shouldCloseInstance) { // user wants to quit if(hasFocus && TaskList.shouldCloseInstance) { // user wants to quit
finish(); finish();
} }
} }

@ -1,3 +1,22 @@
/*
* 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; package com.timsu.astrid.activities;
import java.util.Random; import java.util.Random;
@ -60,7 +79,7 @@ public class TaskViewNotifier extends TaskView {
@Override @Override
public void onClick(DialogInterface dialog, int which) { public void onClick(DialogInterface dialog, int which) {
setResult(Constants.RESULT_GO_HOME); setResult(Constants.RESULT_GO_HOME);
MainActivity.shouldCloseInstance = true; TaskList.shouldCloseInstance = true;
finish(); finish();
} }
}) })
@ -87,7 +106,7 @@ public class TaskViewNotifier extends TaskView {
repeatInterval); repeatInterval);
setResult(Constants.RESULT_GO_HOME); setResult(Constants.RESULT_GO_HOME);
MainActivity.shouldCloseInstance = true; TaskList.shouldCloseInstance = true;
finish(); finish();
} }
}); });

@ -24,7 +24,6 @@ import java.util.Date;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import android.R;
import android.app.Activity; import android.app.Activity;
import android.content.ContentValues; import android.content.ContentValues;
import android.content.Context; import android.content.Context;
@ -35,11 +34,15 @@ import android.database.sqlite.SQLiteOpenHelper;
import com.timsu.astrid.data.AbstractController; import com.timsu.astrid.data.AbstractController;
import com.timsu.astrid.data.sync.SyncDataController; import com.timsu.astrid.data.sync.SyncDataController;
import com.timsu.astrid.data.task.AbstractTaskModel.RepeatInfo;
import com.timsu.astrid.data.task.AbstractTaskModel.TaskModelDatabaseHelper; import com.timsu.astrid.data.task.AbstractTaskModel.TaskModelDatabaseHelper;
import com.timsu.astrid.utilities.Notifications; import com.timsu.astrid.utilities.Notifications;
/** Controller for task-related operations */ /**
* Controller for task-related operations
*
* @author timsu
*
*/
public class TaskController extends AbstractController { public class TaskController extends AbstractController {
private SQLiteDatabase database; private SQLiteDatabase database;

@ -232,4 +232,9 @@ public class TaskModelForList extends AbstractTaskModel {
public void setDefiniteDueDate(Date definiteDueDate) { public void setDefiniteDueDate(Date definiteDueDate) {
super.setDefiniteDueDate(definiteDueDate); super.setDefiniteDueDate(definiteDueDate);
} }
@Override
public void setImportance(Importance importance) {
super.setImportance(importance);
}
} }

@ -1,3 +1,22 @@
/*
* 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.sync; package com.timsu.astrid.sync;
import java.io.IOException; import java.io.IOException;

@ -1,3 +1,22 @@
/*
* 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.sync; package com.timsu.astrid.sync;
import java.io.IOException; import java.io.IOException;

@ -1,3 +1,22 @@
/*
* 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.sync; package com.timsu.astrid.sync;
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.InvocationTargetException;

@ -1,3 +1,22 @@
/*
* 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.sync; package com.timsu.astrid.sync;
import java.util.Date; import java.util.Date;

Loading…
Cancel
Save