ETuVVDx2; zmoi3B3|(k}-OxWOT~xGFs4<<1HIDY<1WY49K(}LSs0!nwIGC$&BgBn>+(&sBg_UOO zzg-HnQliRC8Nok+RiLhE+KLk(#@h&754VsgifiC{KqeyfU=-PgtGL_@LO!mY%qVlh zm$uGQsz>H6(@7IoY)xk6M0}zhLDfLkT4qiWlW5Yl1~yfr5$y=74$=lV^u;rN|HV{f z(2k($Ag2O>Xli&JgpI`>+7VPOB$e4uKYafW?*i&XH&~5CHV;v!)e6R(H68Kp@Ep_` z8<2-5Gx&l%wQ>2{ho1PvbfM6)O|q?L&j~y?2ob;F>pn+)VnpC6wyEQ?D3t#7Os_nw z;Z-#Q3I?1W5m^i<5~!iaeA_^=d{@W87E*?AhNU0^&q3+2(9=VEh$477GoGAuq}dQ; zPEEuMS{^x-mNmzQA~Hxii1O3ktX#&Gt}|M?HFbi9PP2D^>#h^htxd}gB-2nK_$9sh z)6URXX)md2O-)Cch=0>7KW;k|zN(NmIjGA854*|+7rsNa&CvE+@s$lq}H?E5~r zEnOFqs!E2g*j|pS0w9VF`S&hj*-$(sG;y6c@@P<_-ozb-xGBmmbAbI-{h~i*S?u&u zn}}hv8LlKzai^k-dN?{Rnnf)a903KY#Z?sjb&)h`XVQJj$Cs0VlG;^y56wyrPJ{$n zE_B)2nPyhkEL(~YA!+(iC#@1HBn#hC=^B^~pE}`-k4r^^q$*6Mr2;x=o<7;7Jw9el z^5logrBWZ&r~mOCBFiV6J(@Wf7|HSxakFEv a+op z&@eskP8`Pa#yDc47%>No07_hg8>VUqzm#v7x`rzV_0sAxZ;`^048KN>J_Kz8APE(e zuMac>A*G1=%~e0v96NGYuoap*_x1Y*bg~B~4@mYEE9#4(TP?HxWc=hxBq3tx=W~ z0fcr)FUOo}#}n<4UXrQ-czo%(?|fYf9yA+Fl9CGOpowZpn#S55G#7#_HDwma%Qn6E z(|`?J{yO1WM0~l}UWx%jAFWvP OM3a`(c{Z|i5*9dL3j&u^s z)A-_C9AfJlE}`YPb4tU1sOB3Yt5R2e6&gP7q%BFLd94&&q{IxJM)A;NpulChilv~C z0C%_!STJ?d)KMi3MTS*P_YvB~QG38KG-dSt%&2lAk+!W;D#cX>n8Zxu%}SO{ga}&s zPYH6?A~TBMK*Mqgwp1g7q-&S3a0ak&S?jq!Pq#cx=*e)V0uyMd$T9?t1q4YK9bt+} zjflV8bpMY44OUmitj~IT#+5PaefUim7Rs1KFN~TIx7_>pZ%9`NjSG2Lj5^6E+D+XF zO$Rq3TF3?oH49Ns;&PEtw@uggaaLOf3 >9!K8oH3Ov zsJ5o7$N~)=L}iT_5~BmmiACC}nk)w=;sj0lNRqTxnNq|HT0(~;d#W*m<{g(MYk(U! zeDcL_Or-+N>my4_1$NMsFzh4RMpKmWSgZ=5>7-O_hQ> +rb!z5o93|4c>A3)jR}7+-N;GI&m&YhzK2Rv4$gZQ3j~ie7Zy zb*r!K-0QRcIHTz6AK3fSf>9K`FlrP%>@AOfbh@1CFp8p1vPO}usHoSEGkS-)@K70{ zs@s}}gK!^($rM(a{wQ}yKmKqfA}%$G0@Fummh0elcwcc<9f$LJfXnR}Gk{#Sry7o} zTDW~BS8--^d^verhfOrvL9>#96Y;1Hn`mm1)+$qq$Ww<+G&Oq;j!iT@Sp%EsgvaPV zPK81pHqrE?90Z$adZNan2~FvnELf`deJO;T@m5>dMCWI0qKrHoIh@Y|?C_wzaKrGG zJHPc~m_>PdN8il=OL`1rZrya|;GTJ!Sv0xN7-!5Vu>^MqIHGC{;{js9d_7Q5JPa8n zxXTxKqm`b@d~~w@R7*DG@Ic>0*%eDufqUG7OVEYa4cCsLUNx?vN63NCbQ7hkUDt6( zFlUt2nPKN7EzL5jk;M~{sE$oCffLQD1@OOsRYiQFSw=NZ=>f8gYNT+0TIb&W!oi_0 z%`&QystN$3Sw=O|Hx|oimQkIQmAU$!d-P*P%-Jd{O9#D~R)A7zt+2(raWFxlvh*jn z27g?vES-BZ18LZq8@HeN#`V8X)gs#AYo1Lk*IO*r ^Qg?2UkS8rPov*u&UBIDr4&j1Tfdbb(Od@)3fZr# !!c>i@N|^&Z_J&VJnCdp8~Sf*#cxW|Me*+ z_Z#=gZ;kYwJXme;U;U)*ze<-AEgq)4A0bIWTV!)_1etNu(GY!RDuM1HJ3dqb1e)ov zYfP>5-nMrfAe$@Yg8(Sd*EG1IGfQPUu!tkbA>^uKTCR=_so~lk2Ujn19eFw 7!Z6%88&vi*b~n;5E#eA`sOfR7q1;X+zcFcqdUd0H25d+&(6i7Ik>B zNz_!pP8}*YiLSALQ-^PqMo}5m9R2mTzZ_6=rhuB%=hs{AblTniT=MtKUSm!x#g->I z^S{N-&co*fo~%DZ{J0bTy!fS`zx><}rmLhoS&ol6vmLfS6E_(bg)wc^{AVbv%q?BF zLS|}q5L#a7Xr2}Lr6CQ3>zzv&(;O1ij0tKQ1`3nmE@!ysOccn}xn+f}rsKi{ZZO|h zT??_^h_BAPni)$@BGS@cha_d%ZK_o~ng|ek4&rRBF%}eofaZxLPD`cmZy&^|8HN$O z^s 6Sp#7ncG-MnE@%!E z@`AbWp?`brLceibN>%!FMz9O%e&!8dx#z}oCCG6mHU{p1{(2lEWp=|&& zKfCkmgTy;pMT1)Csjxg~0ZO${HWqehF0XoMDzi#n^py|&J>VzG;U7`A=epcq@fUb^ zZ;S7n*=vtMo&R{u-~IwQ{38#Kg3b4S{(?VC!G`AB9_b(>dz+S}gs7mx0?)Th1O6R^ ziJ3^}$2H0D?3kq{2{C_vghw#Hb396+C2%a;*BuY#A`KN+i{V&ZH^RVDRK>=*1#Xu% zR0U=9N8itkDkm~&N17wVWg4$$*+d|r0yJv-0Qa&`^@XjX*LdH3wf3= z_`@wf#bLwnxs3c5hYit7&E0Ge&PO|J7`8tXHyL@PaET)_5Pf7Ong-W&L)XP|HP;Ya zrTC^>devl2hYdr5n#mYZCcw0j85yb;?gO(855-zSX6X)Uw=kTldloV$9YbvnU(Jjq zC+BD;YH7RTqRYuVnkesShYd7J)))(lQl92lp)sRcz|#&Jh|~;Z`>iK^sGZ7onpH`p zphB$E+}%W4#!7VBVFQ(lGGYF^r_5~{r`D(bG{4>DGs~?pYJK-x_kR!6`jLk-VjUY} z{h3et+S2dRbwE?=M>@#rs39(hhty(l9fi0x7}2r{KCnF0Pz)^=24$|Jn$-Fcs#3My zwG m|rpi_9nre41J>!Io;NPgCn |1<(6b}x+=W&ln7YD7d zZ?TVz@N9;TjdNT$RSX-o;ko0Pnxkoo&7o{NmRA~|`sR906N-eTsDKN0EgXF-dZ;jQ z4vpLn+=u0$ND2#l&DAYSGf}${$Ik_RXU3M3g+!4vsr_o+O~eLTRo{stuB2# `e$Dz^#=_fya?o9xU zv&sO&XWd}(X~6gH`{|qdjUzJ;jzYw<_CEjdDMZkON~ZCQ{A{A+p&KB}j_W3>ROy;Vpo;Zv^c_&<5V!9kx`oc+Npo*627>aQJGc{0^ zX6D_@XmX;Drqz@;T+OD5Ft9M0VPk@XiKMJGCKLrfO^6WLQ7!Rl1{{@~fv8u1{rzjw zEltz ;6OvjI+(`NC3b zq1S6KxpRJdq1~P7wER81J0pbroQfM>3o)pF`qr0s`;B`Qo<`$;ARuGDta0HhLejye zsKu|`e&5fgtA*&V8zprnYB)L!P$29LTd|;m`zV2n`vbYEx*=|3GcC{XORuB7eX>v+ z7oJ)=)MkM*&4{=ru5tH~>Fp!b$QH){xZyBB@hsajY}8IvkOr< }=WshjtKUXt4x}w;=`A1YFA%f5{*-NZ z9QV+E w8&La@;Axt6halyB)tEe9Z zlOS|-_>L^--kg=10u_aM#(}^{Kv2YvW =+`{U^fpqYEcKC*_EuUWY3 z758kI3c~Xp&9rbQ6SVjMr6@v0FO}YPIr$5NANX@zPxU<8L0}JaEbcKI5k9yx3irY~ zxQWRNILZ~e8XGzK75vVOEhp88PW}Rknf9BNyqhT8Y5q95*<7P6DGGg}lV5JSDgl7z zTacTwVbbyGpWgk1R1^@M{Br!M00x?WPLexgL4syl$Wl`V7&Gg>Aj1BwGADo9-|CXi z9(kMkl78c)F)xim$2oWWVo;o%X8MikC?^AP;wmnC4K3WIp@ufpZpC&L+~cI<<|e(g zaO~SBtF^1t{s+Q;gm)>3I0}pq6#e6&X|hmrP%vFHb#7__QqV$I_jFO1A_&H{lNn`B zY|^f!u7i~m;e=+f)Iio+W=;`UXr)7IU{kf5h~|K(gS3IYaolH~wwyu?Eu*Uraw>3! z=Hjb^u(4o6yWpf2lFAV0$#41Z#{zLm(+eWMPOIO-J+bAP%}2lO3|t9##PGRMT)FL+ z?>jDCBs5cO*mg$9HdWm)Q9lAVh x#0APA1R10YueiA4d2 pLhlvAnRWm`pku6HrfHoN3d)4?Pu`(F8$$VvGNCMDc{*% Nr}IsV@fb9nmCm+q29|3wr0TK|o^$wYn1+yfxM)HYmn#kBe`!5lngKnj zfDTPn$9aS0qTV%gJPS87*r?sWkwJ_iMLO!rG22(T*Brc=8A(o1(F8+Tv&~8dO%&7D zO=eZ35rpmTVsA#pwXF*rf6uC= uJgjjhKPR{+#*Mucup> zrbW;QsF2#UvkMw2V+A)2!iYqa$?ku>^+_UES1FU-e!JZbT1yPEH4_uG?)8svIW3CH z9C>_HmVfQ(cik^t2sGoWKv6~#Hc{~=3~ jAHFcFTA;KowV`x_?< zs?}=T1-eq%ZZlhP81r=l jVbOd)h$PqFSSs;)YE+E<%Ws4%Ms>fMwSzCG|$?k z=BpVuQP$IR4=IAyCPRv{pXM=^VokNqPBY@=h#FwQ4L`L9Raj`pqjH2)U;@nrCP&g( zY#=7yOb{g1JH0dj^C^qVBEaYDGK>&aLHT5O_=8V;>FNE(QQ0R)k>TSTZr`6G1I?Ps zww@84O%s)weH&LX2ab+(EK^Zz(|1F~@J&=| M}T z6e>fi#Xik=Cet&N_?sJ>(ydLiPsub?2zy%eAeox6a-L=r(dj5N1FZjA6d^gKW lJ`p{|&sSX&qdii!Bm zQC??8mJ@n3GhkBl)r^}605mf|ilDX0kRlY&%m69YRO12743Hyg017Yv=)0bpiU67! zAV)|AGSJKbIg-Z01I-MOB&G}_=3o9dzXnE>2GPXj 4El9Xw;nnx2Q zKkX2ZM#&muK~dJzL q^Z4Sy<~60 zWr`CxzzrT)@Wtc46gLQvBV}tg!h$^8Dht+p%VdxDYLAY~NK5YyM&%&WfvI5o0asnd zoDe6-xK#k9e7FS@#>E}ShA~Xo%CbE(yqvHkIwO)q$uT`ND;YQuLTFJCWt_{5x{{_f z%a$UD$fekkC#^D`h^F1kPTVj9_^$tX!*^57lIRhwWnquMb&>{d(i|EKBt&nUESE~p zmH+)IFS-jzB5-2t4f836lwL{v#;Y#CiNi4|x8i5>=&}CHv)0}EKj|8w* *C(d>k&L zHnbbMs)Mr!+r}M}<{-y6emyg$oK%!sErqt5mHe6r6EtrIefHKMD~iw{C-w+aR0#^Q zbv%U9m|2k3i)LU|Jm3{K4K9@>y3Gd#A*QCH!k~~-TnY0u76NGJ-t=wMEEWDg``EiK z9Os1E%ey_~ @)8F9XO#%UKoXezx(C!S_%U+Csau%*=34~K?b}~ zD97)?>*V6F0XBq!dxx!1@m;et;j1PmR0(A%Ai%|>YfR-j(#{l)c%uMU#aKZY!Y5_; z2u?Ls12@}*&5|}UqsxgxniFbr2hB?MO@s!T6H1b#waSztNYI>6lI%H9PAFNj25`~6 z-+nI@2{b)jmXr$2pgEyri5d$WG$)iaIb}|$OE=!|FF=oTfQtpw!C&t7+Fcf~&iKQN zcir;U@Aex<=N=tJjP>_=&W-8vpm`{A&1a-(6WX}#+8h_Qr~wK)z@?+05D(H79Nbk8 zaa`tax~(3&AxBe+5&>!hg{H0BuFEYy&;rjvNocMidm-c&3oKlGgFq^jhtBdgGoqX* zq^*;2O;IVDUVAa(}_4BGM`RP%`#tt zkTRLCZhib?q2rgPokjA#(?)y}>i(Bk{AvC7dAA_#Ec;|e;$w%bKXd0T-+ywt6lflV zZ0lJSL}P*N;F2v1Wz%%q#+@*@fXno7f@?6{3QL83O=)LYl2ZNNgeOWh#kDlJ^2M@H zOwQJI9XG-|wvIc4HJ2;+2ZCX *e$($mt;sI;s(HWcMN&1FDk zNVS-!rJa%K8A$m@AAY~Lr(2t*N|R}*5b`wlJDHlXGM*LzMyI1p#2 nEx?2 NY(UYiQ_2$xV||(=(x3X44Wn2$T$@^&Q8lFG_bS&Fm)hULmJx3L$}xu@ai* z7Nby5CY#@K>I=rI-$C17UgS%CQM+r5`aS=K2X7NqA2Lsls^2G^_OIupvrgl7w)Kp< z+H@6e7^cttz(bHdJS+~gam|&(Ed{03l+uKNn$+(sNvZm+s<{2jK)Ig~w*r`O`nZ;c z)H55GaiKye&W#n_w>;dwn0+}jo}ApGso!H-uIAK4!A(=YskE#)HWY<8P5q`aq*|2I z)Ne991A+d5E3H?jTbl-KWEv{OI!*m1Q!`e$(=;zS9c3c^N4+nJL@=Z56m_BPvjwk< zn#w-w^%vRFgd(0hZ@KhA{l>jZUl|qpx8C%?zfV^NEk>)fn~bz GkvyuAzo<3D;5>Fnz;yd|WQ3h0JtSC9rVr%QeT;aU1}h z-3|~sq=Z&^E141I1R?Eas8T{RT~sr1A~4VbZKa7?yNoG9#q%exxG@!zq*+vr88itg zPuKuFzIOb7>_|lf?V37yVk%IC7IiC6)>t^9DIPTtRE8_ByJjZ`uAE*XA?G?>xRA!! z48MKR-~Kx;YL6Zn#f_Ig^DEy;7X?l2jGE1gILkLs1;Ruar0U|V6P1>AgZaLr;l?hD z`=wVsR$P~~+N`gLmJ}BP6aiJd(DMQx73qQi#ei&h>MheoISA%CI_ej>_`Nwgf}FUc z)w3OKvRTQLiE=+A3AL$-SUUqR%5|FfoSFsIa-3F#dwN0!a{J96e#7 dD-3;cqe^*Y{#z&aUM7^wSrV0? zj|Q8#*>P%%3(_^sNA*zK({xc&!nP2w=~}7@7Y=Imhlu1s_ BJ%&IY3CxwnRHT 2opDzLq>}K4THP9RFA%8vK*R1WTynB5*v9=xGWuKvcBVU zO?TjO2@M_B@T#B@;V*gsPdn1F()We~$w@3)*lV%TYVJ&w*Sjb4X^gxkkg=8qUzFCg zKs^E*s)hB!!J3g_nS-AO|L~x6*U}^;g%cH`ns!c0A!MwirfK&i63PVi^N#xdu@KaQ z1f-GZl|es#yE`U^#XRM$A3<1p?#WS+eeEk-?@TA322Hu nL2nH3YMmC{uvrQbl*+643)&^K{R& zkW7nmJQ|0GJORPz<>8QWLX37$I;Q1nW=$01G!2hR%bH_DQJ@q3mF3f3Jvm#Kf^NGN zT*{UZT-NK)?q6JpnAAyk7R^qlPS8-apKZ>)Bc*|MP)equLcY^1Rx&kXg*@$`luk#P zq`&RYZ|*_TKPab?M`AsRwkcP}zjD}zet@OF<-ACyEBG~uPW 2!5@9MTg>+qkiB;1v z6whD&_HTZaZd97WOW;F=V5TX71Uklw 8sQD?o*|`5AoLUPeWkIek`{)zX0S^%sa4UVlpat|!g)8z+x@DU#vu7|8H< zKIPgk4hqT;vmX;WN6DLs$csYGeG6eMu48&C_cg`Qy};H{tCHCPKEsW*WkUVK)2rx} ztZa_sN;9Ih>G(FTI_E4jndw_5{5}Xjw;9tkhAYNVUO+>&O0M#;?F`49lZCXxhvSGH zcG9e5>_l9kiRc>0TFcBSB1eUKF~)gS1DmS7I5dY+9i$CB@He0K)z_rhLaWMJ2RRjB zL#y>v2VrB;hZcKN3rTs~01Lixe!p=lq-O_#@Y}h!JfG+=R|1E2MOYJI%e{NuhMg6% zt$@$s-;3>~*?E+zS>UtLspIU90L^sxQoxt|l}C12XLqj=|F>>^W7Cet5sNF2V!hql zaB91=aqq>IZE4rZJ1riqsCChomotA4qBaK&4!=BMapm%EdoQR6cDB4O>khv r$b`pteZ+S41yy!fNTeI79&`Vo!tn0-fq*5l4QKcaYJy0-o8bIt9%x2NCkG =ulmiz2oYRv z27>K5x%i@*I%H5Au8n)Ww!Pf%EcdSsTHUzA%sk`n-qQKsir1TXFGt&+nrKUy5%FkS zdns(qS1{!-YU;Qv7H7BTcUzsl1aq#)G&5jQbO-3V*DZ@BrD74S>o@M7&i|O*l`gWq zQp1a~8ly7FP#}|qc2Ro)1HG8-WmhC>V{aUbyNy3u&E{U ~U Zv;$$1bi2;zmC{adG9+cCakOQC=pTy1#Skwa<;Mnb`r0w?Q&J zd^+ND^sFSLZ&=?rF-Q82#^%M9D_1&@Q%7M8*b6a3#oQjTvvDtc*6p`&TG-zj1VUX; zzBp$1#YGUR;?v`IHpHh (WxY&-$n)nRc*c zab?&Dnm%0cG5hwB_+Y6ud n^zdrljhOMl6?Iv2GnEHn&Y!=3#GEnK6#@u^6TR$aeAi?WyR z|2izNCNAUw8NFHjqu-#Zit?1O;ZB;pz9p>T4ewb?e4+WP<@s{mQmfyfg{jxjxmX@) z_4!D^Y!m`{U-WZ-zd=hfkf$rbh*Tho^Ok;trnt)!leHxJ4cf_54O9$_hkk<=Tp~{c z6z#^L+9yD!euHLq%MbjD+5Y~1bl7+J87E%+xcA05NSg-Pf~GMFmfpTr(sU+O0wVV? zR4t{UcS+OPT!_l@%W-wOeuL%^knOz)gEWaNOGgm|5Brz3!xNfYQkEwX0McrTN_SlW ze`}`hH5GPb8I*@U+A)PR4H@7gMj99I1DHy73D`R{dm{op8sf^bJqvmF|NgO`iXo30 zy(pZ7D>$n+X0^!HQ$q*$6x*R^;TmIC#AYGD7U^`VkE1DEPnOtv?joPPem7g1-_2Ts zIJre}t7wZYh{Xv8^*q)^RCCk=;waEVYWLl*-=IZDB&|(FZ(``t)(N4*BBaqa2chdC zifuTJx p-c)o2ptv$C=EIY zT^FHa3sUxJph@VyIC9!%qjXJ&4oYta1Ud+W)Ce7f-s^x4O1IL`LFkkY9hA<5H;x7! zgx<2y;Xd(quaBXFmVhV{EjywZRn`Qwbu+MBlj)knLdV6WYq+=>cd_ZV8HUK&PRx-r zvL~I%jdoEQeAM8GtL*{(1}!UqNMV8^G^LQ%XHlFIZB^Qw6i$>5dobC(Nur=Qeww{W z Y%Uw2&ZTYfW%O M%e!N+qTzF=g(AJZ_KY@*=sR48ByOv~&7z^E;93aW z_UvkIXc@TP6P0i^%?a#KM|OV{jTf0jy{8~ZkuP^ j1DPN88#Fwp5tvv+ zv@JmDvS<*{Vnj%t7kz6RenO4cArVj Vx)msM5Lb4DFEnQtX?R&=*?88|{yIh$q99vfD>FUb-P`hcZ>Gl9m`i(5|+XVb%|4xy5!rz1Q8A6mSdZ0|)$OpAAurK1Q*o6e)gyUEg0 WnrK%LZXkpf}q~wu|=24KQAp>eeeWoZDr;=R)-ww^*h@g(BuS*U( zi+Vr1;TeyMQIDv_o~YO*Rwpu!c~YY z3=)(SwJ-*TVd7Jr8~RqGX7h(-n;%Ja7L7DJ9kmH0(v(}7o{LZ@drn1|s{#aWzJ!|C zQZFxPE-NV-1mw}SuT-}cP)G9;$WT-YaWt)4hKxM4(PF5i`p+1BvIkhx?@R$@2Y(Ml z5Jq!T%dk2NT`xHQ$4fDE)uGKSa@Je!a&fcq4C}OFYsrXC)0DusRL%_*g-u=4Q7sFX zuH!x!BjgTK4KD$*^Hyc 6eMaK)-1wGy9Bd< c6O~8OIvgA_Q !R+`=`P3E-YxW;tft%$tpeJ%*|%W!s#N&IC(o#saz9B6QM}axwu$ zq&(~@s>wtqqR0hWN(Y?)frB)2gx+Tb?9t>XnVeFTqn#0vX~=^b?QD$RdB!%Rh0l?@ z7Law2SA2v}G!vLS%q$?i?SZGfJq9G&H0NP5axG^9UTgywCNVF-4I8*`L&rrIo?-@p zrdYlb;?o3{j8)lzqVasZ%Z5*p1OAFfmQ#_Ppi~{I#ne12O6fYR`Kie$BG!h_QA_(t zuA(AG>WCzmnj=wO01YncV8DdJhO~N|l0=mv8O;wbNl6~hXsV?w2^mzQIRPaJ5ZHD| zc1476b%aw+y&tnU_tcj(*T* ljwX6T-4xsHttYsFw5?s>)4 zstM4oF0wOfJ&$x7??cs#^UH=ncmj2GoWoDwdr`920lCxDQ3Sh<_pQhAJUuN%p46eb z5$F)cS{)u9Le~{GTOEN<(^FLncy;Il)02`%ygDK~2{dFNuMXsyo&te+hh}d?phvUX zWd(K?_4FtI>Sh_#W4)Cnlv5Yp#?}(}k4l7DZ=M#YfyH!T(!uZOgsy37J}^)5L)$i0 zJ3+nD;=LtJk2oJ+Nrk+Url+G?3i4!oFN${> n!Bmb=#945JMi#d7Q@D>@{XI z3we8S_`hn}A#)h_EpF+$6*5z^gV6FqNAs+}R}-W=v+R6r*y3E=Vw$b@nSO(I3OH XN1 zyzv>$9^j5=8>a0c7r=2{L-i5bVT6jOaK;qF4b&8O&MCv4T%&VwqY3nEIi6bAJJ;xR zlqTpy+x_%@i{h7d06?dph(TN6&OP*epeY}8K1AnZH0490(+V)6IWXz;l;ROhC886N zha{SEAkljUm1rWH-nRgzgT3w}Y@#I`& OU54X_nR8%KuMH3HFT^9k1c7`BDL=m$#-%360ks_qX4O-fz91+5X zq5-yCzZKv`JCv0osT99x8kH0sc?hFv6>d$rrMeeDb_jMtgk`kTJgMPk;p|_Z zHS^gR&S*#;Sz4Voj DKJdmP&oqU4Taen({zQo_(Wy41|*u{gKP1321l9+) O#Xeyi( z9eLoQSyplcWWbANSxI$Ix2)v474Z`^%Sx)>ERenT-}N`kSXK+Iy*yxjHsiNZmPN%e zR$TblWtI{m70eOGSKRbf#pjOgF~w%Cs%mL<{XtPH*7bMKF0?}KulNfb*Bxh?g?A8t z(t|7<=F^r)uFa`Pl`^i#-4>-a&A1{HP{ge*U!fXTWFm@OpsAX40_esSz0V4)qA4+C za!PTEW?YeJ$YT^ui=}s-!6%w=MedqzT+zD}xI{Cq$bDup=^4*>!PXd)XhitS-Cn!P z0@fLcUT1D-yEbROgUeqGMM2dJotviZI2OuODQPwS(%6bzqj|IHuzSdkSvsW&CJ~P) z$o&?@F->31u`_E;R?ktP%Hm WE8EbjW4BE&yXag$Pt aHX7LW_{j$E9eV>zJO(eNAz6FR*pR378$=Gp3~#47Zn@ zbItZ)qRys$%%#7Kb~YDvmVn>k>u_Vt;SKR)W4}SG7cj1)sYX?>Uk=|Sh}uF;QmTU} z7!!hqV|w`6*ocX5?2CTx7oQ#>ejF!$Ac=scT-QNkR}gpjQB)6P9VBFTh_-O|mUxHI zShCRe*@D+?vjBI~_7|bD&F6FNRo~JA9eYGERmJc^4VTlZs%QJI6XF)Z^c*-UrN&0? zQ7>&g@3q*{?Kfz9yR`AF4PQ-pq7Tw=R=)qD0z|u=N1l?RR=8zvzp-}sLiF-VukFaY zK{FTRCnZ`Q6N;-npHzZco~}|2k!H-u6O&gGX=;28RAlr-n$jdsgHRO@)jo-|McRqI z{J^ttxOuMic^Nny>aOB8!6- axE!%KBO+%iEqPYf&V0*sqDw>|& zFp;e+nzos7jAR-gsker>Q5qbkz05@A>PVDJ!J&Nrb-|%LB}F*gs8Qiio|+ adb_CL;23LaJcIaA9{Wa zhcqXW-)?t<){;nADc=2{J05CjE4GUYY`V)4YK1dwPYX;>*DM|w$r*uC;yFR? nl>xfcTvm}k1 t)9l4p!~5gS=yU*ewFSx I9u% z?9EN?C^r4f <`@#<8d7`6ge=(#lD^0?ImRMSc5_dDDPuwKGQ;7$LTz`HHCS|Np%Co zbNpdc+8?b7jq}~aSezFB*@%1V>rgH7eM~o$1c~doN>_r6A`aIf*Chxl@y+6T`!&ZU z+DTM?e@bvEI=HOkj#?QSDlFKWYrtO#@=DRS4!Dz`DUZFh=%CuxP!@S<9+28d5x9GZ zcT|MBbzr~5^2lQDH)ej)i7}UEjraDoI=st6jB9t%w;MV(cU0Xq4BOL?mE@V4qiKrG zt Opm}<*aYBn&t#jX~-_wc@aoyi%F7 DYqDo gBDGtRkmq>O-9*(Y?HNQc(e-G%s(B1eO9S&C6RORe5lwnZ0$AlL1 &0 zCsQ4?6p?oGH~WpX#2Z=&Umd)O{I|3apIW-F096`%)j?S)n$pB%9pvO;l%|r^LPrKa zX^DV!P$7Wx(CwMXWF@)@mOGtxw?88TOpm}xC`7Q_LfHfzKc zI*b4}pE$OrYqpOYkX?lvp%WmB*+Wq&mfn97LFIhAJ-@(b(^iKDYrc7L28!m>G94eI zLS@>|umA{wdMT)^gMzxCvJP5`P`U9Rs8Cr4Jw@KowtX$M2s4xhm9=z#K%lY?%4&ql zI>@O5Dr=!b8Y=6cf)16n^e&7`8dTQNeHJR)-+O9O!+wEe6)m>0|AjAiSkXgwHCzMc zDli&V1nzR(3>_T@nwIJqoasqk%2_2wWK_{(E@=`4e^DePLBq{&LD`4R|G&NOj+3*h z`k$R`nX(C?hAt4m0NG408(K(#zy>0rXjqBQ%sdk&*)q(`rU;6t$V;(MB0)h!sSf-o zioTSHNKsTkL`9^DbQA@I*O!;}o^$VW%TqSXoM+#JKYsaqxa^!;&i9_@+;eWf*I;lR zbxNKB)wSoe>w3g+V >uUk2~Zbmf!iNS4+z=}-;iCx0c^`k~$Xj5_2 zRd(99&ig6v-O*IGWY#OohxM6*By`*oc_W^Q!KU)Ona`I}sdPG-jT><@XQb1X%I@i* zvVFWX`!q*;uo0lWyGy}lb=7G55C{z2y${+F8G*ICBi7zUea(Wv-d%H=6`-)jZWisN z3x(S_y(-!mmjJ}G6GglEoxt7RXQJJ4NnxvXk!UuZz!ZB#n@pS8Tmj8_5UQp-kNs}_ z+f&?m&^8pn>UF(QGvI=Unbx)~e3u}}<^*!3SPUitjJ#naGC8x5hxdEZnQT5?&gM$x zlDJtKPTvm6Cl9Q7pI%iiL_1iprw@`=PpG}0YVcg;vgZ#zEOg7u&{l_hv36Ts+FnFo zAUr(JV(RlfzJR!;LGud!K%h6Go;-DEcPm=RD!-94MYNe{U!w5zP_(g~FC2DXMVk-u z2SD>$G!N~IJ^HWB&=Qy@qxFA%Raf_EPrr2S=ebX#eRlgkaXhicUZaEQXdHqYMs2{% zm9u6#m&n8l=~xLa=`5S&SSgi=B@?-PIwAHW=g_lRv)zVu8KL_eu3&)k-M~QX%({g- zB{C84E$>EKjhz4hZmUk)eL4Y9+@7xUR_ax$69C688QS1uBA}qxqq&%g;6BcnY$A>u ziKdAwT_3vbdwM|HCjSW#lid%}x~mfbL-U5T`@$vw1p36y)>@cnq-|B2fU4{M@#<55 z5aIrj=BVp~6}W|IdDR#;-A8q@Su>eRW=e%bE|W^eGKFj^1{2@eSUguSV=-}owdXZV zu7d5;h}~drh hrzhe>2y-fA Yw^@%|9a p?&3V1sb$y#2EIa Ic?>D!kWGgM&UNjn2XlACIIpDIkZ_e z0(ZNkp{;{a*lON|whl&MN?!+~FvE>Nv~@57H=FY~v3dh{9yB-R9-{H_$s6 zb6Z{70L`xqTw|lo$8Yo$q75E>ipTD^ zXp3@wA!t5~Hd65k0R7o!X%%dYM4P_yS!LbNt?T~R0QYmWC) rODVO6SGFij6oOEU}N5D9%;P^5=9~$2b8_r)8!S>vURX?{+#}Nq vURYh_iWqoKDLe zb*Hn%H>SPKoepg>zFNag?b>ZHnPLjYGwD<@8_ySvQZg1xri@}Smw-!QGG QpBIndN1O1K*Y<;Wn?dejTBvuGzRrUn39rC!pT6dHUAt&4w|w1rP$W zahwTpgICMq_Vgol(&({uA=q6A&CC(FYTl!=iE4_DV^ird8>rCEv9>Nu-CaC*`VWV> zyP&P-ne{4QuaC?uj?~PVNqPJ`Zf27yGikyDO}T8*%)ydRF%eJaaz$_%uqI^fA;^1p z4R-vyR}|bQ{$L#X7zb 86Ldb z>tuNFPQ-LC2ES+oXmg)rVC;N&@UBLHW}C=-b^ZSF7hhV*^_ym$ IAHA z8nC{ZG172Fdm0Xom&*p+d5|_rnFQ g~rNa7Mv~_3yyvON=(e5tv z4a>7vM%%UQ8;iY@Mq70C4?^p+(WcgYL%=TFW@;g OJ*uBul+e>qyBJgR{R=wc{$#i zMu2wSNs4%ER{Spa4_hnCTdpIGO(q(R+f7{fk-+8QOHXYCKB_XKodTWkZ;5l}=YJ$V zA5*K1oWfZDkwEF`hiR{zP4u_4yXF6rM;2PP$NKwXw0&X|EhTi?AC0!heE8WP39Z&$ z`j$68`*lBaHb%7y?zd{dO}klrrazrDV)1gm06x1^%EYtDG7Mr1IWwOvX3AzdW{qX{ z^_a8i)629^__wDT0on|$US?}JEpJTQ>PKelcDuAWDKY@Je{IiEuTvm{a7%$UNlXWT z9+TGdrt=(UOiJ4pOorp>18EarWDIs^M;idrnQLQD+N>v;HM*}&tntiG(I#2R)zlr* zuhxJ48{8q$KC`&>o~ Qy0um)7ph!%$AFy9U<|_+-;(1Q+YmNxZPFPz0@NlKB2fJLfbRo z7Y6+ZiC@m+I567Kz$YkAH%6Py^ohjo%xL{ezYsK+M%z~669784&C)8Ed!tR``mD0< z ~FeFw6N8cv*OpBwj8g6NOSX zpGX&r@k}8Wi)F-!((7%KKCMW{P;xbOqmx#oKJFbKrR6 zR@a$NqHRv5idT0*pFMD=i?|D-wKp*W9WqN51NJbpb+?4NSJ#N;O1WG)Z nBSu I)#E^qH+AwR|1z4ih?Gx09nS zP0$gzy<7VV>YG4x6mB`tI*dLL;E0Si5ca`z97jfT9&|jO9*kzf=pgK_i)OriaMyem zZO)3$8=cjr)_dlqXxsbf71iC-9t-w(nY$-i)k`DAQGDm9Tp2X=*|&Vmgx6o;v}&nP zgvb0~m}X>6c>KuB#Il7_OdRa<+zhFgX`dvVw3NCTQZKVLoR)8*c?L3Dw@Ic=Q<4F= zJ=3HUshc6mAly=*83j53^h-1oOy@a{TcRy;kl}cGCE8>T8H3#^(FQYg=9*8U&E1h% zqf6Sv8qYiuZKy)7rtXmb^6|t{?vQ9tVMw wa`ctuvezb8slBPL!f-wGmLlkMDty0u4?k29MsqqRH}%^vvS z>^94^DGomv-2SO^Z|VZD9~^F3`0UORG=a?q(mE) S&6NA$j!tXSw*lt2Xd65IR91Igr|f?A-Q0E2t|F?9!hXgflRf1uUVuoX z b*w~mTCE~?Q-tt% @3uMy+^K?_i~D7S|RB%|5F@_VT%S z^ )YpV4@GKWa>#-3Sx~(y7WtqU*?T{udpuQ$VVDGLu?E*szYaAredTSJJ;|z~z z=9U1&)7j7rAA!5w$IvEkC~P&?Lc86Kz!W{oCevo-NNBGK5vr#9kLPP!g}MKr_2rD= z%8=18OEXuQ#l}c&W~pYZHVXE6xk9lNi)Z0HawZM;YbA^6Y&lVcL&}Amk q z&^qW7A{{nzq8-kf82;lpGulCl34!M6-e~)7CIpY&;nBvH69YtZfwU#d34wu5aWl3Y z<|b(i_7hTl-EpozV18mk<_t$GeDgv|?y@Ll |-U0i&tOqJzYovIx*9Q1kP@M=ySS|fWhqsga7b7T}Z&; zmIZCZAarde1p|)PXkAQSY{&6+G?VTJk*ABJ*;GF;?4FG_e)Yv)b7(%N3kljd0n>#P zuK3D?&*?$}Tit7Y?wZb ?AnjzBO}WP&0bNU@?NrJyf_j{wj5adY1HF0g2hZ(a%-3^ zALi3k(3rbTH|?mOUm3U#aJ7GldJ^3y6t_fZv#ow%pg;4u3hF~8HnIXn8(I1kho@Kb z+2f*#$L`pC_Be &UbJ^ z;-ch2zEmz}lEq}v1V5KHv$1SCnMoS?a;cCm#Ki*{Y=7aH?hVRx+=%>V-%FP* <(K@dJk|J4y_&}@OPNfpoKB?- zI6#$2r{F2TL@Jhs&FeW6_pGPI2`aW*U3eC;Q^QfLeS=2Vw$u@+qgZ`k#`tJ`EkF6; z>^8Tw$q+vn+%BhU2kM-a9~^F3(3YQkVW2NR`C>bcE27OQ`$6RCl4!$lKQQdBi8dJZ z#b0w#G{@+NJAL`d7gzf7lOMLa>$>^g{pWDkMe7&9X2)T?dKaeT61|2|8!+RsG`v(+ z&J@jfB9%$z)3G!>WtL9m;8oAKAsiOlD&cTg?9E!-t_~%_FbGN%TP1#rZ?8Ut!6Vdu zD*>o$i6!`ZgzzZ!MdQ{Xz{C)8eA4q(+Q%^JX$+oe1S+Jvc3VfppQC-8+Ocb$p{}fl z{rlG=Tv=%rLe7w|mkii-(HW++MlPAnC1bf_3=UM}VsNu%+BA|m!^kI#nYdwC%cL8> zMFk7xCjv8Kg>*f2N4yWDRu7A|DT~h6-KX$5m)-g|a<$j?OMKs=(lQFS9DFW$w2lCK zR 13a`sI)8u&t4U+S3?(yy-!7(R`bDK>rwff-fda@*q_?ede0^RXagC# zkahPIy?(Dn+&$4gwf*+zdc$bc_1DI;aMPD*!XxQ$NF!(FQssCun<&IHFcVP9#f-A$ zq|$h3!&>YV9jxU;^*)c%xwjH$0y_t(mt1%h-5PMqXVET~^vm6Cv1x-qzc}2p1D(}I z;G-fF+QhtHnXr{nn(y$ M>Q{`eBE)Ihkgm@tb zZqF#ki^a4VhwH<#$#l;0b^8hj=Q#4N%Cs$~w!@)BT2Dw7*b+#~uhIIQG@fp6MpF?Q zcefAgfOCeMxBj#>AzJ;JzoMA~BExaK6m1Yr!{X_iXjYj9!0wS~%eq9)njfO=D53F1 z@3SfOoB15t_9R*bbx-r;{4bx)Jq@kOxV$ lDxlQUB3cp;fBreei%EDd8Cd?c-$ z&u7HClGkErpDNL@ap!gF)T%16HH?;fp)Dy8nYv9ZZ4g0Z?{+a=Z&4Rjh!ETopfwFD zdvr52r%vTKj-#Q?FcHCcx*FQRj)=hSY-nDN%35 CVCf^%)^aUMt`MH zGiqz00Q?sW209i7!b58hGwLVJg>lZpK-bXPW7by9{=n3swR39lOtw*M%qf+i)@(;f zxIZviMJ#~J`w`HVYcQPt q5 zV6axW6XiTCw`cae4S)SM&%2u=Z)F5BLI4weDV6PNo~?KB^aDMz^{s%quC7%Yuq`No zQ-p%saXi}ze|qXgo^220X03e{K?1$Y>m$Q`EJ*a`U_X%s^D>@s>5bRoeiEo6^T2w> za%H$w7+qfPTT!niV`< t^4^n-p(5&2pZEi=bH*JZy-C6L zBF=X219m@}=Sv`7F|-uhcE!U(nhz2^01N(syqxmKnh#m Bf7w_fgDxn^jv4}%2AE2%#3kvv1^ zd3Uej8Ma #01HzD`9rF~r+KF9pK9*I zGwm)!%BF?cR{%jnW3oo+mKYN}-`S-rf;=D70;%XVvr!u z1v;Rpz-kt*X#{qGiKH6ZvFghDf|1%h6Pl^zt&f4$*9h!pfHDplwMPA Bd~2@bQnylvjoOd+2$boy?rCFY+m2MJhQ&CF;X2^g5@va< R^`_jTBd+ zR2B~!jq*rs=y2AL=VO3i=&=*#v95A(^6DrQv&BIN^TNOlZ(~@th*`yBp*x3lW01jD z{)aDhw3}?~0T#K76l?}S3{@De*mz@|kFPXN!v7Zsih~HSE}d#{>pw={XWbJ`1A7o} zaN1up%lAhW4DQ6jL-5aC7We*g2K>?X9R9+tN$_Bvdg EVPoo64oSEgni z e6{4AM}^+1Eux}vYW#FC^{!p!< nMx6jvPLN+uAPCMoEcm0JWS)~%LzPx{jl` ${+uQGib{CqOV>SqqfE&9!@+5dZ`^$ `M{M2p2yIyRp>wag-Ng8u99CN z%4|PP5wDa)W_mgIs|C@8zR6;g=0tDcgdHFGmTFAVYejxdbE4NVqT+nL{2gpQb4QTN z{(Oc0f`{L_^#qmt@1o51^@{kaBwnJ3{}e mt97o;N#{ zh_^WW9c&)I@AuANvTrK%*A?G+q(deT?I+4?zgZDyO5%?dah4!D(T9>OMxhU7IpN$H z-`Sw@$3=b(eJI6u^q+FwvWzbc8!0f)XW zh%VV*z+yDnU&INoeD~!MRp@ylzb^X=8Brx#Cw~W MnRylMxTyeBHk zY=2)7djzpVIpCftg6KrwvlWX`=zF%~ge_lPwoVl~Rpi&u_iW9GD$zFbcd&WV^ABE( zxI3873Sv*TW6Pgy@tm@lA?_|PLOGTY_Zdi3^$c+r1VktLa61PpF@(E0;j34^eVih; zF(TyG(1$xDTBcs%PWe07oO;I_%;t2=kh1UC=c2ZMuudFL{v%Ol2dqdy$>>(Yw@4!E zP;=r>1kr{5b{3;#AHI_l_MURpUaHVrMSdOq9gL_FZIHh~_Q%bLGuh8k=nr}HoLBEt z$)6Ktb{wjR&r9MFiuj@+y3qfP#c1ex1FvrQ^p0y){+C659X&T1DiOCA_&eAfz0Z7% zp
PFevKk3*`KP2W6D0_lzqgK{k6C6@si5# zCHsg&_7Pq7(d0$U-glg%(4V{HoEKCtEqtz!NXIuMqwrTH@j^xXx*$4bA9h;Iuw^kv zKYxCo%I{?{!w!oX)-C3-|NWCZll}J<`YX0S_^nP^X6QEpQ^&Q6_<$r{uZRx`q7!}S z5f-Cq&ZC@g$wBviN9BK5 @+->@A2+^pSm8j6xro#R)rIm}HX>%w|Pqi2NG*$bO8dIM0;7gU#4p+rQ4x z|3RVu<2C0W%2pa#nNeji|18UhDvSA)B93V>qfUz%wJhcb3tw5J@_Sj#sKa7Lb&L7W zj{iBCp=bRC?ydf@X8J!rC(DfRPX}$hLmTY!?}x 9?mw%X3HVkBE_=VV13v(-kN ztu|t{+MVBc;c=DUtJOvvtu~^!+NY~`cO&l3ofZ0BR( n4SnbeMpT@yl)r<` zhrYP|Cz$N{WFA9*)Ik><_J~X#=@4ah@@Zf;eb6b1pHwNk1<{E<5@9h4ePjwJ1V34w zrSgYFehqyj%7`jakNh2MKKg@8Ss$}=xze1I&e-}~Hvh+M&by+_PBrxseNPe |^%uidF~iSgzEu$E%Oo!-Xf&_i(x4?_jgG?FugY(-itIoLwDI }7Mp4x1C!ZO(7koWpI-dWHVt`jkvw0*!7h zB-(kYB5oszmn-6Sg6NcebVnAWeWqPG;i~^wH&x}|UgXy_C%O|Oszf`>-@)duLl3{s z^!`SL{-zi1U!%J6kpWR==Pj~~$fqUoRz+MUh)(p8B8ySzBiuYyzp`}2VJg2N@@wcL z+~lc5+}h!9m{XYl?86NGFBSUx^TC4hL1A8bG)51~GQwN}Nc^25a)AQTi9WoC1ATaJ zPI&Ux5#=w!yLq4wPiI8MWKa1!*!*_?ekU{Z&nWbNz3-6y{!=AavVTDlmF(51JUpiC z!%o?UE!ltal;8YN73wAXutWA?UG`r(FwW64tIv80o&PiK_ya#9%Zz?mNTl<9MLa_g zyVR&WdZr*cWgk6<#VE~*a<^Q~?b4$>OY|&}U(=i@clC-h_wx8V*!{#SFA(1Z4v0N{SNm+2@20?VnKH?l1Mt;JQR-gLfYiiIC z@fsOMe#BBLCb!7n!RDb)3?0PKAEIPG_nNui%gN-SI|QaKH7XBnkVG{q58Ww zS&Y)0&@VV)hhxwAzT$M3$ggQm=pIH?iSCuZgUzKqPke%*SEKT-6PA?DSuK-?-xOtb zsZn|OElE_P^6 tNy?<8`#<(? 6GsDa!2@5>Jr@N4XgUq7!}8Ikt+<;^ B0 oKEt{Up(7W4N?_J3G4Z4cG=ipYLW z*JBcBMD}yKl)a9O+0ThM`#F(aSw*W$^Eck03iayeM0R2+Rid5c?_l!>+w6ND)11F6 z^sl~f;@=OK$$Plsq2|1 %~?oSu|K z%XGFUEq}vY+VdwqjJUfyB>L{o*kP|~5;;|r*{vEw {hJGi7e&;)08aP@e4-JYkyLVH>AxYd* z5k~~ki9S?kF$#TXH79I;d1Pyq|74M0Lmz4|qDnL>e+QfKz l6ZhDIQ$DibfORcn#Cye;fFZkzT;OYuO7Z% oQP9%B9`Vnv)_xi zsQg}<6LDxxMAw{~f^WtVclW6Z{pZfzt6TMRB9{n>bf2ymT`Gxcup7Bd5S_A*e4oWA z*+;JCga_|>{ky8r%SC=o_K_ P-y&bqf9U z4?lmG(#9w^xcGf;l4V4>)kUKE@= j`g;`md+USur)BbxviJ8ZqO$i7DB_s*KIF9bA jNF&Q}vpAzpbD^D_!4mjDu PAUCT6}ndB*U(3~ z;;KYkXYqHi`Nmm4<+49kp+9cm+#Sj)Ij=l^pQVb(C4j^e6_E=Rh%WReIMFZTgpNBa zkE;Bi@ AW`)ubVXe)y@sANzq!9_G#tfvTyOF!y*!T%}TS2kL=7 z+~i A2=GX*_6i83dBPnOZMvm{=nh`S1+6MfGfEJmU4*_#v0wVm47 zYB!NzL*Fx<5fzg?MsYKk`;qPGcg|2(9XXu|-=wCQ= z!5M0gXNWg?ME|lZBg9)i5>-7z+>ij#i9S?h^-`J>TFwc(9?`qID%23Q*EA -bgR?NW7=vpuxD|u@F?bAvmoa!7f^Z0f?J<~vK^}vJ z7<>wY3I=O2I17WzF}MkXyD)edgJ&>!9fAlRql!$$U^fh~s0bDn!J;BqR0NBPU{R5a zF}N0kTQR_*B3M)ei;7}VQ7kHoMMdKn%)#Ij7#J9wjKS#`oQJ{X7~F)xT^Kx!!OIxD z4M9%`gY7Yx;SgmH68Fd8Kn!qsy9bYS_2A*Io_-7#V{i-x$6>G(g8>Xq#Naa!SO mOLfiHc7t;0K-Rsaq{_1F$V38ZT#P`R}OP_~0l;~jWPe*XyW)0LJ-P`u}3Z~+Dv zVz3T_Z(?u}2J10ERqFW`2H(cu5(r#JT@S*T%P_bG15}ir+c3BrgWq8A2MnIV;6)5x z!vK|Mau5RqGkI$acE(^Y3}#`F!(a{uhhwk=1JsVmcpP*xo{E~h8iOxjfT}+kt27y* zPQC(z>oNE-26tlcOAJtbC;u6PXEFFY25({TJ_J*`G1vkFROu c;={UByP|}qUvc(*pJE7 m!A%@?eMZB#bRrqZ+c)fddImS#_^XHUJhkkgPh|6D=g#>>7mTmh z u!qpr#Nhv7a03Q-tl5$=^f5cR>a?}3Syc-hpx*(|7#SI?_rlc(hPWsU?v2B{ zlB^2{4rRB8EgC6}4#F4=?mp%R_yg_1KzDxt?mjpKZ>PZ gj#;F2=yJ`-N*8un4P zutv*`z+6AA(Gm;mQNX }gw kvyR`Q@RynNJHZuS-+ zf#lI&Df1VD^=!Lo*>pg9ntjGdbF_)q^;CwJ>xtQQRvtA(Vd&|0*M(=R;GAkzREpi? zVO_8WuMt_7v @ ?W!E z8-Z!43w_pjZ=PAj4%l$90y7$c9v)@ga3PagzpZbE_gypsTiTM&!;736ft}q%Tc#b{ z@#1QWMqq1O B zjU XBNAEzK+)7J1pVMgq#E(M zSnA^Oxvdxn%WZXRRuU!(+qbnM?84v%!h&L@kmB%+6aukng_Tp()PaxN5jzu+R0?H^ zti;GLm@`lls{x5C-8AC^voTg`>$ uU=UW zceq*(P((DaSRp2n2S;Ew8F7D5GBLsu5La-IL8NdM_9&|o;!{|Wi)$m$(W$YXsBFz* zT oW z4Kw!}w)h3GmNHs17nu#CWWeo;fxX@JveF!5-AKO*Zpo^Gu(*%1m8MnOJ66<{w;Ees zSwU`_Y;mF;itofNxMvcVoAgX2nQ`kO(pah_*l^3_h_p%64#djtk|}Suj&jD3s{I>U zC2DdVD_%&}E_W-xtS&cl4kS+L?rBp2s|b-uldosxMLNkc#7a8}MlrB<$Nq-FUc(q1 zWQ|aCLv@ 7DuGm36?+n^+M<8y8L3L(mBi A-IsKD5pTRe=HJa}grNyk+{G?LD-<8&KbPTOCpHx}cKnQ$rWaFJhC zC(QXqlGud7zrS~C$(1{(A!DTpTo=?vhIm(B+>I+bcU}o(85VN0w%m0cu4rA{(hPYe zIefSRqhmg>m&V&OrKXPDx_PFOodqsvEsbS4`B+HN9RRozs%VWXcWf$3vf_PyD`JhM z3cU5&h_>&;dy67oN>hV9e+{UWC{B~IMz@v7hcv82o 42`&^X~L>bfxqN-Vl! zR=fpE^cd_^Q6e|~_BeZ)d{AG-p)d@H#2_7hJG&QFJl6;jZxOjD1yNWOE0U`NL|TX~ zGS^l`cI4vSPKd6;1^fMARA7&Uu$C~H2UFm6C&I(QzQQLI1LZMNiSA`1z+J=`$uwQC zgs3sKPt1ruqh6B4!aE6ilmZ~crfQ>c<;_CRC?q7y=ElNGVoBEt&Bnt@BMabu)?R>u zC%zDaXJyp@ax-kt?1CuNi_N~mr{QA{$OKoAH`uHaZ>R8`VXlp-hPa`Xe_vs!y%JcM zM3 {heb047? vdS2&z^K<;!ds|SC?AJ=#o3DHCZ4?_ z!!p9!#9*b!-bPs{I|C}(S}~L3Y!02pTc{ib@D{Hpuotmr4O_-&1fu%e5o}c>#KJD? za_#8qNw7+RZ*R20V&Ckd_Cm~fi+$+rEUxyYzUb1xE_fAssWwt6@f4`Gu;gmp{tu5d zz%m>&0eBmDe_#joE>QiDwcHqv@~3359zph`5KHe`&x3V7Yv5Nqirp*U2=w*C!qco3 zW&_?U0yUXc<%?yr`0A2%wL0{77s5KGJukcmz{ Em(Slr;7j=46YiutjMVF z1i`eSwa|aE7VBW FP4vSy8))&9{x<6Y2;|rhqYbt *hjIqRqbt~t+!`qu&nZd_%DP&IoQ*Tbd0Y9G>C zpoWJ cIn|Q^6Uz=t&~s`vV{Lsp`yWmdJa)zYKYidox_$=&J|WPC z8w&%o)>KDpfhi|28TVF3dU1(<_BFSjxc$4aPI&1nu%-%v!fljOx%mL?=p$%{zB~?v fS-aWUUqb1qE3S^>)K8dMvKc`ZgUUZOu;zaOoMCKN literal 0 HcmV?d00001 diff --git a/.idea/gradle.xml b/.idea/gradle.xml index 7ac24c7..f43d428 100644 --- a/.idea/gradle.xml +++ b/.idea/gradle.xml @@ -3,6 +3,9 @@ + + + diff --git a/.idea/misc.xml b/.idea/misc.xml index e0d5b93..51fa3e5 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -5,7 +5,7 @@ - +
- @@ -13,18 +13,24 @@
- +
- +
- +
diff --git a/app/build.gradle b/app/build.gradle index 7a7adac..4fbac2b 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -29,6 +29,7 @@ dependencies { androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' implementation 'com.google.firebase:firebase-auth:16.1.0' implementation 'com.google.firebase:firebase-messaging:17.3.4' + implementation 'com.google.firebase:firebase-core:16.0.6' implementation 'com.firebase:firebase-jobdispatcher:0.8.5' implementation 'com.google.android.gms:play-services-auth:16.0.1' implementation 'com.firebaseui:firebase-ui-auth:4.1.0' @@ -36,4 +37,6 @@ dependencies { implementation group: 'postgresql', name: 'postgresql', version: '9.1-901.jdbc4' implementation 'com.squareup.picasso:picasso:2.71828' implementation 'com.android.support:swiperefreshlayout:28.0.0-alpha1' + implementation 'com.android.support:cardview-v7:28.0.0' + implementation 'com.github.danielnilsson9:color-picker-view:1.4.0@aar' } diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 235453a..521b65b 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -12,6 +12,7 @@ android:supportsRtl="true" android:theme="@style/AppTheme" android:usesCleartextTraffic="true"> + - +
- +
- +
- +
@@ -36,17 +37,15 @@ --> - + android:resource="@color/colorAccent" /> - + - + android:label="@string/title_activity_shoppinglist_details" /> \ No newline at end of file diff --git a/app/src/main/java/at/smartshopper/smartshopper/activitys/Colorpicker.java b/app/src/main/java/at/smartshopper/smartshopper/activitys/Colorpicker.java new file mode 100644 index 0000000..e3b0a40 --- /dev/null +++ b/app/src/main/java/at/smartshopper/smartshopper/activitys/Colorpicker.java @@ -0,0 +1,102 @@ +package at.smartshopper.smartshopper.activitys; + +import android.content.Intent; +import android.content.SharedPreferences; +import android.graphics.Color; +import android.net.Uri; +import android.preference.PreferenceManager; +import android.support.v7.app.AppCompatActivity; +import android.os.Bundle; +import android.view.View; +import android.widget.Button; +import android.widget.LinearLayout; + +import com.github.danielnilsson9.colorpickerview.view.ColorPanelView; +import com.github.danielnilsson9.colorpickerview.view.ColorPickerView; + +import at.smartshopper.smartshopper.R; + +public class Colorpicker extends AppCompatActivity { + + private ColorPickerView mColorPickerView; + private ColorPanelView mOldColorPanelView; + private ColorPanelView mNewColorPanelView; + + private Button mOkButton; + private Button mCancelButton; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_colorpicker); + init(); + } + + private void init() { + + + mColorPickerView = (ColorPickerView) findViewById(R.id.colorpickerview__color_picker_view); + mOldColorPanelView = (ColorPanelView) findViewById(R.id.colorpickerview__color_panel_old); + mNewColorPanelView = (ColorPanelView) findViewById(R.id.colorpickerview__color_panel_new); + + mOkButton = (Button) findViewById(R.id.okButton); + mCancelButton = (Button) findViewById(R.id.cancelButton); + + + ((LinearLayout) mOldColorPanelView.getParent()).setPadding( + mColorPickerView.getPaddingLeft(), 0, + mColorPickerView.getPaddingRight(), 0); + + + mColorPickerView.setOnColorChangedListener(new ColorPickerView.OnColorChangedListener() { + @Override + public void onColorChanged(int newColor) { + mNewColorPanelView.setColor(mColorPickerView.getColor()); + } + }); + mColorPickerView.setColor(Color.parseColor("#FFFFFF"), true); + mOldColorPanelView.setColor(Color.parseColor("#FFFFFF")); + + mOkButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + exitResult(mColorPickerView.getColor() + ""); + } + }); + mCancelButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + exitResult(null); + } + }); + + } + + + /** + * Bendet den Colorpicker und sendet an die vorherige activity einen STring + * + * @param result String der an die Aufrufactivity zurückgesendet werden soll. Wenn null dann wird nichts gesendet + */ + private void exitResult(String result) { + if (result == null) { + finish(); + } else { + Intent data = new Intent(); + + //---set the data to pass back--- + data.setData(Uri.parse(result)); + setResult(RESULT_OK, data); + //---close the activity--- + finish(); + } + } + + /** + * Wenn zurück geklickt wird, wird das Program ohne result geschlosen + */ + @Override + public void onBackPressed() { + exitResult(mColorPickerView.getColor() + ""); + } +} diff --git a/app/src/main/java/at/smartshopper/smartshopper/activitys/Dash.java b/app/src/main/java/at/smartshopper/smartshopper/activitys/Dash.java index b3ddaff..bf6a4b0 100644 --- a/app/src/main/java/at/smartshopper/smartshopper/activitys/Dash.java +++ b/app/src/main/java/at/smartshopper/smartshopper/activitys/Dash.java @@ -1,44 +1,90 @@ package at.smartshopper.smartshopper.activitys; import android.content.Intent; +import android.graphics.Color; import android.net.Uri; +import android.os.Build; import android.os.Bundle; import android.os.Handler; +import android.support.design.widget.FloatingActionButton; import android.support.v4.widget.SwipeRefreshLayout; import android.support.v7.app.AppCompatActivity; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; +import android.view.Gravity; +import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuItem; +import android.view.View; +import android.view.ViewGroup; +import android.widget.Button; +import android.widget.EditText; +import android.widget.ImageButton; + +import at.smartshopper.smartshopper.R; + +import android.widget.PopupWindow; import android.widget.TabHost; import android.widget.Toast; import com.google.firebase.auth.FirebaseAuth; import com.google.firebase.auth.FirebaseUser; +import com.squareup.picasso.Picasso; import org.json.JSONException; import java.sql.SQLException; import java.util.List; -import at.smartshopper.smartshopper.R; + import at.smartshopper.smartshopper.db.Database; import at.smartshopper.smartshopper.shoppinglist.Shoppinglist; import at.smartshopper.smartshopper.shoppinglist.ShoppinglistAdapter; -public class Dash extends AppCompatActivity { +public class Dash extends AppCompatActivity implements ShoppinglistAdapter.OnItemClicked, ShoppinglistAdapter.OnChangeItemClick { private Database db = new Database(); private SwipeRefreshLayout ownswiperefresh; + private FloatingActionButton addShoppinglistFab; + private PopupWindow popupWindowAdd; + private String color; + private Button colorBtn; + + /** + * Setzt das atribut color wenn die activity colorpicker beendet wird + * + * @param requestCode + * @param resultCode + * @param data + */ + public void onActivityResult(int requestCode, int resultCode, Intent data) { + if (requestCode == 1) { + if (resultCode == RESULT_OK) { + int color = Integer.parseInt(data.getData().toString()); + this.color = colorToHexString(color); + colorBtn.setBackgroundColor(Color.parseColor(this.color)); + } + } + } + /** + * Convertiert eine int farbe in eine hexa dezimale Farbe + * + * @param color Farbe zum umwandeln in int + * @return farbe als hex im string + */ + private static String colorToHexString(int color) { + return String.format("#%06X", 0xFFFFFFFF & color); + } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_dash); + color = "ffffff"; // Erstellt die Tabs @@ -88,14 +134,135 @@ public class Dash extends AppCompatActivity { } }); + addShoppinglistFab = (FloatingActionButton) findViewById(R.id.addShoppinglistFab); + + addShoppinglistFab.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + try { + showShoppinglistEditView(false, null, v); + } catch (SQLException e) { + e.printStackTrace(); + } catch (JSONException e) { + e.printStackTrace(); + } + } + + + }); + } } + /** + * Zeigt ein Popup das zum Bearbeiten/Erstellen einer Shoppingliste dient. + * Wenn eine Shoppingliste bearbeitet werden soll, muss fromDB true sein und sl_id mit einer id gefüllt + * Wenn erstellt werden soll muss fromDB false sein und sl_id null + * + * @param fromDB True wenn daten von der DB kommen sollen, wenn false dann muss die sl_id null sein + * @param sl_id Muss nur eine sl_id drinnen sein wenn fromDB true ist + * @param v der View auf dem das Popup sein soll + */ + private void showShoppinglistEditView(final boolean fromDB, String sl_id, View v) throws SQLException, JSONException { + final LayoutInflater inflater = (LayoutInflater) getApplicationContext().getSystemService(LAYOUT_INFLATER_SERVICE); + + final String username = FirebaseAuth.getInstance().getCurrentUser().getUid(); + + View customView = inflater.inflate(R.layout.add_shoppinglist_dialog, null); + + ImageButton addClose = (ImageButton) customView.findViewById(R.id.addClose); + colorBtn = (Button) customView.findViewById(R.id.addColor); + Button addFertig = (Button) customView.findViewById(R.id.addFertig); + final EditText name = (EditText) customView.findViewById(R.id.addName); + final EditText description = (EditText) customView.findViewById(R.id.addDescription); + + Picasso.get().load(R.drawable.close).into(addClose); + + if (fromDB) { + Shoppinglist dbShoppinglist = db.getShoppinglist(sl_id); + String colorstring; + if(dbShoppinglist.getcolor().contains("#")){ + colorstring = dbShoppinglist.getcolor(); + }else{ + colorstring = "#" + dbShoppinglist.getcolor(); + } + colorBtn.setBackgroundColor(Color.parseColor(colorstring)); + name.setText(dbShoppinglist.getname()); + description.setText(dbShoppinglist.getdescription()); + } else { + color = "ffffff"; + } + + final String sl_idString = sl_id; + addFertig.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + + if(fromDB){ + try { + db.editShoppinglist(sl_idString, name.getText().toString(), description.getText().toString(), color); + color = "ffffff"; + popupWindowAdd.dismiss(); + showOwnShoppingList(username); + } catch (SQLException e) { + e.printStackTrace(); + } catch (JSONException e) { + e.printStackTrace(); + } + }else { + try { + db.addShoppinglist(name.getText().toString(), description.getText().toString(), username, color); + color = "ffffff"; + popupWindowAdd.dismiss(); + showOwnShoppingList(username); + } catch (SQLException e) { + e.printStackTrace(); + } catch (JSONException e) { + e.printStackTrace(); + } + } + + } + + }); + + colorBtn.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + Intent intent = new Intent(Dash.this, Colorpicker.class); + startActivityForResult(intent, 1); + + } + }); + + addClose.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + popupWindowAdd.dismiss(); + } + }); + + popupWindowAdd = new PopupWindow(customView, ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); + + // Set an elevation value for popup window + // Call requires API level 21 + if (Build.VERSION.SDK_INT >= 21) { + popupWindowAdd.setElevation(5.0f); + } + + popupWindowAdd.setOutsideTouchable(false); + popupWindowAdd.setFocusable(true); + + + popupWindowAdd.showAtLocation(v, Gravity.CENTER, 0, 0); + popupWindowAdd.update(); + } + /** * Logt den User aus und geht zur Login Activity */ - private void logout(){ + private void logout() { finish(); FirebaseAuth.getInstance().signOut(); Intent intent = new Intent(this, LoginActivity.class); @@ -139,7 +306,10 @@ public class Dash extends AppCompatActivity { ownRecycleView.setLayoutManager(new LinearLayoutManager(this)); List ownListsList = db.getMyShoppinglists(uid); ShoppinglistAdapter shpAdapter = new ShoppinglistAdapter(Dash.this, ownListsList); + shpAdapter.setOnDelClick(Dash.this); + shpAdapter.setOnChangeClick(Dash.this); ownRecycleView.setAdapter(shpAdapter); + } /** @@ -166,7 +336,7 @@ public class Dash extends AppCompatActivity { /** * Schickt an die Login Activity einen intend mit dem extra EXIT. Um die app zu schließen */ - private void exit(){ + private void exit() { Intent intent = new Intent(Dash.this, LoginActivity.class); intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); intent.putExtra("EXIT", true); @@ -184,6 +354,7 @@ public class Dash extends AppCompatActivity { /** * Menu item Action listener + * * @param item Action Item * @return True wenn erfolgreich */ @@ -225,9 +396,41 @@ public class Dash extends AppCompatActivity { @Override public void run() { - doubleBackToExitPressedOnce=false; + doubleBackToExitPressedOnce = false; } }, 2000); } + /** + * Das ist der Onclick für die einzelnen shoppinglists. Löscht eine shoppinglist und refreshed alle anderen + * + * @param sl_id Die Shoppingliste dieser Id wird gelöscht + */ + @Override + public void onItemClick(String sl_id) { + try { + db.delShoppinglist(sl_id); + showOwnShoppingList(FirebaseAuth.getInstance().getCurrentUser().getUid()); + } catch (SQLException e) { + e.printStackTrace(); + } catch (JSONException e) { + e.printStackTrace(); + } + } + + /** + * Das ist der oncklick für eine einzelen Shoppinglist. Bearbeitet eine Shoppinglist + * + * @param sl_id Die Shoppinglist die bearbeitet werden soll + */ + @Override + public void onChangeItemClick(String sl_id, View v) { + try { + showShoppinglistEditView(true, sl_id, v); + } catch (SQLException e) { + e.printStackTrace(); + } catch (JSONException e) { + e.printStackTrace(); + } + } } diff --git a/app/src/main/java/at/smartshopper/smartshopper/db/Database.java b/app/src/main/java/at/smartshopper/smartshopper/db/Database.java index d23d2aa..d3b0599 100644 --- a/app/src/main/java/at/smartshopper/smartshopper/db/Database.java +++ b/app/src/main/java/at/smartshopper/smartshopper/db/Database.java @@ -50,6 +50,116 @@ public class Database { System.out.println("Database connected!"); } + /** + * Löscht eine Shoppingliste aus der Tabelle: + * Shoppinglist / - member / -admin + * + * @param sl_id Shoppinglist Id welche gelöscht werden soll + * @throws SQLException + */ + public void delShoppinglist(String sl_id) throws SQLException { + sqlUpdate("DELETE FROM \"Shoppinglist_admin\" WHERE sl_id = ?", sl_id); + sqlUpdate("DELETE FROM \"Shoppinglist_member\" WHERE sl_id = ?", sl_id); + sqlUpdate("DELETE FROM \"Shoppinglist\" WHERE sl_id = ?", sl_id); + } + + + /** + * Erstellt eine neue Shoppingliste mit den dazugehörigen Usern + * + * @param name Name der Shoppingliste + * @param description Beschreibung der Shoppingliste + * @param username Username des erstellers der Shoppingliste + * @param color Farbe der Shoppingliste + * @throws SQLException + */ + public void addShoppinglist(String name, String description, String username, String color) throws SQLException { + String sl_id = generateSL_Id(); + if (!checkIfUserExists(username)) { + createUser(username); + } + createShoppinglist(sl_id, name, description, color); + createAdmin(sl_id, username); + + } + + /** + * Erstellt einen neuen Admin in der Tabelle Shoppinglist_admin + * + * @param sl_id Die Shopppinglist Id + * @param username Der username des Admins + * @throws SQLException + */ + private void createAdmin(String sl_id, String username) throws SQLException { + String SQL = "INSERT INTO \"Shoppinglist_admin\" (username, sl_id) VALUES (?, ?)"; + connectDatabase(); + PreparedStatement pstmt = conect.prepareStatement(SQL); + pstmt.setString(1, username); + pstmt.setString(2, sl_id); + pstmt.executeUpdate(); + } + + /** + * Erstellt einen neue Shoppingliste in der Tabelle Shoppinglist + * + * @param sl_id Shopppinglist Id + * @param name Shoppinglist name + * @param description Shoppinglist beschriebung + * @param color Shoppinglist Farbe + * @throws SQLException + */ + private void createShoppinglist(String sl_id, String name, String description, String color) throws SQLException { + String SQL = "INSERT INTO \"Shoppinglist\" (sl_id, name, description, color) VALUES (?, ?, ?, ?)"; + connectDatabase(); + PreparedStatement pstmt = conect.prepareStatement(SQL); + pstmt.setString(1, sl_id); + pstmt.setString(2, name); + pstmt.setString(3, description); + pstmt.setString(4, color); + pstmt.executeUpdate(); + } + + /** + * Erstellt einen neuen User, wenn keiner existiert + * + * @param username Der Username des neuen Users + * @throws SQLException + */ + private void createUser(String username) throws SQLException { + String SQL = "INSERT INTO \"User\" (username) VALUES (?)"; + connectDatabase(); + PreparedStatement pstmt = conect.prepareStatement(SQL); + pstmt.setString(1, username); + pstmt.executeUpdate(); + + } + + /** + * Prüft ob ein User bereits in der DB vorhanden ist. Wenn ja dann wird true returned + * + * @param username Der username nach dem geprüft werden soll + * @return True wenn User existiert, False wenn nicht + * @throws SQLException + */ + private boolean checkIfUserExists(String username) throws SQLException { + String SQL = "SELECT username FROM \"User\""; + connectDatabase(); + PreparedStatement pstmt = conect.prepareStatement(SQL); + ResultSet rs = pstmt.executeQuery(); + ArrayList outUserList = new ArrayList (); + while (rs.next()) { + outUserList.add(rs.getString(1)); + } + + if (outUserList.contains(username)) { + return true; + } else { + return false; + } + + + } + /** * Verbindet sich mit dem Server * Holt die eigenen Shoppinglisten vom Server. Und speichert diese in eine List mit Shoppinglist Objekten @@ -94,6 +204,7 @@ public class Database { /** * Hoolt alle groups und items der list und erstelt ein Detail objekt von jeder group. Die detail objekte kommen in eine List + * * @param sl_id Shoppinglist Id mit der gearbeitet wird * @return Eine List mit Details über jede Shoppinglist * @throws SQLException @@ -121,6 +232,24 @@ public class Database { } + /** + * Generiert eine neue 8 stellige sl_id + * + * @return Neue Sl_id + */ + private String generateSL_Id() { + String possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; + String output = ""; + + for (int i = 0; i < 8; i++) { + output += possible.charAt((int) Math.floor(Math.random() * possible.length())); + } + + System.out.println("Generate SL_ID: " + output); + + return output; + } + /** * Holt alle Items einer bestimmten shoppingliste, angegeben durch die shoppinglist id * @@ -191,6 +320,84 @@ public class Database { } + /** + * Bearbeitet die Eigenschaften einer Shoppingliste + * @param sl_id Shoppinglist Id welche zu bearbeiten ist + * @param newname Neuer Shoppinglistname + * @param newdescription Neue Shoppinglist Beschreibung + * @param newColor Neue Shoppinglist Farbe + * @throws SQLException + * @throws JSONException + */ + public void editShoppinglist(String sl_id, String newname, String newdescription, String newColor) throws SQLException, JSONException { + Shoppinglist oldShoppinglist = getShoppinglist(sl_id); + + if(!oldShoppinglist.getname().equals(newname) && newname != null){ + sqlUpdate2Param("UPDATE \"Shoppinglist\" SET name = ? WHERE sl_id = ?", newname, sl_id); + } + + if(!oldShoppinglist.getdescription().equals(newdescription) && newdescription != null){ + sqlUpdate2Param("UPDATE \"Shoppinglist\" SET description = ? WHERE sl_id = ?", newdescription, sl_id); + } + + if(!oldShoppinglist.getcolor().equals(newColor) && newColor != null){ + sqlUpdate2Param("UPDATE \"Shoppinglist\" SET color = ? WHERE sl_id = ?", newColor, sl_id); + } + } + + + /** + * Führt einen SQL Befehl durch der keine rückgabe hat. + * + * @param SQL Der SQL befehl + * @param param ein Parameter + * @param param2 ein 2. Parameter + * @throws SQLException + */ + private void sqlUpdate2Param(String SQL, String param, String param2) throws SQLException { + connectDatabase(); + PreparedStatement pstmt = conect.prepareStatement(SQL); + pstmt.setString(1, param); + pstmt.setString(2, param2); + pstmt.executeUpdate(); + } + /** + * Führt einen SQL Befehl durch der keine rückgabe hat. + * + * @param SQL Der SQL befehl + * @param param ein Parameter + * @throws SQLException + */ + private void sqlUpdate(String SQL, String param) throws SQLException { + connectDatabase(); + PreparedStatement pstmt = conect.prepareStatement(SQL); + pstmt.setString(1, param); + pstmt.executeUpdate(); + } + + /** + * Hollt eine Shoppingliste vom server + * @param sl_id Shoppingliste welche heruntergelanden werden soll + * @return Ein Shoppinglist Objekt + * @throws SQLException + * @throws JSONException + */ + public Shoppinglist getShoppinglist(String sl_id) throws SQLException, JSONException { + String SQL = "SELECT row_to_json(\"Shoppinglist\") AS obj FROM \"Shoppinglist\" JOIN \"Shoppinglist_admin\" USING (sl_id) WHERE sl_id = ?"; + connectDatabase(); + + PreparedStatement pstmt = conect.prepareStatement(SQL); + System.out.println(sl_id); + pstmt.setString(1, sl_id); + ResultSet rs = pstmt.executeQuery(); + rs.next(); + String resultString = rs.getString(1); + JSONObject jsonObject = new JSONObject(resultString); + + return new Shoppinglist(sl_id, jsonObject.getString("name"), jsonObject.getString("description"), jsonObject.getString("invitelink"), jsonObject.getString("color")); + } + + /** * NICHT VERWENDEN FUNKTIONIERT NICHT!! * @@ -206,19 +413,15 @@ public class Database { private ResultSet databaseRequest(String SQL, String uid) throws SQLException { connectDatabase(); - ResultSet rs = null; - try (PreparedStatement pstmt = conect.prepareStatement(SQL)) { - pstmt.setString(1, uid); - rs = pstmt.executeQuery(); - System.out.println(uid); + PreparedStatement pstmt = conect.prepareStatement(SQL); + pstmt.setString(1, uid); + ResultSet rs = pstmt.executeQuery(); + System.out.println(uid); - //HIER - //WEITER - //PROGRAMMIEREN + //HIER + //WEITER + //PROGRAMMIEREN - } catch (SQLException ex) { - System.out.println(ex.getMessage()); - } return rs; } } diff --git a/app/src/main/java/at/smartshopper/smartshopper/shoppinglist/ShoppinglistAdapter.java b/app/src/main/java/at/smartshopper/smartshopper/shoppinglist/ShoppinglistAdapter.java index d93f45b..1dee8f7 100644 --- a/app/src/main/java/at/smartshopper/smartshopper/shoppinglist/ShoppinglistAdapter.java +++ b/app/src/main/java/at/smartshopper/smartshopper/shoppinglist/ShoppinglistAdapter.java @@ -2,12 +2,16 @@ package at.smartshopper.smartshopper.shoppinglist; import android.content.Context; import android.content.Intent; +import android.graphics.Color; +import android.media.Image; import android.net.Uri; import android.os.Bundle; +import android.support.v7.widget.CardView; import android.support.v7.widget.RecyclerView; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; +import android.widget.ImageButton; import android.widget.ImageView; import android.widget.TextView; @@ -15,14 +19,19 @@ import com.google.firebase.auth.FirebaseAuth; import com.google.firebase.auth.FirebaseUser; import com.squareup.picasso.Picasso; +import java.sql.SQLException; import java.util.List; import at.smartshopper.smartshopper.R; import at.smartshopper.smartshopper.activitys.ShoppinglistDetails; import at.smartshopper.smartshopper.customViews.RoundCornersTransformation; +import at.smartshopper.smartshopper.db.Database; public class ShoppinglistAdapter extends RecyclerView.Adapter
{ + private OnChangeItemClick onChangeClick; + private OnItemClicked onClick; + private at.smartshopper.smartshopper.db.Database db; //this context we will use to inflate the layout private Context mCtx; @@ -38,6 +47,7 @@ public class ShoppinglistAdapter extends RecyclerView.Adapter 0-ExD9iP0NlCeB%!N*R!qRyFSd8T=%Vjz1~Sj?;B6m&;YvEk^+I zKe<;HOw2LoNGqX2$$jN`pfKkGGcTa5O6rUg3N@$$M?>zYatGG6$qFb9BWz=fp}^)a zmO`=@%s;*mz;BRr=R864!wMBBbUaX?rY|tzD4^5|P;*zNVfqq>nEF@S@7`%m4Mt0$ z&7l~NEA6Fct{*)Lb0L_NzMw^Lwk%iJ*wT`J_rH?M<*Minwv?0?d%Y*+00000NkvXX Hu0mjfV*_jY literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable-mdpi/close.png b/app/src/main/res/drawable-mdpi/close.png new file mode 100644 index 0000000000000000000000000000000000000000..30350ed1f6271d2b6c54f95e76d55f33b3405fa9 GIT binary patch literal 263 zcmV+i0r>ujP) yzS|xj_6Mh_3*#A|ADhP&FX6bAXtaSWD!9 z_$*Z8795r+LgoHL)yk0?ObS4}3CREjG`R~ e>j$}f~ z^eiRGl+=s{N?Eex6crapC@N-?QdGbSJ7pqM*kY(2nibdl45MJs0sss(LDIpq2HOAt N002ovPDHLkV1f#IWLE$H literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable-xhdpi/close.png b/app/src/main/res/drawable-xhdpi/close.png new file mode 100644 index 0000000000000000000000000000000000000000..55b6d753aff5bae22896b27c7b4e40be9d4c8e94 GIT binary patch literal 544 zcmV+*0^j|KP) RCX3LG&DYigoKa~9|6$Tb2gbv zHk&;n$L?h>>?G3wJKs0Q?(OUbfrp2Ohlj_O;S?-e&@EHj8cN_H_zYfwHG|qUYCeGn ztYsXc`8F7XA7Bd%4d{l{5}LnYlO~Mf7<^>&31?u>fOd~svi1XbWYWMccnS8UBHt|B z0IL}^s~kI{R-!9vT!1^K|Al=p0w>^7DmVn|YnrHX%uv!