From cbba46a6d54f390e96fa73c782ce5f36edc8aa0c Mon Sep 17 00:00:00 2001 From: Peter Osterlund Date: Sun, 10 Jan 2016 14:08:54 +0100 Subject: [PATCH] DroidFish: Use the TourGuide library to display usage hints the first time the program is started. --- DroidFish/res/drawable-hdpi/fab_bg_mini.png | Bin 0 -> 8016 bytes DroidFish/res/drawable-hdpi/fab_bg_normal.png | Bin 0 -> 9912 bytes DroidFish/res/drawable-mdpi/fab_bg_mini.png | Bin 0 -> 5893 bytes DroidFish/res/drawable-mdpi/fab_bg_normal.png | Bin 0 -> 7058 bytes .../res/drawable-xhdpi/drop_shadow.9.png | Bin 0 -> 690 bytes DroidFish/res/drawable-xhdpi/fab_bg_mini.png | Bin 0 -> 10146 bytes .../res/drawable-xhdpi/fab_bg_normal.png | Bin 0 -> 13172 bytes DroidFish/res/drawable-xxhdpi/fab_bg_mini.png | Bin 0 -> 6507 bytes .../res/drawable-xxhdpi/fab_bg_normal.png | Bin 0 -> 8548 bytes .../res/drawable-xxxhdpi/fab_bg_mini.png | Bin 0 -> 8966 bytes .../res/drawable-xxxhdpi/fab_bg_normal.png | Bin 0 -> 12045 bytes DroidFish/res/layout-land/main.xml | 1 + DroidFish/res/layout/main.xml | 80 ++- DroidFish/res/layout/main_left_handed.xml | 1 + DroidFish/res/layout/tooltip.xml | 40 ++ DroidFish/res/raw/about.html | 6 + DroidFish/res/values-v14/colors.xml | 7 + DroidFish/res/values/attrs.xml | 39 ++ DroidFish/res/values/colors.xml | 7 + DroidFish/res/values/dimens.xml | 17 + DroidFish/res/values/ids.xml | 5 + DroidFish/res/values/strings.xml | 14 + DroidFish/res/xml/preferences.xml | 6 + .../FloatingActionButton.java | 431 ++++++++++++ .../src/org/petero/droidfish/DroidFish.java | 115 +++- .../tourguide/FrameLayoutWithHole.java | 311 +++++++++ .../src/tourguide/tourguide/Overlay.java | 83 +++ .../src/tourguide/tourguide/Pointer.java | 41 ++ .../src/tourguide/tourguide/Sequence.java | 219 ++++++ .../src/tourguide/tourguide/ToolTip.java | 119 ++++ .../src/tourguide/tourguide/TourGuide.java | 633 ++++++++++++++++++ 31 files changed, 2136 insertions(+), 39 deletions(-) create mode 100644 DroidFish/res/drawable-hdpi/fab_bg_mini.png create mode 100644 DroidFish/res/drawable-hdpi/fab_bg_normal.png create mode 100644 DroidFish/res/drawable-mdpi/fab_bg_mini.png create mode 100644 DroidFish/res/drawable-mdpi/fab_bg_normal.png create mode 100644 DroidFish/res/drawable-xhdpi/drop_shadow.9.png create mode 100644 DroidFish/res/drawable-xhdpi/fab_bg_mini.png create mode 100644 DroidFish/res/drawable-xhdpi/fab_bg_normal.png create mode 100644 DroidFish/res/drawable-xxhdpi/fab_bg_mini.png create mode 100644 DroidFish/res/drawable-xxhdpi/fab_bg_normal.png create mode 100644 DroidFish/res/drawable-xxxhdpi/fab_bg_mini.png create mode 100644 DroidFish/res/drawable-xxxhdpi/fab_bg_normal.png create mode 100644 DroidFish/res/layout/tooltip.xml create mode 100644 DroidFish/res/values-v14/colors.xml create mode 100644 DroidFish/res/values/attrs.xml create mode 100644 DroidFish/res/values/colors.xml create mode 100644 DroidFish/res/values/dimens.xml create mode 100644 DroidFish/res/values/ids.xml create mode 100644 DroidFish/src/net/i2p/android/ext/floatingactionbutton/FloatingActionButton.java create mode 100644 DroidFish/src/tourguide/tourguide/FrameLayoutWithHole.java create mode 100644 DroidFish/src/tourguide/tourguide/Overlay.java create mode 100644 DroidFish/src/tourguide/tourguide/Pointer.java create mode 100644 DroidFish/src/tourguide/tourguide/Sequence.java create mode 100644 DroidFish/src/tourguide/tourguide/ToolTip.java create mode 100644 DroidFish/src/tourguide/tourguide/TourGuide.java diff --git a/DroidFish/res/drawable-hdpi/fab_bg_mini.png b/DroidFish/res/drawable-hdpi/fab_bg_mini.png new file mode 100644 index 0000000000000000000000000000000000000000..a5984b0591d66eb96ff8f2b06b7f5c2a7093cc02 GIT binary patch literal 8016 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�% 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 literal 0 HcmV?d00001 diff --git a/DroidFish/res/drawable-hdpi/fab_bg_normal.png b/DroidFish/res/drawable-hdpi/fab_bg_normal.png new file mode 100644 index 0000000000000000000000000000000000000000..fa69e8ba8f826a2c661faba87f2e57bfa4be9962 GIT binary patch literal 9912 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 literal 0 HcmV?d00001 diff --git a/DroidFish/res/drawable-mdpi/fab_bg_mini.png b/DroidFish/res/drawable-mdpi/fab_bg_mini.png new file mode 100644 index 0000000000000000000000000000000000000000..c4105977c2cb1c5351efe88d79c6c61fcbf9cfc1 GIT binary patch literal 5893 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 literal 0 HcmV?d00001 diff --git a/DroidFish/res/drawable-mdpi/fab_bg_normal.png b/DroidFish/res/drawable-mdpi/fab_bg_normal.png new file mode 100644 index 0000000000000000000000000000000000000000..eafe4ba0336f335da420c3b1ca2309d8b39dd076 GIT binary patch literal 7058 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 Q_eY1ayoOwjjCt690WQe^e*gdg literal 0 HcmV?d00001 diff --git a/DroidFish/res/drawable-xhdpi/drop_shadow.9.png b/DroidFish/res/drawable-xhdpi/drop_shadow.9.png new file mode 100644 index 0000000000000000000000000000000000000000..38081a1044069f8a65a0f11f0dc1bfd960ea011a GIT binary patch literal 690 zcmV;j0!{siP)ub|s6o=C!P10s|-P~H&idMJPO+=h`hj*u<;2lN%q96Q+MG!>P|G?{4q5cj$ftNIG zO&R;tz>kFFJ?D8Zhn#af@BgoXw~JRLC*M&pLhhKn{a8rmG?c1RB*iGnDdu~`*wBF& z!dyrOFef!BmG(*VsEO>k!MBvLF}5RNE@Zhe8W-pkLK$^^EL}18cL5HGKMcP{gu( zWkIsJ3armIF0!g2P{2|xidIX4eurUrS9aUd30AdCG)6_BgeCXOf@JeZup7Fcl8&-{ z4JKa*YEA+A69mC?fo^O2&dPpWT4B;zKn#H_VvbD#dL_Gi0-fiaI!rn#0@VO*OaXc= zyZZuNu!&0DBG6#3K;8^A1I<9Y21@oCG|j*6rKJji&N=_O`Ej7B{@${ACeRMaZ*9`w zJLaS1llRgG>7(>X`Ye5szB&5JI~(^B`sTSlRclErn6<2Wdz_v)vUzqg#)ZSUb)BUovMbhh1Gt`}$ z!OK@cHMXzC%r>Rd%=Vg`-ZH2;mu^y&Tm-UV6%@8vAcT34Ti2yTUmMsuTXNE%maa+J zTfEyO05V7uACXhnc}10KLfN%|VHYQMZmT$5BWQYj>rnYLb7iqVs`hqaDO<3};kH=i z3TpFiiAgm$m!d*{WXqDZD+$JPL{Y=(BHT`*&e*7I>r&JsxqVVRp*Qj;HDhiYSyp4O Yeys}Ke+Mp6rG9{b?FC`0{oo|08bEfaAZfdE3T(gZ{|lK!-A8hKfVXdwFzev!yz~gzkq{5O6=$DaKwnbESqzpMY@22QIjJN%IRP*1THJLn zZ;<$^Jb{KiAJoo)$CKHgqq4ypHpdmrop>-%Dym7Sm`oX?T=J64q8t`3VzNVQWeo9FZ1 zJt?YAgx#j>X9|^FC|azEBXu4FLH#MyEN~14=tTbPU*OltV1oQ5 z-@zxtGfBc(3L(^e7Fc?nf~-rhu7OrxVCDimrx4`7a(#KLgP_ra{sk4yUqT2aq*sOo zc0Irn>Fb|&xOtz;;_wPE(}Wk^5o#fMh)Ezey5x-}3Q@RCD0He7N zf0OVJ3`4MfUjmZQFcSQIb|*g6&Xn)TWWT;ewGg%Y3SFs=6%xE|=Lm_JF&ug+}wWMVd4fg09G1SQ*ib{ptp> z2HG_#weU-Z$NWSXLp=mrL|=ScKIpL7aaJKTV=hLu`yYKr-vpk!chIfloPEXYb^2rE zP2Pp#N90Gc5lcGQO%sQ#g<$;^lRTM>%7#ky9RO($=_v@Sk3%V-M8TONnXA3=MS{mdU_*P6;ScoOK3m#?@4hF7B_dBnUO{q<_ zcRubA?ErS_N0s09u^8O4D-#q(B}BDH#a?pkwoYzOu5wjd>b-Q(Cf(85(Rn9PB$*|_ zCdHP?s@c!u%x9I^mMP4!SOKjJt(L7G=9$W)CyOWDvM00jIbME0h91_mg?qN#TU~!Y z1RW*-(g9rn0)QGIg=3JDAl)u~Bz-4+k7KC8LJPGKL2E~AuThUBDeZJz)r`76k~g=q z>ZF))TA*O998_ssQm<*AFJC`_tsBpz&HcGqr&+~KSV4walUcu7=`2@3DW@{0pj)C_ z+i$dIifqLo@^eOSNp3`Lw_&zn{-!Zy8KiugE?+C3e~`zx$syugU|Snl6P_g%FBLr^ zr?ND+u4>RW*)WM_y=wvFDCcY+%B%Sa23LqLeB=H8Y1lj8Bjb)96%?r%nT5B8r_XT0 zpu%{>KnkV>ztOa9IIOw)1Ek^IT_S^QaErFJwS{~>zWiTNU zkwb~a344G^RK2j2Y7K}+b#wfU(V5j*u>hYyxInZ3c9&~cxexQL;S=1G>;2m0;-9_y z#Rmlh9)t?yeUt(OaRhZl0aP~R_itPfzW2iRwtppMp3$e$nZtt+XiYXe1^o(%&u_J3KQi5gCmqgXbnbA~7H~FCi!9C6*#4lcLE0 zuKz6_r4>b11i~XF#PyHWhKG(f9eS{#S1l0vii>CDJh8s&&N#ROu z!zp}j{N+-oe-ZGzG%+qQpO(FJy7Y%-P{VD*tT44vPCsFXyQ>MrH6qmCkCbyqj)s+vauGQLHVQt>p5bG4ZizQ^F0qi1j(8Z%Q>? z1{U>ayMw~5!b+Q+j1$@zHUl=eHYaOl^_5nuDHM9s*G6Mo5nBq>KjV)!z&;vxYva(M zlg>hD;e*>RS0uNeYX@zYnrTL0z2&K@U%Hh$;}>}s&Fvb;UFwF~4hEQ(u_mz%Y$bJ_ z($!WH^Tme+hXH`t^xe)wujZ4im7&$e?hRYNS=ea!0Ym{39HI`t;TuKk%Fng~hCeqe zzE2+I9kz9w`|10Mo=BcUCMAL;3Wxe)UFP}c7P(8f6S?}L;|FWE9fs_meqD=rVV55$ zvgfznBJhy&o^K7^lAdLweOmU2@F_c>dCGw{O=(4FZ&tz5ARIQH%q*Uvj zw>p-tc0N3+rIedj@#^yXWchTSggiwrkhEtf>mU1C9yFfWb{>4Ge@fo2jOlvgvv;ri zz`PD^^Vx}c$s&Z0e0zV&)aCV}_Y-vBv;H)VSc<79IPS^wWbtHtyq!mbqCl5jCRq72 zf4gW_+Ie;SZP8$|KFmBeq(y}JCF!nO;rpA((n)&p2Ju6QN{Pv+yQnqK+N*&-V^Jxy z+rizm&rLT;)8#q7ZV#R2MkD=ytf!CC1f|zYx_IvwptgG^`@d)1EYFIHBi75i1fc;> zxevxyLy&`Ggq#rcCa(UtZBLFrVD{UaPjI&W2>}wl;Q7eC{BMe<1i? z>wn426lDK^I9r1#H02b@MC=_+$vBxfm{=$TP{_#0J~^70@hOXn|BLXE`OilFyB$$eCu2v;FV2?s zc4U9sH8iq!aRyOP{1x;+$G`e%`{jQG**X17t5^M)-3`Amvof(T|6febmS+Fkz5j>n z?~{MB{X?sN3j6eG8a@$6Q$uHaM^$@!8-dS%ZAT_$YiMq&Y-(uY@rn6A5&zrMPk)=_ z11p<4+1t4M)kuJihvyUX|402#4h2U`(^u>KgM;;7_`4|lPFHO?Wb%MkNbp1Uo0r_va#ZkbeAsJ9lKJq#8ZhUKd;{e5^Ju$mT~gSTT6LOTB1CJ~x~OV4y;u!K zUZU7oKOdCi!&Xqwn*;s#?;{pZ^G#GVM$X?-&&<$pB-{+rPiRGH}-v%Ex zJeU%%HcqBDSn%7YOW_u7|+tanehMR1kXH8|&-eA^Q@w z%rQe84<)&t-_f~M$xn7O>`!~nmnVH)l_P{QJ4TpwaV~^}M;~I`Iw*G@MihM6kf*!v z=c_5CfaTe)Yqe26s8y6gsOv3&f+r(VA~!9PMYM{j*jNp>)1PTbx2JC1{J9rdDBCE+ zXI>i*p4F|p=}DoOq}jI*8!x_(x@Vq?4tEOiu|CJfYx(*4Yvt;dhdF{%oxQwT>#o1e<>+k;F@Mwjkl^#V@m{8r{A4!BOHWY`Bl)l}MQ^3na<*v2=VndA z*dw|6L&<`GO%RU}2v_^7A9>jFPLC`Z3ws-`Cyu&_VUqJ1;l}Nt?}30#aIb>j{Vd`0 z?U3NgG2%+Z$7#-Xg}E1c6z% z!C&X@-WG!$RMhbY0TL9jW`)93@7cUIpiRw{551VaEcc7u&n{z5lOVUv1OBAao`@_zsVq;N^yeIX-?3+Spn>GKY=U#pnR8W&^b(Hjm#VWxt@Q{ z?_ss_<@u(&xejeK1@+q=#Dgosb{hZQ`DV?{zWucAXkUZbr|7JXmy7@vvBbP0Ws~}| z6+xPVI@>J=Jv&8O61@K77C!f3*Pr-6M9eCUpMA=02BK|BM-?X>KKJ6J2jv%E-a$i` zM^m@hbf|D0FWXFVu-=bc&0wc2a?o?ecWHDUs_8gi*>80DtdTzd2|anZIYTU`_GgeVZ+ z2zqVVZ2skP_eWgo?z-36T5iUJrA)=6d19(6-w(|X3FGro=XsG#bd*U+qs8RRy^%3< z;wE+DP?6o0Mr(4X{bIqV1B?I&lQ+djX@1H9V*}Z?`IV~AXsqgk4DCEB_?)Fb;+DBN zI5?gip>zl^sM*T4C z&o$R-Pq_7|9%qCD8=|UCez&OSd$&V0%+Q_bs#(eNCP3v}_@IIG=fE;p+RPQZ3!3e9 z+L}r&1rJtcPdFE4Esatmq+(WDNk>)F4n+la%y?*xg;-SWF2&rPE<}$N;*5Q-E`e_KGLv|=3X#sv%Y=2ddmf8 z^znNHI-cYXNmA0pp33Bm2jyHnwsKllS6LzD&%6Rg(80B5#l_c963?)cYSYd+w&#yP zT$`)<%{%Gs?95pehlA^#?jE(28~4$g;RF*Wj8$Kf~oxxYuy3AsMWn4NY{+42VX zv%*r^vdGdEYGe9p1aHa~CR3L-lGAIaq8E)TrIu+<_Qo;%W&aK?$F-|wxr`w5^(7TWiP207IP!-A8fI5dT z+a89GDW~78-Xp*bnr#n8E9m)L&+EJ+5ZNgEuo}jV`z}WbfOXhcyJb-vLPJAy?ozGa z{VaB1%rv)vj>VH~xR7A@p|(+udMVjrHRVV=Xt1woI!JVPcvw>HEMQLHDi;&|P>eJ= z-hq)e`tS#TjSe~s96ifJ5264IOh_M_VfvC?9#TRrX7hM}UWw=lO@@HtC7{#5YdccG2CcW)vKFP08UiNB-CM;L3AZ7spJl*EE#PT2yO}qMl9})GOb|TfT7MqFpLsQTuIMNn^1BJe#YJLig99 zdR_aP+}~B$$(#hWA?=$tltG>O$<+waA=rPoc3sAXf;5+&H9x1Fh?hyk5ar6MrL1jx zPS$t+1oVQf*2tar8UHvZ(F^zD>rp`G_T7~dk|VL5LeadT?M58zaXs5KzPp7IW1^T6 z$=;asH4DwAP$d5|F5O7k!zhoJMPU>Cn!~ zy$Qk$$OjrHB>Tp=-;<8(`-XZLOYQu8xd&Pi+g%8EN3Qv+C;rsA;QJBnT{_NLSwL1c z+;7^^be8DfKkkj{afL5W%`{9oZCP$goGgn!D%cRpQNp+yl)yC}6RkKe8x8Ve9ga4X zP8BMYp7(2c9Zi1Y$nCsFd__=XL$V7O*&swIPo2 z}NG;tH)%aPa!H2x+pul+*_9R?HGRdm{yV4K0NaR8ri@Vc`9xZ3;FDb<)RRi zBWZlEt&YnH?YlV@g>yIzwxbwXTy1qfY;gnNW@9|E?xwgmY~XE00fDf2SxphMkix~s z7aGC45qy^*hljpd8$AIty7r3M9!X7?!vx2*oZEgHJ%sPYIr8>#{*mF0RiX1zK`B@O zc3fL-gOiZ3*dq(={0x(rrY4R6{~E3HjhE*X#LBxgG}fy9kI-$WbeHiIu7G0RI!RV1 zFcB$xZP7!C$_mxk|i3J`#d@5npn7l zV3H|hP z*sK5I3mW3{z$U5K=Fhhg=t2IOs8`Iv9_g(r8vO&37(@3I8I>+M;3g|rczuM;^lrUt zVs9-hf>^OTF{2_y3h%(Of1fcK~Tlzwpru2 zO6A^qk+bkMm`M0Ha z4sIob4D(N_V#Fk~Ax|~g+<=Amv)P4k;mgQ)26<_`e>Zs^vS)8t`6H=z-IW<~%R!$dQJ$NN|~M!{+VRB?JYqw8~e5{wUnJr!2%; z-Ya1X+I`I#@~DvG1C+`*)3eVd4J{w!qktSvTlZ%6H>>@gjym}O+_SJ7UXYrfRD@pW z3|E-9>fz(x3B<9uSAq0aWR!N{O+HfW-_72mcqZf7^7X(5Y-CAkZ8T&|S>F@u-}d2S z9A-r`YT0E^IXd-^#au8Gjtq`-ZAjamNBcfs<_MN(oYSvdL}Drsc2gwxV;$vXz27EH z!1b91u6dAQX6$f;)R{JG+ZVYUsf9OO9gOv~|7Hf>XSYT(zPPryNp0(x-h1d~C41O!d&{ z5-0oeIl(ObEp81&H#2uIkz>`Ozs;h!fBD%^UHDUd^zw&h*t;+96&|t*p$=*KPJCO8d|44kHqHX1|A8=bO@sm{BPpXOPrQS%-o*5zIJ>#&7yVK88SrS{;@)39;y!{4V3UbgO&Iw= zP#)p|GRA+)439eC_R&VIMG>~5Co1qK=@*l{{$M~)RMH7m0#mCV8Q zYa?=!ht&XXJwgr67beCD6bdd z7M%S$^?nU)d5h9KRL{y}4=slsqJ4CX1&cl**h#g>eRe9eJ}KAZ&}c-ca>qIKY2|-}-^J0VZP!Bl>t99PX~-S!N}p9z|@{?an4{ zhuIY}`qPI~WAfJP_Y2J=b0m9uu+BkTc$YTO^~%su``66X;y&vFtgVYcF>zgbHZEY1 zhm47Mc%OkqBrWUQU?}FdFqH###9G6+S%@-ibpNjq@5EN)(}HaQI;Tolg|P8+2m}HU z+W$U6FK|B{yWa1YSBxIxZ3gqJ6*60SAd{JNrKVBGJ?%Ewqxk{bSVfHZ56uu@Qa4kdG|;9w|oJj~TL^Lr&ALLfx{vzImt;f0%`u z$uWO8ryEAV7rdw)_cX&p<_y2>^z=wh|71PLedN$2T_RPWv}zF+)f<#9X;x&*(O(3n zCp6-}rmn2o%kj(JK;^$S_ISy7&6Je+)_PbiR_Vs>BW@NaG2{OrT9F=eI?W7(FrKCB z%#z~l1u#8X2$qo+PA;_875T}#NmjGS?&C#)ks-WF0C*EI;o5Dr%FA@gE#P#zl4ypz z$_!nusvU(be3cInx2{Hx;+ yw+wza%TJbP=BKniV+edblB7={Z2JH{EGG6!Vm# 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 z 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 RMY#X~002ovPDHLkV1oBynxOyy literal 0 HcmV?d00001 diff --git a/DroidFish/res/drawable-xxhdpi/fab_bg_normal.png b/DroidFish/res/drawable-xxhdpi/fab_bg_normal.png new file mode 100644 index 0000000000000000000000000000000000000000..f1acbc04036d35337b39207b677ef499006847b9 GIT binary patch literal 8548 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 - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/DroidFish/res/raw/about.html b/DroidFish/res/raw/about.html index c0331f9..34e9e04 100644 --- a/DroidFish/res/raw/about.html +++ b/DroidFish/res/raw/about.html @@ -240,6 +240,12 @@

  • Syzygy tablebases probing code, Copyright © 2011-2013 Ronald de Man.
  • +
  • + TourGuide library, Copyright © 2015 Tan Jun Rong. +
  • +
  • + Floating action button library, Copyright © 2014 str4d and Jerzy Chalupski. +
  • Translations

    diff --git a/DroidFish/res/values-v14/colors.xml b/DroidFish/res/values-v14/colors.xml new file mode 100644 index 0000000..78e2bd8 --- /dev/null +++ b/DroidFish/res/values-v14/colors.xml @@ -0,0 +1,7 @@ + + + @android:color/holo_blue_dark + @android:color/holo_blue_light + @android:color/darker_gray + #FFFFFF + diff --git a/DroidFish/res/values/attrs.xml b/DroidFish/res/values/attrs.xml new file mode 100644 index 0000000..b9eaaf5 --- /dev/null +++ b/DroidFish/res/values/attrs.xml @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/DroidFish/res/values/colors.xml b/DroidFish/res/values/colors.xml new file mode 100644 index 0000000..fdae922 --- /dev/null +++ b/DroidFish/res/values/colors.xml @@ -0,0 +1,7 @@ + + + #ff0099cc + #ff33b5e5 + #aaa + #FFFFFF + diff --git a/DroidFish/res/values/dimens.xml b/DroidFish/res/values/dimens.xml new file mode 100644 index 0000000..a2b506e --- /dev/null +++ b/DroidFish/res/values/dimens.xml @@ -0,0 +1,17 @@ + + 56dp + 40dp + + 24dp + + 14dp + 2dp + + 3dp + 9dp + + 1dp + + 16dp + 8dp + diff --git a/DroidFish/res/values/ids.xml b/DroidFish/res/values/ids.xml new file mode 100644 index 0000000..9b7c8b2 --- /dev/null +++ b/DroidFish/res/values/ids.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/DroidFish/res/values/strings.xml b/DroidFish/res/values/strings.xml index c67b913..4fb7ef9 100644 --- a/DroidFish/res/values/strings.xml +++ b/DroidFish/res/values/strings.xml @@ -413,6 +413,20 @@ you are not actively using the program.\ Directory where Syzygy tablebases are installed. Leave blank to use default directory Syzygy Network Directory Directory for network engines where Syzygy tablebases are installed. + Startup Guide + Show the startup guide the next time the program is started. + Left Menu + To open the left menu, tap on the left side of the title bar or swipe from the left side of the screen towards the right side. + Right Menu + To open the right menu, tap on the right side of the title bar or swipe from the right side of the screen towards the left side. + Chess Board + Touch and hold the chess board to open the tools menu. + Buttons + Tap a button to invoke its action. Touch and hold a button to open a menu containing secondary actions. To configure button actions go to Left Menu > Settings > Behavior > Configure Buttons. + Move List + Tap a move in the move list to set the chess board to the corresponding position. Touch and hold the move list to open the Edit Game menu. + Analysis information + When the engine is thinking, touch and hold the analysis information to open the Analysis menu. @string/prefs_custom_button_1 @string/prefs_custom_button_2 @string/prefs_custom_button_3 diff --git a/DroidFish/res/xml/preferences.xml b/DroidFish/res/xml/preferences.xml index 26931b9..0cdc938 100644 --- a/DroidFish/res/xml/preferences.xml +++ b/DroidFish/res/xml/preferences.xml @@ -518,6 +518,12 @@ + + = VERSION_CODES.JELLY_BEAN) { + setBackground(drawable); + } else { + setBackgroundDrawable(drawable); + } + } + + @Override + public void setVisibility(int visibility) { + TextView label = getLabelView(); + if (label != null) { + label.setVisibility(visibility); + } + + super.setVisibility(visibility); + } +} diff --git a/DroidFish/src/org/petero/droidfish/DroidFish.java b/DroidFish/src/org/petero/droidfish/DroidFish.java index 40b0905..5973f3e 100644 --- a/DroidFish/src/org/petero/droidfish/DroidFish.java +++ b/DroidFish/src/org/petero/droidfish/DroidFish.java @@ -60,6 +60,12 @@ import org.petero.droidfish.gamelogic.TimeControlData; import org.petero.droidfish.tb.Probe; import org.petero.droidfish.tb.ProbeResult; +import tourguide.tourguide.Overlay; +import tourguide.tourguide.Pointer; +import tourguide.tourguide.Sequence; +import tourguide.tourguide.ToolTip; +import tourguide.tourguide.TourGuide; + import com.kalab.chess.enginesupport.ChessEngine; import com.kalab.chess.enginesupport.ChessEngineResolver; import com.larvalabs.svgandroid.SVG; @@ -191,7 +197,9 @@ public class DroidFish extends Activity private TextView status; private ScrollView moveListScroll; private MoveListView moveList; + private View thinkingScroll; private TextView thinking; + private View buttons; private ImageButton custom1Button, custom2Button, custom3Button; private ImageButton modeButton, undoButton, redoButton; private ButtonActions custom1ButtonActions, custom2ButtonActions, custom3ButtonActions; @@ -254,6 +262,9 @@ public class DroidFish extends Activity private Typeface figNotation; private Typeface defaultThinkingListTypeFace; + private boolean guideShowOnStart; + private TourGuide tourGuide; + /** Defines all configurable button actions. */ private ActionFactory actionFactory = new ActionFactory() { @@ -486,6 +497,94 @@ public class DroidFish extends Activity else loadPGNFromFile(intentFilename); } + + startTourGuide(); + } + + private void startTourGuide(){ + if (!guideShowOnStart) + return; + + tourGuide = TourGuide.init(this); + ArrayList guides = new ArrayList(); + + TourGuide tg = TourGuide.init(this); + tg.setToolTip(new ToolTip() + .setTitle(getString(R.string.tour_leftMenu_title)) + .setDescription(getString(R.string.tour_leftMenu_desc)) + .setGravity(Gravity.BOTTOM | Gravity.RIGHT)); + tg.playLater(whiteTitleText); + guides.add(tg); + + tg = TourGuide.init(this); + tg.setToolTip(new ToolTip() + .setTitle(getString(R.string.tour_rightMenu_title)) + .setDescription(getString(R.string.tour_rightMenu_desc)) + .setGravity(Gravity.BOTTOM | Gravity.LEFT)); + tg.playLater(blackTitleText); + guides.add(tg); + + tg = TourGuide.init(this); + int gravity = !landScapeView() ? Gravity.BOTTOM : leftHandedView() ? Gravity.LEFT : Gravity.RIGHT; + tg.setToolTip(new ToolTip() + .setTitle(getString(R.string.tour_chessBoard_title)) + .setDescription(getString(R.string.tour_chessBoard_desc)) + .setGravity(gravity)); + tg.playLater(cb); + guides.add(tg); + + tg = TourGuide.init(this); + gravity = !landScapeView() ? Gravity.TOP : Gravity.BOTTOM; + tg.setToolTip(new ToolTip() + .setTitle(getString(R.string.tour_buttons_title)) + .setDescription(getString(R.string.tour_buttons_desc)) + .setGravity(gravity)); + tg.playLater(buttons); + guides.add(tg); + + tg = TourGuide.init(this); + gravity = !landScapeView() ? Gravity.TOP : leftHandedView() ? Gravity.RIGHT : Gravity.LEFT; + tg.setToolTip(new ToolTip() + .setTitle(getString(R.string.tour_moveList_title)) + .setDescription(getString(R.string.tour_moveList_desc)) + .setGravity(gravity)); + tg.playLater(moveListScroll); + guides.add(tg); + + tg = TourGuide.init(this); + tg.setToolTip(new ToolTip() + .setTitle(getString(R.string.tour_analysis_title)) + .setDescription(getString(R.string.tour_analysis_desc)) + .setGravity(Gravity.TOP)); + tg.playLater(thinkingScroll); + guides.add(tg); + + tg.setOverlay(new Overlay() + .setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + guideShowOnStart = false; + Editor editor = settings.edit(); + editor.putBoolean("guideShowOnStart", false); + editor.commit(); + tourGuide.next(); + tourGuide = null; + } + })); + + Sequence sequence = new Sequence.SequenceBuilder() + .add(guides.toArray(new TourGuide[guides.size()])) + .setDefaultOverlay(new Overlay() + .setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + tourGuide.next(); + } + })) + .setDefaultPointer(new Pointer()) + .setContinueMethod(Sequence.ContinueMethod.OverlayListener) + .build(); + tourGuide.playInSequence(sequence); } // Unicode code points for chess pieces @@ -664,12 +763,20 @@ public class DroidFish extends Activity updateThinkingInfo(); ctrl.updateRemainingTime(); ctrl.updateMaterialDiffList(); + if (tourGuide != null) { + tourGuide.cleanUp(); + tourGuide = null; + } } + /** Return true if the current orientation is landscape. */ + private final boolean landScapeView() { + return getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE; + } + /** Return true if left-handed layout should be used. */ private final boolean leftHandedView() { - return settings.getBoolean("leftHanded", false) && - (getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE); + return settings.getBoolean("leftHanded", false) && landScapeView(); } /** Re-read preferences settings. */ @@ -708,6 +815,7 @@ public class DroidFish extends Activity status = (TextView)findViewById(R.id.status); moveListScroll = (ScrollView)findViewById(R.id.scrollView); moveList = (MoveListView)findViewById(R.id.moveList); + thinkingScroll = (View)findViewById(R.id.scrollViewBot); thinking = (TextView)findViewById(R.id.thinking); defaultThinkingListTypeFace = thinking.getTypeface(); status.setFocusable(false); @@ -909,6 +1017,7 @@ public class DroidFish extends Activity } }); + buttons = (View)findViewById(R.id.buttons); custom1Button = (ImageButton)findViewById(R.id.custom1Button); custom1ButtonActions.setImageButton(custom1Button, this); custom2Button = (ImageButton)findViewById(R.id.custom2Button); @@ -1078,6 +1187,8 @@ public class DroidFish extends Activity custom3ButtonActions.readPrefs(settings, actionFactory); updateButtons(); + guideShowOnStart = settings.getBoolean("guideShowOnStart", true); + bookOptions.filename = settings.getString("bookFile", ""); bookOptions.maxLength = getIntSetting("bookMaxLength", 1000000); bookOptions.preferMainLines = settings.getBoolean("bookPreferMainLines", false); diff --git a/DroidFish/src/tourguide/tourguide/FrameLayoutWithHole.java b/DroidFish/src/tourguide/tourguide/FrameLayoutWithHole.java new file mode 100644 index 0000000..69eaf48 --- /dev/null +++ b/DroidFish/src/tourguide/tourguide/FrameLayoutWithHole.java @@ -0,0 +1,311 @@ +package tourguide.tourguide; + +import android.animation.AnimatorSet; +import android.app.Activity; +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.Point; +import android.graphics.PorterDuff; +import android.graphics.PorterDuffXfermode; +import android.support.v4.view.MotionEventCompat; +import android.text.TextPaint; +import android.util.AttributeSet; +import android.util.Log; +import android.view.Display; +import android.view.MotionEvent; +import android.view.View; +import android.view.ViewGroup; +import android.view.animation.Animation; +import android.widget.FrameLayout; +import java.util.ArrayList; + +/** + * TODO: document your custom view class. + */ +public class FrameLayoutWithHole extends FrameLayout { + private TextPaint mTextPaint; + private Activity mActivity; + private TourGuide.MotionType mMotionType; + private Paint mEraser; + + Bitmap mEraserBitmap; + private Canvas mEraserCanvas; + private Paint mPaint; + private Paint transparentPaint; + private View mViewHole; // This is the targeted view to be highlighted, where the hole should be placed + private int mRadius; + private int [] mPos; + private float mDensity; + private Overlay mOverlay; + + private ArrayList mAnimatorSetArrayList; + + public void setViewHole(View viewHole) { + this.mViewHole = viewHole; + enforceMotionType(); + } + public void addAnimatorSet(AnimatorSet animatorSet){ + if (mAnimatorSetArrayList==null){ + mAnimatorSetArrayList = new ArrayList(); + } + mAnimatorSetArrayList.add(animatorSet); + } + private void enforceMotionType(){ + Log.d("tourguide", "enforceMotionType 1"); + if (mViewHole!=null) {Log.d("tourguide","enforceMotionType 2"); + if (mMotionType!=null && mMotionType == TourGuide.MotionType.ClickOnly) { + Log.d("tourguide","enforceMotionType 3"); + Log.d("tourguide","only Clicking"); + mViewHole.setOnTouchListener(new OnTouchListener() { + @Override + public boolean onTouch(View view, MotionEvent motionEvent) { + mViewHole.getParent().requestDisallowInterceptTouchEvent(true); + return false; + } + }); + } else if (mMotionType!=null && mMotionType == TourGuide.MotionType.SwipeOnly) { + Log.d("tourguide","enforceMotionType 4"); + Log.d("tourguide","only Swiping"); + mViewHole.setClickable(false); + } + } + } + + public FrameLayoutWithHole(Activity context, View view) { + this(context, view, TourGuide.MotionType.AllowAll); + } + public FrameLayoutWithHole(Activity context, View view, TourGuide.MotionType motionType) { + this(context, view, motionType, new Overlay()); + } + + public FrameLayoutWithHole(Activity context, View view, TourGuide.MotionType motionType, Overlay overlay) { + super(context); + mActivity = context; + mViewHole = view; + init(null, 0); + enforceMotionType(); + mOverlay = overlay; + + int [] pos = new int[2]; + mViewHole.getLocationOnScreen(pos); + mPos = pos; + + mDensity = context.getResources().getDisplayMetrics().density; + int padding = (int)(20 * mDensity); + + if (mViewHole.getHeight() > mViewHole.getWidth()) { + mRadius = mViewHole.getHeight()/2 + padding; + } else { + mRadius = mViewHole.getWidth()/2 + padding; + } + mMotionType = motionType; + } + private void init(AttributeSet attrs, int defStyle) { + // Load attributes +// final TypedArray a = getContext().obtainStyledAttributes( +// attrs, FrameLayoutWithHole, defStyle, 0); +// +// +// a.recycle(); + setWillNotDraw(false); + // Set up a default TextPaint object + mTextPaint = new TextPaint(); + mTextPaint.setFlags(Paint.ANTI_ALIAS_FLAG); + mTextPaint.setTextAlign(Paint.Align.LEFT); + + Point size = new Point(); + size.x = mActivity.getResources().getDisplayMetrics().widthPixels; + size.y = mActivity.getResources().getDisplayMetrics().heightPixels; + + mEraserBitmap = Bitmap.createBitmap(size.x, size.y, Bitmap.Config.ARGB_8888); + mEraserCanvas = new Canvas(mEraserBitmap); + + mPaint = new Paint(); + mPaint.setColor(0xcc000000); + transparentPaint = new Paint(); + transparentPaint.setColor(getResources().getColor(android.R.color.transparent)); + transparentPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR)); + + mEraser = new Paint(); + mEraser.setColor(0xFFFFFFFF); + mEraser.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR)); + mEraser.setFlags(Paint.ANTI_ALIAS_FLAG); + + Log.d("tourguide","getHeight: "+ size.y); + Log.d("tourguide","getWidth: " + size.x); + + } + + private boolean mCleanUpLock = false; + protected void cleanUp(){ + if (getParent() != null) { + if (mOverlay!=null && mOverlay.mExitAnimation!=null) { + performOverlayExitAnimation(); + } else { + ((ViewGroup) this.getParent()).removeView(this); + } + } + } + private void performOverlayExitAnimation(){ + if (!mCleanUpLock) { + final FrameLayout _pointerToFrameLayout = this; + mCleanUpLock = true; + Log.d("tourguide","Overlay exit animation listener is overwritten..."); + mOverlay.mExitAnimation.setAnimationListener(new Animation.AnimationListener() { + @Override public void onAnimationStart(Animation animation) {} + @Override public void onAnimationRepeat(Animation animation) {} + @Override + public void onAnimationEnd(Animation animation) { + ((ViewGroup) _pointerToFrameLayout.getParent()).removeView(_pointerToFrameLayout); + } + }); + this.startAnimation(mOverlay.mExitAnimation); + } + } + /* comment this whole method to cause a memory leak */ + @Override + protected void onDetachedFromWindow() { + super.onDetachedFromWindow(); + /* cleanup reference to prevent memory leak */ + mEraserCanvas.setBitmap(null); + mEraserBitmap = null; + + if (mAnimatorSetArrayList != null && mAnimatorSetArrayList.size() > 0){ + for(int i=0;i> MotionEvent.ACTION_POINTER_ID_SHIFT); + sb.append(")" ); + } + sb.append("[" ); + for (int i = 0; i < event.getPointerCount(); i++) { + sb.append("#" ).append(i); + sb.append("(pid " ).append(event.getPointerId(i)); + sb.append(")=" ).append((int) event.getX(i)); + sb.append("," ).append((int) event.getY(i)); + if (i + 1 < event.getPointerCount()) + sb.append(";" ); + } + sb.append("]" ); + Log.d("tourguide", sb.toString()); + } + + @Override + public boolean dispatchTouchEvent(MotionEvent ev) { + //first check if the location button should handle the touch event + dumpEvent(ev); + int action = MotionEventCompat.getActionMasked(ev); + if(mViewHole != null) { + int[] pos = new int[2]; + mViewHole.getLocationOnScreen(pos); + Log.d("tourguide", "[dispatchTouchEvent] mViewHole.getHeight(): "+mViewHole.getHeight()); + Log.d("tourguide", "[dispatchTouchEvent] mViewHole.getWidth(): "+mViewHole.getWidth()); + + Log.d("tourguide", "[dispatchTouchEvent] Touch X(): "+ev.getRawX()); + Log.d("tourguide", "[dispatchTouchEvent] Touch Y(): "+ev.getRawY()); + +// Log.d("tourguide", "[dispatchTouchEvent] X of image: "+pos[0]); +// Log.d("tourguide", "[dispatchTouchEvent] Y of image: "+pos[1]); + + Log.d("tourguide", "[dispatchTouchEvent] X lower bound: "+ pos[0]); + Log.d("tourguide", "[dispatchTouchEvent] X higher bound: "+(pos[0] +mViewHole.getWidth())); + + Log.d("tourguide", "[dispatchTouchEvent] Y lower bound: "+ pos[1]); + Log.d("tourguide", "[dispatchTouchEvent] Y higher bound: "+(pos[1] +mViewHole.getHeight())); + + if(ev.getRawY() >= pos[1] && ev.getRawY() <= (pos[1] + mViewHole.getHeight()) && ev.getRawX() >= pos[0] && ev.getRawX() <= (pos[0] + mViewHole.getWidth())) { //location button event + Log.d("tourguide","to the BOTTOM!"); + Log.d("tourguide",""+ev.getAction()); + +// switch(action) { +// case (MotionEvent.ACTION_DOWN) : +// Log.d("tourguide","Action was DOWN"); +// return false; +// case (MotionEvent.ACTION_MOVE) : +// Log.d("tourguide","Action was MOVE"); +// return true; +// case (MotionEvent.ACTION_UP) : +// Log.d("tourguide","Action was UP"); +//// ev.setAction(MotionEvent.ACTION_DOWN|MotionEvent.ACTION_UP); +//// return super.dispatchTouchEvent(ev); +// return false; +// case (MotionEvent.ACTION_CANCEL) : +// Log.d("tourguide","Action was CANCEL"); +// return true; +// case (MotionEvent.ACTION_OUTSIDE) : +// Log.d("tourguide","Movement occurred outside bounds " + +// "of current screen element"); +// return true; +// default : +// return super.dispatchTouchEvent(ev); +// } +// return mViewHole.onTouchEvent(ev); + + return false; + } + } + return super.dispatchTouchEvent(ev); + } + @Override + protected void onDraw(Canvas canvas) { + super.onDraw(canvas); + mEraserBitmap.eraseColor(Color.TRANSPARENT); + + if (mOverlay!=null) { + mEraserCanvas.drawColor(mOverlay.mBackgroundColor); + int padding = (int) (10 * mDensity); + if (mOverlay.mStyle == Overlay.Style.Rectangle) { + mEraserCanvas.drawRect(mPos[0] - padding, mPos[1] - padding, mPos[0] + mViewHole.getWidth() + padding, mPos[1] + mViewHole.getHeight() + padding, mEraser); + } else { + mEraserCanvas.drawCircle(mPos[0] + mViewHole.getWidth() / 2, mPos[1] + mViewHole.getHeight() / 2, mRadius, mEraser); + } + } + canvas.drawBitmap(mEraserBitmap, 0, 0, null); + + } + @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + if (mOverlay!=null && mOverlay.mEnterAnimation!=null) { + this.startAnimation(mOverlay.mEnterAnimation); + } + } + /** + * + * Convenient method to obtain screen width in pixel + * + * @param activity + * @return screen width in pixel + */ + public int getScreenWidth(Activity activity){ + return activity.getResources().getDisplayMetrics().widthPixels; + } + + /** + * + * Convenient method to obtain screen height in pixel + * + * @param activity + * @return screen width in pixel + */ + public int getScreenHeight(Activity activity){ + return activity.getResources().getDisplayMetrics().heightPixels; + } +} diff --git a/DroidFish/src/tourguide/tourguide/Overlay.java b/DroidFish/src/tourguide/tourguide/Overlay.java new file mode 100644 index 0000000..e53ebde --- /dev/null +++ b/DroidFish/src/tourguide/tourguide/Overlay.java @@ -0,0 +1,83 @@ +package tourguide.tourguide; + +import android.graphics.Color; +import android.view.View; +import android.view.animation.Animation; + +/** + * Created by tanjunrong on 6/20/15. + */ +public class Overlay { + public int mBackgroundColor; + public boolean mDisableClick; + public Style mStyle; + public Animation mEnterAnimation, mExitAnimation; + public View.OnClickListener mOnClickListener; + + public enum Style { + Circle, Rectangle + } + public Overlay() { + this(true, Color.parseColor("#55000000"), Style.Circle); + } + + public Overlay(boolean disableClick, int backgroundColor, Style style) { + mDisableClick = disableClick; + mBackgroundColor = backgroundColor; + mStyle = style; + } + + /** + * Set background color + * @param backgroundColor + * @return return ToolTip instance for chaining purpose + */ + public Overlay setBackgroundColor(int backgroundColor){ + mBackgroundColor = backgroundColor; + return this; + } + + /** + * Set to true if you want to block all user input to pass through this overlay, set to false if you want to allow user input under the overlay + * @param yes_no + * @return return Overlay instance for chaining purpose + */ + public Overlay disableClick(boolean yes_no){ + mDisableClick = yes_no; + return this; + } + + public Overlay setStyle(Style style){ + mStyle = style; + return this; + } + + /** + * Set enter animation + * @param enterAnimation + * @return return Overlay instance for chaining purpose + */ + public Overlay setEnterAnimation(Animation enterAnimation){ + mEnterAnimation = enterAnimation; + return this; + } + /** + * Set exit animation + * @param exitAnimation + * @return return Overlay instance for chaining purpose + */ + public Overlay setExitAnimation(Animation exitAnimation){ + mExitAnimation = exitAnimation; + return this; + } + + /** + * Set onClickListener for the Overlay + * @param onClickListener + * @return return Overlay instance for chaining purpose + */ + public Overlay setOnClickListener(View.OnClickListener onClickListener){ + mOnClickListener=onClickListener; + return this; + } +} diff --git a/DroidFish/src/tourguide/tourguide/Pointer.java b/DroidFish/src/tourguide/tourguide/Pointer.java new file mode 100644 index 0000000..c820277 --- /dev/null +++ b/DroidFish/src/tourguide/tourguide/Pointer.java @@ -0,0 +1,41 @@ +package tourguide.tourguide; + +import android.graphics.Color; +import android.view.Gravity; + +/** + * Created by tanjunrong on 6/20/15. + */ +public class Pointer { + public int mGravity = Gravity.CENTER; + public int mColor = Color.WHITE; + + public Pointer() { + this(Gravity.CENTER, Color.parseColor("#FFFFFF")); + } + + public Pointer(int gravity, int color) { + this.mGravity = gravity; + this.mColor = color; + } + + /** + * Set color + * @param color + * @return return Pointer instance for chaining purpose + */ + public Pointer setColor(int color){ + mColor = color; + return this; + } + + /** + * Set gravity + * @param gravity + * @return return Pointer instance for chaining purpose + */ + public Pointer setGravity(int gravity){ + mGravity = gravity; + return this; + } +} diff --git a/DroidFish/src/tourguide/tourguide/Sequence.java b/DroidFish/src/tourguide/tourguide/Sequence.java new file mode 100644 index 0000000..fc64f38 --- /dev/null +++ b/DroidFish/src/tourguide/tourguide/Sequence.java @@ -0,0 +1,219 @@ +package tourguide.tourguide; + +import android.view.View; + +/** + * Created by aaronliew on 8/7/15. + */ +public class Sequence { + TourGuide [] mTourGuideArray; + Overlay mDefaultOverlay; + ToolTip mDefaultToolTip; + Pointer mDefaultPointer; + + ContinueMethod mContinueMethod; + boolean mDisableTargetButton; + public int mCurrentSequence; + TourGuide mParentTourGuide; + public enum ContinueMethod { + Overlay, OverlayListener + } + private Sequence(SequenceBuilder builder){ + this.mTourGuideArray = builder.mTourGuideArray; + this.mDefaultOverlay = builder.mDefaultOverlay; + this.mDefaultToolTip = builder.mDefaultToolTip; + this.mDefaultPointer = builder.mDefaultPointer; + this.mContinueMethod = builder.mContinueMethod; + this.mCurrentSequence = builder.mCurrentSequence; + + // TODO: to be implemented + this.mDisableTargetButton = builder.mDisableTargetButton; + } + + /** + * sets the parent TourGuide that will run this Sequence + */ + protected void setParentTourGuide(TourGuide parentTourGuide){ + mParentTourGuide = parentTourGuide; + + if(mContinueMethod == ContinueMethod.Overlay) { + for (final TourGuide tourGuide : mTourGuideArray) { + tourGuide.mOverlay.mOnClickListener = new View.OnClickListener() { + @Override + public void onClick(View v) { + mParentTourGuide.next(); + } + }; + } + } + } + + public TourGuide getNextTourGuide() { + return mTourGuideArray[mCurrentSequence]; + } + + public ContinueMethod getContinueMethod() { + return mContinueMethod; + } + + public TourGuide[] getTourGuideArray() { + return mTourGuideArray; + } + + public Overlay getDefaultOverlay() { + return mDefaultOverlay; + } + + public ToolTip getDefaultToolTip() { + return mDefaultToolTip; + } + + public ToolTip getToolTip() { + // individual tour guide has higher priority + if (mTourGuideArray[mCurrentSequence].mToolTip != null){ + return mTourGuideArray[mCurrentSequence].mToolTip; + } else { + return mDefaultToolTip; + } + } + + public Overlay getOverlay() { + // Overlay is used as a method to proceed to next TourGuide, so the default overlay is already assigned appropriately if needed + return mTourGuideArray[mCurrentSequence].mOverlay; + } + + public Pointer getPointer() { + // individual tour guide has higher priority + if (mTourGuideArray[mCurrentSequence].mPointer != null){ + return mTourGuideArray[mCurrentSequence].mPointer; + } else { + return mDefaultPointer; + } + } + + public static class SequenceBuilder { + TourGuide [] mTourGuideArray; + Overlay mDefaultOverlay; + ToolTip mDefaultToolTip; + Pointer mDefaultPointer; + ContinueMethod mContinueMethod; + int mCurrentSequence; + boolean mDisableTargetButton; + + public SequenceBuilder add(TourGuide... tourGuideArray){ + mTourGuideArray = tourGuideArray; + return this; + } + + public SequenceBuilder setDefaultOverlay(Overlay defaultOverlay){ + mDefaultOverlay = defaultOverlay; + return this; + } + + // This might not be useful, but who knows.. maybe someone needs it + public SequenceBuilder setDefaultToolTip(ToolTip defaultToolTip){ + mDefaultToolTip = defaultToolTip; + return this; + } + + public SequenceBuilder setDefaultPointer(Pointer defaultPointer){ + mDefaultPointer = defaultPointer; + return this; + } + + // TODO: this is an uncompleted feature, make it private first + // This is intended to be used to disable the button, so people cannot click on in during a Tour, instead, people can only click on Next button or Overlay to proceed + private SequenceBuilder setDisableButton(boolean disableTargetButton){ + mDisableTargetButton = disableTargetButton; + return this; + } + + /** + * @param continueMethod ContinueMethod.Overlay or ContinueMethod.OverlayListener + * ContnueMethod.Overlay - clicking on Overlay will make TourGuide proceed to the next one. + * ContinueMethod.OverlayListener - you need to provide OverlayListeners, and call tourGuideHandler.next() in the listener to proceed to the next one. + */ + public SequenceBuilder setContinueMethod(ContinueMethod continueMethod){ + mContinueMethod = continueMethod; + return this; + } + + public Sequence build(){ + mCurrentSequence = 0; + checkIfContinueMethodNull(); + checkAtLeastTwoTourGuideSupplied(); + checkOverlayListener(mContinueMethod); + + return new Sequence(this); + } + private void checkIfContinueMethodNull(){ + if (mContinueMethod == null){ + throw new IllegalArgumentException("Continue Method is not set. Please provide ContinueMethod in setContinueMethod"); + } + } + private void checkAtLeastTwoTourGuideSupplied() { + if (mTourGuideArray == null || mTourGuideArray.length <= 1){ + throw new IllegalArgumentException("In order to run a sequence, you must at least supply 2 TourGuide into Sequence using add()"); + } + } + private void checkOverlayListener(ContinueMethod continueMethod) { + if(continueMethod == ContinueMethod.OverlayListener){ + boolean pass = true; + if (mDefaultOverlay != null && mDefaultOverlay.mOnClickListener != null) { + pass = true; + // when default listener is available, we loop through individual tour guide, and + // assign default listener to individual tour guide + for (TourGuide tourGuide : mTourGuideArray) { + if (tourGuide.mOverlay == null) { + tourGuide.mOverlay = mDefaultOverlay; + } + if (tourGuide.mOverlay != null && tourGuide.mOverlay.mOnClickListener == null) { + tourGuide.mOverlay.mOnClickListener = mDefaultOverlay.mOnClickListener; + } + } + } else { // case where: default listener is not available + + for (TourGuide tourGuide : mTourGuideArray) { + //Both of the overlay and default listener is not null, throw the error + if (tourGuide.mOverlay != null && tourGuide.mOverlay.mOnClickListener == null) { + pass = false; + break; + } else if (tourGuide.mOverlay == null){ + pass = false; + break; + } + } + + } + + if (!pass){ + throw new IllegalArgumentException("ContinueMethod.OverlayListener is chosen as the ContinueMethod, but no Default Overlay Listener is set, or not all Overlay.mListener is set for all the TourGuide passed in."); + } + } else if(continueMethod == ContinueMethod.Overlay){ + // when Overlay ContinueMethod is used, listener must not be supplied (to avoid unexpected result) + boolean pass = true; + if (mDefaultOverlay != null && mDefaultOverlay.mOnClickListener != null) { + pass = false; + } else { + for (TourGuide tourGuide : mTourGuideArray) { + if (tourGuide.mOverlay != null && tourGuide.mOverlay.mOnClickListener != null ) { + pass = false; + break; + } + } + } + if (mDefaultOverlay != null) { + for (TourGuide tourGuide : mTourGuideArray) { + if (tourGuide.mOverlay == null) { + tourGuide.mOverlay = mDefaultOverlay; + } + } + } + + if (!pass) { + throw new IllegalArgumentException("ContinueMethod.Overlay is chosen as the ContinueMethod, but either default overlay listener is still set OR individual overlay listener is still set, make sure to clear all Overlay listener"); + } + } + } + } +} \ No newline at end of file diff --git a/DroidFish/src/tourguide/tourguide/ToolTip.java b/DroidFish/src/tourguide/tourguide/ToolTip.java new file mode 100644 index 0000000..85ec0ef --- /dev/null +++ b/DroidFish/src/tourguide/tourguide/ToolTip.java @@ -0,0 +1,119 @@ +package tourguide.tourguide; + +import android.graphics.Color; +import android.view.Gravity; +import android.view.View; +import android.view.animation.AlphaAnimation; +import android.view.animation.Animation; +import android.view.animation.BounceInterpolator; + +/** + * Created by tanjunrong on 6/17/15. + */ +public class ToolTip { + public String mTitle, mDescription; + public int mBackgroundColor, mTextColor; + public Animation mEnterAnimation, mExitAnimation; + public boolean mShadow; + public int mGravity; + public View.OnClickListener mOnClickListener; + + public ToolTip(){ + /* default values */ + mTitle = ""; + mDescription = ""; + mBackgroundColor = Color.parseColor("#3498db"); + mTextColor = Color.parseColor("#FFFFFF"); + + mEnterAnimation = new AlphaAnimation(0f, 1f); + mEnterAnimation.setDuration(1000); + mEnterAnimation.setFillAfter(true); + mEnterAnimation.setInterpolator(new BounceInterpolator()); + mShadow = true; + + // TODO: exit animation + mGravity = Gravity.CENTER; + } + /** + * Set title text + * @param title + * @return return ToolTip instance for chaining purpose + */ + public ToolTip setTitle(String title){ + mTitle = title; + return this; + } + + /** + * Set description text + * @param description + * @return return ToolTip instance for chaining purpose + */ + public ToolTip setDescription(String description){ + mDescription = description; + return this; + } + + /** + * Set background color + * @param backgroundColor + * @return return ToolTip instance for chaining purpose + */ + public ToolTip setBackgroundColor(int backgroundColor){ + mBackgroundColor = backgroundColor; + return this; + } + + /** + * Set text color + * @param textColor + * @return return ToolTip instance for chaining purpose + */ + public ToolTip setTextColor(int textColor){ + mTextColor = textColor; + return this; + } + + /** + * Set enter animation + * @param enterAnimation + * @return return ToolTip instance for chaining purpose + */ + public ToolTip setEnterAnimation(Animation enterAnimation){ + mEnterAnimation = enterAnimation; + return this; + } + /** + * Set exit animation + * @param exitAnimation + * @return return ToolTip instance for chaining purpose + */ +// TODO: +// public ToolTip setExitAnimation(Animation exitAnimation){ +// mExitAnimation = exitAnimation; +// return this; +// } + /** + * Set the gravity, the setGravity is centered relative to the targeted button + * @param gravity Gravity.CENTER, Gravity.TOP, Gravity.BOTTOM, etc + * @return return ToolTip instance for chaining purpose + */ + public ToolTip setGravity(int gravity){ + mGravity = gravity; + return this; + } + /** + * Set if you want to have setShadow + * @param shadow + * @return return ToolTip instance for chaining purpose + */ + public ToolTip setShadow(boolean shadow){ + mShadow = shadow; + return this; + } + + public ToolTip setOnClickListener(View.OnClickListener onClickListener){ + mOnClickListener = onClickListener; + return this; + } +} diff --git a/DroidFish/src/tourguide/tourguide/TourGuide.java b/DroidFish/src/tourguide/tourguide/TourGuide.java new file mode 100644 index 0000000..c54beed --- /dev/null +++ b/DroidFish/src/tourguide/tourguide/TourGuide.java @@ -0,0 +1,633 @@ +package tourguide.tourguide; + +import org.petero.droidfish.R; + +import android.animation.Animator; +import android.animation.AnimatorSet; +import android.animation.ObjectAnimator; +import android.animation.ValueAnimator; +import android.app.Activity; +import android.graphics.Color; +import android.graphics.Point; +import android.util.Log; +import android.view.Display; +import android.view.Gravity; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.view.ViewTreeObserver; +import android.widget.FrameLayout; +import android.widget.LinearLayout; +import android.widget.TextView; +import net.i2p.android.ext.floatingactionbutton.FloatingActionButton; + +/** + * Created by tanjunrong on 2/10/15. + */ +public class TourGuide { + /** + * This describes the animation techniques + * */ + public enum Technique { + Click, HorizontalLeft, HorizontalRight, VerticalUpward, VerticalDownward + } + + /** + * This describes the allowable motion, for example if you want the users to learn about clicking, but want to stop them from swiping, then use ClickOnly + */ + public enum MotionType { + AllowAll, ClickOnly, SwipeOnly + } + private Technique mTechnique; + private View mHighlightedView; + private Activity mActivity; + private MotionType mMotionType; + private FrameLayoutWithHole mFrameLayout; + private View mToolTipViewGroup; + public ToolTip mToolTip; + public Pointer mPointer; + public Overlay mOverlay; + + private Sequence mSequence; + + /************* + * + * Public API + * + *************/ + + /* Static builder */ + public static TourGuide init(Activity activity){ + return new TourGuide(activity); + } + + /* Constructor */ + public TourGuide(Activity activity){ + mActivity = activity; + } + + /** + * Setter for the animation to be used + * @param technique Animation to be used + * @return return TourGuide instance for chaining purpose + */ + public TourGuide with(Technique technique) { + mTechnique = technique; + return this; + } + + /** + * Sets which motion type is motionType + * @param motionType + * @return return TourGuide instance for chaining purpose + */ + public TourGuide motionType(MotionType motionType){ + mMotionType = motionType; + return this; + } + + /** + * Sets the duration + * @param view the view in which the tutorial button will be placed on top of + * @return return TourGuide instance for chaining purpose + */ + public TourGuide playOn(View view){ + mHighlightedView = view; + setupView(); + return this; + } + + /** + * Sets the overlay + * @param overlay this overlay object should contain the attributes of the overlay, such as background color, animation, Style, etc + * @return return TourGuide instance for chaining purpose + */ + public TourGuide setOverlay(Overlay overlay){ + mOverlay = overlay; + return this; + } + /** + * Set the toolTip + * @param toolTip this toolTip object should contain the attributes of the ToolTip, such as, the title text, and the description text, background color, etc + * @return return TourGuide instance for chaining purpose + */ + public TourGuide setToolTip(ToolTip toolTip){ + mToolTip = toolTip; + return this; + } + /** + * Set the Pointer + * @param pointer this pointer object should contain the attributes of the Pointer, such as the pointer color, pointer gravity, etc, refer to @Link{pointer} + * @return return TourGuide instance for chaining purpose + */ + public TourGuide setPointer(Pointer pointer){ + mPointer = pointer; + return this; + } + /** + * Clean up the tutorial that is added to the activity + */ + public void cleanUp(){ + mFrameLayout.cleanUp(); + if (mToolTipViewGroup!=null) { + ((ViewGroup) mActivity.getWindow().getDecorView()).removeView(mToolTipViewGroup); + } + } + + public TourGuide playLater(View view){ + mHighlightedView = view; + return this; + } + + /************************** + * Sequence related method + **************************/ + + public TourGuide playInSequence(Sequence sequence){ + setSequence(sequence); + next(); + return this; + } + + public TourGuide setSequence(Sequence sequence){ + mSequence = sequence; + mSequence.setParentTourGuide(this); + for (TourGuide tourGuide : sequence.mTourGuideArray){ + if (tourGuide.mHighlightedView == null) { + throw new NullPointerException("Please specify the view using 'playLater' method"); + } + } + return this; + } + + public TourGuide next(){ + if (mFrameLayout!=null) { + cleanUp(); + } + + if (mSequence.mCurrentSequence < mSequence.mTourGuideArray.length) { + setToolTip(mSequence.getToolTip()); + setPointer(mSequence.getPointer()); + setOverlay(mSequence.getOverlay()); + + mHighlightedView = mSequence.getNextTourGuide().mHighlightedView; + + setupView(); + mSequence.mCurrentSequence++; + } + return this; + } + + /** + * + * @return FrameLayoutWithHole that is used as overlay + */ + public FrameLayoutWithHole getOverlay(){ + return mFrameLayout; + } + /** + * + * @return the ToolTip container View + */ + public View getToolTip(){ + return mToolTipViewGroup; + } + /****** + * + * Private methods + * + *******/ + //TODO: move into Pointer + private int getXBasedOnGravity(int width){ + int [] pos = new int[2]; + mHighlightedView.getLocationOnScreen(pos); + int x = pos[0]; + if((mPointer.mGravity & Gravity.RIGHT) == Gravity.RIGHT){ + return x+mHighlightedView.getWidth()-width; + } else if ((mPointer.mGravity & Gravity.LEFT) == Gravity.LEFT) { + return x; + } else { // this is center + return x+mHighlightedView.getWidth()/2-width/2; + } + } + //TODO: move into Pointer + private int getYBasedOnGravity(int height){ + int [] pos = new int[2]; + mHighlightedView.getLocationInWindow(pos); + int y = pos[1]; + if((mPointer.mGravity & Gravity.BOTTOM) == Gravity.BOTTOM){ + return y+mHighlightedView.getHeight()-height; + } else if ((mPointer.mGravity & Gravity.TOP) == Gravity.TOP) { + return y; + }else { // this is center + return y+mHighlightedView.getHeight()/2-height/2; + } + } + + private void setupView(){ +// TODO: throw exception if either mActivity, mDuration, mHighlightedView is null + checking(); + final ViewTreeObserver viewTreeObserver = mHighlightedView.getViewTreeObserver(); + viewTreeObserver.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { + @Override + public void onGlobalLayout() { + // make sure this only run once + mHighlightedView.getViewTreeObserver().removeGlobalOnLayoutListener(this); + + /* Initialize a frame layout with a hole */ + mFrameLayout = new FrameLayoutWithHole(mActivity, mHighlightedView, mMotionType, mOverlay); + /* handle click disable */ + handleDisableClicking(mFrameLayout); + + /* setup floating action button */ + if (mPointer != null) { + FloatingActionButton fab = setupAndAddFABToFrameLayout(mFrameLayout); + performAnimationOn(fab); + } + setupFrameLayout(); + /* setup tooltip view */ + setupToolTip(); + } + }); + } + private void checking(){ + // There is not check for tooltip because tooltip can be null, it means there no tooltip will be shown + + } + private void handleDisableClicking(FrameLayoutWithHole frameLayoutWithHole){ + // 1. if user provides an overlay listener, use that as 1st priority + if (mOverlay != null && mOverlay.mOnClickListener!=null) { + frameLayoutWithHole.setClickable(true); + frameLayoutWithHole.setOnClickListener(mOverlay.mOnClickListener); + } + // 2. if overlay listener is not provided, check if it's disabled + else if (mOverlay != null && mOverlay.mDisableClick) { + Log.w("tourguide", "Overlay's default OnClickListener is null, it will proceed to next tourguide when it is clicked"); + frameLayoutWithHole.setViewHole(mHighlightedView); + frameLayoutWithHole.setSoundEffectsEnabled(false); + frameLayoutWithHole.setOnClickListener(new View.OnClickListener() { + @Override public void onClick(View v) {} // do nothing, disabled. + }); + } + } + + private void setupToolTip(){ + final FrameLayout.LayoutParams layoutParams = new FrameLayout.LayoutParams(FrameLayout.LayoutParams.WRAP_CONTENT, FrameLayout.LayoutParams.WRAP_CONTENT); + + if (mToolTip != null) { + /* inflate and get views */ + ViewGroup parent = (ViewGroup) mActivity.getWindow().getDecorView(); + LayoutInflater layoutInflater = mActivity.getLayoutInflater(); + mToolTipViewGroup = layoutInflater.inflate(R.layout.tooltip, null); + View toolTipContainer = mToolTipViewGroup.findViewById(R.id.toolTip_container); + TextView toolTipTitleTV = (TextView) mToolTipViewGroup.findViewById(R.id.title); + TextView toolTipDescriptionTV = (TextView) mToolTipViewGroup.findViewById(R.id.description); + + /* set tooltip attributes */ + toolTipContainer.setBackgroundColor(mToolTip.mBackgroundColor); + if (mToolTip.mTitle == null){ + toolTipTitleTV.setVisibility(View.GONE); + } else { + toolTipTitleTV.setText(mToolTip.mTitle); + } + if (mToolTip.mDescription == null){ + toolTipDescriptionTV.setVisibility(View.GONE); + } else { + toolTipDescriptionTV.setText(mToolTip.mDescription); + } + + + mToolTipViewGroup.startAnimation(mToolTip.mEnterAnimation); + + /* add setShadow if it's turned on */ + if (mToolTip.mShadow) { + mToolTipViewGroup.setBackgroundDrawable(mActivity.getResources().getDrawable(R.drawable.drop_shadow)); + } + + /* position and size calculation */ + int [] pos = new int[2]; + mHighlightedView.getLocationOnScreen(pos); + int targetViewX = pos[0]; + final int targetViewY = pos[1]; + + // get measured size of tooltip + mToolTipViewGroup.measure(FrameLayout.LayoutParams.WRAP_CONTENT, FrameLayout.LayoutParams.WRAP_CONTENT); + int toolTipMeasuredWidth = mToolTipViewGroup.getMeasuredWidth(); + int toolTipMeasuredHeight = mToolTipViewGroup.getMeasuredHeight(); + + Point resultPoint = new Point(); // this holds the final position of tooltip + float density = mActivity.getResources().getDisplayMetrics().density; + final float adjustment = 10 * density; //adjustment is that little overlapping area of tooltip and targeted button + + // calculate x position, based on gravity, tooltipMeasuredWidth, parent max width, x position of target view, adjustment + if (toolTipMeasuredWidth > parent.getWidth()){ + resultPoint.x = getXForTooTip(mToolTip.mGravity, parent.getWidth(), targetViewX, adjustment); + } else { + resultPoint.x = getXForTooTip(mToolTip.mGravity, toolTipMeasuredWidth, targetViewX, adjustment); + } + + resultPoint.y = getYForTooTip(mToolTip.mGravity, toolTipMeasuredHeight, targetViewY, adjustment); + + // add view to parent +// ((ViewGroup) mActivity.getWindow().getDecorView().findViewById(android.R.id.content)).addView(mToolTipViewGroup, layoutParams); + parent.addView(mToolTipViewGroup, layoutParams); + + // 1. width < screen check + if (toolTipMeasuredWidth > parent.getWidth()){ + mToolTipViewGroup.getLayoutParams().width = parent.getWidth(); + toolTipMeasuredWidth = parent.getWidth(); + } + // 2. x left boundary check + if (resultPoint.x < 0){ + mToolTipViewGroup.getLayoutParams().width = toolTipMeasuredWidth + resultPoint.x; //since point.x is negative, use plus + resultPoint.x = 0; + } + // 3. x right boundary check + int tempRightX = resultPoint.x + toolTipMeasuredWidth; + if ( tempRightX > parent.getWidth()){ + mToolTipViewGroup.getLayoutParams().width = parent.getWidth() - resultPoint.x; //since point.x is negative, use plus + } + + // pass toolTip onClickListener into toolTipViewGroup + if (mToolTip.mOnClickListener!=null) { + mToolTipViewGroup.setOnClickListener(mToolTip.mOnClickListener); + } + + // TODO: no boundary check for height yet, this is a unlikely case though + // height boundary can be fixed by user changing the gravity to the other size, since there are plenty of space vertically compared to horizontally + + // this needs an viewTreeObserver, that's because TextView measurement of it's vertical height is not accurate (didn't take into account of multiple lines yet) before it's rendered + // re-calculate height again once it's rendered + final ViewTreeObserver viewTreeObserver = mToolTipViewGroup.getViewTreeObserver(); + viewTreeObserver.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { + @Override + public void onGlobalLayout() { + mToolTipViewGroup.getViewTreeObserver().removeGlobalOnLayoutListener(this);// make sure this only run once + + int fixedY; + int toolTipHeightAfterLayouted = mToolTipViewGroup.getHeight(); + fixedY = getYForTooTip(mToolTip.mGravity, toolTipHeightAfterLayouted, targetViewY, adjustment); + layoutParams.setMargins((int)mToolTipViewGroup.getX(),fixedY,0,0); + } + }); + + // set the position using setMargins on the left and top + layoutParams.setMargins(resultPoint.x, resultPoint.y, 0, 0); + } + + } + + private int getXForTooTip(int gravity, int toolTipMeasuredWidth, int targetViewX, float adjustment){ + int x; + if ((gravity & Gravity.LEFT) == Gravity.LEFT){ + x = targetViewX - toolTipMeasuredWidth + (int)adjustment; + } else if ((gravity & Gravity.RIGHT) == Gravity.RIGHT) { + x = targetViewX + mHighlightedView.getWidth() - (int)adjustment; + } else { + x = targetViewX + mHighlightedView.getWidth() / 2 - toolTipMeasuredWidth / 2; + } + return x; + } + private int getYForTooTip(int gravity, int toolTipMeasuredHeight, int targetViewY, float adjustment){ + int y; + if ((gravity & Gravity.TOP) == Gravity.TOP) { + + if (((gravity & Gravity.LEFT) == Gravity.LEFT) || ((gravity & Gravity.RIGHT) == Gravity.RIGHT)) { + y = targetViewY - toolTipMeasuredHeight + (int)adjustment; + } else { + y = targetViewY - toolTipMeasuredHeight - (int)adjustment; + } + } else if ((gravity & Gravity.BOTTOM) == Gravity.BOTTOM) { + if (((gravity & Gravity.LEFT) == Gravity.LEFT) || ((gravity & Gravity.RIGHT) == Gravity.RIGHT)) { + y = targetViewY + mHighlightedView.getHeight() - (int) adjustment; + } else { + y = targetViewY + mHighlightedView.getHeight() + (int) adjustment; + } + } else { // this is center + if (((gravity & Gravity.LEFT) == Gravity.LEFT) || ((gravity & Gravity.RIGHT) == Gravity.RIGHT)) { + y = targetViewY + mHighlightedView.getHeight() / 2 - (int) adjustment; + } else { + y = targetViewY + mHighlightedView.getHeight() / 2 + (int) adjustment; + } + } + return y; + } + + private FloatingActionButton setupAndAddFABToFrameLayout(final FrameLayoutWithHole frameLayoutWithHole){ + // invisFab is invisible, and it's only used for getting the width and height + final FloatingActionButton invisFab = new FloatingActionButton(mActivity); + invisFab.setSize(FloatingActionButton.SIZE_MINI); + invisFab.setVisibility(View.INVISIBLE); + ((ViewGroup)mActivity.getWindow().getDecorView()).addView(invisFab); + + // fab is the real fab that is going to be added + final FloatingActionButton fab = new FloatingActionButton(mActivity); + fab.setBackgroundColor(Color.BLUE); + fab.setSize(FloatingActionButton.SIZE_MINI); + fab.setColorNormal(mPointer.mColor); + fab.setStrokeVisible(false); + fab.setClickable(false); + + // When invisFab is layouted, it's width and height can be used to calculate the correct position of fab + final ViewTreeObserver viewTreeObserver = invisFab.getViewTreeObserver(); + viewTreeObserver.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { + @Override + public void onGlobalLayout() { + // make sure this only run once + invisFab.getViewTreeObserver().removeGlobalOnLayoutListener(this); + final FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT); + frameLayoutWithHole.addView(fab, params); + + // measure size of image to be placed + params.setMargins(getXBasedOnGravity(invisFab.getWidth()), getYBasedOnGravity(invisFab.getHeight()), 0, 0); + } + }); + + + return fab; + } + + private void setupFrameLayout(){ + FrameLayout.LayoutParams layoutParams = new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT); + ViewGroup contentArea = (ViewGroup) mActivity.getWindow().getDecorView().findViewById(android.R.id.content); + int [] pos = new int[2]; + contentArea.getLocationOnScreen(pos); + // frameLayoutWithHole's coordinates are calculated taking full screen height into account + // but we're adding it to the content area only, so we need to offset it to the same Y value of contentArea + + layoutParams.setMargins(0,-pos[1],0,0); + contentArea.addView(mFrameLayout, layoutParams); + } + + private void performAnimationOn(final View view){ + + if (mTechnique != null && mTechnique == Technique.HorizontalLeft){ + + final AnimatorSet animatorSet = new AnimatorSet(); + final AnimatorSet animatorSet2 = new AnimatorSet(); + Animator.AnimatorListener lis1 = new Animator.AnimatorListener() { + @Override public void onAnimationStart(Animator animator) {} + @Override public void onAnimationCancel(Animator animator) {} + @Override public void onAnimationRepeat(Animator animator) {} + @Override + public void onAnimationEnd(Animator animator) { + view.setScaleX(1f); + view.setScaleY(1f); + view.setTranslationX(0); + animatorSet2.start(); + } + }; + Animator.AnimatorListener lis2 = new Animator.AnimatorListener() { + @Override public void onAnimationStart(Animator animator) {} + @Override public void onAnimationCancel(Animator animator) {} + @Override public void onAnimationRepeat(Animator animator) {} + @Override + public void onAnimationEnd(Animator animator) { + view.setScaleX(1f); + view.setScaleY(1f); + view.setTranslationX(0); + animatorSet.start(); + } + }; + + long fadeInDuration = 800; + long scaleDownDuration = 800; + long goLeftXDuration = 2000; + long fadeOutDuration = goLeftXDuration; + float translationX = getScreenWidth()/2; + + final ValueAnimator fadeInAnim = ObjectAnimator.ofFloat(view, "alpha", 0f, 1f); + fadeInAnim.setDuration(fadeInDuration); + final ObjectAnimator scaleDownX = ObjectAnimator.ofFloat(view, "scaleX", 1f, 0.85f); + scaleDownX.setDuration(scaleDownDuration); + final ObjectAnimator scaleDownY = ObjectAnimator.ofFloat(view, "scaleY", 1f, 0.85f); + scaleDownY.setDuration(scaleDownDuration); + final ObjectAnimator goLeftX = ObjectAnimator.ofFloat(view, "translationX", -translationX); + goLeftX.setDuration(goLeftXDuration); + final ValueAnimator fadeOutAnim = ObjectAnimator.ofFloat(view, "alpha", 1f, 0f); + fadeOutAnim.setDuration(fadeOutDuration); + + final ValueAnimator fadeInAnim2 = ObjectAnimator.ofFloat(view, "alpha", 0f, 1f); + fadeInAnim2.setDuration(fadeInDuration); + final ObjectAnimator scaleDownX2 = ObjectAnimator.ofFloat(view, "scaleX", 1f, 0.85f); + scaleDownX2.setDuration(scaleDownDuration); + final ObjectAnimator scaleDownY2 = ObjectAnimator.ofFloat(view, "scaleY", 1f, 0.85f); + scaleDownY2.setDuration(scaleDownDuration); + final ObjectAnimator goLeftX2 = ObjectAnimator.ofFloat(view, "translationX", -translationX); + goLeftX2.setDuration(goLeftXDuration); + final ValueAnimator fadeOutAnim2 = ObjectAnimator.ofFloat(view, "alpha", 1f, 0f); + fadeOutAnim2.setDuration(fadeOutDuration); + + animatorSet.play(fadeInAnim); + animatorSet.play(scaleDownX).with(scaleDownY).after(fadeInAnim); + animatorSet.play(goLeftX).with(fadeOutAnim).after(scaleDownY); + + animatorSet2.play(fadeInAnim2); + animatorSet2.play(scaleDownX2).with(scaleDownY2).after(fadeInAnim2); + animatorSet2.play(goLeftX2).with(fadeOutAnim2).after(scaleDownY2); + + animatorSet.addListener(lis1); + animatorSet2.addListener(lis2); + animatorSet.start(); + + /* these animatorSets are kept track in FrameLayout, so that they can be cleaned up when FrameLayout is detached from window */ + mFrameLayout.addAnimatorSet(animatorSet); + mFrameLayout.addAnimatorSet(animatorSet2); + } else if (mTechnique != null && mTechnique == Technique.HorizontalRight){ + + } else if (mTechnique != null && mTechnique == Technique.VerticalUpward){ + + } else if (mTechnique != null && mTechnique == Technique.VerticalDownward){ + + } else { // do click for default case + final AnimatorSet animatorSet = new AnimatorSet(); + final AnimatorSet animatorSet2 = new AnimatorSet(); + Animator.AnimatorListener lis1 = new Animator.AnimatorListener() { + @Override public void onAnimationStart(Animator animator) {} + @Override public void onAnimationCancel(Animator animator) {} + @Override public void onAnimationRepeat(Animator animator) {} + @Override + public void onAnimationEnd(Animator animator) { + view.setScaleX(1f); + view.setScaleY(1f); + view.setTranslationX(0); + animatorSet2.start(); + } + }; + Animator.AnimatorListener lis2 = new Animator.AnimatorListener() { + @Override public void onAnimationStart(Animator animator) {} + @Override public void onAnimationCancel(Animator animator) {} + @Override public void onAnimationRepeat(Animator animator) {} + @Override + public void onAnimationEnd(Animator animator) { + view.setScaleX(1f); + view.setScaleY(1f); + view.setTranslationX(0); + animatorSet.start(); + } + }; + + long fadeInDuration = 800; + long scaleDownDuration = 800; + long fadeOutDuration = 800; + long delay = 1000; + + final ValueAnimator delayAnim = ObjectAnimator.ofFloat(view, "translationX", 0); + delayAnim.setDuration(delay); + final ValueAnimator fadeInAnim = ObjectAnimator.ofFloat(view, "alpha", 0f, 1f); + fadeInAnim.setDuration(fadeInDuration); + final ObjectAnimator scaleDownX = ObjectAnimator.ofFloat(view, "scaleX", 1f, 0.85f); + scaleDownX.setDuration(scaleDownDuration); + final ObjectAnimator scaleDownY = ObjectAnimator.ofFloat(view, "scaleY", 1f, 0.85f); + scaleDownY.setDuration(scaleDownDuration); + final ObjectAnimator scaleUpX = ObjectAnimator.ofFloat(view, "scaleX", 0.85f, 1f); + scaleUpX.setDuration(scaleDownDuration); + final ObjectAnimator scaleUpY = ObjectAnimator.ofFloat(view, "scaleY", 0.85f, 1f); + scaleUpY.setDuration(scaleDownDuration); + final ValueAnimator fadeOutAnim = ObjectAnimator.ofFloat(view, "alpha", 1f, 0f); + fadeOutAnim.setDuration(fadeOutDuration); + + final ValueAnimator delayAnim2 = ObjectAnimator.ofFloat(view, "translationX", 0); + delayAnim2.setDuration(delay); + final ValueAnimator fadeInAnim2 = ObjectAnimator.ofFloat(view, "alpha", 0f, 1f); + fadeInAnim2.setDuration(fadeInDuration); + final ObjectAnimator scaleDownX2 = ObjectAnimator.ofFloat(view, "scaleX", 1f, 0.85f); + scaleDownX2.setDuration(scaleDownDuration); + final ObjectAnimator scaleDownY2 = ObjectAnimator.ofFloat(view, "scaleY", 1f, 0.85f); + scaleDownY2.setDuration(scaleDownDuration); + final ObjectAnimator scaleUpX2 = ObjectAnimator.ofFloat(view, "scaleX", 0.85f, 1f); + scaleUpX2.setDuration(scaleDownDuration); + final ObjectAnimator scaleUpY2 = ObjectAnimator.ofFloat(view, "scaleY", 0.85f, 1f); + scaleUpY2.setDuration(scaleDownDuration); + final ValueAnimator fadeOutAnim2 = ObjectAnimator.ofFloat(view, "alpha", 1f, 0f); + fadeOutAnim2.setDuration(fadeOutDuration); + view.setAlpha(0); + animatorSet.setStartDelay(mToolTip != null ? mToolTip.mEnterAnimation.getDuration() : 0); + animatorSet.play(fadeInAnim); + animatorSet.play(scaleDownX).with(scaleDownY).after(fadeInAnim); + animatorSet.play(scaleUpX).with(scaleUpY).with(fadeOutAnim).after(scaleDownY); + animatorSet.play(delayAnim).after(scaleUpY); + + animatorSet2.play(fadeInAnim2); + animatorSet2.play(scaleDownX2).with(scaleDownY2).after(fadeInAnim2); + animatorSet2.play(scaleUpX2).with(scaleUpY2).with(fadeOutAnim2).after(scaleDownY2); + animatorSet2.play(delayAnim2).after(scaleUpY2); + + animatorSet.addListener(lis1); + animatorSet2.addListener(lis2); + animatorSet.start(); + + /* these animatorSets are kept track in FrameLayout, so that they can be cleaned up when FrameLayout is detached from window */ + mFrameLayout.addAnimatorSet(animatorSet); + mFrameLayout.addAnimatorSet(animatorSet2); + } + } + private int getScreenWidth(){ + if (mActivity!=null) { + return mActivity.getResources().getDisplayMetrics().widthPixels; + } else { + return 0; + } + } + +}