From afaac7d9f95e99bbdf0b75c94b46eb72a6ee0598 Mon Sep 17 00:00:00 2001 From: Tim Su Date: Fri, 16 Jan 2009 22:35:37 +0000 Subject: [PATCH] Updated synchronization algorithm to handle tasks created in Astrid & also RTM, added time remaining to the dashboard. --- AndroidManifest.xml | 6 +- res/drawable/btn_check_0.png | Bin 2464 -> 1491 bytes res/drawable/btn_check_25.png | Bin 2216 -> 1502 bytes res/drawable/btn_check_50.png | Bin 2234 -> 1508 bytes res/drawable/btn_check_75.png | Bin 2246 -> 1499 bytes res/drawable/btn_check_off.png | Bin 1934 -> 1172 bytes res/drawable/btn_check_off_pressed.png | Bin 2104 -> 1513 bytes res/drawable/btn_check_on.png | Bin 2242 -> 1486 bytes res/drawable/btn_check_on_pressed.png | Bin 2225 -> 1575 bytes res/layout/task_list_row.xml | 9 ++ res/values/colors.xml | 1 + res/values/strings.xml | 5 + .../astrid/activities/SyncPreferences.java | 7 +- src/com/timsu/astrid/activities/TaskList.java | 51 ++++--- .../astrid/activities/TaskListAdapter.java | 36 +++++ .../timsu/astrid/data/AbstractController.java | 4 +- .../astrid/data/alerts/AlertController.java | 6 +- .../astrid/data/sync/SyncDataController.java | 6 +- .../timsu/astrid/data/tag/TagController.java | 5 +- .../astrid/data/task/TaskController.java | 5 +- .../astrid/sync/SynchronizationService.java | 103 +++++++------- src/com/timsu/astrid/sync/Synchronizer.java | 130 +++++++++++------- src/com/timsu/astrid/utilities/Constants.java | 2 +- 23 files changed, 244 insertions(+), 132 deletions(-) diff --git a/AndroidManifest.xml b/AndroidManifest.xml index 6fcda0296..df4574b4f 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -1,13 +1,13 @@ + android:versionCode="60" + android:versionName="2.0.0-rc1"> + android:value="60" /> diff --git a/res/drawable/btn_check_0.png b/res/drawable/btn_check_0.png index 3615c3165c7173085446066c2d0f8a0723a85a9b..8bd6db1f134344147e4a8ebeac5fd3d308013051 100644 GIT binary patch delta 1449 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 TA}Crz00000NkvXXu0mjf%Ktch diff --git a/res/drawable/btn_check_50.png b/res/drawable/btn_check_50.png index ee85bf696a223d0ae5cf9d6b6a1608d609837fe2..60363ca746182fc968df5c9c300031f0102012e2 100644 GIT binary patch delta 1480 zcmV;(1vmP-5#$RYiBL{Q4GJ0x0000DNk~Le0000c0000m2nGNE0DZCuD3Ku+3I71D z007{@44IKY9)H*j8w3IrU$hrq000GKNklGS<@{kNr-9E zbG9PVPwZY)Lm z;kIICYtl9+CrQ)foa04z!fD#3Eo)>Kec?b(c+dO(&VT>?zt8i$M=%V7C$Whw_?YR9s3Np1KeNI$2zGX2wvdG*x1;)mX?-w^Z#1JI&*cVrlv-B?b>w)_{}hk z>D4^8JAW37o%VXY?Ygd`X&RcQq3imlU*@=8ueUuGi=75`uj(-%5Dtg;YMNG7saBc6 zR7?;A!r|~)xc!KFwk}Ve+3goaT&`pFw+B@4W?ui zE?;<`bZV0Dp2PH=eA^nH)&?{EwH~Oo`j``Elz%keeEJHF#k;g`gPGy$+&|Dqur-Px z2Xu~@V&rL-=v$K3!|zUap`{XNkDG;f31EIiD(h`#(Wc1HubT&}z$HwrD>?9J2Ff}#B`1m+CZ{DQ4yL-vQKM$V8p1NFy$}hmI3h8YpICA0* z^7%Z4LSe}UvBWZ|TA?UgldO5U-EIPb01k(PP$)!OTN}w_lHuXu^0LP+ez*jxP=AEE zEF{@O*9+$;6bj^Wxh3OP_<8L(R_QUD%|<4Z!Dh43*w{!Y6arvkVuBktZZJ1DN29xq zXj2ex-6SOql>DOELwz4nZ+D=o>avYwP2_8OY$dRcjt=7S_+kfHrlFyMP$+~XNu*LK zTrL+aEzjU>d=CB2TV$vIM9IO9gMS0~pW2P8s+fgA6|m}xS);zS^od5JjEsyRiXtA5 z2dC3XFc`$=^C3wRvMi(PI{EhV%*}m3uBC$m-N#W?m142TDkc2-cV(qwR{4X$Ad{1m z$g+$e2soWi91aJfD6+7yKt7*mMm0z^zsAACN0*ts5^h6^RaTnTX1CjMxqnX2m_hr>a!SR|LrF*`fU^z<}OclJ;y6i`*Q`Vnn06>Z#Om0$oKj|VlAK~WSWNkSAw zbX`YPRT7B=e!rjk`g+po^t!Xgh80__ybiUt^4G)!q9`KEGMc8LC<;!e6Q9qAq9`kx zz0zami?2&Dvx2QmNtLZN9DinFe7rn(I-RDsx0g&Nv&`(PRU{j!U?r<{9 z008+zyMK{D9)H;Z1q%cWlGIoW000O&Nkl5nn_4`q<>eok6u6T-~aP}KF(t}vLidP zBLfh-UA%JT3Nte^8W3tP^)cb819p3T$h7VY7cX99W@e^uaB%R%xpU`E>$;xSG%X3> zdEUckO9%krIF3`V*B57JXP1GyFTM0q{n=-q?a6E$IDP5Tr7vE(bm^PJ!^0DUgM-Ho z_4W#ZZGYP=EiGMr;e{7oe(kl_t^zlKN^oEI?TrE_UwrY!Z$0wkTjTW`&yDZzUAq1}LV%xSl6tD-`Cxk#LjY2-hKjwZx>AhJ_C$`g(xt{0l?eCDCm_kbB zr!?6MtZ-dd1i?NmcDECMgE#*8Yg!wB;eX5t$Rwaz2i@sMV7JfPKG${Ko?xX9)^VI2 zVB6x~ms!vAcI@*%3n4%V=3n_4)s-uZ$IA>2K(h&(&3?q_mzkV;oJOODZQFjq5Z&o= zw*V70Pp|%*#`~`_ktj2g0M~{4RfU^+n#aEMV_K~SmSrJ?@PDhC zJP24BEQdjWLI|Xk9f!SuYaxSL)#Pt~_$BpQzhfd%;dl}}AXfw{`2;7Q{2`HOKebu~ z&-0K{`lY>RHrT;z5Uf($!e3oo#j-3inG6F1106QH+1HIITZJ6|`txt7u3TXvQQ^eU zHdmWl;0&H6^{MZXK5>S6t19r+#2sLyh7>5WhMs8WK*C?D3zfkhe=LfVDynM;8;zX&1UD$d7c-PLC@6H zVb%vLgkWW5h1<7p)7RIB5Q5FkO_rCJw;PH?qLV#zBg(yvRhHhk!sgN|jDHW9jHN-> zpj3f;L#O}Dw;7)L3L#BVt(5T9{N9>%T~`Ld`cCrsJlSlPQ>RW53WZo(TVs8F zoyEmP&YU?zEEYp4jpF@v-dXr9=6kjnMW&{v(DX3>eq)~6`}0i1%Z#NV ztV5*%n`NkmA7}cr-^Yj@r(zbcEPIFE{Qz#CuMU!Dr4Q796+&Pb2DMs^QmKRxf=DF7 z*w`3C2v%2DdHe0RG0ifDHpt1TC&==5A{42&pj?MSD@FG6KO#DKoPTD$Osi#mC>8p^ z+nMM4j}~bc$`Qi z!szHI>2w+?C83axQhy37^i^v0Fs1Tk>aoZ8^e3LCY1y=z%^ejlm<57>JViG*L7*NTGW~z1$UDA$agvW*i$$zG#iZ_0oC0* z_?mqX8SDtKpmW!C35Ub<_4QFK7RlvuXqtvn3SHlBq%F&$R)4FJzki>GCCHwBl2ABK zskGTC;roGo5E-cM0PAK#2tj{;Kh0(n(=@46Do81ja@%jC(IB7CQ>|7=Bof5qF-&s{ z%d!s8N63ym4jf>?-k`hwyRM5;iot;a9LJ$pC?KUo)3#kzt5wS7GLcAxR4PTI(V)?2 z?Ax^keSf3?>woTNyMryH>m-v&a=9GEVzJZUY;JB64u?slQYfV`O|xfT9Q+{xl6qD! z<)^M7;N8q|9F(RpJUq{fGCDd+EEc0wD(x9iyS1HufVDxZ12OwQANxOw zdyaz;g0b;&HtycVvn)=XI)z~vn5MaFM|QBMBu}()?0*k7ut5a#-hPL=u1h+T!7vP5 z*TpnVY}@XzbP#3(*+8^kduq07n&y^inj@N~N!N8diMm&#Erg)eYJH@qiXahEN{Ypz zQ>)ds+9S{%Oa?7qC=~7$3WcVw>qsd*KP~lIyEP;a^4<1Xp04X`Y;4p`)7${I_G5PK z_19m&o_|iKM=xBs@JN4uzpyOJk5KP0=3V}bO9&wh!=PTTdvkMhD+>z?*MZ!gV0B$z zsZ=UgXJ=>AN-6QoGtXpGsgyo6G$apmFz3%_Z``<1o12@vH9tT9&w9On)i8|P{!_%R zlfh6ZG+|lRQ_*Pjsq^R0e=?KFq~h^-;;^T3wrp*?%gf74moHzw1}ua^p#{sbHhN9I zg{Eo8ZQFhnI0+mFlI^{RHBuCS3%muaD5c(U9OwRl{s3uTj{pPhX}Iw*{1M{;1)$uW l1&-{iX>!ilMLgj&O_LUz;YD9KkQ3hXzJI^-Jpbq8J%V8vJc&c(2_36o zt6=3Gvh_v~1oP(xpaF0J>&m$QFc1c&0jwE9E-={#v|hb>_4THvrtP)0wZ4^3DS{xN zXZH^;iEAP8AJeQX7m4YTMh0sLYZ z#^hoe+kX-a29LYl?q*%r(KHQB)6jK&#V>PRx7*zu3AW}ZGfj3*m|ea>D4r?tWf!IPN&lgtS<&88-{_d>;Eg5D2fYM7J!)=SZT2M zOq@&SP7<3Q$KSG-uA^^R(^F|MQ(sGgDmBO4cz-4y2F1(Ea z1VJ#@DGDa(x}F1DDT@U!oWdpCqNNE$0qi!&s57}e9=(863`{g#`M6dyt#$FUw-N7u zjo$+{2_kVwZ|tIZ`yNzP#V`z0V`5Q@nfav<*m5m+`P>Plf4?E%fpt|7RUj&EXU_{C zGk-TXM>?Ihx>8ya*y9GkmGj-$Cch=%f!ek3AOZJh90Xqc2zzA}$z(DYk;?|NLMe*E z&6_u=udm1N_ZNlw{`2=(GxZ$-54ao4Gj^AMCj<~C#FERTVvV9~Ewbk2bUN{PJXBUz;`8~~v}qHONQ8lbft=fe7f#NDQh(F% zFaZ%-XnXE7s;ZJqCg-i2r{~gnEMH?bn~j;78EiHiH8nN(d_Dk1M@RYn+HX9(`v*1A z%Q>iw2JzXf*nM3eP+_k`QIrLNWG&<+HC700>(;G=LZR#gS*EJ03ZKu1BuPx)`x^&W zse1&a0|_-N_6@sxXn1N1ilSgP27g7sid)Q@^{uT>AP``1a1c=xak*SnS6AcpdZ|zU zfHW6HHnLE&sjLf=b+53q{Q!!hkWQyrq=jF4ROVaEYJRWR%lP;>vMeJA0@X@Cl~d;t zWrev6%&HKbfoXXcySv_9AohH?Wmzo0)3gq|-HyZIAbfX>HB%RnC(ly327l59B-LyL zPO1$&edH5VRYg&h;zzV>DO$e9^1%RHE*HwwKg37QB2{OLvTkJaRXAQl!@)CD*ei*} zVoRb=dx@&m#! z3>rILV$#;hR6JXpW3?~SvVZGvPV9xAS?ooZ)Q?#sv6L(PohP|J)Q>F7?AUdfWHL!A zm0EVeUs4gbM#@q&Qs3N(-EK!wlx0H|OR)K_jE2MEc-{jgM=F(iEU0ie90#JsEH)Yn zg{B)C8XQ1bPfLOzAc`WABoPXQrh(C7p9l^CulDx#KHu2bxPGO3|9?Wqw%fOFKRkBq z*cITMVHm;1z#PC);6P7L&)&ASwpy>(TT>3OOeUlE_4SQ+cX$5^`~-Yy7)E?CFaS63 zEU*Lc19fFQ^Iik~1bzl4=dlz318@KvA06AP%LsN4hyd|?Eeork(^tV(!B)Xm!OAoK Z1G@X$UYHHveED%PDHLkV1j;es}TSI delta 2223 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 UtMut_2mk;807*qoM6N<$f{U7``~Uy| diff --git a/res/drawable/btn_check_off_pressed.png b/res/drawable/btn_check_off_pressed.png index 88be2ddfcf1a5161c9e19adbe2634c8dd42285b5..47c1a460f110f500a9de3d832594dc794e3ae90c 100644 GIT binary patch literal 1513 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 diff --git a/res/drawable/btn_check_on_pressed.png b/res/drawable/btn_check_on_pressed.png index 716a7f778494922f9c64d4f770fdfbeb1af35e6b..ee2175d04711605373602c5b92fa4507e3958be8 100644 GIT binary patch literal 1575 zcmV+?2H5$DP)(RCwC#m|JWVRT#(rXJ&VC>1BJN z&;r|L%cYWn)F25#B0OMB!03}VLZZ^zb*+=0 zVH8EC4+Ep3uX)=~Z_%&c5z9~^i>*8M$%-e!wWASC&jOslxX`OD@vy=C2-gK7Qs=!- zo|RAq!CKo}600yX1Efi;bm^_-wwgie`2w`JERf;TS}^!&G&I%lCd`nf($ZS#`LUKF zBQRkXhNc?7OtJX14~cM&l9v;>4DZz>(f#8u$H3E(^(w^A{B zbAw$U$fFVrMZtW6Rv;76zPLnS;{>z)ci@i(RTaBqzyq-stgpQYK{>$e1j}a01vBmH zU?BtNRhH%$N(W98RG7Z6(Y@w2f;E;A)FkPcb_`&8f^nXK64p(^xW+SNsn6@_OGG>C z3Rx@Ohfiw?z^2NL_#iHKU|HET03A}9fn9o!2$~$ z&lItk1C`EWu^X`3vnPSB#Tu%PWQ^Db{Ww^AC#uXOtZ0wzW@afVZZWRPm&kGDL=rgK zPpzSbWU)8YUW%zhOEi?U*aS%&IOy-d+C@R6qd!BWQedVDM!F1taWJ2>7|(I#DeC>Z zX%Em}MJIzm=Ts1nwIuUG?Wg_1p+*fQ7ciy3UX8Y4=juj+IsrZ%ck;sSOxl&cImH-v zf$gSr@86)C#t5+QEI|#_;gO}WtecN{)mA>jVXRLlCJE3U;}- za}((d7nxm^&$}fRC=KsdP2$v05T}i#%CriSu-<%|u9>Ek3-^w+V#i7f_E8G<*|=h1 z`Ad$-!;%|gCY%)r*Iin^vXTf5`|N-QB?JP}wPdc!keVr}is+W1dc3ou9aj7tI2BTs zXRbWw-oc->43WL85EbdKDfL4a{mL>jzy-M|HK9NWxcSmTbTw{-n2y34I|HxzaxCX< zF_s$#?sJm%nl#=X^Vu;ZL(YbqQ7)9h-WA-4>OczVD<`hIH}9O8!yGNznkK;r2&ikyg59 zsX2*;A{|nf)Tz2~VT<+t(ray6xh=3zPy>@(vXKd=6_o0E63R-GgU= zu#*nYAOCR4<~y2s1y9ZDZH7RpUWwY_Bwc5iv=0m5WWSNPdk^(2-~-Zev=A8806b3e zVCT#A9h=vh6>ZDu(Sh@u-}L80qfOW>H$~U6+1S4KyippZi115*GoZN-g=yp9{V^BRe*?j~B!OE{X%V1@&GFTa`bmM;k Z1^{MNY!bw$v*7>$002ovPDHLkV1m94^?(2X literal 2225 zcmV;i2u}BjP)Px#24YJ`L;wH)0002_L%V+f000SaNLh0L01FcU01FcV0GgZ_00007bV*G`2iXAy z3NtV)e(sL|00=QjL_t(&-tC!fj9g_E$A9O!^S-k?-RGx`QKKMy0=^&`5{wdJG*MDO5fb8Clqk_AB=zkR5wR60LR)NF=)Ui~&+OcL zpW}z;&hG5%Ow85~W>4}YJG1wm^S}T5f6h728Q8Kd+p;YKkhQ-2%g?Xl!#BSW2a-TM zTK@~H01d$J2<-JAAK>{Hr+d1ysS7@O#g6wR;xZibqz_d&e{>1fs;$=TKVLX<<~7i> z4}Nv5^3V_7Zp&;xuB#y@t?9s6$BHIy37^~C!CP~9YSEq+AY3gMME@-N^2-M{_p z@Si9D3cL;!HUgUk_J8ARSU?*M6&nhwoDZPUs}%qhQNkEPLdJF^b`SUY zdx74Kz~*|p?9T3VBSWo@1P#H4%Qbes(vpZEF;GiyxAbIFb|jJ|>w!&p*bHLO&?;61 zdwzh50lesg1q~I9h0v;qicyo zMZh3Xg>DnWPwc>&JYp-3S=)&WEP}GgtcFX4W)YnJchb23DEv4Hh220OumIgNF+Nov z;)7$`@CzQHLfpldc3_QKHH#oa$MA-gRyQU+CJ+glL!=2-TbNzE!8i}0Wy7qdK+1zc9UgwG1?bM`pHpyUxsUsD z7nAeqH0ECg4G>9mECZt!Q44kjSkFM*!s}De2w*%1-5FR2S4%<_vN3qK4BtEBUVU*K z7>O<@!sXQ*cO@_8y*+Uj3a?@pDu`!X{tdxeZO^Q=7!6y%T6agK0?$pt;Sv-rTvarD zalo)U7f@fpe%6Dx3vg%>p7fxw2N;c_6=CO0iaX-tT$9UUYo`b*lS>U)6tg2{ZHQ6p zfZ8a}iommT@K^)R4!g>8w4l`HJia0x!(9vb*1g>>4^9=~iD@`wAb%0C3rIMi$tlB~ zI>wFJ?WF7^mBMM)d`0Ro62Sq`dYFy)gp zaHb58Pr_627NAL>49vhyl~HcnF~UxhCqH)z{19X*!Kf_8UIo^yB?GHbo{g3bVK4)1 ziyklK^jF_)c{EkzshKV&YB8p3@cnn-iB_Pg=&&*eSI+ivPv1pcs5J`nC((MvwaKlq zqPDp+88lnAsM4bDX}?s4sXUxW0y!WZff4xj={$|e924adzaOw}*N;Zm)ZFXeF`49{ zu1mQfrqs%B64d9H9~z=w(JaMoFN28w#e+f9*Y*AI_H2g7)#qK$cO6qI0_?$Ka|P$R zH>ymT=;&90bj9O4vY*SgcSHUdL3MUfr-&m%T4JJW%V4!;!RQ)Ee*w5FTV;Lt>j zfWXNj9i^nf$aOsO>PJB5J-#rrn`_2~@CwHXYE!N}MIhHJELLgT$QYt>yUa<{DXPG| zwn4dQcqV4tqjg7sGai^U*t$!^Rq&?aL-X5tV016>`XoW+ECl{y_7rUiBQ2%etqwMr zs*%zF3+tIEG-GW<>gN}h5DwBI2@BLg0~Xm8)iVQ876V>>6Q*DN&q1f%;X~ zMXk|4$~rSI5XFcnowSIE7$b-gQCWl1HKtYz3J20>Fc}}Im8sNcP`|u3(3Ki!`i+Du zMnsG>59=F(E#xZ{rc2VDig}`8;D$z4*>Hx~tU&e2CSh6!#le>!iamBJ zeDmqwmX882CgL1v_?&C|n?lTEBnY`2*bD5AdaCm?rf+EnfR}*RJ&%8cA=4fG01{ay z8aZa7gUR^@tL{8ODJtAswq;wkWm~po|0nhzNz2Gqvs diff --git a/res/layout/task_list_row.xml b/res/layout/task_list_row.xml index 2cce1354a..2318430ea 100644 --- a/res/layout/task_list_row.xml +++ b/res/layout/task_list_row.xml @@ -69,6 +69,15 @@ android:paddingRight="10dip" android:singleLine="true"/> + + #FFFB6666 #ffF0E89E + #ff88AAFF #ff888888 #ff888888 diff --git a/res/values/strings.xml b/res/values/strings.xml index a78269425..2a05d9ba7 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -84,6 +84,10 @@ Tags: No Tags + Left + Over Time + Spent + Add Tags Display @@ -227,6 +231,7 @@ Wish me luck!\n Synchronize Now! Clear Personal Data Clear data for selected services? + No Synchronizers Enabled! diff --git a/src/com/timsu/astrid/activities/SyncPreferences.java b/src/com/timsu/astrid/activities/SyncPreferences.java index e1e47c397..1b7966cb6 100644 --- a/src/com/timsu/astrid/activities/SyncPreferences.java +++ b/src/com/timsu/astrid/activities/SyncPreferences.java @@ -9,17 +9,16 @@ import android.widget.Button; import com.timsu.astrid.R; 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; public class SyncPreferences extends PreferenceActivity { - + @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); addPreferencesFromResource(R.xml.sync_preferences); - syncFinished = true; getListView().addFooterView(getLayoutInflater().inflate( R.layout.sync_footer, getListView(), false)); @@ -28,7 +27,7 @@ public class SyncPreferences extends PreferenceActivity { syncButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { - Preferences.setSyncLastSync(SyncPreferences.this, null); + setResult(Constants.RESULT_SYNCHRONIZE); finish(); } }); diff --git a/src/com/timsu/astrid/activities/TaskList.java b/src/com/timsu/astrid/activities/TaskList.java index fec5494c0..91c10dc9e 100644 --- a/src/com/timsu/astrid/activities/TaskList.java +++ b/src/com/timsu/astrid/activities/TaskList.java @@ -59,7 +59,9 @@ 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.utilities.StartupReceiver; @@ -105,8 +107,6 @@ public class TaskList extends Activity { public static final int FLING_VEL_THRESHOLD = 300; // UI components - private TaskController controller; - private TagController tagController = null; private ListView listView; private Button addButton; private View layout; @@ -127,6 +127,11 @@ public class TaskList extends Activity { static boolean shouldCloseInstance = false; + // database controllers + private TaskController taskController; + private TagController tagController; + + /* ====================================================================== * ======================================================= initialization * ====================================================================== */ @@ -140,13 +145,15 @@ public class TaskList extends Activity { StartupReceiver.onStartupApplication(this); shouldCloseInstance = false; - controller = new TaskController(this); - controller.open(); + taskController = new TaskController(this); + taskController.open(); tagController = new TagController(this); tagController.open(); + Synchronizer.setTagController(tagController); + Synchronizer.setTaskController(taskController); + setupUIComponents(); - fillData(); // auto sync Integer autoSyncHours = Preferences.autoSyncFrequency(this); @@ -155,11 +162,10 @@ public class TaskList extends Activity { if(lastSync == null || lastSync.getTime() + 1000L*3600*autoSyncHours < System.currentTimeMillis()) { - Synchronizer.synchronize(this, new SynchronizationListener() { - show dialog! - }); + Synchronizer.synchronize(this, true, null); } - } + } else + fillData(); } public void setupUIComponents() { @@ -345,15 +351,15 @@ public class TaskList extends Activity { if(filterTag != null) { List tasks = tagController.getTaggedTasks(this, filterTag.getTagIdentifier()); - tasksCursor = controller.getTaskListCursorById(tasks); + tasksCursor = taskController.getTaskListCursorById(tasks); } else { if(filterShowDone) - tasksCursor = controller.getAllTaskListCursor(); + tasksCursor = taskController.getAllTaskListCursor(); else - tasksCursor = controller.getActiveTaskListCursor(); + tasksCursor = taskController.getActiveTaskListCursor(); } startManagingCursor(tasksCursor); - taskArray = controller.createTaskListFromCursor(tasksCursor); + taskArray = taskController.createTaskListFromCursor(tasksCursor); // read tags and apply filters int hiddenTasks = 0; // # of tasks hidden @@ -456,7 +462,7 @@ public class TaskList extends Activity { @Override public TaskController getTaskController() { - return controller; + return taskController; } @Override @@ -542,7 +548,16 @@ public class TaskList extends Activity { protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); - if(requestCode == ACTIVITY_TAGS && resultCode == RESULT_CANCELED) + if(resultCode == Constants.RESULT_SYNCHRONIZE) { + Synchronizer.synchronize(this, false, new SynchronizerListener() { + @Override + public void onSynchronizerFinished(int numServicesSynced) { + if(numServicesSynced == 0) + DialogUtilities.okDialog(TaskList.this, getResources().getString( + R.string.sync_no_synchronizers), null); + } + }); + } else if(requestCode == ACTIVITY_TAGS && resultCode == RESULT_CANCELED) filterTag = null; } @@ -576,7 +591,7 @@ public class TaskList extends Activity { new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { - controller.deleteTask(taskId); + taskController.deleteTask(taskId); fillData(); } }) @@ -648,7 +663,7 @@ public class TaskList extends Activity { else { task.stopTimerAndUpdateElapsedTime(); } - controller.saveTask(task); + taskController.saveTask(task); fillData(); return true; @@ -697,7 +712,7 @@ public class TaskList extends Activity { @Override protected void onDestroy() { super.onDestroy(); - controller.close(); + taskController.close(); if(tagController != null) tagController.close(); } diff --git a/src/com/timsu/astrid/activities/TaskListAdapter.java b/src/com/timsu/astrid/activities/TaskListAdapter.java index bd6fc55b9..ce3955f00 100644 --- a/src/com/timsu/astrid/activities/TaskListAdapter.java +++ b/src/com/timsu/astrid/activities/TaskListAdapter.java @@ -105,6 +105,7 @@ public class TaskListAdapter extends ArrayAdapter { // find UI components final TextView name = ((TextView)view.findViewById(R.id.text1)); final TextView dueDateView = ((TextView)view.findViewById(R.id.text_dueDate)); + final TextView remainingTimeView = ((TextView)view.findViewById(R.id.text_remainingTime)); final TextView tagsView = ((TextView)view.findViewById(R.id.text_tags)); final CheckBox progress = ((CheckBox)view.findViewById(R.id.cb1)); final ImageView timer = ((ImageView)view.findViewById(R.id.imageLeft)); @@ -114,6 +115,7 @@ public class TaskListAdapter extends ArrayAdapter { view.setTag(task); progress.setTag(task); + // name String nameValue = task.getName(); if(task.getHiddenUntil() != null && task.getHiddenUntil().after(new Date())) nameValue = "(" + r.getString(R.string.taskList_hiddenPrefix) + ") " + nameValue; @@ -172,6 +174,40 @@ public class TaskListAdapter extends ArrayAdapter { dueDateView.setVisibility(View.GONE); } + // remaining time + if(task.getElapsedSeconds() > 0 || task.getEstimatedSeconds() > 0 || + task.getTimerStart() != null) { + remainingTimeView.setVisibility(View.VISIBLE); + int elapsed = task.getElapsedSeconds(); + if(task.getTimerStart() != null) + elapsed += ((System.currentTimeMillis() - task.getTimerStart().getTime())/1000); + String remainingString = ""; + if(!task.isTaskCompleted() && task.getEstimatedSeconds() > 0) { + int remaining = task.getEstimatedSeconds() - elapsed; + remainingString = DateUtilities.getShortDurationString(r, + (int)Math.abs(remaining), 1) + " "; + if(remaining >= 0) + remainingString += r.getString(R.string.taskList_remaining); + else + remainingString += r.getString(R.string.taskList_overtime); + } else if(elapsed > 0) { + remainingString = DateUtilities.getShortDurationString(r, + (int)Math.abs(elapsed), 1) + " " + + r.getString(R.string.taskList_spent); + } + + if(remainingString.length() == 0) { + remainingTimeView.setVisibility(View.GONE); + } else { + hasProperties = true; + remainingTimeView.setText(remainingString); + if(task.isTaskCompleted()) + remainingTimeView.setTextColor(r.getColor(R.color.taskList_completedDate)); + } + } else { + remainingTimeView.setVisibility(View.GONE); + } + // tags List tags = hooks.getTagsFor(task); StringBuilder tagString = new StringBuilder(); diff --git a/src/com/timsu/astrid/data/AbstractController.java b/src/com/timsu/astrid/data/AbstractController.java index f54a330a4..fa14f2107 100644 --- a/src/com/timsu/astrid/data/AbstractController.java +++ b/src/com/timsu/astrid/data/AbstractController.java @@ -34,7 +34,6 @@ abstract public class AbstractController { // special columns public static final String KEY_ROWID = "_id"; - // database and table names protected static final String TASK_TABLE_NAME = "tasks"; protected static final String TAG_TABLE_NAME = "tags"; @@ -42,6 +41,9 @@ abstract public class AbstractController { protected static final String ALERT_TABLE_NAME = "alerts"; protected static final String SYNC_TABLE_NAME = "sync"; + abstract public void open(); + abstract public void close(); + // cursor iterator public static class CursorIterator implements Iterator { diff --git a/src/com/timsu/astrid/data/alerts/AlertController.java b/src/com/timsu/astrid/data/alerts/AlertController.java index a20f26dd9..84d7b0103 100644 --- a/src/com/timsu/astrid/data/alerts/AlertController.java +++ b/src/com/timsu/astrid/data/alerts/AlertController.java @@ -130,14 +130,14 @@ public class AlertController extends AbstractController { * initialization call) * @throws SQLException if the database could be neither opened or created */ - public AlertController open() throws SQLException { + @Override + public void open() throws SQLException { alertDatabase = new AlertDatabaseHelper(context, ALERT_TABLE_NAME, ALERT_TABLE_NAME).getWritableDatabase(); - - return this; } /** Closes database resource */ + @Override public void close() { alertDatabase.close(); } diff --git a/src/com/timsu/astrid/data/sync/SyncDataController.java b/src/com/timsu/astrid/data/sync/SyncDataController.java index 3c74b80c1..d1b95ba7f 100644 --- a/src/com/timsu/astrid/data/sync/SyncDataController.java +++ b/src/com/timsu/astrid/data/sync/SyncDataController.java @@ -119,15 +119,15 @@ public class SyncDataController extends AbstractController { * initialization call) * @throws SQLException if the database could be neither opened or created */ - public SyncDataController open() throws SQLException { + @Override + public void open() throws SQLException { SQLiteOpenHelper helper = new SyncMappingDatabaseHelper(context, SYNC_TABLE_NAME, SYNC_TABLE_NAME); syncDatabase = helper.getWritableDatabase(); - - return this; } /** Closes database resource */ + @Override public void close() { syncDatabase.close(); } diff --git a/src/com/timsu/astrid/data/tag/TagController.java b/src/com/timsu/astrid/data/tag/TagController.java index 026506302..be3697615 100644 --- a/src/com/timsu/astrid/data/tag/TagController.java +++ b/src/com/timsu/astrid/data/tag/TagController.java @@ -209,15 +209,16 @@ public class TagController extends AbstractController { * initialization call) * @throws SQLException if the database could be neither opened or created */ - public TagController open() throws SQLException { + @Override + public void open() throws SQLException { tagToTaskMapDatabase = new TagToTaskMappingDatabaseHelper(context, TAG_TASK_MAP_NAME, TAG_TASK_MAP_NAME).getWritableDatabase(); tagDatabase = new TagModelDatabaseHelper(context, TAG_TABLE_NAME, TAG_TABLE_NAME).getWritableDatabase(); - return this; } /** Closes database resource */ + @Override public void close() { tagDatabase.close(); tagToTaskMapDatabase.close(); diff --git a/src/com/timsu/astrid/data/task/TaskController.java b/src/com/timsu/astrid/data/task/TaskController.java index 8ce850075..6ace154e9 100644 --- a/src/com/timsu/astrid/data/task/TaskController.java +++ b/src/com/timsu/astrid/data/task/TaskController.java @@ -364,14 +364,15 @@ public class TaskController extends AbstractController { * initialization call) * @throws SQLException if the database could be neither opened or created */ - public TaskController open() throws SQLException { + @Override + public void open() throws SQLException { SQLiteOpenHelper databaseHelper = new TaskModelDatabaseHelper( context, TASK_TABLE_NAME, TASK_TABLE_NAME); database = databaseHelper.getWritableDatabase(); - return this; } /** Closes database resource */ + @Override public void close() { database.close(); } diff --git a/src/com/timsu/astrid/sync/SynchronizationService.java b/src/com/timsu/astrid/sync/SynchronizationService.java index 1f4422f99..08b6a35fe 100644 --- a/src/com/timsu/astrid/sync/SynchronizationService.java +++ b/src/com/timsu/astrid/sync/SynchronizationService.java @@ -105,10 +105,10 @@ public abstract class SynchronizationService { /** Create a task on the remote server * - * @return listName list name to create it on. null -> inbox + * @return primaryTag primary tag of this task. null if no tags exist. * @return remote id */ - String createTask(String listName) throws IOException; + String createTask(String primaryTag) throws IOException; /** Fetch remote task. Used to re-read merged tasks * @@ -153,7 +153,7 @@ public abstract class SynchronizationService { TagController tagController = Synchronizer.getTagController(activity); AlertController alertController = Synchronizer.getAlertController(activity); - // get data out of the database + // 1. get data out of the database HashSet mappings = syncController.getSyncMapping(getId()); HashSet activeTasks = taskController. getActiveTaskIdentifiers(); @@ -162,7 +162,7 @@ public abstract class SynchronizationService { HashMap tags = tagController.getAllTagsAsMap(activity); - // build local maps / lists + // 2. build helper data structures HashMap remoteIdToSyncMapping = new HashMap(); HashMap localIdToSyncMapping = @@ -177,34 +177,47 @@ public abstract class SynchronizationService { mappedTasks.add(mapping.getTask()); } - // build remote map + // 3. build map of remote tasks HashMap remoteChangeMap = new HashMap(); + HashMap newRemoteTasks = new HashMap(); for(TaskProxy remoteTask : remoteTasks) { if(remoteIdToSyncMapping.containsKey(remoteTask.getRemoteId())) { SyncMapping mapping = remoteIdToSyncMapping.get(remoteTask.getRemoteId()); remoteChangeMap.put(mapping.getTask(), remoteTask); + } else if(remoteTask.name != null){ + newRemoteTasks.put(remoteTask.name, remoteTask); } } - // grab tasks without a sync mapping and create them remotely + // 4. CREATE: grab tasks without a sync mapping and create them remotely log.append(">> on remote server:\n"); - syncHandler.post(new Runnable() { - @Override - public void run() { - progressDialog.setMessage("Sending locally created tasks"); - progressDialog.setProgress(0); - } - }); + syncHandler.post(new ProgressLabelUpdater("Sending locally created tasks")); HashSet newlyCreatedTasks = new HashSet(activeTasks); newlyCreatedTasks.removeAll(mappedTasks); for(TaskIdentifier taskId : newlyCreatedTasks) { + TaskModelForSync task = taskController.fetchTaskForSync(taskId); + + /* If there exists an incoming remote task with the same name and + * no mapping, we don't want to create this on the remote server. + * Instead, we create a mapping and do an update. */ + if(newRemoteTasks.containsKey(task.getName())) { + TaskProxy remoteTask = newRemoteTasks.get(task.getName()); + SyncMapping mapping = new SyncMapping(taskId, getId(), + remoteTask.getRemoteId()); + syncController.saveSyncMapping(mapping); + localChanges.add(mapping); + remoteChangeMap.put(taskId, remoteTask); + localIdToSyncMapping.put(taskId, mapping); + continue; + } + + // grab the primary tag for this task LinkedList taskTags = tagController.getTaskTags(activity, taskId); String listName = null; if(taskTags.size() > 0) { listName = tags.get(taskTags.get(0)).getName(); - // strip the underline if(listName.startsWith(TagModelForView.HIDDEN_FROM_MAIN_LIST_PREFIX)) listName = listName.substring(1); } @@ -212,27 +225,19 @@ public abstract class SynchronizationService { SyncMapping mapping = new SyncMapping(taskId, getId(), remoteId); syncController.saveSyncMapping(mapping); - TaskModelForSync task = taskController.fetchTaskForSync( - mapping.getTask()); TaskProxy localTask = new TaskProxy(getId(), remoteId, false); localTask.readFromTaskModel(task); helper.pushTask(localTask, mapping); // update stats - log.append("add " + task.getName() + "\n"); + log.append("added " + task.getName() + "\n"); stats.remoteCreatedTasks++; syncHandler.post(new ProgressUpdater(stats.remoteCreatedTasks, newlyCreatedTasks.size())); } - // find deleted tasks and remove them from the list - syncHandler.post(new Runnable() { - @Override - public void run() { - progressDialog.setMessage("Sending locally deleted tasks"); - progressDialog.setProgress(0); - } - }); + // 5. DELETE: find deleted tasks and remove them from the list + syncHandler.post(new ProgressLabelUpdater("Sending locally deleted tasks")); HashSet deletedTasks = new HashSet( mappedTasks); deletedTasks.removeAll(allTasks); @@ -247,20 +252,14 @@ public abstract class SynchronizationService { remoteChangeMap.remove(taskId); // update stats - log.append("del #" + taskId.getId() + "\n"); + log.append("deleted id #" + taskId.getId() + "\n"); stats.remoteDeletedTasks++; syncHandler.post(new ProgressUpdater(stats.remoteDeletedTasks, deletedTasks.size())); } - // for each updated local task - syncHandler.post(new Runnable() { - @Override - public void run() { - progressDialog.setMessage("Sending locally edited tasks"); - progressDialog.setProgress(0); - } - }); + // 6. UPDATE: for each updated local task + syncHandler.post(new ProgressLabelUpdater("Sending locally edited tasks")); for(SyncMapping mapping : localChanges) { TaskProxy localTask = new TaskProxy(getId(), mapping.getRemoteId(), false); @@ -274,9 +273,9 @@ public abstract class SynchronizationService { remoteConflict = remoteChangeMap.get(mapping.getTask()); localTask.mergeWithOther(remoteConflict); stats.mergedTasks++; - log.append("mrg " + task.getName() + "\n"); + log.append("merged " + task.getName() + "\n"); } else { - log.append("upd " + task.getName() + "\n"); + log.append("updated " + task.getName() + "\n"); } try { @@ -297,15 +296,9 @@ public abstract class SynchronizationService { localChanges.size())); } - // load remote information + // 7. REMOTE SYNC load remote information log.append(">> on astrid:\n"); - syncHandler.post(new Runnable() { - @Override - public void run() { - progressDialog.setMessage("Updating local tasks"); - progressDialog.setProgress(0); - } - }); + syncHandler.post(new ProgressLabelUpdater("Updating local tasks")); for(TaskProxy remoteTask : remoteTasks) { SyncMapping mapping = null; TaskModelForSync task = null; @@ -321,22 +314,22 @@ public abstract class SynchronizationService { if(task == null) { task = new TaskModelForSync(); setupTaskDefaults(activity, task); - log.append("add " + remoteTask.name + "\n"); + log.append("added " + remoteTask.name + "\n"); } else { mapping = localIdToSyncMapping.get(task.getTaskIdentifier()); - log.append("mov " + remoteTask.name + "\n"); + log.append("merged " + remoteTask.name + "\n"); } } else { mapping = remoteIdToSyncMapping.get(remoteTask.getRemoteId()); if(remoteTask.isDeleted()) { taskController.deleteTask(mapping.getTask()); syncController.deleteSyncMapping(mapping); - log.append("del " + remoteTask.name + "\n"); + log.append("deleted " + remoteTask.name + "\n"); stats.localDeletedTasks++; continue; } - log.append("upd " + remoteTask.name + "\n"); + log.append("updated " + remoteTask.name + "\n"); task = taskController.fetchTaskForSync( mapping.getTask()); } @@ -424,7 +417,8 @@ public abstract class SynchronizationService { if(localCreatedTasks + localUpdatedTasks + localDeletedTasks + mergedTasks + remoteCreatedTasks + remoteDeletedTasks + remoteUpdatedTasks == 0) { - DialogUtilities.okDialog(activity, "Sync: Up to date!", finishListener); + if(!Synchronizer.isAutoSync()) + DialogUtilities.okDialog(activity, "Sync: Up to date!", finishListener); return; } @@ -467,4 +461,15 @@ public abstract class SynchronizationService { progressDialog.setProgress(100*step/outOf); } } + + protected class ProgressLabelUpdater implements Runnable { + String label; + public ProgressLabelUpdater(String label) { + this.label = label; + } + public void run() { + progressDialog.setMessage(label); + progressDialog.setProgress(0); + } + } } diff --git a/src/com/timsu/astrid/sync/Synchronizer.java b/src/com/timsu/astrid/sync/Synchronizer.java index c14d23b54..25ff9d970 100644 --- a/src/com/timsu/astrid/sync/Synchronizer.java +++ b/src/com/timsu/astrid/sync/Synchronizer.java @@ -1,10 +1,13 @@ package com.timsu.astrid.sync; +import java.lang.reflect.InvocationTargetException; import java.util.Date; import android.app.Activity; import android.content.Context; +import android.util.Log; +import com.timsu.astrid.data.AbstractController; import com.timsu.astrid.data.alerts.AlertController; import com.timsu.astrid.data.sync.SyncDataController; import com.timsu.astrid.data.tag.TagController; @@ -22,9 +25,11 @@ public class Synchronizer { } /** Synchronize all activated sync services */ - public static void synchronize(Activity activity, SynchronizerListener listener) { + public static void synchronize(Activity activity, boolean isAutoSync, + SynchronizerListener listener) { currentStep = ServiceWrapper._FIRST_SERVICE.ordinal(); servicesSynced = 0; + autoSync = isAutoSync; callback = listener; continueSynchronization(activity); } @@ -88,6 +93,9 @@ public class Synchronizer { /** On finished callback */ private static SynchronizerListener callback; + /** If this synchronization was automatically initiated */ + private static boolean autoSync; + /** Called to do the next step of synchronization. Run me on the UI thread! */ static void continueSynchronization(Activity activity) { @@ -119,65 +127,95 @@ public class Synchronizer { callback.onSynchronizerFinished(servicesSynced); } - // --- package helpers + /** Was this sync automatically initiated? */ + static boolean isAutoSync() { + return autoSync; + } - static SyncDataController getSyncController(Activity activity) { - if(syncController == null) { - syncController = new SyncDataController(activity); - syncController.open(); + // --- controller stuff + + private static class ControllerWrapper { + TYPE controller; + Class typeClass; + boolean override; + + public ControllerWrapper(Class cls) { + override = false; + controller = null; + typeClass = cls; + } + + public TYPE get(Activity activity) { + if(controller == null) { + try { + controller = typeClass.getConstructors()[0].newInstance( + activity); + } catch (IllegalArgumentException e) { + Log.e(getClass().getSimpleName(), e.toString()); + } catch (SecurityException e) { + Log.e(getClass().getSimpleName(), e.toString()); + } catch (InstantiationException e) { + Log.e(getClass().getSimpleName(), e.toString()); + } catch (IllegalAccessException e) { + Log.e(getClass().getSimpleName(), e.toString()); + } catch (InvocationTargetException e) { + Log.e(getClass().getSimpleName(), e.toString()); + } + controller.open(); + } + return controller; + } + + public void set(TYPE newController) { + override = newController != null; + controller = newController; + } + + public void close() { + if(controller != null && !override) { + controller.close(); + controller = null; + } } - return syncController; + } + + private static ControllerWrapper syncController = + new ControllerWrapper(SyncDataController.class); + private static ControllerWrapper taskController = + new ControllerWrapper(TaskController.class); + private static ControllerWrapper tagController = + new ControllerWrapper(TagController.class); + private static ControllerWrapper alertController = + new ControllerWrapper(AlertController.class); + + static SyncDataController getSyncController(Activity activity) { + return syncController.get(activity); } static TaskController getTaskController(Activity activity) { - if(taskController == null) { - taskController = new TaskController(activity); - taskController.open(); - } - return taskController; + return taskController.get(activity); } static TagController getTagController(Activity activity) { - if(tagController == null) { - tagController = new TagController(activity); - tagController.open(); - } - return tagController; + return tagController.get(activity); } static AlertController getAlertController(Activity activity) { - if(alertController == null) { - alertController = new AlertController(activity); - alertController.open(); - } - return alertController; + return alertController.get(activity); } - // --- controller stuff - private static SyncDataController syncController = null; - private static TaskController taskController = null; - private static TagController tagController = null; - private static AlertController alertController = null; - - private static void closeControllers() { - if(syncController != null) { - syncController.close(); - syncController = null; - } - - if(taskController != null) { - taskController.close(); - taskController = null; - } + public static void setTaskController(TaskController taskController) { + Synchronizer.taskController.set(taskController); + } - if(tagController != null) { - tagController.close(); - tagController = null; - } + public static void setTagController(TagController tagController) { + Synchronizer.tagController.set(tagController); + } - if(alertController != null) { - alertController.close(); - alertController = null; - } + private static void closeControllers() { + syncController.close(); + taskController.close(); + tagController.close(); + alertController.close(); } } diff --git a/src/com/timsu/astrid/utilities/Constants.java b/src/com/timsu/astrid/utilities/Constants.java index c2cff024d..0c5d2283f 100644 --- a/src/com/timsu/astrid/utilities/Constants.java +++ b/src/com/timsu/astrid/utilities/Constants.java @@ -14,6 +14,6 @@ public class Constants { /** Return to the task list view */ public static final int RESULT_GO_HOME = Activity.RESULT_FIRST_USER; - /** Callback from synchronization */ + /** Callback to force synchronization */ public static final int RESULT_SYNCHRONIZE = Activity.RESULT_FIRST_USER + 1; }