@z_YuwcRQ|eifn>w|3j2x2%OR7G_nM1OOTM=GfkXmT
z9B;}v-gjlWbRezt34)}fLxXIK&0KK&4w#mVv
z-q6;O|NoBVwCA)!?7+%I^enx{XQxmLj>hlI`zdL9*I9){4aR3KL1fqss)@*Dw*^P`u{(
zoBUdg`~myVwg7aGc9$MW^=zzQFq*}S_oenwZH#;-t?`#^LYkkWP%x4AN)Yco=J>n$
z?JvP&4G%MZ;77=`n12{3iM{?{&5odAx9hVGgBT$$8JK2OIatKqv+>U?SqNb-6xH<@wAdM=-lGntw2~>9dsIZ!9qsYbHq3|f}?1k}Id;GOF%&v4V
zRPXjs&wt`zt$)4=1LNpne^8Yd1$w<#4_;r$ccS39xVv{+L-Elh7!x6kdrRaxL!N&}
zRp0H5TM~$f5Kum$oR8kQV{a}A4;=7BJm5Z`I{=T|HgpPony{j)!i}S_uj5cbk;$P*<)Cn#|Px1RRH-PLp^e7
z$7jvdaAwV*8f2GWL{awXOyRszm!-4v+{%%~^D)5jlc7V~RIH-VV1arQc4|G)xOwo1
z&oJV~b&T5MkB_soe|d@+MIc?(J3Vv-XRDj%a{l|RA4?C8v#$sdN>t2yE(b0hbW!W=
z?#xYaC>Otfbnq}L9A184JTsv!GjI857H1Z$G_xm6301ThV|!H|7U-4u?ya*$XvbW-
zU43*D;9miU`7?r}l}2H?bWq>W5B7Hp*{XzB!uFdx(2xoLCL$GtnucpT8=YvP-}Z>z
zDVF#Tt?@aSM=trX&9r#f+rMQ2Jhx(bQ0_4c+x9o++u?7o4-y_Kzs${B)e}oq8M+WBF9Ye;)qf(|X~XdZy~I(WQ7mpaS?}62}2M&|%%kXZcu#{5bui
z(q)e(t=DCiT=IT@C(-d~nv7HWFD6x>6$+Dt6GHSh>Vq;pp6P4
z{H*IG$eLwdzpD6T_}#Pl@7tN25*BHI57w;|)J^DGg#fyCdbZ`9kmCw6h@vQB#Jnxd
zf0vGiM6y30OIYXaQ(ad+Nr*>Nl?tZhits>76zjPAWsVE)CgyjDgTI;{>b8LUnyC6pU7buz^#w^Z12Ylwk2=ha8P50wvBTjws{V4Bu>mw}e>+fGhLA2KY<^ay-9UT;d@xb}N
z+OguWuI`GsRer3Ee5L?BDx?g_E3j~=V%M&wY&A&0VeZYw-?I-5xK0DBv86d!#boK_
z=0Yn&&G$jh9HJaev3&$>d0{lk-;e|;yv1sUXD{y@qzcI+LKb3`?MWqt-b{_P@KD3n
zTT!WiOH0cSQo?vfpA9%*4uAerI`M|^`d5)PqXz0=j2+C&L
z=~tfRJ=jeh)t{(cciJF-<4fZv2Flxox@Jc7Ir+)>!&^9F8Qs?=Z^7ssg{YMI{>;YO
zNQ=l>YLnxlP+B)GHRyuZ@Lm>0SmAyFf=qggT;sS%I=Bi_)zX1wW@O^CO4$KnM>=_w?kY
zt;maB4k*@Sma7mJH5@FPu_Gl~GUu;9CtC#;@r&q@)S|#>kufs@4fq^{9`m~z8$Ri{TPPNT$F71wPV7vst;vT<6jQrX`T;?&@#%u{zZpS
z=HssQOd^>oVR=Neg-cr4-yT%+>XneX{m$aAkuAe6KY{u+Guc2C$LV_=F5ydYs3=y8
zb)V?>cK=fpF&a));ZSn*D4%-zFP{_9{GQ6$9wDO3>-g^Yb%NDs>3NAQBGf<;8UwH(
z$vA?jR|A|7eY8{8V^9^%#MOVcu_^A}krkE#p^6gTtSd(ZDxoEvE{>U-`eDkZw~*Kt
zeks=-uUakqQB4{0BK%?@H<+E+(0YAW3PCQy&{V<@bthOTPpWJvWu-jHB+PoJI9KR~
ztA?}(mMpuuwY%ThmHfa#WktPFJZ3AaE(Z)$iv&7a~Ywzx6}}X@TAg^7b7%(6nGZ
zzv;66`$~<@bUal7TXf4`SY+_1g1%SzyxD{wS^q`-!67}_VbqUwdv0eOA&L?K{5!o2
z$r@w}@@~F0ne*6okR9Q&9}T#m?JZIANP%;Xfvyh5o;F6c{uT73dk_{Q+QI(IA@8*{_VxgnB2Sc-5f|K?tA+H=M8tw1ev#HcXb$P(gg7*1?
zhMgdnNzbn`5%t7iRCK&`h#cu;Nw4^h!VLAmTDaHUJah2HQw_porjlZa~Q@
zR2>S5>Uf}r>Q7@<6}C>AxYqmX)=p!Qv-mYH+H}q2tpYdMu72GO2j-rFIIRd##Yj{{
z)Juel%v69c7o;$t7lK$U0$jf7c9=s^2_}NfCz#RotTH_#F9&yJ<
zK@f-ok?`B9B^SZ=$%V=&
z6eZdRY(7tHOOs3q-1)m+AApqYn^hm&E+RVous7vRk9IM-b1kHd&=81`q8u&6$-Q0W
z;T?jR#X~5pwmD0ho_zFP_}Z>U;3t-{S{C4+t4su-YIUva=S`j1H$jKd=u|5hYx;Tc
zZNtP^tULH--&44)W<5Rx(kXY+j*Cc9tT?umn$SnCr+!YNqk-=A3H(|TvHvNJ#RAr_
zMSKj)E(STix|&YHj^l%v_obm8?#lB6aA*M;yq|r)rc0zoULE?lfs>WgKe}HfeP2C?
zGdzQWyn)*4wV?7dWO^2_2rySWzWB;Ih2x(V8VA?XcMZ-|fk=Ec>aQ1ST5$ZLHBY9IPn%7cGmjSf%#s!S>ZypKvJp#C>QbtHqe0B;?$c
z>_w=TECmVrev_m9K?-hXC=QW7xjo3Yk4cKHpt+HB!Ed!?@sZ}@@T-|1EA?cuuz)}F
zus3rX{#Yc|&_;lrC92yIB7i16RVD41pHjT(=}QgWjin(c(#?{jbwGwd{vIPXM#oQ4
zNUnsY`1Y;q;Sa`-r2U@otw0>)-oCUtBgU`r!wAC}Izn5$GtTE7<MNZ^EALigN5@><_^Mj%KyRw07$kprAMGjnAfDsbNfbcjGqaN&{26kyP5V&-U
zrqRbO+R(zrb8igNYli8d&$RfG-&XU6hM^v#AARjRP1A~}@|SVzq=~8YutyM^pTPm3
z_Bn+!ui4-!fuIO`0Jq~9mIZdSC(LLxRYG%1H94RtA7U;)1eS=mlX0XWpi8wSu8z_K
zpD!fDwVzR(#e&w&U}kvhS4KZa{9T`08HkW8LmvzbGLG4>A6fkDv1YchG*lfw`GK`o
zTAbL4zV6CeFr=%Ae!`yZK4ZA8&?a(LFpJA6s;S!>g5%6)(2;=a(J@in6ZRjNZ<9>{
zSy|Q@rnvb0rAM_tOeMB(e-};P|EzWe$vD(ejH8EBGakv>j>!wHc;KE_ZSLz-=nDBS
zR$d0`2_pe02=>(aQYSLWLP!9ftL66vVBQ$@gy0k
z<&~e6ZdhXdI@vOtnTb0bNoU-0`nLpORmZLK6u1;cT2R>U=ZIpae
zTzW%7hk9O6VYT^1%OAx!>%*gpXgNVeyp3
z2SvV4GdT#5`y(A696+Xg{e6|C+KK`F7eh%~G!0w1d1jL~Abc^=dEl07-)5T={g>k`
z3ANDT<ssZ{sD#@(OR(Df=0R)}QFWp@0^w{x&8A%KH-+-!uV(qoRo}a^)M7cgk~N
zOxy}HnCih&!9@ib7o*fmg-LLfv|jM?(p)l$HgY|W%eDEW&hsJumqCKbDlR3H7@QJJ
zgC$Zj1Kx?efeEyluN;=BkdgZi-+>4Om!t0he4?jqm%X=-6XHph4Rn+B%Xya_h
z?Qod{TSZmAbjQk~95G;fC`1+1zJ>?b!-A^{wBS=M5)N;3B_1UHR+FbCvgiyQ;p@w`
zxtT?ssY1t0$w!eZId4`g+P2~>X?$#>AU}px>NAY?`tqtcx8}v?Z9!M}C;&XOCnLAx
z0!1+ZVwwRrcgW6fpEG0Tdc&~r4-K2`kF?9KF2euFaQp%&pFN+d3%byLCtsi4Z7Ouh
z@7x|^wHl5Qfz>rpp#tC%2>{y0}bfsy`
z82U%rPBa-OJ#SZjSZvlUNkQ$$MiB?}RHpORW3d^a+ws+NG2}9a{&HucDL&lHoXnZZ>kBxey$R8e34klc0N+!F9f^dv}4Aiu1
zzT$jwh=nCkHq!xIgZ_X;r*kgNR{_Dr6bR0nl5MghpE$iw0E7sucQcvWMDIrl_7KL6
z9n`K#8<*)6vZoRM{rZsPOL?6ma(lqgFM^5EQ`mtBYthaC@dvdtOKU~2XZq$gJQ=eI
z0faT9+u_6Hj}|X=qC@enLdP%G9dO|9^$0Jv7&`87@yy=+Bd_8Nx%ppwrmG2cQGw|h
z;|x$ZzzDHWL^}-*>c%G8R!WVdjpc9w&FL&tHPox;-ls2(vCqTmZ!=jx4tOZ7YWSD;
zqpZ(REg%936v8%Gg^6Gm&lFlB_)a6M6w2^vcrRY>72gcIK8r6w+O9M(9f`1E^0+EI
zyIjyE&f+!q_NO@LGk_LAg)%&shnKq-v0A_UDxc%Z==fuGBg5`IQ{ZZPb;_Ajc$K=n@3VY95fF}4v4{dxefQhOCa8TM+B8m
zW52ujE>0n7Zo%D2Jbx}=YfN*}&`{#J2HPChq$5<3pZN
zv204)YqNs~N*&Q$<2+$MO7NVwv(2fF2fv-g>a}clg)J}XQ+fODH;3=yZ9dhqj1i^1
zkPVqlbVu@H{|(v$X~FT@#OC*g5BDZ;Chdn~yM6*Sp{q2LG8zE%13bZF(vECijC7(h#*GnqVh$bgx~5|1r}
zG;lCqlvU*SnGDnVUX1(%f5TUXBYo@ZL*sXF>wTs4EInbG(rdAd0RPc|_j`SpjoLhs
z?GXvMHg|y>X^$_rr?*azEhDbGYJTz9qT7f2=ADC8p9PfMuuui6^hUqGOrJ`Lw04F^
z{F?#{PWKBfBaMCKF(OJTIv0C%HT~OKPXVFxFXu~fT7b^!pTcOH=vQJ8rJW@
z^Ym_TJy0ly)xj}Dp@i^1($qa;xAwvHI;w}d0g$DbU91J8(=SD1{)+5?
z8D1Ly2s80eMSA8tW;g%rzx)E=O<_^2_0S2?YDTy!BPBXEcYv6R&Wjh`@06yYnAypF
zl@mqrZI0DB9aRqda7&sXqKj9#uUZh&9Qqf(rQ#KJA0!&lhrqd
zuo0{f?9zz!K2FQ(xP&O}F+!TNl8Mu&`6V&YpK21f764x!n{Svu;kT(IFC@TM-#I=%
zT+5&CNOVZ9Wr*`^{|VP2AHC1I3QpmFUV>~2RzFjMYm?Kvkje|o>s<9rP+kz#E8R86@+BMfN8>F+RtYz5@^^Qn<#vh@#sHBdEy2mcj?
zw`=%M6jV{SHz^ZKq|M7YIEbRS06Y#%E!1b`7Hl4)^Yg^vj3zxT5?AHHsSQF>(cX=y
zpeIVDqX!S~lh4PO^!6C}rcy#s$w|0aYj94j{?*`8j+|go1|*c_^$%I)tON{
zX#Lm%g+UoXCpGa2fxePz0GrVzFZ1}hkzC8Lh#Yz<{n7aK_nY&5hY~=MD%6O%p`?*dLwdyqBc+Ha+d`TT~Dezu$=C>5w{O^
z3|!q(;KzWwS0Q!=L}`Z4I^kz!S#fo}V3d!vrjvFWVnP}3sC(942xAUbjMRWn)Hhj!
zFQ{jpTk*WfpDaJrvfUF?e(m_GSL)qJH$_j*GQcRS_DPZ9R(C;cyqXJjHNw%v$!$)iiYo-Yee@-FV^@nZJ)+bZa#pbcaf@7Ps^CAj}xut$FDK2YeL
zpk^!~yl2&gkGb&kH)1UWJ9a%j
zRo)7rNldjN;)$;+SpKJ4oKVi<>1lryk3pBAc#n`RmC+tjp0`-TzF;4#qWkioRI47z`fh!QHo8;1I_{k|+DYf6KzH#@K2KZybsX5uR$eZzSENwt6
zn>z-o>66lG9m_jubz+O!(E)dvuMQ}S&Rt;*b*zcEu-o^f1~pj!sBNCt;)?HFS`>8=
z-Syz>sLTU(KIQcgmqTmK>S%uQtgWq!(7=HztW?x25U$#m0$F}E;HkU
zNz|Bw(PIovx9ls@usvqAr?XBYfJ25mY)gzK>@%DhCS96%BYxs>rd<@<_Dxn#yIfWd
zZJ(;OuZcH=7k;+GZMV)r85^MT%#4DmTYd&X&MXfpADEXK%HJgqEA0HCOBLwXJo_F(
zATT0AihEs8*Ouk-B=*<4rZE1@pa_B9EYZVX{=H4QVx1LSUS_Js8DDtr@eAPg?l6*r
zU-47z8(w1;%R`%%#(NaB$j-{Om&e@3%k5UUL^X>u(xX<4i
zd{CT~zSPV@10yrcnIitakijr#1YYJsRUQX8teXN`=jBP5XG*Tm-278y2d1Q-S*c
zIR(2CDVE*|jw`PK(ig=y4rqzyC^?5G*XjepK%rYi_nP;WLagIEFX$DwgG0}4Q<~}c
zZ}7>(&$Tc(*Y5(QP*3#6B#RiW*;L^g-P}~HBl&$T`wVa_sGZc#0>w?uzJTzjTlB|`
zHK^qxm5124)a;M3uEmjwEPW~#lEs(Yr-C&9*%$o;N97}ha!#jf?sCghN9cRuO)|Oj
z@#ncC3sz1^0aSiR7aX^(D%<@-EQ)VKZ!!qW-7V0*4+1bUqycg!7dKETr$z9y8(-P8
z(MW9=1-i<8-O|SuFIL72sog5MW8W;G-#*gnClan$`$!2c6)w#aY{yrz&%WtqQchS>
zIdMBN@j;TKy=B+u4H(k{MQx`AaB6%yFn<1>{A@^8VO?DM^rI;^4Eg|;1}v|iXrNT;
z_8n1Sh6A2k^*Sg3F+D#@KAo5DW}UAoU!X~tBt*Rvo+#y79%HY(8EgnnM|)7dPo4@;@Dy4*&A2dUpG4@jkRv7F{zqXd)-y
zQ_KVdSpb?VFRs&vqIpKUqoH|=<{dWsdyynoyK|wFtzC&{J55ejFRo1DM_p#NCqMP>
zGF({LlQ`TOKKVbpAGf-BP~gI~*7~-!)vG)DB)=}<%21h)e1akkGYiJyqgA0G(YjsG
zO+kW|KuH6$+%J`!k||W}cFU9^*B4`5{<4D~jBWHqN^Ae3aNxDsr!XI9`dJH_5-5h9Ql$qju$ti6pf(dto~4FOp$WQNG!&kq!i`R!T+
z*!*?sP7Cn=Zh#55$5o(gC}9C^@xK6k2-`|rL}VT@{hdz}Wr&`~Yy0EeEe@|++mS+H
zv&8AimG?Wb`;5x$hJURhuW}I!YW($W1F;@nB9hG9H7e}r_vHZ+ZA5;j#hpr~#fj2r
z@r>lbw99PDn`_fRXP9lph^_Frdm_&}cNOwZ@lt
ze(sPN_%yj%LzVREY$r8c4{cjyF(Z#b6m0`htn4)oG@-;7mNRU+gYf-V|0>ESILl!n
z)PA`*T#qZO-`uu}nqWvgRX2nGZqz3g*g4lkj0z5tHNl`SN6kObeY0@OP
z23Hz@=8Lp`DY#2j@WtI7HW}BsXi5Frf8`H@9W7vRPWJvxPXBr%fPN+bzjmX429$if&&`eQ^Y-%ce*5hgYo
z`(kCP_KsJalti)L1J_d}dDt5^HI)Cx*+n9<1P^V5R&X(Ky-X1Y_fCDA`
zfuGy;3=6^_@3U_JM@?VPUYTK(C)7t((id9~u@JM(zL_+6fs(l12EO_^8DKkN8VKs(
zgO9_t*~Nc$NFw*~XC=uP#UFaA=Sr1jtGU~>=e?sGf^GB5HIiN-JUu$$Cw)C8c^u{^
zj=N~B=r2|WdpU=R4l-&tv4uJ2xIfhJ9=(RIn(m1x$<<@`l?S>?@umE}$$Sz<1z>rN
zp#lQD)7}_U8o2>3-m{w2Vd$lAVrk6CyCGmi;
zswA{$ijtpz(YZC$h?wX33sfDws!fgIBRC&dR3H$L`!O^>RrlpG{!qd?Q&Q-0_JI+=
zsP4u?<||wXEH|P@pcI@0w7*cWwYIe~>C1+}8d8A4VM<9kmd$PSXUXwD4O6P}ez1T%
zn%Q2jXrJ}^gx!Ymu{La5{qo0#)=~*yE&pAAl_k7U3D0`nL+IQsWpnoD--xtDAC~f_)^A1y(eJZKu#|=JV~l9RSN@cw<`9^8ztMiiOJ`@Jg-j#ybq2L|OsOl&RW
z#m<6W$goA{$U7*qyd-VIY*~IY+YG`qU%RTeyMoNVwz+c&5;|LsPPfb&Cn1B5F*Ig)
z!vu7ckpS53V@%gI76
zNSgutQq~ZD*G3u-Gd7}lF=}|{r%d(ZU5)Elc!yipy|z}2iW+FF=d(w#!4BuNczCB$
z@EJ^Wn(Iav04HSXA@^1`q2Si_Nb*|cvo#NUeM#zL8;y5q^?>^c5ZD$9C$#sI1a8L7
zPqB0=<+10yw^Fp`XrdCDn2AsW9Y{omD)^DkVJp76@W$WOkVCe4VC4i%nuyzHQ>3?s
zwapU@Ps$W~B=OaFlgEP{Qxs2w$r&M0t~WHphj^1A5dm!zP5wQc`S%M2xPBHRdIFuS
zhUgSw(WGU1hc+qyZkAa~Vz#~ib{`fqQna_BBlehaAL*26RZr-tJnDl3Wk&(AEYLdx
z7`ADy^4EiGrA{eQm%d2hO&DD7c4mqW-y5yzPHKzhh@59BW30m9_c}0;T)@wF&hKlH
zeTzqX{Hcos&s$h0d|k8QJZ(xfC*|AzoBHD(w`
zKorPvYr@}2ao8KqUcdI%wkjbyJyhB1s(cP797z8`bW+cQYnWHFvIA?)>7-+CNJL~4
z@2-V?7Va@S(4XInIAfJju?xQxKX(sTv9(U!c%m0JSIvLh326($P7&3kvSKQj(sy$c
zG>y;wd?fXlcn+7E4!lWIKI^Nu)h}u3pwI?^UPK2T4U%K;yBdeiO$f6xz8kCI0T$}B
zcu?ZTtkdkYL^rs;^o&BeC`BuPXgw8}>taiPlKhAqhaZOw2nxq5B*CIzkSwv+wC{;ZB?@cZ4Upo;Mk`Zvczt(
z9}4Bu&aY(|%osEq^<5t&+otY9Uxb`J$8k6)tN&4J3dfXgK*9UxU=U?-UAyJ0Z&&5J
za{6aVC|$fwCKbw+==M*~40bo6XZ@qcfqu2@xqAFMOz*
zzL^&yzIcGWj*~A5sEjup7hd*#flXGt$DN#HczC8`>I+$0M*;5BOqkhZ3MuN<+-B$-
zW9HqB^A%_iyHq%{&94S1z7|A(=o<>d?f@RAcjOZh3aP7jY+homS+UzzQw!c<8dmU0
z*Vc@^(gEg~DV96-TxaEpINm!1tuQkig$KO+4PEyg<)NZJYUGgri5SdPB$T~enH
zl=i6MyC{W$n
zzgTsQ%b_6a^miLn$h-+bL(GLh_8u)SoM!cC$C;WXzEmI13aS;^`Pz;A#m1+|ArL5|
z>ZOgu^ZyWrlqDbPXeqVZb?
z^mimGC>&+65dB-8R-5gjK2nMixS&(ymZ&KH5bhn$OFZz>^6u3
zt#Y$mMGD_D*~@8j(jjmduJjMxbdBq*JpaK1Y<7#>XQ{p2_+10Okvfwa0LvOVpV#c!re3XNb-ACoF&`-2lKscc?lBSYQxqoDi*E$&h?7NVe+
zX4-|Olb_@4s>9w&oz{Pfl{qkXXeAO+d|5)fGB7(5^`kx_zcqW|6bSH*O++hceS^VOnKxGJ09(<-c
z!xVnJk>sclm
zaiw4+{0;oa^M}nd;n7!bZfNu|pZi7Yd~5Pa9wSqz7_xWmt?&2TeBx%R}K@
zhk_cW9IB=>YsVO3cBgWhUlmrTK`$K+KnEf2FtJybWA`)?t{JyUG*4|2!Ex!JK&B$(
z?)<|ucZ(Boisb(i;fF|Hsm}M9L$E~fm_gQwd9U;@G3LB{`xIA;$1kstK3Ne^fF3H0
zGKHjq6nty>7a{{-f~SS&itfjaM&<;u7b2lsC!YE5)Q3_7ZSIxof$|%)w_RcH;sdX2
z>)`pf{?)Su%5iI%y#ylq!gr}H)|Ml0qDQgWxespqd7d>r1rk_}l*_7V+SMVX0^sCo
zC#<1yrMtr=E36_dZyYF~XQd1?`;~eDnXuC4gFiTZ7~?zReRBW(dD2F1TmAh5_l%$(
zlY-$Zw?LBx1OFG4-VWdAiNt!-#rOWfE0h_+$xRo%u~Wum0EIv5Xa`Ei-JFzRUv#+5
z;8(ddZyZM9s^1tOpDE?*t1p+NvQ`d@{tL*u$Lw3=5k=n0n;+Csx@be5zzH2~Wz*yS
zH+k!6R!w&hQsECzWZxM?j~%{;1bf{zfB0?30$L)6j~tC69iNW1w8H!*DFP*bYN3jQ
zS-bVWy(JA41)F?w$QI9yOgEU1Vh|^uU(~L6VkGoiBU!-h%{|f{6bH@aQ`|TcvaCd}
zzl`s!mtm`2_3*AGVf=P={$DhmbzD^6+qH+GySq#22I=nZ?(Psl(xE{br8}iNrBjeb
zK@cRRC8c4Qd5+)T`+m;fXXf<2_qx}*_TlTSf4YeClzFNJi@Ay>7_Xx&5=dHTU*Rem
zY#Qdd=GGAVVAX|bA?{6mc~7y_`h!6&j0=8O6Li0>V{Fv}7%G$%knQ7arG`Z&Xiy8&)(*3IO+oY<=%n^Nwt^Zo{LvAB
zl51=ugm1&8xI2DnNB==71AIW!&o=0i{_nUCAx(Sy=9`;JC4B{eC0JXB!0uDp2%01FM{mUQ#C!NniMZ
zNHIeqf>dPRCKqiZEU~e9$1$C-2HcfV$%WpS&Uj!
z?K_=&%u7%sE|9tv=G-w3^;I3K_)=rk^(AZma;S>Bfryc1eGC4aCM>^74!!Iz*i`6W)LZnI%16-`XK=-X~GIm$UUIE(g*iROU
zXf4#+2}%Q|VwdJgbZ%o10=Ijxme!ec+fPxmIsSj={7#fAZOJDG*Cy)Idslz}Au)2h
zFrm{}KSV2ip)_Z99^7|3Q(_vx$2H3IYvVLy`K6U#mKlLC2w|GRM&R
zbzypN?2uz1+V3tYhpI5s|-O%86Pl+BC#KgcH2>31F-Di@tsCa!s2c$^WI
zJW8L|YC{{d45Qq3krG}DK}6~d{WqmJLrXBqTYL`5f~}l?REO;a
zc%*7~U2C>m2>RU+EsCWn&SCqqtL}0yQOd@-tkz-7INYNg7*x;aBkIT8LfnWf>x&o_H6&K`KeeYU>WX8cG1)jPq4
z-CK6T8)%`IPJr^}l|Em7oKnBIbId0u$Cqd8zM5(67AZ9N93k
zCWkLiTM$_x9+EBqic-k{xCt!LD$}^#L+Ud@nG++5U7_K*Z?)0X|ri7U|F2F7iVS
zTeNbTvQ*S!k=-aN`630-RS~yhIGPN4OCKUB;#yLWw|65-1M-s?!6l9)-Pr
z!0RRWe4Gk2)++aD5+`3%)-hpcxp@Q0)uV0Mqq!}`F1ZlX{Nr`kk7=UouHOb4RQJ8i
zC{-BA&<-W=7$}_at)JHfOxU}Tt&(JhsSYo3mjn6>1IH))L?V4ieygJxZe)L8ie8RI
z8l)&$gRg66K{^@yi-4J&>4jQ)Bqxv$2jC;sIl&{k{a79q@a7d6P^)?8J2IRdqa%|ocA*K3|ef4S*>IHQ$`vJ4OXpR<)n;_tfm_y3{EFA2E@|)vJ
z4wij}Qxy~S?l4vc^`b^Vfa!Kzws}slJyB&1pw7@+pmzb8n3TV=%FAaGXeAusHuDgX`+@ArWyW;8bw7?*)mIC*_Ar(nI~=PZm}dm!w|qM%
z$q(le870DF^eFByRgPIpqZm87bO2=IdKbxT$xaZ~ig-1_5=eP@y>_|kRa*H
zevxp9r>X&H1EwS707HZE5Pg{59DI-ajwER{v`ud%eohuVboE1gzL3>(^3@`p4JUsg
zZ~k_#BNNDJI3wi;6Da%gfo5QV_QSIEGhkP(cj(#vtwl@EtPnt{zCn@t1NCuH(RU5a
zwG=%u{P$%7iPM`b_l#Gl?_1Y~DdY(w
zOv}*GcN~E5Jb#7U@XsXv{N+TXE%krhT0l7T-kehFV%nQd&AWkbcMC%wRMCjNN!m%v
z^j(6}>~Dlqi(uovtt4#`K-iqsAbdEcw~72C68hQNw>hW}rx(ocPWY15tFhA;
zWEw?TMC}BFwKOTd5Nlc<;$LQZd@DN2{B7ND?En4qU?ma=fdIBua{I){&gR02MOl{H
zy^ZmetPr{ue2JI%;r3l?9e{0--C$9P04PG9BVGWvu?y$Q+^e6bP%wG8STMM01q_&-yvFrZjXLg0D59HNj*y-g>ir}ub{go
z2noW`FJs-xd0+IzdcO&OjQ3R7$t8jE9+N6B6WRV|r>#HsWB@Gq|2B8xwQ|x=F#-rI
zo?O~M-eYM7S`sPUG_JgG@{EuFSrD)WUr478LDm8xIzvC~q#S6$RTTHN<;o0t?y(wMN9RkYRmxUkqsKLViR3(Ohpeiw+C
zSW(mr(J}@D7=?Y^6I9u-!rRHb{~Vg&gxY3C`+U4y!Q|ztB&+F3fiQgZ!8Zuvhmj&z
zZkPI3AxKLC4dE=rDqW<6ShK=>#!auVHkJC>_9Iv5``+szKA(UW|Ix@CCC#OOl
zTS;u^=x+nqMt!=T8sp{}Kys)STxxKM0OQhgIh|8*
z{cUITunJxdUTo5F;jwk{dbbv;U0OnnN$xue&zKjl)T0TD{4>4Nr{ejUWPrG#alX`8*Td
zPC&)^;;GCC9ed5=k?2zdJRu&*P)};UdUq>$sFad8$2vr_@*Jl+c9S|9TFR?G3ba7+
z5z2e`i3Uw5ZAv-cw^BNF^%P|p6;zmhavSUXtrKT;-oy+N#wuP>6rqfb9k4osk*e-l
z^T3P)?Q|V7qGM=5XQ6-z_l#fnn{M%}$zqXGCKzFR#482Mk7Pps1S8++XCZ3iBdRD+
z!w9Ax0|DvaNGUdg<)=4m!URyrKm$Oah`cq?n9(A!C9W|D`|ZE77j|hI=5}!3zENB=
z{{fWE=>YdPnQ8;81qeE(_93mx7Nba+R+w4{DQbJ)1k*t8YB)eGtM*kYQ(TEqFc1o2
zeL@AtBgs>lnU9c|oRdrNewR`C(cmPvieEiW{9b93_UIiqI_!B*2Hq>HU$g9w?r;4I
zpEzHd5Y?+!LJf@P%HqL})dkA@kXP>c?D2}FVRNxoj*aytFViHvDc0{sO!L>_o$}2&
zI=XXCJ^u->EfxNpT(Ni=9{(_!Y9mro9Z}gkfChFO>XejjUAsBGZet-EeU2=4Fj@E^
zwaR#mNuhQm<(2GT^)EOvUFq3jYIu0;fa?h>{4t_zloV?jw^n!CjTQq5WKv(diJ#ya
zviAd(AZ+QI;&K4Ib4oY-;%&n5aK%;2u`%}uiuP^eA#3|9GS^-A+jgCsd(>xFj2dAw
z3vA}0<3Y{Xvt0$Agl29?7eUi<{=aV_$rdvj$set0h!X09axp%_q519UzFZ>l?Rgue
zen1Q|ERAT9LdsRNeCQc4`EWU@O=+S5knaFs5Ky`U
zn=t#93oY7?lu`!U;^EM1?6eZwasMX5Fu;4cE?CxYxf&TP`FhcUxYl}%IG;azrO`NFz3JB?f>KiAx
z!WDj3_DR!4C{uz_yy(#L$c3>p0ffPgAftiJzm$4>ru$dYl%bY0$NKb|L?;ver%VXh
z#>N1R%jW{Fj0y9I?w}xJcD}O%AO>>yd$-&N4x>T<&W0lbuNaRXW%%c^xB;Oz!=PV0
zNDfzc#r1It?6}`u*9XL5hJUV?)geuvGK%IruW`nw(~hs_ilJZ
z2OnjX{@yq1;T)iz#phjl6qJXz{{@WmF+;TR9%Kdu2^i~JxE$g%Z+imy$Rt4CC_5Zj
zaa)bdLe7Y`BQ>v;<3{Ex(IsA0cDO#??#g{M>QWSKV!x
zw>NisPqXkos}1z3OpH1#Ogz*{nfZX}LEye@&zcMh>ERgKY#5W2)-{YUU7q-)Ge1(K
zc7eMSpKjs~wdG
z3Od)`qO<`*qN?1gf*CV5Q+Yi_s9~2V37Ew+ZsWOI>AkyK^a6_fXp^s*DhNvcy#5sF^xKembrs(x$=;lQfoiJcJ``LH73
z758e#e`9OuPf^gyScUC|$)?uH<6D0u8oZkI%->d&!t?<-@zrf7Df0kP3xw!BKDsQ%
zaL5Km%@|uln1^2jAKTG@RBI>QM@6CvAxP;UgpFfb+n6g6fj{ol`q(JkmYlcR&}
zdG4%jTm|l|^*gbW6_`WUPCF7&3Xz=a!WA|xQjny1mOlUDXb32Txf_dckL(@hRq(XP|wY0jmcC_swtte*e
zkrxipzN4RpsQ?vVL>NdK``q)pSrvHi(2)}j`6)c1j*{KJImjRd3CM6(=T`pFPt?Fn
zE9FwGK8Eo%Z-pK+;7x2i0oH$DOsxFu43#u-IC9mQDo&b&@gRKIq79rCYRNG058^kakWM)XXy(rW_N_WW{_uZSaf`TkB8F~qJu>Fh0|4rc{H2iT61?*k
zIP63!G!7rON6N{X3VVWP;zfP>YFfmNE!y|0*uP8G;$b9rD(wAv;Kf2s4P|w@N94;P
z+95&*NZ(cpT|%=Q+Mg5C56qjj!}1r)g_U(YI52>?xys))bYg`n>^9>*J^KHrBkmGs
z5E|#$(4=jdC`(6xmD&Ax8MI{a{`Fii1`=`QAT
ze`oHsWCB^DzM@h$CJE2F+V$=R+8^pFTV$w_ncf4MWM*)#K00HDYfuqui9xm7<=)h?S?AmYQSE=Vths+aRdQu39DT{mVO4
zJ&-p@o_u50A7d4Zj=FouswXBv>*Z+5b_NeF?7~kv`2A0y)6bXKh;#O~`Zik8r9gc^
zw7em-h5SPvp$aH&aQ(1?+7v(uP2t>Wi>5ma+W}G%fW7N1!YRQQz(Wi0&v@^%l4F5)
zb=bp*4s(2A%7F=^ppPiH@R@^Wp0%636JmX#P$Ew?7ysDoio*3zD$=yBfAM8kwt+j3
zq86y2?JcVR6z*a-&vUx4w|O{H-;Va>c^qfr@ILUW1>r+|+4wxLVG0@#KoFjGTmgKfAwz<>Y2&7EZyG)+}O&4!I%X1C%R2#s}b1Y
zL7V8z-Fb9n!b%yqD|qpgpH3HKPl-Znce
zQF2Czl1r$>$(`!90A1dil1NWKAYU501!0l-p=t9X0@6$ZU4}^78K({24c|FlKiG9W^M>A`R=v#=T||Dgnj
z1k0Xoh?fS|$iGc`b}h~H3zdqY#7o|-n(?#M(~Wif0TIlL8ip7(pnteNG-gJqt;dK8824jn!v{
zh{hC#=6%l&_;$qxzI}xj4WPkN#tmfSp3e*&;CKmqPcQ%Ne*H(XFewEqGRgDev(g;x
zKx^R(5dV2e6Yu5d8m$DROv4QgwsM@6N;4jI&i1ri7>%4nby@dM>P$u2|ISN7@)tE|
zl>NhuO}g6LS8OH_>bs_3ChJ;SbKK^qA>~u4J8OBYbg~l*cDOHF>)|61b;m2gOdkO3
z(=pLA?C+HBH|*4kElo%jRjyWVgdwcLoi7yS3qEc^9!7{qwxoQ$vWGV3EkzX@@aO(f
zrO{5=21%dTc=^SVtjQh_(S-q6l8u}FEzEnqzIW{0aLMS!lxrdytvFp%Q(U-Zth~!I
zZ;!Z~#KYzvaa!PGz|a;B1jifsL&VqZxT=6O&4^nBX9N!+#;46%&o14!*q15^h>}Rn
ztkcjWyxKIz0Obq!e}@&eQ!_~+_iDL|~y
zcUM(6Ru2p)c&_|XQ^LOP#PEJ73g9SkX^#*#4VYksJet*0kfmlN7W5*hE74Ll>C4RKY
zVy)F#?6p|(I%#%e1$%zTW@@sAZ&H2wj7ho*^znkUj%|
zgvxt$$MZCB2|(Ki)>`jkDn$2Z5t`fjQl0)KbQ`+v<`D7ckVm!)EVFAAQ-YR&bhkV8
z`tXE>!rS+vUUp)|!R-ny!etzOyG=?RkHC*vpIJ@87P!zI)~xhL>BuBV_ul0)4{*f-
zcS$V9^CB;C!`ALVq3y92J?62t>LE`<<;X)
z%%n>G5!%Sn3tam7so|gUD6Jg4dH$}c{IOF^KTs)cvU*yd^|K0rc4Fq>ARUqk?_&*+
z0s4DhTuwc(@bb(}OO_)zEK6Ky64~}Mt>xW{Rf;b3JLKuSn~}`!4Z&Pmqk6h_;n`1T
z;vUDhLsssI0Kk0pajzGk-(!lh+YR7--6i|nuLYxCRf9YVt;z2m_`3_;MjSem{SyK(
z{vqGnFPznp%uV*lb#Je3!xFp6m`lm+@lG+5J|sy#atTn4;wfWLyhdb;8yv&kdxvDf
zzVvLF76$+Ik4Kxf)RRV9&x|m!(_@&c5OCj(TISLvWE`6KG;SF7S@?CiL(5mKUzIn)RXrJa2Q9
zNda?c6~JQ=ojLN^^k}62;|j)`VQ}zc4$sM$VdTI~*e%$-G^<0B@^!HeDHh%@yiSq}
zJ;J?}0>FGZH4Paq^4lBBsEO1}Aepl_HzR($yDqOcOv(ujt1LZN9cW10-Yj^fT8jA$cFw-821n-N4xMgdR
zf=|gWEbQU!B7A%ltNWHpr51d0y9*mszs|+KAN&H$QHpwUeHpS~JftwO)s=Qhkwoq<
zme;~ps*{@fDk8Hy!A*?TFWjUM8O;=HO(GG1=2}Ne551kjXIe)|AZ(49`_tY21Av!|
z&)YG1i@Ig{mg5!hrpz%|#mZIh7K@G*Luza*@%>Wkv|si8eCH*zXPCnnHZM5DxArFE
zDArBaV(bmV8^*E$K+_2nEpy*e}
zlo{xk^9x6#ng
z!q}Hqnx(9lw3By^f-eDpX8MB5TPK7X6TYF>U-(gzvo8A4{7MzUTg-S-08jBv@RG3i
zcJdECi%Ot}*J8dGpNPAO@U+h#|J~dWk;f|G9BnSlni%QUy{&pQ5lKxxyzAwA@=F3L
zqs@GrpM~_-n1=_AeS(~fiG5}sx|!7d-#8s~^$Sm+lES+-j>L6OGlo1CpH){~)8<*EK`R5+_ryIPd7quU?AAX1MD}cJy
zMoZT|Vi3=ghMa%T96Oj*QboolPSU3R$%JsByd7e23*3Of;msUl&$k9hQcnE7yQ>XxR<_PFdAl-S~_j|s&iDiw%(EmdR#-~Pvy2<|iJ
z^dOE;MNb%0^pnE!-;k?Ij2f913_tv#ox)vsn!35u_CI2oz{-c+$z>u`%uwmBCGahuwMgVl-rXBl{}AnI8=e
zRj43zO3X2&e#7EONTA+T9N}WPhWMQD^DT8fxJ`MTg1DU+xG&=l)44;hvsxcmp6vAJ
zieyNbtZ(XF@Tt3BV~-{fv8|2cDH}qB>Jp+XM$%GsaAm@*XkMXB(7?>}`tf1XQGyJr2ok5sOVHf{LejWc|L
zX^QmryR7pKz_#c)hD*I-TA};>M)%Q5l>4bJ&E>IA;j*!oj|=gp*G`0LnsRM7Kni^z
zo~US3A3&uTV*g%}X$3#3yOp)yaZ-t&l@yZ7w4qpe_GkK}6
z|>``@X81AdKDk$M^y
z+_-hz{-*RV$A@A-6$R~!@_q3lZQ48wNGY~I^^5Q`9oEzR5~M6o3zHB$+Cu+bSEqaT
z!ob;Oa5^4|ZW^Ie(ukSsx`4NlM%lNF#S5VBRPTJ>$)CV5Z_{Spl3)ghBUzWC1mV<@
zP#|M8ZR3Djy}MHI;0k&3Z0Vq3vUkT`#Gl%sv}QEnl^RV+ffF(X1}u^+r)u%i)?-
zz)Mx%pg}XPl)awAyPz&Wryt%9Wd
zKdDz0ZW1W`uE-rzvf6r6CePMtCF?!?o=AgXWGM8-+JZL8v)o|%Ew`6&aYKR
z-2BY21eN$7vkNr7J5ugl(1*k$Hyk~o*4qId5`K2r$XlFPd`$w-tI$cnRQ__x3VL1=
z*%0RcUN#VCcWgld7uTOoodYk5ogmdT^`wVcTN33{6v+tF)A)pM%U65};fGUb(88Nk
zPpVozi6`z)ZZAkd+rQYWrg#M#;%d0-!;YXYJNh-B>vr_ppMsW9&3B)f
z!D&c?lLlWr7$ey4BijIeV^H4(yK&ev!38O=9xRr!G>1llfQ#(9Ypo#&Oja~x^UZw>
z+{4TDrJGhFy3Fm)Rc!?TT7bB{Z|?qZCHXZ4KW_~nZkpcyjbmW19I&-c(nXeV2u{
zQbQTlzM#S@qX5EdE5Cz_V}td0_E6uLr`B!RPO`fYK
zAD9D*txX5rnP2ADkEnn&XS%vuV9{BAJRBL}IK{&!gS(GN(=scLaX9R`q`RK7!TZQ+@JS6x6eyJi+bw^b_ra%;+G!Nk?6!6S
z3}45bbOX~d55T>w0`1%nzi;&D*tq_{d|0x&Poe(&t1I1D5SYm#h0VVN;VmPN+`Re0l$e`=^VXIvl7D8h{9l|zSwdiorfdcAq=+`D9)FTj0WXjUzT*m
zY6Lo}$Poaz<~h!58@ZNFoB-8fgoC#F&$A6346~0
zZ-=u#5@$NdBCX}C;mO1*MY_Mub#>f6$1;hbTd~TV{
z_XumAqhe=9gkX6uef>hlA;!v{20UG>nV=S_#KiTdM`^>s)$m{|tF*LM0L%8a+8X!e
zRuF)w$uq6869js?8lC1BiPoYciDLq*6nbxC?vv9%(5NSP_whH88viD9EVYOsv9n4*{c;IQ|JXt_d>~PU2GRf!3mhqfh2G6n}sVc(u)aTi|qB>(R6Ag7v
zL0K13$Js`%fns3ePBvj%9ROK
z`KFBUhSKpD27kO>Md{#^3V3Tc(SG);CaH679r|IxWG7BgHp8HlNNQCZH*ZXAuP?hs`Ky#tWWDhO23b^pq8OE3Xh&v!O7r
zIl^85bfMd5r?4U-#HzTz!tFx2ta6I2)Wi4#UtR%6(n`X0;7S*dULnkI@^sU}Q133L
zJcGh;uo7`z^}h9LQ|FR47|sg%6_F~kFEu#~q^dtcv0h9~m9X65=l9VF?(a1=udFeCjA|kF8=z<8{m!Z3m
z#@ZfuAA5<8xHptqMcA-pB$ITV^EPL0v5;&4lFc#X0*G|qt7FhmX`(LDEJqPWayPxK
zPT%#GJJKdsX`ZG=FQ+884kUomP#0ey{qEL4z)RN&LtGkI+Fm38R_N(E)!3xnIm*S8
zio9+8HOXQ23Ug)StRRd9^h4PFEa>*2QCGjHwn4(Ga}i%h7YY7Lhw^;7ruk1F-`#ev
z=3RzEjBF0Nt8-(*93FC*&lb#R!C)u7GeTGKjrHY?PdrE#iUt*~%hf!Lw}uZDgSrW0_@Wg|T$Vf3ATUQwM?a9A%=0%#Td@y!<`Ir5@`RIX&>
zSgVGsCx%hS+?Q_8J4z6rY7q`(kPt-pVf%`F9jXB05Kx`i*ev8w*17g-;+&_&$u5nr4?-elwT+}q
z47DPPS^h2z;EtJDZ>d(+kaJ|Y$*C%Pp#W#qCeMGTg>=0uADMeCsmd5o+>s5zU+gtms5I!>tu{zbCNy
zT3Ixt7id?pK0oMzjPr2r|723E9O;v)lRv4x87FRT*!MSpO@(lA6
z>KSn1-ydS!&czAh+>TtOC?RK;O6(2-m#IhAg^Wq}SN=I)R?HYHC->MnrDOv(F5<8~
zS#pFnIYyTG^}q)4$MglazM4zjB!AgSU7ZD;Po&b*Ycbl=1A>~eWZ_usTCjbl?3Evy0n9VV
zntEcP^pcz#uSfEvegAl^)i~8Jy_rO!e?!I81zb$LPLZ#nB7)0U>9zW&qbUpNXmC3r
zL3Pd(>{IGVve~4!2fO8s2S!`9!6Y#ObZs$$wPajX`J+nUQRdz}@*#%h$6T?c9
zJg=LS^08+^NVC)9Sm+qTh(M$ArXWgYQRLiq4d3hH@eV2?5~--e<;_Ba`tN=nVr9&Z
zqiPfg=dlXWm0$ua>kuzLNRBA=-dfaWJ^V`j_>QCKk0qqv$b3^2nK4}Md*Z^dU)PyF
z&LF*lV1Nkfs$(gwJZjP+Ju`H&9fO(^5|cl$x{hznRbq9!(+`IcB6m{?>wZK+BooZb
zEZX@98uaDybKBUMXD@q{(j>5rLS<5{h7c#r~3}`boc+3Vnm{D|hXY
zDAfZ<%XgVn;!E55l*I%y29QqqkD{x;t)kteEKBUYXr!R_RTNA#e4i2^99;2np0^|1
z!F~59OQR5E5pQ>CH!7)EqCsQ0)nh+c8k?{*#8QEAppPvU{DyF-3AG(DPWx}FG=g;9
zDs*5oYZh^u{VnZmVk)7AtYR-))f_RZT-($7-TY+a&dbEZQQPnlDQNUggKb06|#eC$w~(yOqfz{{2imcK&s3klGmX
z($Zto?GFLB7#2_*cLprW^I9eCLl4i3nv%Sie-<|?*-1W8-7ak{B`}C5FZD478ko2e
zjNu?B>QDcyJ(f_`-FZ#IKQN=y*LW#4F*JjO+uG47UXkYh;RB+d2*e+*)9GxYb%4ve0
zXD5AetPbWc>Tb%AMkC%>usicWGg=*8X}3QX)4}D}*jClGYn*Z({uz;MXXNQ4I4s-#
zC`pCf;pliR}JOb8w|jnqtt
zlQq|OGt?XBt%sPhGdCG$<+b4jjK(jDDFTbS(uGy#E@0);*#>Z^D+_adW9Ddkb2
zA}vJiXwPc}-#E8+OB&D-iKzZ|GKQl(oLrhc^DUy7*{)$zH#PI!c|ZVSsGYQCC$c>^P`Tqpdt)53Juhsp5LY
z8n7w*EHii;-1ZShLYXeoK;o1vnhM0C@kp0iQ1WdzqNZpyGw!m*Qbdck5o$
zc{DbkKo}@9ST?djuYa$bYap)|xn7s-Z2TgD@K%GTP5RaITSf)<1f;$4U^+AygL9#$MX
z9ouu%=sQR06~;%_H^!rC#9v1n3ATW8qBo~SS7wzT&P}_Bw+dTOfs3*escfmdHLP-`
z;>u|*@MN}ma4?~Lz%#cE&hyOLfCUuaN?D=D}m~b`6p*1r)AQ~+pAa1qbNAAuA09!ts2BI0PvxDJ@3B2^ChCpVqU!*!EI^B>xDC(
zB7(HK!3u1=+%x+0h(GOvTF;$^w+6c$k@ZPXqaG3HD1q-3z}lwc{V@S>7}tmAfOY5R
zM1{vayPXBzg+W;TdG+@%ui!xcj+e<+jR&|`DpU6L*9XVlr_VydoPwQF=Bc-Ar7<}QP|jCDdiS>wK5*!utkCCnEnO<
zaI|jM9=m>C=Tm{SKj)S%OFrZYSr5oi5A^1M`gzS|KoS3h0)ujZHLLj#X5+%Vmx>@y
zv_|?mp@p7o-_`|&{5=XHxDglr7F`+G^5f|7qW}W%ImUum$wEih;Ts0NDN0Mx00D0-^2t9EthbvyjQRr*sJ
z0Y=z`IzqaGXaz`C0dx}DT~`yNngcv2amV0erP@*OiYfx=JK(!NFuBwM4h^YZAUUJA
zgaOncX`HHR{}Lg!pxj^T_SbOS*zQ(>U|>NWWPRseTb82=b8w62`k{<$Ietc}RaiqD
zHZw~|2V59LKSqON^=WQ7|HFj>h$zYRa6D({5*QN
zK3J36+#c<+U>W9SmPB(~1xzK1{(!e=;vPUDLcqNqYnu@|d;}$0oa<>t1l0Vvl%^YB
zC^vTh>-Jh4_RJ13CK?Dck{HLw{t0)FZm^9q9*jJ{UHV4(F>?`_4w>i5T7JD^02LY3
zocynqLoc!M6V3Va*l$+RdE~Hcp8bIJY&@Xi{(X6V=h2q%>ho`bp`S{O;x4JFI76F{
zPsHSz|A6c4X@sgVgc(P*LZYTx=+$R4@O7>ZxKH;XEdMjNOLNV
zA82Uhb>#fcV1{O;o8*lx)x}0-p#=!@__d$!N6dk4rb?0xQl@mx=ZnSjHCH3@=xVO#
zJDr?O9z$TA=0fZ^1ECGV9Vj#!tLPX^_p3H&im6+sSrb^MrW{f`Qtk0lcg?(H
zAhF+QpS|g5r~}?n>v=n}R?Xr{^VM6To{Ce2zadXOlAu3d
zXBveJfbufxIutOI-;=K#r7wQ@O>065lwiH!mvOB=P6e_aqM)T;AP_B+ioA^e#7h)4
zB2axWFdy{ug&(3wHxMS<7g`?t*zEAD{a_Q1O16bqmOo0NB?2_-(30^m-(*U`ix`AaXc6O#UMII4Zn?|Wj|zbs}J-fMJeEu!D!Q{
z0x6D~_yp$e1nF-NNPb6(MJ3Ct0Zy8)pm9QjBizdYQmLsWUac15e?tY=RAy8@fQ|1@
zp01dZoPPlyRbK4Z?ni=7#8lO)W|$SiWP-B2uYhroR0q^IFDx7F%#NWiLnj;K{Or=J
zUr+fsJ{&&tbhg~{MN?yy0l$I~l-ssE@UTR3j!eK;+`XwfWwAJ4jDwOU0FgKk=+_xf
zHTqcpWS)zFnt2pFay)cLgaF(ZqP*2eU{Jro^v(aRFMTCnzp^L!{1u)j^$A8#9Ijoo
zA?{O-q}}IrEttl?Z!7WHCy+2h5MjsTkAt
z>mt8b2&c+L0tc_c^F#0TYkZu*j&ic4zHg;xTj{4S@xxG!<6&(InqF5@{SC8Yj(Ux2
z6`YIT0THuU^{tK@<>43X|W<qqBgz0*jNj=)
zw+FqD`*1>IN^RXYtSnsNhErL!k~pN+Ml0=e4v;lNlqf0;#+07@P4f`IBXnSd*`f9q
z-UyjcLWxbsTMMl6${5PZrkBCy6WUoC<;dvBpulz-X+3Db{WZI%R;{^)Oc*Yv89x5b6_;kX5Jb024?;b8hiiJWZ^tZ98CV0Gx{kkn*M5`D}4)+C{m;|Spf9(9M9L1yc
z2MyFFRkD;iAckI@TR@AWKQ3FwyWAV0-~ROV128+fF#&CTPSHqCt1daj`Ku&zu?m^3
z%8%zr@kQEclN%kJRaiv?X0}CFm#dMT@SpKD)HU<#!pI)qzsS8W72n0cY)AeS*rJJj
z=Ks|(bb95+a%o__go{h4dbryhALm{yz(UJI%KHphT2ODf&}%|4@F~z5TyA8%2y|7^
z34%bW+5(8j-p}r$SoaZTH1PpWXSh>}M<3DRgkAAxxn}hL85p0vPEU(vJn&FLfpF`Q
zXdulpi(sOGiha}_3NXkhhYozIhW6Lv%-M92+Jd6R0?6jZibBz0iFiyxq5R(m=aU6|
zYOPH1LOG>#yTz)WHh`kY!)g$%nOVHSqaU5TaO8Jf%h$TZqU4yrC!zhN0P-<38D%R_
zHWtrZba|t&`B^%`aQ!{09&*ZM+YiH%xv^9anbw^Ra1=J
zlFpFdDN?ls>TKoQw3YAJgRd5B@Jj4>2$XAo8x47rlF<&mIXZr+cFjyjGqfK|L83KB
zEH74QvhMWAoyX9DUoA$wt!7FmJA8Ghp4~0RB#7ZptA3GqF$z>9P%`W=8@yf+7yR*q
zD^;!G<Z^E`a8`~mk;x7ss$lW-0Mp8KLm~^i*FN_B3{8e(CE879x
z7#P4mFwn)xNK&7c2}f&fkf(=>f=c{hbH!BM+}`~*x^1D?L7VcMWn5IVvxfsS}=G!F5zN9)bEPpG8ayN?!}c-b3S
zdN~!qym4u3R>31Jdx>OH5M5>U^R|tTFP0wzlzf6UEMXuA&k8X=-?C4roK19}@>$nG
z1519OEtnd)`FA6BRpwVygKy?^BTn9pa$U^M3}k`+VKdxglkE^AyK?HlzL<7@~n$ypQ^SwOx5U)Ze@R3?_qQ$R5U$JzLq=
zA%w~n$-YGlGWIQuEqhVfvQvzGiO9Z0q`oM!53*$6jWEpp_};&Mf8P7Lcm8?)`kXoE
zInO!gS>F$i5QAc`6#*~ANI@~mcrd;Xf4aWbJmGA*?@9m5>s!lObO8k2nX)cBWa
z`OUjO-oqK4g>FgL$H;4ZQNO#SWL0RYP{R}MI4Y$(%^_|y`cgC-RJJ&*xBnSaFdQ<8
zh&yd`nvEl?AAG^B%68e2<1%7&f2XYgm`g)@`tQtG=||QcG73Zra#;}A_rT0}eG2DY
z(dZmbZt=J+vWTA8z6E@FeVqGkha)J3wpYzz4)uMk9izrmht#aB3ZZ8`B`4GI@=RrH
zhx&cdZ<33TdX=K&nG4FrsFVl>3rmP^@!7pj4@CMi
zZSoTA&3bQw;39j&si+<)_uy(yXo#k?ElmAZRU-b;WypueD?{;hfV%OvX6CA@RU`=p
zUbV&`J)1jb`9||fAmIlI%-U;2fs<_G=>t_g8Q0`w9o3Mrp-Xwe(l<(97|$}NgFtcq
zE-cID7EU5Yf9M7$q#ch&>yR2_(7ToF{D+`jb$ggRt1D6SxqgB|-4yRpYV%tTcjX`p
zRQaF$#zikl6XNY+|139}K4i)Ux(6g0F2h#Si_8rusO_uXr-AABV8bS$qno
zqO4%;Eq$-Gm*3wauIro~f4}M2YO(tGGzdrVaMv1b{$3pN<$G1*r;ggeF#K(vV($?|
zhxXvinPkftZ?@pWoRgU_Z)W-Mp*Srb(4;VeLT8)@9oi7M{c
zpjj*GuR)}lmf?8IiC61KHu(zP^#iqo5~of^yxDpOf7=Y+!hLuzC+n0VO6Vl$Jto^{
z?>sho=WVWdzMJI22nD!);dSiQv9A%T4__@s+}^Q5KZ)R1dzCps0Y~(ObxBZHB;j<$
znhEP3Gb1Df>XvMDjK_eJsydC#ByQ&f5Q+a}Y|7Dz#IE6Lb63S)-!Jy6F|}-(;O`=_
zT?v}CpmrMf+iwlnW;(C5Z{AplgTdqnAJhrN^EwAzq5Py
z`QPCA;Fov><}-m2C#8<&vJUFCs_6Sat!K$3vtNeuUuebgWmt
zb^$q#l8gSnm6Pxr3+1*5EwKmW{-Uz4Bfyj50>ykB|FT*qF-rYViz=HTDg?dJEqOBw
zGb7?DAlY4%$S7c~^lexWqbBkvO7fwmSkV4kEz6Zy-sX;im%k(%d-5p}a!$>O`PI7>
z=e}RP-H93LPGCn3uGxS=v&=eEJD%vMEeYYlqLJwZ38|J@o=q>OKVXuH@eX{oIFtXhkqI-@vu-FJ`x!$_4#sgTQVzbCcW=%vXDGjlI{fTb+Dgj1!+Q;-VNV(Z*=W
zsYS)byWdv?k&AJNB4ymiK9j1@8nr`2n9o8Yju%7$*X)#+miEr^WDH5D%8B2
z-%z!#1K2FwF`M7364mZ}H2W~xd?*cUM94qyS?AC7jV70ro}Zh{ZShT}jgSM4^J#RQCoFhgS44Tp!
zk#rajv)+3(L>;CR=+|~C5i3#Z^2yXpI+y_>+i!A|b`3{>Z~DWzE1Y-Coqp{$??AuZ
zK#q}Y$yl#DB!z?_$hIWv!n?Y|r8_R37=9o`R$t`7Kk;^+z6F7s?o-e8#ATr`W;gug
ze5f~#$3KJd28X!ZY%cqjk2#0Jl+!+a4h7GdDa4KO^9;PQWvW1A&M86~qkk0;Yi7;4uf5b*RtIupIP5wDg<#h1HJA48eYG9;dv8~ZCz#1lzL$t
zblKT%cgo-Ip|r0_l#3QGn@Po~ut~y@+{rL|74(`+5;QSTjg1@+EAZ6UIW
z9BHAGP)Uvv{z;pK*I!aB%r4p!`jEvwV?}Y?eII`OEn4Mou?`#b(RfmU{h+J}PFAV=
z;dt8Eos@RmB}1I4t5aMS@28C_=^h=^Z<*fQ5ZUamzTQk~O4{u6@4jA_q)I#t0di8r
zH?5B4m;>5-uV1JdkwnFfo!4#AjW|vR?jE7Hl|HKKyMnq+YLlZ(==1>mYnnCUsPxuz
z@Rg_Yw&58w-_Z{!Pb>aXkf3-=_keVU!lMwnq3|
ziItzB7VoV2Aw8jW`e+jw^|Xff(icg+5r5;yu@=&ffWJND85oh~hQouM+&^m{GdIq5
zGLt8>m@GcbNo$XWktqs*C^~X<(6|28UpZGmXQZ)=h4b3T%Mscw!FQk4ct=&idt|q9
zoTs>A9kP>2AxAC$Jeqb{QV3)wNV9Ip!dc6m6s>~|)v&Wzws7gzYlOM^o
zkykQYNW^>cj*%JFnJAio@dZW|!uE?OK8E!!O$J-TNP;cp!e)%6%OnxsV`l7LCw}Ss
zqg{?pJ^;X(i*b{Z%MsdeFu}$3#J|hrB=2%LU0WC+F;8v#UL~(4d@`P}5WW!c!7G64
z6|pbutZ36`D0_p{DF?+?EBD4u4K!-2~sXbd2RTt3XjcehvSbglT`?(~K
z9+KXQ9*dWQ!Q)b9dPTx`OEqYbppQm2?7s4yCy!_p0nl6?&G33+AsV*=Fw3p%eyaFK
zC-9-U&wVR>b^H)_Lz4{yfQ9yz^!<=hVV&OGJMl?Y+wn!^&wS?nf(6T0?+0x!_^pW>
zEMQ2~86k9(fG0N@pi9_NYv=ubCo1H8ZBwQWOLQ}DXr%rTSyJRheZB!=E|%j9G&bu)DgydBD(gInpCHLrpA;
z%=S1Qw_#%rH>tmh&Uq27)O*m?!aEHGD1W0%NB5yf;@N!Kx>dWh&>xALtee@L51C_N
zz
zBdGHE6iakDxDSU5#Qar#*1+%Ex-)V|f#Y7<52~LeZ@G~=AY|4urNQtFiHDQXW6m`V
zCgjD5y0n-G22`e>6C{*qezkXIkyCJzWM2D4
z+MxU)`%0r@jAr3>4LsZwx44&Y)b&bIOEe9D#QGEQ$ae*h+APqCY;J6G$ws5*V
zK2Xvp?Ns~kh23a^{pn&oY{ZBb%38KS(A+`v+yQ#`L`SGpy9+zkqvfR_)N
zEl^o&?LS!LkKXgKeGK(8)%fIm+Xqj!B6(=eV;P}_^f&dt5i(jyY9L0b{-tuCTn~0}AcdBQryDFJcIR0%w)525Sq~
zT2SKiJWkG&Vl+&YNwGYtBBXjm@PbO>B-MxuEOhZe-5$qZrvMru%MTrn-@}HU4*6nD
z2Iu0!vUPv1a=5NF5Nis1-KnvJw`>e7?8lx9cXTLb#9Z75#gFvB8=OKK9UgHyyNcB+
zW`TkwXUc;wXrv(Gr#6oq=;fJiUoM{NifnhU;^q
zVIQtJJ`G3dQ14rB>KQW7qzNWv?%yBn;a097Wk_=Y&UYzI~M>v)|>u=
zVAHAMnA_qo;F}MKljZHX(v7};(5!O8^;bb2orqn<_|&izs}hJu%1h4KAcH;aoN`@;
ze3o38%kZds<+QZ}g9_$7d_C9WO+&5iUp9`J6F&s-kew{F)b`NvL+9$Fwf^z}&o??UNX%k=eyXwXxc!gw!z
z_j7SCYF4kPbWM;U^c*y)rF{If)FHiij)?V)(3^Zj_FMErP3Z*ojt_qi_?MKu2<)uP?6
zof?8xm{mw8>u@2~^LkzTZD{WkPZTd0&&2ur8R6f=4N;CPzdRCM7htp|Rd~h$%Ll-~
zs_M&jT;HPvGTe#HygCd>=gi*`li*S1Cvv<;MGLt~)T+iUCB$+mJtX<^r72lQ?$}(!
z_1`bakk-xir-&v9n8H4iUFFwh1?0hL+`I&;e@+QVT!Wk)__iFasJ^V6NSY}QsUydO
z$%Lvr!N(UGF+Y6#Bjd=Dyc-=DGE8M^S!Eerq2|~t29V>Sd;jF=41x$4r-)C#drCTI
z;@5Buc%xqHQ%}p3dZuGuYIsTO9Y^h^tP>er_5(ftHU0~}TU4sdCQ0A?vfB=af(3`;
zr?^m@K8uDkgccC+ogzLnwzNPo))Y3_<|M{5dR?!BP6mj8DtBKV$Gzd(#P;{lkSE;6
zc8=j+E4~?m{q<0X4G$D|uYz8izCl6Y0E37J-jT{PWiENU{yDXquFN?U*DV2)csF9@
z38(t^UfSf54onF!9s3zAn}F4PD`ugDOX$<}skNjgs)GamY(0I@E4Dg$y_uvP!A-G4
ziiGeJNc3lD%$HoGKYG$#oY$Ce%AJ#Bp$UJ9v&vn=`pGk=e*BRSF94^TtwApY?}d(i
zp1wp&uFeQPUyCE#%!mHYF;_z$UZ7Tjp}UhaFtxio%-aDhZZTq~x_?s`;!LNmvttQy
z-C`c{Cj0?)dRpq3#k?}G#@M)MdqScu`Bjf$S#4$^F1ULYnGDAPC0fHCG{#XdipO&^!8f^oMP_He)pHuo)ho7*WQGZgXF
z@x?ZkjV^?n`AeuKL;K7ys~ULLxIR(=k?pok5d_6@=i+VZPf!NJPM6C0{%4mnyXpun
z&0kU8-Jd+`)bMT}y>9V>mouCW2AR_v+JYg@3wobz8Y%H4!_TE7FYbI-GM`I**5q~z
zcZBBdePaR3AWB7NHXiuN0nr!Bi3z<(v^1PsE@d9v``Lz9#+F9!bKv|As-s_TUx;la
z8Oh@G)gQlmBFX3dU4P_=H%%-YqJMm;&MagLEkI?IKA1v?4xOZ445pQ53YJlufp9b)
zUSVI$`aSNNsh0ZhHxI0Sx!#K^AdV%v%k{1qvfbF8&E5ps0i7
zf(IahATJ0wE=Vqx0+RpjC;!jG|HmT!?`QuX!~X}5{wK_#O8^^e+w9ZvH7mCQ;7?lv
KG@R6I!v70A?k)%b
literal 0
HcmV?d00001
diff --git a/app/src/main/java/au/gov/health/covidsafe/HasBlockingState.kt b/app/src/main/java/au/gov/health/covidsafe/HasBlockingState.kt
new file mode 100644
index 0000000..21c5fef
--- /dev/null
+++ b/app/src/main/java/au/gov/health/covidsafe/HasBlockingState.kt
@@ -0,0 +1,5 @@
+package au.gov.health.covidsafe
+
+interface HasBlockingState {
+ var isUiBlocked: Boolean
+}
\ No newline at end of file
diff --git a/app/src/main/java/au/gov/health/covidsafe/HomeActivity.kt b/app/src/main/java/au/gov/health/covidsafe/HomeActivity.kt
new file mode 100644
index 0000000..04e0f08
--- /dev/null
+++ b/app/src/main/java/au/gov/health/covidsafe/HomeActivity.kt
@@ -0,0 +1,15 @@
+package au.gov.health.covidsafe
+
+import android.os.Bundle
+import androidx.fragment.app.FragmentActivity
+
+class HomeActivity : FragmentActivity() {
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setContentView(R.layout.activity_home)
+
+ Utils.startBluetoothMonitoringService(this)
+
+ }
+}
diff --git a/app/src/main/java/au/gov/health/covidsafe/PeekActivity.kt b/app/src/main/java/au/gov/health/covidsafe/PeekActivity.kt
new file mode 100644
index 0000000..bf2bf8d
--- /dev/null
+++ b/app/src/main/java/au/gov/health/covidsafe/PeekActivity.kt
@@ -0,0 +1,147 @@
+package au.gov.health.covidsafe
+
+import android.content.Intent
+import android.os.Bundle
+import android.view.View
+import android.widget.Toast
+import androidx.appcompat.app.AlertDialog
+import androidx.appcompat.app.AppCompatActivity
+import androidx.lifecycle.Observer
+import androidx.lifecycle.ViewModelProvider
+import androidx.recyclerview.widget.DividerItemDecoration
+import androidx.recyclerview.widget.LinearLayoutManager
+import androidx.recyclerview.widget.RecyclerView
+import com.google.android.material.floatingactionbutton.FloatingActionButton
+import io.reactivex.Observable
+import io.reactivex.android.schedulers.AndroidSchedulers
+import io.reactivex.schedulers.Schedulers
+import au.gov.health.covidsafe.streetpass.persistence.StreetPassRecordStorage
+import au.gov.health.covidsafe.streetpass.view.RecordViewModel
+
+
+class PeekActivity : AppCompatActivity() {
+
+ private lateinit var viewModel: RecordViewModel
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ newPeek()
+ }
+
+ private fun newPeek() {
+ setContentView(R.layout.database_peek)
+ val recyclerView = findViewById(R.id.recyclerview)
+ val adapter = RecordListAdapter(this)
+ recyclerView.adapter = adapter
+ val layoutManager = LinearLayoutManager(this)
+ recyclerView.layoutManager = layoutManager
+
+ val dividerItemDecoration = DividerItemDecoration(
+ recyclerView.context,
+ layoutManager.orientation
+ )
+ recyclerView.addItemDecoration(dividerItemDecoration)
+
+ viewModel = ViewModelProvider(this).get(RecordViewModel::class.java)
+ viewModel.allRecords.observe(this, Observer { records ->
+ adapter.setSourceData(records)
+ })
+
+ findViewById(R.id.expand)
+ .setOnClickListener {
+ viewModel.allRecords.value?.let {
+ adapter.setMode(RecordListAdapter.MODE.ALL)
+ }
+ }
+
+ findViewById(R.id.collapse)
+ .setOnClickListener {
+ viewModel.allRecords.value?.let {
+ adapter.setMode(RecordListAdapter.MODE.COLLAPSE)
+ }
+ }
+
+
+ val start = findViewById(R.id.start)
+ start.setOnClickListener {
+ startService()
+ }
+
+ val stop = findViewById(R.id.stop)
+ stop.setOnClickListener {
+ stopService()
+ }
+
+ val delete = findViewById(R.id.delete)
+ delete.setOnClickListener { view ->
+ view.isEnabled = false
+
+ val builder = AlertDialog.Builder(this)
+ builder
+ .setTitle("Are you sure?")
+ .setCancelable(false)
+ .setMessage("Deleting the DB records is irreversible")
+ .setPositiveButton("DELETE") { dialog, which ->
+ Observable.create {
+ StreetPassRecordStorage(this).nukeDb()
+ it.onNext(true)
+ }
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribeOn(Schedulers.io())
+ .subscribe { result ->
+ Toast.makeText(this, "Database nuked: $result", Toast.LENGTH_SHORT)
+ .show()
+ view.isEnabled = true
+ dialog.cancel()
+ }
+ }
+
+ .setNegativeButton("DON'T DELETE") { dialog, which ->
+ view.isEnabled = true
+ dialog.cancel()
+ }
+
+ val dialog: AlertDialog = builder.create()
+ dialog.show()
+
+ }
+
+ val plot = findViewById(R.id.plot)
+ plot.setOnClickListener { view ->
+ val intent = Intent(this, PlotActivity::class.java)
+ intent.putExtra("time_period", nextTimePeriod())
+ startActivity(intent)
+ }
+
+ if(!BuildConfig.DEBUG) {
+ start.visibility = View.GONE
+ stop.visibility = View.GONE
+ delete.visibility = View.GONE
+ }
+
+ }
+
+ private var timePeriod: Int = 0
+
+ private fun nextTimePeriod(): Int {
+ timePeriod = when (timePeriod) {
+ 1 -> 3
+ 3 -> 6
+ 6 -> 12
+ 12 -> 24
+ else -> 1
+ }
+
+ return timePeriod
+ }
+
+
+ private fun startService() {
+ Utils.startBluetoothMonitoringService(this)
+ }
+
+ private fun stopService() {
+ Utils.stopBluetoothMonitoringService(this)
+ }
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/au/gov/health/covidsafe/PlotActivity.kt b/app/src/main/java/au/gov/health/covidsafe/PlotActivity.kt
new file mode 100644
index 0000000..a8febf1
--- /dev/null
+++ b/app/src/main/java/au/gov/health/covidsafe/PlotActivity.kt
@@ -0,0 +1,231 @@
+package au.gov.health.covidsafe
+
+import android.os.Build
+import android.os.Bundle
+import android.webkit.WebView
+import android.webkit.WebViewClient
+import androidx.annotation.RequiresApi
+import androidx.appcompat.app.AppCompatActivity
+import io.reactivex.Observable
+import io.reactivex.android.schedulers.AndroidSchedulers
+import io.reactivex.functions.BiFunction
+import io.reactivex.schedulers.Schedulers
+import au.gov.health.covidsafe.status.persistence.StatusRecord
+import au.gov.health.covidsafe.status.persistence.StatusRecordStorage
+import au.gov.health.covidsafe.streetpass.persistence.StreetPassRecord
+import au.gov.health.covidsafe.streetpass.persistence.StreetPassRecordStorage
+import au.gov.health.covidsafe.ui.upload.model.DebugData
+import java.text.SimpleDateFormat
+import java.util.*
+import kotlin.Comparator
+
+class PlotActivity : AppCompatActivity() {
+ private var TAG = "PlotActivity"
+
+ @RequiresApi(Build.VERSION_CODES.O)
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ setContentView(R.layout.activity_plot)
+
+ val webView = findViewById(R.id.webView)
+ webView.webViewClient = WebViewClient()
+ webView.settings.javaScriptEnabled = true
+
+ val displayTimePeriod = intent.getIntExtra("time_period", 1) // in hours
+
+ val observableStreetRecords = Observable.create> {
+ val result = StreetPassRecordStorage(this).getAllRecords()
+ it.onNext(result)
+ }
+ val observableStatusRecords = Observable.create> {
+ val result = StatusRecordStorage(this).getAllRecords()
+ it.onNext(result)
+ }
+
+ val zipResult = Observable.zip(observableStreetRecords, observableStatusRecords,
+ BiFunction, List, DebugData> { records, _ -> DebugData(records) }
+ )
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribeOn(Schedulers.io())
+ .subscribe { exportedData ->
+
+ if(exportedData.records.isEmpty()){
+ return@subscribe
+ }
+
+ val dateFormatter = SimpleDateFormat("yyyy-MM-dd HH:mm:ss")
+
+ // Use the date of the last record as the end time (Epoch time in seconds)
+ val endTime =
+ exportedData.records.sortedByDescending { it.timestamp }[0].timestamp / 1000 + 1 * 60
+ val endTimeString = dateFormatter.format(Date(endTime * 1000))
+
+ val startTime =
+ endTime - displayTimePeriod * 3600 // ignore records older than X hour(s)
+ val startTimeString = dateFormatter.format(Date(startTime * 1000))
+
+ val filteredRecords = exportedData.records.filter {
+ it.timestamp / 1000 in startTime..endTime
+ }
+
+ if (filteredRecords.isNotEmpty()) {
+ val dataByModelC = filteredRecords.groupBy { it.modelC }
+ val dataByModelP = filteredRecords.groupBy { it.modelP }
+
+ // get all models
+ val allModelList = dataByModelC.keys union dataByModelP.keys.toList()
+
+ // sort the list by the models that appear the most frequently
+ val sortedModelList =
+ allModelList.sortedWith(Comparator { a: String, b: String ->
+ val aSize = (dataByModelC[a]?.size ?: 0) + (dataByModelP[a]?.size ?: 0)
+ val bSize = (dataByModelC[b]?.size ?: 0) + (dataByModelP[b]?.size ?: 0)
+
+ bSize - aSize
+ })
+
+ val individualData = sortedModelList.joinToString(separator = "\n") { model ->
+ val index = sortedModelList.indexOf(model) + 1
+
+ val hasC = dataByModelC.containsKey(model)
+ val hasP = dataByModelP.containsKey(model)
+
+ val x1 = dataByModelC[model]?.joinToString(separator = "\", \"", prefix = "[\"", postfix = "\"]") {
+ dateFormatter.format(Date(it.timestamp))
+ }
+
+ val y1 = dataByModelC[model]?.map { it.rssi }
+ ?.joinToString(separator = ", ", prefix = "[", postfix = "]")
+
+ val x2 = dataByModelP[model]?.joinToString(separator = "\", \"", prefix = "[\"", postfix = "\"]") {
+ dateFormatter.format(Date(it.timestamp))
+ }
+
+ val y2 = dataByModelP[model]?.map { it.rssi }
+ ?.joinToString(separator = ", ", prefix = "[", postfix = "]")
+
+ val dataHead = "var data${index} = [];"
+
+ val dataA = if (!hasC) "" else """
+ var data${index}a = {
+ name: 'central',
+ x: ${x1},
+ y: ${y1},
+ xaxis: 'x${index}',
+ yaxis: 'y${index}',
+ mode: 'markers',
+ type: 'scatter',
+ line: {color: 'blue'}
+ };
+ data${index} = data${index}.concat(data${index}a);
+ """.trimIndent()
+
+ val dataB = if (!hasP) "" else """
+ var data${index}b = {
+ name: 'peripheral',
+ x: ${x2},
+ y: ${y2},
+ xaxis: 'x${index}',
+ yaxis: 'y${index}',
+ mode: 'markers',
+ type: 'scatter',
+ line: {color: 'red'}
+ };
+ data${index} = data${index}.concat(data${index}b);
+ """.trimIndent()
+
+ val data = dataHead + dataA + dataB
+
+ data
+
+ }
+
+ val top = 20
+
+ val combinedData = sortedModelList.joinToString(separator = "\n") { model ->
+ val index = sortedModelList.indexOf(model) + 1
+ if (index < top) """
+ data = data.concat(data${index});
+ """.trimIndent() else ""
+ }
+
+ val xAxis = sortedModelList.joinToString(separator = ",\n") { model ->
+ val index = sortedModelList.indexOf(model) + 1
+ if (index < top) """
+ xaxis${index}: {
+ type: 'date',
+ tickformat: '%H:%M:%S',
+ range: ['${startTimeString}', '${endTimeString}'],
+ dtick: ${displayTimePeriod * 5} * 60 * 1000
+ }
+ """.trimIndent() else ""
+ }
+
+ val yAxis = sortedModelList.joinToString(separator = ",\n") { model ->
+ val index = sortedModelList.indexOf(model) + 1
+ if (index < top) """
+ yaxis${index}: {
+ range: [-100, -30],
+ ticks: 'outside',
+ dtick: 10,
+ title: {
+ text: "$model"
+ }
+ }
+ """.trimIndent() else ""
+ }
+
+ // Form the complete HTML
+ val customHtml = """
+
+
+
+
+
+
+
+ """.trimIndent()
+
+ webView.loadData(customHtml, "text/html", "UTF-8")
+ } else {
+ webView.loadData(
+ "No data received in the last $displayTimePeriod hour(s) or more.",
+ "text/html",
+ "UTF-8"
+ )
+ }
+ }
+ webView.loadData("Loading...", "text/html", "UTF-8")
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/au/gov/health/covidsafe/Preference.kt b/app/src/main/java/au/gov/health/covidsafe/Preference.kt
new file mode 100644
index 0000000..b5e4587
--- /dev/null
+++ b/app/src/main/java/au/gov/health/covidsafe/Preference.kt
@@ -0,0 +1,162 @@
+package au.gov.health.covidsafe
+
+import android.content.Context
+import androidx.security.crypto.EncryptedSharedPreferences
+import androidx.security.crypto.MasterKeys
+
+object Preference {
+ private const val PREF_ID = "Tracer_pref"
+ private const val IS_ONBOARDED = "IS_ONBOARDED"
+ private const val PHONE_NUMBER = "PHONE_NUMBER"
+ private const val HANDSHAKE_PIN = "HANDSHAKE_PIN"
+ private const val DEVICE_ID = "DEVICE_ID"
+ private const val JWT_TOKEN = "JWT_TOKEN"
+ private const val IS_DATA_UPLOADED = "IS_DATA_UPLOADED"
+ private const val DATA_UPLOADED_DATE_MS = "DATA_UPLOADED_DATE_MS"
+ private const val UPLOADED_MORE_THAN_24_HRS = "UPLOADED_MORE_THAN_24_HRS"
+
+ private const val NEXT_FETCH_TIME = "NEXT_FETCH_TIME"
+ private const val EXPIRY_TIME = "EXPIRY_TIME"
+ private const val NAME = "NAME"
+ private const val IS_MINOR = "IS_MINOR"
+ private const val POST_CODE = "POST_CODE"
+ private const val AGE = "AGE"
+
+ fun putDeviceID(context: Context, value: String) {
+ context.getSharedPreferences(PREF_ID, Context.MODE_PRIVATE)
+ .edit().putString(DEVICE_ID, value)?.apply()
+ }
+
+ fun getDeviceID(context: Context?): String {
+ return context?.getSharedPreferences(PREF_ID, Context.MODE_PRIVATE)
+ ?.getString(DEVICE_ID, "") ?: ""
+ }
+
+ fun putEncrypterJWTToken(context: Context?, jwtToken: String?) {
+ context?.let {
+ val masterKeyAlias = MasterKeys.getOrCreate(MasterKeys.AES256_GCM_SPEC)
+ EncryptedSharedPreferences.create(
+ PREF_ID,
+ masterKeyAlias,
+ context,
+ EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
+ EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
+ ).edit()?.putString(JWT_TOKEN, jwtToken)?.apply()
+ }
+ }
+
+ fun getEncrypterJWTToken(context: Context?): String? {
+ return context?.let {
+ val masterKeyAlias = MasterKeys.getOrCreate(MasterKeys.AES256_GCM_SPEC)
+ EncryptedSharedPreferences.create(
+ PREF_ID,
+ masterKeyAlias,
+ context,
+ EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
+ EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
+ ).getString(JWT_TOKEN, null)
+ }
+ }
+
+ fun putHandShakePin(context: Context?, value: String?) {
+ context?.getSharedPreferences(PREF_ID, Context.MODE_PRIVATE)
+ ?.edit()?.putString(HANDSHAKE_PIN, value)?.apply()
+ }
+
+ fun putIsOnBoarded(context: Context, value: Boolean) {
+ context.getSharedPreferences(PREF_ID, Context.MODE_PRIVATE)
+ .edit().putBoolean(IS_ONBOARDED, value).apply()
+ }
+
+ fun isOnBoarded(context: Context): Boolean {
+ return context.getSharedPreferences(PREF_ID, Context.MODE_PRIVATE)
+ .getBoolean(IS_ONBOARDED, false)
+ }
+
+ fun putPhoneNumber(context: Context, value: String) {
+ context.getSharedPreferences(PREF_ID, Context.MODE_PRIVATE)
+ .edit().putString(PHONE_NUMBER, value).apply()
+ }
+
+ fun putNextFetchTimeInMillis(context: Context, time: Long) {
+ context.getSharedPreferences(PREF_ID, Context.MODE_PRIVATE)
+ .edit().putLong(NEXT_FETCH_TIME, time).apply()
+ }
+
+ fun getNextFetchTimeInMillis(context: Context): Long {
+ return context.getSharedPreferences(PREF_ID, Context.MODE_PRIVATE)
+ .getLong(
+ NEXT_FETCH_TIME, 0
+ )
+ }
+
+ fun putExpiryTimeInMillis(context: Context, time: Long) {
+ context.getSharedPreferences(PREF_ID, Context.MODE_PRIVATE)
+ .edit().putLong(EXPIRY_TIME, time).apply()
+ }
+
+ fun getExpiryTimeInMillis(context: Context): Long {
+ return context.getSharedPreferences(PREF_ID, Context.MODE_PRIVATE)
+ .getLong(
+ EXPIRY_TIME, 0
+ )
+ }
+
+ fun isDataUploaded(context: Context): Boolean {
+ return context.getSharedPreferences(PREF_ID, Context.MODE_PRIVATE).getBoolean(IS_DATA_UPLOADED, false)
+ }
+
+ fun setDataIsUploaded(context: Context, value: Boolean) {
+ context.getSharedPreferences(PREF_ID, Context.MODE_PRIVATE).edit().also { editor ->
+ editor.putBoolean(IS_DATA_UPLOADED, value)
+ if (value) {
+ editor.putLong(DATA_UPLOADED_DATE_MS, System.currentTimeMillis())
+ } else {
+ editor.remove(DATA_UPLOADED_DATE_MS)
+ }
+ }.apply()
+ }
+
+ fun getDataUploadedDateMs(context: Context): Long {
+ return context.getSharedPreferences(PREF_ID, Context.MODE_PRIVATE).getLong(DATA_UPLOADED_DATE_MS, System.currentTimeMillis())
+ }
+
+ fun putName(context: Context, name: String): Boolean {
+ return context.getSharedPreferences(PREF_ID, Context.MODE_PRIVATE)
+ .edit().putString(NAME, name).commit()
+ }
+
+ fun getName(context: Context): String? {
+ return context.getSharedPreferences(PREF_ID, Context.MODE_PRIVATE).getString(NAME, null)
+ }
+
+ fun putIsMinor(context: Context, minor: Boolean): Boolean {
+ return context.getSharedPreferences(PREF_ID, Context.MODE_PRIVATE)
+ .edit().putBoolean(IS_MINOR, minor).commit()
+ }
+
+ fun isMinor(context: Context): Boolean {
+ return context.getSharedPreferences(PREF_ID, Context.MODE_PRIVATE).getBoolean(IS_MINOR, false)
+ }
+
+ fun putPostCode(context: Context, state: String): Boolean {
+ return context.getSharedPreferences(PREF_ID, Context.MODE_PRIVATE)
+ .edit().putString(POST_CODE, state).commit()
+ }
+
+ fun getPostCode(context: Context): String? {
+ return context.getSharedPreferences(PREF_ID, Context.MODE_PRIVATE)
+ .getString(POST_CODE, null)
+ }
+
+ fun putAge(context: Context, age: String): Boolean {
+ return context.getSharedPreferences(PREF_ID, Context.MODE_PRIVATE)
+ .edit().putString(AGE, age).commit()
+ }
+
+ fun getAge(context: Context): String? {
+ return context.getSharedPreferences(PREF_ID, Context.MODE_PRIVATE)
+ .getString(AGE, null)
+ }
+
+}
diff --git a/app/src/main/java/au/gov/health/covidsafe/RecordListAdapter.kt b/app/src/main/java/au/gov/health/covidsafe/RecordListAdapter.kt
new file mode 100644
index 0000000..616c331
--- /dev/null
+++ b/app/src/main/java/au/gov/health/covidsafe/RecordListAdapter.kt
@@ -0,0 +1,170 @@
+package au.gov.health.covidsafe
+
+import android.content.Context
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.widget.TextView
+import androidx.recyclerview.widget.RecyclerView
+import au.gov.health.covidsafe.streetpass.persistence.StreetPassRecord
+import au.gov.health.covidsafe.streetpass.view.StreetPassRecordViewModel
+
+
+class RecordListAdapter internal constructor(context: Context) :
+ RecyclerView.Adapter() {
+ private val inflater: LayoutInflater = LayoutInflater.from(context)
+ private var records = emptyList() // Cached copy of records
+ private var sourceData = emptyList()
+
+ enum class MODE {
+ ALL, COLLAPSE, MODEL_P, MODEL_C
+ }
+
+ private var mode = MODE.ALL
+
+ inner class RecordViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
+ val modelCView: TextView = itemView.findViewById(R.id.modelc)
+ val modelPView: TextView = itemView.findViewById(R.id.modelp)
+ val timestampView: TextView = itemView.findViewById(R.id.timestamp)
+ val findsView: TextView = itemView.findViewById(R.id.finds)
+ val txpowerView: TextView = itemView.findViewById(R.id.txpower)
+ val signalStrengthView: TextView = itemView.findViewById(R.id.signal_strength)
+ val filterModelP: View = itemView.findViewById(R.id.filter_by_modelp)
+ val filterModelC: View = itemView.findViewById(R.id.filter_by_modelc)
+ val msgView: TextView = itemView.findViewById(R.id.msg)
+ val version: TextView = itemView.findViewById(R.id.version)
+ val org: TextView = itemView.findViewById(R.id.org)
+ }
+
+ override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecordViewHolder {
+ val itemView = inflater.inflate(R.layout.recycler_view_item, parent, false)
+ return RecordViewHolder(itemView)
+ }
+
+ override fun onBindViewHolder(holder: RecordViewHolder, position: Int) {
+ val current = records[position]
+ holder.msgView.text = current.msg
+ holder.modelCView.text = current.modelC
+ holder.modelPView.text = current.modelP
+ holder.findsView.text = "Detections: ${current.number}"
+ val readableDate = Utils.getDate(current.timeStamp)
+ holder.timestampView.text = readableDate
+ holder.version.text = "v: ${current.version}"
+ holder.org.text = "ORG: ${current.org}"
+
+ holder.filterModelP.tag = current
+ holder.filterModelC.tag = current
+
+ holder.signalStrengthView.text = "Signal Strength: ${current.rssi}"
+
+ holder.txpowerView.text = "Tx Power: ${current.transmissionPower}"
+
+ holder.filterModelP.setOnClickListener {
+ val model = it.tag as StreetPassRecordViewModel
+ setMode(MODE.MODEL_P, model)
+ }
+
+ holder.filterModelC.setOnClickListener {
+ val model = it.tag as StreetPassRecordViewModel
+ setMode(MODE.MODEL_C, model)
+ }
+ }
+
+ private fun filter(sample: StreetPassRecordViewModel?): List {
+ return when (mode) {
+ MODE.COLLAPSE -> prepareCollapsedData(sourceData)
+
+ MODE.ALL -> prepareViewData(sourceData)
+
+ MODE.MODEL_P -> filterByModelP(sample, sourceData)
+
+ MODE.MODEL_C -> filterByModelC(sample, sourceData)
+
+ else -> {
+ prepareViewData(sourceData)
+ }
+ }
+ }
+
+ private fun filterByModelC(
+ model: StreetPassRecordViewModel?,
+ words: List
+ ): List {
+ if (model != null) {
+ return prepareViewData(words.filter { it.modelC == model.modelC })
+ }
+ return prepareViewData(words)
+ }
+
+ private fun filterByModelP(
+ model: StreetPassRecordViewModel?,
+ words: List
+ ): List {
+
+ if (model != null) {
+ return prepareViewData(words.filter { it.modelP == model.modelP })
+ }
+ return prepareViewData(words)
+ }
+
+
+ private fun prepareCollapsedData(words: List): List {
+ //we'll need to count the number of unique device IDs
+ val countMap = words.groupBy {
+ it.modelC
+ }
+
+ val distinctAddresses = words.distinctBy { it.modelC }
+
+ return distinctAddresses.map { record ->
+ val count = countMap[record.modelC]?.size
+
+ count?.let { count ->
+ val mostRecentRecord = countMap[record.modelC]?.maxBy { it.timestamp }
+
+ if (mostRecentRecord != null) {
+ return@map StreetPassRecordViewModel(mostRecentRecord, count)
+ }
+
+ return@map StreetPassRecordViewModel(record, count)
+ }
+ //fallback - unintended
+ return@map StreetPassRecordViewModel(record)
+ }
+ }
+
+ private fun prepareViewData(words: List): List {
+
+ words.let {
+
+ val reversed = it.reversed()
+ return reversed.map { streetPassRecord ->
+ return@map StreetPassRecordViewModel(streetPassRecord)
+ }
+ }
+ }
+
+ fun setMode(mode: MODE) {
+ setMode(mode, null)
+ }
+
+ private fun setMode(mode: MODE, model: StreetPassRecordViewModel?) {
+ this.mode = mode
+
+ val list = filter(model)
+ setRecords(list)
+ }
+
+ private fun setRecords(records: List) {
+ this.records = records
+ notifyDataSetChanged()
+ }
+
+ internal fun setSourceData(records: List