From 97c25896328fdc9822073f2aea4468ca7100a1c9 Mon Sep 17 00:00:00 2001 From: Eric Kok Date: Mon, 29 Jul 2013 12:07:42 +0200 Subject: [PATCH] Added background services for app updates, RSS feeds and server checks for new and finished torrents. --- core/AndroidManifest.xml | 4 +- .../ic_stat_notification.png | Bin 0 -> 942 bytes .../drawable-hdpi/ic_stat_notification.png | Bin 0 -> 1057 bytes .../ic_stat_notification.png | Bin 0 -> 578 bytes .../drawable-mdpi/ic_stat_notification.png | Bin 0 -> 632 bytes .../ic_stat_notification.png | Bin 0 -> 1398 bytes .../drawable-xhdpi/ic_stat_notification.png | Bin 0 -> 1475 bytes .../ic_stat_notification.png | Bin 0 -> 2285 bytes .../drawable-xxhdpi/ic_stat_notification.png | Bin 0 -> 2483 bytes core/res/values/changelog.xml | 4 +- core/res/values/strings.xml | 30 ++- .../app/settings/ApplicationSettings.java | 28 +++ .../app/settings/NotificationSettings.java | 9 + .../core/app/settings/ServerSetting.java | 17 ++ .../transdroid/core/gui/TorrentsActivity.java | 12 +- core/src/org/transdroid/core/gui/log/Log.java | 2 +- .../NotificationSettingsActivity.java | 43 +++- .../gui/settings/SystemSettingsActivity.java | 16 +- .../core/service/AlarmReceiver.java | 34 ++++ .../core/service/AppUpdateService.java | 136 +++++++++++++ .../transdroid/core/service/BootReceiver.java | 88 +++++++++ .../core/service/ConnectivityHelper.java | 29 +++ .../core/service/RssCheckerService.java | 107 ++++++++++ .../core/service/ServerCheckerService.java | 183 ++++++++++++++++++ full/AndroidManifest.xml | 54 ++++-- lite/AndroidManifest.xml | 23 ++- 26 files changed, 780 insertions(+), 39 deletions(-) create mode 100644 core/res/drawable-hdpi-v11/ic_stat_notification.png create mode 100644 core/res/drawable-hdpi/ic_stat_notification.png create mode 100644 core/res/drawable-mdpi-v11/ic_stat_notification.png create mode 100644 core/res/drawable-mdpi/ic_stat_notification.png create mode 100644 core/res/drawable-xhdpi-v11/ic_stat_notification.png create mode 100644 core/res/drawable-xhdpi/ic_stat_notification.png create mode 100644 core/res/drawable-xxhdpi-v11/ic_stat_notification.png create mode 100644 core/res/drawable-xxhdpi/ic_stat_notification.png create mode 100644 core/src/org/transdroid/core/service/AlarmReceiver.java create mode 100644 core/src/org/transdroid/core/service/AppUpdateService.java create mode 100644 core/src/org/transdroid/core/service/BootReceiver.java create mode 100644 core/src/org/transdroid/core/service/ConnectivityHelper.java create mode 100644 core/src/org/transdroid/core/service/RssCheckerService.java create mode 100644 core/src/org/transdroid/core/service/ServerCheckerService.java diff --git a/core/AndroidManifest.xml b/core/AndroidManifest.xml index 6f69f60e..34f5d8e2 100644 --- a/core/AndroidManifest.xml +++ b/core/AndroidManifest.xml @@ -1,8 +1,8 @@ + android:versionCode="3" + android:versionName="2.0-alpha3" > +N5^klS3 zP&69Ni$o%o+%p;2ZrIB2X8U;}gN<7dovomw3orSg5!F)2enWPzbyK5{{-DFj&dIkQy`o6zimH3;!FjVqypQtc1cbPo?8jdz{<=}oO!<%7z&AoP>VWV=gTmz$3n4s_|?dC$IwglTpOov2fF%>63qT1?`nAM0n zuMXXUwBz1@SpjOaRG-6G1H~_}83sx%ewX&P0t9J?Zy{(1&(=UmfP-z+X-n8>Am|`A z9YXvaqW(C7UdISxy0pA+LeO-*r9KCC#UW@fwn{Vo0tTRIn}et-_mmY-p}Fkg)A$ID zHX#0hsUYk(VXwqnae@+j*#A;1)vcP@M*05NxSV*jmTii6!9~lJhsxu{sYgq zz$Lv$|0BdGl$`?;AN6ZtIgBw7W1GCBpoF|5q)j=3ik*TKp@py~AVT^U(yGwNCm4u& zh0P2rnwn;>OA#uAi!l+BB~c1{hfdOH6?WN}--1a2qTW$A4f|T`uTq}pW9$14nXIyp zI2}PJ4Rq3*@@4Q!l65@K$F3T?ft0mxWd(@$U1cWw&@nn%XGK)WQSZlWM`!OT=VDWX z7`+k|pvB^MdGAA32PQl9Uu;Y0P>Vp*tWQ=gYvrZSFq68+mYD7Q-Wec9Re?LTX)aS@ zJg3g+ypGO0Q9i6_@cSXZhf>z!kjSvhKbKh}jwjpeo9Qk*fv?a9#=%t3Cveaep6%_{ z>GnOO#F<`SjE+dP|2MTmOsY;D+6Z;($cgLJk>Yciwu+{>&SlQ!fw*LxT=f-NpYX}m zCCEP8YfJb7?R&O&iL?Lx7|iph(Dwg+`0Z}4|A(|QmoX)6`~Of_9%ya-Kla3HG_1Z0 Qr~m)}07*qoM6N<$f^m_|F#rGn literal 0 HcmV?d00001 diff --git a/core/res/drawable-hdpi/ic_stat_notification.png b/core/res/drawable-hdpi/ic_stat_notification.png new file mode 100644 index 0000000000000000000000000000000000000000..32ba0b7324914e82c2e5345641fa8058b7339a4e GIT binary patch literal 1057 zcmV++1m63JP)-L+AE4#P5t|`8GVVJoyXU_M1=bV|*YIgY3?y!H& zE^YbnL1>kTvYF#eZ6zp!HAc3(S2YUWBq5-A> zKr$MQ(#p!pcu-JK7j}mV3JPkJKt2lWc6$O2_P{t7c6rOo%d$h!aG=@?FaX4wgK;-@ zb0(8%+ymqT(B9sDiG?9pNo%YFCedRM92_hGq=#54N{0Rcz_}F`F^h|fw^`ik`EV1q zC#aY*o6S}MvhK)cvzb|V4D(I1Rx?D22v?!SVriEFtX8WoIy%}$Sdxi(;%ySwVAi^JS{yImp3^g#P0HE6UEOC7P|DX-8P!>%C60UCdo8DJglWZEI`G zVDT=oPZFi*t$_Igl9ocBqoxceJrMgu-=tUC^f7 z`7G9tDZ(^iFc_+}R8J;O>R=h~?S%0MS~2s_0Nbyo0DwQl`v&UUusf;PKS4!R%OOHy z4-V244Ca}SL)}p}_S0e%APYFXNgKoN8zg)R1n@zW=V)76S~5^j0*mv?B!`EG-zzFA zdcYTtlK}t@uO)MdKD)fUysx#jwZZ9hmMfEtvUezGmdmuEp&<>11A>LOA;}jiGK-6g z2OAq3PXHin;^=cLDk^#fIf(T_(5L`#VUv!{pnTAxqM{zbA{nEz%5o&pPQY7P??AE^ z>4`8KBe_NVn+TynrG!7SZ+Lcg_RjiM0YekIL;*n`rGrKP2BJZnDJGAd3H(=d@l3{3tW89(!O=DSh3Ky`^bO8!*ueU#LoWD=k} zHl3!|)5}k$mvf1KiT^SDIA9(Nb2%*D--Zthhp-VRFW7LOwm|cogYS&Ph<7dZexxc literal 0 HcmV?d00001 diff --git a/core/res/drawable-mdpi-v11/ic_stat_notification.png b/core/res/drawable-mdpi-v11/ic_stat_notification.png new file mode 100644 index 0000000000000000000000000000000000000000..a598474c25b9e9fbc9a769790946fe9274baf743 GIT binary patch literal 578 zcmV-I0=@l-P)5W)qh1H}tS4Q_B#?{aCY(TnM;4dq zFM=urWM&a1N`SxrhDSOK$zR2|L^9g}xIyHnbIe-+m6i-To8U)`fpxcT3=0sl)8!OM zrehz9LDnFiB5QC20ofc9;8j3&0@WvIhQ@dzJI4Itdmc7K#5sd$jBPH#FR;m?yOk@9 z*)AP31aEiFpp?c}XwjUHX^+B~L7)q}ZT3P^+l$k7CXwEr!#baED^s9v|0 zR<=~&5cLGe&s4%QEP-5tdWqVmUI*=x2y%Gjq-VkhkSit2CAjgR`FB;Q6v_SL-(_b{ z)wk=~nM*|Cd2lK@SaNSglvtijIb`xgx;>c>*jM3wPo^VX{yh3FTQ6?Y3)F4H8B$t4 Q=Kufz07*qoM6N<$f(8r%)c^nh literal 0 HcmV?d00001 diff --git a/core/res/drawable-mdpi/ic_stat_notification.png b/core/res/drawable-mdpi/ic_stat_notification.png new file mode 100644 index 0000000000000000000000000000000000000000..ddbf26ce8eb323caef3b435ca3710a0525e2d75a GIT binary patch literal 632 zcmV-;0*C#HP)X1^@s6IQ*`u0006%Nkl>Rhk`fpR;)Mg5~x%`t59r}_;E=T(tuDAVlWVb0fY2Vw5`&L zLjD17ZRu6;AoQRJ29cb!`P?M_W;z?jC@S>Uf#IEfKc4q_pPgM{?Q)4-)?0fKAeYPS zV-29qd3ITAG=yEP)oS14@p#SD?H3J-#o}p#$JzJV?e;+-gd>x?-L4l11io>6!2S@6 z#nya~9stGXD2fue+-NjZoI(o_Wo_HGg2AACUIeTJ$ZMIS0Ay#+^KJlevDIqziu_{J zQfxVn^Md_EG7)lSoR{b_|F2@7kpzLwOtaZs7WsUB5XUA09Wy$8Ci#fjxzp*C8Dq!? z357zR@QsQ>p>VriuRkQPUkme|^D~Mc_1)50d{>E$sM&?s4u`|DlEDO2mI%1ABWneE zHoggy0lru06sGH0?Qg?s97Y3ksww;{O0`TTGY0Z?_Lcri zBcJ>42PMy8^)!)4toN=mAW^zS0h#*K4fNkXISVi>C6mdL8rkHOPNz@tJVNpuAcI&( zgL&Am#OByrsZ?siulwzfBsJk;9YEM|L@Hw~to)qcAnK35e$FPF|2^-4f8tO7X+u*8 S;W_630000E0*eNWL?<4TB7)eyuIqG~Ilgjf=5qG?F22~xCaR3a6r5F!nUSQ1eo))uj( zvHOGACH}cWTM=q2T3Xlhe3{|g)AOA(=bn4-=X;Z1=FI(O-g$pB@65b2b6aD2Tv|PD z|CnJVFfcGMfez_!wOYT~G32M3l3xJn>8U>*&Q1J~Kn;7hP0xEZVhw*`azeM>M0d<*t+fWUn4 zBZOwxH9VHDMSvdOmWu0W@K+U(f0*Pv@D^AMZV8J24n4kk$p232h-?t@H<6wM4hN5* z@BE-ol4HsLoJy!JfkRRJ7`PKW0G(8n_5t1yIMuhYhN|yQq74N@J9<_n!sxsp|;-=R(1C z^s#djm3ivg8%4_+5}1gd4}fl%B9BsNMs%h8Dx^=|3Ur~WIB79TPPWA+1VVlN```-e zowy6YEZY4mmdz7zw{-=QggaoFs4Q+SPw+iCrTa2vUgP_WkoSDiKGc+m5y3355CZPf zZTXh~y?845x!{0ALv*)ez#hQiuNtwuT6=UI@LXY(dsA}qnLn|8HGA7bUVFpEp;?r5~=HXwA0&+Y3O(Z9S`z-K?Q+w7YYRG zOyZ?9esrkUNBSg4bw6YMGd-+eyoVzr->MEMpTt90MqLReVZ`Tw#15pM8``8w#Gp*$ z-M}1mF9q7)iBi0b4sj#92SaAYhP~+GTX*szt4KVSo=zQi;kHo6TMiE{Dbjf(%P6c9 zO1PXEpTrwD4YI!Olqa!w=z6DtoTGD_T*Nu^uL!jC9dTumE>Vd-;XXnt?zA->dS!*jtAV(no z#N@{1LqrpaPfQo1NoojeP;C; zb|nUn&NFKPf!==TE+Euf|7wurq$6CY(Ht0I+mh7(16lG7PZnAELjV8(07*qoM6N<$ Eg8H|V=>Px# literal 0 HcmV?d00001 diff --git a/core/res/drawable-xhdpi/ic_stat_notification.png b/core/res/drawable-xhdpi/ic_stat_notification.png new file mode 100644 index 0000000000000000000000000000000000000000..abf1f2082b9e61e9a7336db10217ccb38b2fcddf GIT binary patch literal 1475 zcmV;!1w8tRP)H0OrYabqQzY6o(Wcu2QbesZwoM;ueP9dAu&)8az_71nF=@09 zO;agIN(~s(G$vY8?JA(yqSF|XQQFd%28Nyf?v$Y{^L_J;KJYSM@@4MKJ@@?o=YP&U z_l{b%!H;@_@l|dDZUx*5xD{|&fws1`@Y&f}Yierh2;-Rf`FT}xa&m7|Q&X^)mzOp% zF>%0^=B9`MKs!7CNjV)I z9d9xI1u|x5WMm06HZ}&PrKODl8jZsqNYoR(h&sgA*LMuyKmnlDYR9IgrUEq@%`H^u z#Wo$=BMAu!=j=YDh{zHE_$LDoCmz*Z_$~Fx(4~ zJ&Fm`*494eVeEqqKVz~4FTRrNlCe5BS3{7 z<%cswu5q>6T~$h`z&j7@F?L$_ate^&1c6+cE%q5;FEPK}3Fuws2eFHmv8&9^&d#&d z06kofEbogB#~Ui4>nN-vNY8gT4VKd$;1L3Og->xNY;j~u3Wi0;#NVfCkzA z4u_K>7#nM9YJO8fAS)|tS9NuDE$cEI5fCavV2Hrqwxyu1uI>q1>@_B~I+Bf@@gh#I z3IaqIEY}HfHv-vR_4W0U`0STR!+pS?&&bFaRnz&z$yLhUv?<`oIs4bhf|n;ICY}Yr zPp$?aJ4rwJ0iQSUnIJ{h-`{@-<%`ucpu;dNWx={GWtR`|rx=@9@UD`*N4;FYX92d~ zOW8wo0K4KK%-HS2HX<>i^z z9>=GNMz+cYxupYTxo3#q#9dLm&K~mq3AJ?GZnx?}g1lX%!3w)jSn8DA{aiQ=EvPh1 z{+nKiXGKb`GY)sbxzmd(zymosIlI>?wC+;MwhW@$hcNsZ0#EY!uvas2Ast1{ z2rj33dW8I?w4$P73&7rDOQFOi#`~P*)k$Z(OtI~+s;bhF;%WNC_)2@(KPC_m&E+Z! zMCE6>NTde)P2~Lr2#7<>;Ui0+xVZT7($dmDINPJx9bckGB~prxDK0MJR=aJiLZi`m zkbDo(HGg-enxcQlE1f_<52&@MsOS#k17ePP=`SlQ+lky-#-e}~KQ0qc>{+3qn}AyZ dw*nikz`y+P){@ppc_;bhzBljA%$eUEIdkUR*D+ID>N3#L=BGXbZGsx~kUk-3 z`t<3`qSw@pj*jWEexz^fchVoONLSBgNdJ6B_xQdd=w2E+nFe2!x5n@F0*> zmN`C-4!Mk7|@DgwsZ5}nq-@H$vVv(3ZU~rW9G@plo zq2Oj<50&IWkn~p>Bo)$w!m#-ZCBI0NByBl9e+Xs|X5q@n(zfZ(kl$XoM93^L z1F8GdsP3h#gEYy@B}EamBU)Sot^?Pi*i;DGgwGC}86lfe=kwILG?g6;4g!ag=G>J_ ziXzBv|76hQ8?HBjAHi9lf1P!5F=a>E@>p@U% zol+A-H+J=Kb@?z^zDZk|@9*^|cF<)N^m|{O(9d=b1V1OMX|uX!A!uC+e*wG(oMVKO z@vFd-=FwCFPJ$^`MR+L!gC=d4`>Nfe zjU+i5yLunKtY;fl?LiQ;IvKA3u3kE^T4gd$OPZ6n&bg1k5fG)Pvxn3o$aD!Bowmjn zy689MH;SRkaQY_jL)cnPk9gUEYST~Btbgz zM*$a(tTG;QPOq1_)*R%)-gdxqD7}SQyRx3e1Atz>G6-^5Oa@s`HS}?Nh>)Dq^*WgB zK~QkCPawnAAa)gGc|t0$SMLMLUAzo}D0>=ZOU`2;?@H@D`6xJ=az9HNTuG4M_Yd9H z4QU>^ii)~c!IBA5=Z8X%&!ev!S?!~n;?=Cy5Yq#vYaltWOIxwa<>pl6+*2w+p?v3} zThb~S-S6-piN3u~VQNm&JoVMcbQkcNR_vl4=KV2)2=YPd6Q3ikqTK&B*bQC1_L6!8 zxwL!Gw|9n0AEQF&u`Vfdh=uZP$D0`+=56E~i@q-TjR^_{qVv!gFdzdtW^rY4_n)kO zbMyI7P?S?&^Fg=ouR%Y*Zx7basPD(*yBs+G^e`AJC+R)8=@j6ZcOEe~;SF#lNE}po z%Bz>x{gg(dC%L=iti&jhGa>1Yq?~F5oyw2#iA9z=FQ<~R^hSNx9!cl1cI(yemcs{n zF1QEmoJH9vk0&2z)Fj9o3mRgRsKjUv%d_hnD3t^od@ohLx@9d(w2Wa8OAg{Nk@&! zv&?IBtwg0>yM#u#WzlTNv++%eGjQsZ#Gs>WCudJgvpDn8FY{EG*HE+}yV3yXg>IqT zt@z@y>eeHx+(5>|DBFEhLjE{DxJAk;%^8Z}kiV#{hOs##DxFZ&u>tA2K8zkqBUMvNM(KZ*9C;jh;$T?hf}hThpk-po<%}B46$YYc$cQYq>|4XpcBV$Z=(~4i7i8NIG{% zwHk6d%q&vFOWUfKg#Df*)oY*=yXWd%`?}~M>fX2!beR(wC7Z)6Qlbc#t53=Ew277T zk@bY(=A5*Cl}0{5+P>s79v~*iW&IqWd!nINv<`XMU9JT??cEL>2QH~Wl0!xFUQ-|u zC;5H2-#CDn9M9_XMZ0npA?cm=VKpoI*hi9#p4XH&(6fh7=b|L&gVGD&yBd2{={dSJ zE&#h1!Aw*}x?>-dE%V^exWNm-oe*FYSlm1GVme=)6LAsG0`B9PkeVB(ID= z7BFDS7=kIlSfn1O9l2PEGM7e>bwHBylIPxK{xwA>h*UBYX8AfDHB2;6k6&W)CcV;yvjB~4JiE&AXH*@$|$)W00000NkvXX Hu0mjfi2hS% literal 0 HcmV?d00001 diff --git a/core/res/drawable-xxhdpi/ic_stat_notification.png b/core/res/drawable-xxhdpi/ic_stat_notification.png new file mode 100644 index 0000000000000000000000000000000000000000..e5afa3014352482055f42c5a61fa13a149ce9fe7 GIT binary patch literal 2483 zcmV;k2~75hP)_TnKo@&x!G*)Syff_!L@7Go|-giQnBz=S67>O z?;jf*d)*yp+|B?F^v1Xm*RNk6KXKy3OGGxhva-?~7#MgKr+RY^Dlacj=js;}6qJIY zTcH2y>eZ{Q8#HJTL#9?PS+b;2{rdHD8QaM7mv_{Gyu7?Sl+E zL&^|Zx^(G41aBdl7ZALbh_}V)zA8$LXmDVszrX(p=thyXrrf`b16z#vv|yW_DL|m2KDIFQ{lT2c~Mu!8v5)u+LT}Fpf2Pn*S2q*y#g{1ouMi&S)bU4)3 z6-Y+lR3qevqpvyH7+I4eP8|S~?-7|;i4MY;WGVw|%V=AtfIA!d1tU>TD6^)b677X+dAyvz`S|$!f@ayLPPFE!MadWWzstwV$H%`f{(r09QvtSqAvtm2Ba}#4vr)mZFqbL?03T3$p4Aqni&*x zKDBU&+tVPUhy^uY8bHkf=?Gb<)^a;i7AJv@WeU^oT}X4S(aTgW3!d1*xt`YJb?Jny zHNceF7^A<&iOw!Y{uJ~NyU3%HpXa89i@XQP#&eS=PcF4WNEZP1I}q^#JPTEcJbdEt z2I$wQ(A#H{sK$};7_OZZ`K1qwpcanmHftGJuwX&6;Nai^WMJTxD_5RJSdap2N}=n@ zG}i^5jSBP*@M6UsMvL+ra`wOp*h=0S!V!L*s!$qGZ8H2#U6o{uJSb_C>nXEF-6HH_p7C z1SEEe9BRP~e7hOR!T58m0Sih?OY^Y%iqya>Fr^a3jx1nGDFi<25E3T+y3~y-pS^G# z(D2OhJwdocFq9Tg;Y0rs=+jItxc`(5;I^vzgbwH)L00%J?X)PNkk?_nR)nr#QJO2DRD`jK6xF9F(KbazCg0bI zJc|=iME)Cjzj^cKy2ZuCqj;BrzLpkR!-+$8QuImvp=*@rvVKyQm6b&h$uI;zgwUrM zd_gg-nsS`DiEyb0*>T=QuOc5S(kMP4^E@+EYwD&yuSQGD0BlsnYjoHp+wc3)bs9at zhlGR_SU)N4C1qu0<)9?NB@GZubXel$Dm!cPr6${wCbH9lw6|gP4v_s%nW|kqkhz%R zci3ofsC){m4aOg4UJr2voA$611Eogfy&Nj*i!*&>uS{BauwP{Byn_Smhi0=(|KGM$ zG@HS`{5D~@vDJfURTI5Zt_|VaOJw|ADQr(NXQ!u%=N{N{8Cx22HLf-O%BuR zAXd)L$j09|v5ISZuKm4HuoUR$$T^0ZH@G%swt1Q9(z`$M;XohymTqJrZ_b=K=QA=g z-a&DyB}#vB4dyiLVlDL2RmzVR&E=WdC5=|&?OC&CwO}EPXOrza9QeYk8L$@yq@v{` zhW9_<$Y_k1%l#mslW3A1+4Iy48I2rqan?qURYytFRg)|KN4phN03n%eOKPYV9)yc9%bU@Zr=Q2R4zpE&@XtlV0hr{MqAn z(Mn58YnPgudR!k%PfypjTvAd}ta!GkaVP4wJZ?kCQ^nrm_c`FLNV+S>mjQPp=Pf>8 x2D}wXcjfpp;BMr+#pla_w<77ToO?b4{{pc$rG3(HcRv6C002ovPDHLkV1hRAvCIGf literal 0 HcmV?d00001 diff --git a/core/res/values/changelog.xml b/core/res/values/changelog.xml index 16aa4560..7cd22624 100644 --- a/core/res/values/changelog.xml +++ b/core/res/values/changelog.xml @@ -4,6 +4,8 @@ Transdroid 2.0.0-alpha3\n - Added torrent search\n - Setting of max. up/down speeds\n +- Update trackers and set labels (where supported)\n +- Background RSS and server checker services\n \n Transdroid 2.0.0-alpha2\n - Fixed Transmission adapter folder setting\n @@ -16,4 +18,4 @@ Transdroid 2.0.0-alpha1\n \n Older changes: http://www.transdroid.org/about/changelog/ - \ No newline at end of file + diff --git a/core/res/values/strings.xml b/core/res/values/strings.xml index bc7a0c4d..4231864f 100644 --- a/core/res/values/strings.xml +++ b/core/res/values/strings.xml @@ -100,15 +100,28 @@ KB/S Reset Update - PICK A LABEL - NEW LABEL - Remove label - E.g. movies or linux + + New torrent added + %1$s new torrents added + + + Torrent is finished + %1$s torrents are finished + + + %1$s added, %2$s finished torrent + %1$s added, %2$s finished torrents + + %1$s and others All labels Unlabeled New label Setting a label is not supported by your client + PICK A LABEL + NEW LABEL + Remove label + E.g. movies or linux %1$s added (refreshing) %1$s removed @@ -153,6 +166,11 @@ %1$d item selected %1$d items selected + + New RSS feed torrent available + %1$s new RSS feed torrents + + New torrents for %1$s Servers Add new server @@ -320,6 +338,10 @@ Please enter a positive number Please enter a valid label or pick from the list + New Transdroid version available + New Transdroid search module available + You can now update to %1$s + Transdroid \u00A9 Eric Kok, 2312 development Published under GNU General Public License v3 diff --git a/core/src/org/transdroid/core/app/settings/ApplicationSettings.java b/core/src/org/transdroid/core/app/settings/ApplicationSettings.java index 7f2c524e..cbaee05e 100644 --- a/core/src/org/transdroid/core/app/settings/ApplicationSettings.java +++ b/core/src/org/transdroid/core/app/settings/ApplicationSettings.java @@ -9,6 +9,8 @@ import org.androidannotations.annotations.Bean; import org.androidannotations.annotations.EBean; import org.androidannotations.annotations.EBean.Scope; import org.androidannotations.annotations.RootContext; +import org.json.JSONArray; +import org.json.JSONException; import org.transdroid.core.app.search.SearchHelper; import org.transdroid.core.app.search.SearchSite; import org.transdroid.core.gui.search.SearchSetting; @@ -460,4 +462,30 @@ public class ApplicationSettings { prefs.edit().putString("header_setsearchsite", site.getKey()).commit(); } + /** + * Returns the statistics of this server as it was last seen by the background server checker service. + * @param server The server for which to retrieved the statistics from the stored preferences + * @return A JSON array of JSON objects, each which represent a since torrent + */ + public JSONArray getServerLastStats(ServerSetting server) { + String lastStats = prefs.getString(server.getUniqueIdentifier(), null); + if (lastStats == null) + return null; + try { + return new JSONArray(lastStats); + } catch (JSONException e) { + return null; + } + } + + /** + * Stores the now-last seen statistics of the supplied server by the background server checker service to the + * internal stored preferences. + * @param server The server to which the statistics apply to + * @param lastStats A JSON array of JSON objects that each represent a single seen torrent + */ + public void setServerLastStats(ServerSetting server, JSONArray lastStats) { + prefs.edit().putString(server.getUniqueIdentifier(), lastStats.toString()).commit(); + } + } diff --git a/core/src/org/transdroid/core/app/settings/NotificationSettings.java b/core/src/org/transdroid/core/app/settings/NotificationSettings.java index 58bd4003..44368c62 100644 --- a/core/src/org/transdroid/core/app/settings/NotificationSettings.java +++ b/core/src/org/transdroid/core/app/settings/NotificationSettings.java @@ -71,6 +71,15 @@ public class NotificationSettings { return prefs.getBoolean("notifications_vibrate", false); } + /** + * Returns the default vibrate pattern to use if the user enabled notification vibrations; check + * {@link #shouldVibrate()}, + * @return A unique pattern for vibrations in Transdroid + */ + public long[] getDefaultVibratePattern() { + return new long[]{100, 100, 200, 300, 400, 700}; // Unique pattern? + } + private int getRawLedColour() { return prefs.getInt("notifications_ledcolour", -1); } diff --git a/core/src/org/transdroid/core/app/settings/ServerSetting.java b/core/src/org/transdroid/core/app/settings/ServerSetting.java index 2b8690f0..fdd29b81 100644 --- a/core/src/org/transdroid/core/app/settings/ServerSetting.java +++ b/core/src/org/transdroid/core/app/settings/ServerSetting.java @@ -189,6 +189,10 @@ public class ServerSetting implements SimpleListItem { return this.key; } + /** + * Returns a string that the user can use to identify the server by internal settings (rather than the name). + * @return A human-readable identifier in the form [https://]username@address:port/folder + */ public String getHumanReadableIdentifier() { if (isAutoGenerated) { // Hide the 'implementation details'; just give the username and server @@ -201,6 +205,19 @@ public class ServerSetting implements SimpleListItem { + (Daemon.supportsCustomFolder(getType()) && getFolder() != null ? getFolder() : ""); } + /** + * Returns a string that acts as a unique identifier for this server, non-depending on the internal storage + * order/index. THis may be used to store additional details about this server elsewhere. It may change if the user + * changes server settings, but not with name or notification settings. + * @return A unique identifying string, based primarily on the configured address, port number, SSL settings and + * user name; returns null if the server is not yet fully identifiable (during configuration, for example) + */ + public String getUniqueIdentifier() { + if (getType() == null || getAddress() == null || getAddress().equals("")) + return null; + return getType().toString() + "|" + getHumanReadableIdentifier(); + } + @Override public boolean equals(Object o) { if (o instanceof ServerSetting) { diff --git a/core/src/org/transdroid/core/gui/TorrentsActivity.java b/core/src/org/transdroid/core/gui/TorrentsActivity.java index 755526db..b87eedca 100644 --- a/core/src/org/transdroid/core/gui/TorrentsActivity.java +++ b/core/src/org/transdroid/core/gui/TorrentsActivity.java @@ -38,6 +38,7 @@ import org.transdroid.core.gui.search.BarcodeHelper; import org.transdroid.core.gui.search.FilePickerHelper; import org.transdroid.core.gui.search.UrlEntryDialog; import org.transdroid.core.gui.settings.*; +import org.transdroid.core.service.BootReceiver; import org.transdroid.daemon.Daemon; import org.transdroid.daemon.IDaemonAdapter; import org.transdroid.daemon.Priority; @@ -185,12 +186,17 @@ public class TorrentsActivity extends SherlockFragmentActivity implements OnNavi // Log messages from the server daemons using our singleton logger DLog.setLogger(Log_.getInstance_(this)); - // Connect to the last used server + // Connect to the last used server or a server that was supplied in the starting intent ServerSetting lastUsed = applicationSettings.getLastUsedServer(); if (lastUsed == null) { // No server settings yet; return; } + if (getIntent().getExtras() == null && getIntent().hasExtra("org.transdroid.START_SERVER")) { + lastUsed = applicationSettings.getServerSetting(getIntent().getExtras().getInt( + "org.transdroid.START_SERVER")); + } + // Set this as selection in the action bar spinner; we can use the server setting key since we have stable ids getSupportActionBar().setSelectedNavigationItem(lastUsed.getOrder() + 1); skipNextOnNavigationItemSelectedCall = true; @@ -200,6 +206,10 @@ public class TorrentsActivity extends SherlockFragmentActivity implements OnNavi currentConnection = lastUsed.createServerAdapter(); handleStartIntent(); } + + // Start the alarms for the background services, if needed + BootReceiver.startBackgroundServices(getApplicationContext(), false); + BootReceiver.startAppUpdatesService(getApplicationContext()); } diff --git a/core/src/org/transdroid/core/gui/log/Log.java b/core/src/org/transdroid/core/gui/log/Log.java index bd9c7430..51c0c79f 100644 --- a/core/src/org/transdroid/core/gui/log/Log.java +++ b/core/src/org/transdroid/core/gui/log/Log.java @@ -37,7 +37,7 @@ public class Log implements ITLogger { } protected void log(String logName, int priority, String message) { - if (!navigationHelper.inDebugMode()) + if (navigationHelper.inDebugMode()) android.util.Log.println(priority, LOG_NAME, message); try { // Store this log message to the database diff --git a/core/src/org/transdroid/core/gui/settings/NotificationSettingsActivity.java b/core/src/org/transdroid/core/gui/settings/NotificationSettingsActivity.java index 06631546..73fc4775 100644 --- a/core/src/org/transdroid/core/gui/settings/NotificationSettingsActivity.java +++ b/core/src/org/transdroid/core/gui/settings/NotificationSettingsActivity.java @@ -4,31 +4,50 @@ import org.androidannotations.annotations.Bean; import org.androidannotations.annotations.EActivity; import org.androidannotations.annotations.OptionsItem; import org.transdroid.core.R; -import org.transdroid.core.app.settings.ApplicationSettings; +import org.transdroid.core.app.settings.NotificationSettings; +import org.transdroid.core.service.BootReceiver; import android.annotation.TargetApi; import android.content.Intent; +import android.content.SharedPreferences; +import android.content.SharedPreferences.OnSharedPreferenceChangeListener; import android.os.Build; import android.os.Bundle; import com.actionbarsherlock.app.SherlockPreferenceActivity; @EActivity -public class NotificationSettingsActivity extends SherlockPreferenceActivity { +public class NotificationSettingsActivity extends SherlockPreferenceActivity implements + OnSharedPreferenceChangeListener { @Bean - protected ApplicationSettings applicationSettings; - + protected NotificationSettings notificationSettings; + @SuppressWarnings("deprecation") @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); getSupportActionBar().setDisplayHomeAsUpEnabled(true); - + // Just load the notification-related preferences from XML addPreferencesFromResource(R.xml.pref_notifications); - + + } + + @SuppressWarnings("deprecation") + @Override + protected void onResume() { + super.onResume(); + // Start/stop the background service appropriately + getPreferenceScreen().getSharedPreferences().registerOnSharedPreferenceChangeListener(this); + } + + @SuppressWarnings("deprecation") + @Override + protected void onPause() { + super.onPause(); + getPreferenceScreen().getSharedPreferences().unregisterOnSharedPreferenceChangeListener(this); } @TargetApi(Build.VERSION_CODES.HONEYCOMB) @@ -37,4 +56,16 @@ public class NotificationSettingsActivity extends SherlockPreferenceActivity { MainSettingsActivity_.intent(this).flags(Intent.FLAG_ACTIVITY_CLEAR_TOP).start(); } + @Override + public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { + + if (!notificationSettings.isEnabled()) { + // Disabled background notifications; disable the alarms that start the service + BootReceiver.cancelBackgroundServices(getApplicationContext()); + } + + // (Re-)enable the alarms for the background services + BootReceiver.startBackgroundServices(getApplicationContext(), true); + } + } diff --git a/core/src/org/transdroid/core/gui/settings/SystemSettingsActivity.java b/core/src/org/transdroid/core/gui/settings/SystemSettingsActivity.java index 8e6da89f..8b9f6352 100644 --- a/core/src/org/transdroid/core/gui/settings/SystemSettingsActivity.java +++ b/core/src/org/transdroid/core/gui/settings/SystemSettingsActivity.java @@ -13,6 +13,7 @@ import org.transdroid.core.app.settings.SettingsPersistence; import org.transdroid.core.gui.log.ErrorLogSender; import org.transdroid.core.gui.navigation.DialogHelper; import org.transdroid.core.gui.navigation.NavigationHelper; +import org.transdroid.core.service.BootReceiver; import android.annotation.TargetApi; import android.app.AlertDialog; @@ -24,6 +25,7 @@ import android.content.SharedPreferences; import android.net.Uri; import android.os.Build; import android.os.Bundle; +import android.preference.CheckBoxPreference; import android.preference.Preference; import android.preference.Preference.OnPreferenceClickListener; import android.preference.PreferenceManager; @@ -58,7 +60,8 @@ public class SystemSettingsActivity extends SherlockPreferenceActivity { // Just load the system-related preferences from XML addPreferencesFromResource(R.xml.pref_system); - // Handle outgoing links + // Handle outgoing links and preference changes + findPreference("system_checkupdates").setOnPreferenceClickListener(onCheckUpdatesClick); findPreference("system_sendlog").setOnPreferenceClickListener(onSendLogClick); findPreference("system_installhelp").setOnPreferenceClickListener(onInstallHelpClick); findPreference("system_changelog").setOnPreferenceClickListener(onChangeLogClick); @@ -73,6 +76,17 @@ public class SystemSettingsActivity extends SherlockPreferenceActivity { MainSettingsActivity_.intent(this).flags(Intent.FLAG_ACTIVITY_CLEAR_TOP).start(); } + private OnPreferenceClickListener onCheckUpdatesClick = new OnPreferenceClickListener() { + @Override + public boolean onPreferenceClick(Preference preference) { + if (((CheckBoxPreference) preference).isChecked()) + BootReceiver.startAppUpdatesService(getApplicationContext()); + else + BootReceiver.cancelAppUpdates(getApplicationContext()); + return true; + } + }; + private OnPreferenceClickListener onSendLogClick = new OnPreferenceClickListener() { @Override public boolean onPreferenceClick(Preference preference) { diff --git a/core/src/org/transdroid/core/service/AlarmReceiver.java b/core/src/org/transdroid/core/service/AlarmReceiver.java new file mode 100644 index 00000000..384e9e58 --- /dev/null +++ b/core/src/org/transdroid/core/service/AlarmReceiver.java @@ -0,0 +1,34 @@ +package org.transdroid.core.service; + +import org.androidannotations.annotations.EReceiver; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; + +/** + * Acts simply as an intermediary to start the appropriate background service when an alarm goes off. + * @author Eric Kok + */ +@EReceiver +public class AlarmReceiver extends BroadcastReceiver { + + @Override + public void onReceive(Context context, Intent intent) { + switch (intent.getIntExtra("service", -1)) { + case BootReceiver.ALARM_SERVERCHECKER: + context.startService(new Intent(context, ServerCheckerService_.class)); + break; + case BootReceiver.ALARM_RSSCHECKER: + context.startService(new Intent(context, RssCheckerService_.class)); + break; + case BootReceiver.ALARM_APPUPDATES: + context.startService(new Intent(context, AppUpdateService_.class)); + break; + default: + // No valid service start ID + break; + } + } + +} diff --git a/core/src/org/transdroid/core/service/AppUpdateService.java b/core/src/org/transdroid/core/service/AppUpdateService.java new file mode 100644 index 00000000..f99fdfd4 --- /dev/null +++ b/core/src/org/transdroid/core/service/AppUpdateService.java @@ -0,0 +1,136 @@ +package org.transdroid.core.service; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Random; + +import org.androidannotations.annotations.Bean; +import org.androidannotations.annotations.EService; +import org.androidannotations.annotations.SystemService; +import org.apache.http.HttpResponse; +import org.apache.http.client.ClientProtocolException; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.impl.client.AbstractHttpClient; +import org.apache.http.impl.client.DefaultHttpClient; +import org.transdroid.core.R; +import org.transdroid.core.app.settings.NotificationSettings; +import org.transdroid.core.app.settings.SystemSettings; +import org.transdroid.core.gui.log.Log; +import org.transdroid.daemon.util.HttpHelper; + +import android.app.IntentService; +import android.app.NotificationManager; +import android.app.PendingIntent; +import android.content.Intent; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager.NameNotFoundException; +import android.net.Uri; +import android.support.v4.app.NotificationCompat; +import android.support.v4.app.NotificationCompat.Builder; + +@EService +public class AppUpdateService extends IntentService { + + private static final String LATEST_URL_APP = "http://www.transdroid.org/update/latest-app.php"; + private static final String LATEST_URL_SEARCH = "http://www.transdroid.org/update/latest-search.php"; + private static final String DOWNLOAD_URL_APP = "http://www.transdroid.org/latest"; + private static final String DOWNLOAD_URL_SEARCH = "http://www.transdroid.org/latest-search"; + + @Bean + protected ConnectivityHelper connectivityHelper; + @Bean + protected SystemSettings systemSettings; + @Bean + protected NotificationSettings notificationSettings; + @SystemService + protected NotificationManager notificationManager; + + public AppUpdateService() { + super("AppUpdateService"); + } + + @Override + protected void onHandleIntent(Intent intent) { + + if (!connectivityHelper.shouldPerformActions() || !systemSettings.checkForUpdates()) { + Log.d(this, + "Skip the app update service, as background data is disabled, the service is disabled or we are not connected."); + return; + } + + DefaultHttpClient httpclient = new DefaultHttpClient(); + Random random = new Random(); + + try { + + // Retrieve what is the latest released app and search module versions + String[] app = retrieveLatestVersion(httpclient, LATEST_URL_APP); + String[] search = retrieveLatestVersion(httpclient, LATEST_URL_SEARCH); + int appVersion = Integer.parseInt(app[0].trim()); + int searchVersion = Integer.parseInt(search[0].trim()); + + // New version of the app? + try { + PackageInfo appPackage = getPackageManager().getPackageInfo(getPackageName(), 0); + if (appPackage.versionCode < appVersion) { + // New version available! Notify the user. + newNotification(getString(R.string.update_app_newversion), + getString(R.string.update_app_newversion), + getString(R.string.update_updateto, app[1].trim()), + DOWNLOAD_URL_APP + "?" + Integer.toString(random.nextInt()), 90000); + } + } catch (NameNotFoundException e) { + // Not installed... this can never happen since this Service is part of the app itself + } + + // New version of the search module? + try { + PackageInfo searchPackage = getPackageManager().getPackageInfo("org.transdroid.search", 0); + if (searchPackage.versionCode < searchVersion) { + // New version available! Notify the user. + newNotification(getString(R.string.update_search_newversion), + getString(R.string.update_search_newversion), + getString(R.string.update_updateto, search[1].trim()), + DOWNLOAD_URL_SEARCH + "?" + Integer.toString(random.nextInt()), 90001); + } + } catch (NameNotFoundException e) { + // The search module isn't installed yet at all; ignore and wait for the user to manually + // install it (when the first search is initiated) + } + + } catch (Exception e) { + // Cannot check right now for some reason; log and ignore + Log.d(this, "Cannot retrieve latest app or search module version code from the site: " + e.toString()); + } + + } + + /** + * Retrieves the latest version number of the app or search module by checking an online text file that looks like + * '160|1.1.15' for version code 160 and version name 1.1.15. + * @param httpclient An already instantiated HTTP client + * @param url The URL of the the text file that contains the current latest version code and name + * @return A string array with two elements: the version code and the version number + * @throws ClientProtocolException Thrown when the provided URL is invalid + * @throws IOException Thrown when the last version information could not be retrieved + */ + private String[] retrieveLatestVersion(AbstractHttpClient httpclient, String url) throws ClientProtocolException, + IOException { + HttpResponse request = httpclient.execute(new HttpGet(url)); + InputStream stream = request.getEntity().getContent(); + String appVersion[] = HttpHelper.convertStreamToString(stream).split("\\|"); + stream.close(); + return appVersion; + } + + private void newNotification(String ticker, String title, String text, String downloadUrl, int notifyID) { + PendingIntent pi = PendingIntent.getActivity(this, notifyID, + new Intent(Intent.ACTION_VIEW, Uri.parse(downloadUrl)), Intent.FLAG_ACTIVITY_NEW_TASK); + Builder builder = new NotificationCompat.Builder(this).setSmallIcon(R.drawable.ic_stat_notification) + .setTicker(ticker).setContentTitle(title).setContentText(text) + .setLights(notificationSettings.getDesiredLedColour(), 600, 1000) + .setSound(notificationSettings.getSound()).setAutoCancel(true).setContentIntent(pi); + notificationManager.notify(notifyID, builder.build()); + } + +} diff --git a/core/src/org/transdroid/core/service/BootReceiver.java b/core/src/org/transdroid/core/service/BootReceiver.java new file mode 100644 index 00000000..ceab951f --- /dev/null +++ b/core/src/org/transdroid/core/service/BootReceiver.java @@ -0,0 +1,88 @@ +package org.transdroid.core.service; + +import org.transdroid.core.app.settings.NotificationSettings; +import org.transdroid.core.app.settings.NotificationSettings_; +import org.transdroid.core.app.settings.SystemSettings; +import org.transdroid.core.app.settings.SystemSettings_; +import org.transdroid.core.gui.log.Log; + +import android.app.AlarmManager; +import android.app.PendingIntent; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.os.SystemClock; + +/** + * Receives the intent that the device has been started in order to set up proper alarms for all background services. + * @author Eric Kok + */ +public class BootReceiver extends BroadcastReceiver { + + public static final int ALARM_SERVERCHECKER = 0; + public static final int ALARM_RSSCHECKER = 1; + public static final int ALARM_APPUPDATES = 2; + + public static PendingIntent piServerChecker = null, piRssChecker = null, piAppUpdates = null; + + @Override + public void onReceive(Context context, Intent intent) { + startBackgroundServices(context, false); + startAppUpdatesService(context); + } + + public static void startBackgroundServices(Context context, boolean forceReload) { + NotificationSettings notificationSettings = NotificationSettings_.getInstance_(context); + AlarmManager alarms = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); + if (notificationSettings.isEnabled() && (forceReload || (piServerChecker == null && piRssChecker == null))) { + + Log.d(context, "Boot signal received, starting server and rss checker background services"); + // Schedule repeating alarms, with the first being (somewhat) in 1 second from now + piServerChecker = PendingIntent.getBroadcast(context, ALARM_SERVERCHECKER, new Intent(context, + AlarmReceiver_.class).putExtra("service", ALARM_SERVERCHECKER), 0); + piRssChecker = PendingIntent.getBroadcast(context, ALARM_RSSCHECKER, new Intent(context, + AlarmReceiver_.class).putExtra("service", ALARM_RSSCHECKER), 0); + alarms.setInexactRepeating(AlarmManager.ELAPSED_REALTIME, SystemClock.elapsedRealtime() + 1000, + notificationSettings.getInvervalInMilliseconds(), piServerChecker); + alarms.setInexactRepeating(AlarmManager.ELAPSED_REALTIME, SystemClock.elapsedRealtime() + 1000, + notificationSettings.getInvervalInMilliseconds(), piRssChecker); + + } + } + + public static void startAppUpdatesService(Context context) { + SystemSettings systemSettings = SystemSettings_.getInstance_(context); + AlarmManager alarms = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); + if (systemSettings.checkForUpdates() && piAppUpdates == null) { + + Log.d(context, "Boot signal received, starting app update checker service"); + // Schedule a daily, with the first being (somewhat) in 1 second from now + piAppUpdates = PendingIntent.getBroadcast(context, ALARM_APPUPDATES, new Intent(context, + AlarmReceiver_.class).putExtra("service", ALARM_APPUPDATES), 0); + alarms.setInexactRepeating(AlarmManager.ELAPSED_REALTIME, SystemClock.elapsedRealtime() + 1000, + AlarmManager.INTERVAL_DAY, piAppUpdates); + + } + } + + public static void cancelBackgroundServices(Context context) { + AlarmManager alarms = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); + if (piServerChecker != null) { + alarms.cancel(piServerChecker); + piServerChecker = null; + } + if (piRssChecker != null) { + alarms.cancel(piRssChecker); + piRssChecker = null; + } + } + + public static void cancelAppUpdates(Context context) { + if (piAppUpdates != null) { + AlarmManager alarms = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); + alarms.cancel(piAppUpdates); + piAppUpdates = null; + } + } + +} diff --git a/core/src/org/transdroid/core/service/ConnectivityHelper.java b/core/src/org/transdroid/core/service/ConnectivityHelper.java new file mode 100644 index 00000000..fe043320 --- /dev/null +++ b/core/src/org/transdroid/core/service/ConnectivityHelper.java @@ -0,0 +1,29 @@ +package org.transdroid.core.service; + +import org.androidannotations.annotations.EBean; +import org.androidannotations.annotations.SystemService; +import org.androidannotations.annotations.EBean.Scope; + +import android.content.Context; +import android.net.ConnectivityManager; + +@EBean(scope = Scope.Singleton) +public class ConnectivityHelper { + + @SystemService + protected ConnectivityManager connectivityManager; + + public ConnectivityHelper(Context context) { + } + + @SuppressWarnings("deprecation") + public boolean shouldPerformActions() { + // First check the old background data setting (this will always be true for ICS+) + if (!connectivityManager.getBackgroundDataSetting()) + return false; + + // Still good? Check the current active network instead + return connectivityManager.getActiveNetworkInfo().isConnected(); + } + +} diff --git a/core/src/org/transdroid/core/service/RssCheckerService.java b/core/src/org/transdroid/core/service/RssCheckerService.java new file mode 100644 index 00000000..307b8807 --- /dev/null +++ b/core/src/org/transdroid/core/service/RssCheckerService.java @@ -0,0 +1,107 @@ +package org.transdroid.core.service; + +import java.util.LinkedHashSet; +import java.util.Set; + +import org.androidannotations.annotations.Bean; +import org.androidannotations.annotations.EService; +import org.androidannotations.annotations.SystemService; +import org.transdroid.core.R; +import org.transdroid.core.app.settings.ApplicationSettings; +import org.transdroid.core.app.settings.NotificationSettings; +import org.transdroid.core.app.settings.RssfeedSetting; +import org.transdroid.core.gui.log.Log; +import org.transdroid.core.gui.rss.RssfeedsActivity_; +import org.transdroid.core.rssparser.Item; +import org.transdroid.core.rssparser.RssParser; +import org.transdroid.daemon.util.Collections2; + +import android.app.IntentService; +import android.app.NotificationManager; +import android.app.PendingIntent; +import android.content.Intent; +import android.support.v4.app.NotificationCompat; +import android.support.v4.app.NotificationCompat.Builder; + +/** + * A background service that checks all user-configured RSS feeds for new items. + * @author Eric Kok + */ +@EService +public class RssCheckerService extends IntentService { + + @Bean + protected ConnectivityHelper connectivityHelper; + @Bean + protected NotificationSettings notificationSettings; + @Bean + protected ApplicationSettings applicationSettings; + @SystemService + protected NotificationManager notificationManager; + + public RssCheckerService() { + super("RssCheckerService"); + } + + @Override + protected void onHandleIntent(Intent intent) { + + if (!connectivityHelper.shouldPerformActions() || !notificationSettings.isEnabled()) { + Log.d(this, + "Skip the RSS checker service, as background data is disabled, the service is disabled or we are not connected."); + return; + } + + // Check every RSS feed for new items + int unread = 0; + Set hasUnread = new LinkedHashSet(); + for (RssfeedSetting feed : applicationSettings.getRssfeedSettings()) { + try { + + Log.d(this, "Try to parse " + feed.getName() + " (" + feed.getUrl() + ")"); + RssParser parser = new RssParser(feed.getUrl()); + parser.parse(); + if (parser.getChannel() == null) + continue; + + // Find the last item that is newer than the last viewed date + for (Item item : parser.getChannel().getItems()) { + if (item.getPubdate() != null && item.getPubdate().before(feed.getLastViewed())) { + break; + } else { + unread++; + if (!hasUnread.contains(feed.getName())) + hasUnread.add(feed.getName()); + } + } + + Log.d(this, feed.getName() + " has " + (hasUnread.contains(feed.getName()) ? "" : "no ") + + "unread items"); + + } catch (Exception e) { + // Ignore RSS feeds that could not be retrieved or parsed + } + } + + if (unread == 0) { + // No new items; just exit + return; + } + + // Provide a notification, since there are new RSS items + PendingIntent pi = PendingIntent.getActivity(this, 80000, new Intent(this, RssfeedsActivity_.class), + Intent.FLAG_ACTIVITY_NEW_TASK); + String title = getResources().getQuantityString(R.plurals.rss_service_new, unread, Integer.toString(unread)); + String forString = Collections2.joinString(hasUnread, ", "); + Builder builder = new NotificationCompat.Builder(this).setSmallIcon(R.drawable.ic_stat_notification) + .setTicker(title).setContentTitle(title) + .setContentText(getString(R.string.rss_service_newfor, forString)).setNumber(unread) + .setLights(notificationSettings.getDesiredLedColour(), 600, 1000) + .setSound(notificationSettings.getSound()).setAutoCancel(true).setContentIntent(pi); + if (notificationSettings.shouldVibrate()) + builder.setVibrate(notificationSettings.getDefaultVibratePattern()); + notificationManager.notify(80001, builder.build()); + + } + +} diff --git a/core/src/org/transdroid/core/service/ServerCheckerService.java b/core/src/org/transdroid/core/service/ServerCheckerService.java new file mode 100644 index 00000000..a04a528a --- /dev/null +++ b/core/src/org/transdroid/core/service/ServerCheckerService.java @@ -0,0 +1,183 @@ +package org.transdroid.core.service; + +import java.util.ArrayList; +import java.util.List; + +import org.androidannotations.annotations.Bean; +import org.androidannotations.annotations.EService; +import org.androidannotations.annotations.SystemService; +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; +import org.transdroid.core.R; +import org.transdroid.core.app.settings.ApplicationSettings; +import org.transdroid.core.app.settings.NotificationSettings; +import org.transdroid.core.app.settings.ServerSetting; +import org.transdroid.core.gui.TorrentsActivity_; +import org.transdroid.core.gui.log.Log; +import org.transdroid.daemon.IDaemonAdapter; +import org.transdroid.daemon.Torrent; +import org.transdroid.daemon.task.DaemonTaskResult; +import org.transdroid.daemon.task.RetrieveTask; +import org.transdroid.daemon.task.RetrieveTaskSuccessResult; +import org.transdroid.daemon.util.Collections2; + +import android.app.IntentService; +import android.app.NotificationManager; +import android.app.PendingIntent; +import android.content.Intent; +import android.support.v4.app.NotificationCompat; +import android.support.v4.app.NotificationCompat.Builder; +import android.support.v4.app.NotificationCompat.InboxStyle; + +/** + * A background service that checks all user-configured servers (if so desired) for new and finished torrents. + * @author Eric Kok + */ +@EService +public class ServerCheckerService extends IntentService { + + @Bean + protected ConnectivityHelper connectivityHelper; + @Bean + protected NotificationSettings notificationSettings; + @Bean + protected ApplicationSettings applicationSettings; + @SystemService + protected NotificationManager notificationManager; + + public ServerCheckerService() { + super("ServerCheckerService"); + } + + @Override + protected void onHandleIntent(Intent intent) { + + if (!connectivityHelper.shouldPerformActions() || !notificationSettings.isEnabled()) { + Log.d(this, + "Skip the server checker service, as background data is disabled, the service is disabled or we are not connected."); + return; + } + + int notifyBase = 10000; + for (ServerSetting server : applicationSettings.getServerSettings()) { + + // No need to check if the server is not properly configured or none of the two types of notifications are + // enabled by the user for this specific server + if (server.getType() == null || server.getAddress() == null || server.getAddress().equals("") + || !(server.shouldAlarmOnFinishedDownload() || server.shouldAlarmOnNewTorrent())) + return; + + // Get the statistics for the last time we checked this server + JSONArray lastStats = applicationSettings.getServerLastStats(server); + + // Synchronously retrieve torrents listing + IDaemonAdapter adapter = server.createServerAdapter(); + DaemonTaskResult result = RetrieveTask.create(adapter).execute(); + if (!(result instanceof RetrieveTaskSuccessResult)) { + // Cannot retrieve torrents at this time + return; + } + List retrieved = ((RetrieveTaskSuccessResult) result).getTorrents(); + Log.d(this, server.getName() + ": Retrieved torrent listing"); + + // Check for differences between the last and the current stats + JSONArray currentStats = new JSONArray(); + List newTorrents = new ArrayList(); + List doneTorrents = new ArrayList(); + for (Torrent torrent : retrieved) { + + // Remember this torrent for the next time + try { + currentStats.put(new JSONObject().put("id", torrent.getUniqueID()).put("done", + torrent.getPartDone() == 1F)); + } catch (JSONException e) { + // Can't build the JSON object; this should not happen and we can safely ignore it + } + + // See if this torrent was done the last time we checked + if (lastStats != null) { + Boolean wasDone = findLastDoneStat(lastStats, torrent); + if (server.shouldAlarmOnNewTorrent() && wasDone == null) { + // This torrent wasn't present earlier + newTorrents.add(torrent); + continue; + } + if (server.shouldAlarmOnFinishedDownload() && torrent.getPartDone() == 1F && !wasDone) + // This torrent is now done, but wasn't before + doneTorrents.add(torrent); + } + + } + + // Store the now-current statistics on torrents for the next time we check this server + applicationSettings.setServerLastStats(server, currentStats); + + // Notify on new and now-done torrents for this server + Log.d(this, server.getName() + ": " + newTorrents.size() + " new torrents, " + doneTorrents.size() + + " newly finished torrents."); + Intent i = new Intent(this, TorrentsActivity_.class); + i.putExtra("org.transdroid.START_SERVER", server.getOrder()); + // Should start the main activity directly into this server + PendingIntent pi = PendingIntent.getActivity(this, notifyBase + server.getOrder(), i, + Intent.FLAG_ACTIVITY_NEW_TASK); + ArrayList affectedTorrents = new ArrayList(newTorrents.size() + doneTorrents.size()); + affectedTorrents.addAll(newTorrents); + affectedTorrents.addAll(doneTorrents); + String title, forString = Collections2.joinString(affectedTorrents, ", "); + if (newTorrents.size() > 0 && doneTorrents.size() > 0) { + // Note: use the 'one' plural iif 1 new torrent was added and 1 was newly finished + title = getResources().getQuantityString(R.plurals.status_service_finished, + newTorrents.size() + doneTorrents.size() == 2 ? 1 : 2, Integer.toString(newTorrents.size()), + Integer.toString(doneTorrents.size())); + } else if (newTorrents.size() > 0) { + title = getResources().getQuantityString(R.plurals.status_service_added, newTorrents.size(), + Integer.toString(newTorrents.size())); + } else if (doneTorrents.size() > 0) { + title = getResources().getQuantityString(R.plurals.status_service_finished, doneTorrents.size(), + Integer.toString(doneTorrents.size())); + } else { + // No notification to show + continue; + } + + // Build the basic notification + Builder builder = new NotificationCompat.Builder(this).setSmallIcon(R.drawable.ic_stat_notification) + .setTicker(title).setContentTitle(title).setContentText(forString) + .setNumber(affectedTorrents.size()) + .setLights(notificationSettings.getDesiredLedColour(), 600, 1000) + .setSound(notificationSettings.getSound()).setAutoCancel(true).setContentIntent(pi); + if (notificationSettings.shouldVibrate()) + builder.setVibrate(notificationSettings.getDefaultVibratePattern()); + + // Add at most 5 lines with the affected torrents + InboxStyle inbox = new NotificationCompat.InboxStyle(builder); + if (affectedTorrents.size() < 6) { + for (Torrent affectedTorrent : affectedTorrents) { + inbox.addLine(affectedTorrent.getName()); + } + } else { + for (int j = 0; j < 4; j++) { + inbox.addLine(affectedTorrents.get(j).getName()); + } + inbox.addLine(getString(R.string.status_service_andothers, affectedTorrents.get(5).getName())); + } + notificationManager.notify(notifyBase + server.getOrder(), inbox.build()); + + } + + } + + private Boolean findLastDoneStat(JSONArray lastStats, Torrent torrent) { + for (int i = 0; i < lastStats.length(); i++) { + try { + if (lastStats.getJSONObject(i).getString("id").equals(torrent.getUniqueID())) + return lastStats.getJSONObject(i).getBoolean("done"); + } catch (JSONException e) { + return null; + } + } + return null; + } + +} diff --git a/full/AndroidManifest.xml b/full/AndroidManifest.xml index 81243c33..c3a1ecbc 100644 --- a/full/AndroidManifest.xml +++ b/full/AndroidManifest.xml @@ -1,8 +1,8 @@ + android:versionCode="3" + android:versionName="2.0-alpha3" > + @@ -26,10 +27,10 @@ + android:theme="@style/Theme.Sherlock" > @@ -68,8 +69,8 @@ @@ -79,8 +80,8 @@ @@ -90,8 +91,8 @@ @@ -101,8 +102,8 @@ @@ -112,8 +113,8 @@ @@ -123,8 +124,8 @@ @@ -134,8 +135,8 @@ @@ -146,6 +147,7 @@ + @@ -170,8 +172,8 @@ android:name="org.transdroid.core.gui.search.SearchActivity_" android:icon="@drawable/ic_launcher" android:label="@string/search_torrentsearch" - android:theme="@style/TransdroidTheme" - android:launchMode="singleTask" > + android:launchMode="singleTask" + android:theme="@style/TransdroidTheme" > @@ -192,11 +194,12 @@ android:name="android.app.default_searchable" android:value="org.transdroid.core.gui.search.SearchActivity_" /> - - + + + + + + + + + + + + + + \ No newline at end of file diff --git a/lite/AndroidManifest.xml b/lite/AndroidManifest.xml index 055e0598..9a290bc3 100644 --- a/lite/AndroidManifest.xml +++ b/lite/AndroidManifest.xml @@ -1,8 +1,8 @@ + android:versionCode="3" + android:versionName="2.0-alpha3" > + @@ -26,10 +27,10 @@ + android:theme="@style/Theme.Sherlock" > + + + + + + + + + + + + \ No newline at end of file