From b94f795c257e32a1d7be1115568b5b6f576dec37 Mon Sep 17 00:00:00 2001 From: Daniel Sandler Date: Thu, 28 Jan 2010 15:12:04 -0500 Subject: [PATCH] New full-screen activity for USB mass storage interaction. Still TODO: patch into forthcoming callbacks from MountService so the USB storage activity always shows the correct state of the device. (Right now it only refreshes its display onResume.) Bug: 2299129 --- .../internal/app/UsbStorageActivity.java | 114 ++++++++++++---- .../internal/app/UsbStorageStopActivity.java | 123 ------------------ core/res/AndroidManifest.xml | 1 - core/res/res/drawable-hdpi/usb_android.png | Bin 0 -> 4924 bytes .../drawable-hdpi/usb_android_connected.png | Bin 0 -> 4734 bytes .../res/layout-land/usb_storage_activity.xml | 70 ++++++++++ core/res/res/layout/usb_storage_activity.xml | 61 +++++++++ core/res/res/values/strings.xml | 22 ++-- .../java/com/android/server/MountService.java | 2 +- 9 files changed, 230 insertions(+), 163 deletions(-) delete mode 100644 core/java/com/android/internal/app/UsbStorageStopActivity.java create mode 100644 core/res/res/drawable-hdpi/usb_android.png create mode 100644 core/res/res/drawable-hdpi/usb_android_connected.png create mode 100644 core/res/res/layout-land/usb_storage_activity.xml create mode 100644 core/res/res/layout/usb_storage_activity.xml diff --git a/core/java/com/android/internal/app/UsbStorageActivity.java b/core/java/com/android/internal/app/UsbStorageActivity.java index b8a2136cc9d00..37ea3527841ae 100644 --- a/core/java/com/android/internal/app/UsbStorageActivity.java +++ b/core/java/com/android/internal/app/UsbStorageActivity.java @@ -16,7 +16,7 @@ package com.android.internal.app; -import android.app.AlertDialog; +import android.app.Activity; import android.content.BroadcastReceiver; import android.content.Context; import android.content.DialogInterface; @@ -28,16 +28,23 @@ import android.os.IMountService; import android.os.Message; import android.os.RemoteException; import android.os.ServiceManager; +import android.widget.ImageView; +import android.widget.Button; +import android.widget.TextView; import android.widget.Toast; +import android.view.View; /** * This activity is shown to the user for him/her to enable USB mass storage * on-demand (that is, when the USB cable is connected). It uses the alert * dialog style. It will be launched from a notification. */ -public class UsbStorageActivity extends AlertActivity implements DialogInterface.OnClickListener { - - private static final int POSITIVE_BUTTON = AlertDialog.BUTTON1; +public class UsbStorageActivity extends Activity { + private Button mMountButton; + private Button mUnmountButton; + private TextView mBanner; + private TextView mMessage; + private ImageView mIcon; /** Used to detect when the USB cable is unplugged, so we can call finish() */ private BroadcastReceiver mBatteryReceiver = new BroadcastReceiver() { @@ -53,16 +60,49 @@ public class UsbStorageActivity extends AlertActivity implements DialogInterface protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - // Set up the "dialog" - final AlertController.AlertParams p = mAlertParams; - p.mIconId = com.android.internal.R.drawable.ic_dialog_usb; - p.mTitle = getString(com.android.internal.R.string.usb_storage_title); - p.mMessage = getString(com.android.internal.R.string.usb_storage_message); - p.mPositiveButtonText = getString(com.android.internal.R.string.usb_storage_button_mount); - p.mPositiveButtonListener = this; - p.mNegativeButtonText = getString(com.android.internal.R.string.usb_storage_button_unmount); - p.mNegativeButtonListener = this; - setupAlert(); + setTitle(getString(com.android.internal.R.string.usb_storage_activity_title)); + + setContentView(com.android.internal.R.layout.usb_storage_activity); + + mIcon = (ImageView) findViewById(com.android.internal.R.id.icon); + mBanner = (TextView) findViewById(com.android.internal.R.id.banner); + mMessage = (TextView) findViewById(com.android.internal.R.id.message); + + mMountButton = (Button) findViewById(com.android.internal.R.id.mount_button); + mMountButton.setOnClickListener( + new View.OnClickListener() { + public void onClick(View v) { + mountAsUsbStorage(); + // TODO: replace with forthcoming MountService callbacks + switchDisplay(true); + } + }); + + mUnmountButton = (Button) findViewById(com.android.internal.R.id.unmount_button); + mUnmountButton.setOnClickListener( + new View.OnClickListener() { + public void onClick(View v) { + stopUsbStorage(); + // TODO: replace with forthcoming MountService callbacks + switchDisplay(false); + } + }); + } + + private void switchDisplay(boolean usbStorageInUse) { + if (usbStorageInUse) { + mUnmountButton.setVisibility(View.VISIBLE); + mMountButton.setVisibility(View.GONE); + mIcon.setImageResource(com.android.internal.R.drawable.usb_android_connected); + mBanner.setText(com.android.internal.R.string.usb_storage_stop_title); + mMessage.setText(com.android.internal.R.string.usb_storage_stop_message); + } else { + mUnmountButton.setVisibility(View.GONE); + mMountButton.setVisibility(View.VISIBLE); + mIcon.setImageResource(com.android.internal.R.drawable.usb_android); + mBanner.setText(com.android.internal.R.string.usb_storage_title); + mMessage.setText(com.android.internal.R.string.usb_storage_message); + } } @Override @@ -70,6 +110,18 @@ public class UsbStorageActivity extends AlertActivity implements DialogInterface super.onResume(); registerReceiver(mBatteryReceiver, new IntentFilter(Intent.ACTION_BATTERY_CHANGED)); + + boolean umsOn = false; + try { + IMountService mountService = IMountService.Stub.asInterface(ServiceManager + .getService("mount")); + if (mountService != null) { + umsOn = mountService.getMassStorageEnabled(); + } + } catch (android.os.RemoteException exc) { + // pass + } + switchDisplay(umsOn); } @Override @@ -79,19 +131,6 @@ public class UsbStorageActivity extends AlertActivity implements DialogInterface unregisterReceiver(mBatteryReceiver); } - /** - * {@inheritDoc} - */ - public void onClick(DialogInterface dialog, int which) { - - if (which == POSITIVE_BUTTON) { - mountAsUsbStorage(); - } - - // No matter what, finish the activity - finish(); - } - private void mountAsUsbStorage() { IMountService mountService = IMountService.Stub.asInterface(ServiceManager .getService("mount")); @@ -108,6 +147,22 @@ public class UsbStorageActivity extends AlertActivity implements DialogInterface } } + private void stopUsbStorage() { + IMountService mountService = IMountService.Stub.asInterface(ServiceManager + .getService("mount")); + if (mountService == null) { + showStoppingError(); + return; + } + + try { + mountService.setMassStorageEnabled(false); + } catch (RemoteException e) { + showStoppingError(); + return; + } + } + private void handleBatteryChanged(Intent intent) { int pluggedType = intent.getIntExtra("plugged", 0); if (pluggedType == 0) { @@ -120,5 +175,10 @@ public class UsbStorageActivity extends AlertActivity implements DialogInterface Toast.makeText(this, com.android.internal.R.string.usb_storage_error_message, Toast.LENGTH_LONG).show(); } + + private void showStoppingError() { + Toast.makeText(this, com.android.internal.R.string.usb_storage_stop_error_message, + Toast.LENGTH_LONG).show(); + } } diff --git a/core/java/com/android/internal/app/UsbStorageStopActivity.java b/core/java/com/android/internal/app/UsbStorageStopActivity.java deleted file mode 100644 index 557a5232a6246..0000000000000 --- a/core/java/com/android/internal/app/UsbStorageStopActivity.java +++ /dev/null @@ -1,123 +0,0 @@ -/* - * Copyright (C) 2007 Google Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.internal.app; - -import android.app.AlertDialog; -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.DialogInterface; -import android.content.Intent; -import android.content.IntentFilter; -import android.os.Bundle; -import android.os.Handler; -import android.os.IMountService; -import android.os.Message; -import android.os.RemoteException; -import android.os.ServiceManager; -import android.widget.Toast; - -/** - * This activity is shown to the user for him/her to disable USB mass storage. - * It uses the alert dialog style. It will be launched from a notification. - */ -public class UsbStorageStopActivity extends AlertActivity implements DialogInterface.OnClickListener { - - private static final int POSITIVE_BUTTON = AlertDialog.BUTTON1; - - /** Used to detect when the USB cable is unplugged, so we can call finish() */ - private BroadcastReceiver mBatteryReceiver = new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - if (intent.getAction() == Intent.ACTION_BATTERY_CHANGED) { - handleBatteryChanged(intent); - } - } - }; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - // Set up the "dialog" - final AlertController.AlertParams p = mAlertParams; - p.mIconId = com.android.internal.R.drawable.ic_dialog_alert; - p.mTitle = getString(com.android.internal.R.string.usb_storage_stop_title); - p.mMessage = getString(com.android.internal.R.string.usb_storage_stop_message); - p.mPositiveButtonText = getString(com.android.internal.R.string.usb_storage_stop_button_mount); - p.mPositiveButtonListener = this; - p.mNegativeButtonText = getString(com.android.internal.R.string.usb_storage_stop_button_unmount); - p.mNegativeButtonListener = this; - setupAlert(); - } - - @Override - protected void onResume() { - super.onResume(); - - registerReceiver(mBatteryReceiver, new IntentFilter(Intent.ACTION_BATTERY_CHANGED)); - } - - @Override - protected void onPause() { - super.onPause(); - - unregisterReceiver(mBatteryReceiver); - } - - /** - * {@inheritDoc} - */ - public void onClick(DialogInterface dialog, int which) { - - if (which == POSITIVE_BUTTON) { - stopUsbStorage(); - } - - // No matter what, finish the activity - finish(); - } - - private void stopUsbStorage() { - IMountService mountService = IMountService.Stub.asInterface(ServiceManager - .getService("mount")); - if (mountService == null) { - showStoppingError(); - return; - } - - try { - mountService.setMassStorageEnabled(false); - } catch (RemoteException e) { - showStoppingError(); - return; - } - } - - private void handleBatteryChanged(Intent intent) { - int pluggedType = intent.getIntExtra("plugged", 0); - if (pluggedType == 0) { - // It was disconnected from the plug, so finish - finish(); - } - } - - private void showStoppingError() { - Toast.makeText(this, com.android.internal.R.string.usb_storage_stop_error_message, - Toast.LENGTH_LONG).show(); - } - -} diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 54e15a598d54e..665088a4c5baf 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -1240,7 +1240,6 @@ &BNa=L!RZEK&vDI1zq0AVB)-JYKV+p#5Dv@arMd@NFEfQ6<_C~~3 zv_K80w6x2UcOT1NVvc6RbRMIh4M%F`x#0)Z9h+&-Oi1iZD>aO%|<$)A|g%!k~SX50R%_@tPlVK zltQp^f>~W73OkPrfD>%grtezgf$;ThbC<2V&0Nmf3@G`caw81RoNZHYHmaEix`_B| zqDdMYzMk`dzgPW1IL2bgV(^3E-@H|=<&t5b7is*afbN2_S`;GU-x z0{%V_b=f|d5YJvRa?&1*72+-Sv#;iH$u!X{DK@1Tr#9`Z+~5Ahp;s$Mowhb0?##*O z-qwf=t;~Ozd6$8hWFgb-a>ASy_z`c znZu9jJ;V`?6>gTTGLah71*@5G|E1j@LN!9xEfQqP-3_CZ(Joe!y}OS272Lv~ zLfr?Ar4XG$#gi_Cl+FGmIBig21{H>@=r3@G7gnft504M8i9)))TEfA&AXueCUt5{~ zS2VIvg%hcpz^Uckq0SATiGzI|H77T(y&cEnZ!JD-)T?3XTJr>!?8g>mfL?rlANMIU zv?B!B^fda2dH+Q8p!Fk|$NrUdZp?KWlE_07v#*5VxP{ItL2jt}@Za9p+DW^JN6Ro6 zr%nJlk@g%YFjuKG_E^29-<7sX11Axv&Cnz5;r>!T zaLmm71N8dCMm3sq@km88BQ}bDdvEYKD3t(K_33NRV;=^lTh^9uU2HzGNN}k4*y}1P zG&m__tmWFPTAb0myDqP(g(bgv(D-u#@cc_*59XH7M)kz&ojHHZPU4qt%n{wzk-(1& z=2^dpXPSa~e|eAC0F7urV(K-VuZz`|*GDhD8dVq(AWC;YOci}P?>sPNHCf&r1`&tT zvzkT}Ara!^xaI4NS-pry&HE)gtIUzI1|FK12};9*%|d2W7xhiZUAp_+y?&@9aq`kg zdB%$-e)Un1oX<(p25=;--OxVVFtss|X0!qk3N71y<^adY$2eYgEW4JMsT7!$?;QK`G*gG0#E>Xi!b z9>uMA984NcXxAw5&^VM!Ctg2Xu4ow&-{@1fuUX@y7p@ZZ;o{H-@m&s78N~e)^oE6L zpqkCbix9oU623Sv56y&Qr_8y8UYqF!b97#JFu=8Z+XT{5)?x9zppv1y}wJIRW1@JgnMPhO6#pp z=HTT#m>U?2!)=u#Hxp?@5vW`A2ov!x>ur4^iXo%7-B8XUIycPVfpkD9n-KHSftL2>yeBw#j}8RF5hQz^QPS z1!h>9ZBU>xJf+U}Phov$#L0Q<;SnNO zl<^u@EH1C6{Z;6u5WPBb6q~RX!2>_yqxbVsIcuafKm=fX`IC*eGPX>`^#Bz}1 z<&kZ|8Fq`b0W2Bn@`Uh+rg=CCL5M4@;*Q`z;Cz4q&J>m-+xsJlFX9m&#wJgLXMrQS?f18 z=U4|kgpxrh2GK{8`a*m1~8C`T5>1 z^`LaCFUI^t`{eTiQ6-@fI%5-#JDRYp31?K=Tk$KUl+}YhG#fJ15c@75h+fWkY5MXt zd+5O-u26ZV$s@DGuCDIk!K=wJnK7v9)mj!X3DU1Ac7Z zx@W=_)xs4r=64l}8^cg?Eb;Z7oASj(_r;5Yi@>po$B&q4_<`lGSa%`_M-#>gD_6Hq+)-0wmEAsy-B|2|V2CA>-+{JPLRc**h<%}i2gHn!9y+!1c{G-Q*(Z7`K!m-e+J()X3vdKv(OH)0h zU{uE)eWbk}zlB@>d-SMvQXTee_HKP`4_n@|-gXmB1AzRQWC>y76`13N83Th;JJ-gg z_3qg(o0i?ni_KTe&MKkCzMIisKt&}cdUlcU!(Osa=d5J(UL`e_#$V}Ajc}D(MfYTD z`0!ILEs;)z+5@E*krKj{IlBvH9nYTS7zK^K)mT_|NwBM~8Q{=AeNVaD<#ky$%dJn5 z$=S=sEOp&-;D6~wulhj#+4vHOo^o!YyQ;&X7x!vs``%O0_u6h7f>MYgQr1E6%gn4| z>e=8{jv-F(hls9QjD)8LCe+ou=?tw&E#?o`@)cED1$XM~l$r$4fB9Fxl ze5ZLuDa7s~TfhL$z(=iY(1#pM=qQipt9{ny-- zunyEmR*DvyOI_@oWtFlGN)dPJZtz1I&cW#eVvM_Kk^XrrCrRA`WL8vCC>hmIKOtWG zClJzb`HuLkIw;~n)>=>uB#6odX^4Z17kcqcC!@=x5hqDM3zKW6!Nl1rumPf41&iwB z*!i6=rTo+Rdr3kN#o!(2pN;3jAe5`opT~u2r4U)6pK;DFEp0e7w<_h%n|{WLOS}_V zO|PwALeynDc*$}LkiK}#q#A1<=zee&R6ZHhIybZ%ZW45dAdCMz2E~)BIqe>+{dYny z(ju~gmB{1@o6Rbk`u$b!6U{3bCCX0%#0Sfg`TX3#GPyQmLG3qT+NZ-e{Oi^j+)Iov z{*OZ?gk&aNXfb=#F0t2Yr2YEvG7=Hz{rYm)#qC!uPG7#Nub(A43*kwYY-i zJ5kOKIwG_*u5yCgDj|i9Zl$iwOk1vag|eauwTmFv*rL?YuJ^f(&MrZO6h=iphK-6d zq`W)iURk3|BvVYRd1zhXht*DSvLCf4P+wr2^6Yef-}v3oFIA(aB6|qKZD&qoxWb4J zhFSa?3auVMx_>0jDyuzEjj;6|!`>I&-V3I3a&Eyx6SNs|ZG?w?Tv#UzMVl02PP-%R&Z`7ii zP5@V>=pNj|Rkc_!{hBg*{GarI%D{;3K=+zAWR0zlfeYSP2g-+EO>9-l%zF2T1m6XI zt>q}rvRKeQE4Hw*_P^28j|Bc7NR0(k{(@f+;6D({Kd63m|AXpZAdSV~-&CBep!|P9 zxgS)?r2ONot%2Lb5bUhYHI(9t#R7?66P~zGl%uNqhX*Z6JgSi6qjh zF?*_M;-$HV5Xo{(H`#!Ptg-P?v7UdK#{~kL4V(^n%l$T$AiI4>W`a`V>nQ1^%O!*|7iv?wLG^VWdI&XDnj=-j7|~{+9@Ihh!kbbYh(x4iq*UC8=7%BE zveMnPl_0ba&e|p~RLhQtB`b5kJ0EsmPsFT+6C}tbx;IiyZI=^;QgsC?$2)*zVnYM0 zf`yI?eyZ45+^X{bSTwZzDs*tI<*iyKd7y}{|G3t>G~``7L)>;dA#K6-qJabd>qLI} zGWVqn{^a#aIj8vt#$hHwHiPmnsm+^zfwO<3@PF&$Z{2>^nuQsMl_y>Xalt103g(6E z;wj>%^`A(84rIu^NGX>SApN=Xd970o=|X0@5Y`kGb#ASLu4GG zpm0{uA@+X;w>Nl-H}iMK*bP2R_}gH^Z$^JL-k9Gx8nPiJv%&2YK%-RPNu^R(WSF%5A{HAl3DVoD6oF28y~#Hx)oUy z=iW=iDk>_nl9qLdX*YXoSEc53`?nmYPIuQgOU)vRbZ3PPh`V%hl2B^N36-`*Rf}-C zm1K^_w-A-x>I&I?`CVf4CjaDVXtZ0dNqf`aoRZZmJlQo9H6m{ z*_sT?9Buk#Br1uO9s+tgI#vHs)%g#HvATg_=L4L-ho z!Y4ZPy{{PnuxsC$lThasmU(hk z=X=jqeKHDuy|vWY5tG|`btm9(w|Ex7iY_bx1BgOy6L!c`0D>k|dLL3q&@@;^^u-b! zi@Ym#DRj5+IMelCbi)QrB%QR__kwy9DIsb3oSs1$=lJI$|0{!45U3M~|5W#v<~J5k zopG*Ne1pWLNTawi{upQJ{RB%HbFl+0Pob9*covCO210u+Iu$c*QQ|h@e;7eO`xfa} zkPl8={M;sx+C2Xn1*%pPh3KDM7?Us5Yz-t8&(S?_vI5hs8X~f&IhEsYS%@VwvyNx9 znym-ls}Zen>Cs%J5oVrKGXu(E+!({U(y6A%Mj*t^(M+0|fJQCM5OdcqN8xJVIO1t^N8r>-vdby>Vq964B<5wslD( zB<&(GKrRrh!dc=qmud;vkc|O}XiK+_zs7IykmGD*U1Yf^p? zN87+?mPs3KwUuV^oLPhyKiq&-5FF__MMoS|Xrnurf72{Q0W1N#Vy75KJ8;Lc=M?JN zPs9Im98%|!+Q;9&L`P+stNCWYH%R>p*K;2%o|KL!9P}z4Zht1m$j@u06uA)RHe|d= zb`4+by+ZL7$klYhc;)ELoaTtbZnM{K-dK$}j?qApO#UY%%NlQfM_V(!Q5x|rndE;Q z_aa+Lp3*5jl+5TmtUd|zZq#7$9V9BYZ_MS#a-*K^SV+_TbSRC0y09ZADC)D`mtFdU zHe6a?1vvv`DLFc|K-t}TBYJRKm}?xfb!2nS`JK9>>M=LXayi3wT~s2WEZ|-2Q}a%2 z=um=t&Cr(*TbpbucyOfBgTU96+mMkZ*Sd=?v1Tx(4Ld2mI8KYcbI3~r;z9V}#^R*WCE5e8;JE!d zxWM`>gbO5(f5H6#vR#Hzrc;M9 zF+1^*=<}h2!H#!?+?@}q*cghL6eE*#cF2<@U>fb}Hr(kVYXuu|!KxQESVzW2k{72e z%eOfLo}#E)jk>Ig+xR=XdpsJGF1@=tMfj9?ckuna>XW=qK5e}bM3!Lkk^<|;0*D7^ zPzIMH>$A0;hE9dryT0pXK2Ok(N9U+%59{c4s}~7eTR7#E+0-dft+=o&jimAWZdk&| zwS}Avqq+`lcw~TeFcaSMHe;FOdj)qiuxgQ4`et8T7S*A%HCxIxGNn}`J_-cQso)we z$&!~8nX=H$Sc$hE!gduMwg<0AN-~U(oajXEyc1NTUJi*jpa@*uD=hBLUPB*!TH48-U#&52c_oHC;p~WQ1i9jeF z@6Nu5{?2`{r4_Iljl+-T(vsQZP8Y$9V@VdO}8IXz7%&m8_XbZlh{nJ{RT)QpZ; zQ<7lbC$bYk4xBjut z#F5(rE@UGN|FhT~m|nCD7H!#&dWJ~wXeyP)5?t65#|N`+6WFCRw(BKr!*0*xH{%KH zV7G}}X#}B`r0uA4d}~cU_Pg$-xJa(U0Q6M*L6j9eDEL(iU+rbdo`r`&=}P|d{;AS> zZ&Q5Xv1>J6F+7Y!9v{88c^|Syrrk562Y6jfMSNM)nR?%xH#2{t`Km;ll^8VJhIp#( zM9?8wu>GJ5^=M*Wr#ud*<-gK7QNw;~g{yN5+VbRKsk>Xy_#OVGup&gXfwd7Tj&Q$> znvIhlNQpP&c~iYDdx9jG7@$ghuu^)9;qZzRRuH7g#R8n3+w8UX%;d8fGF}p}ex3Ew ziRbK-IgIv7AuKvf0sghDrzqR$uJN{Yj0SQCq&9+YkvKh8*J9}UIw8+rS9I=FFHp=A z(!_6^ja6;t!n|2#@8TdFkEIQ5m?CwfO z58oz9T^ThAreRiquxJ^fz@tiGEB#{KWx5nh*6+*plXUA8Q_azc7VX!sUiJ<>Q=mBX z4vJRm2QGG_UyqVkC18geh^VVWj#YVc9qI~{bB`)9jLOPfu?Nl4uwvv8*QP;tJ=L3> z&$+&yq>(TY+9K>A$x7o)pH7<`MGz&t7Y7gr)7+ZglBC}XyV9+5iLA$Ufd@Jr%-H&e zy+OqBqv3#`i9rCyKQb9pA=^(_5J9sScK;k1U^)vL)E^lL>V8bKMQ|tA9s-D>QhG&u z1uuLqI#}I!DJf4G*K=$?tJ~I?8*oIlEERu4682-aUsDAUU)XzWKThtQ3S~nEOBHrO zeVT~AiS|C(3VD{i$+&3L7WXv3VE$@nWh1b)4@)&j724d!#`rD0QL66>yws;Rsrmd; zmJA5>I$>f~nAr85AH$$_1;P4Pn7u|gO54()NtdP@>IJm~^ zQQwpjB*P%Yv!tmiAZb!s+`oXxP}Jb}i*dnNE}%txx41tKCrxgvF-evgmQ{Hbk?mlz zmpc!Q)QwS2o^x^@Z}@nsG-;Ls~xk>LwE`!7K=9I`9ukI6$Y?{K?k4mi{ z8M`?NG|2rZ3eB!cg?#zyT=6xoJ7_cdiPq5kz3dbHFTO58+vQ$&_qMIQC?Lc64(7$B*xs)_p z2~QcE;q#)~KZN0%!^x(`7vHr>k-R=ey7j^(#|^ zEiLq|(-Ftdi1%%R+7Aat{^At8$|o;4t+$BgpX++EW`8g(m5WbBv$VK4k!ThX4TgF4 z6k^PP;~q^roo<=?t!6(-rOJc#zXLu+>2Rr;4mjG`gKJAXN=BM2BbsoJ$#uEUf-b93`jzt@0|8vz*sjtM0|(#o~0 z$ilKZVe~@_w!~#;iptaoS`TGfHvn{*o8jn3D8SUq^)ZLHLwH6Q5 zd@*G@cLpt=**_ejr*~!zKR0g*1I@*2PgY&|Hi=OV+DRS@wB*^p+mD>(pGOz&1Ti-tInCLPWMcFl@Ul^qR>(FwS4eJ1sB~N9Hg*|dX&`gqn9v_kjsPmuS*4mG$ z`CQv#deN1sP&!pFaLhS9^j0X*Koo+-e{1GO&};{8QOCv0xVChvSx8<1T`AyhZ!vNw zrI|aILOw05C<+-DpAU$a#^QNmK{<~;h#Jt(f&H;(MT6>lH13UB*pceW@$J*%+9tON z1>dzILA0fAzX@Vgikp!+xL_o*T0sJqDmi?K(Wea&TJ=r;2{$l65F(xXrrbf~)pEy0 zzk(rnOV;6T7sZBBVAeJ2tpCpG#2FU4gBmef%(861yI|=C#*x}1w;c7{ACNX>AUWJy~$V+3*h^t4sfbKA>(-SC%2-#q8{r(?piHgvF5i${TO9k7{b8@D)ny)g*l*A8#)uP(1^#m!Cr@doQ$j*$saAoa=3yrx--$ zh8~vM`@ut~X7;O6iClO9GkFIj$+?Pszp?XIyBatHLkM_smhQ^9A~%vh%LyTkbY!%| z7QRf$$O$S=q<7($29%JRp@J1LKVtg3Uv-v{0aSfhrk^IyFfs&VduRu&J0T(tQxc!9?CQK4!3~ zQu)vnQSV((H@wh-`aYz2y!!U)DLD7_WgUgijd;^sSbbOPa)@P&=~?imtD2>{Y8C1P zhT19Kq~`6g+p^0mkO7|eb!u2Krg0Bhj8JZ(U>2$$lCwS&Y@~XU1_-527fRx$y?=7W zKePFNb?6TQhR?Z{^~S=Me&L0F{R8cmRr#PPoZ0ve=3ig6}$`9Q8(3|WxON}&Di!r!tCg&yQO^pc4O5#*~ zSb2^cPe{cd(_%%v;gZ{&1DxTXPB&;J>1e?~sO=eUELZ=XI*$|jQ20qp%3vG3@OG|` z&?fOd@06`Ja>pE8c%w;O`-`z*wCmz*oHEx4&hCcuZS%Q}1cRS-%TE&dpWODp2j$<7 zMxfx&p8=!hxer2~gD4;Y9(vACZ`uctJ8h^tv3MNwl3S<$U!5$@fnsvTaPZmh#{6qe z8pb^i@)VBpe&0BK^}%qKM)h5SEu9?Z5(S^ggmTT{`0pEDc(cS1_$dR&gnQ3huRaG2 zbp)I8pYoX6i1voXs3qdp!C1?O24Y)LOUH9XKoHx#5A_oDbj*G7$JdnbdOmt|$hn>i zr+ke&)lb{r`r0MD`2Lv}ztOkxnZh12uj|avy2BT$cV*wSYItP3I0%wlSt_%6B37o+ z{t@SaNd2~E37K7REDr=C7b8c@XBPd4vdhjjW1OQOgcnxhy*5MLo_%g#?yC$Awk(6Glf!QDClk5lQAQ?E1 + + + + + + + + + + + + +