From 3308f277685b039d226e1c47dbeceb8b27305b44 Mon Sep 17 00:00:00 2001 From: serso Date: Sun, 18 Sep 2011 22:25:28 +0400 Subject: [PATCH] settings and about activity --- AndroidManifest.xml | 1 + res/drawable/timepicker_down_btn.xml | 50 ++ res/drawable/timepicker_down_disabled.9.png | Bin 0 -> 422 bytes .../timepicker_down_disabled_focused.9.png | Bin 0 -> 580 bytes res/drawable/timepicker_down_normal.9.png | Bin 0 -> 795 bytes res/drawable/timepicker_down_pressed.9.png | Bin 0 -> 1161 bytes res/drawable/timepicker_down_selected.9.png | Bin 0 -> 1170 bytes res/drawable/timepicker_input.xml | 36 ++ res/drawable/timepicker_input_disabled.9.png | Bin 0 -> 280 bytes res/drawable/timepicker_input_normal.9.png | Bin 0 -> 582 bytes res/drawable/timepicker_input_pressed.9.png | Bin 0 -> 604 bytes res/drawable/timepicker_input_selected.9.png | Bin 0 -> 517 bytes res/drawable/timepicker_up_btn.xml | 37 ++ res/drawable/timepicker_up_disabled.9.png | Bin 0 -> 491 bytes .../timepicker_up_disabled_focused.9.png | Bin 0 -> 728 bytes res/drawable/timepicker_up_normal.9.png | Bin 0 -> 989 bytes res/drawable/timepicker_up_pressed.9.png | Bin 0 -> 1433 bytes res/drawable/timepicker_up_selected.9.png | Bin 0 -> 1428 bytes res/layout-land/about.xml | 22 + res/layout-port/about.xml | 22 + res/layout-port/number_picker.xml | 49 ++ res/menu/main_menu.xml | 5 +- res/values/strings.xml | 10 + res/xml/preferences.xml | 21 +- .../android/calculator/AboutActivity.java | 31 + .../calculator/CalculatorActivity.java | 27 +- .../CalculatorPreferencesActivity.java | 14 - .../android/calculator/HelpActivity.java | 17 - .../android/view/AutoResizeTextView.java | 290 ++++++++++ .../view/DragPreferencesChangeListener.java | 21 + ...DragPreferencesChangeListenerRegister.java | 20 + .../solovyev/android/view/NumberPicker.java | 538 ++++++++++++++++++ .../android/view/NumberPickerButton.java | 106 ++++ .../android/view/SeekBarPreference.java | 134 +++++ .../android/view/SimpleOnDragListener.java | 11 +- 35 files changed, 1395 insertions(+), 67 deletions(-) create mode 100644 res/drawable/timepicker_down_btn.xml create mode 100644 res/drawable/timepicker_down_disabled.9.png create mode 100644 res/drawable/timepicker_down_disabled_focused.9.png create mode 100644 res/drawable/timepicker_down_normal.9.png create mode 100644 res/drawable/timepicker_down_pressed.9.png create mode 100644 res/drawable/timepicker_down_selected.9.png create mode 100644 res/drawable/timepicker_input.xml create mode 100644 res/drawable/timepicker_input_disabled.9.png create mode 100644 res/drawable/timepicker_input_normal.9.png create mode 100644 res/drawable/timepicker_input_pressed.9.png create mode 100644 res/drawable/timepicker_input_selected.9.png create mode 100644 res/drawable/timepicker_up_btn.xml create mode 100644 res/drawable/timepicker_up_disabled.9.png create mode 100644 res/drawable/timepicker_up_disabled_focused.9.png create mode 100644 res/drawable/timepicker_up_normal.9.png create mode 100644 res/drawable/timepicker_up_pressed.9.png create mode 100644 res/drawable/timepicker_up_selected.9.png create mode 100644 res/layout-land/about.xml create mode 100644 res/layout-port/about.xml create mode 100644 res/layout-port/number_picker.xml create mode 100644 src/main/java/org/solovyev/android/calculator/AboutActivity.java delete mode 100644 src/main/java/org/solovyev/android/calculator/HelpActivity.java create mode 100644 src/main/java/org/solovyev/android/view/AutoResizeTextView.java create mode 100644 src/main/java/org/solovyev/android/view/DragPreferencesChangeListener.java create mode 100644 src/main/java/org/solovyev/android/view/DragPreferencesChangeListenerRegister.java create mode 100644 src/main/java/org/solovyev/android/view/NumberPicker.java create mode 100644 src/main/java/org/solovyev/android/view/NumberPickerButton.java create mode 100644 src/main/java/org/solovyev/android/view/SeekBarPreference.java diff --git a/AndroidManifest.xml b/AndroidManifest.xml index c18c9208..afdbc3f6 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -19,6 +19,7 @@ + \ No newline at end of file diff --git a/res/drawable/timepicker_down_btn.xml b/res/drawable/timepicker_down_btn.xml new file mode 100644 index 00000000..b77375a8 --- /dev/null +++ b/res/drawable/timepicker_down_btn.xml @@ -0,0 +1,50 @@ + + + + + + + + + + + + + + + + + + + diff --git a/res/drawable/timepicker_down_disabled.9.png b/res/drawable/timepicker_down_disabled.9.png new file mode 100644 index 0000000000000000000000000000000000000000..596294b6208043eb7e9261172b8dae6c4e9adb19 GIT binary patch literal 422 zcmV;X0a^ZuP)sKG^^=Od5&NRlKU`sM2Tdq7zRK)WaanSlTT2p|CvKmY+GTLLN= z>;MG}EMWivbTr5Nym(brft2#prbisd_fZtRI5`7t@pjdNCG0AoS$_Ya$HDG_X7AeP z{>2gokh3s=oP}A!00Iag0T4g{0qinwnq~n_Kb^^Z6TqpI@)fdvI+N)pfHzr|z1Ma9 z4BJQ%)V<~cPm3K*PVmU zbWL=f>l!;c`5tuLb~A4_G+^BW)!A=fBHYFR2q1vuuRjqU_4hM)$MPw_0J{ZWlY=wF Q8UO$Q07*qoM6N<$f-gI1)gpAP zzwYW9`+YE|t1SBCn;Bghb0(bqzc})q70I!4z8{@qNHf-2{6gH!|2*cCzOr}=En?(= z?~jlnE(0w9pTF-OjO}iFS!gx8zSThkU(hY0g&eR_1{~2s3dk#Pqgu!TO~6qt00_W7 z1T?V*k}c3c2O7v5e-oPkBwP4o7ZlLIHX6t|XNsZ#uv5TBJWS#Cr*N}62OQPH*NM7Q zF$_aXrBXpV=Lmkd^DVMA$PGE)vjru~Ic>%n9;9u>p84fNM;G9Q6E#tKOa zU?O&6EbXmm`9ZTZf;lYOr)v4wv0+^{OHgO{!eWSR#Fq77NGpzOX0450%bTh_U6+P$ zHjj+lX`1bbLuc(Qkr6Rw#h4KzS literal 0 HcmV?d00001 diff --git a/res/drawable/timepicker_down_normal.9.png b/res/drawable/timepicker_down_normal.9.png new file mode 100644 index 0000000000000000000000000000000000000000..f17e8f942ac659a47bbfaf81d490eaa8e367d5cd GIT binary patch literal 795 zcmV+$1LXXPP)y zi$@GRQLwFxsD-q2C;b8WSJ@vB{s+>c2y|NuBa_H*6%nB=<|#7Tx%TeszVX~UKO9B9 z=fD9SXNG$}e7|$2hMl}z zE*Syb1c-$a;9BDZ@fx0j#R>(;PZmk>4`65o62KS$l8yh{Hxxj!@J$vfcx2%?EQ}H6 z$7m^Y6JR2dzyV@pIXn!YDL}nUv=+t`Cv_CX+_D^#wehqtWOaXR~n*a@}*tm%U!^Q6`hAnEFQ}S%LHU{A0CR{R)R) zL3ZStg8{q?`2w=mZntlN63i3_dj)jZ66Y2J$uJA3p{1cmVkX z@-gH&Pi$ZyC2P9wg z3}i$o04xje@ckD&G9CZ3oqCb83g9{rLCj=ras@O_4T8V;0O0l-Rc3VQ(AeZtWJ$nfM}7GEUYDm?7c ze5;#QDF#vS`iE>H3Hbq%2Tq9{D5W7)OS46Z^oOO@DnP1-NG5T^44A?%WwZgR^-sy0 z!U=#iwIP|~I>h>Mvi$d!4nPlFnP1CQfF3j|$P~sZOa|z^b+sH=@gX`Pz!*F-6(S~F zh`u<95Rai{>p75|vJX+!f{@}qG6^EaW^w``gG2ORvj9?Z1@!=i5Y>8^>N-Omm~bPi zI#>ZzX379qm%C*MFmhl<{xGz6Q1iSNaV;$X8vsn1!jh{PVh1uUw~+gf8pj|K2wUm^ ztd;(^c5&UD_7RcIAr+tuz^Ua=72-za3(dTumMosZ)p9tm1Er1wn~e8g`r*OTJuThi z?zJsAHTtJx(-|$Fh0p>h1BenbH_`2gC-3&b>0>rb9BB^X+NJ{^zYoIH$a3e_MD*d2 zRx^PD$=HP*n8N*584e8tY^-lIb-mgf4A|Mzt6g<<$U~!@6@w=e?Ai$O`qeY==Jm^# z?s56zd7#R(_9qq!)q2Sz;d17zE>NC}D^ zibbfr09VV>`qXFJ+wN&#zPlr^c*t)lF1~Dt=?IU=USO zQR^SWgFYMPmnut&O`h3*g>`#v_W02OyE5th(2|`=7y&iRM+Dn z>p9dmZ0ce!>jvx6+eah2PmGGOUA7!=*i|&qeJqEpuXBs;{Mnn!+dMkMdd%9$lYb7d z&aqCi##jdnnnQlE7FZuxuUJ2H61Z~We%26cpr8P^1BgG9&g(@ldeMtsG)^Ma!?QE1 be*z2u(+b1W3dTl!00000NkvXXu0mjf*$ELm literal 0 HcmV?d00001 diff --git a/res/drawable/timepicker_down_selected.9.png b/res/drawable/timepicker_down_selected.9.png new file mode 100644 index 0000000000000000000000000000000000000000..b45db62121d416b1b97143c80d67cae4b3e4503c GIT binary patch literal 1170 zcmV;D1a13?P)BocMpC5B?ljvTs&w9ArXQY5z!E$Cgim{(`9wf zY<9NOvpb#aqC2xiidmZJ+4OJ!)zw`+0F5z%*0YBE&uD0SMN<#2K_CDGJ5j=Ic`zP3 z^4J{*z;ysGKX}cJ*W-71#(ryy0Db@pB2f+28HcBajS3!kuJJB2z|9qalFtAcqM(Rf z4gpGrjs(c?3Px092DtqjpjfH^tQqkcRv*Lwih_j#i)$f3Kd!)hPJs%b3SS$5!8Ust zpnzPPOd#ln`5-Mo3grA3d0l|i;)Lpi7coGG#~7f60!4`-mNbB*@uG}L4KqJ%#zRNSOF9<^@--qf$vwk;PFh)zMgUK>;_yoyw$wS9I20lEkIEL z*(`hh?I2t{>A_I{-XQ+k@L_tZ56+9l=0!1vBC-QWk;exLqn>IPW-v%06R zkVU3mNFJPR-N0NIh&Eod7l5F3Y;ZEUHF^oA-o4q^Gj5Dt1zLR3upiY3+7_S=vRONi zA%6je$M5Xx8Kv0?2paXnqG5b>>d9Wx4@D+U4OU>Vw4nUIO1YW>6Is**h@nII3Wg0| zMr#SC+5*Knq}dBYtdp^0R*#C&@EK#CdN1sRDNa^$^fF*3_r(e7Q&%@U5+TPXiMSra z3)o3*h$&u;ZemjT=iqkQF#1Dg;3=0(($9fzCwB;s^C(_TptMC*Y9V6Ugtv3WaHf(+ znr?xabl51cD|(&53paxCvk)C#OP2Q0@X=I}hVW2bDbNaFXLtr6Tz-bov_%@P3><4# z@zhjW7LpA>ls9t-BX=b2JP&hLQAG)mRfw+5n+aZ|Kv_w^oWGe+elyFn31P3m>XXx5 z1TdEcRoZ#AO(ha9y#jv}(VHt2a1doppOXj?~ya~!^8kd&^`)-t*Ssyq7JI? zT^IQA*!YC3dOTr&sg(_O*#Y+3>|>LUM!L_O=o#pAaWIbnc>?<~{dIkI + + + + + + + + + + + + + \ No newline at end of file diff --git a/res/drawable/timepicker_input_disabled.9.png b/res/drawable/timepicker_input_disabled.9.png new file mode 100644 index 0000000000000000000000000000000000000000..f73658e73e57dda68df78005dca55c4fc53280d9 GIT binary patch literal 280 zcmeAS@N?(olHy`uVBq!ia0vp^0YI$D!3HE3*{oXvq$EpRBT9nv(@M${i&7aJQ}UBi z6+Ckj(^G>|6H_V+Po~-c747nLaSW-r_2$+_t|kYW0}qX*tOd54wI8^e{>f3QN?^Xu zA=`qY?aOj@C>931{O$YcxxmjI&H~Oi>K;#%*YiHJ&i8W6uYLBcwOUN4{_SgTD&r}aE YT!zopr01@D8t^fc4 literal 0 HcmV?d00001 diff --git a/res/drawable/timepicker_input_normal.9.png b/res/drawable/timepicker_input_normal.9.png new file mode 100644 index 0000000000000000000000000000000000000000..8032adac1441f1e7e8467d6969cb724a612a08aa GIT binary patch literal 582 zcmV-M0=fN(P){;gwEXCn{NqB)z0W+)_m|7X zeDHmrJkJvevFkc&+t#!e18^Y_{{$Ygu4^ia!dZc| zTCEK6D2gZu0+SK}Hcex)sw#7J1_Td zz~FklTEAum$YsmwLt-A36ufvaNHB0#AYd^xsdFHY7a(s5XcV9=r6~~Vn;O3YoR|ln z0#IrRKUIMeS7Db5RDiDl2v`cRa~l7s0*4SK17sGn6?m(IcizT4q(I%qJg|)c?S4uu zVT>6n>~oj}Pc%naLi-l52rfd2c-dY6;C+zphWfE!$1MP z0w5a;*;Iq8!~<1kz-?!M?27|D*t__2RvJJySc1if!P2t=grN|AICKJUoK$lW5&}Yk ziL(MvrxTseXX6iKpR`nuG}MP=)4dbGEXycOQ{5%^;&?n-@82UUX-lyan{NRI0OgQN UP_?K6b^rhX07*qoM6N<$f@t3I1^@s6 literal 0 HcmV?d00001 diff --git a/res/drawable/timepicker_input_pressed.9.png b/res/drawable/timepicker_input_pressed.9.png new file mode 100644 index 0000000000000000000000000000000000000000..30d8d5fa15af4797e0d4a1183ef4b35f9abb5814 GIT binary patch literal 604 zcmV-i0;BzjP) zhwut|10VH-`sfYx3O@L#M-T!UgRmXj?h3oG!P@QvaXLw%*+8@N<^N}<0**iEm|sq{ zJbR$q>x%?&Kk(-6(R^M01`y!=1yY175MVh?Wa0qMHrxBB4;T*+5P5+iQ#9tE1Xt&y z0DxbM%{I@6aC#y^FaxL!z_&%wVA0089pGxY*^b8(m`n^n!3>}VSfgmL@Kzw{?LHey z7>pE1$xDJoL6EHsiz&nbRDB7(OoC+QVPQP1`qW?%1Q4w9q>(Z+3%jmqa*qdSiXV#d zFjoBR>~3=JPJzq-y30?n0*j^sOA9juXcbrl0Ms?=&c0?LI|Y`x>-ZXoj1?GaVMx`at`Hn zC$z1ITW9vnS@|49?l5G=kG$qNKLy(B3^J3Th6n}L#iog`g>CK_36MMw`hN>oT zT5r~C?H0Ehs0%9d76AaU_NUo=4N4*i$q%5-{p>J6Yy~KL+kO80%67)f?ME*_fvTSd q literal 0 HcmV?d00001 diff --git a/res/drawable/timepicker_input_selected.9.png b/res/drawable/timepicker_input_selected.9.png new file mode 100644 index 0000000000000000000000000000000000000000..874f18f2bb021d7c984c49089488c6a6d3e6cd2f GIT binary patch literal 517 zcmV+g0{Z=lP)gC&qDV-2 z0K9=a{D5P50I%Z60gn6tp1}p45TcYzi{b@Gvg4>G7(0kcyDO!&sx<0Ock5LOaQK0T z{NaS>wMV>Lp99A}*46#f*VBdjn)IIe{=!;;;RoElu}m6ZYnKlvH&B)(2q8$pAR=^K z2bZtc5rDgmUDkCCRaKD$EQ$i)c{6*OAMj&43QGxjoqT<8aU#UvL>fb0H)+ z9ERlV2NblcZ<4oQz<&UAwibP3vG045H&pfU(>Ch`6waHPrm=spPy=k+7Rvdwg%>bi zjqB6fGqNMfv3^)ZRG>WK`7UV=lm)|t#&2#jBiro9rvL*0a=6CWO2M0;00000NkvXX Hu0mjf8xY$? literal 0 HcmV?d00001 diff --git a/res/drawable/timepicker_up_btn.xml b/res/drawable/timepicker_up_btn.xml new file mode 100644 index 00000000..22e32a3f --- /dev/null +++ b/res/drawable/timepicker_up_btn.xml @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/res/drawable/timepicker_up_disabled.9.png b/res/drawable/timepicker_up_disabled.9.png new file mode 100644 index 0000000000000000000000000000000000000000..327b0b5ad926c56f5c67259fe612527a1f7112b1 GIT binary patch literal 491 zcmVUD{k{l`FwJ}bH8zye*w7UzKP>_?KsZLFpTSSq*qndHqY~y zFbp60WzPLN1Yo1MyCg~OEz4TLq%6I%+yp`Jz?VuDb-bezi>OApjSqY1%;6ua?PT2tXaU_LdHS04N{88UV)uBmn$BzzbO|TolD+ zmSx{r)bqRz$A}MAW+2G`@CV-RwQTneutesnKuL=L5Eh1oXQjZC#+YtY`K{74y%oJb zam119D0hr_GI7L#g=e)y8vDuc9m*c%n%T8)e(laY-}j$Y*}{l1n1Kk=N-Gcm0T2Lt z@nqttGCIacu|y6<94wKUSs4GyjRZ)@fv_;PFphGtFcb&{POm_M0!J#4>RSA~2@&Bz hluj`xp)98GC9G>HON6fMv(1~J}2WZ>5&+EzZh8Gy!pv` zYTbd0OL7;S+YJ#Ebpmn#GB9D$$IJQSnH0k>U;qN~NDVv?0Q(&p4&X3>RH!Hb zTMA@i`15--&%yv|STGS4fdBuEh8w()`^P|X05XpTAkF|}#ukA8(V8<%|No8#96SL3 z5=epn|NmzMg3&BYZWcxhyg?Iy&z?QwzJC3>D2T18srgz;O6u!i3_xtn7{-4(Kk#+d>chfEe8%FP(2f1?42l7&ePz|kyBN){eXfz(fd zO#kULTY%hDAq4jB-K&62oYqYow6Gwm83V3FV3=M_m4P&aP!>iC3nD9#(fKMw7A7kN z{)Z%8T4!ZY_8Sal5X!{GW_UWOT|IIRpoBEj%Q` zkDM}aG}v&0(EuC`z#$NTjDx|Y$juX4H&sXg#Px*-gG=!>aYnQ7Fw4UKM+1=j0K_^g zH#kbcL0Re%H7mzB8h|(hkjP48G!x@WbHoklj|L#lAR{gXGP5E$uE!$kM|1$Pl92*g z*hUi`kqv1^tO3Z%J{nTQ_Cy%};~2XF*3P4YT1WuY+`}dg5MThEz3ltmO$v(u0000< KMNUMnLSTXl7dOuU literal 0 HcmV?d00001 diff --git a/res/drawable/timepicker_up_normal.9.png b/res/drawable/timepicker_up_normal.9.png new file mode 100644 index 0000000000000000000000000000000000000000..dcd26e0115e0e46357b5c5c141cb96ae0e1e8e9b GIT binary patch literal 989 zcmV<310wv1P)8v^^wxth>qVeEhyQZ*~ND8tcK!vzPeHug|mz*W$z%i7ya;<5NC=iC+`{ zP5h4=fGOf{udc2>B@&7EhGDGvk>2Cu-rjx=g+iOX zp1)ItrfG10fBz#Ei+v%d--$QaJZkdPSFKh*nx?r)zJdN-K81+zaO}tenJh0aU)ulz z58xsR+jj_$g7Y&#dPF9JIRT~tTp)k2*6ntE4UYqHZIIrk0yGdB>wX7FAtDV2+a~;CE2te6j&(F`{@bJ)T zmr5nb=kqE<)d`?34AAR3@bvT)wzjq)nM}gvbgD~z-jPg!3~GQkj-ZM z4{4e}>rz$9b0}kk02&k^t3uU=z$i34(q*L=DjcZKT@J*Do~A&VaIyb~hX<>TM_CG- zrb4*>E7Z-4JbTsz){QNw`VljLV zH%t`=2M188RL1Tt6bg{b<)kanO&zE#gilURtih+#Y3_BZz<4}v`O3=5$n{pM1?6%X z*4Nin)2XQ8VZ1_%kaG4+t z!x${P<20y(L~QB}Hd0CE**-<31GRG!ynA1^Io zoB--DzAL9gU`{3$!XovNPfchR)&ppcxlwFajx8YVGm_u(;VipydMFec7LdU&6Z&tY zv6vSII5#&pQdIg~fe6rHV_o9{#7*r$ed8+RDR7ds0(t_h{s}Mu#Lr?Bm;wV400000 LNkvXXu0mjf*EPs! literal 0 HcmV?d00001 diff --git a/res/drawable/timepicker_up_pressed.9.png b/res/drawable/timepicker_up_pressed.9.png new file mode 100644 index 0000000000000000000000000000000000000000..7dac77868749d88becf9c7d02c47edc68b0b4d64 GIT binary patch literal 1433 zcmV;K1!nq*P))HxC%ta3MyGkVTEI z1SKj8>`eE2WxbD{>6v*m+a0%ivORC9Om|OD)BT%&RlQd=0P8adHs08X_gue$W8Rx% z+sU?_ZFWle++w@Nw!(I!2*3x~9{y$N)9`^4FCimz@77R@d1e0 z{big1IE@*?0t9Nn{9JUvPse61CZ$7M$865_C($i!AO8Ou2cCa2g8Ozf;hPl&i+h$!UAFbZ zwD1f#PNEtr76E9~$M7iWWE2Q}u3WYPXPN+yKD5bfHw4(2TvZ%&DkP&oWkJgP00Rp! zwPaX%6~sNob1#`#x0SrD5?Ns)?kN!ia&y7BKn!GP`T@w8nn~w{NxW_v2ofeTB!6)-wqITnOS7V!NRftYQN||W+zNEF7iW_gf8LC{ z3cP-!Q?UXEmcy(&VgLV}F;H1>=B7)$@$;7(@ZOo`(DALa0nE)Rqdcnu1#f<~4K6LO z4xM}Il{OsQ8<*+KCIq?_ZO*l@v^9jX6?ppVEzsH*!2Sm!@B2w?lgN%)WQ*X5-TmkN z^{;}ZA7`Mo`+DgG8I(l;X5f_x@b1k3d**|#s}>zwHfh81!;CDdK(fIzwX!nsYcD?o z7cXA;U)Olyxu=2dd|96S(qcRnRG?l%lNwRYTBot^t{>pBW3PJO`}mX3`}SWye$;zj z=hw3!%bH@jG7GAL3QW`YP%C7eZ#4P)iZS1~bae(E*=28UwM}sQ+Yc%)GGLYi6ah&6 z^Pou##aT?I6KPrTO;Dczv&V7*#DR77Bvv8}P`U6S)dar5!6h;lbm45Ff(9>06)8}q z+f+R?=yf-}Y{mgZ*Ag|=92}ByB~A4PFXEM3enerWNL(?K~Yl8>c@|zyfPw6b(R;=oz6~ z46oHxDj^Zxz6$LtAN3w22&(nlZat74((q`ERN!D&jsb=&!v?F5U1&`SZuHhBj@{SY zI16xeQ)%!D0x`575Xv7W>-t+6Po_d43=7SqfC~q<52`=dt!H74RF_AM?in2m*B8#0 zM068HlQ)Gh@9$!!W!Qd*QK405zOEc{%1JXY5ss91z1dmepm3R36ac8hA4RUo9H1vl z)mLyHIRQ48rY6X?WH#crgq(c nV_@iaVJs-mo{Q1Ayq z{DqoLL^c|d$=bhJZ zi+yu!``PB%Zl6*yRi(t7%V^{_e;|<>|$Sq24DbDD3=YByN`afo&g9&0QWD>bPmC_ zD*$mC;50@E2M~G^7H8xk9}{3Zfb+6VU42*!7c7bYOL1vnF;K1!7u-9-vWR zY_|vH04lG(*awod>ryjyjSqgAg{STd;lQ0O_^zX2B|hIwB>;zAnRu>&HV}&{0DIPj zDM_T8_te|M0G1Zpc0DvgP)&Na?<5+*#BNGwn3M!4%sIxun1XyAp#g}RADoI^24I#t zZg^P40ZWs~UdkmBUT#sMl;V4NsW zjRjx=Q9z|cI#FPv#4!NexZ1PeOuP7R7xe6S0N$7aRlS4v+NBUqoonT`&&~*#YqJ8q z3!B-{o9Fhzx9it)$DVw66IS>2YIcWe3KXOrI6I505#ZThcf-nE0uIhch5O^!4Z!=_ z$QHpv3&Y1<=Je*P|JtxJcd52}B$i-c80e}1y+Mt|m^+xqy?ew!X%*XUX$;+c))vi% zIOG5Zh2<b=vE^Ftk@V2ytE2+^C)XS}4tCGOgRj0=xbO6t zvqS$Md+n9N<9gqJ0=nklMuVAbNq{hbM>R%Zb@3-_MeUpQHaxJ`-Cidqxc=F@W2rTu zXFyN^V0r)@SB*t;1XIc<(wtO}t=5A>Sxrd=;sBmm<|>8_^hOzgy1(AggTxUt63?SN zz`)J_tZ9EC%3QeS9%)Jgq<#k)hX*MZBMMk_6sWH!43jc&hj=y2oyFY#kO73bPO61>(*g~p>*eX94b^!TU^KuDw22{9aUPdDon2oW*(iV + + + + + + + + \ No newline at end of file diff --git a/res/layout-port/about.xml b/res/layout-port/about.xml new file mode 100644 index 00000000..28933ac3 --- /dev/null +++ b/res/layout-port/about.xml @@ -0,0 +1,22 @@ + + + + + + + + + \ No newline at end of file diff --git a/res/layout-port/number_picker.xml b/res/layout-port/number_picker.xml new file mode 100644 index 00000000..1b1a31c8 --- /dev/null +++ b/res/layout-port/number_picker.xml @@ -0,0 +1,49 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/res/menu/main_menu.xml b/res/menu/main_menu.xml index 53b9290d..3998aa42 100644 --- a/res/menu/main_menu.xml +++ b/res/menu/main_menu.xml @@ -1,9 +1,10 @@ + - + \ No newline at end of file diff --git a/res/values/strings.xml b/res/values/strings.xml index 1fb71d86..e85e9973 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -6,10 +6,20 @@ Result copied to clipboard! Settings Help + About + Main settings + Drag buttons settings + + Drag button calibration Allows to calibrate drag button behaviour Down Up Restart + + Copyright (c) 2009-2011.\nCreated by serso aka se.solovyev.\n + For more information please\ncontact me via email\nse.solovyev@gmail.com + \nor visit\nhttp://se.solovyev.org + diff --git a/res/xml/preferences.xml b/res/xml/preferences.xml index 0ee7d313..07075ce1 100644 --- a/res/xml/preferences.xml +++ b/res/xml/preferences.xml @@ -1,20 +1,15 @@ - - - - - - + + \ No newline at end of file diff --git a/src/main/java/org/solovyev/android/calculator/AboutActivity.java b/src/main/java/org/solovyev/android/calculator/AboutActivity.java new file mode 100644 index 00000000..0fb25b9b --- /dev/null +++ b/src/main/java/org/solovyev/android/calculator/AboutActivity.java @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2009-2011. Created by serso aka se.solovyev. + * For more information, please, contact se.solovyev@gmail.com + */ + +package org.solovyev.android.calculator; + +import android.app.Activity; +import android.os.Bundle; +import android.text.Html; +import android.text.method.LinkMovementMethod; +import android.widget.TextView; +import org.jetbrains.annotations.Nullable; + +/** + * User: serso + * Date: 9/16/11 + * Time: 11:52 PM + */ +public class AboutActivity extends Activity { + + @Override + public void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.about); + + final TextView about = (TextView) findViewById(R.id.aboutTextView); + about.setMovementMethod(LinkMovementMethod.getInstance()); + + } +} diff --git a/src/main/java/org/solovyev/android/calculator/CalculatorActivity.java b/src/main/java/org/solovyev/android/calculator/CalculatorActivity.java index 10160c89..efe4c966 100644 --- a/src/main/java/org/solovyev/android/calculator/CalculatorActivity.java +++ b/src/main/java/org/solovyev/android/calculator/CalculatorActivity.java @@ -25,15 +25,13 @@ import org.solovyev.common.utils.history.HistoryAction; import java.lang.reflect.Field; import java.lang.reflect.Modifier; -import java.util.ArrayList; -import java.util.List; public class CalculatorActivity extends Activity implements FontSizeAdjuster { private static final int HVGA_WIDTH_PIXELS = 320; @NotNull - private List onDragListeners = new ArrayList(); + private final DragPreferencesChangeListenerRegister dpclRegister = new DragPreferencesChangeListenerRegister(); @NotNull private CalculatorView calculatorView; @@ -64,7 +62,7 @@ public class CalculatorActivity extends Activity implements FontSizeAdjuster { final DragButtonCalibrationActivity.Preferences dragPreferences = DragButtonCalibrationActivity.getPreferences(this); final SimpleOnDragListener onDragListener = new SimpleOnDragListener(new DigitButtonDragProcessor(calculatorView), dragPreferences); - onDragListeners.add(onDragListener); + dpclRegister.addListener(onDragListener); // todo serso: check if there is more convenient method for doing this final R.id ids = new R.id(); @@ -86,12 +84,12 @@ public class CalculatorActivity extends Activity implements FontSizeAdjuster { final SimpleOnDragListener historyOnDragListener = new SimpleOnDragListener(new HistoryDragProcessor(this.calculatorView), dragPreferences); ((DragButton) findViewById(R.id.historyButton)).setOnDragListener(historyOnDragListener); - onDragListeners.add(historyOnDragListener); + dpclRegister.addListener(historyOnDragListener); final SimpleOnDragListener toPositionOnDragListener = new SimpleOnDragListener(new CursorDragProcessor(calculatorView), dragPreferences); ((DragButton) findViewById(R.id.rightButton)).setOnDragListener(toPositionOnDragListener); ((DragButton) findViewById(R.id.leftButton)).setOnDragListener(toPositionOnDragListener); - onDragListeners.add(toPositionOnDragListener); + dpclRegister.addListener(toPositionOnDragListener); preferencesChangesReceiver = new BroadcastReceiver() { @@ -100,9 +98,7 @@ public class CalculatorActivity extends Activity implements FontSizeAdjuster { if (DragButtonCalibrationActivity.INTENT_ACTION.equals(intent.getAction())) { final DragButtonCalibrationActivity.Preferences preferences = DragButtonCalibrationActivity.getPreferences(CalculatorActivity.this); - for (SimpleOnDragListener dragListener : onDragListeners) { - dragListener.setPreferences(preferences); - } + dpclRegister.announce().onDragPreferencesChange(preferences); } } }; @@ -189,9 +185,8 @@ public class CalculatorActivity extends Activity implements FontSizeAdjuster { @Override public boolean onCreateOptionsMenu(Menu menu) { - // todo serso: inflate menu as soon as it will implemented in proper way -/* final MenuInflater menuInflater = getMenuInflater(); - menuInflater.inflate(R.menu.main_menu, menu);*/ + final MenuInflater menuInflater = getMenuInflater(); + menuInflater.inflate(R.menu.main_menu, menu); return true; } @@ -204,8 +199,8 @@ public class CalculatorActivity extends Activity implements FontSizeAdjuster { showSettings(); result = true; break; - case R.id.menu_item_help: - showHelp(); + case R.id.menu_item_about: + showAbout(); result = true; break; default: @@ -219,8 +214,8 @@ public class CalculatorActivity extends Activity implements FontSizeAdjuster { startActivity(new Intent(this, CalculatorPreferencesActivity.class)); } - private void showHelp() { - Log.d(CalculatorActivity.class + "showHelp()", "Show help!"); + private void showAbout() { + startActivity(new Intent(this, AboutActivity.class)); } /** diff --git a/src/main/java/org/solovyev/android/calculator/CalculatorPreferencesActivity.java b/src/main/java/org/solovyev/android/calculator/CalculatorPreferencesActivity.java index cc3d5532..e9df1be7 100644 --- a/src/main/java/org/solovyev/android/calculator/CalculatorPreferencesActivity.java +++ b/src/main/java/org/solovyev/android/calculator/CalculatorPreferencesActivity.java @@ -22,19 +22,5 @@ public class CalculatorPreferencesActivity extends PreferenceActivity { super.onCreate(savedInstanceState); addPreferencesFromResource(R.xml.preferences); - - final Preference dragButtonCalibration = findPreference("dragButtonCalibration"); - dragButtonCalibration.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() { - - public boolean onPreferenceClick(Preference preference) { - startActivity(new Intent(CalculatorPreferencesActivity.this, DragButtonCalibrationActivity.class)); - return true; - } - }); - } - - @Override - protected void onRestoreInstanceState(Bundle state) { - super.onRestoreInstanceState(state); } } diff --git a/src/main/java/org/solovyev/android/calculator/HelpActivity.java b/src/main/java/org/solovyev/android/calculator/HelpActivity.java deleted file mode 100644 index 45d04900..00000000 --- a/src/main/java/org/solovyev/android/calculator/HelpActivity.java +++ /dev/null @@ -1,17 +0,0 @@ -/* - * Copyright (c) 2009-2011. Created by serso aka se.solovyev. - * For more information, please, contact se.solovyev@gmail.com - */ - -package org.solovyev.android.calculator; - -import android.app.Activity; - -/** - * User: serso - * Date: 9/16/11 - * Time: 11:52 PM - */ -public class HelpActivity extends Activity { - // todo serso: implement -} diff --git a/src/main/java/org/solovyev/android/view/AutoResizeTextView.java b/src/main/java/org/solovyev/android/view/AutoResizeTextView.java new file mode 100644 index 00000000..207870a0 --- /dev/null +++ b/src/main/java/org/solovyev/android/view/AutoResizeTextView.java @@ -0,0 +1,290 @@ +/* + * Copyright (c) 2009-2011. Created by serso aka se.solovyev. + * For more information, please, contact se.solovyev@gmail.com + * or visit http://se.solovyev.org + */ + +package org.solovyev.android.view; + +import android.content.Context; +import android.graphics.Canvas; +import android.text.Layout.Alignment; +import android.text.StaticLayout; +import android.text.TextPaint; +import android.util.AttributeSet; +import android.util.TypedValue; +import android.widget.TextView; + +/** + * Text view that auto adjusts text size to fit within the view. + * If the text size equals the minimum text size and still does not + * fit, append with an ellipsis. + * + * @author Chase Colburn + * @since Apr 4, 2011 + */ +public class AutoResizeTextView extends TextView { + + // Minimum text size for this text view + public static final float MIN_TEXT_SIZE = 20; + + // Interface for resize notifications + public interface OnTextResizeListener { + public void onTextResize(TextView textView, float oldSize, float newSize); + } + + // Off screen canvas for text size rendering + private static final Canvas sTextResizeCanvas = new Canvas(); + + // Our ellipse string + private static final String mEllipsis = "..."; + + // Registered resize listener + private OnTextResizeListener mTextResizeListener; + + // Flag for text and/or size changes to force a resize + private boolean mNeedsResize = false; + + // Text size that is set from code. This acts as a starting point for resizing + private float mTextSize; + + // Temporary upper bounds on the starting text size + private float mMaxTextSize = 0; + + // Lower bounds for text size + private float mMinTextSize = MIN_TEXT_SIZE; + + // Text view line spacing multiplier + private float mSpacingMult = 1.0f; + + // Text view additional line spacing + private float mSpacingAdd = 0.0f; + + // Add ellipsis to text that overflows at the smallest text size + private boolean mAddEllipsis = true; + + // Default constructor override + public AutoResizeTextView(Context context) { + this(context, null); + } + + // Default constructor when inflating from XML file + public AutoResizeTextView(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + // Default constructor override + public AutoResizeTextView(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + mTextSize = getTextSize(); + } + + /** + * When text changes, set the force resize flag to true and reset the text size. + */ + @Override + protected void onTextChanged(final CharSequence text, final int start, final int before, final int after) { + mNeedsResize = true; + // Since this view may be reused, it is good to reset the text size + resetTextSize(); + } + + /** + * If the text view size changed, set the force resize flag to true + */ + @Override + protected void onSizeChanged(int w, int h, int oldw, int oldh) { + if (w != oldw || h != oldh) { + mNeedsResize = true; + } + } + + /** + * Register listener to receive resize notifications + * @param listener + */ + public void setOnResizeListener(OnTextResizeListener listener) { + mTextResizeListener = listener; + } + + /** + * Override the set text size to update our internal reference values + */ + @Override + public void setTextSize(float size) { + super.setTextSize(size); + mTextSize = getTextSize(); + } + + /** + * Override the set text size to update our internal reference values + */ + @Override + public void setTextSize(int unit, float size) { + super.setTextSize(unit, size); + mTextSize = getTextSize(); + } + + /** + * Override the set line spacing to update our internal reference values + */ + @Override + public void setLineSpacing(float add, float mult) { + super.setLineSpacing(add, mult); + mSpacingMult = mult; + mSpacingAdd = add; + } + + /** + * Set the upper text size limit and invalidate the view + * @param maxTextSize + */ + public void setMaxTextSize(float maxTextSize) { + mMaxTextSize = maxTextSize; + requestLayout(); + invalidate(); + } + + /** + * Return upper text size limit + * @return + */ + public float getMaxTextSize() { + return mMaxTextSize; + } + + /** + * Set the lower text size limit and invalidate the view + * @param minTextSize + */ + public void setMinTextSize(float minTextSize) { + mMinTextSize = minTextSize; + requestLayout(); + invalidate(); + } + + /** + * Return lower text size limit + * @return + */ + public float getMinTextSize() { + return mMinTextSize; + } + + /** + * Set flag to add ellipsis to text that overflows at the smallest text size + * @param addEllipsis + */ + public void setAddEllipsis(boolean addEllipsis) { + mAddEllipsis = addEllipsis; + } + + /** + * Return flag to add ellipsis to text that overflows at the smallest text size + * @return + */ + public boolean getAddEllipsis() { + return mAddEllipsis; + } + + /** + * Reset the text to the original size + */ + public void resetTextSize() { + super.setTextSize(TypedValue.COMPLEX_UNIT_PX, mTextSize); + mMaxTextSize = mTextSize; + } + + /** + * Resize text after measuring + */ + @Override + protected void onLayout(boolean changed, int left, int top, int right, int bottom) { + if(changed || mNeedsResize) { + resizeText(right - left, bottom - top); + } + super.onLayout(changed, left, top, right, bottom); + } + + /** + * Resize the text size with default width and height + */ + public void resizeText() { + int heightLimit = getHeight() - getPaddingBottom() - getPaddingTop(); + int widthLimit = getWidth() - getPaddingLeft() - getPaddingRight(); + resizeText(widthLimit, heightLimit); + } + + /** + * Resize the text size with specified width and height + * @param width + * @param height + */ + public void resizeText(int width, int height) { + CharSequence text = getText(); + // Do not resize if the view does not have dimensions or there is no text + if(text == null || text.length() == 0 || height <= 0 || width <= 0) { + return; + } + + // Get the text view's paint object + TextPaint textPaint = getPaint(); + + // Store the current text size + float oldTextSize = textPaint.getTextSize(); + // If there is a max text size set, use the lesser of that and the default text size + float targetTextSize = mMaxTextSize > 0 ? Math.min(mTextSize, mMaxTextSize) : mTextSize; + + // Get the required text height + int textHeight = getTextHeight(text, textPaint, width, targetTextSize); + + // Until we either fit within our text view or we had reached our min text size, incrementally try smaller sizes + while(textHeight > height && targetTextSize > mMinTextSize) { + targetTextSize = Math.max(targetTextSize - 2, mMinTextSize); + textHeight = getTextHeight(text, textPaint, width, targetTextSize); + } + + // If we had reached our minimum text size and still don't fit, append an ellipsis + if(mAddEllipsis && targetTextSize == mMinTextSize && textHeight > height) { + // Draw using a static layout + StaticLayout layout = new StaticLayout(text, textPaint, width, Alignment.ALIGN_NORMAL, mSpacingMult, mSpacingAdd, false); + layout.draw(sTextResizeCanvas); + int lastLine = layout.getLineForVertical(height) - 1; + int start = layout.getLineStart(lastLine); + int end = layout.getLineEnd(lastLine); + float lineWidth = layout.getLineWidth(lastLine); + float ellipseWidth = textPaint.measureText(mEllipsis); + + // Trim characters off until we have enough room to draw the ellipsis + while(width < lineWidth + ellipseWidth) { + lineWidth = textPaint.measureText(text.subSequence(start, --end + 1).toString()); + } + setText(text.subSequence(0, end) + mEllipsis); + + } + + // Some devices try to auto adjust line spacing, so force default line spacing + // and invalidate the layout as a side effect + textPaint.setTextSize(targetTextSize); + setLineSpacing(mSpacingAdd, mSpacingMult); + + // Notify the listener if registered + if(mTextResizeListener != null) { + mTextResizeListener.onTextResize(this, oldTextSize, targetTextSize); + } + + // Reset force resize flag + mNeedsResize = false; + } + + // Set the text size of the text paint object and use a static layout to render text off screen before measuring + private int getTextHeight(CharSequence source, TextPaint paint, int width, float textSize) { + // Update the text paint object + paint.setTextSize(textSize); + // Draw using a static layout + StaticLayout layout = new StaticLayout(source, paint, width, Alignment.ALIGN_NORMAL, mSpacingMult, mSpacingAdd, false); + layout.draw(sTextResizeCanvas); + return layout.getHeight(); + } + +} diff --git a/src/main/java/org/solovyev/android/view/DragPreferencesChangeListener.java b/src/main/java/org/solovyev/android/view/DragPreferencesChangeListener.java new file mode 100644 index 00000000..69266330 --- /dev/null +++ b/src/main/java/org/solovyev/android/view/DragPreferencesChangeListener.java @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2009-2011. Created by serso aka se.solovyev. + * For more information, please, contact se.solovyev@gmail.com + */ + +package org.solovyev.android.view; + +import org.jetbrains.annotations.NotNull; +import org.solovyev.android.calculator.DragButtonCalibrationActivity; + +import java.util.EventListener; + +/** + * User: serso + * Date: 9/18/11 + * Time: 8:48 PM + */ +public interface DragPreferencesChangeListener extends EventListener{ + + void onDragPreferencesChange(@NotNull DragButtonCalibrationActivity.Preferences preferences ); +} diff --git a/src/main/java/org/solovyev/android/view/DragPreferencesChangeListenerRegister.java b/src/main/java/org/solovyev/android/view/DragPreferencesChangeListenerRegister.java new file mode 100644 index 00000000..0451003a --- /dev/null +++ b/src/main/java/org/solovyev/android/view/DragPreferencesChangeListenerRegister.java @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2009-2011. Created by serso aka se.solovyev. + * For more information, please, contact se.solovyev@gmail.com + */ + +package org.solovyev.android.view; + +import org.solovyev.common.utils.Announcer; + +/** + * User: serso + * Date: 9/18/11 + * Time: 8:53 PM + */ +public class DragPreferencesChangeListenerRegister extends Announcer { + + public DragPreferencesChangeListenerRegister() { + super(DragPreferencesChangeListener.class); + } +} diff --git a/src/main/java/org/solovyev/android/view/NumberPicker.java b/src/main/java/org/solovyev/android/view/NumberPicker.java new file mode 100644 index 00000000..071b14a6 --- /dev/null +++ b/src/main/java/org/solovyev/android/view/NumberPicker.java @@ -0,0 +1,538 @@ +/* + * Copyright (c) 2009-2011. Created by serso aka se.solovyev. + * For more information, please, contact se.solovyev@gmail.com + * or visit http://se.solovyev.org + */ + +package org.solovyev.android.view; + +/** + * User: serso + * Date: 9/18/11 + * Time: 10:03 PM + */ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +import android.content.Context; +import android.os.Handler; +import android.text.InputFilter; +import android.text.InputType; +import android.text.Spanned; +import android.text.method.NumberKeyListener; +import android.util.AttributeSet; +import android.view.LayoutInflater; +import android.view.View; +import android.widget.EditText; +import android.widget.LinearLayout; +import android.widget.TextView; +import org.solovyev.android.calculator.R; + +/** + * A view for selecting a number + * + * For a dialog using this view, see {@link android.app.TimePickerDialog}. + * @hide + */ +public class NumberPicker extends LinearLayout { + + /** + * The callback interface used to indicate the number value has been adjusted. + */ + public interface OnChangedListener { + /** + * @param picker The NumberPicker associated with this listener. + * @param oldVal The previous value. + * @param newVal The new value. + */ + void onChanged(NumberPicker picker, int oldVal, int newVal); + } + + /** + * Interface used to format the number into a string for presentation + */ + public interface Formatter { + String toString(int value); + } + + /* + * Use a custom NumberPicker formatting callback to use two-digit + * minutes strings like "01". Keeping a static formatter etc. is the + * most efficient way to do this; it avoids creating temporary objects + * on every call to format(). + */ + public static final NumberPicker.Formatter TWO_DIGIT_FORMATTER = + new NumberPicker.Formatter() { + final StringBuilder mBuilder = new StringBuilder(); + final java.util.Formatter mFmt = new java.util.Formatter( + mBuilder, java.util.Locale.US); + final Object[] mArgs = new Object[1]; + public String toString(int value) { + mArgs[0] = value; + mBuilder.delete(0, mBuilder.length()); + mFmt.format("%02d", mArgs); + return mFmt.toString(); + } + }; + + private final Handler mHandler; + private final Runnable mRunnable = new Runnable() { + public void run() { + if (mIncrement) { + changeCurrent(mCurrent + 1); + mHandler.postDelayed(this, mSpeed); + } else if (mDecrement) { + changeCurrent(mCurrent - 1); + mHandler.postDelayed(this, mSpeed); + } + } + }; + + private final EditText mText; + private final InputFilter mNumberInputFilter; + + private String[] mDisplayedValues; + + /** + * Lower value of the range of numbers allowed for the NumberPicker + */ + private int mStart; + + /** + * Upper value of the range of numbers allowed for the NumberPicker + */ + private int mEnd; + + /** + * Current value of this NumberPicker + */ + private int mCurrent; + + /** + * Previous value of this NumberPicker. + */ + private int mPrevious; + private OnChangedListener mListener; + private Formatter mFormatter; + private long mSpeed = 300; + + private boolean mIncrement; + private boolean mDecrement; + + /** + * Create a new number picker + * @param context the application environment + */ + public NumberPicker(Context context) { + this(context, null); + } + + /** + * Create a new number picker + * @param context the application environment + * @param attrs a collection of attributes + */ + public NumberPicker(Context context, AttributeSet attrs) { + super(context, attrs); + setOrientation(VERTICAL); + LayoutInflater inflater = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE); + inflater.inflate(R.layout.number_picker, this, true); + mHandler = new Handler(); + + OnClickListener clickListener = new OnClickListener() { + public void onClick(View v) { + validateInput(mText); + if (!mText.hasFocus()) mText.requestFocus(); + + // now perform the increment/decrement + if (R.id.increment == v.getId()) { + changeCurrent(mCurrent + 1); + } else if (R.id.decrement == v.getId()) { + changeCurrent(mCurrent - 1); + } + } + }; + + OnFocusChangeListener focusListener = new OnFocusChangeListener() { + public void onFocusChange(View v, boolean hasFocus) { + + /* When focus is lost check that the text field + * has valid values. + */ + if (!hasFocus) { + validateInput(v); + } + } + }; + + OnLongClickListener longClickListener = new OnLongClickListener() { + /** + * We start the long click here but rely on the {@link NumberPickerButton} + * to inform us when the long click has ended. + */ + public boolean onLongClick(View v) { + /* The text view may still have focus so clear it's focus which will + * trigger the on focus changed and any typed values to be pulled. + */ + mText.clearFocus(); + + if (R.id.increment == v.getId()) { + mIncrement = true; + mHandler.post(mRunnable); + } else if (R.id.decrement == v.getId()) { + mDecrement = true; + mHandler.post(mRunnable); + } + return true; + } + }; + + InputFilter inputFilter = new NumberPickerInputFilter(); + mNumberInputFilter = new NumberRangeKeyListener(); + mIncrementButton = (NumberPickerButton) findViewById(R.id.increment); + mIncrementButton.setOnClickListener(clickListener); + mIncrementButton.setOnLongClickListener(longClickListener); + mIncrementButton.setNumberPicker(this); + + mDecrementButton = (NumberPickerButton) findViewById(R.id.decrement); + mDecrementButton.setOnClickListener(clickListener); + mDecrementButton.setOnLongClickListener(longClickListener); + mDecrementButton.setNumberPicker(this); + + mText = (EditText) findViewById(R.id.timepicker_input); + mText.setOnFocusChangeListener(focusListener); + mText.setFilters(new InputFilter[] {inputFilter}); + mText.setRawInputType(InputType.TYPE_CLASS_NUMBER); + + if (!isEnabled()) { + setEnabled(false); + } + } + + /** + * Set the enabled state of this view. The interpretation of the enabled + * state varies by subclass. + * + * @param enabled True if this view is enabled, false otherwise. + */ + @Override + public void setEnabled(boolean enabled) { + super.setEnabled(enabled); + mIncrementButton.setEnabled(enabled); + mDecrementButton.setEnabled(enabled); + mText.setEnabled(enabled); + } + + /** + * Set the callback that indicates the number has been adjusted by the user. + * @param listener the callback, should not be null. + */ + public void setOnChangeListener(OnChangedListener listener) { + mListener = listener; + } + + /** + * Set the formatter that will be used to format the number for presentation + * @param formatter the formatter object. If formatter is null, String.valueOf() + * will be used + */ + public void setFormatter(Formatter formatter) { + mFormatter = formatter; + } + + /** + * Set the range of numbers allowed for the number picker. The current + * value will be automatically set to the start. + * + * @param start the start of the range (inclusive) + * @param end the end of the range (inclusive) + */ + public void setRange(int start, int end) { + setRange(start, end, null/*displayedValues*/); + } + + /** + * Set the range of numbers allowed for the number picker. The current + * value will be automatically set to the start. Also provide a mapping + * for values used to display to the user. + * + * @param start the start of the range (inclusive) + * @param end the end of the range (inclusive) + * @param displayedValues the values displayed to the user. + */ + public void setRange(int start, int end, String[] displayedValues) { + mDisplayedValues = displayedValues; + mStart = start; + mEnd = end; + mCurrent = start; + updateView(); + + if (displayedValues != null) { + // Allow text entry rather than strictly numeric entry. + mText.setRawInputType(InputType.TYPE_CLASS_TEXT | + InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS); + } + } + + /** + * Set the current value for the number picker. + * + * @param current the current value the start of the range (inclusive) + * @throws IllegalArgumentException when current is not within the range + * of of the number picker + */ + public void setCurrent(int current) { + if (current < mStart || current > mEnd) { + throw new IllegalArgumentException( + "current should be >= start and <= end"); + } + mCurrent = current; + updateView(); + } + + /** + * Sets the speed at which the numbers will scroll when the +/- + * buttons are longpressed + * + * @param speed The speed (in milliseconds) at which the numbers will scroll + * default 300ms + */ + public void setSpeed(long speed) { + mSpeed = speed; + } + + private String formatNumber(int value) { + return (mFormatter != null) + ? mFormatter.toString(value) + : String.valueOf(value); + } + + /** + * Sets the current value of this NumberPicker, and sets mPrevious to the previous + * value. If current is greater than mEnd less than mStart, the value of mCurrent + * is wrapped around. + * + * Subclasses can override this to change the wrapping behavior + * + * @param current the new value of the NumberPicker + */ + protected void changeCurrent(int current) { + // Wrap around the values if we go past the start or end + if (current > mEnd) { + current = mStart; + } else if (current < mStart) { + current = mEnd; + } + mPrevious = mCurrent; + mCurrent = current; + notifyChange(); + updateView(); + } + + /** + * Notifies the listener, if registered, of a change of the value of this + * NumberPicker. + */ + private void notifyChange() { + if (mListener != null) { + mListener.onChanged(this, mPrevious, mCurrent); + } + } + + /** + * Updates the view of this NumberPicker. If displayValues were specified + * in {@link #setRange}, the string corresponding to the index specified by + * the current value will be returned. Otherwise, the formatter specified + * in will be used to format the number. + */ + private void updateView() { + /* If we don't have displayed values then use the + * current number else find the correct value in the + * displayed values for the current number. + */ + if (mDisplayedValues == null) { + mText.setText(formatNumber(mCurrent)); + } else { + mText.setText(mDisplayedValues[mCurrent - mStart]); + } + mText.setSelection(mText.getText().length()); + } + + private void validateCurrentView(CharSequence str) { + int val = getSelectedPos(str.toString()); + if ((val >= mStart) && (val <= mEnd)) { + if (mCurrent != val) { + mPrevious = mCurrent; + mCurrent = val; + notifyChange(); + } + } + updateView(); + } + + private void validateInput(View v) { + String str = String.valueOf(((TextView) v).getText()); + if ("".equals(str)) { + + // Restore to the old value as we don't allow empty values + updateView(); + } else { + + // Check the new value and ensure it's in range + validateCurrentView(str); + } + } + + /** + * @hide + */ + public void cancelIncrement() { + mIncrement = false; + } + + /** + * @hide + */ + public void cancelDecrement() { + mDecrement = false; + } + + private static final char[] DIGIT_CHARACTERS = new char[] { + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' + }; + + private NumberPickerButton mIncrementButton; + private NumberPickerButton mDecrementButton; + + private class NumberPickerInputFilter implements InputFilter { + public CharSequence filter(CharSequence source, int start, int end, + Spanned dest, int dstart, int dend) { + if (mDisplayedValues == null) { + return mNumberInputFilter.filter(source, start, end, dest, dstart, dend); + } + CharSequence filtered = String.valueOf(source.subSequence(start, end)); + String result = String.valueOf(dest.subSequence(0, dstart)) + + filtered + + dest.subSequence(dend, dest.length()); + String str = String.valueOf(result).toLowerCase(); + for (String val : mDisplayedValues) { + val = val.toLowerCase(); + if (val.startsWith(str)) { + return filtered; + } + } + return ""; + } + } + + private class NumberRangeKeyListener extends NumberKeyListener { + + // XXX This doesn't allow for range limits when controlled by a + // soft input method! + public int getInputType() { + return InputType.TYPE_CLASS_NUMBER; + } + + @Override + protected char[] getAcceptedChars() { + return DIGIT_CHARACTERS; + } + + @Override + public CharSequence filter(CharSequence source, int start, int end, + Spanned dest, int dstart, int dend) { + + CharSequence filtered = super.filter(source, start, end, dest, dstart, dend); + if (filtered == null) { + filtered = source.subSequence(start, end); + } + + String result = String.valueOf(dest.subSequence(0, dstart)) + + filtered + + dest.subSequence(dend, dest.length()); + + if ("".equals(result)) { + return result; + } + int val = getSelectedPos(result); + + /* Ensure the user can't type in a value greater + * than the max allowed. We have to allow less than min + * as the user might want to delete some numbers + * and then type a new number. + */ + if (val > mEnd) { + return ""; + } else { + return filtered; + } + } + } + + private int getSelectedPos(String str) { + if (mDisplayedValues == null) { + try { + return Integer.parseInt(str); + } catch (NumberFormatException e) { + /* Ignore as if it's not a number we don't care */ + } + } else { + for (int i = 0; i < mDisplayedValues.length; i++) { + /* Don't force the user to type in jan when ja will do */ + str = str.toLowerCase(); + if (mDisplayedValues[i].toLowerCase().startsWith(str)) { + return mStart + i; + } + } + + /* The user might have typed in a number into the month field i.e. + * 10 instead of OCT so support that too. + */ + try { + return Integer.parseInt(str); + } catch (NumberFormatException e) { + + /* Ignore as if it's not a number we don't care */ + } + } + return mStart; + } + + /** + * Returns the current value of the NumberPicker + * @return the current value. + */ + public int getCurrent() { + return mCurrent; + } + + /** + * Returns the upper value of the range of the NumberPicker + * @return the uppper number of the range. + */ + protected int getEndRange() { + return mEnd; + } + + /** + * Returns the lower value of the range of the NumberPicker + * @return the lower number of the range. + */ + protected int getBeginRange() { + return mStart; + } +} \ No newline at end of file diff --git a/src/main/java/org/solovyev/android/view/NumberPickerButton.java b/src/main/java/org/solovyev/android/view/NumberPickerButton.java new file mode 100644 index 00000000..7207a01b --- /dev/null +++ b/src/main/java/org/solovyev/android/view/NumberPickerButton.java @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2009-2011. Created by serso aka se.solovyev. + * For more information, please, contact se.solovyev@gmail.com + * or visit http://se.solovyev.org + */ + +package org.solovyev.android.view; + +/** + * User: serso + * Date: 9/18/11 + * Time: 10:04 PM + */ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +import android.content.Context; +import android.util.AttributeSet; +import android.view.KeyEvent; +import android.view.MotionEvent; +import android.widget.ImageButton; +import org.solovyev.android.calculator.R; + +/** + * This class exists purely to cancel long click events, that got + * started in NumberPicker + */ +class NumberPickerButton extends ImageButton { + + private NumberPicker mNumberPicker; + + public NumberPickerButton(Context context, AttributeSet attrs, + int defStyle) { + super(context, attrs, defStyle); + } + + public NumberPickerButton(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public NumberPickerButton(Context context) { + super(context); + } + + public void setNumberPicker(NumberPicker picker) { + mNumberPicker = picker; + } + + @Override + public boolean onTouchEvent(MotionEvent event) { + cancelLongpressIfRequired(event); + return super.onTouchEvent(event); + } + + @Override + public boolean onTrackballEvent(MotionEvent event) { + cancelLongpressIfRequired(event); + return super.onTrackballEvent(event); + } + + @Override + public boolean onKeyUp(int keyCode, KeyEvent event) { + if ((keyCode == KeyEvent.KEYCODE_DPAD_CENTER) + || (keyCode == KeyEvent.KEYCODE_ENTER)) { + cancelLongpress(); + } + return super.onKeyUp(keyCode, event); + } + + private void cancelLongpressIfRequired(MotionEvent event) { + if ((event.getAction() == MotionEvent.ACTION_CANCEL) + || (event.getAction() == MotionEvent.ACTION_UP)) { + cancelLongpress(); + } + } + + private void cancelLongpress() { + if (R.id.increment == getId()) { + mNumberPicker.cancelIncrement(); + } else if (R.id.decrement == getId()) { + mNumberPicker.cancelDecrement(); + } + } + + public void onWindowFocusChanged(boolean hasWindowFocus) { + super.onWindowFocusChanged(hasWindowFocus); + if (!hasWindowFocus) { + cancelLongpress(); + } + } + +} \ No newline at end of file diff --git a/src/main/java/org/solovyev/android/view/SeekBarPreference.java b/src/main/java/org/solovyev/android/view/SeekBarPreference.java new file mode 100644 index 00000000..01b6cc0a --- /dev/null +++ b/src/main/java/org/solovyev/android/view/SeekBarPreference.java @@ -0,0 +1,134 @@ +/* + * Copyright (c) 2009-2011. Created by serso aka se.solovyev. + * For more information, please, contact se.solovyev@gmail.com + * or visit http://se.solovyev.org + */ + +package org.solovyev.android.view; + +import android.preference.DialogPreference; +import android.content.Context; +import android.util.AttributeSet; +import android.view.Gravity; +import android.view.View; +import android.widget.SeekBar; +import android.widget.TextView; +import android.widget.LinearLayout; +import org.jetbrains.annotations.NotNull; + + +/* The following code was written by Matthew Wiggins + * and is released under the APACHE 2.0 license + * + * http://www.apache.org/licenses/LICENSE-2.0 + */ + +public class SeekBarPreference extends DialogPreference implements SeekBar.OnSeekBarChangeListener { + + private static final String androidns = "http://schemas.android.com/apk/res/android"; + + @NotNull + private SeekBar seekBar; + + @NotNull + private TextView splashText, valueText; + + @NotNull + private final Context context; + + private String dialogMessage, suffix; + + private int defaultValue, max, value = 0; + + public SeekBarPreference(Context context, AttributeSet attrs) { + super(context, attrs); + this.context = context; + + dialogMessage = attrs.getAttributeValue(androidns, "dialogMessage"); + suffix = attrs.getAttributeValue(androidns, "text"); + defaultValue = attrs.getAttributeIntValue(androidns, "defaultValue", 0); + max = attrs.getAttributeIntValue(androidns, "max", 100); + + } + + @Override + protected View onCreateDialogView() { + LinearLayout.LayoutParams params; + LinearLayout layout = new LinearLayout(context); + layout.setOrientation(LinearLayout.VERTICAL); + layout.setPadding(6, 6, 6, 6); + + splashText = new TextView(context); + if (dialogMessage != null) + splashText.setText(dialogMessage); + layout.addView(splashText); + + valueText = new TextView(context); + valueText.setGravity(Gravity.CENTER_HORIZONTAL); + valueText.setTextSize(32); + params = new LinearLayout.LayoutParams( + LinearLayout.LayoutParams.FILL_PARENT, + LinearLayout.LayoutParams.WRAP_CONTENT); + layout.addView(valueText, params); + + seekBar = new SeekBar(context); + seekBar.setOnSeekBarChangeListener(this); + layout.addView(seekBar, new LinearLayout.LayoutParams(LinearLayout.LayoutParams.FILL_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT)); + + if (shouldPersist()) + value = getPersistedInt(defaultValue); + + seekBar.setMax(max); + seekBar.setProgress(value); + return layout; + } + + @Override + protected void onBindDialogView(View v) { + super.onBindDialogView(v); + seekBar.setMax(max); + seekBar.setProgress(value); + } + + @Override + protected void onSetInitialValue(boolean restore, Object defaultValue) { + super.onSetInitialValue(restore, defaultValue); + if (restore) + value = shouldPersist() ? getPersistedInt(this.defaultValue) : 0; + else + value = (Integer) defaultValue; + } + + public void onProgressChanged(SeekBar seek, int value, boolean fromTouch) { + String t = String.valueOf(value); + valueText.setText(suffix == null ? t : t.concat(suffix)); + if (shouldPersist()) + persistInt(value); + callChangeListener(new Integer(value)); + } + + public void onStartTrackingTouch(SeekBar seek) { + } + + public void onStopTrackingTouch(SeekBar seek) { + } + + public void setMax(int max) { + this.max = max; + } + + public int getMax() { + return max; + } + + public void setProgress(int progress) { + value = progress; + if (seekBar != null) + seekBar.setProgress(progress); + } + + public int getProgress() { + return value; + } +} + diff --git a/src/main/java/org/solovyev/android/view/SimpleOnDragListener.java b/src/main/java/org/solovyev/android/view/SimpleOnDragListener.java index 90ff38e1..2f8ebf3b 100644 --- a/src/main/java/org/solovyev/android/view/SimpleOnDragListener.java +++ b/src/main/java/org/solovyev/android/view/SimpleOnDragListener.java @@ -15,7 +15,7 @@ import org.solovyev.common.utils.Point2d; import java.util.Map; -public class SimpleOnDragListener implements OnDragListener { +public class SimpleOnDragListener implements OnDragListener, DragPreferencesChangeListener { @NotNull public static final Point2d axis = new Point2d(0, 1); @@ -35,10 +35,6 @@ public class SimpleOnDragListener implements OnDragListener { this.preferences = preferences; } - public void setPreferences(@NotNull DragButtonCalibrationActivity.Preferences preferences) { - this.preferences = preferences; - } - @Override public boolean onDrag(@NotNull DragButton dragButton, @NotNull DragEvent event) { boolean result = false; @@ -119,6 +115,11 @@ public class SimpleOnDragListener implements OnDragListener { this.dragProcessor = dragProcessor; } + @Override + public void onDragPreferencesChange(@NotNull DragButtonCalibrationActivity.Preferences preferences) { + this.preferences = preferences; + } + public interface DragProcessor { boolean processDragEvent(@NotNull DragDirection dragDirection, @NotNull DragButton dragButton, @NotNull Point2d startPoint2d, @NotNull MotionEvent motionEvent);