From 3ddba7ee37c02c071b6b7580cc21ad8007861ec9 Mon Sep 17 00:00:00 2001 From: Joe Malin Date: Mon, 4 Mar 2013 10:33:30 -0800 Subject: [PATCH] Android U: Making Apps Location-Aware Change-Id: I8f44c6ca6d797ceb8ada5b2c723a8cca0081cf0a --- .../training/ActivityRecognition.zip | Bin 0 -> 46482 bytes docs/downloads/training/GeofenceDetection.zip | Bin 0 -> 69289 bytes docs/downloads/training/LocationUpdates.zip | Bin 0 -> 49600 bytes docs/html/guide/topics/location/index.jd | 40 +- docs/html/guide/topics/location/strategies.jd | 51 +- .../basics/location/currentlocation.jd | 163 -- .../training/basics/location/geocoding.jd | 98 -- docs/html/training/basics/location/index.jd | 50 - .../basics/location/locationmanager.jd | 120 -- .../training/location/activity-recognition.jd | 781 +++++++++ .../html/training/location/display-address.jd | 280 ++++ docs/html/training/location/geofencing.jd | 1407 +++++++++++++++++ docs/html/training/location/index.jd | 88 ++ .../location/receive-location-updates.jd | 590 +++++++ .../training/location/retrieve-current.jd | 387 +++++ docs/html/training/training_toc.cs | 92 +- location/java/android/location/package.html | 15 +- 17 files changed, 3650 insertions(+), 512 deletions(-) create mode 100644 docs/downloads/training/ActivityRecognition.zip create mode 100644 docs/downloads/training/GeofenceDetection.zip create mode 100755 docs/downloads/training/LocationUpdates.zip delete mode 100644 docs/html/training/basics/location/currentlocation.jd delete mode 100644 docs/html/training/basics/location/geocoding.jd delete mode 100644 docs/html/training/basics/location/index.jd delete mode 100644 docs/html/training/basics/location/locationmanager.jd create mode 100644 docs/html/training/location/activity-recognition.jd create mode 100644 docs/html/training/location/display-address.jd create mode 100644 docs/html/training/location/geofencing.jd create mode 100644 docs/html/training/location/index.jd create mode 100644 docs/html/training/location/receive-location-updates.jd create mode 100644 docs/html/training/location/retrieve-current.jd diff --git a/docs/downloads/training/ActivityRecognition.zip b/docs/downloads/training/ActivityRecognition.zip new file mode 100644 index 0000000000000000000000000000000000000000..39c04e1177dc926cc421b4f284844bf3e47f855e GIT binary patch literal 46482 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 literal 0 HcmV?d00001 diff --git a/docs/downloads/training/GeofenceDetection.zip b/docs/downloads/training/GeofenceDetection.zip new file mode 100644 index 0000000000000000000000000000000000000000..bb89136db1d72d0c328e4223c75e31898970e1d7 GIT binary patch literal 69289 zcmbrmV~{Ujv+wz9_io#^ZQHhO+ugfu+qS!R+qQPwwr$M+IrrX~I5*DBnR%un){0dT zRo{%N7oW^rnNSAi_Vrg$jud4D3 z03s(Fqp0)GaP@!&0D*=l005xj$N+%CKUygN+zko9_(v<6T6?Do1OQNm1OQ0?OD#b= z6DNC16Inw$OEXhv7dj7HoBxpK|3~^?yt}<=J%S$rO{~<_r6|5$zF3i;J!qBp1Q%DylxB3i-r|N( z7-HHZ%Hb$ki_zn@FAKV}FZv6(%pBh@_UK^0HF`$U+uHg8y5J3@M zj8y@(Qh$I6%r~rou?uXJfsSb&h@i50d6BiwWv6`Ptu1N?TLdab3HG(|Bpl${chSsV z>T%muS*fE|8B3lq_UG&<*g1+cVQSs#mNEij>@BpzPwE?`T5O@;Ae*`0 zy&N`PEyT|tW|>Tm8%6eDEW>TXHw|>RVSg9VrEbgle8a*4Vt~bwVqlnDjCPNcTC|<` z$QXJ3ny-o8m<(B}v)2=?5oaTf**27)%MkHQ10!bOFBO zX91|L%EvFPNv)XHC}dRziMv!WRD6mc$x)V~o}+Y&P{kYXrbbChw~>ZB2O6>oNG9j} ztr>K#{2Uw0}!GoPV*3=dfe zn3o8frJQcImSsWT+B#R)vkJb0P4;hxYBM=@9sZ*v_LP08GK7u%Ay z&6BGu&|k416)oA{cE|^Q&q2AF-{M7H^pnvr89M1v!p6O`yXZBWeNuNF>#~m|W-_D| z$7!#f6z+Tcp?~5YRJVpj32(@YaJ+Hfw{yi@N%B7Fk#znBorvpDld;svvX|3GXDZFK zqha={JPKxTaHsx#ap!toztB>?^{QLdYFhDpbh)Mf7YY7rs-XRsBye^z{%^_hU+ZE0 z-|89L+y0xHME{SPrXGg24mPI$rZ(08D{aI7LB@a62J`@xd|p~ zv*BJ)0KgI+01*54j450G>m~j#|3YVF=w|qTx|V;rm;a6ZHER8K2W$x4AJpLOi*P!g zo!zXE3Ux_#A>eX|-kqz&v@xh|q>7{!93wa1O~aLzy=f6zYHY5ir27`{6a$`f1X@g$WPQ>`RQld5)c16M(+4!8xgnnNHUX^H!+mMCtB;7IV%{| z!OIj%COMUm?ksT;OdA)^e*lSKQ|}qI2Y5IG;iqPKFtbbYjc)AR@Z0V^-0vB-Z;!NM zxBd2C`g$kZ9(l2Tf5N6&(cg{p@MVS|nK;GqsV%PXH*gXrFHP-)H3AsYfRUs2uhNn_VjvB&4_PunYp|g!j&4| zP{>4(bDk)^K#@g^k79D}r7CFSG&1{1qd97JB2{^c<${x(2_K6oK-~D#BO6_d1|2S4 zQlplaekg`JQLB>3&nOG}ka$#IK!%(onynX8U0TRiNp+3oZ$IL(X)O4m!vH%RR6_{E z@Fe8;q2p-ztGi;$9b;H}y(gvGCD|q}g>X~NN`gI4`nB085p~xaqimUvhnc^vLa>Ce z2h+xqdzbo2d1K?U19dH1o~G_99iub9LbgXqJ;uZ`piBq9Xb;F%T|4*thB#ZG&+#(J zn~q?QiSSomnpZsL#<5G{^;a4jF0+~PEzb!QvSR7^^q)_}I@O{Ly&%h$iZb3#?TSHkHs%qTg@2ZQ#%UFy@Xg*VP^=Vbj)R5}eobAPeuu)f?)XEQ17$$W#> z*|I(&cATdoe!5XYbo1TfvsNO=wm-1b7XKu(h=WV>9n!9-Z;54gG7v@iU*Hj|3znw* zX$&|A$6Vj*u&e0A3T;|&++xJQW2L)oSH(wyWR+MO(q}IY8|T^qnuf-a@`yiQETOPn zX=HFcQOlR?4^k;eQ_QUGj7>^I#&6Xv32dfyvhc&lMr=afVdjkJNF~*k3F<6_{RJ@? zr9XPsMBhv%>#f=4gCBhJK$*PkboCg!X8|7J;IS87_=~>GnZZ*x#^3759gYg02adW6 zezx!E(G#kY@mU>_Q<_jf?ySh-o&5$=L2T1E{Nnioy$?gF3DtU)gG0AZIp-T(5EA?WX6M8QVZv6J6Bo2y<2JccvE~a2ahc`k z@$_lD1}iSZ*YP%LxBq$eIK))+x*qgXUZ30XiHN(KgIpW_GCO0O%=nl@SFVw{7DJ9F zSdYv%*#8w7{|le4(gSV+U;scXA^;%z@8R=*fm7AR(#H9}r>DBC^ZtKC&K(@n#gGht z(x&C6`=Q}3u#En+jFXfl8%>IF#~!8}B3M~{f3$oG%JB7(OcF9`e7(EASXZ!eo%R2c z5$Q>w(Y1VKc!SIv&GQR#5bfV@K%oJrFT(*=919;1iu`R+{C5IlMpLA{kP(Ha6sd`E zLNfU0oE@aCSc$QJ@4*2Ee=5-LtMcbADquAn_<9VyZBBtA5<^CCfC=i6u&^C`gb>Ny za|BZS$GTL#BoE8ff}z$L#mkK#Igm zmV}joQCX{TL+FO2W-q*;72RMRGOP#HANg@lp4uTW0O;*H__yaILXUQjeS3j1#O@oz z-DU+cS8mkKOutjzUeWH{`J|y?*j3lxQSB6gUOM> zMghH#UPVbvdj*I>$%w@`3&D<&I|#bSwJC~Hnt?JRCCUn<*_08LT}Q*TXb>qwl4^rf zR`}A>Okn)pyu`tOAyX?WGg2iGmZ=K`?VYSZ7v}~#b|Sn*n4Zd{1~Vy=b!>zN<*%ae z_u+g#20EMU2QETj`El8swj2P?>YnV_i1~mpSl)47IQcDv_E^h@8f3TXg-%+!?`UK?6Pj|b^<-o!_b-U z^u#*b`^8wpytk^`5a(3JrrpFzHNhXu)M3lJMrn1LTUUhpWvl0MJ-?r(HwQ->$!&g+ zW*5q8mT0S2_OyU}i?CJg93Fv+A{i6YEsWgesHTF7uc&3zJLAZ=7x_YOq5!zXw^Oy% zOVZabWtQ+W8=5-1a%R)t=tQh4=5*Sa5Cy1oaT_W6BCD0A-Yj;aNhTsiQVOiFpip|y z%tr-*fvB-|cS7^Q6Zg6+$`4sFg`(r3(#n${*&9*ZBo#W>?SF{-vX*fF`dXi-ce(r5 zrn(I^*P+Ggv);RC^5Up&sxxqT+1o}QUAk1CkUz^mtKiflKi@`{?n0c`K#zvl=W(l# zDs5y8Iw%c$V}B28Iw{y*nQgKJ9i_%9wq=a0PvBa~*+W^PqupKI^QnU5E8!!yEBlWfzOxVB@;7Fx6>(|5~`& z%4wdGgI`kxV=~+L{z>MUylU!-+vDwQuMr_C6*Gi_*MIm;CK7p#j;S}H$}7w=veZ3; z!Qh;;9CE1YOVh=^$=r|DW-#K^Og41q}cY(*gjJ|Nro8n1~=K}fZYXjccByfp?MYl*aQN%eUTW4`3T}7vT zy89h(I7%fFQP02`zNQg_#PI#ScApa|e2e#xYYa)zgnD)@9H-PdrYg|cL|kNTwbIM? z{XldG#ijF_90cx{5+SnNHUv{9WYh8a%qXXElcbdu{0+A0a zIxbvpp?Q_QJhW6FgQYs~G?Eg)zP^W-j*h_EN&aL@NwyRMdt1(>a4SI@RSUy#BoHb! z{AQJ@bdTPd03H8qdkf~r6P^B+Xc8r*#L`4on2iw-0GVT z1){#3;#AL)Kh$KJf5|mRiJkl&SX97|QEYUXou`+O`60Fz#3GQ00NPQ)pQ-${S5j@M z>8!;(kRl7YN<$8(B7P7&6<5u*wXRPiw}p`EP(5ZcJCznLZ*yG>4RmcQpj@~xh`U*LJT1)>-4u>8i4O3_F z+?}WkpEYfNZ;nxQ^VwJMt9Qi7oM3h@<2q$T@|X;~-Yy zxh_Q&4s$&t4)r2rj9(xJpKpo1WC=l|<=jp$w)18vY49Xxs`{8T#)do33P zY!Y$X8AYP#C~lOX!*=ET;BkeLKdy71jGGhgoZX%_)BDU_QA1Tu>ao^ky^iQ``B=#W z{}@b{P5MhMzTSDI;R>%2trH`Ldt?OV!p5r9BB~+ev$NBviSLT4rHFtCY^k>yV<6Ln z7Ebaat*(8RP^Q$OdB0h<3eMK2V4KOW$Y9m**pg@rqN?N%337jnwf}JTlB;o`rR(8I`+EIV@re6FFWC*<9 z!j@Gor2MlZoa%;>gIQ#H<6V9zid`2PxG2(!nqz)OQPZ+HypRPAjZCuHVsmH-$cBfN z9n6jEqQqD$9nWZdz!2y1j8@^_w2D>7?RfOM&QRZSoqWE zieTKqub5reWEY!5SGr7R46lOEPoFPZ=P}~-OHCy61aZ`AWTuHsK|)2~2mhyOQ5$yK zoQdUP>h*X;#x2}hCtTcTlF_tM4O$qPgF#MO0A{WDbm(+n8#j#RC^eLH-4%70)e*Y3 z0BBId&iervWN4-#4J``AX+@qYS4=tkvf{qi)N$i5cSRgLVEo*ZEvrrq1Bgp^l zw}70_Vj2M2rO!Wt(sC? zwa=XoHVF6dOCU)ghi_?(io>g=N1vx>a~i$%L7ZW0^6}Wrk2$uo^ebPw$-{v`crad}zsXy{Px7Q&q-m?HzQ27^_`GV=Z ziiuNP@FH~Nh)kJX1twEnT{gLTg~pp$;45&+O0&AY`jw7k{D-6 ze^Wnj^cAOgwuJ`EcNPA`T+L_j*l^8KzK*Pw5T2t3tfEF-Enh@AX5I-}z^yQ6Tww3m z5vae;iLv*&kjkCB?-$?=WmUn0#cdm?xc;u|+V-}x_9lS#H>p=;K6G@KP7$t3Wn5YZ zJu%cS+rMjHmP5Ub{X7od=3R97xXH6R&EU2h?!8ugt{hJMtKIy{FhuTKI}Rgs`o-qd zY<}!^2^;(%?q(r?xL$|l5Uq=AZx#@G4&J6wvGIxGzrUSkhA!SEUV-8E0%$Do^ zMd3bom%rQ?aMqnV9buPmN=b5RZ@JE(2Dm^CdDq!811HAY643tL9Om&f3_WbM<_jjO@1>gEjDDcy2vnOb zABRrrJ33uczMiqKzR7BVfgTspx zy&Il0Xl5SQE$HpLz_BnScjtC~%iatH{9skVZ4m4h@8v=itQ1XqtHb0Lcnb)fnlTpF zX)5M9I9i#avfH+=+Cs%J?2PIf>W*j-g+$`*q21EaWpi&=4xY!ycC`Ha*%>;IN5G+mc&2VnyMOi=)U$iJ5@ z$QoMO3I0=lb+dHw{BIM5E8Yz^Z1%*PZ>Z>J!eqDwJm(inmKjW~LfqW3#MI*K!w3kI zAeFeRt<9eBW4q5AZPhlGxtQZ=H#wv%_>TGjA=Lr}c9gkU`rHO+x0Kpe%_bijjg#~2 zr|<85{0m+9C}G`-3{w&}gJKxwXrV zL&zAvkx}DVVgr!~qV-XuUDO;bW(o$ezO`3jF;^rOP8ukde>}c@{;Yv!Gr-N;yz!*k zKAf-*R`p+b-0~XKc=3`nj5()9{7@FbP~_TT>Vv2`^MU9X_ZV7^yfPu*R!bmW$fBVt zHeNRJ=%Qff{sKi7^Gp!^)98?1>Igc21>VP&TmR=f9d^|GQuQ|vjyvMm?BIBuJ3R8U z^KX{(9l6jT#!l>ps?LpzJrfUi=$~%0uMPb*`N53~I^;ek3~?U~a|xh)OhRr^i7ssD zz5IF!QmHqssUlGpi1|d*@w7nN1@Z(^0YvR68`L9?8cqTXqI3=|Q84<`DsWwnxd-!E z|JfTv1Q~MzJ_i9IP@M^ldXzJvK2Uu=Z}8O;&HhK^U8lJ`^uNj~HuS#mb;NjOlOsi* znJ7e*f$EwQgPgNb3F%Rg5*D_eA-()4HM3!l*0TpIiaH)68fHFU7}L%eByw4C3p^fR zHc_Gnl6tjwKf7nWG)DU7KJE8=O4cnT4Cp>I2-jm4bjbsC3ayH(~uAh3^}HRLhR0k2?3SmKsTg4u7i}nHjRV*7)y1jJf z23SA@5K#|k(T&Rj2BG3=$`jDB8VuI?Y8-$R(gX9S>h1knnfBALd+iL@k{hhfEl zF6f~eISmBgsA!Xff&||igq@$+6>*43t<_8nGU7wnQUXF5kObCX3d#)DAEUL3{=mxN z-`;k@AVein{02RaFGE5FpTeD)Ars1j%rv90K>~a9nqbYk1Maf}VGD===;Dx)$|z-u z^jY`G1~r7n^v&!$goL;18lmj#pQt39*J^G49Jt-uF=WE|wd2QvWA&aqdgqAB=c@+L z0t{n^Zqti%yD>Mr@iz5fO~Dd2n~NW5z<9b^&lAqTerZ z?gKG9k#^ZpX%v7-Bxlz!hLh8o=>Nc&qHM7zpu<(R7@1Zj*f6fu4$}#M@rbV#E{z^w z2^5_o;;|smwS8Fq-Ie<1L5|j{=^@rJx>*ZO!V11WhK~YZX#<7NMj6fy zq*$I`f6WlY;*0!oNup_1Zz?JM)7WrcZ=q;f?Cl)?RU zfCM*EEN(3Tlc4bgk;_yTkE@aabi_w|cLVt%9Lmk?VV0G%sna0&5y~QH7<_qRP(?GT zj$o?CD#fcS>>S7kh=be z>43_Rn-liMN36C{=N}NLV#f)soc<1BEyrg9AH7p!hevIS-6)m7hMj>oaYn`PSf#?h zHmS04;!v+*r1r9v-8(7{8l{t?V)@M9&^T-2^iNT0><<$Z$-2<06@ACfK0?Sm7L#0Z zQKxHG39%FYL*e$ys#pfrK#f|YKt7xw`BglUg#Uyqk zSE-}z{Kg2GUxQP4P>S}heyDBz+neV~Umw1fAH->b`@6bL^5&i5k|8?~0|k8tdA1NG zmF)edJp>(WPBYakFMP*^0c$Jfd`vKTC7QpgSXj@rryxN?2xBXDIK@hF@Su_(v#TBZ zi>tf6GqGUagTad(G)#)4Ni7#(!zNj7}9e=mMgd3(}%usvHJGBkmV2#)HH zQ<1QUWdFd}=jB<(LU_a=^$kNv4Fjo^(UNqlK|WtcKT9CS{=WeVF5c+Tkr8k~1<_?u zVX=X+B&8>$#|jjo>3T`&Acho1EdFt>Y5HJ#m?iFx-jx)8MC=3;_!!Tc*HTN8@l|$2 zl1C0S3zSr`2z<-D1Kv_24rV+7&;Xu*>hNRJsXw(SN{T$Chm)Bi57v0YM3>n?5=#$q zUZVL4&m;)1XifMfhXyDZY%$Wn8hZvujpc|~rosdn1hiL7LDLK&HD3WZytkS(YMugb z^5aYy_%SZ!M2zt|Q<(M8L>vaDR`+R0DSdX=p`6~?e&}-Kl}<|Pl%Y}g&^}~8Y3us! zcy1#8x#Px}$LYSH13=>prp&pMw!%)3q&oF}sX;k|IzfqN8bL%V+v|1^C8%zS;^9|ZI(f^j~=qEe%J%o3)P z$Po27#}ry}gJoK%3vLGprk(E z_a#Y1HZr*oMUovNWUCsqMm;JsOrP;~{yEsTA1tyKfKbcyP4hD(_cVQw&6&W_cI5b$ zT`+5ZwT8@4&MP_ku_D!Wk1O`e6CG$Lz@!ICPH0>M^yqTNl-1gl8YF&3xTE~(ogb;*57+fA@qaYJo8La$LFsCoZVxb+ERxJOGGqlOk5n}hF za~sM%6S~r=o{n@Tzu(e5DwgnZvTM6~N4if_Hz_UqG5^GuA*k`#6S9#w$@5n)^`0(% zX@ndk_v11FU5^3{2XM*8N{s1{baK$_!r_yl5Q1b$*XZ4{2CdbfbAmVW`|%Bft(Qh#*eNEFvX}f>?)olwjI_6B2)fyeeDheJjUGZ42luYX_%m$Of zTG}^T+knCE^a{Pe(C1sZ*Vmj>Tv-AesBqd?44Qw6b$k^CrlUmxa8?h$ct*z`xfcD$%79ybGsE-Z@jJNPRpO)hz z!8YEdO5h8K46k55t`;ZXiY5h{x=~5}!>1Y%3e7%;EIF?TxT_qXng*@A;sG-qsYT*QGw95b^7Q0fxp|u&vakltwSpj;>)Rd%k%J2p3ucVRyz(wA}l?# zwQa65>)A)v9m$;@y0fuKhmWo3EXc^5*FSdL$1J3JT#uOVSbiX@#dTSSIg^ttz3T*l zz{6PbX$VT4B+i*V_l)cMDEFAR)zcV#CjMK_;XWnLJMRMI={D>e2*=&+jKiFj=TH(oNv9OVy+Y25ut}V%E8LsDlsAO}=u!$84<8 zXamsJf?=+Y{OR#@PLd1$gLi1nIMvK6}T?ul{w8+amQ-Q|5|Ai!C9927YaXVhhvwZ*%EfQX|gi)0jzR-Iw}Y+8jPP(`%vWNCN%JZZ(8zE5N)4R{phofiHxI<0z z@*MUOw`Cj7&xK}lz44^%giCj9n-!4$DI!MX#6@EL4zm3e#)Z;)^~#4fPHLm25zE+R z+q**Qq0-BQqH2so&|>|av|lY}-_C2+?y%f~xqKc4o?nGjNNlY&Fm;%>LiZVPGNK_f zd9(*EVquRoGw+d%eH2;{uOOT4QDEVua8->YDQVs$ft3B89BuHYul=+#Dfs^zPg03S)4|6hW;SbcfpjwtVBS zo8w8oM{diB;qr`sx_4Hlp1+yQT2{d5#0h@)MgG}d)8E+i`(qu7D(->TO@HS3=;nd9 zGj+x8{4u@x%{Wy<1!a3!F|Aq6ey-D%AtmYF!>6VTxk0 zwa5W!kVgvsWMLZ13OXyO-*GFIHwJ?GrF&gvExNxE5L}r5Ay@s8SNXvPY*9uBUnO%B z(xX+Kd;F|VC8p~$6K2-Ea=6YF;_mzD$(-GiBgQLv)^fRWfkU?QICtXROu&yG zWPh)g>J9yzkJq(ujfzU`LZEqm$229$aIm;j7W(nWz?D{%o75$22O-QymRL$iu1J_$ zNdCN0haFb5yvRKoEZpO-s7Gk$rN_7pBr;a2$9i{zb=}rS?hlCFkab_Tv_XxOcAvpjA+u? z^EWWCAFTo=tV(E==VXaw*x5;Wzy!g?BaOY_plPbRt$Mv&H+n6OuDXHYn^%B_Ck(7RUM)A zDaN*dKN=SG9_Ur95{7ch+FI^JZ2F%_)s5NfJg0q~m#?Cc0D-<66i_5qH5iF)9JEK3 zne3y!ziZF?jMGbBL8pD9zk)F{5GTyWi$lriZ(4l$m+tP%6UR$uPw*Pz_brg1LqjQs zk}^bqbpHWhU}+h+{MC&gu(dM%sk2JoG+t6+c`yfr2tX!Jqb90`j&GIdN%t9p7XM6d z8cNZ;o7l5Wfr*P9o2jEhOO8&vf1uyGPzK0XLD$ZvmarxJ&WeG&1>_IT!`la4`9s%~ zNqx{mC0B+O4UZQIlY=)K<~Fg+D3)RJGr^#aG``3i!1ARd2G)IxcKiLfHM<v@h6aSeyus*hG{s|hZLr7z`eW*Blnse?#26mT!~9n>gXDHfwsXpgV|FK;$F=x zvx9biNc^TUV8UBeo%>>QZTG-Ag5yFHtd!?OjSr;NLjp>17`L}k1qIVo4{*&Tiwf^q zJTP^MC{K#ZFfMF&3k#CkZt`-9JenrvM4#!kdJym5_C5)f>|PYFGxX1kXQG6J`jaPa zMoizGpICyvn7dhwk4Ep62Tx01UNom=a2#$*h~y#jz6f8k0$xIjuBv|sOPH~{Z_x{! zQ$wJmmqP8;xQ~BoPpJa*@fTF4IsXr^Jy_M_G8QVGs-cqbyv`Reu)Nc=ux;y^?N8d7 z>~E%Q9v%2q`(8=IPxJ1-x63T|xKUiR9sTmMgso!V#TD9l*UP)d1F9tf@)Ptd@7cx2 zmQ?F1yngHSg50E`MB%c6r}3G$QowWnbWocJwmd)uS)QPWB^lLPf%nw&q*por90EHE zS%?#KR~ER3Tb<_AV#VGsP;LKKhJAPTx8fMUCkgjknj`uJ`_BXeQ5ni#%JaFTGWZv1 zFB%Rw;5*emQ~c{mZ@^G!wi;?hjz*DFtK0p6+->V1YR;i=LO7e=Dt%mSN`&GK#Z-A6V1GNmuQNDgRkY%e!IZvqYBFt&V5jo+Pq}NSzxNQ4tk^RS>nvq+1&?zUbW70CHf&Q0A1oO zh;rtI>}vy(crrFOs$0Fi#VF#!c3Df{_^rfa8Bv9$ z2MX;fZmJT$*Iw4XfrhrBg>x_5zzKdwyYn8!(3fmGXx8JMTZi2aZ+CnP{2GGmo-gSD ztB*@hXjzzgRyfbkC5n5>Q0u&x1$YS2>((p`dBXaimFhvUSGf4CGQjE)=UYoloOq{A zBA)l=B%JHH%oZ}<1UFK{_rjQGnvm0qbGMS|0@p-# zF`vg1ZM|-rrWTsucH@TCRd6Ku-c*u0 z)AraLHAkar72A6nwVry~@BDFi0XDc-9W~#@*Xgy?>9W<`T1VO2{oL%mvWxARuDs+| zqNUCy}9cL2`K1|>I7`a3WHnfx9(jZOkC^ao2UZPRX1-fnVt)? z>DDVHPexZ?t?9#xpw4J3)UXwld!4{i*N&T_eiE6M>JqH`469#Ej<2v-`Rg@lF>SV1 z3W|%OOJ#_PHC9^H_w?Q}q*TB>dmyzEoYyFmABvP%I#uD}Y~1WO{(o*4WG4Flr>X=1 zq+Aj(FRiY z`{N5feTwtdvbs3jF#Qf?gh?GS%$fA(IH6x}@lHmBXNf12C#D@(IEM*skN>3UWy-K$EnW(w{TM^oB0Vw~q&LRHOrRKHTr?Fjb^le`@YoFp zeYglCjV`o{4JM2EGJdbsvYW6Tsp)GfX8il|DRr`Bl*UNDQj?x?{d`-~0|#cLpIYR~ zOSkyf3Kq%_IXaZNPd9KXVP&{3@p3S&Ae900@Lf(quvbMe;#oDcmfUnArJSKrK@8GL z`x-&bP`Gj(yTG~z6vQ{iz#Y)%QQV+oTA&Xc`Arx)^6O=s*tY=^xG_fTt_R0ySds%r zpFR05Jl&o!$EkY&u_*^!?=;FZz!0i1X*9Ky@dO*8w9&tq884zD+`;OM#+(_B*>g}e zk~uKCDrByy8VhIyg$xneFuj5QYAOo+yjg4PK^-c1^6XAKl%Q=CD#nwUt_%nLwR}m) zMG2E)M}&!$hjp9rtCv;$V>|9d!qKPpHJLiH7=LFT+Q_hYpus!Ltzq;vCLRfsl&Q^7 za>jL~aTFZ`QXMkQlO0QxgOF9A)FfMc2r8n)8cgoIiR1JthN9f)xCFd=;98A$3>QrE z-dd|xq*0^JacGL6&8!1KBAHsW9|=?Q9lG|*QAAlfmW@;f=+JWlz8k;VhTTgZj<b#>&^euEC_Br=?+7}2Id4f0PJiLKI)$ur z`^VSj@=zWqoNw`FOt?Pa=FbWBx;-C`JJ+}z2W*pMd!pSW^X4=OtI<$ z)7S~-DGe{o)l3VPQ!v>u$K|P-#JwAF%=(}q9zq+pO#8yo#@JbKAd!)zXFchvIxe+c zwT7c<966Fn6UWX47ku9D>9GyX>N4b+69ua+RN@qw2omjS3IHO1o@u*v|Vz zNWr5$_t~$n@GDisr@J@fV6`n67EvkZ0B8hnmtgkRrBFOrsO?Wwo7G(ld9gL!f!%qaJKcbM4(8}|C-y}7HGtu;88wd3F8qf5I^<-Bp_ z{q|99R$!{`!#{-U);i}5Vl5(~K3auw@gS8J_vT-D=Z&6~j@=5Fm8;RC)R z&Z#qQr1)!SSJTM0-T&;mnV%OV;n*TUTT-8(Om^*AeqCN%%a%&fRYn1`Y({n{u%wbS z4K5fyQtO|M9)B&%*l=ur2gvwFYN)323NoY-$Rkupp2#D(X%}@8Vwrl1n!n zk?C1=%;wwnFV+qzs05c?Zjc>ZUQ6B7^|{)IPgL$Sr1V!hO53mbcv@ob{03UEh^`!Q z19vGg`&hLPkG0E-l2)FA2y^;49%&K3X=DUZ+w~3hbZ~d1bIe+9yC#ktg}M|=MfbpS zcx(c=w4(V1=(}eU8F%vI>EN<_Y6()Hx~#mT|7VYzvGzoi4FLeeVF3W@|DMNHGBq}} zbTd_PGPHBH{MU~UoF(mCOzm8hO`Y52DBjL!(qCQ9@CW{6K=;Uk?EJ@m$9^5UXf zOjE2$*SM9IE`lB*!_aLOh1F zPzYI;5!lR$jD79kMmgg=Z0|9A7Hwt_cUsbEqB-!#bhus^@#Ni`W8~+hbH&{z%`~8Q zaQY)w?!ZTPj@(^DWqSs7=)ie=p?DNDZ2LU$Tbe&An~X8VD(%Wb4x>MhdRTc)lWvdN zL5uDBN05Ed=NM_wslK;Y{8_STKXHgMB@q&hxyLY3BN>6zf{b%rdKEl5=}_#@+DKSN z2J|TYHo6>DIF!9zBk2=dg)l|?mzJ0(xS=m7W~68`vzvf#`z9};JOe;ugeL$X1KP{maMapguzff8_f7s*4~p;r zGh6zRVdbM(_eVuRaOId9G;1wi;?ueNYJ{npz;JB02H_1{$4Hix>c)-;2x1r+lI%|I z(0{W=L#)M960k&BAZdgg2?L({z^7}Ij?h=+R5FGdg(^J;ek*fd&D6AyrEwGugDiEG z3BnZGMzIL0tRz)?rN~6KRQykpYv+fb5WST#4EVfmHVNHLZ7@XF_&E*2jTQfUcxd$evi}IISB8EP^=9(C;=+^Bs=|E?EEBCx z)3byXK<`Xw4do{c%+DM~O+_w&g2C?P*987&LSq^h&S`)LYE_dq#t@8WC9&Sb%1FH? zd63+sIwLv$j;D~b7}&psjH7B9*KnjA*<5wGu&M?qch9iMDh@542Aj4^RO6Q>HBHgv zq=gI9d+k&|AVx*0FI<-vII-a&|;~@%*@Qp%+Qir%*@Qp%*@QpQj3|H znbl&QzM0vz+q3y^XLi#pEUl_CJu|XAJUpL=Ki9^Vn;J#o-lT%MLw?}Ah zW|!#}ZBQ66#f{3Za2tQBZgQe=l&`Ouc5Yu>B@elVlqGjeT(MgDWsu~1 zFdvc9t8y8tF4K=i=$mr?qP%Zok1ONcdv!d$?C`}I#W}%+IqSO1m}0I5xD3!)f}pxH z+}s)6LKIf104d8ai*|xt%d9xKyY!fJNEcUXnoy*nPJGupVe58D(+}qV0f-IU9C&gG zIeG*!vmyI^da2doV23A`V3Yp9Woxo8iI2qs1z)^gk$qe zbbKkSGUD`Vd~0ma$&&&6D>&MkE9*3uz5hPwPsI`U3ZbHpX%4I>y;&==;LbgEKA6{A z=GuG;t*MR3AjG-ioqxMB*q^21k-HXeTpC^42lViY#LHx}oMgW2LNT95;R~nVJ-w8^ zYq?}#(fa(O**R}#fm=&Z8bvOSRC#hbNabWP*7pW9-Vz{pz!6a-FhxJk0G)@=N9w#6 z7{c^qe^eEjmGZtW)5FclbF05{HHD}Oxjtv=D z0L4m!HR6?V%ht7}iwW`t&NBrcnMD@bn0>`iQ6ireWY2&g?P;>Cf$a>nU|Gg>mL>q? z6%2i{ngf$8!tc--N`V~0#0$;UY;$e0)xKg7G?N*i&PeD)3v0FqF9~{#Y`@sG1b0a5 zM?QY&XG%lr(C|N63-sMp6l-kwi}fFN3mOO&Po?IAo)QJbrwkjzopDhnP(A#uo5{P#+r{%jb{1xQ7_aLV+Fzz1 za?!35k5eqNiD8u%?*t*hiy3`OH~ITY)5zXleH$3NC9?uS#U<^Qo=(qp-w@|3H0KGI zFoHsk-$d%ar9h8=P^ZSjSw(D(ttL%Gox*i~O_x+|g1Xb~eW1X@0P^mB=o#oRt#D%# zthvBG`S=~J6aAZ_7RPq5~>ND#g0;k z689;bhV7I-&%S&sFW+M0I*dg9)GSu!230HxG}cb*iD=xos0-!-ZDSM0lJVwl@6#=! z(zUKKN_86!ssl}TM!(Tu0-NLGD4WV~-NwcXz&asa<2ht19I$;g-~FrYWGr-HnpVF2 zy!an|&mn?>8ek^csZEjPsI4^t1|s`IM7C zu9{vU+DPb**^#h|`d}&3^1#nCC5KN-KIQ^2&505NW40hpI0GV{=7$cO0$deQD_4=l zjq@1AG5UK2Q6Qg3>l*QiZJTIe`iO_Np*t$~8_BZ=o|k@=wiz2ujph}u?#&>sL_6{% zFD2)^eq1`A*QM8$IBwBlY0cvW1>j5iZ-+UZvm}9Gfna@A1r|ph%k@hB8d}Y4MYDvF z1Q}ED?Sv4exkxs1q)($)jc_xL5ni_|TtI*mXC>Y3m&2E)-D$`Pl$iG8 z4os-oKLy2oXMZY?z#yiNHlU9V-X+EsV}1knet@$cFkJy59wUe6Oi%}Gv{{b zTbhZ>%Sh;lYvDdIu0RtD?YVi1$W>%zl|+?O+T$TpVsb6>@ zeyiyR`WTFakolow0l&>vefjEzLHY7?mK?gNh?XH)g37#QEqCG90CjiNVprhxbk=Y; zfqv!_Hj+6`xmZoJ%PudJW#G3=*erpA%?}^2%^gsn8m@((;0^lB z-3WWkq5F34r=vqrAWMa_y7Xj9T7On77Hluq^GlkvB!9*)p%K=cINFPyS1>G(sxJ5R zGewdLV80Z&#`up7uy=c<+(|WR6{(TkMg$mfr?*GYMg{bg7hg#kP2`Y(29o`?D_EeyqtFJ=;D_od5+X=s$VzcP4(Z3hb#Zjzy(a8*X)3F*7P$0>RyNN6m5gGoO`CmIB%f)$zVif}=S1EOrFor&b5{@$v-pOrEz}SpeyPU% zYU)>E3JtxEueZq?DPj?hRk{sTax68%5T=1`&-l&x2b@Vvi5FUf>r-pJ`ao5U$q4E( z%Y!N=!!`)j9vk$Tn+-s7)NUSp5MYPPtD0~sx2l5?*gm$+YCZtodI(VUdq3z@$u zP$#DwfeT7Kkk^aJ4NSLAPuO0?7thUf=7m})HX)R$rP;Z7cnC_2R22zElb36)pe0pg zv1BJme}HgkCoZ%qvG=d!i^q<}ye;2Ni+;8p!M*vFZlW43hX>b6HnI>+f>?zLG6}FF z8-$|se|@j~XS*>Q@Wo?oU0FS1X+w^DnB0Ruq!RubSeU_$QfPhfE4tR6TFz|bk3#|x z1RHY~43}hXG>2ZJS7b&;^iu(SDkiS<-DZL6eLJch)&HSOwED@`)=^+5c*OrP!=39| z8CWC$gjwSo)V#|z!%b_)kG7lBkl@up5s_GE-vEp&;iqdK@`smT3i*}|*`~MlFXRXg zi~QYN7`C^ws485|tOrphxkQp*oYkaDDO(vU3VYH_Y{HY%?e}WbVR7drN}Y57B1cb^ zMXI=@dfJUu2uuvh-Q#|FnL*JqzuY84Cmo|*qGQWusFykjAJ#Og;fBj^BlK6Lh9#^M zS8u3Vx@9H$>wrP6p&YY7MqJ4%yK>gcM8Jiju1WmkQLPDiuy$ZF)Qw-UYVy0pC>&*Z zGT1s54d{<((B7OS>=ghlwU-R+=Vys=Eh^U%_YL*z_B8htoC>o{9Q85Z_B&&b-j_QU zSw~TNH#nLHV<(MS^@a#ge1N21RS5(sO|)v@Z_LzG)vQAhVvAWEzF8r`-=_Qg`H24{ zE1Pc%W&~e84X^fP7pRe-dN2;|%~`V3VOI8NDYvjp1M#5AmL$9Q)|#HNTD^V>YX(QK z!MX4(%0HXQS+qBRGn3?A62gF6OeLB?E@5JN%n=Xk7Y=9scz%eZVAT%4!MG=?nINOd z&TcJ*u8pdHu1U{M@f4DRN-d|63Xz^Z^Cg7mxE=;go`2ga!|1j$z%1tVKB4d&!Wx@$ z(U}a*f|omkc}>Eyw9go1z5B7p*&1A&fGcBQJy}{cEH=J20DY*(06e^kl5k7HjGw!4Z0p|6cj zRWoGsA4I8tGG-tt?BN7r6b?EBy~zR#$UG_OP0B(iIc7mv5m?0(xK!NFXL0S z-+*W$w?~I*{y^Vnju%aKk%)QY zn(XEr1z$495R3*pT+E8i*Z_EdSYL#v+k@=89-X6B)-{9d@zPR;1aSkkvTQ^*a5>g_ zZTO!M{1mgqmW8$l@9cp@h8h34L}SEv&J;7WT;ax_u!VEC4!eliux`TS$>9B^x=P~< zDM%tpee0kWXv&~;_Xg#}_hudzF*^6|!;R4y3l2vQs5n8)O(~jZd+t0v@Ro~oocir0 zL_QaGARoZPm@XbU`bk30t^sxOCZn3*HDU~XcQohyJ;3=*IuUfn+2#C&9l(3KqabqvuX z2|)94kz+=j_oPKA&f)}N?=voM0_gPkymz~-aX}lcc+jkfV_PCwK7)(chbRT}CO4;n zHOHx5@E%eYEAY?1XgGAHO{c@t>TDCT!w5o)BF>c>Ya}^XqXE=3y@jCfW*)}nev4f* z9|Zi=p5&3xF#x(>U+@R`Lw}Q?3HYVVrSD(biErGHjo86HSf!V5F+O^?fmLit8+l=q zXy$8!2H1Ll$ZmfR^Aqus#uL%Ef9SSVoNZ>8iTwG6NQ!U$$1$i8cdMOwU_r`2jk0>* zN8g4?{F^81OwoVRFR)IL&_)oK+_{AX=mHTQ)Y)JfwlQQfmDJz(DKF;U1UY~R)6dM) z?d@fLk|+^UWZ3-_2*D%E20pv7{E>sQ*2iS%X1xe6PGSO9pRIV_MmSCb9mA}ShX1&J zE#V2ebhwyP7-F_K)N=4c71Kn`oB}Jk6mGTt*DTG6E`Rf-GvAAFPTBHWb5V$y2@%j# z98sQ;6I;flT7@Zp8UCHsTIPj(fc|t_0JhBy>U4tTfeF?8`ugL;oLVOfffWvvdQrKw z*tqa0-kR68*6kHcS+obuF1w<5YGfp(SY%nsSO#hX;#S|lf%hQ_IiFvnYq_=o{-|YF zYg>0u=5}Y6UO@Gg3a;~yz?j&L?jX&EGBbXw&*?{d^RUgDrTNFrvvYF_cwA=lJS~%H z^oserFx;I(klXoIiqU0XgqTASo;Vl?mt4NWCSr1e$g8A-1?h>artGrxWGzBse%Zh?x+sEVH~}Wy&!%6SQO?kzaxu9P zFt-|Z4j&YD^R~J7QkIRQI}~nlEFUEhW6{641(Coid_K7PhgyE3E?c&wG_`OnEEI1# zu_UP*k=>!U4-)M@hy#Y6&sH=BgEWiG&}7arFzHR)tP_@-?cOhx4sv5-93ax5VC=T@ zuLy|YmJk}2Auq{PVB zDx*T^y-8zl9oB_e_D_tyVo7c&kE8%m6L?gJBR4-4*Ly;G@SXLB=oC_^ZC{a-#3MO< z_}_Q(ltdj)uyT)1dm~D#Stm$ad@5J|ILY30m{APp$qC1&LSe(UWaRn;kk3jzOkmu~ z99tG=>3zwlea7RoqO9l5IR7*$u!dzD~Un&!rSIU!iUHBP?Ou-WeR0}{;&aBv@KiQb z4xLA#*kOH4uGF@lw_GZgL?y%|2-k8r^EW|;(0bD>u*ZqnV_r|HZNWHKOJJi4_XBmj z&SaBu=L%gonyEi?pVsznHm8U!&s`t}@06!jCB;X<-+xF4^f&#!Gq;~fflUo)pihaF$0hzHGW{a zQ6U?5{)r0h_N7{{8CN!fc69%$nXRbZN%pPu=oH_{*;=~i-h-o+49!PBrzZkD?~h*H ztC3b(=7$zH?cu>T&yLPyyEUi2yxH`sJTRSVUNy$0G)lV(68RU5$}bp9Itk$DkU2CD zJOTc9@@#iKB#3Ao0|nz@1|kihbM4TbI5OK+7SL%H*XHB4)+%-oLwyXd-B$gtH8+eu zJ+h<#~t)jKH zHu*Qo)qgtmPy7E+u7=6Wioro+{nIZv32|Y??>*vo!-V`^%K~73uPy@sfWRb#1(aRa zE)%(+`}q-r!C<&<0pU9ujZF(&>MQEdX25OC|kefV$r#gZ3;t_nv)O~h3ej#bi zOyaRfr0dR7Le|W1wl*TM=Bp;F^~eTPPR~4|W2*r2@MOZcr3&Vwd`KLQdLVe3aZcok zzaLWlaJ* z37cm5t-KP=)D3l(Z2;aSZ@Fe`P>$X5jn^JCZ9pHNnZYe58@>0PKc4xl?L7d(ksAtv z0+ru{xZi|eBC^8Og8KgdTE&R}_y3sbw@Ums^nqG@Tb2DA!u+SP{THSG-zx*@cW+7m zO&LmgPOAX`;IIEshSYH>a|M*)pPPA!`9TKw;t~QN1mDS@cyT=BLAq!H!Yq(|egm0d z0?x(IRw23l!GY*9#I7kU6=y-cxzJpEm<+5QDw^i{BBS#Q$9I74AQJhMqFAMU?I}|KGfOS z`Q+wCUZSO?<>B^LKzkRo_lO)54k|+|!pt=PNlY95K@TH|CVf(bkosyE0E4_EO zy_I?J<$JIUxMXo+VSCw(jEudczfIQG*6y#hf1Bt7G}72sENhF3Na8Z*Utis)seTYN zdadcF_Ek>&v}9E$M#?)pG%ho5TGN|3J~ojgP1rnFxA;m*o&8598hi)^Zj6+Zm)GXs zqn?fqf!UV`n{F^Kkj{UYHF^(-@p3qrPH4Nq1@Gf z5f4!y$ zajP+JfivJ(IXEb=B2RXA^T;rsB42$c!SCNbm|0ki#h-mkXf7}xfimEN=^&7ik%J$f zpI6|5nlKS+SM_ykmKWHv6w4Oig8%&sLqo&-zS5h6PwL7^*Ka95ad1dtMhqn%-E~GU zxWAuKT-*=)j?H3a+LWM)senmv2hJ>JZf;9SbaeE+%aMN$p${1#z?v|1aZ&sCGxG8X z?Ax@BTGs#3kZ*l~n@;TO6Odxz<&8$(_aTG~5=^P6sMw>3ENEJ_+@_@O1#TGVj{k&B z8b9<#hYk|0QzxbZMN6H_$@4zbtL7sfV*s`1D3t!!X28OtQIGhW^!1HYUDw)pqWi2vIlRk>^s`?lcc|29ZBS6>; zwH3~(psZLbd63HXJCACV0Y|a>czq;H8DG2-1z*_M(B94y3Nba3F9tqS^t}`fCVJgM z{Yk5*2sD(#>&3}MPfvd!8mz3Rra(x$y* zZVm3EZ7|M1?roXFR~=A4jS!RT)fk}{Ftxj!Pr|2-*eo+r8@xP|(X3#Z&m$u<`CK;X z%k+-7Y)3YEim|pd_|(4gB)aCiY&vMHX=$0?HQfDsLu@3>V)hm+;yNcN#iF+iv4lFa zy42U-RtVxd*ox9)|F#So2!Is#)+96IdW?9gGk&M{?*+0ax9kB;%vC6`S`dMctRC$f zotohn!O5GtB~l-OiUtnw`!xdz?u2dd17Dj|yfMb6#DPQGxe`3#>3AAv`KJlXnj_9} z)0tNt!m_66-nj>5DkpQ(b3(j&Nh=|+@Nt^Eyo_rmtOU7CX_)c=fgK6-kgfOvF_>8^ zB)kdH4nDnN+?IwA3u7iJIC9KF;#{r(vM&~k3d0Fj;k_Brz)b?BF(fV!NtaixU}|h7 zaY8;bGV%!2Bcinb@ML_gE%`g1{{jkz?+A$qsDPp?qHqg>ue0kfoUM<=bnsD{nxKt( zO;beDA?e7eBU0gUVBpEJ`Sx()AbRpY?Y{XL_*c^&MyX8vW7k>yV0KNT4nU*aYv-s0vw+4dhkhbQNcZV@KVKu_=v7E?{9TB#m9V=ur417;V^A9LFVbl1q7`4lgPk+zy2-&tE-jmb$I)^9pedI^=tr% z!Bad6CTO*biy=s}>2L(vz{VhG?D%~LWpHx50U0mx|G!un&o@pF z4O0vdM0w+2Ryj=n4l3Z(G}%*&`YzO^mITh2w+i@W^?MGNfgK7^{M=+vdm?-f|9vR@yHdc%i5w*(VPfr+bkcFsd5if9KpihD*>;(N4 zxfIkBkJFAZTe67-!gnpu#Qvrlnr6q4%)x1}N>m->caeO*yn9_T`enK(JTx%~P=P3b z)4p6X>oDY(xW+XBW8y+BGo*1d%}ZwIEI|5XtKCRsl@D+v=12g96?2T403t7xnF#CR1f>`hzYio?Qp|=WKELGW+NeW3= zG3L&Me>C7sgUatRfh89J8fkF`va0eEV2rxP2PA8JaF@^^oM0{Kvvqss&-;DAxk%;Y zJ6aW^mADn{-%Rt0n~*ftzihwQQ$J#=`Z zW#hJ${AIkOjf191{kCzndUuYZ&09E?=JpXnWepY^E zF&ce4CB{17kXgqirQciJ)_Hs134aJM?(f- z@-?ZM#f&z7I@v5UX$WV6zSwCAFPNRDZrt&4eUM58_QT_4n^r1~u0Fg}A*01Z_zdz# z#}6B3QoSgy<#LLX)iV^iQx-Alp0Q-=&$4l!>Y6%eq$2={+)!v<$DlyN-Hca6%#>w3@I=P;}YdOkHgvBC>oRz7p;8a~$}? z3*jtL9wEbG8L2$ZAk9u>gS2onTF|9j-(DnJ-DPLe7b|B1ypb6(>p5rs`5;jZlY>~n6;Ha`_e!USM3LM-UxdA~0#!7{Ptrs{9lsaf=j-!V$cWm)0a3$3|o z7?Aan8|s6-G|+icdzQdWF$|3YQUG)??r033NVrN(-B3MrM*CLkjK^JM30A|3-)};uLGP18~Qmfu|ld{BD5HU7$pfpd5U3c||#~ojP;RT8?or)K0 z9b4olImB7xgGRJ|y}waH!@W@bbsSDBPV1Q-oZV4?zaC#8^xh9?mH5qe3`xiaBsyGm z5r$O=sJ5AL=m*At4I{il%Fv~;>6`-6&Zn1~;;)ZHf2*+NX;o4>!m=U>G< zkO?@rVZ?zxGb`T4=H_P0;Yds~bXslpQ1k-5*II2M@WCyX-Y{IB;7ClSVi{lgX4>~v zmAgWT1W0Fn85y{X^_FXyOxD1lpt!2@s`hqH*Nc_97zA8yS7;zkr?Z6-t!B#?3vqEs z2^||$RMg$FvN9#n7~BtXO6))5&d~}uVxQ+!8pyp%Trlgrx1=0ZfbV?T$U{PE)a&+!;)ukGXX`=` z@VsrVHrnoQF|rkZMiI*V35NT*L}Kv$?icX>{(h-cEbWHa@^jUg3Sd?e8$b%B#d*4< zxAY;se70CY28YA>AQbMaqM|Zcp<1Qc>HYEg_g4OuB*$FrM=PJ#^UbatVlI(bw9!-= z(=i2ibP7t8sGcr5D5G(oKiIf@TJ@hllI(VSgCDN%J%`aRTCX3}asbp$5pz9lI z*@p<;iX%|OEV&o~q!e16*Z1e!{Srl_a6Ms)*WyppHFPbRfjwb85a^XwJ45hK@+XA} zD)CpfTdgczMMZl^zK(@}7+dJjD=tP#N^{8aN1e#zS))G~kXxCYPUcuw_ZrnZQv9@u z@A8qtZ!>PM3GOo#bXslphvuCIf2huQJR4+zW{Xb1hM&K)TRyOQ0*8QrfKZ-(&LoD= zX}8AOr?Cck+0}G|qCE6L;JvdkFxU~3>coCSEKvp@Ki+ed*z534-!Kq$qV1csZX;Y_ zymaxxeUGQI)_L6S4(wX9KFC|DCe?i3uUhw`LtY=xQ^J10A}S(N9=B+6 zf>iiz2Pf2g{mG~FSfeg%sLZnZ0|@PMDa5nA9UmrlamGQ?CeO3 z!M6bUQ*(fhGMz ztaAVHGecst$32bc!{z$lqQJnwlAG%gd13dDgRz8&8*LVa#xv5O57T#h(Oh>XAt9jv zyOX$pu_MUKd#$kGoFgXw6XKA;k&$VBe!aXww%w+C+q)%%z`bWn^%~u-?7@2-bUIy6 zI3S;}&H{ zm;VBXuTqk+ndkp5eWnJ>>nDCetSv9DD6JW|_(#A_a#5+VZe+^8tK*{D?(RCxNjj+< z38|=CJv-*_Rww2(ExZ$0kNi*Cp$$9;B0PB`#^e0VwyL;aHV+nTcYst`bb5*h zOv3Clj^z_%+-TDQMFH-S>ys6G%at@#Sl=Cd)*7K_ZV>&6TB8%jf$%X! zm|f-9PRZgn(n8xDs6{-9b7RQ_L=A>vvS+pPE!D&1B{^{g_I(9)9QIYs#C2uKQES?a znI4iM4KpX@5rB_cAkGy*TLQBgNpkCa$G$GO-kPKAO^^=rF?ppcOCx7fN_)hCKRK=~PtQ0xJr><-1px2?|ACNZLA>LLG>x!b=4`F= z@euenitiQN15U5=av>oJMeq~R{d1oIX0L_laY%Onp^p@@=e zIsER|hgsVbnX3&BChIg`8^kX6Z7LKY;5 z3xIL~c}1uN0A<;;Im9xcoZ(-6A_EHadN-r&Ud~-_Z(9{^8Oe#-<7!5wwxiegV0vZt zvDhe#t6Fcwo&ENDx@G)HX_~LiW?_2-La&e^Qj*ew?Ss1*F=bX*}{Ys}{T2`e2Z zcn36ivmoPXH5b$Fbn0F;M?mKznsU%x*l7J>tLcV18nOK1k^Js;ob(34-V^ooB61bS%3 z;r+hd(>5Q?#ao;>s#fNSW1mAUz5P_^8Bf_iIX)@Y-xqYCnw;UY_Bf;ybFIl0Q~eg(zydATu0miHvr=Znd>ZJ6K4!Nmr%8-7ALV<-Y$&U&$2C8u&( zL8|33x%b;i`}yrvG6!DSl6Wo6LUhMzYWwE$R-MkuQ zuzz5`4nzghtBjvCAagrpDbjdmYO*C+mFr%YT*5AsI&k;~%!}^i?bpc_seu(QFY--o zAGIl1?K{Gu<9T6U3YNl$&HzWv;LM`q`+o(H!>-Hd2#>?Z@GKOgdgTiHLd9W%EVN(a z_+;f4iVGMpGMEppV}NR>1&8(eV%>6HjWOG>G4-m;FYxM|c1HOPt*QrMXsg{SyRCZs z3Jpp($noQGKX;d@$n}BBxjON#MoDN|g9@4%LKZA+<_hmMA37Uw8@)W^IyOv%J>Jk? z80`<~grgRp@(n5%9VxvJfev%rZI%%j5cz7uyF8M|$pw&B61{55;FZc*a>f1%-~|Nt z^|@H85q$c7ee@tW9hPgUe6bZc12L?6{4073RRGIkA&KC@^ zn*NxPG&b{R#_TSpd^3Ae0W+IrOw^HveH2g%(FQZMB~ihSJ-Fxy#u-Mni}z|P8PYB) zSE|Cn#dVvjBB^4qT4ag}m7_Ny$oF;D{hED(YmSo3;B*6i8*BaVg<;ln;p)({CaHU*en}dO~W6ZsZz36EaNVOXx_iZABU+ zW{agVFpaC_SAS)k(Xy3;@)ow678LE~!7IN0OsDLs{>x;#!g{{};xYef|H|)1t<@@0 zH#J0ZHEbRJ0X)Ct-Mg}UY+<6V$Dv8VVn-ki8@y(1F8uQcT%sas!&L4KL;c?BNSrq+ z*BTQ2_YA+U80Xqit^^pyysxScAhg!OD?o<{p~ebfep%2WQ_GU)8mqql`KTw)HY|Yl z8P3}>H9(1VHz#6KEhU7?Ih?qpP3BwMTV^O|+IVIrhJCExYm(Vy!?p%Cvda1Au6gsC z7c5+=27vXvGx!4QtB3^xJV3bHPn*hSwpLiCR7(!_OH4zpIxuSY=0#1h>%rI*xoA4i zMyLczCDwRQ(CjK1Cf`e^A=jhgNI>jW%Y0j%`OxOK!>PgkvsFWM=B&b&l2- zOIsDthocGpvAzh#W7buj_SL4@VW4z#Snkv}acB`Bms|}+N~ewLDR54$&nn3ns^RcU zSscg&$pV5sva--k@mq4ok%n#Fu2VC^m3v+fG9@;ZLaDWAnCXB?348@Te@_;-o=W+h zabuXIR?7E60+VWORfeu=nSv>{Gb#rBCw8(ZFj?#ojKC4o(Atj*9W98EhbzPjSs$%d z(#cZAwHznqoohOE)W|na%nuzi&q&k60mQm}Dyof^D4>LHIIh{XkXhNLExhcA#Ss<_ zcdj>4POrJw5;C8rTOdi`K`_zk^=nZcZT16iC=?Kjyg5nhy!myfM=96ZON-4lP_d^= z#EZE$CydRaxcGWJ)YrZ}mA;6E6mXkqJq<+4@n+m$KPv4C`IGl{`Ji&N;@y*RLBr^l zb*4%w0Es**gsDdXzc<*fbNb9*NEwwtZ8t65hHpM%U z*Kf0YSDkVs5ae_Ut7cD1)Dl8gD`ncvUcy#$TnN-obkn@$6wFbmdl z6gfh0$_289t~pj?sNyx+_*r*N_E77!5VStw>4E0ZY8iImY?w|w2HdgFX`oU_mFlqo z4joDxxMsQ`Whmt{j${HVdX}hGqVda%!Qf(+-wkDJf8n^Fe*C7MM6mxml2-JKkYTFg z42Y5iSPB^OEgE-*f!!D)GNhvWaMlbafKOIWo7Hy%e2CgxHa(_s2>3e@D~kkp|E5c6 z{C8iJ$0Jm`qMj-XElerhWjvb+gw(QiV?bA-UScu78OFtwGZ2Z0KJ1L$x)Zc4 zR-|9zVh8urZU4^cG@1ucI0Ug4xgJOeiV!XxGyDzf+;O~jHVq_yW?7%b}M zsc6jbaX+o7x*m%Pv_83zC5WssyXg-8!)iAZS!>_$ON8|j@~k_u-A&pRmSZJ43;Dv)8R(6Izk*#9TGB|oNcolvDuZ-p6N)~ zH}Na_N0(CT#Y_IERV26s`@f$bmj}a@ho_oiAe;$7l3URv*Hcyac9wTygTa^{P zwexsX?AExU!Q$cEu1)A~QSuxRSxv~T#YWS#ldN5O$(=<_bEZ<;=wSpGY)Su6-#WZ> zqw?=4nNSL;+p9%-xwH2osEjH6Z02b{X3Gj4M% zsI?t{kJ)e2)OGupCN7@i`YdP>abG!9hgBdtBfO$&2bZpnVKkg&|Mc#&2 zcRM~MJ4tLqY4q5C^*Wppo=*KVc3zSrNGG6w%NrI=e}%zkZikBzo0I(6MmqrhhEd13 zNZUa;>i*)+M8R$vpS#gQ+Kd*=9V8^i&|w%(02&*Y?v8_x@0NzesCTyBdQE81P&8;AZ%2 zQm)KGP%d)1Ev;?}mQ8~83l`OnIL8I zx8tOeKqclyB4rC4!CZ@d&AqM1dyM-Ayo%k7Tx7G)ahB>PCt$|+#Y;rY>!tdSA#Ohl z+YCY!3%11X#KJYUioMGizS8k3bDwglr815t{Tp1)RF$8j-^CQ>zh{`+KUoJH24~t@_9!Q2TWp6C?4njv37hqyUg(KnqFlLRg4HD6IPIDs7x^N>fzg5mL zDFMmbv*TuY#T}WxO{n9d#v^{%irf8;BSZUr`iiHY%_)J?4E_^Z~^PDybrhy`P=0cGg=R7^d*csx(U*bl|{OiROiVa`X zM1wq`n6hCejW#n)fqi;2OFq@8+w%zF^e1XAJ5%D)AIkyf_MKMM<6kN#b9p=gUJ@kb zb^?tq$dB(MY1Pzc2}a)WzugNbX$C3YYS}Y5%V>~KC)=3Y_S&pSVD#Qwa5@Gq@ zE%ZF2Ld__Nli=lB9dL0hFfzo9id{P`yGi$dsL{)j5#arwWS@0 z`PHy<{bC5ABf8o95cp3i3-$ZjBFiE&5T;LW(9LEUz2M3a+u6$vULvvYsK{8=OSrF` z=(^3OU^O@v$Y6Y#D6RSdv2@yhMYJ?jC_#z4eW>9GyGesh)vy3xd>#+^%L`VzO3lez zLavX49DRu*u1eP4^GOea6xJOQ4%uF_$#m{Q?V@1hbofDZ(&DU6c3GtolEjm%r(Di( z(d*UszftqZ%38hzaK9|uH|o2qD(Q7PuM;y4vn>o;=O@jA3ft6epcV>$v^@@6wSYA0h?8^{x zVt_1}eJ>KM0ruM~KaQ!%1dbe%bx^}!xpwtE&Gv|eeUH<5+$-92qS|tQ+?}=XwG_ZN zz1&TQrOdR>YIHheZP=!w*NTFa_9)~Qno6qF%li{6k$q4Ee;4F|S#9b;4k8a6>Eujh z^^7uX?27RCZ&5mw+6w^D)&k@!?FZD+RhW|3r+d1@VPgVu{- zJ5SuCdh@4%lIKt!6&xs@0nv>I$pZiJv`<5qa;3X%3KvEce#Zd+50KnkM}9*i zPMaAeyu2QHEF;r<0_ZM6<+DDdR#Zbj!4_%GY^FqL`~u`#@c%>FTL;DQb^G4K;O-LK z-3e}kySqbhcLD@=3GO7gyA#}lTX1*x;Lbb0bFS2_y5~L5Q+KNNOwB)gckf=kcK2T2 z^;xsYd%G|i)Q$c-4HCUeW!~R8wZ1=BzoIa4|L!T6r{B1wTj(hBndc1k;{K^?LLv>s zDj_Dcp=o*<_^Nbd*|n9@3A0jIOZ0*Tl8kw5hyLDW+=2Ey%9>R0b8Jqc!aDIwucBdpicti^#$BlX zG$o-QCU$wsQeafq`ZMpw$`*rStf67?RJwx{|XUt7#DCZa^s$hmM$D>|iN~ZUcv$uJ9~=$x%Sj zAYd2@ysw{MPuZqzT|IzTKIKkr0Tvz=i724)gE>H~`Kea)w}gcdtUmqRPgw>ufUFwc z@&%>}RRNTB`R>>zY{I(54U;;7R>G|}rgyhe!&dCubIR+zcSp**Xy$GxS$Ri`Uu=b} zs{3u%%(e_91q1l?pcEI4gY`M?FfXRm#ml-}mDaO-(uZGQ03iSwFSINRP zxqj_wsMSb>~`96f)GyRa0o<5`A^7p@E*nV$?jKp4e%=AydCwu+ZHS6LPeN?e z2nBcy!|rUd`h6iQSYG4gxF2p2e$-|wlk$Dj{Geja3K~oHd4!}>!Tfz#=Tv$#>JT+8 zU5DRFDicA;X9uh!Y0$mlViuB#3HzGxQP5k9wEQbAQei>npt1TS1g#Z0{(V|N_32=4 zU$ra>a&DI_y|&LISGBoJR&hj4bg%d;S;BjQB0+xtCZ#<8j9c!X+o+C8Vc*D{wgH9{ zq5Q=Lr~R>R{N!o%r=IQ4c7}}Np915L$DmQG%?*pN;Q-q^v+5Mp3N({}ib2DD6hEyp z3`NNy66j5Tjl~3vsV_~u+~zGdn*BftS?trlmJHS)SRld8q;x7gc{Gk6YD015-4-#d zSNpXCa1RHTmGG5|Vw6h{pUa#gf*Bl7*AqBxr#9bj0)1FVZa@CiK)&@UH9yGj3QqYK zZ30YIU_^6!jB`;(%YHx)E6lcowY};*VUVqySi%!%r!ixeiFm zk;arF2~Y{;Zm`M92BH!sJmh|;i?yvDb@*GM@NvZKtVapg4e73B+gC3vN?I%jZh>?F z84zY4TAw)t2XZ8SKCP$F&0g?CiK>@Ke=E*wZlCZAa)1-0bCk;W$k7wV7mx`}R`1N# zk*v#v=I8gQzOdW0S;z0bN%*))slsXn+jmJ_aNEupa}0h78c4tSY|UG2zkrzslvn$XPY! zwTGObUC3Mq?)23445&NtZbhn5?yb`TLQ}Nh5buBA=~0PL21?&J`^36zOd9~r$?{ni zXD?&6%{98r2|ND~J1)jY+d=M(run;vrg0adl8r4qM%-Gz*&-d}bI_kby88jB$1jI1 zB6I6S_kxod1Sfx)e8>Uw;^1Snf2vIk5sJB<)I>ealXM_l>SXQ`Lr7OX%Imu6bq!Zy zacEU?bhXR?+<~BS)u`~{KC=co(<2)eMFa4L~s2dY=w;#y((7DP)^sos6;p!CdITg|veWKV=aN!TWl}J13 zkg!nwZ4&-n#xt)ey;oWm;wyfP0)HwI!#swgl80LQ_05Z9oz`}J&jq{&v3~ak`=qJs z2J+!U({qbsz-|=r#^B1)A_I2isQB4i>L=d z`!|7%Sk%rxbM_F|eTRy{lvFxYDOb6vS6%ZVNcWZx66k(j(R4R=Sq{p#6rP*Cn`k`X zH-T&HbAD^^Wuk+pCDE3;Jy*WkWlEdYufero(HQZimUno_;K2R7Xq2e(^ZUlkO4Qme z9u#7RHMx_B`pa)2A*BKUC4&}UwHH3r%`=ZP>C=|eYM_4l9-C0Wb8{79Hm^&q?;`nz z{bSXkFuX;L_9?V95EGhiuEHhkw(YuqnLr3Faa zbsC#0uZ6ee#-Xlsl2>Gt9T<(*iYd%brf~b(EBbDgaImRwMzMI+X5`ODk zjtRtU=KPJkqNGaPl_Lou%^>O*vF9uk>!PZiJqI)J)NUd3EGLvi6}b1dSwix+bM1E0 zNU8HOPEpV5iB35SPS=~t!AeTpcih?8Pf@+>m0B(+g|qzTNyeud8|*%sl}TN9ozbWQ zF<`+P7%p*(r+;!}oGCa+J`0sn!oGm3VXS}L1=`GJ`2Qg85c(Pln?%c4-^W|G8aZmM zFU3DOqG&6Rbi=U-^n^~g%e3Qzb--;)Nr5SifuMyahYy}Ik`0b{2!9IsL*y6@DRaT{ z$lPfg80_eJ$>rXAv$-Tv#CP3l-FwK;1Zm$J(M!i%nU+F-b9FaUG3xQt4Bj#^Xh6b* zxkacpzE}lNk8LYUJ|S}-7?0gU%lelTyAR{bHc++pf(-7>i-oWVf^Uaq*3Kxg%4aB+ zDx;~ewu>#lq4H{rPYe0u*Jv|j6Y))~4Qx53IJ%V;mA#MBMy;S^1j6V2KVmRtt~ml1 z4S1b@8C0qIyfur^V0^)8vw4boJgiJ(brlTA$_#))`Xq z@7J2q%-Ylf8cIo%u2FZ9+9(QR7Y!^j)FI|{iDt}Yrq#{|NV zb~qrrrd{<8AZK@=)5Y)v08sppIzu5!_9M_KU}6Zt$#Ysc?^fxRXa-LvTn_tf*5(G* zMfB>a!)(;HLXg1+2d8DGmyS8l4%!FDV?)1?Q;wL0sIKoaT2qFfoU)z$Vi8Gtq94^2 zkBCFr)G6&?mxMxpyk^zN<8#`~4gV>S@|PR|pbMQI1!q7(bDz7@(ULJJ>OIG9&N%R! z4t{4dXmeZUu!UHhv2UfSb=vNWgoHuDQ~Vn6PB3oLXQWv;?7HJiO2_s})D!%$?IC!O zZrbdB1y*A%yF?dy?0d;=!3sb2mQ4x0oPFkVfbhln6tRUXp-ziJiWG7zP2#yHILUw6 zAz0J>{!DOfD9-ejss~In@_SOcY(315bc6K_M<#cCofb5dN<=^#$At{h`g&)zP^oiR zXF8P4?Rm8&2$mAUo0l``S;JTAG+6wm$?;+TTiGz#X4xNxtV|3DL50NX!V2J`rbF`~ z>43#87-O5Q=Jeen(v-*n01y}d(E?~|_2>J1eQxC0iJ-U_yqy-Cs;a0^%VSt?eB-xY zY1F#t{&)|kB`U;KG*%s$2oxhKJ5OJz(yQ>rkq20iiA>kpDH+gcHj?l}}!P7&j zIx?GUqZ;benM9*R{B2%CD~26Axi~Qf8W+$98T3uObvv-P$wbcvYcPe90R0oOLyAKs z!QU(?!F4GT-`jg!eW%`NB0e{(&}PbXJT6{WaNynb`M4yf+h)F;HyHdTOaIxO56^+BBo(&)- zX&ap1t0evlzPO}2GHN4(v|k;3Eh5ZJzlb~)Wl+Ejav^aK5>fsPo-a9{RTB0UZqgt` z-ULSVccFs3w*q0SNjNOV5tC8d42=6tJV?92gY}_%-Fu!lEmEVWqX(4c2ScLp0Afnd zlut+ouUJ|XTOi2j7bV>Y4hxM)sMkCVgya`Yt15$tIa)RporkiGaSQg#>Hs zhXG8bSEH$H!SK(H8v^-43|iG|R}DFDXQELK)SvD|ZsR3R7lw)sHq=@EkX5?71mdeyhF0SFjKIO)Herjs{DQEIhBI%3f zOQt(|qNWZcy9Vd{t68ZZk4h+tVH?*za>u)#^b^5Txp$v1pbqPj33# z?j+Y}r&Yd5Ws;~b7OHu1g@Zz$@%x-5^^mnd3-uLPARggy7TxQ=2-9kEta5ULOWU!KYu;itVM)cn61-MKJwQ78Si#3*08he9KU4{) za58p8M}`JJh#sp8Y>QoWU?(pynK)&_FOSEY?U>~}_(_Yv^Wcw>V~J5%P#GnNM=<>E zNK2f?UGPb?8;{*=*pPK(EphVA0SoQt<1Wd^Bd=`Qf_>ul0)1LxCWNYzy|1rtcsPmQ z)xkT z1+=uK{hcGVet&mN6B5qBUcD zd^xG85mEre6l-wXu?#wveg?JJ?N9xIr7tA3xptrYcwX%GtF%is#(sX>jjItjZ=Ag- zbn|*^wI?RUZjH~Gz64LZZ*=ywqQjDq|NE=N+@||vhu4h-{+-B@q3qra9$+~lu^QXN zHxBGpP;>q2U%c;5=9e)SSh^lIzC9ElVLoW5nD}zi$J_Hi^^?3`Rfq(`WNjm^mT^8; zuR64!)6r|AthPe`!n^CmT*U*_A@t^H5>+XaUAuR=?nKfU0Xp+u*zue?}_{8<23i}X-o=>d5S7&fo`pJiS=LLeVysDlf0V1h7Gir*6 z_GXq)=aETk)=G;9=#Z708AyIIV4!5c5pLjnH;-qS*`(q71A6$LWZiwyVCt;>vJ1Aw zgde$LNqik5O~v*c3KH$%Ys!h0Zh)cJKDMJJ{S&VO4yxK>1p@dx#OjjtQ*DM zU0JPRv$9(J6@3idGfvUiO3kkcV4IFMwtznTfXc6C& z$Btzv$r=Km+d&@!goo<3WD=eNK5W*9BojDz_0sGEQv;C)AJXuJysL9pkea|-6fpe@H*N|<>mb*|c%&_E*;O84Kmx!v@@8vuk%?}$Hf{4w0G}&K8 zH{@Jky^v7h9VZv;8n1o%x^0+qomRN?yI!xy_MwJj?ekxh-^|)C+D;Wei+}wr$XZ3C zkcl;ohKe+?f8@#s(MSS*nA_1DyIL&A+Z=bP=<~2)?Fd=%s~+fKpl5_e^C9I)-$Q+i z)&1#jKNtSeK03eV2tmGi>U(mg4)~K)vywd>0_ry#gGamg(eJ&gB$;ryyeE(YWdwIv zv`vI#d}7ymasF$wD>Aksv$jhYYuJN_7<|4Ys{7lKjY`6ReCFuZTc~U)Y~E;(y4~-? zg~mS}5>Nu5kK)Bpyt-`{s}b}80|qp67wQfH%I~b^HjG@p(@G0*K>hY-(h+qb;B(~h! zLDR!EM50y~%`66dllpY^0tZ02ATsYcWYN0S5pB{K68PE$5f-ueJ*>M_9gCR$`(~m> z*)ZgxWyICojhll$iPB9dzUH$89ILrLjAw2O(iQ0ldubFdn|d^S5}LEbsMq6bY}Z_x zQ0gtuF>KT(@`!%|RgRINRAfn%AmL&y?-=Q#uLc3AmAR`ga=pGRil(dY;V1tKKBzUi z(j$N|5Ff-zEK3jmp~5yg-}-=Ecv+0|t8n=ji7izBr4-r2#LnH^mC5xh7b~lo)4!1; z0CP}zf(6bIkOLnA%fH&atDBb{m?-UOZ}6`T*Fm{>g{3C|LfHE9Ap^5fU-I%sr_sdH}^M; zg>NC^xRc5da?$wgvn_<%NgPIOhuLQ1bW-Pa0g|{!0b*eqtsRpILOzm*BUX6GHCtY2 z;)lw&J1JLW4m{ZwM zc{3Qb%Mq1MI(puZ-&4n~HWQUko=&&0S1a<7OwAtKB}~BVaGR~2B6PxrZ{oYm9Noi> zM*YyY|7}{N%Zj3Yto+3;XmtTSiTPz~B35r-nHJj1qtX8HPpO}GQY;HJJ7W=35<`fpib7?}%Z;8| z-ncRA@!gYv8{Z;bH&P9?$v%lYyCL(aeTRqzq~=ixu8 zM5e}gj9VWcA041dr2I1bS_(80s2$-CvMto)OKIeO#K6Z*|1$ukp>)~U$NLZ}=vr}4 z*9mT-HX0-VFG~^JzlX;haPa&F8V9DB23{xll7Y&hSFEb##gml8lNPDxfjXZdPp0=y zr}xZdngY-xF3CS#!AArKq?5?{izY&rnNrdaN4;ahMhz=SoPm6T1GY)MJY=tfI8{+3 zVM(2BZT1d`!4*qYD;RfRc4(}^f*;rB7_Y9U8HWozBI)4}gsHE2^Pe`}uYe*=5BIT01*^CR6{8M#%Gz&a^bPZC@9v1PYv^R< zyx&<#X4^Ax3J04b!Y#wX>g!kX8C+=aySimqIv2d27G7Fj*ZH>1ixL#XYt+?{p$7?OoQB{m^47VO>^65bGKE{(ptQ=oSG5RIOnWu zyhTDmX+C6d-ePf1W?A0zIK4k#X>~o!Tv|Sk)Vb-hs{fWS^RmDC%h%`iH7$);|gBgxgGp#KdP6^pBFdHCw(8J^$<)C+6Wq(&}UPVeerMjgQ&hUg1Vr&^Q8Wj$eVFR-aw!sv%;Ap;5bE z*2Ae8?A;kvG%okY^PTM&M0@AULxnB3pF!QD2kY!ws5enubH`e(Nib+QxI{@~DqDE- z?U&TkQg2~jU(foOP9=GYtE;OBJ(r^)qpGI%ktg^tUnIhY?yaC(9AU>!PfmdQh~?#) zk?G4T)q<{sWb>=5h{+h9rW>yQIjOIQ8aVg&i<{O#ggScdzl`s;<4Cw+)_Giuhu~-r zNxyvsCzNNaTKgLrZI=FA)1O#adVF+P6)xTxvDD;9Nmc*Glx~NQMR+zoHb!u_b-&x~ zZ_2sp`@+S<$ap4w@t~B$B~xkPO$68U1WqF%+uk0VGVI`SKDLduone_5X%_N4?%buf z^|4)le^KLEB&=3_uW;EeOfGJ|-_fXO{ONjqZSjdSrA^~o5Mxx-OdCa$(=2rX?sg~v z;aRmz7j(FT$@XB7U4kGvw!8b{cYOQoalbMo3`KeMpM2({-wW&OYiDUL)-rBX72(qP zvIYE!3GsN?W~ZiDL?4GH%es4lrm~@V`M4}_I^M6&SJ{&WN7FfMtL0$_$QF6RmUww!bA<&-kg8+8pm7K66J*$(fwES_k}(t4RB8r#fFL+lshfyEY{ z1uQeE?Mb|%B%jr0M@Q^ruXY4NVG)re0WP*xHwGW$_I3xH;bnRo8w2_U*mnfh*2_tt z#P33QOf2e7ldY7rv=7H-1-#Nd@SbxPeLJdExNbKLbY6U8<9#8o4~QR^$*|JelADE& zPA2Rnnqt~qSGPv|w8fqMLTM)T+ zY3>T2BSum6V1)M0&QFPy76D__zNDnwK%ymmB}b;r*3&9<4AOj>LkQ2p#TR!}_tm-~l@YSd}=^W*f zk8<^@DR)>efARhB`1oc-71Cs<5CxJ>+UVRrs0cUxlfy#WmXTqN*gnzu) zI`$$XGf~zaqW0@r-MM#nuPm*uw(#0C=}fI4y8aU|OEV>iOZ2JfqWQ>+<8&Z~fV+5o z{y`D5nI58w*{Dk&4oBIPm+o zdyo`EJw@&}Ffvb(!}or5r>@QYG!>4h)l3R>4c^p?6_=36%WSl}`wn;v===Wt`?uU> zL=PypU`(-GN)eG8tQq^|RE@kxWPLB%5Q)6n;C4@>hhzSVU|$~EMdW^>78KiUuw=Kf z|EoB^XSPu|_uk7+x@M@6*x_RdG;4mCp~mOJ^Tpo&ZLg|Nnuz?KP{Pg?p_tlN_^+jb zn6C&+LziwSr;9uO(~VZa)$?5y?s;{uPjOR&dba!IKEz>(at6vTL6h9rvbeN)+HEx} zt(bv0#t1z;>7E$y+qqGuP}FW>6%?R>H)UjHARi;8`rM*4JmT+F^he+)zCoEr5YgpS zuL_r+bbD@bvnE<^j;P&hIt&+3M~&vhNg3S?rJEG88(#FvTLk|C6W-I`P795Sk2J}{ zdZ8t}*1AeRaEg)k_wBIFufA<@%H#!nob3_Ei>cWnqNV^yCQ7{k6jIxjZMpB$_4r+m~0~sz#j)!-`~K;>x{p(m;it zF$?JcS!2Z9zkERLG@oQQlxphjm9J2G3vj?3BPCo{6dx)>e`6U19z0##NGRs@jyand zdJt}+9F#KAM5PnEGEE3C#s`#4pIfAopCUQ1Y=93pn;@*OGCYgDr5aA!_hC#HoD{ub z>jx;pnm`Ter%LVNk`plO=Y&!~Uc@7N6Qu|_@B6Se;?{aKH0$Cy=`fV9U`@rr>}`{1 zvN+cZuXD00!RPbqtPk7YC^gHV!mcbVmxRrhYqpRKDN~+^3IK6&5ZbK^K-8A+6=;$@ z;yb^#i$l@K-q1Y?e!JfEqR1WcC*FmMZ#>Oy=c5K;Eyl;Oc8#HEbIYt*;!X09GbXQN zuBN{62xXePMVO(R+{@~zs0}G%r!{mRq}5TRIo3zvC+m}oG+g56)>BZ^;iy(Ylp757 zYJd*h^@+#4+q$`dGu$M&)DishGA9NYq!ApS!!Yxk3jAnD0*3mxyI(L?>T-%3E#d9; z$}&$|a=tc1owqn;olh~D&%?D@__e?%02*JIBUp#yi`GaW+zXNMMKYA;DIfxEf-ck* zsK+Yd>%-tgNug{pCyOly>ff_iiLaW8x-}=?cjwGK_BET(_pMZ!q|v+j@a-t{=i!~` zo(jnnWCONoDktIuVoB>U3iRr5Uxr|*?mX1@gSk397<2ZMH8KVP7E&kTVFyl-q=2UF z4lU#sNQL+lH-k))`S&rEq!b-dHI9!9&jn6)r=;CNn$Ud>yXD0vM%>{o)tV(|26j(M z%#crs6BEyte7@83}+Qkyq*S0KL*Ok@Jky)*k~cvuCy2{MOQ!lK>8 z#T>XRY?q_MU9=(6$(Ns=T?Y(B|5%s?oect|2Me>amZwzLCm=LLT3pU-c=`2QqSs5y z%~h&Ib*9p}D*X?o7i5XqUQC?zG}%@??`;qv_9!$_mA+$RIP^n&4apA?55NVmB!6; z3en{b2zWFJ6U9FtTrXP^9(j#3zlR^V$a=nNk;+=<0pNx{J zR1*cdn@Y>l$dJ%rj3pwJ-N=beBhyPUX`7Jmie0&pl6{77PDzOPISYXb9@7DYPMJ5T z(K-SN_4n&=J^~GH(%h*>TXUPx$d7Wx(*YkGt+`>tVH>EQrg-1pUI61Y-ENN<{YWJ# zZ!~;pvhtR%9@1Z}aVB&dG>I9EU#?~CISMAI309_WAn4A?@xt=%b^IW<{G8HH6^(Fo zjSC<=J#A{VxiARZyOF7g`=`%l5W4e97X{D~kGzC>kRiIzV+D@C7)op>B?drNgi$rj zdA#p_h1%`rh+eusJN1E)TfbtC>s#v;E_821QrmylLGzmw@`SKx8W@Oj6&hZA|B=<) z>0inXlqX#^liJ4$J_)F@=%!_2gCML+A|VARVWl*zke-;U9_8EK3($#C=#kTt3xqHQmW^yMQ{Zz`{tOwm zhLD%gRF5u`cK9f|O6ncl=I|$>0Gz+{a#KxcvskGv zOzf`#*g9$1f31Sz0Xo+jOiobiL-)}d=zX{8zCj(tSgToxy!mXAYz~#*&9jtTWtR|! zpN6T!H3xNMgn(9pJgy>P6HTp#@JH{Kq@YNj;rDi4MXrsrfCib?)xddp&E}5Bhs<^AV)WS*>n|WR@aB}FKMeILAmltqQ-~Fb zckrgPk5@nghCjrF6MpDXs6Jv~l_1}U%?eLhv=FOfdJ|xp1u#J}^6!Ea63dx9eFL?S zBh=tX*@M`4!)`d`(K2wJKVf80kp}^Cmbp`7!qppQ?{TGdKDPPU97!oR?OwpeL8`w~ zrpw1;KRPHR7DWaHhYCMf1+{er<8Kknj@_@xim79Y+2eU^p23>gla7>xKxOye37tw6 zKO6Xjewv|sX?ScSgHq!b6o*0Xlb>9I;~yb88n7~34lR|WG!JQZ(ZrfEx|anpq61=h zArvP0Slchq(Y>qh7+)p+1Q7`sa+FB@{e-w5M@-!87gNaap+9p9EtIqw zDI1qAX`zwqr1gtUgbj3;utLjobbF_Sh;%^8axH=aygI*vvItGPQndz(L|L^wh#>vV z+tDp^{}R^e?E~{0`!Z8H6^C^IOq)aO=lNQE>@l*kEa6(0#(EZ&iAByDD7WX?MQ^M^ zNzuR+uX)s^W#5VGqr^BfG;~)J>~-;I^D{l;ktYQ0bLt<&4Rq`gz1vy=c70%b@mWiyhgidUn&`1AiI<(Wh`V< z?Px5Sa#%8U{)Vrxop!VP1RyFXQxMX$U+x7|$^W4kAtI&Z%+G_Ca#SoLAZF~)|-kZKApGm)PAc- zdV?2Gs1LIuAJAeb)a)+L*N%u|mK6xCiz)Ob(jF`y;5UrnHiJVBB|1}a zK!=rZqKsq^1x5RG${>6_%se85J|rbZh@_Q;xXZXAR}LU`1RV|NNr~g;M5k81MfOJgP7=kZ>9MYzUOmT(GQp^16?_*V2a7PjON+sqcVm*aOJejvm zmg;?u9YuS!zpNSDH1ZhB3qYT@?vO@s`H0B!DGD|V@W~A!@-zDxjOF!~!aY3w6`}sT z*5h9y?QTOjI4FHiq^3lNc9AsMFvdIfM^6FZwP?f3V4cP$c8@nn)E@NniPe&uJVnIM zvXp_-O!TeXmu+R>{(a_6ytOVK$VVSRj=KAWOSH6l6o2}-)|*BkGAbd$kT<` zBEKI761cQwTA0^YINKy?N4T){GgwcQNoQ(+42A22VdbDQ-Q2-<8-f!%f-Ygjd)zr< zKg|zsePyUE7OFl}LLOH4fsLRho9r-3J@@!}Ui6B*3E?ue3FJiDU^{3x))^MaN}KNj z7=x}**??s}mqhzE?@l4v14nf%0tt=Bw;L5_jxbb`SV#FGEv<#pDbsmNv&vmCooz@S ztobm&GQP`CeG5PQ{nxt4JM9 zh4TRmV}5{x^S9JL1>pC~izz#*>DR3=ug*8byusW(M=;#bbW396-T&qo{r2kW>gfJ_ zwIq*sheD>d#N6CmCnY7N=sojdu1rn1MYE$Da})ZVqol{%i0h@9y zxls@k6?jAM!W}FgS)-Cm?SQgh>RRTCaL_RbY8ECLrK4@Jl8H;MI^*wZtRhP0QvR8i z5B(8BCYQ#X#bw+R@YDkdTbG}oe-XpIq1^#S%&o6LzVI-edpNH)+w&6=5lz%Eu&`KS zQp@-J>cU+TKtWJ_1ykt;yRP~WSsW>c?NSd0Ox{=B2dO!#;B@r+zrl&(d7(pZd0f)y1#EDzM3`i@mD7gb~H@`0$`TLsx zL;jnan{&e0x;*H(J1U5KSA(<-q)Jc_?KznEajB@FAc2Wdok83TR!7)JgTZ(3N>4;s zKaG+(5Kj7G#Q^u%{%?y9`)NkmkO3czW0nGXVtAoL~VdpDx=mF@u(gA9$p)4@O8Kdby2mV=MbixH4!8j!#S zmb{J3mMDFs4G&8z0#Z9VI{cw5-~EC~?PskDFlxX2jv5Yxgv~wzA1D5b5!A=oa)Tx4 z*&ll90T~Nv{*0WY710oG3mrW;iv>Q6qEaiXX*Z2-!r z@ofgMB-Ch)_NZS0KPKSUrXPQuR*fN^hol;k-_dlQFBn9r1DtY3LO}U*-lvF-S_8H9 z2&2aQrd6%qYP>g`@UUBo4<8WUe74r=`Uy<7vQ6$TS4ugP;aM`}X_y zZ|-dW_a~iFh5lZIfVR`sHZiino8Fl$E-R?kPy;8Z8W>ujAYcJ39ZZo(Y zH;{&3g+u}&tVEd}i_znI1(aE6U#tt+;NU`sceYI91i1>q;;ZD#k>Il}B+^|uPp(%P z#Dh`D<-~{c)imT0&WmAZN~HTWYf_7Oq3Wx1l*l%bA6)jjh^9$UNOEz+f}K`;F>!vl3f9)ExNoO_=G z_64}aaJd;hU0bjgz0z;!>C(K=_SFCkWo>*$wUucox?2}| z6YPha*T}7_naX^ASbF67E(^Eg@!%?qbN_9@`58t=`^@_V0Wpg?5Gp-Jt~i~7hD{oJ zKnp9!I>U^Bp%{Gw5^i69NL=OnPMtCXSIjMfx`cza^D>~999WqR={poOn?3s#g>^9e z{qg+D8;QibK*f>u!ia~Juy@(XX@le;9J1pX(K|TqO3B~J4}cZeXMSX#oA}BmlNuo1 z3B?vZlLBSIE=P-0I%YBcyDfwSJtv5z&I>9P5<&wJhgv->Jv&^5M~_v(&HGSArAo5F zYsQL@zdG?}x8-LTI|+2+gvx?8%}KEcBu43_(aT@)c0ZtU?BrJ`FLmf#Gul%V!&4^9 zBn}ccg?6A;<(0l19D6P5qMc9DK(JHX>bCPLh#MQXkd9iGR%jW<3o0!NxfD&`;y^ZT zn26o$PdKC->N+JhZT*dK2{{ilELOR0{yf!8zN?*JoX8wE_}lw9(^Q_;WcxyqS5|cB zhf#*5D|?a8jv8;jL6G9gg6_>cl-5FJz0nV|6iBYb;*%=%GvP++4qI#NKX=TZwj43A z;WwG-my|(Y&W@-`3~cFJwKBv9|CY_^GRw+q>BgDIXROe)v&G+r#|ntS_z72z*x2sD zTXwa`e!EDVbi=-Cg|%QZtu$GsmerE|D?8RrCXnB>Uz2E{U6bp(&6Y)K7}LD$vRS?< zR~pB@sJqnQH^x(bI$=OJ!#-ib!|<|TGANFA{FfaR8EM5~ zD30(^#4u2MzQ9me{SJ#kH~w{T*dw4@JE79iqkl&Z>&H2xkq%XqCnJJUy*N^!QANVZ zi57I?@sPlteN#rlnzOCm3 z9}*;?`~rL`qlCSQd|UIRYp$ffVWPiszN^hQopQwusoh>k`PY@;JKr(>;_UaR$c|ya zd&O_4LtmM)as2jN=cxIzaO>Bw{J2j)fcBiZyd+Ql?PrX?cmf_yw-?vD5dDbK0xl!E z*j9ywDMjF!CD%Jdk%_w3Z>X*!R?VjP?}HR44QR0a1K8wwQDZv+2qyU3;?#W$Cr3V+ z8&%Z5;F@OzZ0C^9wZ5epSeu>OA-(6m-0)p0-aT_M46b37d|RTK-KWeYf4b)~!ETjx z9?W&GD=&NFp1YemMX0yU?wdY`LZwAckU=R} z9-~;r7en_axQl!3jlLfy&xL=5|Ic9ZKQ8-!e*T4;f&Xw-|Gk=lLXL37+h8@;KkZ+r z8Tda3_&4=9|CgG9;OO}OC=<5<#3}npB_O^FIBM)#tX;JMX^cbM+q0kEl@KS+B!lJ-3bUU#0d+u> zYx(;;=GEh=mT_}-q+MNVJS3P)t1;@8>tqJ^WkvrYG0CrlWVFBmgfpC}n=VbS9KTzw zcO0~(SoEU2tW!BjExX;j96ySZEWg$vXlWF810w)A1menvz}063TWYE;KQc^g!S4BU z4_6>a5EA~c5&qxpmK1!}?Ej&)5Hwph1D7{M{~va%)h-Juf~8AK{OyVl$VuftRtTYU>_9swv%Vs zSiw~KC#}UhiZLK-5MojxNtwEh&H?)%SH=`!Dm3YFR%F1cpHK6M&p|CCKzOFLhALEuNW59SPHAQLT#c%z5o193dskz|((|GNe zBq+;9WO$p_{<5Ap1__x`18thQ$r08_?n1LaiET3i6qlEyyPui=XUn$ z;|gfV|G(D00<5a#d;0)~2I+33yQI6jyBm(AhnAKGkyN@vLPA0Wqy!O=Rtcp+z@(+& z+gy|1cfI1h|LwCM^mv$et=aEdGqYx`*<)LR#oMTuQp0p!)LyVRKUt@Opmd&$$Y5A* zhEYPwT!}&HDr>^L!1mSS$|=+DeOB*SCmLgWZO}=8FP!!Z<-V zE+l-}WINV+#mbY~ui3XGKEIaczDIvC17ZB-#Q1!QC9CHjrP?NcK(D$2SjX8d{3j6q z-9$J5#X0^m5k1)b2*9-;eKiq-AthJ|!f7<*Z5Di+ta~jlp0FtzArU*B$X$;gg;j$O z4-ATELM1<@m#%`(ijHQ3Kc>9979vnmx8>9%Bm{Rj$H`Ia;;lA*Y%7L-SsJOnozMtW z3A-Eg)X%un=Vh!5TvgC+J61+lT(OqTWx4eCYt0&iy>Td9~ zVffi?!R#PPq^iN>r1G`rFLIpl_XU3qgRlyMLW$lUEa_kS*|o;B-R089{WC7J2jdk z)`5zo%RU+9{!t7VzpuF`0ej#cJp@bd0ulYQaTbz4U zPx0QpPND&f(mqyZ>2Q74jK}kY{1-6iv{ODey&cZt^*D&j6~oJQSp{<#XP~G*XBGEk z64z}2Y0NS6#aX==MmKMI2Ir1W2Gh+fEG{z+k~7pclL*4asIGJX9Z`un?8A|xS`bl5 zmc*L5zEVGGnMmKem^D!Rma4y9fBr}}KZE>s{d!sZrC8&)&Hh-{3Zb&=9Rc&DBH+Ec zzn+wRZPpqByv*UZ#Tl`hBhDG(n0~u@jy_jut+4M#myOBfKPbMrk%;P;SEZMI()Pf=ic?)r{tlwd};hOPYvC3(IQ7F z$1{niJi|$Qp_g>=Dsd3fXzAahks0kWm9@=yFSvmsXlcAPP_Dh}NMVS5F5o8I9nn19 z2}53REUI{EgEwrNe5~0Thho9xaYGd*t=c-PDzBUPrD&UVnT!sxj6y2u{)jHkEGR_B3&D#41vr`b9Rr0<|w*#N#+0Z4If1++2&Oos8aj>Sfgl zfnyGEtnRo$gDO~zO}Da7@`<>V|H-xaS9YA*pFOc>N+xCb)#0fu!F|^atUTw*xPkKf?7lx zgpAt)KeCyPjpJ$9usu# zEnvE8sn-EX)~XNZ%trBSXqJjR^|uR_msTl*N^s2j#Z68=$(VG6b#?yGq0wl^FCZ9z zks%_Pk7`#ER4;>`1~I<1KAmQkx+N`G$?(1KCjuz^3Gc%V$lbE$$?T7ta{@_AF<#%* zAiQ)|_#=K+_@f>lL8gPWms(&$iQZW1B%>JPsq|>g8X>V!eF_8D_L<_a;<|3Drxoef z!XGKp1}7oCtntQcHpV;r6lO&$DdiuabOBOND=LS5UH6))=p`Bu)pe(qt}>G|zr1lR zS204<3TNuUtyFgbco)wprijOy@pf{r!*gQa^=8nJ!&&PqN+nyz+hS$X8lei(9Hne`L%PV={+hA{8<{cDNqa@70!K*jmXlJ?#Hjl{u#xhw1iG~>Uf`7J3ejTPExPrc5}g9Ek5@i!lErMB z$ztpkc}L;aZKdR;o!o;-C^?ftwv(#|K3vFs=sdwo${JCD!@xNPx5*f)BSU35vg!7% zUa0o0UI;7VRv-?CDAbabiJ(%h?r0l2f6#_ZD`X30?Rx2<#Tk}$ZDi9sK=_ksm&qsB zM;ohtr+=k1|KOyi+^eZK04MJwfpX=pBUTYEaWOfA7`$XS{1^MX>tzHc{4!~=0={LRFAf4}Aay?(T~wK3YM z{QH{a1=ekCDIliGFzqKA3nnwZDWq|NodhkWi01j_u?bK5Iyz=fj8dyGyA=sp;rp4i z;~_5mj~hp3^IZ0mDLu8x9wR8k3JRUNvDvQ8E$DTHTb@PI&~9n8S+3|6q~In6Y>B?I1lJ(t0QMfo;OIRi zQoXlFTOXj8^cG`cHr%tbZ8@5QdL6GP4nChu)}^ozZV6|9yIqJXAC}MIt(TUYBi*Mi zjhkmlv~U@waR#LZ51OW=>Qnl-7B+9#nq20sc$h7#leePMCis3bl$FeFV9-TjN2rCI zDCP!V@%@70gU)!`1jYy51)&Z_J@V4k;#PGA@tmAQDgn+?gVt}+eW*yK_(enY4%wf$ zR?{9Z>y|lrvn1RUg_sPNXQeFR$1L9|e5HSkFcPiHBv)i%!$r(I!J4l*(8yPOWuBFp zw)duNSTUT{7J}HRhA7z;k;@$SqH3t)!6+orTd9Y&Q5bJbFN`fP zehE*?0`+R08k{(10v5rBOne&Ty~=u}m-~rUxd+&l@%inAjehanx-F8aLCgbc%|toZ zr2L@!{V%}twTY(-U8D)!a-tG>oHvxEOWO5glT}s|0tLd`TCS}=P@4x%EZkJB?Q$usci<5Y~p!S#Bi` z#ZBvo&^9=cPBfyov=-r&oucHpogJ@w?eSed$@kdTx-CQXHP?|0FBO(78^5l2AdviOgS|~L#M5q{8WL>DkZuf&ECkJmlS0C#z1}0#*ov8fX$W6w z3?BwTqj^DE!2{D%ag!> z+k_Y9oFpmP%3~nSBeq;13t?Q4d_>6GJEUPv#mA39kIaRqJLU$Vrxdl%;)r!P z#tNrZKrTXMNN1Vf){+m?R=%lS4m;eceuFHuRKJTmp9?zFq`og5B_ojU1@D4ek%9}n zayAOtgEx=w#y|{~xL9;Y{_~qZ-~6v(dfQ`uMm2jLxn1l8dq3zMe8oC zHg9sip*SKKHZA5V#GOJVgcil14&}C72ZOsOlOJ2!-ijVv;e(khtK3)hb6i_*QgAK0 z>yk2{9XtI5wYJnaj9@&pn~rF%xCnh|a4$**`BwyXpeRS$Nun>eoSwbxm12_Xkx3_7 zD<8%)!$NFP3jm?sv5qA&L)F41N~Zyl>wGq!v&+TwDK)E~*OGN^lk=^fd{?<@o_qE9 zuvFy3YGCN+)z=glJI2vXFQtp)A?DA07FQ>po-QnEwFciLm@pp=7RZwCBd@kj8*vR( zzSE7)D(*&LgG*83c+dE-qhMzmg}M-*p_GGNExW#CXk$?^amb$vE@M*7`n267*Q|NP zs@^Rk!uIw8)zs@Z;ri%p2+@%>uI&=EWw($$@*bRCQ&F*B@6cAlz2ZNc%VZIvn%A35 zqEWkv#_P1A$$8ZwppgUR(Oq^{oY_$0=v=QAM7%0q2|V9U<2R_+!&Z)kSOahPNrX-8 zB%f@{TYnH#q`2AS&+kXnCZ>PQltG2;##6=^OYJh4jqP)kDdB z2zV95t7-j-t3--#nwUP-8OVR&IS7qrDEx?Tow)CRLTRJyzrPa7rc3ys+5xk(#mB2mvvnQ9?4v$5bQmK~ zqN$T$gJ?bR?#2!iY8^(FK?4cjXxS+8z8`w~SsN0b*uYPJ-Mo0hJ zYo8q}Ch;pmnz^g?`_T=vQXVuf`J0Z;xpUNHmnk2j1;Qei;pJCeV~cV$R`qlmO+x4M zHh=Eg_~AIa@;$F>V1tHR|Kf~Q>!-P1M3t*Wj^(#0v?|76Hs6;AwC?0h2cxg-*LI#% zC%zZY(+YnkSYMK=I5N~0H$`@}&6iulh=W&aIRTW#8oK=22VL5WBp=FcHftL(d6bP} z)Q1L%F-b8L#ORdnvS7pCHthuRKwjP5pM5<(T-HjV-dHaGQ6+0Dsjo`z^dH{L_Ui%R zdno8j0RE36K_wMz6m`II_3XYttn@p=e-8xF0E=&$zXCz?pwv_#5ajzc5R@vW3M+vd z(J&0Av0!h9=LhAD3~OjMQfV&{XBaJ z9>ouwXFl3}(6sO7(KmRfr+nOBG@J?H@Y2bVkw?b4f8k^YrVcp68AMIGzlO)5h8nt>IS)k-u zL~f}g3sX=^T`5wJsH2-OuwFIc-p0~32-U_Fv^Rt^yNM=J{>EcCheI++*oE9lwj}E= z=Ct?&;IjCM1ZzL ze^IdOZJ5=MSmg8_l^6*N+^7CvEUI23#e`5t{c6rD$?7w_()he%cUTJVE&0Z`gnisY z@l4-Drx{J#7xQpg%w93QQk$XGCMYr_(AM@SL;19lE3My%0rvn2A!J)`E;ud>H|B|3 zqj9lwgHlMx+_*Uf`JTRf?IbQ-i>X?lJx~4{D5H24AL7|Jd5F>9xeKDPI5rqH&8`*V zUeBCYeE>ISMOAMY`|2aFRqbTF>DN!Z$=~lB;2a0!I5%WqijrfzTHA%)W|*>&ZAP3+ zYFXDV)y_^GT6tGsS~dPo;Cc~9!8F|Z{dmbd@gmK)QBN%M`gF^0U%zF_*l)iJ@?)V% zc@`CuGnbYB;;noByeorv6|QZaFGv(Waln=_XNdb!M93%9IHAc)L2+S)&mjjV$y|mN z_$F0)_(9xLy`E<+B*;gi677MmqS{^G0)7!YR+f=GPDz0-$z7QIC1{)fc?KdNfGfXp zg1mIfhWEYjT)uR8;1hAmAQv?g%lm{TYy>dZ3k*OMs2kqGOI!PBlOH{A=(z!HEkGN4 zK6;XHr744}mI16T=Z2)tc@D4{!ON>VZ%6@|^jYwsf?3w}h8`Q&TVM-_Kp4Qc)^-Dn zH5qFr9j$pY0?|Lu2djN9*b&WA9jcwYd4eLme%ux)X{PKL`X*X4e#bKbmhqk5mi`ylx-l97_H$a;5<9lfJal01l3rqk+GkxW9uj7!Ki) zMr0PO2oIHztDn=adU@md!&TCw4h^L`*UH5$L3E>toY|MgY#jRf9uG4=th!^xhM=rWc%T^6v1I913v;`@a15R zi99&#>tu-NRy3fy;;`d$kzENyywWVhcA=n5B=DWg>gAw7`F0pL`ECRp{s*1H*zL#i zNKq34y(gPG_JRe&PQzJ>22X6s4_8VkgVM#R>)bTuZ)Ew_N!P=#M2cuuU1r8nu~tw9 zM{(B#@p3uP=geeNRY>7m-sg;$m%PJvn-_i!=2`cQn@C0*7Gesi+cVj`_Y*Sm;;@E!XO4Hiy zAk#fKL{=Utg&;;JlXUUTq>Jy+t%930w zL~DjQ@LlXVYqXu*pV?E|F-8j%6m%aE?7_suJ{seccQzv9Aspaa?O|`tme#z?IqdT) zFEB2?`_smuQhN^0;a0F76uY+xUcT5&=dTFu|jzRV`7n-&JmPv=%2sp1E(+OtM z&#imnB^vfm7kjOB5*xNoHW$#j5Ju$+9y?Ojt-mUZXhF(Evw7Kx2S z2@S<+<^CXxp-ekU-!@3`VF(1t_4N}`sFUTG)}cD9cOQaUz`8x6!uIjf{q`bhVr=yn zB9E=XoG6(|xL4G#*|JR55LQ*pysNsEXcgb{$bs4=w^bQMVdqpkti97Efa{&kd-dGx zS8_KY@O=vAc+s|cJJKjVT%25#a4f53V#KbmMJm+i38=9dgP*PNldE5jzm5x3P-?&u zY~gZ<;r{=~kdHS5ag32>{1oEf)T(u}V^Djn^ zDp=zXdGl~-D0R~MIBR_|Txo)e9rJ06B`)o(F}WB*mgAL7xjcFPBv@O$b5I+k|w&=fkTp&tNmug|XvFwIN*8?|O5jnkVHGhKxJo9F?eF6C4l8uTw z>)txzn@mb>tRm`&03$Z^Y(rN{N2_QEEi1d60-6uPQ0CECF&4Nq@~ zW1x)*>=FKK|42-v??OgN36Rmg;`i{=n_6cDCG%^S$0Vu8o_S(aEg4`Z1s#wyNihZ` zXBnmv&~Xb)3Hl`B6z^tjA&zAMFY3j4C@Q)s&?T|~>6#iF^Q06JIX04*+Z%o0?&WpL z%E#A03fh*)!2@&H(CKPxFWK1Hu_Ggp$xCyKh>&5Ck(pR>5MtBQ(HUA)DpZXx65v|i z*H=bHMi!dW;U^*?d5uFvq(IhTTN~ElaCEmIKVSFp8sgpJ;#;Z8#YDsR93cTz?w+0} z4yPx_tko7s2brWi@!-$qVo<^cms!*7oSai3keI27$?-98RI-`O&7)7K#3UqQ@a@(> zU5S!~bI{OxZfBINkRF&ii!#;2*PE49un|i^C6B_e4B^M^W`^hiayS? z9CFEvwU(Ab)PNcf)_YA^sNE3Y>mtC3IEA^nlw^ax`&E$Y3E+I7Dil;yubXjkaoW1N zmG%%whX((&=}cnome({A>`E>GDPa2-B>nu z_ERd*EXIv0!c2t^ zWtD;+i83=guu7-2IlroVE5H_cN))_~PQokP44eUNg{g24$hE&-+}OA;Jmu8)l&ci65R#L(dIRM$Yr*V(p7W2xRxEI2YjX`tz|?bP z`Z4CfGSggeHzla+t`S&LEb2falyLo6_)ah3Vnm+r%s}}t(rplHRPG{{3qFH$j9LRy zZ~!)XP?%`Ao5h?*NME0Ki#js6XzX;Q_!PvWI~-Gd(`WqGQ`58m`F2tLa%%b|bbO&? zksnd%PaYu}4}kDM1F*@%e37Ovw()3_sNs^qf%$ZHAD1timr;DY#REc?}!+pA_T@+dk-(7Nph>O-s+!-K~9Dl zAzDh+%J$@1lJ{h;F7rs@=wU)Puv_JC^3pxFT8yf~nP8!!Qda8e!z3T&V%EF2k*K5g z#0{SP?R@;Q&I9H={`Lnw6tzsuP*ML7p1`m*gOi7_)&&pITOo`4M2U~0CcK|2c6os+ z1mO%~uheUA8P|}=L=2aT{k5SzAU-BmWb-Io=Q?o@a>LXSU$P61Oa`UC+o_Gq2V0+@ zdJk?P`X@oVR831^ja==mOj9|Gx@uP^;gl@%L>LpKH7Lmu*|QIQ^^ksn4~4 z@Q>}=cw0ERI@=mZ}MAZ&-S`5+^sC~v%35Mv(Ik6??`8wKDZ##e^ArGq0Wp3 z@WW^KkJ0iB+|1%)Y69Trqtt)iza-zaKI`9g=&yi*j8~7}bnB0`2R&g^GX=2jm<5L9 z%U=P$497BV{|In~@_iC`0j7SZ3GkQmOzH6W70`FHaQqXd{ES3*uFohue~aY(SFy@( zy6=Z0!KBt)B4q)A&h9JUvCfDQcwwyn18RJmz<=>GC;2l0YsB_4AVTpFn?f?az;q;d|GfQGdDUSNeS?%ulDIUkvU1+34SvS!WMYzWCy3 z|G~xltnT^8`SI>=1-J-le>~3fXMq2#t8&Q&q5gxS=TGSU8J(r#;^_a?nUbAj)ZCL_ zA%CyB|0f##=-Tr&_di<-U-xey{-y%|pD_R4P|i;{e4hOZ^ShyZD=GbJ$#elXe!gP$ z=co7T;$Z)QJwKbtd9T1e_*-Sa7b0F%+w+BvKWm%t2WbCC>N1|Atoq1B)%`17>0(qm zU$*wMzO{b>_kW}?#W{sdU;Iwt3y<1Aljyu7X!s?_*QDToX3h^saK1|EXGd`9<*)Sk z3h{Hz(my@6A7JOJR(=L^03zDo7D_*Y2WQKfuV5D)mw$krFGu(p>~!)sVE=jY|i zZ+@%p*Ce!yb?o^Rw4YU#UcMO8e^B#$X3@{!?^Z7i{$D2@ROcL8*w(LrzfRM?lpOz4 l=l-zD^Lt1?+sNESU@qbY;DIO{2*eBgNd?+J+&TN#{{w(1%+de= literal 0 HcmV?d00001 diff --git a/docs/downloads/training/LocationUpdates.zip b/docs/downloads/training/LocationUpdates.zip new file mode 100755 index 0000000000000000000000000000000000000000..d3ebc6f53e6da667b17b1a37179312220c1910c5 GIT binary patch literal 49600 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≫uzM-j5Mv4}6sOk*VO|`PVzOEQ3?tHH{%Eak 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 - -

Location and maps-based apps offer a compelling experience on mobile devices. You -can build these capabilities into your app using the classes of the {@link -android.location} package and the Google Maps Android API. The sections below provide -an introduction to how you can add the features. +

+

+ Note: This is a guide to the Android framework location APIs in the + package {@link android.location}. The Google Location Services API, part of Google Play + Services, provides a more powerful, high-level framework that automates tasks such as + location provider choice and power management. Location Services also provides new + features such as activity detection that aren't available in the framework API. Developers who + are using the framework API, as well as developers who are just now adding location-awareness + to their apps, should strongly consider using the Location Services API. +

+

+ To learn more about the Location Services API, see + Google Location Services for Android. +

+
+

+ Location and maps-based apps offer a compelling experience on mobile devices. You + can build these capabilities into your app using the classes of the {@link + android.location} package and the Google Maps Android API. The sections below provide + an introduction to how you can add the features.

Location Services

Android gives your applications access to the location services supported by the device through classes in the {@code android.location} package. The -central component of the location framework is the +central component of the location framework is the {@link android.location.LocationManager} system service, which provides APIs to determine location and bearing of the underlying device (if available).

@@ -59,7 +67,7 @@ href="{@docRoot}guide/topics/location/strategies.html">Location Strategies g

Google Maps Android API

-

With the +

With the Google Maps Android API, you can add maps to your app that are based on Google Maps data. The API automatically handles access to Google Maps servers, data downloading, diff --git a/docs/html/guide/topics/location/strategies.jd b/docs/html/guide/topics/location/strategies.jd index 2f7e6c37be9..155c86e29cd 100644 --- a/docs/html/guide/topics/location/strategies.jd +++ b/docs/html/guide/topics/location/strategies.jd @@ -2,15 +2,8 @@ page.title=Location Strategies page.tags="geolocation","maps","mapview" @jd:body -

-
- -

Quickview

-
    -
  • The Network Location Provider provides good location data without using GPS
  • -
  • Obtaining user location can consume a lot of battery, so be careful how -long you listen for updates
  • -
+
+

In this document

  1. Challenges in Determining User Location
  2. @@ -38,7 +31,21 @@ long you listen for updates
- +
+

+ Note: The strategies described in this guide apply to the platform location + API in {@link android.location}. The Google Location Services API, part of Google Play + Services, provides a more powerful, high-level framework that automatically handles location + providers, user movement, and location accuracy. It also handles + location update scheduling based on power consumption parameters you provide. In most cases, + you'll get better battery performance, as well as more appropriate accuracy, by using the + Location Services API. +

+

+ To learn more about the Location Services API, see + Google Location Services for Android. +

+

Knowing where the user is allows your application to be smarter and deliver better information to the user. When developing a location-aware application for Android, you can utilize GPS and Android's Network Location Provider to acquire the user location. Although @@ -185,7 +192,7 @@ application is listening for location updates and the events that occur during t

Figure 1. A timeline representing the window in which an application listens for location updates.

- +

This model of a window—during which location updates are received—frames many of the decisions you need to make when adding location-based services to your application.

@@ -210,7 +217,7 @@ locationManager.requestLocationUpdates(locationProvider, 0, 0, locationListener)

Getting a fast fix with the last known location

- +

The time it takes for your location listener to receive the first location fix is often too long for users wait. Until a more accurate location is provided to your location listener, you should utilize a cached location by calling {@link @@ -224,7 +231,7 @@ Location lastKnownLocation = locationManager.getLastKnownLocation(locationProvid

Deciding when to stop listening for updates

- +

The logic of deciding when new fixes are no longer necessary might range from very simple to very complex depending on your application. A short gap between when the location is acquired and when the location is used, improves the accuracy of the estimate. Always beware that listening for a @@ -251,7 +258,7 @@ varies depending on the use-cases of the application and field testing.

estimate.
  • Check which provider the new location is from and determine if you trust it more.
  • - +

    An elaborate example of this logic can look something like this:

    @@ -320,13 +327,13 @@ good performance needs some adjustment. Here are some things you might change to
     balance between the two.

    Reduce the size of the window

    - +

    A smaller window in which you listen for location updates means less interaction with GPS and network location services, thus, preserving battery life. But it also allows for fewer locations from which to choose a best estimate.

    Set the location providers to return updates less frequently

    - +

    Reducing the rate at which new updates appear during the window can also improve battery efficiency, but at the cost of accuracy. The value of the trade-off depends on how your application is used. You can reduce the rate of updates by increasing the parameters in {@link @@ -334,14 +341,14 @@ android.location.LocationManager#requestLocationUpdates requestLocationUpdates() interval time and minimum distance change.

    Restrict a set of providers

    - +

    Depending on the environment where your application is used or the desired level of accuracy, you might choose to use only the Network Location Provider or only GPS, instead of both. Interacting with only one of the services reduces battery usage at a potential cost of accuracy.

    Common application cases

    - +

    There are many reasons you might want to obtain the user location in your application. Below are a couple scenarios in which you can use the user location to enrich your application. Each scenario also describes good practices for when you should start and stop listening for the @@ -349,7 +356,7 @@ location, in order to get a good reading and help preserve battery life.

    Tagging user-created content with a location

    - +

    You might be creating an application where user-created content is tagged with a location. Think of users sharing their local experiences, posting a review for a restaurant, or recording some content that can be augmented with their current location. A model of how this @@ -358,7 +365,7 @@ interaction might happen, with respect to the location services, is visualized i

    Figure 2. A timeline representing the window in which the user location is obtained and listening stops when the user consumes the current location.

    - +

    This lines up with the previous model of how user location is obtained in code (figure 1). For best location accuracy, you might choose to start listening for location updates when users begin creating @@ -368,7 +375,7 @@ content takes and judge if this duration allows for efficient collection of a lo

    Helping the user decide on where to go

    - +

    You might be creating an application that attempts to provide users with a set of options about where to go. For example, you're looking to provide a list of nearby restaurants, stores, and entertainment and the order of recommendations changes depending on the user @@ -381,7 +388,7 @@ location.

    This kind of model is visualized in figure 3.

    - +

    Figure 3. A timeline representing the window in which a dynamic set of data is updated each time the user location updates.

    diff --git a/docs/html/training/basics/location/currentlocation.jd b/docs/html/training/basics/location/currentlocation.jd deleted file mode 100644 index 29b0fa6d20d..00000000000 --- a/docs/html/training/basics/location/currentlocation.jd +++ /dev/null @@ -1,163 +0,0 @@ -page.title=Obtaining the Current Location -parent.title=Making Your App Location Aware -parent.link=index.html - -trainingnavtop=true -previous.title=Using the Location Manager -previous.link=locationmanager.html -next.title=Displaying the Location Address -next.link=geocoding.html - - -@jd:body - - - -
    -
    - -

    This lesson teaches you to

    -
      -
    1. Set Up the Location Listener
    2. -
    3. Handle Multiple Sources of Location Updates
    4. -
    5. Use getLastKnownLocation() Wisely
    6. -
    7. Terminate Location Updates
    8. -
    - -

    You should also read

    - - - -

    Try it out

    - -
    -Download - the sample app -

    LocationAware.zip

    -
    - -
    -
    - -

    After setting up your application to work with {@link android.location.LocationManager}, you can begin to obtain location updates.

    - -

    Set Up the Location Listener

    - -

    The {@link android.location.LocationManager} class exposes a number of methods for applications to receive location updates. In its simplest form, you register an event listener, identify the location manager from which you'd like to receive location updates, and specify the minimum time and distance intervals at which to receive location updates. The {@link android.location.LocationListener#onLocationChanged(android.location.Location) onLocationChanged()} callback will be invoked with the frequency that correlates with time and distance intervals.

    - -

    -In the sample code snippet below, the location listener is set up to receive notifications at least every 10 seconds and if the device moves by more than 10 meters. The other callback methods notify the application any status change coming from the location provider. -

    - -
    -private final LocationListener listener = new LocationListener() {
    -
    -    @Override
    -    public void onLocationChanged(Location location) {
    -        // A new location update is received.  Do something useful with it.  In this case,
    -        // we're sending the update to a handler which then updates the UI with the new
    -        // location.
    -        Message.obtain(mHandler,
    -                UPDATE_LATLNG,
    -                location.getLatitude() + ", " +
    -                location.getLongitude()).sendToTarget();
    -
    -            ...
    -        }
    -    ...
    -};
    -
    -mLocationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER,
    -        10000,          // 10-second interval.
    -        10,             // 10 meters.
    -        listener);
    -
    - -

    Handle Multiple Sources of Location Updates

    - -

    Generally speaking, a location provider with greater accuracy (GPS) requires a longer fix time than one with lower accuracy (network-based). If you want to display location data as quickly as possible and update it as more accurate data becomes available, a common practice is to register a location listener with both GPS and network providers. In the {@link android.location.LocationListener#onLocationChanged(android.location.Location) onLocationChanged()} callback, you'll receive location updates from multiple location providers that may have different timestamps and varying levels of accuracy. You'll need to incorporate logic to disambiguate the location providers and discard updates that are stale and less accurate. The code snippet below demonstrates a sample implementation of this logic.

    - -
    -private static final int TWO_MINUTES = 1000 * 60 * 2;
    -
    -/** Determines whether one Location reading is better than the current Location fix
    -  * @param location  The new Location that you want to evaluate
    -  * @param currentBestLocation  The current Location fix, to which you want to compare the new one
    -  */
    -protected boolean isBetterLocation(Location location, Location currentBestLocation) {
    -    if (currentBestLocation == null) {
    -        // A new location is always better than no location
    -        return true;
    -    }
    -
    -    // Check whether the new location fix is newer or older
    -    long timeDelta = location.getTime() - currentBestLocation.getTime();
    -    boolean isSignificantlyNewer = timeDelta > TWO_MINUTES;
    -    boolean isSignificantlyOlder = timeDelta < -TWO_MINUTES;
    -    boolean isNewer = timeDelta > 0;
    -
    -    // If it's been more than two minutes since the current location, use the new location
    -    // because the user has likely moved
    -    if (isSignificantlyNewer) {
    -        return true;
    -    // If the new location is more than two minutes older, it must be worse
    -    } else if (isSignificantlyOlder) {
    -        return false;
    -    }
    -
    -    // Check whether the new location fix is more or less accurate
    -    int accuracyDelta = (int) (location.getAccuracy() - currentBestLocation.getAccuracy());
    -    boolean isLessAccurate = accuracyDelta > 0;
    -    boolean isMoreAccurate = accuracyDelta < 0;
    -    boolean isSignificantlyLessAccurate = accuracyDelta > 200;
    -
    -    // Check if the old and new location are from the same provider
    -    boolean isFromSameProvider = isSameProvider(location.getProvider(),
    -            currentBestLocation.getProvider());
    -
    -    // Determine location quality using a combination of timeliness and accuracy
    -    if (isMoreAccurate) {
    -        return true;
    -    } else if (isNewer && !isLessAccurate) {
    -        return true;
    -    } else if (isNewer && !isSignificantlyLessAccurate && isFromSameProvider) {
    -        return true;
    -    }
    -    return false;
    -}
    -
    -/** Checks whether two providers are the same */
    -private boolean isSameProvider(String provider1, String provider2) {
    -    if (provider1 == null) {
    -      return provider2 == null;
    -    }
    -    return provider1.equals(provider2);
    -}
    -
    - -

    Use getLastKnownLocation() Wisely

    - -

    The setup time for getting a reasonable location fix may not be acceptable for certain applications. You should consider calling the {@link android.location.LocationManager#getLastKnownLocation(java.lang.String) getLastKnownLocation()} method which simply queries Android for the last location update previously received by any location providers. Keep in mind that the returned location may be stale. You should check the timestamp and accuracy of the returned location and decide whether it is useful for your application. If you elect to discard the location update returned from {@link android.location.LocationManager#getLastKnownLocation(java.lang.String) getLastKnownLocation()} and wait for fresh updates from the location provider(s), you should consider displaying an appropriate message before location data is received.

    - -

    Terminate Location Updates

    - -

    When you are done with using location data, you should terminate location update to reduce -unnecessary consumption of power and network bandwidth. For example, if the user navigates away -from an activity where location updates are displayed, you should stop location update by calling -{@link android.location.LocationManager#removeUpdates(android.location.LocationListener) -removeUpdates()} in {@link android.app.Activity#onStop()}. ({@link android.app.Activity#onStop()} -is called when the activity is no longer visible. If you want to learn more about activity -lifecycle, read up on the Stopping and Restarting an -Activity lesson.

    - -
    -protected void onStop() {
    -    super.onStop();
    -    mLocationManager.removeUpdates(listener);
    -}
    -
    - -

    Note: For applications that need to continuously receive and process location updates like a near-real time mapping application, it is best to incorporate the location update logic in a background service and make use of the system notification bar to make the user aware that location data is being used.

    \ No newline at end of file diff --git a/docs/html/training/basics/location/geocoding.jd b/docs/html/training/basics/location/geocoding.jd deleted file mode 100644 index 3192d14fdb9..00000000000 --- a/docs/html/training/basics/location/geocoding.jd +++ /dev/null @@ -1,98 +0,0 @@ -page.title=Displaying the Location Address -parent.title=Making Your App Location Aware -parent.link=index.html - -trainingnavtop=true -previous.title=Obtaining the Current Location -previous.link=currentlocation.html - -@jd:body - - - -
    -
    - -

    This lesson teaches you to

    -
      -
    1. Perform Reverse Geocoding
    2. -
    - -

    You should also read

    - - - -

    Try it out

    - -
    -Download - the sample app -

    LocationAware.zip

    -
    - -
    -
    - -

    As shown in previous lessons, location updates are received in the form of latitude and longitude coordinates. While this format is useful for calculating distance or displaying a pushpin on a map, the decimal numbers make no sense to most end users. If you need to display a location to user, it is much more preferable to display the address instead.

    - -

    Perform Reverse Geocoding

    - -

    Reverse-geocoding is the process of translating latitude longitude coordinates to a human-readable address. The {@link android.location.Geocoder} API is available for this purpose. Note that behind the scene, the API is dependent on a web service. If such service is unavailable on the device, the API will throw a "Service not Available exception" or return an empty list of addresses. A helper method called {@link android.location.Geocoder#isPresent()} was added in Android 2.3 (API level 9) to check for the existence of the service.

    - -

    The following code snippet demonstrates the use of the {@link android.location.Geocoder} API to perform reverse-geocoding. Since the {@link android.location.Geocoder#getFromLocation(double, double, int) getFromLocation()} method is synchronous, you should not invoke it from the UI thread, hence an {@link android.os.AsyncTask} is used in the snippet.

    - -
    -private final LocationListener listener = new LocationListener() {
    -
    -    public void onLocationChanged(Location location) {
    -        // Bypass reverse-geocoding if the Geocoder service is not available on the
    -        // device. The isPresent() convenient method is only available on Gingerbread or above.
    -        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD && Geocoder.isPresent()) {
    -            // Since the geocoding API is synchronous and may take a while.  You don't want to lock
    -            // up the UI thread.  Invoking reverse geocoding in an AsyncTask.
    -            (new ReverseGeocodingTask(this)).execute(new Location[] {location});
    -        }
    -    }
    -    ...
    -};
    -
    -// AsyncTask encapsulating the reverse-geocoding API.  Since the geocoder API is blocked,
    -// we do not want to invoke it from the UI thread.
    -private class ReverseGeocodingTask extends AsyncTask<Location, Void, Void> {
    -    Context mContext;
    -
    -    public ReverseGeocodingTask(Context context) {
    -        super();
    -        mContext = context;
    -    }
    -
    -    @Override
    -    protected Void doInBackground(Location... params) {
    -        Geocoder geocoder = new Geocoder(mContext, Locale.getDefault());
    -
    -        Location loc = params[0];
    -        List<Address> addresses = null;
    -        try {
    -            // Call the synchronous getFromLocation() method by passing in the lat/long values.
    -            addresses = geocoder.getFromLocation(loc.getLatitude(), loc.getLongitude(), 1);
    -        } catch (IOException e) {
    -            e.printStackTrace();
    -            // Update UI field with the exception.
    -            Message.obtain(mHandler, UPDATE_ADDRESS, e.toString()).sendToTarget();
    -        }
    -        if (addresses != null && addresses.size() > 0) {
    -            Address address = addresses.get(0);
    -            // Format the first line of address (if available), city, and country name.
    -            String addressText = String.format("%s, %s, %s",
    -                    address.getMaxAddressLineIndex() > 0 ? address.getAddressLine(0) : "",
    -                    address.getLocality(),
    -                    address.getCountryName());
    -            // Update the UI via a message handler.
    -            Message.obtain(mHandler, UPDATE_ADDRESS, addressText).sendToTarget();
    -        }
    -        return null;
    -    }
    -}
    -
    diff --git a/docs/html/training/basics/location/index.jd b/docs/html/training/basics/location/index.jd deleted file mode 100644 index 240bbb2e9a6..00000000000 --- a/docs/html/training/basics/location/index.jd +++ /dev/null @@ -1,50 +0,0 @@ -page.title=Making Your App Location Aware -page.tags="geolocation","maps" - -trainingnavtop=true -startpage=true - -@jd:body - -
    -
    - -

    Dependencies and prerequisites

    - -
      -
    • Android 1.0 or higher (2.3+ for the sample app)
    • -
    - -

    You should also read

    - - - -

    Try it out

    - -
    -Download - the sample app -

    LocationAware.zip

    -
    - -
    -
    - -

    Users bring their mobile devices with them almost everywhere. One of the unique features available to mobile applications is location awareness. Knowing the location and using the information wisely can bring a more contextual experience to your users.

    - -

    This class teaches you how to incorporate location based services in your Android application. You'll learn a number of methods to receive location updates and related best practices.

    - -

    Lessons

    - -
    -
    Using the Location Manager
    -
    Learn how to set up your application before it can receive location updates in Android.
    - -
    Obtaining the Current Location
    -
    Learn how to work with underlying location technologies available on the platform to obtain current location.
    - -
    Displaying a Location Address
    -
    Learn how to translate location coordinates into addresses that are readable to users.
    -
    diff --git a/docs/html/training/basics/location/locationmanager.jd b/docs/html/training/basics/location/locationmanager.jd deleted file mode 100644 index 61abcbdcf29..00000000000 --- a/docs/html/training/basics/location/locationmanager.jd +++ /dev/null @@ -1,120 +0,0 @@ -page.title=Using the Location Manager -parent.title=Making Your App Location Aware -parent.link=index.html - -trainingnavtop=true -next.title=Obtaining the Current Location -next.link=currentlocation.html - -@jd:body - - - -
    -
    - -

    This lesson teaches you to

    -
      -
    1. Declare Proper Permissions in Android Manifest
    2. -
    3. Get a Reference to LocationManager
    4. -
    5. Pick a Location Provider
    6. -
    7. Verify the Location Provider is Enabled
    8. -
    - -

    You should also read

    - - - -

    Try it out

    - -
    -Download - the sample app -

    LocationAware.zip

    -
    - -
    -
    - -

    Before your application can begin receiving location updates, it needs to perform some simple steps to set up access. In this lesson, you'll learn what these steps entail.

    - -

    Declare Proper Permissions in Android Manifest

    - -

    The first step of setting up location update access is to declare proper permissions in the manifest. If permissions are missing, the application will get a {@link java.lang.SecurityException} at runtime.

    - -

    Depending on the {@link android.location.LocationManager} methods used, either {@link android.Manifest.permission#ACCESS_COARSE_LOCATION} or {@link android.Manifest.permission#ACCESS_FINE_LOCATION} permission is needed. For example, you need to declare the {@link android.Manifest.permission#ACCESS_COARSE_LOCATION} permission if your application uses a network-based location provider only. The more accurate GPS requires the {@link android.Manifest.permission#ACCESS_FINE_LOCATION} permission. -Note that declaring the {@link android.Manifest.permission#ACCESS_FINE_LOCATION} permission implies {@link android.Manifest.permission#ACCESS_COARSE_LOCATION} already.

    - -

    Also, if a network-based location provider is used in the application, you'll need to declare the internet permission as well.

    - -
    -<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
    -<uses-permission android:name="android.permission.INTERNET" />
    -
    - -

    Get a Reference to LocationManager

    - -

    {@link android.location.LocationManager} is the main class through which your application can access location services on Android. Similar to other system services, a reference can be obtained from calling the {@link android.content.Context#getSystemService(java.lang.String) getSystemService()} method. If your application intends to receive location updates in the foreground (within an {@link android.app.Activity}), you should usually perform this step in the {@link android.app.Activity#onCreate(android.os.Bundle) onCreate()} method.

    - -
    -LocationManager locationManager =
    -        (LocationManager) this.getSystemService(Context.LOCATION_SERVICE);
    -
    - -

    Pick a Location Provider

    - -

    While not required, most modern Android-powered devices can receive location updates through multiple underlying technologies, which are abstracted to an application as {@link android.location.LocationProvider} objects. Location providers may have different performance characteristics in terms of time-to-fix, accuracy, monetary cost, power consumption, and so on. Generally, a location provider with a greater accuracy, like the GPS, requires a longer fix time than a less accurate one, such as a network-based location provider.

    - -

    Depending on your application's use case, you have to choose a specific location provider, or multiple providers, based on similar tradeoffs. For example, a points of interest check-in application would require higher location accuracy than say, a retail store locator where a city level location fix would suffice. The snippet below asks for a provider backed by the GPS.

    - -
    -LocationProvider provider =
    -        locationManager.getProvider(LocationManager.GPS_PROVIDER);
    -
    - -

    Alternatively, you can provide some input criteria such as accuracy, power requirement, monetary cost, and so on, and let Android decide a closest match location provider. The snippet below asks for a location provider with fine accuracy and no monetary cost. Note that the criteria may not resolve to any providers, in which case a null will be returned. Your application should be prepared to gracefully handle the situation.

    - -
    -// Retrieve a list of location providers that have fine accuracy, no monetary cost, etc
    -Criteria criteria = new Criteria();
    -criteria.setAccuracy(Criteria.ACCURACY_FINE);
    -criteria.setCostAllowed(false);
    -...
    -String providerName = locManager.getBestProvider(criteria, true);
    -
    -// If no suitable provider is found, null is returned.
    -if (providerName != null) {
    -   ...
    -}
    -
    - -

    Verify the Location Provider is Enabled

    - -

    Some location providers such as the GPS can be disabled in Settings. It is good practice to check whether the desired location provider is currently enabled by calling the {@link android.location.LocationManager#isProviderEnabled(java.lang.String) isProviderEnabled()} method. If the location provider is disabled, you can offer the user an opportunity to enable it in Settings by firing an {@link android.content.Intent} with the {@link android.provider.Settings#ACTION_LOCATION_SOURCE_SETTINGS} action.

    - -
    -@Override
    -protected void onStart() {
    -    super.onStart();
    -
    -    // This verification should be done during onStart() because the system calls
    -    // this method when the user returns to the activity, which ensures the desired
    -    // location provider is enabled each time the activity resumes from the stopped state.
    -    LocationManager locationManager =
    -            (LocationManager) getSystemService(Context.LOCATION_SERVICE);
    -    final boolean gpsEnabled = locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER);
    -
    -    if (!gpsEnabled) {
    -        // Build an alert dialog here that requests that the user enable
    -        // the location services, then when the user clicks the "OK" button,
    -        // call enableLocationSettings()
    -    }
    -}
    -
    -private void enableLocationSettings() {
    -    Intent settingsIntent = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS);
    -    startActivity(settingsIntent);
    -}
    -
    diff --git a/docs/html/training/location/activity-recognition.jd b/docs/html/training/location/activity-recognition.jd new file mode 100644 index 00000000000..79804db2102 --- /dev/null +++ b/docs/html/training/location/activity-recognition.jd @@ -0,0 +1,781 @@ +page.title=Recognizing the User's Current Activity + +trainingnavtop=true +@jd:body + +
    +
    +

    This lesson teaches you to

    +
      +
    1. Request Activity Recognition Updates
    2. +
    3. Handle Activity Updates +
    4. Stop Activity Recognition Updates +
    +

    You should also read

    + +

    Try it out

    + +
    + Download the sample +

    ActivityRecognition.zip

    +
    + +
    +
    + +

    + This lesson shows you how to request activity recognition updates from Location Services. + Activity recognition tries to detect the user's current physical activity, such as walking, + driving, or standing still. Requests for updates go through an activity recognition client, + which, while different from the location client used by location or geofencing, follows a + similar pattern. Based on the update interval you choose, Location Services sends out + activity information containing one or more possible activities and the confidence level for + each one. +

    +

    Request Activity Recognition Updates

    +

    + Requesting activity recognition updates from Location Services is similar to requesting + periodic location updates. You send the request through a client, and Location Services sends + updates back to your app by means of a {@link android.app.PendingIntent}. However, you need to + request a special permission before you request activity updates, and you use a different type + of client to make requests. The following sections show how to request the permission, + connect the client, and request updates. +

    +

    Request permission to receive updates

    +

    + An app that wants to get activity recognition updates must have the permission + {@code com.google.android.gms.permission.ACTIVITY_RECOGNITION}. To request this permission for + your app, add the following XML element to your manifest as a child element of the +<manifest> + element: +

    +
    +<uses-permission
    +    android:name="com.google.android.gms.permission.ACTIVITY_RECOGNITION"/>
    +
    +

    + Activity recognition does not require the permissions + {@link android.Manifest.permission#ACCESS_COARSE_LOCATION ACCESS_COARSE_LOCATION} or + {@link android.Manifest.permission#ACCESS_FINE_LOCATION ACCESS_FINE_LOCATION}. +

    + +

    Check for Google Play Services

    +

    + Location Services is part of the Google Play services APK. Since it's hard to anticipate the + state of the user's device, you should always check that the APK is installed before you attempt + to connect to Location Services. To check that the APK is installed, call +GooglePlayServicesUtil.isGooglePlayServicesAvailable(), + which returns one of the + integer result codes listed in the API reference documentation. If you encounter an error, + call +GooglePlayServicesUtil.getErrorDialog() + to retrieve localized dialog that prompts users to take the correct action, then display + the dialog in a {@link android.support.v4.app.DialogFragment}. The dialog may allow the + user to correct the problem, in which case Google Play services may send a result back to your + activity. To handle this result, override the method + {@link android.support.v4.app.FragmentActivity#onActivityResult onActivityResult()} + +

    +

    + Note: To make your app compatible with + platform version 1.6 and later, the activity that displays the + {@link android.support.v4.app.DialogFragment} must subclass + {@link android.support.v4.app.FragmentActivity} instead of {@link android.app.Activity}. Using + {@link android.support.v4.app.FragmentActivity} also allows you to call + {@link android.support.v4.app.FragmentActivity#getSupportFragmentManager + getSupportFragmentManager()} to display the {@link android.support.v4.app.DialogFragment}. +

    +

    + Since you usually need to check for Google Play services in more than one place in your code, + define a method that encapsulates the check, then call the method before each connection + attempt. The following snippet contains all of the code required to check for Google + Play services: +

    +
    +public class MainActivity extends FragmentActivity {
    +    ...
    +    // Global constants
    +    /*
    +     * Define a request code to send to Google Play services
    +     * This code is returned in Activity.onActivityResult
    +     */
    +    private final static int
    +            CONNECTION_FAILURE_RESOLUTION_REQUEST = 9000;
    +    ...
    +    // Define a DialogFragment that displays the error dialog
    +    public static class ErrorDialogFragment extends DialogFragment {
    +        // Global field to contain the error dialog
    +        private Dialog mDialog;
    +        // Default constructor. Sets the dialog field to null
    +        public ErrorDialogFragment() {
    +            super();
    +            mDialog = null;
    +        }
    +        // Set the dialog to display
    +        public void setDialog(Dialog dialog) {
    +            mDialog = dialog;
    +        }
    +        // Return a Dialog to the DialogFragment.
    +        @Override
    +        public Dialog onCreateDialog(Bundle savedInstanceState) {
    +            return mDialog;
    +        }
    +    }
    +    ...
    +    /*
    +     * Handle results returned to the FragmentActivity
    +     * by Google Play services
    +     */
    +    @Override
    +    protected void onActivityResult(
    +            int requestCode, int resultCode, Intent data) {
    +        // Decide what to do based on the original request code
    +        switch (requestCode) {
    +            ...
    +            case CONNECTION_FAILURE_RESOLUTION_REQUEST :
    +            /*
    +             * If the result code is Activity.RESULT_OK, try
    +             * to connect again
    +             */
    +                switch (resultCode) {
    +                    case Activity.RESULT_OK :
    +                    /*
    +                     * Try the request again
    +                     */
    +                    ...
    +                    break;
    +                }
    +            ...
    +        }
    +        ...
    +    }
    +    ...
    +    private boolean servicesConnected() {
    +        // Check that Google Play services is available
    +        int resultCode =
    +                GooglePlayServicesUtil.
    +                        isGooglePlayServicesAvailable(this);
    +        // If Google Play services is available
    +        if (ConnectionResult.SUCCESS == resultCode) {
    +            // In debug mode, log the status
    +            Log.d("Activity Recognition",
    +                    "Google Play services is available.");
    +            // Continue
    +            return true;
    +        // Google Play services was not available for some reason
    +        } else {
    +            // Get the error code
    +            int errorCode = connectionResult.getErrorCode();
    +            // Get the error dialog from Google Play services
    +            Dialog errorDialog = GooglePlayServicesUtil.getErrorDialog(
    +                    errorCode,
    +                    this,
    +                    CONNECTION_FAILURE_RESOLUTION_REQUEST);
    +
    +            // If Google Play services can provide an error dialog
    +            if (errorDialog != null) {
    +                // Create a new DialogFragment for the error dialog
    +                ErrorDialogFragment errorFragment =
    +                        new ErrorDialogFragment();
    +                // Set the dialog in the DialogFragment
    +                errorFragment.setDialog(errorDialog);
    +                // Show the error dialog in the DialogFragment
    +                errorFragment.show(
    +                        getSupportFragmentManager(),
    +                        "Activity Recognition");
    +            }
    +            return false;
    +        }
    +    }
    +    ...
    +}
    +
    +

    + Snippets in the following sections call this method to verify that Google Play services is + available. +

    +

    Send the activity update request

    +

    + Send the update request from an {@link android.app.Activity} or + {@link android.support.v4.app.Fragment} that implements the callback methods required by + Location Services. Making the request is an asynchronous process that starts when you request + a connection to an activity recognition client. When the client is connected, Location Services + invokes your implementation of +onConnected(). + In this method, you can send the update request to Location Services; this request is + synchronous. Once you've made the request, you can disconnect the client. +

    +

    + This process is described in the following snippets. +

    +

    Define the Activity or Fragment

    +

    + Define an {@link android.support.v4.app.FragmentActivity} or + {@link android.support.v4.app.Fragment} that implements the following interfaces: +

    +
    +
    +ConnectionCallbacks +
    +
    + Specifies methods that Location Services calls when the client is connected or + disconnected. +
    +
    +OnConnectionFailedListener +
    +
    + Specifies a method that Location Services calls if an error occurs while attempting to + connect the client. +
    +
    +

    + For example: +

    +
    +public class MainActivity extends FragmentActivity implements
    +        ConnectionCallbacks, OnConnectionFailedListener {
    +    ...
    +}
    +
    +

    + Next, define global variables and constants. Define constants for the update interval, + add a variable for the activity recognition client, and another for the + {@link android.app.PendingIntent} that Location Services uses to send updates to your app: +

    +
    +public class MainActivity extends FragmentActivity implements
    +        ConnectionCallbacks, OnConnectionFailedListener {
    +    ...
    +    // Constants that define the activity detection interval
    +    public static final int MILLISECONDS_PER_SECOND = 1000;
    +    public static final int DETECTION_INTERVAL_SECONDS = 20;
    +    public static final int DETECTION_INTERVAL_MILLISECONDS =
    +            MILLISECONDS_PER_SECOND * DETECTION_INTERVAL_SECONDS;
    +    ...
    +    /*
    +     * Store the PendingIntent used to send activity recognition events
    +     * back to the app
    +     */
    +    private PendingIntent mActivityRecognitionPendingIntent;
    +    // Store the current activity recognition client
    +    private ActivityRecognitionClient mActivityRecognitionClient;
    +    ...
    +}
    +
    +

    + In {@link android.app.Activity#onCreate onCreate()}, instantiate the activity recognition + client and the {@link android.app.PendingIntent}: +

    +
    +public class MainActivity extends FragmentActivity implements
    +        ConnectionCallbacks, OnConnectionFailedListener {
    +    ...
    +    @Override
    +    onCreate(Bundle savedInstanceState) {
    +        ...
    +        /*
    +         * Instantiate a new activity recognition client. Since the
    +         * parent Activity implements the connection listener and
    +         * connection failure listener, the constructor uses "this"
    +         * to specify the values of those parameters.
    +         */
    +        mActivityRecognitionClient =
    +                new ActivityRecognitionClient(mContext, this, this);
    +        /*
    +         * Create the PendingIntent that Location Services uses
    +         * to send activity recognition updates back to this app.
    +         */
    +        Intent intent = new Intent(
    +                mContext, ActivityRecognitionIntentService.class);
    +        /*
    +         * Return a PendingIntent that starts the IntentService.
    +         */
    +        mActivityRecognitionPendingIntent =
    +                PendingIntent.getService(mContext, 0, intent,
    +                PendingIntent.FLAG_UPDATE_CURRENT);
    +        ...
    +    }
    +    ...
    +}
    +
    +

    Start the request process

    +

    + Define a method that requests activity recognition updates. In the method, request a + connection to Location Services. You can call this method from anywhere in your activity; its + purpose is to start the chain of method calls for requesting updates. +

    +

    + To guard against race conditions that might arise if your app tries to start another request + before the first one finishes, define a boolean flag that tracks the state of the current + request. Set the flag to {@code true} when you start a request, and then set it to + {@code false} when the request completes. +

    +

    + The following snippet shows how to start a request for updates: +

    +
    +public class MainActivity extends FragmentActivity implements
    +        ConnectionCallbacks, OnConnectionFailedListener {
    +    ...
    +    // Global constants
    +    ...
    +    // Flag that indicates if a request is underway.
    +    private boolean mInProgress;
    +    ...
    +    @Override
    +    onCreate(Bundle savedInstanceState) {
    +        ...
    +        // Start with the request flag set to false
    +        mInProgress = false;
    +        ...
    +    }
    +    ...
    +    /**
    +     * Request activity recognition updates based on the current
    +     * detection interval.
    +     *
    +     */
    +     public void startUpdates() {
    +        // Check for Google Play services
    +
    +        if (!servicesConnected()) {
    +            return;
    +        }
    +        // If a request is not already underway
    +        if (!mInProgress) {
    +            // Indicate that a request is in progress
    +            mInProgress = true;
    +            // Request a connection to Location Services
    +            mActivityRecognitionClient.connect();
    +        //
    +        } else {
    +            /*
    +             * A request is already underway. You can handle
    +             * this situation by disconnecting the client,
    +             * re-setting the flag, and then re-trying the
    +             * request.
    +             */
    +        }
    +    }
    +    ...
    +}
    +
    +

    + Implement +onConnected(). + In this method, request activity recognition updates from Location Services. When Location + Services finishes connecting to the client and calls +onConnected(), + the update request is called immediately: +

    +
    +public class MainActivity extends FragmentActivity implements
    +        ConnectionCallbacks, OnConnectionFailedListener {
    +    ...
    +    /*
    +     * Called by Location Services once the location client is connected.
    +     *
    +     * Continue by requesting activity updates.
    +     */
    +    @Override
    +    public void onConnected(Bundle dataBundle) {
    +        /*
    +         * Request activity recognition updates using the preset
    +         * detection interval and PendingIntent. This call is
    +         * synchronous.
    +         */
    +        mActivityRecognitionClient.requestActivityUpdates(
    +                DETECTION_INTERVAL_MILLISECONDS,
    +                mActivityRecognitionPendingIntent);
    +        /*
    +         * Since the preceding call is synchronous, turn off the
    +         * in progress flag and disconnect the client
    +         */
    +        mInProgress = false;
    +        mActivityRecognitionClient.disconnect();
    +    }
    +    ...
    +}
    +
    +

    Handle disconnections

    +

    + In some cases, Location Services may disconnect from the activity recognition client before + you call +disconnect(). + To handle this situation, implement +onDisconnected(). + In this method, set the request flag to indicate that a request is not in progress, and + delete the client: +

    +
    +public class MainActivity extends FragmentActivity implements
    +        ConnectionCallbacks, OnConnectionFailedListener {
    +    ...
    +    /*
    +     * Called by Location Services once the activity recognition
    +     * client is disconnected.
    +     */
    +    @Override
    +    public void onDisconnected() {
    +        // Turn off the request flag
    +        mInProgress = false;
    +        // Delete the client
    +        mActivityRecognitionClient = null;
    +    }
    +    ...
    +}
    +
    + +

    Handle connection errors

    +

    + Besides handling the normal callbacks from Location Services, you have to provide a callback + method that Location Services calls if a connection error occurs. This callback method + can re-use the {@link android.support.v4.app.DialogFragment} class that you defined to + handle the check for Google Play services. It can also re-use the override you defined + for {@link android.support.v4.app.FragmentActivity#onActivityResult onActivityResult()} that + receives any Google Play services results that occur when the user interacts with the + error dialog. The following snippet shows you a sample implementation of the callback method: +

    +
    +public class MainActivity extends FragmentActivity implements
    +        ConnectionCallbacks, OnConnectionFailedListener {
    +    ...
    +    // Implementation of OnConnectionFailedListener.onConnectionFailed
    +    @Override
    +    public void onConnectionFailed(ConnectionResult connectionResult) {
    +        // Turn off the request flag
    +        mInProgress = false;
    +        /*
    +         * If the error has a resolution, start a Google Play services
    +         * activity to resolve it.
    +         */
    +        if (connectionResult.hasResolution()) {
    +            try {
    +                connectionResult.startResolutionForResult(
    +                        this,
    +                        CONNECTION_FAILURE_RESOLUTION_REQUEST);
    +            } catch (SendIntentException e) {
    +                // Log the error
    +                e.printStackTrace();
    +            }
    +        // If no resolution is available, display an error dialog
    +        } else {
    +            // Get the error code
    +            int errorCode = connectionResult.getErrorCode();
    +            // Get the error dialog from Google Play services
    +            Dialog errorDialog = GooglePlayServicesUtil.getErrorDialog(
    +                    errorCode,
    +                    this,
    +                    CONNECTION_FAILURE_RESOLUTION_REQUEST);
    +            // If Google Play services can provide an error dialog
    +            if (errorDialog != null) {
    +                // Create a new DialogFragment for the error dialog
    +                ErrorDialogFragment errorFragment =
    +                        new ErrorDialogFragment();
    +                // Set the dialog in the DialogFragment
    +                errorFragment.setDialog(errorDialog);
    +                // Show the error dialog in the DialogFragment
    +                errorFragment.show(
    +                        getSupportFragmentManager(),
    +                        "Activity Recognition");
    +            }
    +        }
    +        ...
    +    }
    +    ...
    +}
    +
    + +

    Handle Activity Updates

    +

    + To handle the {@link android.content.Intent} that Location Services sends for each update + interval, define an {@link android.app.IntentService} and its required method + {@link android.app.IntentService#onHandleIntent onHandleIntent()}. Location Services + sends out activity recognition updates as {@link android.content.Intent} objects, using the + the {@link android.app.PendingIntent} you provided when you called +requestActivityUpdates(). + Since you provided an explicit intent for the {@link android.app.PendingIntent}, the only + component that receives the intent is the {@link android.app.IntentService} you're defining. +

    +

    + The following snippets demonstrate how to examine the data in an activity recognition + update. +

    +

    Define an IntentService

    +

    + Start by defining the class and the required method + {@link android.app.IntentService#onHandleIntent onHandleIntent()}: +

    +
    +/**
    + * Service that receives ActivityRecognition updates. It receives
    + * updates in the background, even if the main Activity is not visible.
    + */
    +public class ActivityRecognitionIntentService extends IntentService {
    +    ...
    +    /**
    +     * Called when a new activity detection update is available.
    +     */
    +    @Override
    +    protected void onHandleIntent(Intent intent) {
    +        ...
    +    }
    +    ...
    +}
    +
    +

    + Next, examine the data in the intent. From the update, you can get a list of possible activities + and the probability of each one. The following snippet shows how to get the most probable + activity, the confidence level for the activity (the probability that this is the actual + activity), and its type: +

    +
    +public class ActivityRecognitionIntentService extends IntentService {
    +    ...
    +    @Override
    +    protected void onHandleIntent(Intent intent) {
    +        ...
    +        // If the incoming intent contains an update
    +        if (ActivityRecognitionResult.hasResult(intent)) {
    +            // Get the update
    +            ActivityRecognitionResult result =
    +                    ActivityRecognitionResult.extractResult(intent);
    +            // Get the most probable activity
    +            DetectedActivity mostProbableActivity =
    +                    result.getMostProbableActivity();
    +            /*
    +             * Get the probability that this activity is the
    +             * the user's actual activity
    +             */
    +            int confidence = mostProbableActivity.getConfidence();
    +            /*
    +             * Get an integer describing the type of activity
    +             */
    +            int activityType = mostProbableActivity.getType();
    +            String activityName = getNameFromType(activityType);
    +            /*
    +             * At this point, you have retrieved all the information
    +             * for the current update. You can display this
    +             * information to the user in a notification, or
    +             * send it to an Activity or Service in a broadcast
    +             * Intent.
    +             */
    +            ...
    +        } else {
    +            /*
    +             * This implementation ignores intents that don't contain
    +             * an activity update. If you wish, you can report them as
    +             * errors.
    +             */
    +        }
    +        ...
    +    }
    +    ...
    +}
    +
    +

    + The method {@code getNameFromType()} converts activity types into descriptive + strings. In a production app, you should retrieve the strings from resources instead of + using fixed values: +

    +
    +public class ActivityRecognitionIntentService extends IntentService {
    +    ...
    +    /**
    +     * Map detected activity types to strings
    +     *@param activityType The detected activity type
    +     *@return A user-readable name for the type
    +     */
    +    private String getNameFromType(int activityType) {
    +        switch(activityType) {
    +            case DetectedActivity.IN_VEHICLE:
    +                return "in_vehicle";
    +            case DetectedActivity.ON_BICYCLE:
    +                return "on_bicycle";
    +            case DetectedActivity.ON_FOOT:
    +                return "on_foot";
    +            case DetectedActivity.STILL:
    +                return "still";
    +            case DetectedActivity.UNKNOWN:
    +                return "unknown";
    +            case DetectedActivity.TILTING:
    +                return "tilting";
    +        }
    +        return "unknown";
    +    }
    +    ...
    +}
    +
    + +

    Specify the IntentService in the manifest

    +

    + To identify the {@link android.app.IntentService} to the system, add a + <service> + element to the app manifest. For example: +

    +
    +<service
    +    android:name="com.example.android.location.ActivityRecognitionIntentService"
    +    android:label="@string/app_name"
    +    android:exported="false">
    +</service>
    +
    +

    + Notice that you don't have to specify intent filters for the service, because it only receives + explicit intents. How the incoming activity update intents are created is described in the + section Define the Activity or Fragment. +

    +

    Stop Activity Recognition Updates

    +

    + To stop activity recognition updates, use the same pattern you used to request updates, + but call +removeActivityUpdates() + instead of requestActivityUpdates(). +

    +

    +

    + Since removing updates uses some of the methods you use to add updates, start by defining + request types for the two operations: +

    +
    +public class MainActivity extends FragmentActivity implements
    +        ConnectionCallbacks, OnConnectionFailedListener {
    +    ...
    +    public enum REQUEST_TYPE = {START, STOP}
    +    private REQUEST_TYPE mRequestType;
    +    ...
    +}
    +
    +

    + Modify the code that starts activity recognition so that it uses the {@code START} + request type: +

    +
    +public class MainActivity extends FragmentActivity implements
    +        ConnectionCallbacks, OnConnectionFailedListener {
    +    ...
    +    public void startUpdates() {
    +        // Set the request type to START
    +        mRequestType = START;
    +        /*
    +         * Test for Google Play services after setting the request type.
    +         * If Google Play services isn't present, the proper request type
    +         * can be restarted.
    +         */
    +        if (!servicesConnected()) {
    +            return;
    +        }
    +        ...
    +    }
    +    ...
    +    public void onConnected(Bundle dataBundle) {
    +        switch (mRequestType) {
    +            case START :
    +                /*
    +                 * Request activity recognition updates using the
    +                 * preset detection interval and PendingIntent.
    +                 * This call is synchronous.
    +                 */
    +                mActivityRecognitionClient.requestActivityUpdates(
    +                        DETECTION_INTERVAL_MILLISECONDS,
    +                        mActivityRecognitionPendingIntent());
    +                break;
    +            ...
    +        }
    +        ...
    +    }
    +    ...
    +}
    +
    +

    Start the process

    +

    + Define a method that requests a stop to activity recognition updates. In the method, + set the request type and then request a connection to Location Services. You can call this + method from anywhere in your activity; its purpose is to start the chain of method calls that + stop activity updates: +

    +
    +public class MainActivity extends FragmentActivity implements
    +        ConnectionCallbacks, OnConnectionFailedListener {
    +    ...
    +    /**
    +     * Turn off activity recognition updates
    +     *
    +     */
    +    public void stopUpdates() {
    +        // Set the request type to STOP
    +        mRequestType = STOP;
    +        /*
    +         * Test for Google Play services after setting the request type.
    +         * If Google Play services isn't present, the request can be
    +         * restarted.
    +         */
    +        if (!servicesConnected()) {
    +            return;
    +        }
    +        // If a request is not already underway
    +        if (!mInProgress) {
    +            // Indicate that a request is in progress
    +            mInProgress = true;
    +            // Request a connection to Location Services
    +            mActivityRecognitionClient.connect();
    +        //
    +        } else {
    +            /*
    +             * A request is already underway. You can handle
    +             * this situation by disconnecting the client,
    +             * re-setting the flag, and then re-trying the
    +             * request.
    +             */
    +        }
    +        ...
    +    }
    +    ...
    +}
    +
    +

    + In +onConnected(), + if the request type is STOP, call +removeActivityUpdates(). + Pass the {@link android.app.PendingIntent} you used to start updates as the parameter to +removeActivityUpdates(): +

    +
    +public class MainActivity extends FragmentActivity implements
    +        ConnectionCallbacks, OnConnectionFailedListener {
    +    ...
    +    public void onConnected(Bundle dataBundle) {
    +        switch (mRequestType) {
    +            ...
    +            case STOP :
    +            mActivityRecognitionClient.removeActivityUpdates(
    +                    mActivityRecognitionPendingIntent);
    +            break;
    +
    +        }
    +        ...
    +    }
    +    ...
    +}
    +
    +

    + You do not have to modify your implementation of +onDisconnected() + or +onConnectionFailed(), + because these methods do not depend on the request type. +

    +

    + You now have the basic structure of an app that implements activity recognition. You can combine + activity recognition with other location-aware features, such as periodic location updates or + geofencing, which are described in other lessons in this class. +

    diff --git a/docs/html/training/location/display-address.jd b/docs/html/training/location/display-address.jd new file mode 100644 index 00000000000..621b0822432 --- /dev/null +++ b/docs/html/training/location/display-address.jd @@ -0,0 +1,280 @@ +page.title=Displaying a Location Address + +trainingnavtop=true + +@jd:body + + + +
    +
    + +

    This lesson teaches you to

    +
      +
    1. Define the Address Lookup Task
    2. +
    3. Define a Method to Display the Results
    4. +
    5. Run the Lookup Task
    6. +
    + +

    You should also read

    + +

    Try it out

    + +
    +Download + the sample app +

    LocationUpdates.zip

    +
    + +
    +
    + +

    + The lessons Retrieving the Current Location and + Receiving Location Updates describe how to get the + user's current location in the form of a {@link android.location.Location} object that + contains latitude and longitude coordinates. Although latitude and longitude are useful for + calculating distance or displaying a map position, in many cases the address of the location is + more useful. +

    +

    + The Android platform API provides a feature that returns an estimated street addresses for + latitude and longitude values. This lesson shows you how to use this address lookup feature. +

    +

    + Note: Address lookup requires a backend service that is not included in the + core Android framework. If this backend service is not available, + {@link android.location.Geocoder#getFromLocation Geocoder.getFromLocation()} returns an empty + list. The helper method {@link android.location.Geocoder#isPresent isPresent()}, available + in API level 9 and later, checks to see if the backend service is available. +

    +

    + The snippets in the following sections assume that your app has already retrieved the + current location and stored it as a {@link android.location.Location} object in the global + variable {@code mLocation}. +

    + +

    Define the Address Lookup Task

    +

    +To get an address for a given latitude and longitude, call +{@link android.location.Geocoder#getFromLocation Geocoder.getFromLocation()}, which returns a +list of addresses. The method is synchronous, and may take a long time to do its work, so you +should call the method from the {@link android.os.AsyncTask#doInBackground +doInBackground()} method of an {@link android.os.AsyncTask}. +

    +

    +While your app is getting the address, display an indeterminate activity +indicator to show that your app is working in the background. Set the indicator's initial state +to {@code android:visibility="gone"}, to make it invisible and remove it from the layout +hierarchy. When you start the address lookup, you set its visibility to "visible". +

    +

    +The following snippet shows how to add an indeterminate {@link android.widget.ProgressBar} to +your layout file: +

    +
    +<ProgressBar
    +android:id="@+id/address_progress"
    +android:layout_width="wrap_content"
    +android:layout_height="wrap_content"
    +android:layout_centerHorizontal="true"
    +android:indeterminate="true"
    +android:visibility="gone" />
    +
    +

    +To create the background task, define a subclass of {@link android.os.AsyncTask} that calls +{@link android.location.Geocoder#getFromLocation getFromLocation()} and returns an address. +Define a {@link android.widget.TextView} object {@code mAddress} to contain the returned +address, and a {@link android.widget.ProgressBar} object that allows you to control the +indeterminate activity indicator. For example: +

    +
    +public class MainActivity extends FragmentActivity {
    +    ...
    +    private TextView mAddress;
    +    private ProgressBar mActivityIndicator;
    +    ...
    +    @Override
    +    protected void onCreate(Bundle savedInstanceState) {
    +    super.onCreate(savedInstanceState);
    +    ...
    +    mAddress = (TextView) findViewById(R.id.address);
    +    mActivityIndicator =
    +            (ProgressBar) findViewById(R.id.address_progress);
    +    }
    +    ...
    +    /**
    +    * A subclass of AsyncTask that calls getFromLocation() in the
    +    * background. The class definition has these generic types:
    +    * Location - A {@link android.location.Location} object containing
    +    * the current location.
    +    * Void     - indicates that progress units are not used
    +    * String   - An address passed to onPostExecute()
    +    */
    +    private class GetAddressTask extends
    +            AsyncTask<Location, Void, String> {
    +        Context mContext;
    +        public GetAddressTask(Context context) {
    +            super();
    +            mContext = context;
    +        }
    +        ...
    +        /**
    +         * Get a Geocoder instance, get the latitude and longitude
    +         * look up the address, and return it
    +         *
    +         * @params params One or more Location objects
    +         * @return A string containing the address of the current
    +         * location, or an empty string if no address can be found,
    +         * or an error message
    +         */
    +        @Override
    +        protected String doInBackground(Location... params) {
    +            Geocoder geocoder =
    +                    new Geocoder(mContext, Locale.getDefault());
    +            // Get the current location from the input parameter list
    +            Location loc = params[0];
    +            // Create a list to contain the result address
    +            List<Address> addresses = null;
    +            try {
    +                /*
    +                 * Return 1 address.
    +                 */
    +                addresses = geocoder.getFromLocation(loc.getLatitude(),
    +                        loc.getLongitude(), 1);
    +            } catch (IOException e1) {
    +            Log.e("LocationSampleActivity",
    +                    "IO Exception in getFromLocation()");
    +            e1.printStackTrace();
    +            return ("IO Exception trying to get address");
    +            } catch (IllegalArgumentException e2) {
    +            // Error message to post in the log
    +            String errorString = "Illegal arguments " +
    +                    Double.toString(loc.getLatitude()) +
    +                    " , " +
    +                    Double.toString(loc.getLongitude()) +
    +                    " passed to address service";
    +            Log.e("LocationSampleActivity", errorString);
    +            e2.printStackTrace();
    +            return errorString;
    +            }
    +            // If the reverse geocode returned an address
    +            if (addresses != null && addresses.size() > 0) {
    +                // Get the first address
    +                Address address = addresses.get(0);
    +                /*
    +                 * Format the first line of address (if available),
    +                 * city, and country name.
    +                 */
    +                String addressText = String.format(
    +                        "%s, %s, %s",
    +                        // If there's a street address, add it
    +                        address.getMaxAddressLineIndex() > 0 ?
    +                                address.getAddressLine(0) : "",
    +                        // Locality is usually a city
    +                        address.getLocality(),
    +                        // The country of the address
    +                        address.getCountryName());
    +                // Return the text
    +                return addressText;
    +            } else {
    +                return "No address found";
    +            }
    +        }
    +        ...
    +    }
    +    ...
    +}
    +
    +

    +The next section shows you how to display the address in the user interface. +

    + +

    Define a Method to Display the Results

    +

    + {@link android.os.AsyncTask#doInBackground doInBackground()} returns the result of the address + lookup as a {@link java.lang.String}. This value is passed to + {@link android.os.AsyncTask#onPostExecute onPostExecute()}, where you do further processing + on the results. Since {@link android.os.AsyncTask#onPostExecute onPostExecute()} + runs on the UI thread, it can update the user interface; for example, it can turn off the + activity indicator and display the results to the user: +

    +
    +    private class GetAddressTask extends
    +            AsyncTask<Location, Void, String> {
    +        ...
    +        /**
    +         * A method that's called once doInBackground() completes. Turn
    +         * off the indeterminate activity indicator and set
    +         * the text of the UI element that shows the address. If the
    +         * lookup failed, display the error message.
    +         */
    +        @Override
    +        protected void onPostExecute(String address) {
    +            // Set activity indicator visibility to "gone"
    +            mActivityIndicator.setVisibility(View.GONE);
    +            // Display the results of the lookup.
    +            mAddress.setText(address);
    +        }
    +        ...
    +    }
    +
    +

    + The final step is to run the address lookup. +

    + +

    Run the Lookup Task

    +

    + To get the address, call {@link android.os.AsyncTask#execute execute()}. For example, the + following snippet starts the address lookup when the user clicks the "Get Address" button: +

    +
    +public class MainActivity extends FragmentActivity {
    +    ...
    +    /**
    +     * The "Get Address" button in the UI is defined with
    +     * android:onClick="getAddress". The method is invoked whenever the
    +     * user clicks the button.
    +     *
    +     * @param v The view object associated with this method,
    +     * in this case a Button.
    +     */
    +    public void getAddress(View v) {
    +        // Ensure that a Geocoder services is available
    +        if (Build.VERSION.SDK_INT >=
    +                Build.VERSION_CODES.GINGERBREAD
    +                            &&
    +                Geocoder.isPresent()) {
    +            // Show the activity indicator
    +            mActivityIndicator.setVisibility(View.VISIBLE);
    +            /*
    +             * Reverse geocoding is long-running and synchronous.
    +             * Run it on a background thread.
    +             * Pass the current location to the background task.
    +             * When the task finishes,
    +             * onPostExecute() displays the address.
    +             */
    +            (new GetAddressTask(this)).execute(mLocation);
    +        }
    +        ...
    +    }
    +    ...
    +}
    +
    +

    + The next lesson, Creating and Monitoring Geofences, demonstrates + how to define locations of interest called geofences and how to use geofence monitoring + to detect the user's proximity to a location of interest. +

    diff --git a/docs/html/training/location/geofencing.jd b/docs/html/training/location/geofencing.jd new file mode 100644 index 00000000000..748b6ece8b8 --- /dev/null +++ b/docs/html/training/location/geofencing.jd @@ -0,0 +1,1407 @@ +page.title=Creating and Monitoring Geofences + +trainingnavtop=true +@jd:body + + +
    +
    + +

    This lesson teaches you to

    +
      +
    1. Request Geofence Monitoring
    2. +
    3. Handle Geofence Transitions
    4. +
    5. Stop Geofence Monitoring
    6. +
    + +

    You should also read

    + + +

    Try it out

    + +
    + Download the sample +

    GeofenceDetection.zip

    +
    + +
    +
    +

    + Geofencing combines awareness of the user's current location with awareness of nearby + features, defined as the user's proximity to locations that may be of interest. To mark a + location of interest, you specify its latitude and longitude. To adjust the proximity for the + location, you add a radius. The latitude, longitude, and radius define a geofence. + You can have multiple active geofences at one time. +

    +

    + Location Services treats a geofences as an area rather than as a points and proximity. This + allows it to detect when the user enters or exits a geofence. For each geofence, you can ask + Location Services to send you entrance events or exit events or both. You can also limit the + duration of a geofence by specifying an expiration duration in milliseconds. After the geofence + expires, Location Services automatically removes it. +

    + +

    Request Geofence Monitoring

    +

    + The first step in requesting geofence monitoring is to request the necessary permission. + To use geofencing, your app must request + {@link android.Manifest.permission#ACCESS_FINE_LOCATION ACCESS_FINE_LOCATION}. To request this + permission, add the following element as a child element of the +<manifest> + element: +

    +
    +<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
    +
    + +

    Check for Google Play Services

    +

    + Location Services is part of the Google Play services APK. Since it's hard to anticipate the + state of the user's device, you should always check that the APK is installed before you attempt + to connect to Location Services. To check that the APK is installed, call +GooglePlayServicesUtil.isGooglePlayServicesAvailable(), + which returns one of the + integer result codes listed in the API reference documentation. If you encounter an error, + call +GooglePlayServicesUtil.getErrorDialog() + to retrieve localized dialog that prompts users to take the correct action, then display + the dialog in a {@link android.support.v4.app.DialogFragment}. The dialog may allow the + user to correct the problem, in which case Google Play services may send a result back to your + activity. To handle this result, override the method + {@link android.support.v4.app.FragmentActivity#onActivityResult onActivityResult()} + +

    +

    + Note: To make your app compatible with + platform version 1.6 and later, the activity that displays the + {@link android.support.v4.app.DialogFragment} must subclass + {@link android.support.v4.app.FragmentActivity} instead of {@link android.app.Activity}. Using + {@link android.support.v4.app.FragmentActivity} also allows you to call + {@link android.support.v4.app.FragmentActivity#getSupportFragmentManager + getSupportFragmentManager()} to display the {@link android.support.v4.app.DialogFragment}. +

    +

    + Since you usually need to check for Google Play services in more than one place in your code, + define a method that encapsulates the check, then call the method before each connection + attempt. The following snippet contains all of the code required to check for Google + Play services: +

    +
    +public class MainActivity extends FragmentActivity {
    +    ...
    +    // Global constants
    +    /*
    +     * Define a request code to send to Google Play services
    +     * This code is returned in Activity.onActivityResult
    +     */
    +    private final static int
    +            CONNECTION_FAILURE_RESOLUTION_REQUEST = 9000;
    +    ...
    +    // Define a DialogFragment that displays the error dialog
    +    public static class ErrorDialogFragment extends DialogFragment {
    +        // Global field to contain the error dialog
    +        private Dialog mDialog;
    +        ...
    +        // Default constructor. Sets the dialog field to null
    +        public ErrorDialogFragment() {
    +            super();
    +            mDialog = null;
    +        }
    +        ...
    +        // Set the dialog to display
    +        public void setDialog(Dialog dialog) {
    +            mDialog = dialog;
    +        }
    +        ...
    +        // Return a Dialog to the DialogFragment.
    +        @Override
    +        public Dialog onCreateDialog(Bundle savedInstanceState) {
    +            return mDialog;
    +        }
    +        ...
    +    }
    +    ...
    +    /*
    +     * Handle results returned to the FragmentActivity
    +     * by Google Play services
    +     */
    +     @Override
    +    protected void onActivityResult(
    +            int requestCode, int resultCode, Intent data) {
    +        // Decide what to do based on the original request code
    +        switch (requestCode) {
    +            ...
    +            case CONNECTION_FAILURE_RESOLUTION_REQUEST :
    +            /*
    +             * If the result code is Activity.RESULT_OK, try
    +             * to connect again
    +             */
    +                switch (resultCode) {
    +                    ...
    +                    case Activity.RESULT_OK :
    +                    /*
    +                     * Try the request again
    +                     */
    +                    ...
    +                    break;
    +                }
    +            ...
    +        }
    +        ...
    +    }
    +    ...
    +    private boolean servicesConnected() {
    +        // Check that Google Play services is available
    +        int resultCode =
    +                GooglePlayServicesUtil.
    +                        isGooglePlayServicesAvailable(this);
    +        // If Google Play services is available
    +        if (ConnectionResult.SUCCESS == resultCode) {
    +            // In debug mode, log the status
    +            Log.d("Geofence Detection",
    +                    "Google Play services is available.");
    +            // Continue
    +            return true;
    +        // Google Play services was not available for some reason
    +        } else {
    +            // Get the error code
    +            int errorCode = connectionResult.getErrorCode();
    +            // Get the error dialog from Google Play services
    +            Dialog errorDialog = GooglePlayServicesUtil.getErrorDialog(
    +                    errorCode,
    +                    this,
    +                    CONNECTION_FAILURE_RESOLUTION_REQUEST);
    +
    +            // If Google Play services can provide an error dialog
    +            if (errorDialog != null) {
    +                // Create a new DialogFragment for the error dialog
    +                ErrorDialogFragment errorFragment =
    +                        new ErrorDialogFragment();
    +                // Set the dialog in the DialogFragment
    +                errorFragment.setDialog(errorDialog);
    +                // Show the error dialog in the DialogFragment
    +                errorFragment.show(
    +                        getSupportFragmentManager(),
    +                        "Geofence Detection");
    +            }
    +        }
    +    }
    +    ...
    +}
    +
    +

    + Snippets in the following sections call this method to verify that Google Play services is + available. +

    +

    + To use geofencing, start by defining the geofences you want to monitor. Although you usually + store geofence data in a local database or download it from the network, you need to send + a geofence to Location Services as an instance of +Geofence, + which you create with +Geofence.Builder. + Each +Geofence + object contains the following information: +

    +
    +
    Latitude, longitude, and radius
    +
    + Define a circular area for the geofence. Use the latitude and longitude to mark a location + of interest, and then use the radius to adjust how close the user needs to approach the + location before the geofence is detected. The larger the radius, the more likely the + user will trigger a geofence transition alert by approaching the geofence. For example, + providing a large radius for a geofencing app that turns on lights in the user's house as + the user returns home might cause the lights to go on even if the user is simply passing by. +
    +
    Expiration time
    +
    + How long the geofence should remain active. Once the expiration time is reached, Location + Services deletes the geofence. Most of the time, you should specify an expiration time, but + you may want to keep permanent geofences for the user's home or place of work. +
    +
    Transition type
    +
    + Location Services can detect when the user steps within the radius of the geofence ("entry") + and when the user steps outside the radius of the geofence ("exit"), or both. +
    +
    Geofence ID
    +
    + A string that is stored with the geofence. You should make this unique, so that you can + use it to remove a geofence from Location Services tracking. +
    +
    +

    Define geofence storage

    +

    + A geofencing app needs to read and write geofence data to persistent storage. You shouldn't use +Geofence + objects to do this; instead, use storage techniques such as databases that can store groups of + related data. +

    +

    + As an example of storing geofence data, the following snippet defines two classes that use + the app's {@link android.content.SharedPreferences} instance for persistent storage. The class + {@code SimpleGeofence}, analogous to a database record, stores the + data for a single +Geofence + object in a "flattened" form. The class {@code SimpleGeofenceStore}, analogous to a database, + reads and writes {@code SimpleGeofence} data to the + {@link android.content.SharedPreferences} instance. +

    +
    +public class MainActivity extends FragmentActivity {
    +    ...
    +    /**
    +     * A single Geofence object, defined by its center and radius.
    +     */
    +    public class SimpleGeofence {
    +            // Instance variables
    +            private final String mId;
    +            private final double mLatitude;
    +            private final double mLongitude;
    +            private final float mRadius;
    +            private long mExpirationDuration;
    +            private int mTransitionType;
    +
    +        /**
    +         * @param geofenceId The Geofence's request ID
    +         * @param latitude Latitude of the Geofence's center.
    +         * @param longitude Longitude of the Geofence's center.
    +         * @param radius Radius of the geofence circle.
    +         * @param expiration Geofence expiration duration
    +         * @param transition Type of Geofence transition.
    +         */
    +        public SimpleGeofence(
    +                String geofenceId,
    +                double latitude,
    +                double longitude,
    +                float radius,
    +                long expiration,
    +                int transition) {
    +            // Set the instance fields from the constructor
    +            this.mId = geofenceId;
    +            this.mLatitude = latitude;
    +            this.mLongitude = longitude;
    +            this.mRadius = radius;
    +            this.mExpirationDuration = expiration;
    +            this.mTransitionType = transition;
    +        }
    +        // Instance field getters
    +        public String getId() {
    +            return mId;
    +        }
    +        public double getLatitude() {
    +            return mLatitude;
    +        }
    +        public double getLongitude() {
    +            return mLongitude;
    +        }
    +        public float getRadius() {
    +            return mRadius;
    +        }
    +        public long getExpirationDuration() {
    +            return mExpirationDuration;
    +        }
    +        public int getTransitionType() {
    +            return mTransitionType;
    +        }
    +        /**
    +         * Creates a Location Services Geofence object from a
    +         * SimpleGeofence.
    +         *
    +         * @return A Geofence object
    +         */
    +        public Geofence toGeofence() {
    +            // Build a new Geofence object
    +            return new Geofence.Builder()
    +                    .setRequestId(getId())
    +                    .setTransitionTypes(mTransitionType)
    +                    .setCircularRegion(
    +                            getLatitude(), getLongitude(), getRadius())
    +                    .setExpirationDuration(mExpirationDuration)
    +                    .build();
    +        }
    +    }
    +    ...
    +    /**
    +     * Storage for geofence values, implemented in SharedPreferences.
    +     */
    +    public class SimpleGeofenceStore {
    +        // Keys for flattened geofences stored in SharedPreferences
    +        public static final String KEY_LATITUDE =
    +                "com.example.android.geofence.KEY_LATITUDE";
    +        public static final String KEY_LONGITUDE =
    +                "com.example.android.geofence.KEY_LONGITUDE";
    +        public static final String KEY_RADIUS =
    +                "com.example.android.geofence.KEY_RADIUS";
    +        public static final String KEY_EXPIRATION_DURATION =
    +                "com.example.android.geofence.KEY_EXPIRATION_DURATION";
    +        public static final String KEY_TRANSITION_TYPE =
    +                "com.example.android.geofence.KEY_TRANSITION_TYPE";
    +        // The prefix for flattened geofence keys
    +        public static final String KEY_PREFIX =
    +                "com.example.android.geofence.KEY";
    +        /*
    +         * Invalid values, used to test geofence storage when
    +         * retrieving geofences
    +         */
    +        public static final long INVALID_LONG_VALUE = -999l;
    +        public static final float INVALID_FLOAT_VALUE = -999.0f;
    +        public static final int INVALID_INT_VALUE = -999;
    +        // The SharedPreferences object in which geofences are stored
    +        private final SharedPreferences mPrefs;
    +        // The name of the SharedPreferences
    +        private static final String SHARED_PREFERENCES =
    +                "SharedPreferences";
    +        // Create the SharedPreferences storage with private access only
    +        public SimpleGeofenceStore(Context context) {
    +            mPrefs =
    +                    context.getSharedPreferences(
    +                            SHARED_PREFERENCES,
    +                            Context.MODE_PRIVATE);
    +        }
    +        /**
    +         * Returns a stored geofence by its id, or returns {@code null}
    +         * if it's not found.
    +         *
    +         * @param id The ID of a stored geofence
    +         * @return A geofence defined by its center and radius. See
    +         */
    +        public SimpleGeofence getGeofence(String id) {
    +            /*
    +             * Get the latitude for the geofence identified by id, or
    +             * INVALID_FLOAT_VALUE if it doesn't exist
    +             */
    +            double lat = mPrefs.getFloat(
    +                    getGeofenceFieldKey(id, KEY_LATITUDE),
    +                    INVALID_FLOAT_VALUE);
    +            /*
    +             * Get the longitude for the geofence identified by id, or
    +             * INVALID_FLOAT_VALUE if it doesn't exist
    +             */
    +            double lng = mPrefs.getFloat(
    +                    getGeofenceFieldKey(id, KEY_LONGITUDE),
    +                    INVALID_FLOAT_VALUE);
    +            /*
    +             * Get the radius for the geofence identified by id, or
    +             * INVALID_FLOAT_VALUE if it doesn't exist
    +             */
    +            float radius = mPrefs.getFloat(
    +                    getGeofenceFieldKey(id, KEY_RADIUS),
    +                    INVALID_FLOAT_VALUE);
    +            /*
    +             * Get the expiration duration for the geofence identified
    +             * by id, or INVALID_LONG_VALUE if it doesn't exist
    +             */
    +            long expirationDuration = mPrefs.getLong(
    +                    getGeofenceFieldKey(id, KEY_EXPIRATION_DURATION),
    +                    INVALID_LONG_VALUE);
    +            /*
    +             * Get the transition type for the geofence identified by
    +             * id, or INVALID_INT_VALUE if it doesn't exist
    +             */
    +            int transitionType = mPrefs.getInt(
    +                    getGeofenceFieldKey(id, KEY_TRANSITION_TYPE),
    +                    INVALID_INT_VALUE);
    +            // If none of the values is incorrect, return the object
    +            if (
    +                lat != GeofenceUtils.INVALID_FLOAT_VALUE &&
    +                lng != GeofenceUtils.INVALID_FLOAT_VALUE &&
    +                radius != GeofenceUtils.INVALID_FLOAT_VALUE &&
    +                expirationDuration !=
    +                        GeofenceUtils.INVALID_LONG_VALUE &&
    +                transitionType != GeofenceUtils.INVALID_INT_VALUE) {
    +
    +                // Return a true Geofence object
    +                return new SimpleGeofence(
    +                        id, lat, lng, radius, expirationDuration,
    +                        transitionType);
    +            // Otherwise, return null.
    +            } else {
    +                return null;
    +            }
    +        }
    +        /**
    +         * Save a geofence.
    +         * @param geofence The SimpleGeofence containing the
    +         * values you want to save in SharedPreferences
    +         */
    +        public void setGeofence(String id, SimpleGeofence geofence) {
    +            /*
    +             * Get a SharedPreferences editor instance. Among other
    +             * things, SharedPreferences ensures that updates are atomic
    +             * and non-concurrent
    +             */
    +            Editor editor = mPrefs.edit();
    +            // Write the Geofence values to SharedPreferences
    +            editor.putFloat(
    +                    getGeofenceFieldKey(id, KEY_LATITUDE),
    +                    (float) geofence.getLatitude());
    +            editor.putFloat(
    +                    getGeofenceFieldKey(id, KEY_LONGITUDE),
    +                    (float) geofence.getLongitude());
    +            editor.putFloat(
    +                    getGeofenceFieldKey(id, KEY_RADIUS),
    +                    geofence.getRadius());
    +            editor.putLong(
    +                    getGeofenceFieldKey(id, KEY_EXPIRATION_DURATION),
    +                    geofence.getExpirationDuration());
    +            editor.putInt(
    +                    getGeofenceFieldKey(id, KEY_TRANSITION_TYPE),
    +                    geofence.getTransitionType());
    +            // Commit the changes
    +            editor.commit();
    +        }
    +        public void clearGeofence(String id) {
    +            /*
    +             * Remove a flattened geofence object from storage by
    +             * removing all of its keys
    +             */
    +            Editor editor = mPrefs.edit();
    +            editor.remove(getGeofenceFieldKey(id, KEY_LATITUDE));
    +            editor.remove(getGeofenceFieldKey(id, KEY_LONGITUDE));
    +            editor.remove(getGeofenceFieldKey(id, KEY_RADIUS));
    +            editor.remove(getGeofenceFieldKey(id,
    +                    KEY_EXPIRATION_DURATION));
    +            editor.remove(getGeofenceFieldKey(id, KEY_TRANSITION_TYPE));
    +            editor.commit();
    +        }
    +        /**
    +         * Given a Geofence object's ID and the name of a field
    +         * (for example, KEY_LATITUDE), return the key name of the
    +         * object's values in SharedPreferences.
    +         *
    +         * @param id The ID of a Geofence object
    +         * @param fieldName The field represented by the key
    +         * @return The full key name of a value in SharedPreferences
    +         */
    +        private String getGeofenceFieldKey(String id,
    +                String fieldName) {
    +            return KEY_PREFIX + "_" + id + "_" + fieldName;
    +        }
    +    }
    +    ...
    +}
    +
    +

    Create Geofence objects

    +

    + The following snippet uses the {@code SimpleGeofence} and {@code SimpleGeofenceStore} classes + gets geofence data from the UI, stores it in {@code SimpleGeofence} objects, stores these + objects in a {@code SimpleGeofenceStore} object, and then creates +Geofence + objects: +

    +
    +public class MainActivity extends FragmentActivity {
    +    ...
    +    /*
    +     * Use to set an expiration time for a geofence. After this amount
    +     * of time Location Services will stop tracking the geofence.
    +     */
    +    private static final long SECONDS_PER_HOUR = 60;
    +    private static final long MILLISECONDS_PER_SECOND = 1000;
    +    private static final long GEOFENCE_EXPIRATION_IN_HOURS = 12;
    +    private static final long GEOFENCE_EXPIRATION_TIME =
    +            GEOFENCE_EXPIRATION_IN_HOURS *
    +            SECONDS_PER_HOUR *
    +            MILLISECONDS_PER_SECOND;
    +    ...
    +    /*
    +     * Handles to UI views containing geofence data
    +     */
    +    // Handle to geofence 1 latitude in the UI
    +    private EditText mLatitude1;
    +    // Handle to geofence 1 longitude in the UI
    +    private EditText mLongitude1;
    +    // Handle to geofence 1 radius in the UI
    +    private EditText mRadius1;
    +    // Handle to geofence 2 latitude in the UI
    +    private EditText mLatitude2;
    +    // Handle to geofence 2 longitude in the UI
    +    private EditText mLongitude2;
    +    // Handle to geofence 2 radius in the UI
    +    private EditText mRadius2;
    +    /*
    +     * Internal geofence objects for geofence 1 and 2
    +     */
    +    private SimpleGeofence mUIGeofence1;
    +    private SimpleGeofence mUIGeofence2;
    +    ...
    +    // Internal List of Geofence objects
    +    List<Geofence> mGeofenceList;
    +    // Persistent storage for geofences
    +    private SimpleGeofenceStore mGeofenceStorage;
    +    ...
    +    @Override
    +    protected void onCreate(Bundle savedInstanceState) {
    +        super.onCreate(savedInstanceState);
    +        ...
    +        // Instantiate a new geofence storage area
    +        mGeofenceStorage = new SimpleGeofenceStore(this);
    +
    +        // Instantiate the current List of geofences
    +        mCurrentGeofences = new ArrayList<Geofence>();
    +    }
    +    ...
    +    /**
    +     * Get the geofence parameters for each geofence from the UI
    +     * and add them to a List.
    +     */
    +    public void createGeofences() {
    +        /*
    +         * Create an internal object to store the data. Set its
    +         * ID to "1". This is a "flattened" object that contains
    +         * a set of strings
    +         */
    +        mUIGeofence1 = new SimpleGeofence(
    +                "1",
    +                Double.valueOf(mLatitude1.getText().toString()),
    +                Double.valueOf(mLongitude1.getText().toString()),
    +                Float.valueOf(mRadius1.getText().toString()),
    +                GEOFENCE_EXPIRATION_TIME,
    +                // This geofence records only entry transitions
    +                Geofence.GEOFENCE_TRANSITION_ENTER);
    +        // Store this flat version
    +        mGeofenceStorage.setGeofence("1", mUIGeofence1);
    +        // Create another internal object. Set its ID to "2"
    +        mUIGeofence2 = new SimpleGeofence(
    +                "2",
    +                Double.valueOf(mLatitude2.getText().toString()),
    +                Double.valueOf(mLongitude2.getText().toString()),
    +                Float.valueOf(mRadius2.getText().toString()),
    +                GEOFENCE_EXPIRATION_TIME,
    +                // This geofence records both entry and exit transitions
    +                Geofence.GEOFENCE_TRANSITION_ENTER |
    +                Geofence.GEOFENCE_TRANSITION_EXIT);
    +        // Store this flat version
    +        mGeofenceStorage.setGeofence(2, mUIGeofence2);
    +        mGeofenceList.add(mUIGeofence1.toGeofence());
    +        mGeofenceList.add(mUIGeofence2.toGeofence());
    +    }
    +    ...
    +}
    +
    +

    + In addition to the {@link java.util.List} of +Geofence + objects you want to monitor, you need to provide Location Services with the + {@link android.content.Intent} that it sends to your app when it detects geofence + transitions. +

    Define a Intent for geofence transitions

    +

    + The {@link android.content.Intent} sent from Location Services can trigger various actions in + your app, but you should not have it start an activity or fragment, because components + should only become visible in response to a user action. In many cases, an + {@link android.app.IntentService} is a good way to handle the intent. An + {@link android.app.IntentService} can post a notification, do long-running background work, + send intents to other services, or send a broadcast intent. The following snippet shows how + how to define a {@link android.app.PendingIntent} that starts an + {@link android.app.IntentService}: +

    +
    +public class MainActivity extends FragmentActivity {
    +    ...
    +    /*
    +     * Create a PendingIntent that triggers an IntentService in your
    +     * app when a geofence transition occurs.
    +     */
    +    private PendingIntent getTransitionPendingIntent() {
    +        // Create an explicit Intent
    +        Intent intent = new Intent(this,
    +                ReceiveTransitionsIntentService.class);
    +        /*
    +         * Return the PendingIntent
    +         */
    +        return PendingIntent.getService(
    +                this,
    +                0,
    +                intent,
    +                PendingIntent.FLAG_UPDATE_CURRENT);
    +    }
    +    ...
    +}
    +
    +

    + Now you have all the code you need to send a request to monitor geofences to Location + Services. +

    + +

    Send the monitoring request

    +

    + Sending the monitoring request requires two asynchronous operations. The first operation gets a + location client for the request, and the second makes the request using the client. In both + cases, Location Services invokes a callback method when it finishes the operation. The best way + to handle these operations is to chain together the method calls. The following snippets + demonstrate how to set up an activity, define the methods, and call them in the proper order. +

    +

    + First, modify the activity's class definition to implement the necessary callback interfaces. + Add the following interfaces: +

    +
    +
    +ConnectionCallbacks +
    +
    + Specifies methods that Location Services calls when a location client is connected or + disconnected. +
    +
    +OnConnectionFailedListener +
    +
    + Specifies a method that Location Services calls if an error occurs while attempting to + connect the location client. +
    +
    +OnAddGeofencesResultListener +
    +
    + Specifies a method that Location Services calls once it has added the geofences. +
    +
    +

    + For example: +

    +
    +public class MainActivity extends FragmentActivity implements
    +        ConnectionCallbacks,
    +        OnConnectionFailedListener,
    +        OnAddGeofencesResultListener {
    +    ...
    +}
    +
    +

    Start the request process

    +

    + Next, define a method that starts the request process by connecting to Location Services. + Mark this as a request to add a geofence by setting a global variable. This allows you to + use the callback +ConnectionCallbacks.onConnected() + to add geofences and to remove them, as described in succeeding sections. +

    +

    +

    + To guard against race conditions that might arise if your app tries to start another request + before the first one finishes, define a boolean flag that tracks the state of the current + request: +

    +
    +public class MainActivity extends FragmentActivity implements
    +        ConnectionCallbacks,
    +        OnConnectionFailedListener,
    +        OnAddGeofencesResultListener {
    +    ...
    +    // Holds the location client
    +    private LocationClient mLocationClient;
    +    // Stores the PendingIntent used to request geofence monitoring
    +    private PendingIntent mGeofenceRequestIntent;
    +    // Defines the allowable request types.
    +    public enum REQUEST_TYPE = {ADD}
    +    private REQUEST_TYPE mRequestType;
    +    // Flag that indicates if a request is underway.
    +    private boolean mInProgress;
    +    ...
    +    @Override
    +    protected void onCreate(Bundle savedInstanceState) {
    +        ...
    +        // Start with the request flag set to false
    +        mInProgress = false;
    +        ...
    +    }
    +    ...
    +    /**
    +     * Start a request for geofence monitoring by calling
    +     * LocationClient.connect().
    +     */
    +    public void addGeofences() {
    +        // Start a request to add geofences
    +        mRequestType = ADD;
    +        /*
    +         * Test for Google Play services after setting the request type.
    +         * If Google Play services isn't present, the proper request
    +         * can be restarted.
    +         */
    +        if (!servicesConnected()) {
    +            return;
    +        }
    +        /*
    +         * Create a new location client object. Since the current
    +         * activity class implements ConnectionCallbacks and
    +         * OnConnectionFailedListener, pass the current activity object
    +         * as the listener for both parameters
    +         */
    +        mLocationClient = new LocationClient(this, this, this)
    +        // If a request is not already underway
    +        if (!mInProgress) {
    +            // Indicate that a request is underway
    +            mInProgress = true;
    +            // Request a connection from the client to Location Services
    +            mLocationClient.connect();
    +        } else {
    +            /*
    +             * A request is already underway. You can handle
    +             * this situation by disconnecting the client,
    +             * re-setting the flag, and then re-trying the
    +             * request.
    +             */
    +        }
    +    }
    +    ...
    +}
    +
    +

    Send a request to add the geofences

    +

    + In your implementation of +ConnectionCallbacks.onConnected(), + call +LocationClient.addGeofences(). + Notice that if the connection fails, +onConnected() + isn't called, and the request stops. +

    +
    +public class MainActivity extends FragmentActivity implements
    +        ConnectionCallbacks,
    +        OnConnectionFailedListener,
    +        OnAddGeofencesResultListener {
    +    ...
    +    /*
    +     * Provide the implementation of ConnectionCallbacks.onConnected()
    +     * Once the connection is available, send a request to add the
    +     * Geofences
    +     */
    +    @Override
    +    private void onConnected(Bundle dataBundle) {
    +        ...
    +        switch (mRequestType) {
    +            case ADD :
    +                // Get the PendingIntent for the request
    +                mTransitionPendingIntent =
    +                        getTransitionPendingIntent();
    +                // Send a request to add the current geofences
    +                mLocationClient.addGeofences(
    +                        mCurrentGeofences, pendingIntent, this);
    +            ...
    +        }
    +    }
    +    ...
    +}
    +
    +

    + Notice that +addGeofences() + returns immediately, but the status of the request is indeterminate until Location Services + calls +onAddGeofencesResult() + Once this method is called, you can determine if the request was successful or not. +

    +

    Check the result returned by Location Services

    +

    + When Location Services invokes your implementation of the callback method +onAddGeofencesResult(), + indicating that the request is complete, examine the incoming status code. If the request + was successful, the geofences you requested are active. If the request was unsuccessful, + the geofences aren't active, and you need to re-try the request or report an error. For example: +

    +
    +public class MainActivity extends FragmentActivity implements
    +        ConnectionCallbacks,
    +        OnConnectionFailedListener,
    +        OnAddGeofencesResultListener {
    +        ...
    +    /*
    +     * Provide the implementation of
    +     * OnAddGeofencesResultListener.onAddGeofencesResult.
    +     * Handle the result of adding the geofences
    +     *
    +     */
    +    @Override
    +    public void onAddGeofencesResult(
    +            int statusCode, String[] geofenceRequestIds) {
    +        // If adding the geofences was successful
    +        if (LocationStatusCodes.SUCCESS == statusCode) {
    +            /*
    +             * Handle successful addition of geofences here.
    +             * You can send out a broadcast intent or update the UI.
    +             * geofences into the Intent's extended data.
    +             */
    +        } else {
    +        // If adding the geofences failed
    +            /*
    +             * Report errors here.
    +             * You can log the error using Log.e() or update
    +             * the UI.
    +             */
    +        }
    +        // Turn off the in progress flag and disconnect the client
    +        mInProgress = false;
    +        mLocationClient.disconnect();
    +    }
    +    ...
    +}
    +
    + +

    Handle disconnections

    +

    + In some cases, Location Services may disconnect from the activity recognition client before + you call +disconnect(). + To handle this situation, implement +onDisconnected(). + In this method, set the request flag to indicate that a request is not in progress, and + delete the client: +

    +
    +public class MainActivity extends FragmentActivity implements
    +        ConnectionCallbacks,
    +        OnConnectionFailedListener,
    +        OnAddGeofencesResultListener {
    +    ...
    +    /*
    +     * Implement ConnectionCallbacks.onDisconnected()
    +     * Called by Location Services once the location client is
    +     * disconnected.
    +     */
    +    @Override
    +    public void onDisconnected() {
    +        // Turn off the request flag
    +        mInProgress = false;
    +        // Destroy the current location client
    +        mLocationClient = null;
    +    }
    +    ...
    +}
    +
    + +

    Handle connection errors

    +

    + Besides handling the normal callbacks from Location Services, you have to provide a callback + method that Location Services calls if a connection error occurs. This callback method + can re-use the {@link android.support.v4.app.DialogFragment} class that you defined to + handle the check for Google Play services. It can also re-use the override you defined + for {@link android.support.v4.app.FragmentActivity#onActivityResult onActivityResult()} that + receives any Google Play services results that occur when the user interacts with the + error dialog. The following snippet shows you a sample implementation of the callback method: +

    +
    +public class MainActivity extends FragmentActivity implements
    +        ConnectionCallbacks,
    +        OnConnectionFailedListener,
    +        OnAddGeofencesResultListener {
    +    ...
    +    // Implementation of OnConnectionFailedListener.onConnectionFailed
    +    @Override
    +    public void onConnectionFailed(ConnectionResult connectionResult) {
    +        // Turn off the request flag
    +        mInProgress = false;
    +        /*
    +         * If the error has a resolution, start a Google Play services
    +         * activity to resolve it.
    +         */
    +        if (connectionResult.hasResolution()) {
    +            try {
    +                connectionResult.startResolutionForResult(
    +                        this,
    +                        CONNECTION_FAILURE_RESOLUTION_REQUEST);
    +            } catch (SendIntentException e) {
    +                // Log the error
    +                e.printStackTrace();
    +            }
    +        // If no resolution is available, display an error dialog
    +        } else {
    +            // Get the error code
    +            int errorCode = connectionResult.getErrorCode();
    +            // Get the error dialog from Google Play services
    +            Dialog errorDialog = GooglePlayServicesUtil.getErrorDialog(
    +                    errorCode,
    +                    this,
    +                    CONNECTION_FAILURE_RESOLUTION_REQUEST);
    +            // If Google Play services can provide an error dialog
    +            if (errorDialog != null) {
    +                // Create a new DialogFragment for the error dialog
    +                ErrorDialogFragment errorFragment =
    +                        new ErrorDialogFragment();
    +                // Set the dialog in the DialogFragment
    +                errorFragment.setDialog(errorDialog);
    +                // Show the error dialog in the DialogFragment
    +                errorFragment.show(
    +                        getSupportFragmentManager(),
    +                        "Geofence Detection");
    +            }
    +        }
    +    }
    +    ...
    +}
    +
    + +

    Handle Geofence Transitions

    +

    + When Location Services detects that the user has entered or exited a geofence, it + sends out the {@link android.content.Intent} contained in the {@link android.app.PendingIntent} + you included in the request to add geofences. This {@link android.content.Intent} is +

    +

    Define an IntentService

    +

    + The following snippet shows how to define an {@link android.app.IntentService} that posts a + notification when a geofence transition occurs. When the user clicks the notification, the + app's main activity appears: +

    +
    +public class ReceiveTransitionsIntentService extends IntentService {
    +    ...
    +    /**
    +     * Sets an identifier for the service
    +     */
    +    public ReceiveTransitionsIntentService() {
    +        super("ReceiveTransitionsIntentService");
    +    }
    +    /**
    +     * Handles incoming intents
    +     *@param intent The Intent sent by Location Services. This
    +     * Intent is provided
    +     * to Location Services (inside a PendingIntent) when you call
    +     * addGeofences()
    +     */
    +    @Override
    +    protected void onHandleIntent(Intent intent) {
    +        // First check for errors
    +        if (LocationClient.hasError(intent)) {
    +            // Get the error code with a static method
    +            int errorCode = LocationClient.getErrorCode(intent);
    +            // Log the error
    +            Log.e("ReceiveTransitionsIntentService",
    +                    "Location Services error: " +
    +                    Integer.toString(errorCode));
    +            /*
    +             * You can also send the error code to an Activity or
    +             * Fragment with a broadcast Intent
    +             */
    +        /*
    +         * If there's no error, get the transition type and the IDs
    +         * of the geofence or geofences that triggered the transition
    +         */
    +        } else {
    +            // Get the type of transition (entry or exit)
    +            int transitionType =
    +                    LocationClient.getGeofenceTransition(intent);
    +            // Test that a valid transition was reported
    +            if (
    +                (transitionType == Geofence.GEOFENCE_TRANSITION_ENTER)
    +                 ||
    +                (transitionType == Geofence.GEOFENCE_TRANSITION_EXIT)
    +               ) {
    +                List <Geofence> triggerList =
    +                        getTriggeringGeofences(intent);
    +
    +                String[] triggerIds = new String[geofenceList.size()];
    +
    +                for (int i = 0; i < triggerIds.length; i++) {
    +                    // Store the Id of each geofence
    +                    triggerIds[i] = triggerList.get(i).getRequestId();
    +                }
    +                /*
    +                 * At this point, you can store the IDs for further use
    +                 * display them, or display the details associated with
    +                 * them.
    +                 */
    +            }
    +        // An invalid transition was reported
    +        } else {
    +            Log.e("ReceiveTransitionsIntentService",
    +                    "Geofence transition error: " +
    +                    Integer.toString()transitionType));
    +        }
    +    }
    +    ...
    +}
    +
    + +

    Specify the IntentService in the manifest

    +

    + To identify the {@link android.app.IntentService} to the system, add a + <service> + element to the app manifest. For example: +

    +
    +<service
    +    android:name="com.example.android.location.ReceiveTransitionsIntentService"
    +    android:label="@string/app_name"
    +    android:exported="false">
    +</service>
    +
    +

    + Notice that you don't have to specify intent filters for the service, because it only receives + explicit intents. How the incoming geofence transition intents are created is described in the + section Send the monitoring request. +

    + +

    Stop Geofence Monitoring

    +

    + To stop geofence monitoring, you remove the geofences themselves. You can remove a specific + set of geofences or all the geofences associated with a {@link android.app.PendingIntent}. The + procedure is similar to adding geofences. The first operation gets a location + client for the removal request, and the second makes the request using the client. +

    +

    + The callback methods that Location Services invokes when it has finished removing geofences + are defined in the interface +LocationClient.OnRemoveGeofencesResultListener. Declare + this interface as part of your class definition, and then add definitions for its two methods: +

    +
    +
    +onRemoveGeofencesByPendingIntentResult() +
    +
    + Callback invoked when Location Services finishes a request to remove all geofences made + by the method +removeGeofences(PendingIntent, LocationClient.OnRemoveGeofencesResultListener). +
    +
    +onRemoveGeofencesByRequestIdsResult(List<String>, LocationClient.OnRemoveGeofencesResultListener) +
    +
    + Callback invoked when Location Services finished a request to remove a set of geofences, + specified by their geofence IDs, by the method +removeGeofences(List<String>, LocationClient.OnRemoveGeofencesResultListener). +
    +
    +

    + Examples of implementing these methods are shown in the next snippets. +

    +

    Remove all geofences

    +

    + Since removing geofences uses some of the methods you use to add geofences, start by defining + another request type: +

    +
    +public class MainActivity extends FragmentActivity implements
    +        ConnectionCallbacks,
    +        OnConnectionFailedListener,
    +        OnAddGeofencesResultListener {
    +    ...
    +    // Enum type for controlling the type of removal requested
    +    public enum REQUEST_TYPE = {ADD, REMOVE_INTENT}
    +    ...
    +}
    +
    +

    + Start the removal request by getting a connection to Location Services. If the connection fails, +onConnected() isn't called, + and the request stops. The following snippet shows how to start the request: +

    +
    +public class MainActivity extends FragmentActivity implements
    +        ConnectionCallbacks,
    +        OnConnectionFailedListener,
    +        OnAddGeofencesResultListener {
    +    ...
    +    /**
    +     * Start a request to remove geofences by calling
    +     * LocationClient.connect()
    +     */
    +    public void removeGeofences(PendingIntent requestIntent) {
    +        // Record the type of removal request
    +        mRequestType = REMOVE_INTENT;
    +        /*
    +         * Test for Google Play services after setting the request type.
    +         * If Google Play services isn't present, the request can be
    +         * restarted.
    +         */
    +        if (!servicesConnected()) {
    +            return;
    +        }
    +        // Store the PendingIntent
    +        mGeofenceRequestIntent = requestIntent;
    +        /*
    +         * Create a new location client object. Since the current
    +         * activity class implements ConnectionCallbacks and
    +         * OnConnectionFailedListener, pass the current activity object
    +         * as the listener for both parameters
    +         */
    +        mLocationClient = new LocationClient(this, this, this);
    +        // If a request is not already underway
    +        if (!mInProgress) {
    +            // Indicate that a request is underway
    +            mInProgress = true;
    +            // Request a connection from the client to Location Services
    +            mLocationClient.connect();
    +        } else {
    +            /*
    +             * A request is already underway. You can handle
    +             * this situation by disconnecting the client,
    +             * re-setting the flag, and then re-trying the
    +             * request.
    +             */
    +        }
    +    }
    +    ...
    +}
    +
    +

    + When Location Services invokes the callback method indicating that the connection is open, + make the request to remove all geofences. Disconnect the client after making the request. + For example: +

    +
    +public class MainActivity extends FragmentActivity implements
    +        ConnectionCallbacks,
    +        OnConnectionFailedListener,
    +        OnAddGeofencesResultListener {
    +    ...
    +    /**
    +     * Once the connection is available, send a request to remove the
    +     * Geofences. The method signature used depends on which type of
    +     * remove request was originally received.
    +     */
    +    private void onConnected(Bundle dataBundle) {
    +        /*
    +         * Choose what to do based on the request type set in
    +         * removeGeofences
    +         */
    +        switch (mRequestType) {
    +            ...
    +            case REMOVE_INTENT :
    +                mLocationClient.removeGeofences(
    +                        mGeofenceRequestIntent, this);
    +                break;
    +            ...
    +        }
    +    }
    +    ...
    +}
    +
    +

    + Although the call to +removeGeofences(PendingIntent, LocationClient.OnRemoveGeofencesResultListener) Services calls + returns immediately, the result of the removal request is indeterminate until Location Services + calls +onRemoveGeofencesByPendingIntentResult(). + The following snippet shows how to define this method: +

    +
    +public class MainActivity extends FragmentActivity implements
    +        ConnectionCallbacks,
    +        OnConnectionFailedListener,
    +        OnAddGeofencesResultListener {
    +    ...
    +    /**
    +     * When the request to remove geofences by PendingIntent returns,
    +     * handle the result.
    +     *
    +     *@param statusCode the code returned by Location Services
    +     *@param requestIntent The Intent used to request the removal.
    +     */
    +    @Override
    +    public void onRemoveGeofencesByPendingIntentResult(int statusCode,
    +            PendingIntent requestIntent) {
    +        // If removing the geofences was successful
    +        if (statusCode == LocationStatusCodes.SUCCESS) {
    +            /*
    +             * Handle successful removal of geofences here.
    +             * You can send out a broadcast intent or update the UI.
    +             * geofences into the Intent's extended data.
    +             */
    +        } else {
    +        // If adding the geocodes failed
    +            /*
    +             * Report errors here.
    +             * You can log the error using Log.e() or update
    +             * the UI.
    +             */
    +        }
    +        /*
    +         * Disconnect the location client regardless of the
    +         * request status, and indicate that a request is no
    +         * longer in progress
    +         */
    +        mInProgress = false;
    +        mLocationClient.disconnect();
    +    }
    +    ...
    +}
    +
    +

    Remove individual geofences

    +

    + The procedure for removing an individual geofence or set of geofences is similar to the + removal of all geofences. To specify the geofences you want remove, add their geofence ID + values to a {@link java.util.List} of String objects. Pass this {@link java.util.List} to a + different definition of {@code removeGeofences} with the appropriate signature. This method + then starts the removal process. +

    +

    + Start by adding a request type for removing geofences by a list, and also add a global variable + for storing the list of geofences: +

    +
    +    ...
    +    // Enum type for controlling the type of removal requested
    +    public enum REQUEST_TYPE = {ADD, REMOVE_INTENT, REMOVE_LIST}
    +    // Store the list of geofence Ids to remove
    +    String<List> mGeofencesToRemove;
    +
    +

    + Next, define a list of geofences you want to remove. For example, this snippet removes the +Geofence + defined by the geofence ID "1": +

    +
    +public class MainActivity extends FragmentActivity implements
    +        ConnectionCallbacks,
    +        OnConnectionFailedListener,
    +        OnAddGeofencesResultListener {
    +    ...
    +        List<String> listOfGeofences =
    +                Collections.singletonList("1");
    +        removeGeofences(listOfGeofences);
    +    ...
    +}
    +
    +

    + The following snippet defines the {@code removeGeofences()} method: +

    +
    +public class MainActivity extends FragmentActivity implements
    +        ConnectionCallbacks,
    +        OnConnectionFailedListener,
    +        OnAddGeofencesResultListener {
    +    ...
    +    /**
    +     * Start a request to remove monitoring by
    +     * calling LocationClient.connect()
    +     *
    +     */
    +    public void removeGeofences(List<String> geofenceIds) {
    +        // If Google Play services is unavailable, exit
    +        // Record the type of removal request
    +        mRequestType = REMOVE_LIST;
    +        /*
    +         * Test for Google Play services after setting the request type.
    +         * If Google Play services isn't present, the request can be
    +         * restarted.
    +         */
    +        if (!servicesConnected()) {
    +            return;
    +        }
    +        // Store the list of geofences to remove
    +        mGeofencesToRemove = geofenceIds;
    +        /*
    +         * Create a new location client object. Since the current
    +         * activity class implements ConnectionCallbacks and
    +         * OnConnectionFailedListener, pass the current activity object
    +         * as the listener for both parameters
    +         */
    +        mLocationClient = new LocationClient(this, this, this);
    +        // If a request is not already underway
    +        if (!mInProgress) {
    +            // Indicate that a request is underway
    +            mInProgress = true;
    +            // Request a connection from the client to Location Services
    +            mLocationClient.connect();
    +        } else {
    +            /*
    +             * A request is already underway. You can handle
    +             * this situation by disconnecting the client,
    +             * re-setting the flag, and then re-trying the
    +             * request.
    +             */
    +        }
    +    }
    +    ...
    +}
    +
    +

    + When Location Services invokes the callback method indicating that the connection is open, + make the request to remove the list of geofences. Disconnect the client after making the request. + For example: +

    +
    +public class MainActivity extends FragmentActivity implements
    +        ConnectionCallbacks,
    +        OnConnectionFailedListener,
    +        OnAddGeofencesResultListener {
    +    ...
    +    private void onConnected(Bundle dataBundle) {
    +        ...
    +        switch (mRequestType) {
    +        ...
    +        // If removeGeofencesById was called
    +            case REMOVE_LIST :
    +                mLocationClient.removeGeofences(
    +                        mGeofencesToRemove, this);
    +                break;
    +        ...
    +        }
    +        ...
    +    }
    +    ...
    +}
    +
    +

    + Define an implementation of +onRemoveGeofencesByRequestIdsResult(). + Location Services invokes this callback method to indicate that the request to remove a list of + geofences is complete. In this method, examine the incoming status code and take the + appropriate action: +

    +
    +public class MainActivity extends FragmentActivity implements
    +        ConnectionCallbacks,
    +        OnConnectionFailedListener,
    +        OnAddGeofencesResultListener {
    +    ...
    +    /**
    +     * When the request to remove geofences by IDs returns, handle the
    +     * result.
    +     *
    +     * @param statusCode The code returned by Location Services
    +     * @param geofenceRequestIds The IDs removed
    +     */
    +    @Override
    +    public void onRemoveGeofencesByRequestIdsResult(
    +            int statusCode, String[] geofenceRequestIds) {
    +        // If removing the geocodes was successful
    +        if (LocationStatusCodes.SUCCESS == statusCode) {
    +            /*
    +             * Handle successful removal of geofences here.
    +             * You can send out a broadcast intent or update the UI.
    +             * geofences into the Intent's extended data.
    +             */
    +        } else {
    +        // If removing the geofences failed
    +            /*
    +             * Report errors here.
    +             * You can log the error using Log.e() or update
    +             * the UI.
    +             */
    +        }
    +        // Indicate that a request is no longer in progress
    +        mInProgress = false;
    +        // Disconnect the location client
    +        mLocationClient.disconnect();
    +    }
    +    ...
    +}
    +
    +

    + You can combine geofencing with other location-aware features, such as periodic location updates + or activity recognition, which are described in other lessons in this class. +

    +

    + The next lesson, + Recognizing the User's Current Activity, shows you how + to request and receive activity updates. At regular intervals, Location Services can send you + information about the user's current physical activity. Based on this information, you can + change your app's behavior; for example, you can switch to a longer update interval if you + detect that the user is walking instead of driving. +

    diff --git a/docs/html/training/location/index.jd b/docs/html/training/location/index.jd new file mode 100644 index 00000000000..5ebbb84f69e --- /dev/null +++ b/docs/html/training/location/index.jd @@ -0,0 +1,88 @@ +page.title=Making Your App Location-Aware +page.tags="location","geofence", "geofencing", "activity recognition", "activity detection", "gps" + +trainingnavtop=true +startpage=true + + +@jd:body + +
    +
    + + +

    Dependencies and prerequisites

    +
      +
    • Google Play services client library (latest version)
    • +
    • Android version 2.2 (API level 8) or later
    • +
    + + +

    You should also read

    + + +
    +
    + +

    + One of the unique features of mobile applications is location awareness. Mobile users bring + their devices with them everywhere, and adding location awareness to your app offers users a + more contextual experience. The new Location Services API available in Google Play services + facilitates adding location awareness to your app with automated location tracking, + geofencing, and activity recognition. This API adds significant advantages over the plaform's + location API. +

    +

    + This class shows you how to use Location Services in your app to get the current location, + get periodic location updates, look up addresses, create and monitor geofences, and + detect user activities. The class includes sample apps and code snippets that you can use as a + starting point for adding location awareness to your own app. +

    +

    + Note: Since this class is based on the Google Play services client library, + make sure you install the latest version before using the sample apps or code snippets. To learn + how to set up the client library with the latest version, see + Setup in the Google Play services guide. +

    +

    Lessons

    +
    +
    + Retrieving the Current Location +
    +
    + Learn how to retrieve the user's current location. +
    +
    + Receiving Location Updates +
    +
    + Learn how to request and receive periodic location updates. +
    +
    + Displaying a Location Address +
    +
    + Learn how to convert a location's latitude and longitude into an address (reverse + geocoding). +
    +
    + + Creating and Monitoring Geofences + +
    +
    + Learn how to define one or more geographic areas as locations of interest, called geofences, + and detect when the user is close to or inside a geofence. +
    +
    + Recognizing the User's Current Activity +
    +
    + Learn how to recognize the user's current activity, such as walking, bicycling, + or driving a car, and how to use this information to modify your app's location strategy. +
    +
    diff --git a/docs/html/training/location/receive-location-updates.jd b/docs/html/training/location/receive-location-updates.jd new file mode 100644 index 00000000000..eb4ffa3aeec --- /dev/null +++ b/docs/html/training/location/receive-location-updates.jd @@ -0,0 +1,590 @@ +page.title=Receiving Location Updates +trainingnavtop=true +@jd:body + + +

    + If your app does navigation or tracking, you probably want to get the user's + location at regular intervals. While you can do this with +LocationClient.getLastLocation(), + a more direct approach is to request periodic updates from Location Services. In + response, Location Services automatically updates your app with the best available location, + based on the currently-available location providers such as WiFi and GPS. +

    +

    + To get periodic location updates from Location Services, you send a request using a location + client. Depending on the form of the request, Location Services either invokes a callback + method and passes in a {@link android.location.Location} object, or issues an + {@link android.content.Intent} that contains the location in its extended data. The accuracy and + frequency of the updates are affected by the location permissions you've requested and the + parameters you pass to Location Services with the request. +

    + +

    Specify App Permissions

    +

    + Apps that use Location Services must request location permissions. Android has two location + permissions, {@link android.Manifest.permission#ACCESS_COARSE_LOCATION ACCESS_COARSE_LOCATION} + and {@link android.Manifest.permission#ACCESS_FINE_LOCATION ACCESS_FINE_LOCATION}. The + permission you choose affects the accuracy of the location updates you receive. + For example, If you request only coarse location permission, Location Services obfuscates the + updated location to an accuracy that's roughly equivalent to a city block. +

    +

    + Requesting {@link android.Manifest.permission#ACCESS_FINE_LOCATION ACCESS_FINE_LOCATION} implies + a request for {@link android.Manifest.permission#ACCESS_COARSE_LOCATION ACCESS_COARSE_LOCATION}. +

    +

    + For example, to add the coarse location permission to your manifest, insert the following as a + child element of + the +<manifest> + element: +

    +
    +<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
    +
    + +

    Check for Google Play Services

    +

    + Location Services is part of the Google Play services APK. Since it's hard to anticipate the + state of the user's device, you should always check that the APK is installed before you attempt + to connect to Location Services. To check that the APK is installed, call +GooglePlayServicesUtil.isGooglePlayServicesAvailable(), + which returns one of the + integer result codes listed in the API reference documentation. If you encounter an error, + call +GooglePlayServicesUtil.getErrorDialog() + to retrieve localized dialog that prompts users to take the correct action, then display + the dialog in a {@link android.support.v4.app.DialogFragment}. The dialog may allow the + user to correct the problem, in which case Google Play services may send a result back to your + activity. To handle this result, override the method + {@link android.support.v4.app.FragmentActivity#onActivityResult onActivityResult()} + +

    +

    + Note: To make your app compatible with + platform version 1.6 and later, the activity that displays the + {@link android.support.v4.app.DialogFragment} must subclass + {@link android.support.v4.app.FragmentActivity} instead of {@link android.app.Activity}. Using + {@link android.support.v4.app.FragmentActivity} also allows you to call + {@link android.support.v4.app.FragmentActivity#getSupportFragmentManager + getSupportFragmentManager()} to display the {@link android.support.v4.app.DialogFragment}. +

    +

    + Since you usually need to check for Google Play services in more than one place in your code, + define a method that encapsulates the check, then call the method before each connection + attempt. The following snippet contains all of the code required to check for Google + Play services: +

    +
    +public class MainActivity extends FragmentActivity {
    +    ...
    +    // Global constants
    +    /*
    +     * Define a request code to send to Google Play services
    +     * This code is returned in Activity.onActivityResult
    +     */
    +    private final static int
    +            CONNECTION_FAILURE_RESOLUTION_REQUEST = 9000;
    +    ...
    +    // Define a DialogFragment that displays the error dialog
    +    public static class ErrorDialogFragment extends DialogFragment {
    +        // Global field to contain the error dialog
    +        private Dialog mDialog;
    +        // Default constructor. Sets the dialog field to null
    +        public ErrorDialogFragment() {
    +            super();
    +            mDialog = null;
    +        }
    +        // Set the dialog to display
    +        public void setDialog(Dialog dialog) {
    +            mDialog = dialog;
    +        }
    +        // Return a Dialog to the DialogFragment.
    +        @Override
    +        public Dialog onCreateDialog(Bundle savedInstanceState) {
    +            return mDialog;
    +        }
    +    }
    +    ...
    +    /*
    +     * Handle results returned to the FragmentActivity
    +     * by Google Play services
    +     */
    +    @Override
    +    protected void onActivityResult(
    +            int requestCode, int resultCode, Intent data) {
    +        // Decide what to do based on the original request code
    +        switch (requestCode) {
    +            ...
    +            case CONNECTION_FAILURE_RESOLUTION_REQUEST :
    +            /*
    +             * If the result code is Activity.RESULT_OK, try
    +             * to connect again
    +             */
    +                switch (resultCode) {
    +                    case Activity.RESULT_OK :
    +                    /*
    +                     * Try the request again
    +                     */
    +                    ...
    +                    break;
    +                }
    +            ...
    +        }
    +        ...
    +    }
    +    ...
    +    private boolean servicesConnected() {
    +        // Check that Google Play services is available
    +        int resultCode =
    +                GooglePlayServicesUtil.
    +                        isGooglePlayServicesAvailable(this);
    +        // If Google Play services is available
    +        if (ConnectionResult.SUCCESS == resultCode) {
    +            // In debug mode, log the status
    +            Log.d("Location Updates",
    +                    "Google Play services is available.");
    +            // Continue
    +            return true;
    +        // Google Play services was not available for some reason
    +        } else {
    +            // Get the error code
    +            int errorCode = connectionResult.getErrorCode();
    +            // Get the error dialog from Google Play services
    +            Dialog errorDialog = GooglePlayServicesUtil.getErrorDialog(
    +                    errorCode,
    +                    this,
    +                    CONNECTION_FAILURE_RESOLUTION_REQUEST);
    +            // If Google Play services can provide an error dialog
    +            if (errorDialog != null) {
    +                // Create a new DialogFragment for the error dialog
    +                ErrorDialogFragment errorFragment =
    +                        new ErrorDialogFragment();
    +                // Set the dialog in the DialogFragment
    +                errorFragment.setDialog(errorDialog);
    +                // Show the error dialog in the DialogFragment
    +                errorFragment.show(
    +                        getSupportFragmentManager(),
    +                        "Location Updates");
    +            }
    +        }
    +    }
    +    ...
    +}
    +
    +

    + Snippets in the following sections call this method to verify that Google Play services is + available. +

    + +

    Define Location Services Callbacks

    +

    + Before you request location updates, you must first implement the interfaces that Location + Services uses to communicate connection status to your app: +

    +
    +
    +ConnectionCallbacks +
    +
    + Specifies methods that Location Services calls when a location client is connected or + disconnected. +
    +
    +OnConnectionFailedListener +
    +
    + Specifies a method that Location Services calls if an error occurs while attempting to + connect the location client. This method uses the previously-defined {@code showErrorDialog} + method to display an error dialog that attempts to fix the problem using Google Play + services. +
    +
    +

    + The following snippet shows how to specify the interfaces and define the methods: +

    +
    +public class MainActivity extends FragmentActivity implements
    +        GooglePlayServicesClient.ConnectionCallbacks,
    +        GooglePlayServicesClient.OnConnectionFailedListener {
    +    ...
    +    /*
    +     * Called by Location Services when the request to connect the
    +     * client finishes successfully. At this point, you can
    +     * request the current location or start periodic updates
    +     */
    +    @Override
    +    public void onConnected(Bundle dataBundle) {
    +        // Display the connection status
    +        Toast.makeText(this, "Connected", Toast.LENGTH_SHORT).show();
    +    }
    +    ...
    +    /*
    +     * Called by Location Services if the connection to the
    +     * location client drops because of an error.
    +     */
    +    @Override
    +    public void onDisconnected() {
    +        // Display the connection status
    +        Toast.makeText(this, "Disconnected. Please re-connect.",
    +                Toast.LENGTH_SHORT).show();
    +    }
    +    ...
    +    /*
    +     * Called by Location Services if the attempt to
    +     * Location Services fails.
    +     */
    +    @Override
    +    public void onConnectionFailed(ConnectionResult connectionResult) {
    +        /*
    +         * Google Play services can resolve some errors it detects.
    +         * If the error has a resolution, try sending an Intent to
    +         * start a Google Play services activity that can resolve
    +         * error.
    +         */
    +        if (connectionResult.hasResolution()) {
    +            try {
    +                // Start an Activity that tries to resolve the error
    +                connectionResult.startResolutionForResult(
    +                        this,
    +                        CONNECTION_FAILURE_RESOLUTION_REQUEST);
    +                /*
    +                * Thrown if Google Play services canceled the original
    +                * PendingIntent
    +                */
    +            } catch (IntentSender.SendIntentException e) {
    +                // Log the error
    +                e.printStackTrace();
    +            }
    +        } else {
    +            /*
    +             * If no resolution is available, display a dialog to the
    +             * user with the error.
    +             */
    +            showErrorDialog(connectionResult.getErrorCode());
    +        }
    +    }
    +    ...
    +}
    +
    +

    Define the location update callback

    +

    + Location Services sends location updates to your app either as an {@link android.content.Intent} + or as an argument passed to a callback method you define. This lesson shows you how to get the + update using a callback method, because that pattern works best for most use cases. If you want + to receive updates in the form of an {@link android.content.Intent}, read the lesson + Recognizing the User's Current Activity, which + presents a similar pattern. +

    +

    + The callback method that Location Services invokes to send a location update to your app is + specified in the +LocationListener + interface, in the method +onLocationChanged(). + The incoming argument is a {@link android.location.Location} object containing the location's + latitude and longitude. The following snippet shows how to specify the interface and define + the method: +

    +
    +public class MainActivity extends FragmentActivity implements
    +        GooglePlayServicesClient.ConnectionCallbacks,
    +        GooglePlayServicesClient.OnConnectionFailedListener,
    +        LocationListener {
    +    ...
    +    // Define the callback method that receives location updates
    +    @Override
    +    public void onLocationChanged(Location location) {
    +        // Report to the UI that the location was updated
    +        String msg = "Updated Location: " +
    +                Double.toString(location.getLatitude()) + "," +
    +                Double.toString(location.getLongitude());
    +        Toast.makeText(this, msg, Toast.LENGTH_SHORT).show();
    +    }
    +    ...
    +}
    +
    +

    + Now that you have the callbacks prepared, you can set up the request for location updates. + The first step is to specify the parameters that control the updates. +

    + +

    Specify Update Parameters

    +

    + Location Services allows you to control the interval between updates and the location accuracy + you want, by setting the values in a +LocationRequest + object and then sending this object as part of your request to start updates. +

    +

    + First, set the following interval parameters: +

    +
    +
    + Update interval +
    +
    + Set by +LocationRequest.setInterval(). + This method sets the rate in milliseconds at which your app prefers to receive location + updates. If no other apps are receiving updates from Location Services, your app will + receive updates at this rate. +
    +
    + Fastest update interval +
    +
    + Set by +LocationRequest.setFastestInterval(). + This method sets the fastest rate in milliseconds at which your app can handle + location updates. You need to set this rate because other apps also affect the rate + at which updates are sent. Location Services sends out updates at the fastest rate that any + app requested by calling +LocationRequest.setInterval(). + If this rate is faster than your app can handle, you may encounter problems with UI flicker + or data overflow. To prevent this, call +LocationRequest.setFastestInterval() + to set an upper limit to the update rate. +

    + Calling +LocationRequest.setFastestInterval() + also helps to save power. When you request a preferred update rate by calling +LocationRequest.setInterval(), + and a maximum rate by calling +LocationRequest.setFastestInterval(), + then your app gets the same update rate as the fastest rate in the system. If other + apps have requested a faster rate, you get the benefit of a faster rate. If no other + apps have a faster rate request outstanding, your app receives updates at the rate you specified + with +LocationRequest.setInterval(). +

    +
    +
    +

    + Next, set the accuracy parameter. In a foreground app, you need constant location updates with + high accuracy, so use the setting +LocationRequest.PRIORITY_HIGH_ACCURACY. +

    +

    + The following snippet shows how to set the update interval and accuracy in + {@link android.support.v4.app.FragmentActivity#onCreate onCreate()}: +

    +
    +public class MainActivity extends FragmentActivity implements
    +        GooglePlayServicesClient.ConnectionCallbacks,
    +        GooglePlayServicesClient.OnConnectionFailedListener,
    +        LocationListener {
    +    ...
    +    // Global constants
    +    ...
    +    // Milliseconds per second
    +    private static final int MILLISECONDS_PER_SECOND = 1000;
    +    // Update frequency in seconds
    +    public static final int UPDATE_INTERVAL_IN_SECONDS = 5;
    +    // Update frequency in milliseconds
    +    private static final long UPDATE_INTERVAL =
    +            MILLISECONDS_PER_SECOND * UPDATE_INTERVAL_IN SECONDS;
    +    // The fastest update frequency, in seconds
    +    private static final int FASTEST_INTERVAL_IN_SECONDS = 1;
    +    // A fast frequency ceiling in milliseconds
    +    private static final long FASTEST_INTERVAL =
    +            MILLISECONDS_PER_SECOND * FASTEST_INTERVAL_IN_SECONDS;
    +    ...
    +    // Define an object that holds accuracy and frequency parameters
    +    LocationResult mLocationRequest;
    +    ...
    +    @Override
    +    protected void onCreate(Bundle savedInstanceState) {
    +        super.onCreate(savedInstanceState);
    +        // Create the LocationRequest object
    +        mLocationRequest = LocationRequest.create();
    +        // Use high accuracy
    +        mLocationRequest.setPriority(
    +                LocationRequest.PRIORITY_HIGH_ACCURACY);
    +        // Set the update interval to 5 seconds
    +        mLocationRequest.setInterval(UPDATE_INTERVAL);
    +        // Set the fastest update interval to 1 second
    +        mLocationRequest.setFastestInterval(FASTEST_INTERVAL);
    +        ...
    +    }
    +    ...
    +}
    +
    +

    + Note: If your app accesses the network or does other long-running work after + receiving a location update, adjust the fastest interval to a slower value. This prevents your + app from receiving updates it can't use. Once the long-running work is done, set the fastest + interval back to a fast value. +

    + +

    Start Location Updates

    +

    + To send the request for location updates, create a location client in + {@link android.support.v4.app.FragmentActivity#onCreate onCreate()}, then connect it and make + the request by calling +requestLocationUpdates(). + Since your client must be connected for your app to receive updates, you should + connect the client and make the request in + {@link android.support.v4.app.FragmentActivity#onStart onStart()}. This ensures that you always + have a valid, connected client while your app is visible. +

    +

    + Remember that the user may want to turn off location updates for various reasons. You should + provide a way for the user to do this, and you should ensure that you don't start updates in + {@link android.support.v4.app.FragmentActivity#onStart onStart()} if updates were previously + turned off. To track the user's preference, store it in your app's + {@link android.content.SharedPreferences} in + {@link android.support.v4.app.FragmentActivity#onPause onPause()} and retrieve it in + {@link android.support.v4.app.FragmentActivity#onResume onResume()}. +

    +

    + The following snippet shows how to set up the client in + {@link android.support.v4.app.FragmentActivity#onCreate onCreate()}, and how to connect it + and request updates in {@link android.support.v4.app.FragmentActivity#onStart onStart()}: +

    +
    +public class MainActivity extends FragmentActivity implements
    +        GooglePlayServicesClient.ConnectionCallbacks,
    +        GooglePlayServicesClient.OnConnectionFailedListener,
    +        LocationListener {
    +    ...
    +    // Global variables
    +    ...
    +    LocationClient mLocationClient;
    +    boolean mUpdatesRequested;
    +    ...
    +    @Override
    +    protected void onCreate(Bundle savedInstanceState) {
    +        ...
    +        // Open the shared preferences
    +        mPrefs = getSharedPreferences("SharedPreferences",
    +                Context.MODE_PRIVATE);
    +        // Get a SharedPreferences editor
    +        mEditor = mPrefs.edit();
    +        /*
    +         * Create a new location client, using the enclosing class to
    +         * handle callbacks.
    +         */
    +        mLocationClient = new LocationClient(this, this, this);
    +        // Start with updates turned off
    +        mUpdatesRequested = false;
    +        ...
    +    }
    +    ...
    +    @Override
    +    protected void onPause() {
    +        // Save the current setting for updates
    +        mEditor.putBoolean("KEY_UPDATES_ON", mUpdatesRequested);
    +        mEditor.commit();
    +        super.onPause();
    +    }
    +    ...
    +    @Override
    +    protected void onStart() {
    +        ...
    +        mLocationClient.connect();
    +    }
    +    ...
    +    @Override
    +    protected void onResume() {
    +        /*
    +         * Get any previous setting for location updates
    +         * Gets "false" if an error occurs
    +         */
    +        if (mPrefs.contains("KEY_UPDATES_ON")) {
    +            mUpdatesRequested =
    +                    mPrefs.getBoolean("KEY_UPDATES_ON", false);
    +
    +        // Otherwise, turn off location updates
    +        } else {
    +            mEditor.putBoolean("KEY_UPDATES_ON", false);
    +            mEditor.commit();
    +        }
    +    }
    +    ...
    +}
    +
    +

    + For more information about saving preferences, read +Saving Key-Value Sets. +

    + +

    Stop Location Updates

    +

    + To stop location updates, save the state of the update flag in + {@link android.support.v4.app.FragmentActivity#onPause onPause()}, and stop updates in + {@link android.support.v4.app.FragmentActivity#onStop onStop()} by calling +removeLocationUpdates(LocationListener). + For example: +

    +
    +public class MainActivity extends FragmentActivity implements
    +        GooglePlayServicesClient.ConnectionCallbacks,
    +        GooglePlayServicesClient.OnConnectionFailedListener,
    +        LocationListener {
    +    ...
    +    /*
    +     * Called when the Activity is no longer visible at all.
    +     * Stop updates and disconnect.
    +     */
    +    @Override
    +    protected void onStop() {
    +        // If the client is connected
    +        if (mLocationClient.isConnected()) {
    +            stopPeriodicUpdates();
    +        }
    +        /*
    +         * After disconnect() is called, the client is
    +         * considered "dead".
    +         */
    +        mLocationClient.disconnect();
    +        super.onStop();
    +    }
    +    ...
    +}
    +
    +

    + You now have the basic structure of an app that requests and receives periodic location updates. + You can combine the features described in this lesson with the geofencing, activity recognition, + or reverse geocoding features described in other lessons in this class. +

    +

    + The next lesson, Displaying a Location Address, shows you how + to use the current location to display the current street address. +

    diff --git a/docs/html/training/location/retrieve-current.jd b/docs/html/training/location/retrieve-current.jd new file mode 100644 index 00000000000..99e475f8747 --- /dev/null +++ b/docs/html/training/location/retrieve-current.jd @@ -0,0 +1,387 @@ +page.title=Retrieving the Current Location +trainingnavtop=true +@jd:body +
    +
    + +

    This lesson teaches you to

    +
      +
    1. Specify App Permissions
    2. +
    3. Check for Google Play services
    4. +
    5. Define Location Services Callbacks
    6. +
    7. Connect the Location Client
    8. +
    9. Get the Current Location
    10. +
    + +

    You should also read

    + + +

    Try it out

    + +
    + Download the sample +

    LocationUpdates.zip

    +
    + +
    +
    + +

    + Location Services automatically maintains the user's current location, so all your app has to do + is retrieve it as needed. The location's accuracy is based on the location permissions you've + requested and location sensors that are currently active for the device. +

    + Location Services sends the current location to your app through a location client, which is + an instance of the Location Services class +LocationClient. + All requests for location information go through this client. +

    +

    + Note: Before you start the lesson, be sure that your development environment + and test device are set up correctly. To learn more about this, read the + Setup section in the Google Play + services guide. +

    + +

    Specify App Permissions

    +

    + Apps that use Location Services must request location permissions. Android has two location + permissions: {@link android.Manifest.permission#ACCESS_COARSE_LOCATION ACCESS_COARSE_LOCATION} + and {@link android.Manifest.permission#ACCESS_FINE_LOCATION ACCESS_FINE_LOCATION}. The + permission you choose controls the accuracy of the current location. If you request only coarse + location permission, Location Services obfuscates the returned location to an accuracy + that's roughly equivalent to a city block. +

    +

    + Requesting {@link android.Manifest.permission#ACCESS_FINE_LOCATION ACCESS_FINE_LOCATION} implies + a request for {@link android.Manifest.permission#ACCESS_COARSE_LOCATION ACCESS_COARSE_LOCATION}. +

    +

    + For example, to add {@link android.Manifest.permission#ACCESS_COARSE_LOCATION + ACCESS_COARSE_LOCATION}, insert the following as a child element of the + <manifest> + element: +

    +
    +<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
    +
    + +

    Check for Google Play Services

    +

    + Location Services is part of the Google Play services APK. Since it's hard to anticipate the + state of the user's device, you should always check that the APK is installed before you attempt + to connect to Location Services. To check that the APK is installed, call +GooglePlayServicesUtil.isGooglePlayServicesAvailable(), + which returns one of the + integer result codes listed in the reference documentation for +ConnectionResult. + If you encounter an error, call +GooglePlayServicesUtil.getErrorDialog() + to retrieve localized dialog that prompts users to take the correct action, then display + the dialog in a {@link android.support.v4.app.DialogFragment}. The dialog may allow the + user to correct the problem, in which case Google Play services may send a result back to your + activity. To handle this result, override the method + {@link android.support.v4.app.FragmentActivity#onActivityResult onActivityResult()}. +

    +

    + Since you usually need to check for Google Play services in more than one place in your code, + define a method that encapsulates the check, then call the method before each connection + attempt. The following snippet contains all of the code required to check for Google + Play services: +

    +
    +public class MainActivity extends FragmentActivity {
    +    ...
    +    // Global constants
    +    /*
    +     * Define a request code to send to Google Play services
    +     * This code is returned in Activity.onActivityResult
    +     */
    +    private final static int
    +            CONNECTION_FAILURE_RESOLUTION_REQUEST = 9000;
    +    ...
    +    // Define a DialogFragment that displays the error dialog
    +    public static class ErrorDialogFragment extends DialogFragment {
    +        // Global field to contain the error dialog
    +        private Dialog mDialog;
    +        // Default constructor. Sets the dialog field to null
    +        public ErrorDialogFragment() {
    +            super();
    +            mDialog = null;
    +        }
    +        // Set the dialog to display
    +        public void setDialog(Dialog dialog) {
    +            mDialog = dialog;
    +        }
    +        // Return a Dialog to the DialogFragment.
    +        @Override
    +        public Dialog onCreateDialog(Bundle savedInstanceState) {
    +            return mDialog;
    +        }
    +    }
    +    ...
    +    /*
    +     * Handle results returned to the FragmentActivity
    +     * by Google Play services
    +     */
    +    @Override
    +    protected void onActivityResult(
    +            int requestCode, int resultCode, Intent data) {
    +        // Decide what to do based on the original request code
    +        switch (requestCode) {
    +            ...
    +            case CONNECTION_FAILURE_RESOLUTION_REQUEST :
    +            /*
    +             * If the result code is Activity.RESULT_OK, try
    +             * to connect again
    +             */
    +                switch (resultCode) {
    +                    case Activity.RESULT_OK :
    +                    /*
    +                     * Try the request again
    +                     */
    +                    ...
    +                    break;
    +                }
    +            ...
    +        }
    +     }
    +    ...
    +    private boolean servicesConnected() {
    +        // Check that Google Play services is available
    +        int resultCode =
    +                GooglePlayServicesUtil.
    +                        isGooglePlayServicesAvailable(this);
    +        // If Google Play services is available
    +        if (ConnectionResult.SUCCESS == resultCode) {
    +            // In debug mode, log the status
    +            Log.d("Location Updates",
    +                    "Google Play services is available.");
    +            // Continue
    +            return true;
    +        // Google Play services was not available for some reason
    +        } else {
    +            // Get the error code
    +            int errorCode = connectionResult.getErrorCode();
    +            // Get the error dialog from Google Play services
    +            Dialog errorDialog = GooglePlayServicesUtil.getErrorDialog(
    +                    errorCode,
    +                    this,
    +                    CONNECTION_FAILURE_RESOLUTION_REQUEST);
    +
    +            // If Google Play services can provide an error dialog
    +            if (errorDialog != null) {
    +                // Create a new DialogFragment for the error dialog
    +                ErrorDialogFragment errorFragment =
    +                        new ErrorDialogFragment();
    +                // Set the dialog in the DialogFragment
    +                errorFragment.setDialog(errorDialog);
    +                // Show the error dialog in the DialogFragment
    +                errorFragment.show(getSupportFragmentManager(),
    +                        "Location Updates");
    +            }
    +        }
    +    }
    +    ...
    +}
    +
    +

    + Snippets in the following sections call this method to verify that Google Play services is + available. +

    + +

    Define Location Services Callbacks

    +

    + To get the current location, create a location client, connect it + to Location Services, and then call its +getLastLocation() + method. The return value is the best, most recent location, based on the permissions your + app requested and the currently-enabled location sensors. +

    +

    + Before you create the location client, implement the interfaces that Location Services uses to + communicate with your app: +

    +
    +
    +ConnectionCallbacks +
    +
    + Specifies methods that Location Services calls when a location client is connected or + disconnected. +
    +
    +OnConnectionFailedListener +
    +
    + Specifies a method that Location Services calls if an error occurs while attempting to + connect the location client. This method uses the previously-defined {@code showErrorDialog} + method to display an error dialog that attempts to fix the problem using Google Play + services. +
    +
    +

    + The following snippet shows how to specify the interfaces and define the methods: +

    +
    +public class MainActivity extends FragmentActivity implements
    +        GooglePlayServicesClient.ConnectionCallbacks,
    +        GooglePlayServicesClient.OnConnectionFailedListener {
    +    ...
    +    /*
    +     * Called by Location Services when the request to connect the
    +     * client finishes successfully. At this point, you can
    +     * request the current location or start periodic updates
    +     */
    +    @Override
    +    public void onConnected(Bundle dataBundle) {
    +        // Display the connection status
    +        Toast.makeText(this, "Connected", Toast.LENGTH_SHORT).show();
    +
    +    }
    +    ...
    +    /*
    +     * Called by Location Services if the connection to the
    +     * location client drops because of an error.
    +     */
    +    @Override
    +    public void onDisconnected() {
    +        // Display the connection status
    +        Toast.makeText(this, "Disconnected. Please re-connect.",
    +                Toast.LENGTH_SHORT).show();
    +    }
    +    ...
    +    /*
    +     * Called by Location Services if the attempt to
    +     * Location Services fails.
    +     */
    +    @Override
    +    public void onConnectionFailed(ConnectionResult connectionResult) {
    +        /*
    +         * Google Play services can resolve some errors it detects.
    +         * If the error has a resolution, try sending an Intent to
    +         * start a Google Play services activity that can resolve
    +         * error.
    +         */
    +        if (connectionResult.hasResolution()) {
    +            try {
    +                // Start an Activity that tries to resolve the error
    +                connectionResult.startResolutionForResult(
    +                        this,
    +                        CONNECTION_FAILURE_RESOLUTION_REQUEST);
    +                /*
    +                 * Thrown if Google Play services canceled the original
    +                 * PendingIntent
    +                 */
    +            } catch (IntentSender.SendIntentException e) {
    +                // Log the error
    +                e.printStackTrace();
    +            }
    +        } else {
    +            /*
    +             * If no resolution is available, display a dialog to the
    +             * user with the error.
    +             */
    +            showErrorDialog(connectionResult.getErrorCode());
    +        }
    +    }
    +    ...
    +}
    +
    + +

    Connect the Location Client

    +

    + Now that the callback methods are in place, create the location client and connect it to + Location Services. +

    +

    + You should create the location client in {@link android.support.v4.app.FragmentActivity#onCreate + onCreate()}, then connect it in + {@link android.support.v4.app.FragmentActivity#onStart onStart()}, so that Location Services + maintains the current location while your activity is fully visible. Disconnect the client in + {@link android.support.v4.app.FragmentActivity#onStop onStop()}, so that when your app is not + visible, Location Services is not maintaining the current location. Following this pattern of + connection and disconnection helps save battery power. For example: +

    +

    + Note: The current location is only maintained while a location client is + connected to Location Service. Assuming that no other apps are connected to Location Services, + if you disconnect the client and then sometime later call +getLastLocation(), + the result may be out of date. +

    +
    +public class MainActivity extends FragmentActivity implements
    +        GooglePlayServicesClient.ConnectionCallbacks,
    +        GooglePlayServicesClient.OnConnectionFailedListener {
    +    ...
    +    @Override
    +    protected void onCreate(Bundle savedInstanceState) {
    +        ...
    +        /*
    +         * Create a new location client, using the enclosing class to
    +         * handle callbacks.
    +         */
    +        mLocationClient = new LocationClient(this, this, this);
    +        ...
    +    }
    +    ...
    +    /*
    +     * Called when the Activity becomes visible.
    +     */
    +    @Override
    +    protected void onStart() {
    +        super.onStart();
    +        // Connect the client.
    +        mLocationClient.connect();
    +    }
    +    ...
    +    /*
    +     * Called when the Activity is no longer visible.
    +     */
    +    @Override
    +    protected void onStop() {
    +        // Disconnecting the client invalidates it.
    +        mLocationClient.disconnect();
    +        super.onStop();
    +    }
    +    ...
    +}
    +
    + +

    Get the Current Location

    +

    + To get the current location, call +getLastLocation(). + For example: +

    +
    +public class MainActivity extends FragmentActivity implements
    +        GooglePlayServicesClient.ConnectionCallbacks,
    +        GooglePlayServicesClient.OnConnectionFailedListener {
    +    ...
    +    // Global variable to hold the current location
    +    Location mCurrentLocation;
    +    ...
    +    mCurrentLocation = mLocationClient.getLastLocation();
    +    ...
    +}
    +
    +

    + The next lesson, Receiving Location Updates, shows + you how to receive periodic location updates from Location Services. +

    diff --git a/docs/html/training/training_toc.cs b/docs/html/training/training_toc.cs index f0d33595e77..ee6913c1a22 100644 --- a/docs/html/training/training_toc.cs +++ b/docs/html/training/training_toc.cs @@ -193,9 +193,9 @@ Saving Files - - - + + + - - + + - - + + - + - + - + - + - + - + - +
  • Keeping Your App Responsive
  • - +
  • - - + + - + - +