From 9284472d8b1a2a5564ca67ef571567936fd1b4db Mon Sep 17 00:00:00 2001 From: Marc Bonnici Date: Thu, 29 Jun 2017 13:31:24 +0100 Subject: [PATCH] BaseUiAutomator: Ports additional functionality Ports required functionality from WA2 for new workloads. --- .../com/arm/wa/uiauto/BaseUiAutomation.java | 222 ++++++++++++++++++ wa/framework/uiauto/uiauto.aar | Bin 5659 -> 9427 bytes 2 files changed, 222 insertions(+) diff --git a/wa/framework/uiauto/app/src/main/java/com/arm/wa/uiauto/BaseUiAutomation.java b/wa/framework/uiauto/app/src/main/java/com/arm/wa/uiauto/BaseUiAutomation.java index f76c9578..ed8138d4 100644 --- a/wa/framework/uiauto/app/src/main/java/com/arm/wa/uiauto/BaseUiAutomation.java +++ b/wa/framework/uiauto/app/src/main/java/com/arm/wa/uiauto/BaseUiAutomation.java @@ -38,6 +38,14 @@ import java.util.concurrent.TimeoutException; public class BaseUiAutomation { + public enum FindByCriteria {BY_ID, BY_TEXT, BY_DESC} + + // Time in milliseconds + public long uiAutoTimeout = 4 * 1000; + + public static final int CLICK_REPEAT_INTERVAL_MINIMUM = 5; + public static final int CLICK_REPEAT_INTERVAL_DEFAULT = 50; + public Instrumentation mInstrumentation; public Context mContext; public UiDevice mDevice; @@ -140,4 +148,218 @@ public class BaseUiAutomation { throw new TimeoutException("Timed out waiting for Logcat text \"%s\"".format(searchText)); } } + + public void repeatClickUiObject(UiObject view, int repeatCount, int intervalInMillis) throws Exception { + int repeatInterval = intervalInMillis > CLICK_REPEAT_INTERVAL_MINIMUM + ? intervalInMillis : CLICK_REPEAT_INTERVAL_DEFAULT; + if (repeatCount < 1 || !view.isClickable()) { + return; + } + + for (int i = 0; i < repeatCount; ++i) { + view.click(); + SystemClock.sleep(repeatInterval); // in order to register as separate click + } + } + + + public UiObject clickUiObject(FindByCriteria criteria, String matching) throws Exception { + return clickUiObject(criteria, matching, null, false); + } + + public UiObject clickUiObject(FindByCriteria criteria, String matching, boolean wait) throws Exception { + return clickUiObject(criteria, matching, null, wait); + } + + public UiObject clickUiObject(FindByCriteria criteria, String matching, String clazz) throws Exception { + return clickUiObject(criteria, matching, clazz, false); + } + + public UiObject clickUiObject(FindByCriteria criteria, String matching, String clazz, boolean wait) throws Exception { + UiObject view; + + switch (criteria) { + case BY_ID: + view = (clazz == null) + ? getUiObjectByResourceId(matching) : getUiObjectByResourceId(matching, clazz); + break; + case BY_DESC: + view = (clazz == null) + ? getUiObjectByDescription(matching) : getUiObjectByDescription(matching, clazz); + break; + case BY_TEXT: + default: + view = (clazz == null) + ? getUiObjectByText(matching) : getUiObjectByText(matching, clazz); + break; + } + + if (wait) { + view.clickAndWaitForNewWindow(); + } else { + view.click(); + } + return view; + } + + public UiObject getUiObjectByResourceId(String resourceId, String className) throws Exception { + return getUiObjectByResourceId(resourceId, className, uiAutoTimeout); + } + + public UiObject getUiObjectByResourceId(String resourceId, String className, long timeout) throws Exception { + UiObject object = mDevice.findObject(new UiSelector().resourceId(resourceId) + .className(className)); + if (!object.waitForExists(timeout)) { + throw new UiObjectNotFoundException(String.format("Could not find \"%s\" \"%s\"", + resourceId, className)); + } + return object; + } + + public UiObject getUiObjectByResourceId(String id) throws Exception { + UiObject object = mDevice.findObject(new UiSelector().resourceId(id)); + + if (!object.waitForExists(uiAutoTimeout)) { + throw new UiObjectNotFoundException("Could not find view with resource ID: " + id); + } + return object; + } + + public UiObject getUiObjectByDescription(String description, String className) throws Exception { + return getUiObjectByDescription(description, className, uiAutoTimeout); + } + + public UiObject getUiObjectByDescription(String description, String className, long timeout) throws Exception { + UiObject object = mDevice.findObject(new UiSelector().descriptionContains(description) + .className(className)); + if (!object.waitForExists(timeout)) { + throw new UiObjectNotFoundException(String.format("Could not find \"%s\" \"%s\"", + description, className)); + } + return object; + } + + public UiObject getUiObjectByDescription(String desc) throws Exception { + UiObject object = mDevice.findObject(new UiSelector().descriptionContains(desc)); + + if (!object.waitForExists(uiAutoTimeout)) { + throw new UiObjectNotFoundException("Could not find view with description: " + desc); + } + return object; + } + + public UiObject getUiObjectByText(String text, String className) throws Exception { + return getUiObjectByText(text, className, uiAutoTimeout); + } + + public UiObject getUiObjectByText(String text, String className, long timeout) throws Exception { + UiObject object = mDevice.findObject(new UiSelector().textContains(text) + .className(className)); + if (!object.waitForExists(timeout)) { + throw new UiObjectNotFoundException(String.format("Could not find \"%s\" \"%s\"", + text, className)); + } + return object; + } + + public UiObject getUiObjectByText(String text) throws Exception { + UiObject object = mDevice.findObject(new UiSelector().textContains(text)); + + if (!object.waitForExists(uiAutoTimeout)) { + throw new UiObjectNotFoundException("Could not find view with text: " + text); + } + return object; + } + + // Override getParams function to decode a url encoded parameter bundle before + // passing it to workloads. + public Bundle getParams() { + // Get the original parameter bundle + Bundle parameters = getArguments(); + + // Decode each parameter in the bundle, except null values and "class", as this + // used to control instrumentation and therefore not encoded. + for (String key : parameters.keySet()) { + String param = parameters.getString(key); + if (param != null && !key.equals("class")) { + param = android.net.Uri.decode(param); + parameters = decode(parameters, key, param); + } + } + return parameters; + } + + // Helper function to decode a string and insert it as an appropriate type + // into a provided bundle with its key. + // Each bundle parameter will be a urlencoded string with 2 characters prefixed to the value + // used to store the original type information, e.g. 'fl' -> list of floats. + private Bundle decode(Bundle parameters, String key, String value) { + char value_type = value.charAt(0); + char value_dimension = value.charAt(1); + String param = value.substring(2); + + if (value_dimension == 's') { + if (value_type == 's') { + parameters.putString(key, param); + } else if (value_type == 'f') { + parameters.putFloat(key, Float.parseFloat(param)); + } else if (value_type == 'd') { + parameters.putDouble(key, Double.parseDouble(param)); + } else if (value_type == 'b') { + parameters.putBoolean(key, Boolean.parseBoolean(param)); + } else if (value_type == 'i') { + parameters.putInt(key, Integer.parseInt(param)); + } else if (value_type == 'n') { + parameters.putString(key, "None"); + } else { + throw new IllegalArgumentException("Error decoding:" + key + value + + " - unknown format"); + } + } else if (value_dimension == 'l') { + return decodeArray(parameters, key, value_type, param); + } else { + throw new IllegalArgumentException("Error decoding:" + key + value + + " - unknown format"); + } + return parameters; + } + + // Helper function to deal with decoding arrays and update the bundle with + // an appropriate array type. The string "0newelement0" is used to distinguish + // each element from each other in the array when encoded. + private Bundle decodeArray(Bundle parameters, String key, char type, String value) { + String[] string_list = value.split("0newelement0"); + if (type == 's') { + parameters.putStringArray(key, string_list); + } + else if (type == 'i') { + int[] int_list = new int[string_list.length]; + for (int i = 0; i < string_list.length; i++){ + int_list[i] = Integer.parseInt(string_list[i]); + } + parameters.putIntArray(key, int_list); + } else if (type == 'f') { + float[] float_list = new float[string_list.length]; + for (int i = 0; i < string_list.length; i++){ + float_list[i] = Float.parseFloat(string_list[i]); + } + parameters.putFloatArray(key, float_list); + } else if (type == 'd') { + double[] double_list = new double[string_list.length]; + for (int i = 0; i < string_list.length; i++){ + double_list[i] = Double.parseDouble(string_list[i]); + } + parameters.putDoubleArray(key, double_list); + } else if (type == 'b') { + boolean[] boolean_list = new boolean[string_list.length]; + for (int i = 0; i < string_list.length; i++){ + boolean_list[i] = Boolean.parseBoolean(string_list[i]); + } + parameters.putBooleanArray(key, boolean_list); + } else { + throw new IllegalArgumentException("Error decoding array: " + + value + " - unknown format"); + } + return parameters; + } } diff --git a/wa/framework/uiauto/uiauto.aar b/wa/framework/uiauto/uiauto.aar index 3a1b9d0b166a04064bf95d41bb82e64f3e9e74e6..d3719af1b421757c6a544d430b1e81d926b252df 100644 GIT binary patch literal 9427 zcma)?Wl&vFwyh8DF2N7M0>L4;yF0<%9RdV*hXBEYySqb>Lx6)Eg1fuByS(J~t?Jud z@AZ9a*WByR{MOtxM(sbVhO!(CEHVIqhzJ1NuE?S%8^i7a0Dw#=008IDF5+P3>}YMK zVC-ORVeaC};bR;ZgI&-Su{a)e%_o#%Xltt!NCUmcYpoe#o3;P z^(9Xw6D>0Ee)njsYUnL?j00f~aD4Lv?x_O=2G2f$?E1 z2eFOimy+-w)W_*>BG};7LYKqc6n4)%bQ)f{ffMJjb=hLIM?+!$HODgpU=Ep^B{rgQ zBeLp(yIcV9#%hfMT;tzS#;**Hw%RPrINc7dw%IaOt7hPxr6gFqb0f2o<008BW zE~a+IE-vOSOg6^O$?0&)q)U@$T&E7i3Qq3s?ntMw?(_To`oVH>EUaCi^I9jwzeN_Y$W`G0761S06-b~l{Xat3y^zf z>E_~Y>E`I-R@=!lKNYG!Ahoi%A~|4i`5nj7MalaeW=2us&+zbAPI$mU&G#;O001ey zoCyVcK9HE&;RDr?d7cQ%7`0Ka9~Gux5j>~EN7FX!hK!Ib>UK{(jIt})w>cK`;_>&D zXO~Hw+fJ=pp(bivGg1PeW!(qg&N09o*Q*H>!!ugDcLaIYZSbbJEyW6n@Aq4jt8NkA zgHF*CPu6cy(h(ZdIApLKALVi&3jS;0=NCNbSde6nf%v^}b%SoH*VZfVoSxQ5V$Vr| z59oVykGHS0bO6EWhJ}1U)TaDMzL+32ZlC`?r3RO8cI(Z@DDx_dIu6JMTBGnv_G!sE z-o=sDm*>5^Y(=ESe08Nja$vJftb{VASEgqPH%KcouFCyX93}(#bM~f6?!#v6J}t78 zsB}|xJ4$!m*~fDFgZ}ZvgRvkp7@B0v%ZOdGLSGuBLMGP&d6?;L_KS^Sg;U&@(%2*^ zqcl7%XkMsFHmUUsyw@psr&JyVPKwo; z7qZ*oi;H+`tx@-{u)g#1LSQ3gOOOM(FsHOvq&s=1r6=OJXk!%?2>~w?>VT;<7E+@9 zWBxVngc5a6!s00zMF3kq=f7J&i- z*mHD>XS=I?#W1pF6-F92IP^HlQvF%WD<8vx(v1`xoTJ*)EQ#_BI*v9(yZhIAt-t#2 z=Ade0QIfJ-rL{hg(@w;ca&Q|aP^f^tmM4RIwh?^5U0=^TXZ6a<_wt}Fa6x}>MK5O_ z5LQfWow4G*yZ2C*JNWXWn=13?R)EAoMiXgyJ1@IhR8fDZNORhCl9MPvRk&0x8~Kg6)ChV(0x((|P8n9zrs~&VoViT4PL-fyJSI!fQK*V3E)wyb68Mr+OzQ|Y zJOk&vA~|eZ_^W;Ew*Zh(N+zF#GRs~W8_E52jofrXlTlUP_tXLtF7Sk~_WJJ4il*{> zN<@szS3$z)ZsdFjKLQ!Ruhb8LVQ!gVUIV%$I07#W)Fii(f%(xKN}nM9iR2r77KjU* zJvg#Z)8fE_jCJvG;+pzjg``Su+;4wr9z;vhjQ#f22HJ*ZYt{?k!Q zxhmv)(alvs`jyHHzR+<5J<{C1gjdnH9*vDutd&pvON@DNTpBDU0&~Vyt!Pt>zzi43a!(5X zT!GLl8EF0^lF+fFB7Z2y$BxqJ_D*;8=mfa4vOp#sJK)qB$0t&&V-6nH?~cySy6^og zHMH|VALm|fgg^7b{7hFgUWp6t_j7OwG6h7SH(u&k9G&MKo>k(rF$_?a(;^>eG@`G{ zDU_@mT5E+k+=iI!X*iAIxEx=OV6NXgUL>m@uv$wP@zH#H-xFS+HH!n{qs7XDG_%jj zPrdsf@&NV}eq9!v66ZeFRp+!6Gz!&pd36gYfO<|#)tq-?{UUwHs0cknDCc9ZAPa`+ z`h5&j&G}Q?q}hs4krW?#ISe0f?vq5D`l7eUHV^at!5!3s1h1z+no>H!T6QBWAmRXWiR(ND-9lzZGv*7AuD#5oHi!SE3 z9{2kg7T~IrwQOFrPA`sDD1=3cnC!Wc#$U)HO-M)-K)k`)R2cLdMXsYsdvsE#U-8r5 z+^Wi#)HGIVyzk_qa@jbi6S>E0pI^LolADyBnmsO{1*RzieRr^KigXyeq|@%G-D%z; zNWCg(cV_dn4eo*wmx+mStA3Ka3&{>}kVZTpo=l7#CvzlGgHa?M{p}>ap0aWdUn{vS zUQS0+x3+&%0DF{l_q@xfMzZb6?8=2urJXc&e-;@pwve4Ztl`CUA+1tF@OHmkb-;?KPxK+`2J%^r9CaHp}A$Eb8T?K)@ z$M|1<+Gp%tY$)-T!)Z8)CBqwFA?2_~vra?qS?ksx8^#~O>LAOwsGZ@1A!J9!gZ4J5 zWp>XAx7IyNq?-8ojnIzwt_kQZ`6{7Ugtj~X)cdcNUvmwfil>ODIpyzr$MdzT4{ zs-QYY8LGHdc}-@6R9vX+)?tEOrM}-$I~Siuk&>9z^MNE&oS`^z4Zo=Z`L^Pr$I1eq zX!LuHkOX0izWK5meT~@H9g}kFUTiP+g+k0(HW9?BN%O4AhCr(n%!U}4)+ze)fvAQD zg&L7MY2fEp;$9_#?9N(z*TUMpdI)lCkk~ZhbD9{rvA-JdOZ7LM9ewCmz%{3D=69S6 zRfk8#>RrX?G~$C^E>xf?Cn^hQ@oT_0P#?G?qOOD3N4|of8~XrDM*ifoibag7S%-kj z!o(XL-_U0hWe6t>sU-JDxY>XPlGod+YY(JH4-*!gFCb4yy^PpXIwV8ueilNcmdH8= zO>Ow23Y1#y#dgUZLaIiPD%x%hs9YE)P`6BUlN;=Ax6Y8u!zAqL>F{9M@>xM_j?G^`ZenS&f4hx_*&*( z&u!oOd{}WAlfO=Md151WUwzV+`l?xv7HDo@|OIdSCbj7X+Kl>Tulodf2sG88RE39Tx z=a3q>&y^46l1afDZlq)oM|S@r*(fshE2|_`PB(U}QQDc!ua~904oGO%2giI9U{^rD zQ7oae6~&t!|1*aDs(FB{)LSa=`jYQ9_^V}kGBk42n6)I4rXvQeRc0Sw3OBB=e+)C# zS8|lx^+hh2TZ$&*nQVg3L{uH;0doFy1!c8&6 zv!(Km1YLw3+n7ste`k^5Jg*Z9KYRExFO~j`(=A!7s6wgUBc;YpqcXlfRI^2WYP&?` z%vVtkJ9=Bb2`rh;ZktgpK9N@a6ehr;*3$UZV;>h-ake0Mxab^VnQ=$;7O-t;-+*vQ-~dhcAFWKA~Tvx@7J`8)$6ni6)+t`Y1^N@B6T zBAwT{WU%bvI@?98UKBLkEk&0@-xY;@n7{yP%q);W^ec`qit;3a-hl4R>(b9Fjl$$C zi%=2)G!an2+NW}}8A2=5avr^JWoev_iA>WIa+Bb%wBGNwp)qT>uHV(F*3!VD9{-j} zt#9;%#k0n1;83dYM|6%#-aY$1#x?>0HRa%Gg)2y?GeVc*f8e|dbyC3a*d@mtplXGI zHD!uwqk?ggYX0{8gm%z#wg*#$7eq7MA1a1I5bt43Fn^E7@K&br) z%G6jksYq{qPRQEw4A|Si{mfwA@$5R2eQa-z=Q4^z%@I})F@8T)JH@HntT9oWk}3K$ z92Iq)TUr-A%rV3QHzRw6&<}27H zBB`E&8gS)(>E5r%STfT=BFWQY4n1`a9w;!IDUo}|{;F@dhcZ;oGCfA$m2hJRL8;&H zXmb^EexiAF(0@1;88VoYIhm$N!}|@)ur5ZER|>8-g%N)L(0NzU!w%9lIlX_qOV8y3 z!CEY#Nu3jeeOlc4ndhfyiusvnioA&yx*;KEaPeS5cv|B5r`1s1dpIR)i6iSi-Nn!& zwGBS;(BCuIm@v^J7LlmTq3Ef6DExQmxc!_%d-1pq7w2?i($hNe4Jwif28B(^#&BYU-HZa0M;BCK8ayUE%8 zuCR7j{6vm+)zoBLHbuVOYGknbu1l)dyL@Wv#!VN@X;~*fDu~CF;_7$3@;i)Fa+p#F z&Rp4q`cezm{LL;Tmn+-sLxHNY_+xWcaFra6q@!F-DPu_nY+Z6WR{-R&MleEXq1D1t zEeQ#e{==0x={Ln=k1Y~2jwle0V>7;F{`)D$^4d5h>g8R_TF=e;+%1jc6wJBj9dEgv zq`sKv%x#||g8F1?^Yo~5yC5P0q-K~^XMwz2Lum8d9r!`sX5d}(r`nwQ^41MyuS!W~ zG>tEInvQ_VJT$|G?j2REarh#|Qh~W|Iu^epPg3J0y=10p{Cr8B4kLqQ$T3{xxRk!J zK2c@wZf^tI3n1t6N)YB_-Cc7RQt_q5j93)&4QebS1LrE$C_#5!HHRJ71$Uhf#Ss2vLBi4wcJwdx1u}3D}p9o`$Euf->cymOP3KR`A_EFNX z4>JA$X%;!4y^8vAIwX$MzR$KU@M{+vs2Dh!(2@&h4>xaZ+mxg78%XKb*-PnGP2LrG zroAJLSf??}QUxb7L>NRF*oGHV|I9zbY1dH;|8<=w5&N-jvkBoB%0*g*WKs_Hw${Bi zTP1PGZnhD&Y)Bvqb6=y4bDg_9rSqE=kTkY8{Ammce%gByXX{>!8YH|+S(OMgeUso~A3Myrz;w_3d=1%xNP0ElAWOT(OD ze~~_0bO>$t*;$vxKT>({-r+vPpd%e7Eq@jyAk#v`484M|F1Ak(rOHnS<~Xl09_QdT z(GqzjeptpWjsQ*k&Tty+f|_p4M0#_r#}}$y~X^YuKf5nj!btO^7inI4p zw|~%&FZd`5MIW%$i`KWFUgkh@OUvdP!d^I)Od-9Bk~#kScuVN;$!FqB^dOrFC&1VK z!+O~YVbqtz_QKo!Vl@)@@@$J63W5io7IT$XK2SaH=VMm+VGPodeS?Cy&`R;obvR49 zl|*{=JyQO9RJS2*%R2SgN?Sg9m3m60s^M{M+#ehbNM$f(i$WU?AM%aFz`7T}Xp;OicR0yKqjTa9$Dt~uP;trE*eT>*St8&2bf)2FI zwh)|PR)o*Ij(BwNRDDozdo5FCY$~(vwJ?2WqSVZa8W?GKrcrh^8zUuQbBzE*lwtZX z#X)o*r*i{CU*PIrp{RHg@qho!>aFIQ+F%|2ePr%KZtih3tjsc!PW_%}ewqGC z7haNuD8)95UfnL+Z-;wOY;~D`t#@=^wHnx7Ee)#^5LHitI{8-ez=AGMW3da79V5({s>n4tYL@){StZ8@?5=R+r48*U1UbZtB3 z4V8nV;SsWeu)_hf8%(o}{~2vn^X}wl2k_9BtxsL$@ZmuVpAR#`XBEEdU$^gjbs(k~ zV((3D>hS3LZ8CR}Cx09X9{KRdO_VcH%Js;dEe}*J!ZG-$YECDfOQVz)rzEb!{Q874 zp8X^nx#`_v*)5fbRP|j;@47(G^`lUDf?rXyw^8;Wf&08Mx>TXu#nfniHj>>k!kad# z1ZL39$6f>gD)*Z{YtpXcx4eWD{}kcRdUp0zD{pz2>2bHEP2_<>i!CCh2jj7iP!Lhd zqGb6k1oG3W%Eu2~9@_D_X2G6_kv&pP{S^pRYs)#R@VOV=@4KzNiAR95tf7ByS6@M0 zU?$Cyv$fRQ9-l`O#3dO1tN~!s_R1w#N3&h3E$MK=^5Wjx5*?P5<=ciT z6UmozjFggqoaolsTw;A7u@e+qi$WiK`imm8txU z1g)j)CKxGlIfsUZbXm=1Y>7(a=-y42+~f50;G!Oy=} z{7IEuNof08E7bEvZZ>JpHS+mlTQ&78jjp$QA~5w=QruB}!_AZ6KxH2RE5S^OgmtD^ z(acKhw>jfSp1$+)RDI5b%bu^U4cU<&-3_diSI&OIE}eL2R)|*#bb}TIWjwE>`5jQ`-p83&kP$<_;;i*Bd5xD6e@<&4)XYM zGrL*5!H?GV?8x^OoAFTI71pQO1cs%%;X;DtwO+EBcno_!&dgc5d$n4=GiP z2vDhI;=>LGIu^I$dn{Rr?il=WB!6aK@r8$1hRZtSPBk0Q1Jbz$@09E5XbmB2?RKY% zx(^N8MM3L~uc5P=I8)OM+z6=YeJ?||nrf2F9L}o$SD1vfrAs5I0GNCKJ%Ie13Rghb z6>u4lZ+(rf1oI>j)A@S=T+IgC4VU)Q^P7?}7M%rC@Yt$#NnC)`_EZl~l+{zQx$V0# zlwsRrO@wSLBjt$ukN`%@LkiWlnTeZgd4N0qotsktt%>h`SZ*DxDG9IfmS9Ej*fJ&~ zdXTc}cB4sko9ZVh=7jq}F7 zU{uC;*jG!NAWsKNZ8FWg2s%!GgjKz(Xx4KuSY{e+T90HBYtrcwP(e^&X($+?g(ZQN{!&nRpqd5p?wP7 zQ;et$I_-@|UP<7Qo$9wiR!9{{@<)P8`1j`as zQm-EN_{snJ^-f92wGDocdIP_Mw_u%7BZbE~!;~|)C&6CZ{}~=#yXIJ%GW`p86o6pk zN3I}M-Q#Dv7B?0J%J$en429~<>m^iM8*SSh#*Gsmv) zk*|?C@iT=!pYIvi5Rkz*^K8Nr6P5^qN=&BB;%TDA_eP@pZWr?25}9i@9S*$-0KYcY z+%oRp*g@OJA{wfo$V6Dt%SCYM#y_Wlafg)<+A+H<}cfNUvagA+0 zy{ai;d>klEb&lG0S>65w8dO{Y0slthj8wXxC+Wq)#p8@Y@ggC*Gf>IgZFP3{+(rVfSu zhEnT7V_J2#rLK|ogL5+a%tOEK(IoH>lDbD3w#fuTy>Z`_2zwSyVEa5_^N~?a_Aua6wQbQ+CJw;Kk+6D}y6!p9>g%lN*)GB_$tVn^UztVUH=&1+6Bk!XE?Kh?U?bmJn; z+`H)hNv#qaGHfJNfr(xp{UrQ>!!MEawJjl;aNLB^94SFS?0bxoC~&d3V)(~ecSi*B zA+C~rQvSFHbim57;d#w%keuLes?WzV&_c33CUmx1 zspzJ$&y}9ko@Q#|uESQXoa2@~m7ileM#P4{17$baRfSiJbl%yiu$b(&Kh(ty9T?1S zn_&ofI6P1Ix7qtsPeg5OAwSk`6jO`IeThy3WpD(q)qVKQG)hcG*EbJ92F9aK*ThY} zoa-r;#FA&@8^))!p+~5a=?@hra3JiQn;-B;9AC(}Cm20Hf1D_#QM}pUhd?xY%((pay+eanU@+xp>m1G3X7WMO_5jpxU!9Q_e~@dYRn@1NHb0%+9Ra3lxo74xnQX zeMGaQF*(l$7~UnRae&E;{MkObZ@0{Qt>c;z{CbU7xh7Yw?QiCIU!jS8i!XpVHFVqF zi(aYQ4?y|1uh~YA@22vp-XIxo zN~aeX(EeWyUj4BG;$Ebn{e>Q$gBJ~Da$nzZJaPF~+`ff{znKz3U5rs~0yBDmvnFr! zi_gsentu3}jhPSplY01DaDPgb$<@R4_b|N*Efd|)u$-ne18u~Z+_1uDmQofab`~}O zG!x)oPxu1?p#Q%9EQCMxH$CC%!VL2t+WrnQwl=f-k4>cK7BmckwfW|iiD`6YwIU^#}QH)&CRpSM`5F{(qsr9{eZduLu7`3jZK~9r^z!2LBL$ k{rOMCzkb&7AL75fy2gi+79@rSkzvcuG52 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