From 5aa45b1dc6677d140c28a57f1e11d90677a6f5ca Mon Sep 17 00:00:00 2001 From: Tim Su Date: Sun, 1 Feb 2009 09:11:52 +0000 Subject: [PATCH] Bug fixes to the chagnes made in Astrid. --- AndroidManifest.xml | 4 +- res/drawable/notification_clock.png | Bin 882 -> 1141 bytes res/drawable/notification_tag_pink.png | Bin 579 -> 1418 bytes .../timsu/astrid/activities/MainActivity.java | 366 ------- .../{TagList.java => TagListSubActivity.java} | 37 +- src/com/timsu/astrid/activities/TaskEdit.java | 12 +- src/com/timsu/astrid/activities/TaskList.java | 987 +++++++----------- .../astrid/activities/TaskListAdapter.java | 31 +- .../activities/TaskListSubActivity.java | 725 +++++++++++++ src/com/timsu/astrid/activities/TaskView.java | 2 +- .../astrid/activities/TaskViewNotifier.java | 23 +- .../astrid/data/task/TaskController.java | 9 +- .../astrid/data/task/TaskModelForList.java | 5 + src/com/timsu/astrid/sync/RTMSyncService.java | 19 + .../astrid/sync/SynchronizationService.java | 19 + src/com/timsu/astrid/sync/Synchronizer.java | 19 + src/com/timsu/astrid/sync/TaskProxy.java | 19 + 17 files changed, 1253 insertions(+), 1024 deletions(-) delete mode 100644 src/com/timsu/astrid/activities/MainActivity.java rename src/com/timsu/astrid/activities/{TagList.java => TagListSubActivity.java} (89%) create mode 100644 src/com/timsu/astrid/activities/TaskListSubActivity.java diff --git a/AndroidManifest.xml b/AndroidManifest.xml index 48bc5dc43..0b832b265 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -1,7 +1,7 @@ - diff --git a/res/drawable/notification_clock.png b/res/drawable/notification_clock.png index e2672c20676177efb2fdea593b8f000fd5f12342..040d39ca00fc4b411d8d2a7997af574f9706680e 100644 GIT binary patch literal 1141 zcmV-*1d98KP)P000>X1^@s6#OZ}&00001b5ch_0Itp) z=>Px#24YJ`L;(K){{a7>y{D4^000SaNLh0L01FcU01FcV0GgZ_00007bV*G`2iXDv z2o5I>0~#Ly00Z?&L_t(Y$K8}$Y*a-U$A2^DoIPiEyW5MU6}xR9C0Z^fB&eV@5H%>5 zDD??7#)QO#805t#6Jv}qMxO{Tyf@VNfC*7V3|_!k6K+AORYD1&U~9K)fvw%$v%BZa znel;x0)YmQC%)u+n3?~~FW-Fs8Ms$M%(^H<%5j{A`op^ttVm06XOnB74pCX`c&d8! zf;}4mcqV`i*tH(;QV{7`AQj*@V6WNo={I7>E73KO-Me>-{{H^PVzIdN$dMyY1VPYY zj8Q=5#phpIGqyV0)>$1hDTK$fix3QawFAYqxLTzIo4{FBT0C z54&w`?P_vzLT9t-RxlvtWwpNQh64kBhz>Mb;fPRpmd^?TZ`^$N*4N^lZRTHy)>?#N zXcY>DOto4~8)Iy(b)>b4W@f5HGK5-!uo&VrCm~Q!Q<%EKR70e1kV=CsCDLgn5M504 z0^9zpq_?-%9~l|h-`CftfaO42+{lJUmakt`7b=S%8D})GN!l>u!%PjK^kwHr0SQ1G zLmvH+PrgI3f%~12g~|fY*xMJ6oIjI-61*BG=NGMp;Vi z^P2bv{HhZc1Iib$>jW+A~I;J*((QD2jJZ?M8EoNR+PMX zC^x5MN!Oz(d+b=geRyd;Xk0Oe>4}8*!c@Jra-lT;gMrB*vF2?3mcRNdVK)A_5<7a` zSkY<`S#^{#DCw&OI&0rw&*3}hQ|_3YaK0n32E^c_2XDKQvh_Zkp%;s8}r$h6cUMpt8KZ&`memftw9r2BH zXZp!+CFaEnJk?xA$eKJbHrNPn>;Axd{TeCT;zL2Z2lqOE#$fse--k~9GCAnb$WrIY zUzF=05;%7ScRp;-^ba=g4+!_yjOVxZHrAy=-Qy;3W{V_ws3i3@rj zN6Uq**d4Ifp5hMksmBf!JrmJQC(GkeNV1GOW##ZtQFwF|H80+P{}RL%{YNk_%ft(I z#;Cz`v~ti@ZP83cwQ@*mu2Dwn=d>z@4RV(M%#h6&LDo!N4vuc zo|;?G=MQ0U0&vMd?WX`v6t{tSSteePbgELlMO}xKy8^QHBD{2ALkbX%5TpZC;3a)d zD?(MwgLzpdUa8!vFvP diff --git a/res/drawable/notification_tag_pink.png b/res/drawable/notification_tag_pink.png index 76e2296cc1b8a8b906f98f852fd0318b46d886f5..175f93c51ec230f16876752fff8a4c488e01c167 100644 GIT binary patch literal 1418 zcmV;51$Fv~P)P000>X1^@s6#OZ}&00001b5ch_0Itp) z=>Px#24YJ`L;(K){{a7>y{D4^000SaNLh0L01FcU01FcV0GgZ_00007bV*G`2iXDv z2o5#AQB);q0fRh34yPL}<`G;Q zbwb1O$TBdUDUQuf)rs3*Ium+eO&%ivsBZsjunCAD2oMP9cSPA)vnO=myMfNHesX#G zY+)FbK;d*o$y$`8#!4z*sdJ|T!1{T=!=_RRKm=Dr4v*B>)7);J7uKZITX`h_KqCh$ zO5-rghy^;eqNU=SVDjIw3xWttQ*%g*P>Xou8>J$&Qoi6SNX=3=jbbB7lBBRPXix!$F7(;}Ke& z1+R>wrhcCF{2J~U^YhEzcsS8|+e!eeYwY!1Z5;@~=@*HQ0$~oI^ESw;xvc8K0CAk8ToVav76h;k2Jv<9E^g|INgG?_22Ld?);Co$= ztK_7fIgLp~F@{LY%`MsBXg&T;4>uXIb%}0bRn$B;>wNdHXNP6T+i}TkhwaX3OjtRS z{CXvvoi>C9-H^w~pf>3dI%!6vWr)SaaWp$s2MI$lBYJ%D2>+oDmCpIz#|z6EEw+(M z?N)fR3`|kE6jpl>_RBT|9RWl`QAG7JL{(DoVK84Pil`tUDhT#l_GGf2+1s9F)F}>T zrRW#kpO;7)FVDjJnh`qL2`)|p&DLU|FpaKwL#*M_DExf1bi@^`JzSRWm)tIS`SEM4 z@snNXI92UVxp(yy36SUwgmdCd@ldE0gm?{O1*zbc=8z@8vkr?jIB54*Tztfq_oCtsr9 Y06b%0qAZx%nE(I)07*qoM6N<$g5FhlhB@q!hxI!PKQbx4SpL{`r}VmgkO zKJ1rs_WIV^Cp$u~*F(44MZ4WbtJOlY*+iq!K)qf^tya4OAlxdU)9GNp-=EcdKIf2f zxqK5MkTVHpvl(}uPNxop&R7Z*3Wci>fg)2gxzRzjTCMB44$HFmIRQ(h60+IsMTkJI z$3`aJvdFXbdX25Rfl*oUyUApNd_Ip%CUXW6$oUxAwr!Budf7tzCgJ6?1fzU_jK^b2 zfn+jy0uk7ZrmCvEX}8;PXYF3X>c?-ad@8i>vI7~7Mw9~4Xw-oSl}hCko6%Ghg*TZa zojb2QDqe$2yuO1BheJw%P$$M1@gl$?5#tJcsvfD&-d}drYvCA3ZZKYBvF_B1^w22%tHlRHzus60N_IkZ8=MgV0M=TZ-Baw(nn|S=hBxdI) z;;R6va5(H{FKyC-4F-dY;+p`fU@++R`~4<~Jsywi;wt~|cV(__CjM~k{x92pJ|?T2 RMYR9`002ovPDHLkV1iy62OIzZ diff --git a/src/com/timsu/astrid/activities/MainActivity.java b/src/com/timsu/astrid/activities/MainActivity.java deleted file mode 100644 index d7674bf38..000000000 --- a/src/com/timsu/astrid/activities/MainActivity.java +++ /dev/null @@ -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); - } -} diff --git a/src/com/timsu/astrid/activities/TagList.java b/src/com/timsu/astrid/activities/TagListSubActivity.java similarity index 89% rename from src/com/timsu/astrid/activities/TagList.java rename to src/com/timsu/astrid/activities/TagListSubActivity.java index 0f5422aa2..131fdca05 100644 --- a/src/com/timsu/astrid/activities/TagList.java +++ b/src/com/timsu/astrid/activities/TagListSubActivity.java @@ -32,6 +32,7 @@ import android.content.res.Resources; import android.database.Cursor; import android.os.Bundle; import android.view.ContextMenu; +import android.view.KeyEvent; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuItem; @@ -47,8 +48,8 @@ import android.widget.AdapterView.AdapterContextMenuInfo; import android.widget.AdapterView.OnItemClickListener; import com.timsu.astrid.R; -import com.timsu.astrid.activities.MainActivity.SubActivities; -import com.timsu.astrid.activities.MainActivity.SubActivity; +import com.timsu.astrid.activities.TaskList.ActivityCode; +import com.timsu.astrid.activities.TaskList.SubActivity; import com.timsu.astrid.data.tag.TagIdentifier; import com.timsu.astrid.data.tag.TagModelForView; import com.timsu.astrid.data.task.TaskIdentifier; @@ -61,7 +62,7 @@ import com.timsu.astrid.data.task.TaskModelForList; * @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 MENU_SORT_ALPHA_ID = Menu.FIRST; @@ -78,7 +79,7 @@ public class TagList extends SubActivity { private static SortMode sortMode = SortMode.SIZE; 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); } @@ -93,18 +94,18 @@ public class TagList extends SubActivity { private enum SortMode { ALPHA { @Override - int compareTo(TagList self, TagModelForView arg0, TagModelForView arg1) { + int compareTo(TagListSubActivity self, TagModelForView arg0, TagModelForView arg1) { return arg0.getName().compareTo(arg1.getName()); } }, SIZE { @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); } }; - abstract int compareTo(TagList self, TagModelForView arg0, TagModelForView arg1); + abstract int compareTo(TagListSubActivity self, TagModelForView arg0, TagModelForView arg1); }; private void sortTagArray() { @@ -137,7 +138,7 @@ public class TagList extends SubActivity { Collections.sort(tagArray, new Comparator() { @Override public int compare(TagModelForView arg0, TagModelForView arg1) { - return sortMode.compareTo(TagList.this, arg0, arg1); + return sortMode.compareTo(TagListSubActivity.this, arg0, arg1); } }); if(sortReverse) @@ -175,8 +176,8 @@ public class TagList extends SubActivity { TagModelForView tag = (TagModelForView)view.getTag(); Bundle bundle = new Bundle(); - bundle.putLong(TaskList.TAG_TOKEN, tag.getTagIdentifier().getId()); - switchToActivity(SubActivities.TASK_LIST_W_TAG, bundle); + bundle.putLong(TaskListSubActivity.TAG_TOKEN, tag.getTagIdentifier().getId()); + switchToActivity(ActivityCode.TASK_LIST_W_TAG, bundle); } }); @@ -237,9 +238,21 @@ public class TagList extends SubActivity { .setNegativeButton(android.R.string.cancel, null) .show(); } + + @Override + /** 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 - public boolean onOptionsItemSelected(MenuItem item) { + /** Picked item in the options list */ + public boolean onMenuItemSelected(int featureId, MenuItem item) { switch(item.getItemId()) { case MENU_SORT_ALPHA_ID: if(sortMode == SortMode.ALPHA) @@ -336,6 +349,8 @@ public class TagList extends SubActivity { if(tagToTaskCount.get(tag) == 0) name.setTextColor(r.getColor(R.color.task_list_done)); + else + name.setTextColor(r.getColor(android.R.color.white)); } } diff --git a/src/com/timsu/astrid/activities/TaskEdit.java b/src/com/timsu/astrid/activities/TaskEdit.java index e439fbaec..6d06dc866 100644 --- a/src/com/timsu/astrid/activities/TaskEdit.java +++ b/src/com/timsu/astrid/activities/TaskEdit.java @@ -80,7 +80,8 @@ import com.timsu.astrid.widget.TimeDurationControlSet.TimeDurationType; public class TaskEdit extends TaskModificationTabbedActivity { // 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 private static final int SAVE_ID = Menu.FIRST; @@ -169,7 +170,12 @@ public class TaskEdit extends TaskModificationTabbedActivity { // set UI components based on model variables if(model.getCursor() != null) startManagingCursor(model.getCursor()); - name.setText(model.getName()); + 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()); if(model.getName().length() > 0) setTitle(new StringBuilder(). append(r.getString(R.string.taskEdit_titlePrefix)). @@ -507,7 +513,7 @@ public class TaskEdit extends TaskModificationTabbedActivity { public void onWindowFocusChanged(boolean hasFocus) { super.onWindowFocusChanged(hasFocus); - if(hasFocus && MainActivity.shouldCloseInstance) { // user wants to quit + if(hasFocus && TaskList.shouldCloseInstance) { // user wants to quit finish(); } } diff --git a/src/com/timsu/astrid/activities/TaskList.java b/src/com/timsu/astrid/activities/TaskList.java index 5d66846e4..add1cf49d 100644 --- a/src/com/timsu/astrid/activities/TaskList.java +++ b/src/com/timsu/astrid/activities/TaskList.java @@ -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; -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.app.Activity; import android.content.Intent; import android.content.res.Resources; import android.database.Cursor; -import android.net.Uri; import android.os.Bundle; +import android.util.Log; import android.view.ContextMenu; +import android.view.GestureDetector; +import android.view.KeyEvent; import android.view.Menu; import android.view.MenuItem; +import android.view.MotionEvent; 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 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.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.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; - +import com.timsu.astrid.utilities.StartupReceiver; -/** - * Primary view for the Astrid Application. Lists all of the tasks in the - * system, and allows users to edit them. - * +/** + * Main activity uses a ViewFlipper to flip between child views. + * * @author Tim Su (timsu@stanfordalumni.org) - * */ -public class TaskList 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 tagMap; - private ArrayList taskArray; - private HashMap> 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; - +public class TaskList extends Activity { + + /** + * 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 SubActivity(TaskList parent, ActivityCode code, View view) { + this.parent = parent; + this.code = code; + this.view = view; + view.setTag(this); + } + + // --- pass-through to activity listeners + + /** Called when this subactivity is displayed to the user */ + void onDisplay(Bundle variables) { + // + } + + boolean onPrepareOptionsMenu(Menu menu) { + return false; + } + + void onActivityResult(int requestCode, int resultCode, Intent data) { + // + } + + boolean onMenuItemSelected(int featureId, MenuItem item) { + return false; + } + + + void onWindowFocusChanged(boolean hasFocus) { + // + } + + boolean onKeyDown(int keyCode, KeyEvent event) { + return false; + } + + // --- 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(ActivityCode activity, Bundle state) { + parent.switchToActivity(activity, state); + } + + // --- internal methods + + protected ActivityCode getActivityCode() { + return code; + } + + protected View getView() { + return view; + } + } + /* ====================================================================== - * ======================================================= initialization + * ======================================================= internal stuff * ====================================================================== */ + - public TaskList(MainActivity parent, SubActivities code, View view) { - super(parent, code, view); - } + public enum ActivityCode { + TASK_LIST, + TAG_LIST, + TASK_LIST_W_TAG + }; + 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 */ - public void onDisplay(Bundle variables) { - // load tag map - tagMap = getTagController().getAllTagsAsMap(getParent()); + /** 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(); - // 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); + if(savedInstanceState != null && savedInstanceState.containsKey(TAG_LAST_ACTIVITY)) { + viewFlipper.setDisplayedChild(savedInstanceState.getInt(TAG_LAST_ACTIVITY)); + Bundle variables = savedInstanceState.getBundle(TAG_LAST_BUNDLE); + getCurrentSubActivity().onDisplay(variables); + } else { + getCurrentSubActivity().onDisplay(null); } - - setupUIComponents(); - loadTaskListSort(); - fillData(); - } - - 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(); - } - }); - - layout = findViewById(R.id.tasklist_layout); - layout.setOnCreateContextMenuListener( - new OnCreateContextMenuListener() { - @Override - public void onCreateContextMenu(ContextMenu menu, View v, - ContextMenuInfo menuInfo) { - if(menu.hasVisibleItems()) - return; - onCreateMoreOptionsMenu(menu); - } - }); - } - - @Override - 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'); + // auto sync if requested + Integer autoSyncHours = Preferences.autoSyncFrequency(this); + if(autoSyncHours != null) { + final Date lastSync = Preferences.getSyncLastSync(this); - 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'); + if(lastSync == null || lastSync.getTime() + + 1000L*3600*autoSyncHours < System.currentTimeMillis()) { + Synchronizer.synchronize(this, true, null); + } } - - 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; - } - - 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 - * ====================================================================== */ - - private boolean isTaskHidden(TaskModelForList task) { - if(task.isHidden()) - return true; - - if(filterTag == null) { - for(TagModelForView tags : taskTags.get(task)) { - if(tags.shouldHideFromMainList()) + + /** Set up user interface components */ + private void setupUIComponents() { + gestureDetector = new GestureDetector(new AstridGestureDetector()); + viewFlipper = (ViewFlipper)findViewById(R.id.main); + taskList = new TaskListSubActivity(this, ActivityCode.TASK_LIST, + 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)); + + 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; } - } - - 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 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>(); - for(Iterator i = taskArray.iterator(); i.hasNext();) { - TaskModelForList task = i.next(); - - if(task.isTaskCompleted()) { - if(!filterShowDone) { - i.remove(); - continue; + + 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(ActivityCode.TAG_LIST, null); + return true; + default: + return false; + } } - } - - // get list of tags - LinkedList tagIds = getTagController().getTaskTags(getParent(), - task.getTaskIdentifier()); - LinkedList tags = new LinkedList(); - 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; + // 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(ActivityCode.TAG_LIST, null); + return true; + case TAG_LIST: + switchToActivity(ActivityCode.TASK_LIST, null); + return true; + default: + return false; + } } + } catch (Exception e) { + // ignore! } - if(task.isTaskCompleted()) - completedTasks++; - } - int activeTasks = taskArray.size() - completedTasks; - - // sort task list - // do sort - Collections.sort(taskArray, new Comparator() { - @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(" "); + return false; } - - 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() { - // set up our adapter - TaskListAdapter tasks = new TaskListAdapter(getParent(), - R.layout.task_list_row, taskArray, new TaskListAdapterHooks() { - @Override - public TagController getTagController() { - return getTagController(); - } - - @Override - public List getTagsFor( - TaskModelForList task) { - return taskTags.get(task); - } - - @Override - public List getTaskArray() { - return taskArray; - } - - @Override - public TaskController getTaskController() { - 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); + /* ====================================================================== + * ==================================================== subactivity stuff + * ====================================================================== */ + + private void switchToActivity(ActivityCode activity, Bundle variables) { + closeOptionsMenu(); + + // and flip to them + switch(getCurrentSubActivity().getActivityCode()) { + case TASK_LIST: + viewFlipper.setInAnimation(mInAnimationForward); + viewFlipper.setOutAnimation(mOutAnimationForward); + viewFlipper.showNext(); + if(activity == ActivityCode.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; } - }); - - listView.setOnTouchListener(getGestureListener()); + 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(); + } + /* ====================================================================== - * ======================================================= event handlers + * ======================================================= event handling * ====================================================================== */ - + @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(SubActivities.TAG_LIST, null); + protected void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + outState.putInt(TAG_LAST_ACTIVITY, getCurrentSubActivity().code.ordinal()); + outState.putBundle(TAG_LAST_BUNDLE, lastActivityBundle); } - + @Override - public void onWindowFocusChanged(boolean hasFocus) { - // refresh, since stuff might have changed... - if(hasFocus) { - fillData(); - } + public boolean onKeyDown(int keyCode, KeyEvent event) { + if(getCurrentSubActivity().onKeyDown(keyCode, event)) + return true; + else + return super.onKeyDown(keyCode, event); } - - private void createTask() { - Intent intent = new Intent(getParent(), TaskEdit.class); - if(filterTag != null) - intent.putExtra(TaskEdit.TAG_NAME_TOKEN, filterTag.getName()); - launchActivity(intent, ACTIVITY_CREATE); + + @Override + public boolean onPrepareOptionsMenu(Menu menu) { + menu.clear(); + return getCurrentSubActivity().onPrepareOptionsMenu(menu); } - 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(); + @Override + protected void onActivityResult(int requestCode, int resultCode, Intent data) { + super.onActivityResult(requestCode, resultCode, data); + + if(resultCode == Constants.RESULT_GO_HOME) { + switchToActivity(ActivityCode.TASK_LIST, null); + } else + getCurrentSubActivity().onActivityResult(requestCode, resultCode, data); } - public void showTagsView() { - switchToActivity(SubActivities.TAG_LIST, null); + @Override + public void onWindowFocusChanged(boolean hasFocus) { + super.onWindowFocusChanged(hasFocus); + + if(hasFocus && shouldCloseInstance) { // user wants to quit + finish(); + } else + getCurrentSubActivity().onWindowFocusChanged(hasFocus); } - - /** 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); + + @Override + public boolean onMenuItemSelected(int featureId, MenuItem item) { + if(getCurrentSubActivity().onMenuItemSelected(featureId, item)) + return true; + else + return super.onMenuItemSelected(featureId, item); } - - /** 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 onTouchEvent(MotionEvent event) { + if (gestureDetector.onTouchEvent(event)) + return true; + else + return false; } @Override - public boolean onOptionsItemSelected(MenuItem item) { - Intent intent; - 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; - - case CONTEXT_FILTER_HIDDEN: - TaskList.filterShowHidden = !filterShowHidden; - saveTaskListSort(); - 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 false; + protected void onDestroy() { + super.onDestroy(); + taskController.close(); + tagController.close(); + Synchronizer.setTagController(null); + Synchronizer.setTaskController(null); } -} \ No newline at end of file +} diff --git a/src/com/timsu/astrid/activities/TaskListAdapter.java b/src/com/timsu/astrid/activities/TaskListAdapter.java index 1eae43421..218a4710a 100644 --- a/src/com/timsu/astrid/activities/TaskListAdapter.java +++ b/src/com/timsu/astrid/activities/TaskListAdapter.java @@ -29,12 +29,14 @@ import android.content.res.Resources; import android.graphics.Paint; import android.graphics.Typeface; import android.view.ContextMenu; +import android.view.KeyEvent; import android.view.LayoutInflater; import android.view.Menu; import android.view.View; import android.view.ViewGroup; import android.view.ContextMenu.ContextMenuInfo; import android.view.View.OnCreateContextMenuListener; +import android.view.View.OnKeyListener; import android.widget.ArrayAdapter; import android.widget.CheckBox; import android.widget.CompoundButton; @@ -44,6 +46,7 @@ import android.widget.CompoundButton.OnCheckedChangeListener; import com.timsu.astrid.R; 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.TagModelForView; import com.timsu.astrid.data.task.TaskController; @@ -101,8 +104,8 @@ public class TaskListAdapter extends ArrayAdapter { public interface TaskListAdapterHooks { List getTaskArray(); List getTagsFor(TaskModelForList task); - TaskController getTaskController(); - TagController getTagController(); + TaskController taskController(); + TagController tagController(); void performItemClick(View v, int position); void onCreatedTaskListView(View v, TaskModelForList task); } @@ -178,6 +181,8 @@ public class TaskListAdapter extends ArrayAdapter { if(task.getTimerStart() != null) timer.setImageDrawable(r.getDrawable(R.drawable.icon_timer)); + else + timer.setImageDrawable(null); progress.setChecked(task.isTaskCompleted()); setFieldContentsAndVisibility(view, task); @@ -425,6 +430,24 @@ public class TaskListAdapter extends ArrayAdapter { hooks.performItemClick(view, position); } }); + + 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 view.setOnCreateContextMenuListener(new OnCreateContextMenuListener() { @@ -458,7 +481,7 @@ public class TaskListAdapter extends ArrayAdapter { private void setTaskProgress(final TaskModelForList task, View view, int progress) { final ImageView timer = ((ImageView)view.findViewById(R.id.imageLeft)); task.setProgressPercentage(progress); - hooks.getTaskController().saveTask(task); + hooks.taskController().saveTask(task); // show this task as completed even if it has repeats if(progress == 100) @@ -477,7 +500,7 @@ public class TaskListAdapter extends ArrayAdapter { @Override public void onClick(DialogInterface dialog, int which) { task.stopTimerAndUpdateElapsedTime(); - hooks.getTaskController().saveTask(task); + hooks.taskController().saveTask(task); timer.setVisibility(View.GONE); } }) diff --git a/src/com/timsu/astrid/activities/TaskListSubActivity.java b/src/com/timsu/astrid/activities/TaskListSubActivity.java new file mode 100644 index 000000000..142aa4b21 --- /dev/null +++ b/src/com/timsu/astrid/activities/TaskListSubActivity.java @@ -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 tagMap; + private ArrayList taskArray; + private HashMap> 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 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>(); + for(Iterator i = taskArray.iterator(); i.hasNext();) { + TaskModelForList task = i.next(); + + if(task.isTaskCompleted()) { + if(!filterShowDone) { + i.remove(); + continue; + } + } + + // get list of tags + LinkedList tagIds = getTagController().getTaskTags(getParent(), + task.getTaskIdentifier()); + LinkedList tags = new LinkedList(); + 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() { + @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 getTagsFor( + TaskModelForList task) { + return taskTags.get(task); + } + + @Override + public List 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; + } +} \ No newline at end of file diff --git a/src/com/timsu/astrid/activities/TaskView.java b/src/com/timsu/astrid/activities/TaskView.java index 7278e1806..868d7ae7d 100644 --- a/src/com/timsu/astrid/activities/TaskView.java +++ b/src/com/timsu/astrid/activities/TaskView.java @@ -232,7 +232,7 @@ public class TaskView extends TaskModificationActivity { public void onWindowFocusChanged(boolean hasFocus) { super.onWindowFocusChanged(hasFocus); - if(hasFocus && MainActivity.shouldCloseInstance) { // user wants to quit + if(hasFocus && TaskList.shouldCloseInstance) { // user wants to quit finish(); } } diff --git a/src/com/timsu/astrid/activities/TaskViewNotifier.java b/src/com/timsu/astrid/activities/TaskViewNotifier.java index 8af7b4f0b..ff04caa49 100644 --- a/src/com/timsu/astrid/activities/TaskViewNotifier.java +++ b/src/com/timsu/astrid/activities/TaskViewNotifier.java @@ -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; import java.util.Random; @@ -60,7 +79,7 @@ public class TaskViewNotifier extends TaskView { @Override public void onClick(DialogInterface dialog, int which) { setResult(Constants.RESULT_GO_HOME); - MainActivity.shouldCloseInstance = true; + TaskList.shouldCloseInstance = true; finish(); } }) @@ -87,7 +106,7 @@ public class TaskViewNotifier extends TaskView { repeatInterval); setResult(Constants.RESULT_GO_HOME); - MainActivity.shouldCloseInstance = true; + TaskList.shouldCloseInstance = true; finish(); } }); diff --git a/src/com/timsu/astrid/data/task/TaskController.java b/src/com/timsu/astrid/data/task/TaskController.java index 811ffbd98..096d4a351 100644 --- a/src/com/timsu/astrid/data/task/TaskController.java +++ b/src/com/timsu/astrid/data/task/TaskController.java @@ -24,7 +24,6 @@ import java.util.Date; import java.util.HashSet; import java.util.List; -import android.R; import android.app.Activity; import android.content.ContentValues; import android.content.Context; @@ -35,11 +34,15 @@ import android.database.sqlite.SQLiteOpenHelper; import com.timsu.astrid.data.AbstractController; 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.utilities.Notifications; -/** Controller for task-related operations */ +/** + * Controller for task-related operations + * + * @author timsu + * + */ public class TaskController extends AbstractController { private SQLiteDatabase database; diff --git a/src/com/timsu/astrid/data/task/TaskModelForList.java b/src/com/timsu/astrid/data/task/TaskModelForList.java index 1b1aa402e..128279b7b 100644 --- a/src/com/timsu/astrid/data/task/TaskModelForList.java +++ b/src/com/timsu/astrid/data/task/TaskModelForList.java @@ -232,4 +232,9 @@ public class TaskModelForList extends AbstractTaskModel { public void setDefiniteDueDate(Date definiteDueDate) { super.setDefiniteDueDate(definiteDueDate); } + + @Override + public void setImportance(Importance importance) { + super.setImportance(importance); + } } diff --git a/src/com/timsu/astrid/sync/RTMSyncService.java b/src/com/timsu/astrid/sync/RTMSyncService.java index 85725c09b..0d1c8d160 100644 --- a/src/com/timsu/astrid/sync/RTMSyncService.java +++ b/src/com/timsu/astrid/sync/RTMSyncService.java @@ -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; import java.io.IOException; diff --git a/src/com/timsu/astrid/sync/SynchronizationService.java b/src/com/timsu/astrid/sync/SynchronizationService.java index 574da0eb5..761190cf3 100644 --- a/src/com/timsu/astrid/sync/SynchronizationService.java +++ b/src/com/timsu/astrid/sync/SynchronizationService.java @@ -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; import java.io.IOException; diff --git a/src/com/timsu/astrid/sync/Synchronizer.java b/src/com/timsu/astrid/sync/Synchronizer.java index 5e211c55d..6a95c88c5 100644 --- a/src/com/timsu/astrid/sync/Synchronizer.java +++ b/src/com/timsu/astrid/sync/Synchronizer.java @@ -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; import java.lang.reflect.InvocationTargetException; diff --git a/src/com/timsu/astrid/sync/TaskProxy.java b/src/com/timsu/astrid/sync/TaskProxy.java index 02e741141..3c31c6432 100644 --- a/src/com/timsu/astrid/sync/TaskProxy.java +++ b/src/com/timsu/astrid/sync/TaskProxy.java @@ -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; import java.util.Date;