From 0052abdafd7179fc4a7458f09d2f95c74dda0021 Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Thu, 22 Sep 2011 15:49:04 -0700 Subject: [PATCH] charger: update charger UI with official graphics and animation Change-Id: I1b36fa9e380797fe01812b57ac5d8c2c38857993 Signed-off-by: Dima Zavin --- charger/Android.mk | 26 +++ charger/charger.c | 283 +++++++++++++++++++++++------- charger/images/battery_0.png | Bin 0 -> 1292 bytes charger/images/battery_1.png | Bin 0 -> 1290 bytes charger/images/battery_2.png | Bin 0 -> 1289 bytes charger/images/battery_3.png | Bin 0 -> 1291 bytes charger/images/battery_4.png | Bin 0 -> 1269 bytes charger/images/battery_charge.png | Bin 0 -> 2516 bytes charger/images/battery_fail.png | Bin 0 -> 1805 bytes charger/images/charging.png | Bin 9895 -> 0 bytes 10 files changed, 248 insertions(+), 61 deletions(-) create mode 100644 charger/images/battery_0.png create mode 100644 charger/images/battery_1.png create mode 100644 charger/images/battery_2.png create mode 100644 charger/images/battery_3.png create mode 100644 charger/images/battery_4.png create mode 100644 charger/images/battery_charge.png create mode 100644 charger/images/battery_fail.png delete mode 100644 charger/images/charging.png diff --git a/charger/Android.mk b/charger/Android.mk index 75e78d546..ba21a9b2b 100644 --- a/charger/Android.mk +++ b/charger/Android.mk @@ -18,3 +18,29 @@ LOCAL_STATIC_LIBRARIES := libminui libpixelflinger_static libpng LOCAL_STATIC_LIBRARIES += libz libstdc++ libcutils libc include $(BUILD_EXECUTABLE) + +define _add-charger-image +include $$(CLEAR_VARS) +LOCAL_MODULE := system_core_charger_$(notdir $(1)) +LOCAL_MODULE_STEM := $(notdir $(1)) +_img_modules += $$(LOCAL_MODULE) +LOCAL_SRC_FILES := $1 +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_CLASS := ETC +LOCAL_MODULE_PATH := $$(TARGET_ROOT_OUT)/res/images/charger +include $$(BUILD_PREBUILT) +endef + +_img_modules := +_images := +$(foreach _img, $(call find-subdir-subdir-files, "images", "*.png"), \ + $(eval $(call _add-charger-image,$(_img)))) + +include $(CLEAR_VARS) +LOCAL_MODULE := charger_res_images +LOCAL_MODULE_TAGS := optional +LOCAL_REQUIRED_MODULES := $(_img_modules) +include $(BUILD_PHONY_PACKAGE) + +_add-charger-image := +_img_modules := diff --git a/charger/charger.c b/charger/charger.c index 9320e8a55..a6f8509da 100644 --- a/charger/charger.c +++ b/charger/charger.c @@ -49,10 +49,12 @@ #define min(a,b) ((a) < (b) ? (a) : (b)) #endif +#define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0])) + #define MSEC_PER_SEC (1000LL) #define NSEC_PER_MSEC (1000000LL) -#define SCREEN_ON_TIME (5 * MSEC_PER_SEC) +#define BATTERY_UNKNOWN_TIME (2 * MSEC_PER_SEC) #define POWER_ON_KEY_TIME (2 * MSEC_PER_SEC) #define UNPLUGGED_SHUTDOWN_TIME (10 * MSEC_PER_SEC) @@ -72,22 +74,46 @@ struct power_supply { char type[32]; bool online; bool valid; + char cap_path[PATH_MAX]; +}; + +struct frame { + const char *name; + int disp_time; + int min_capacity; + + gr_surface surface; +}; + +struct animation { + bool run; + + struct frame *frames; + int cur_frame; + int num_frames; + + int cur_cycle; + int num_cycles; + + /* current capacity being animated */ + int capacity; }; struct charger { int64_t next_screen_transition; int64_t next_key_check; int64_t next_pwr_check; - bool screen_on; struct key_state keys[KEY_MAX + 1]; - gr_surface surf_charging; int uevent_fd; struct listnode supplies; int num_supplies; int num_supplies_online; + struct animation *batt_anim; + gr_surface surf_unknown; + struct power_supply *battery; }; @@ -100,11 +126,47 @@ struct uevent { const char *ps_online; }; +static struct frame batt_anim_frames[] = { + { + .name = "charger/battery_0", + .disp_time = 750, + .min_capacity = 0, + }, + { + .name = "charger/battery_1", + .disp_time = 750, + .min_capacity = 20, + }, + { + .name = "charger/battery_2", + .disp_time = 750, + .min_capacity = 40, + }, + { + .name = "charger/battery_3", + .disp_time = 750, + .min_capacity = 60, + }, + { + .name = "charger/battery_4", + .disp_time = 750, + .min_capacity = 80, + }, +}; + +static struct animation battery_animation = { + .frames = batt_anim_frames, + .num_frames = ARRAY_SIZE(batt_anim_frames), + .num_cycles = 3, +}; + +static struct charger charger_state = { + .batt_anim = &battery_animation, +}; + static int char_width; static int char_height; -struct charger charger_state; - /* current time in milliseconds */ static int64_t curr_time_ms(void) { @@ -185,7 +247,7 @@ static struct power_supply *find_supply(struct charger *charger, static struct power_supply *add_supply(struct charger *charger, const char *name, const char *type, - bool online) + const char *path, bool online) { struct power_supply *supply; @@ -195,6 +257,8 @@ static struct power_supply *add_supply(struct charger *charger, strlcpy(supply->name, name, sizeof(supply->name)); strlcpy(supply->type, type, sizeof(supply->type)); + snprintf(supply->cap_path, sizeof(supply->cap_path), + "/sys/%s/capacity", path); supply->online = online; list_add_tail(&charger->supplies, &supply->list); charger->num_supplies++; @@ -293,7 +357,8 @@ static void process_ps_uevent(struct charger *charger, struct uevent *uevent) if (!strcmp(uevent->action, "add")) { if (!supply) { - supply = add_supply(charger, uevent->ps_name, ps_type, online); + supply = add_supply(charger, uevent->ps_name, ps_type, uevent->path, + online); if (!supply) { LOGE("cannot add supply '%s' (%s %d)\n", uevent->ps_name, uevent->ps_type, online); @@ -459,74 +524,153 @@ static void android_green(void) gr_color(0xa4, 0xc6, 0x39, 255); } -static void redraw_screen(struct charger *charger) +/* returns the last y-offset of where the surface ends */ +static int draw_surface_centered(struct charger *charger, gr_surface surface) { - int surf_height; - int surf_width; + int w; + int h; int x; - int y = 0; - int batt_cap; - int ret; - char cap_string[128]; - char cap_path[256]; + int y; - clear_screen(); + w = gr_get_width(surface); + h = gr_get_height(surface); + x = (gr_fb_width() - w) / 2 ; + y = (gr_fb_height() - h) / 2 ; - if (charger->surf_charging) { - surf_width = gr_get_width(charger->surf_charging); - surf_height = gr_get_height(charger->surf_charging); - x = (gr_fb_width() - surf_width) / 2 ; - y = (gr_fb_height() - surf_height) / 2 ; + LOGV("drawing surface %dx%d+%d+%d\n", w, h, x, y); + gr_blit(surface, 0, 0, w, h, x, y); + return y + h; +} - gr_blit(charger->surf_charging, 0, 0, - surf_width, surf_height, - x, y); - y += surf_height; +static void draw_unknown(struct charger *charger) +{ + int y; + if (charger->surf_unknown) { + draw_surface_centered(charger, charger->surf_unknown); } else { android_green(); y = draw_text("Charging!", -1, -1); + draw_text("?\?/100", -1, y + 25); } +} - cap_string[0] = '\0'; - if (charger->battery) { - ret = snprintf(cap_path, sizeof(cap_path), - "/sys/class/power_supply/%s/capacity", - charger->battery->name); - if (ret <= 0) - goto done; - ret = read_file_int(cap_path, &batt_cap); - if (ret >= 0) - snprintf(cap_string, sizeof(cap_string), "%d/100", batt_cap); +static void draw_battery(struct charger *charger) +{ + struct animation *batt_anim = charger->batt_anim; + struct frame *frame = &batt_anim->frames[batt_anim->cur_frame]; + + if (batt_anim->num_frames != 0) { + draw_surface_centered(charger, frame->surface); + LOGV("drawing frame #%d name=%s min_cap=%d time=%d\n", + batt_anim->cur_frame, frame->name, frame->min_capacity, + frame->disp_time); } +} - if (cap_string[0] == '\0') - snprintf(cap_string, sizeof(cap_string), "?\?/100"); +static void redraw_screen(struct charger *charger) +{ + struct animation *batt_anim = charger->batt_anim; - y += 25; - android_green(); - draw_text(cap_string, -1, y); + clear_screen(); -done: + /* try to display *something* */ + if (batt_anim->capacity < 0 || batt_anim->num_frames == 0) + draw_unknown(charger); + else + draw_battery(charger); gr_flip(); } -static void update_screen_state(struct charger *charger, int64_t now, - bool force) +static void kick_animation(struct animation *anim) { - if (!force && ((now < charger->next_screen_transition) || - (charger->next_screen_transition == -1))) + anim->run = true; +} + +static void reset_animation(struct animation *anim) +{ + anim->cur_cycle = 0; + anim->cur_frame = 0; + anim->run = false; +} + +static void update_screen_state(struct charger *charger, int64_t now) +{ + struct animation *batt_anim = charger->batt_anim; + int cur_frame; + int disp_time; + + if (!batt_anim->run || now < charger->next_screen_transition) return; - if (!charger->screen_on) - charger->next_screen_transition = now + SCREEN_ON_TIME; - else + /* animation is over, blank screen and leave */ + if (batt_anim->cur_cycle == batt_anim->num_cycles) { + reset_animation(batt_anim); charger->next_screen_transition = -1; - charger->screen_on = !charger->screen_on; + gr_fb_blank(true); + LOGV("[%lld] animation done\n", now); + return; + } - gr_fb_blank(!charger->screen_on); - if (charger->screen_on) - redraw_screen(charger); - LOGV("[%lld] screen %s\n", now, charger->screen_on ? "on" : "off"); + disp_time = batt_anim->frames[batt_anim->cur_frame].disp_time; + + /* animation starting, set up the animation */ + if (batt_anim->cur_frame == 0) { + int batt_cap; + int ret; + + LOGV("[%lld] animation starting\n", now); + ret = read_file_int(charger->battery->cap_path, &batt_cap); + if (ret < 0 || batt_cap > 100) { + batt_cap = -1; + } else if (batt_anim->num_frames != 0) { + int i; + + /* find first frame given current capacity */ + for (i = 1; i < batt_anim->num_frames; i++) { + if (batt_cap < batt_anim->frames[i].min_capacity) + break; + } + batt_anim->cur_frame = i - 1; + + /* show the first frame for twice as long */ + disp_time = batt_anim->frames[batt_anim->cur_frame].disp_time * 2; + } + + batt_anim->capacity = batt_cap; + } + + /* unblank the screen on first cycle */ + if (batt_anim->cur_cycle == 0) + gr_fb_blank(false); + + /* draw the new frame (@ cur_frame) */ + redraw_screen(charger); + + /* if we don't have anim frames, we only have one image, so just bump + * the cycle counter and exit + */ + if (batt_anim->num_frames == 0 || batt_anim->capacity < 0) { + LOGV("[%lld] animation missing or unknown battery status\n", now); + charger->next_screen_transition = now + BATTERY_UNKNOWN_TIME; + batt_anim->cur_cycle++; + return; + } + + /* schedule next screen transition */ + charger->next_screen_transition = now + disp_time; + + /* advance frame cntr to the next valid frame + * if necessary, advance cycle cntr, and reset frame cntr + */ + batt_anim->cur_frame++; + if (batt_anim->cur_frame == batt_anim->num_frames) { + batt_anim->cur_cycle++; + batt_anim->cur_frame = 0; + + /* don't reset the cycle counter, since we use that as a signal + * in a test above to check if animation is over + */ + } } static void update_input_state(struct charger *charger, @@ -585,7 +729,7 @@ static void process_key(struct charger *charger, int code, int64_t now) } else { /* if the power key got released, force screen state cycle */ if (key->pending) - update_screen_state(charger, now, true); + kick_animation(charger->batt_anim); } } @@ -617,7 +761,7 @@ static void handle_power_supply_state(struct charger *charger, int64_t now) /* online supply present, reset shutdown timer if set */ if (charger->next_pwr_check != -1) { LOGI("[%lld] device plugged in: shutdown cancelled\n", now); - update_screen_state(charger, now, true); + kick_animation(charger->batt_anim); } charger->next_pwr_check = -1; } @@ -634,8 +778,6 @@ static void wait_next_event(struct charger *charger, int64_t now) charger->next_screen_transition, charger->next_key_check, charger->next_pwr_check); - /* TODO: right now it's just screen on/off and keys, but later I'm sure - * there will be animations */ if (charger->next_screen_transition != -1) next_event = charger->next_screen_transition; if (charger->next_key_check != -1 && charger->next_key_check < next_event) @@ -675,9 +817,13 @@ static void event_loop(struct charger *charger) LOGV("[%lld] event_loop()\n", now); handle_input_state(charger, now); - update_screen_state(charger, now, false); handle_power_supply_state(charger, now); + /* do screen update last in case any of the above want to start + * screen transitions (animations, etc) + */ + update_screen_state(charger, now); + wait_next_event(charger, now); } } @@ -688,6 +834,7 @@ int main(int argc, char **argv) struct charger *charger = &charger_state; int64_t now = curr_time_ms() - 1; int fd; + int i; list_init(&charger->supplies); @@ -707,10 +854,23 @@ int main(int argc, char **argv) charger->uevent_fd = fd; coldboot(charger, "/sys/class/power_supply", "add"); - ret = res_create_surface("charging", &charger->surf_charging); + ret = res_create_surface("charger/battery_fail", &charger->surf_unknown); if (ret < 0) { LOGE("Cannot load image\n"); - charger->surf_charging = NULL; + charger->surf_unknown = NULL; + } + + for (i = 0; i < charger->batt_anim->num_frames; i++) { + struct frame *frame = &charger->batt_anim->frames[i]; + + ret = res_create_surface(frame->name, &frame->surface); + if (ret < 0) { + LOGE("Cannot load image %s\n", frame->name); + /* TODO: free the already allocated surfaces... */ + charger->batt_anim->num_frames = 0; + charger->batt_anim->num_cycles = 1; + break; + } } gr_fb_blank(true); @@ -718,7 +878,8 @@ int main(int argc, char **argv) charger->next_screen_transition = now - 1; charger->next_key_check = -1; charger->next_pwr_check = -1; - charger->screen_on = false; + reset_animation(charger->batt_anim); + kick_animation(charger->batt_anim); event_loop(charger); diff --git a/charger/images/battery_0.png b/charger/images/battery_0.png new file mode 100644 index 0000000000000000000000000000000000000000..8128acd43ef5dfb9ef08b5c76416dd0cc7a7bdc6 GIT binary patch literal 1292 zcmd5+OH9;27_RsLQH%#-A_p^;#F*%AXS$DWD=X}_UERb*TsDz-qO=3sKp(b)TQ+(S zFM3ihCPoh&^s0$q;wvggZ_7bKIB+vDYUD&r^x||M=z(zXWRrIOfBygb{>S{&TSJ3q zx;lG08HVY~pH+%<4e0x-qm6#Q96KV>CxjNy!7khio6T*R6&bNK`A zW0;-eW@&_s6wXPyV@0*lCmL8TMKeraf8c8RBqm@CPndR^U4Q+K1*VZ^FD44G;L5mc zo~?R#xH?$UtCPBDu>Ge&Um#He3lj|l)|Bl_L7J`eN>mQFITqAi$Yh#rBsEeP0eDiQ_7j zO0*J-I^G0_L{SVmcs@csBL0j`v>;;py)6a>`?_bk#B^*BGHPSaG)c2G(?$uFTPSQ2 z+kUfAv}IhNxg3f@&a%R~>fSyn;y-C@>Ft+hT+9`*?@W6-J&*C;7MS*K`#_;1%|<%! zne!+PCfTvmBrP6`Pm z6$@?e|Z7>{0!qiZWG46~yzuVhNW>bK>^ zz3;*PfeXWLPR;&$`e~Pa=Rj@k(&O5X+b=tAgImvDEU!MCzxo2@)~{`xIJ0zb;KrUv zZ7$QxbX}Uu+&EOOwRQC@TB zN39gKYskp=^k_W2mJ{CbiqyQ2->- zi055{9Ev(B+gi~?7S6~?hFHS7p^HFL1fK6y;*jIw2?zxU1VK?^F#r-$G?A`qY{ylk zRJ>CHX<3p52o*x;Or<%Xq#_9=mXM?>H)DBNwKP=qYY@K`uCgOnlw71@+s)hdXte`+ z3pTdBg6+^!UyRPGnqdX&;CME&m62tS|aq(^X@BObo9GI-3W^WzDH(##(2>wnPrAJ=q HyfXb8V?CdJ literal 0 HcmV?d00001 diff --git a/charger/images/battery_2.png b/charger/images/battery_2.png new file mode 100644 index 0000000000000000000000000000000000000000..c710ca9ebb27f12d4263e96bbce26e6afed0242a GIT binary patch literal 1289 zcmcIkOK8+U7*6X0AFNss(HNl_3Kp&rzu7g-PlD}o0PDxxRRlQ^^8tqMg@Fp$jrGvD|9kNGF(h6Z=W zR&}pp7$#O2&`M;C$UD2DlYBpH*{qOZ6V@wu*c-)lBS1{f@Uj-HPcs34>x1k>e9K!}Rt=zG2o7W=GJN?Pj^p4@KA_DQAr940Bq~wh_}ERRZ3dE<%?KZu8Og*@O-^q zPt=nMFBs#YEX!0w5aI+84<}u0L~%FlX)9NGO6949Z?{OM%feC z2EvagoTW~{cxF%}*wTa&rSNtPaQ3GUPFDQH7c)J6Jsvh>j zs^_!naGEU|rtMOj9?yKX8Vc+a$jS$v!#3-y*o*iFPy!H&DIf}#@$gpB#M3i3qzCu9)~r2LdwIC#JUj6H$mf%{?|(Ue>)pUi?(BwY zW2WoW@_1t=w>nily*zes>&>p*lk3!KC_^`{PLE%_v@bsQyyGB2=b7$%JI+${vTln0 zh1TRZI%5<@L_1>?N!6GA9p%M^Pt$K-9k}p6w0{fd%GA=0%;Z|w*mmvmI{I@e=!4pw I{$po;0Mz53E&u=k literal 0 HcmV?d00001 diff --git a/charger/images/battery_3.png b/charger/images/battery_3.png new file mode 100644 index 0000000000000000000000000000000000000000..f7a926bb3784614a378d62383994b2baed926848 GIT binary patch literal 1291 zcmb_bOK8+U7>+9zsaC{;Xh9hw>OprilYL|pyV`A%)djcK-35!Nm?qP1Xp@P_)J=O3 zs)F_=Dtgd^;6Xen3VNxcUOebQ){BUbv-kkTi+E6+Yo&2c?DLeDTqF$F_-#CCK1=X(!%U|TtUU%CWKo{DSs zu1OzPCi|=ARXUq+hFvmA_6*iB43p{8-=xC0g4QjA7094{* zkWRq32{RC6Py!`Wk|b>fB1BMtqJYFWl9Z&VKnPk7&)j@#SSf3TmKR&)_!_02A_(<* zJyB04h(97AS(YOWQH(Q0JeY8)5ystMLt8<^f$7^GwTTNNMPrDJ(;UwtolU{8z@@xkWdihHK)sug!d0T(}@tQGIMs;#Ub7Yj?mvjZhNQ zhzHb48k7vvcB6H4JhRzq*td^jtKbs{H0!I_^Y}xQg$PM0D2cF{x13USQB*Th8c78y z$+x)Hztl`%!3a@v{Lw7!U#tV8>0Ilx#oXn=F1s;4yBbrc4mUV%(YB(NuZFKay+)mn z!1CU`m6?w}_FUh5t8#7T{Hpg~el9t=^UT)ivj=Y6{dVZsn>APOUik5J@12c}EBR%q zT4QPNiPg2nl_lMGdQWuRSoiqyx#QCvakjbTVQ~Aj=6!z|TueTHu%Z#`>Q2QXV5&CN z1TgkgN7w#YnOHQ5lo*UDy|}dc9eMVpv;gF{D|7oh)&EL)doZ?~tAmwC?th(OW#qV` L-ml&4JAC{X$3CNb literal 0 HcmV?d00001 diff --git a/charger/images/battery_4.png b/charger/images/battery_4.png new file mode 100644 index 0000000000000000000000000000000000000000..51c4d248ac76bae0aba9dcddb32eb7587f63628d GIT binary patch literal 1269 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 literal 0 HcmV?d00001 diff --git a/charger/images/battery_fail.png b/charger/images/battery_fail.png new file mode 100644 index 0000000000000000000000000000000000000000..25c7e97173770bca8782b4b008902cb2519021ec GIT binary patch literal 1805 zcmbVNdrT8|9Ix`wsby?35hi*m4qt2Ut_8{+R4Bay%`6~q6*rIepcUGy*Ta_jz!bBI zQHczPlaZk=Zqo#!TP%W;>6{Z8t^?5lVgyCXV3HwBM2P#9hwcw!7B6?d$M^U7K7XHY zt}s3}nmfmL4u`|xDq@HPFq^?wGkYc&{|s1+gUN@Lr?H82CTk)YiX&Ci85E?@ky%s% zMXK|*)Kg&`j!ULCDUD52uEABbPCz;_0<+Elv^kuxaI=9_Wm7DaL1k(665c><9S_o~ zCA`!iC89LQD2+D8!cd8p*d&!DTZO54;j5uAGY$;sD3*lGx*WX`H%oXEc5$$FieVl! z0b#Qxyvd-_l<|;^W+*5~fbdmFFaiam0#p-|GrI0xUt;*F=dR9S5c;G~!)~aztCdb6$ zNO>?S6b6T&D25^!CYJ{VAxMZUFeGZ)#>;f0q)`Bi$q6EaKv6j&#blV2AVgwRC=$!0 z@@ZX#-pG=A6*cWw3;a&$60g+7Wei2KG?PTrInxY?*U&6&)X)Y7;prB?=C5+b zHR+%=5pqkIe0O=?_w4ul!(|4?M^k(EQpP~j+nT`QJK-Zs`v;q=TSUHPE%iTQp`qJ$ z?2vpL5*HWO)6*lXyVhIi^@he?;TEW|&yFZqV6TwQ)!1kH25MFuOm08xH7ROXZa>ws zw?S+q-T^r0=_IZ~THwxi4qV^wcY)!59yaVdDlTiZ{BURBF$}{M`2nS+rDeAC^z_U3 zepl_RqiQ=Ygr^+Kzdx&_t>DJpZ6?#>v9a4*e;XekA0E!-+P_RbW%cMfp>3m=#WpoI zavlDKXP)|BUh-&UWTeXaUA$xA)$uv4 z8=vpi`HOd4M1Jf}MNQfUq0Difso&OVEV#ZZT8EvNy5KFpb|1QjuQLlo^Bs;w#i@10 zwyFp29l6gA^GQk8d&BhkvM_d8_rl@sRaX{Ogsv#6CQWVj@{;54u4t`qoP}R`u=Lu` z_m6&Bp8g~=FU9_Zdn38y!_OD9%&7QbYQY0e`R(n2qb>BH-KYD6c5`jzn*Nj&X5gmA z^mWbU3m*2~?dNLe4OzP$;~&>7w7PTGM*zB5yc{QQ;qIfKrTaKq_3c3-uNTDY_0IN_ vDKEYrwE%f5q(vTlnd<}>hq@j`@0!86IOZ~cabk0m^9QAn#}bv1o2>r=oGZPf literal 0 HcmV?d00001 diff --git a/charger/images/charging.png b/charger/images/charging.png deleted file mode 100644 index 94ce4b16b77d32833e6fed5114de3174ab5523b2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9895 zcmbVy1yCH{x8~qBgh2v>Gq^h>Sa1&nm*7rt0s#U9cL)Rv7Tg^|aEHMif-`8)0E0`g zH+lR2*Z%gs-KtkxU0q%0T7UO+-*dln&xur1mB+;5Ue+5`Ps<%^S_gtgzYXo57PFs3GbaFC=F7XNf~ihI0RtRYue zhAqN&&*K6T#|}mKCj8bsS3k)E)M#j<)W|XdB52bhRY1Ok)MC`VRZ-y}vZB={s?fVi z7h!#Xe0kgb%CLCcg#I`>7H{{lrHj}fDU*xAS_w>~!WESVD0FP$`y&xKlM>2nMYPnC?QoTsInsJhU434_c*dFt_7Zh!X_FGlkgYHxP5pkRjCBcHk)bwY= zG@R0cCp1R(g}VB0xKKnR>h=~qMh;F10Y;bI{LBRj^_+L3%p`a>}*X?g4zg~1D7EDOZIf(}Yskyr8F-`^#yzjy9sVe7WXFAX! z2Xijp8M`0}iS1}PyvhS*MwJ>uvtXX^bv^eD9%wyY?t&U2&6nL*E-e&~)T-%W%V9Ox77?-+vINu-W2<=4pN`C3Im#rI1cL^M@@+;9DLX9%FK&fut}; zeRBf-PTqJ6gDXh!I{STYQqQGly%~yZ#A@)&!=s8)Xq+)@px-P)w$q0CmSvskM!@q& z&pJMkH~;QBT^U(54PR<~<^^6&AlQ70%YVu>Da#$T4YTUomwt5Knxazc1tSJOPx`{7 zn_d<21qop7LhU*%7^yS%XOU*!nkf(Iz=+B9wTP33l5Kj}gUHP#ZI~YL_P#xs`}gBo zrk2viWuK4(4a$K4GaBfmwU<4l>(hLN1wx1B6F)@e{=iCq^5eRmBDaj&joLbWs*&!( zqlBPFJ=0(O`>HytUPWkH#tmAUMpx?yMr|PQSFHTFq_utY(-R1J%k8VQ>-$&I>^iUo zxPip2=bI!Auvji7`5=v)VK$Oc^jR;|cUPe4PA7Wz z{+YO3IwSY*Tn>jE?yX=c;QF<0Mdg)CWSLW8&u7oZzHqK1ft6RC7uub2{tH98aB8S7 zHHF}c&zN2Hy{-e=OQunpn;cMZ>Z@Wd;Vhf5syh?*TtD;i>MGl~tY8&TmQ?#h4Zq$d ze`f&hh1g_&K~m3YFZS|_?0_IC4ReA=c*iBgF?#5Yd;hv}I>y3_NT2G3fGv5UoC#3A z5_LrjKtmhepit`K;)zB@O8zY}$;gKx-!X;0puoW8D6=v*2T({8awESZO(NPu^63S-ckdE=)z8A*Tw04)zaVCDDBQV!t zK#*Rq0DXAl8Rnxe7pRaux{>Mo*4UwkFmb-8AfwiPWC*?Uc;L5O?uw39CEI?b#I2}e z7FF-9G4@+c++mpO8}z%Ru(1YOpqO*}N-s-*!rnN~t-RdlHQl z2m9I}{!4|$bYH!{?SO%@n`z{^Ny+!7!LXr}>g`B7nl_Pl+y*bQf&xy+U#4hm9x+IB zJ9SNy`Fk9U8Z)zT4SZ29$CJUqf0&(5vuubB*TDXG+x*hlR=T@I)9bX>Q9##>#0C|} zT}ZSe+ML3~!p38#(0G=H(P#?8V`6=BT#o?8Wd5WaTYd>)KwgE>KY0*=YEskJvuEZv z@6cwm{ke~3-KHwv%p4eL6`HcmoGwTES)%YlO9YK?t!Wa{d!8UfN*mWH&H+Nkn~P5u zR?-4rl$C6yBo=2*2RWkE4J~`W$p&@?OmzE(fd!-lbo3Q5{UqCJKRRQg+QF}_kNW4& zpVh?!BR=7wEVuokFSvM$F$+N3H-FjtInG&JxCh-lLfvSr>CT4pi!A_a2dG-g%}v?m zE*N|YY~rST0;$z8SQREj*7WNDEGy_biF?$ahy+LVt18Kd}AJWMY5 zyUxqgfeb&XjX-Qcbmq5!UEP{Y9hc7 zJ1dl1v~6njd7ozKC?}*GYjZb zvRW4!SJ8NFJ0pa|FyKx^$!XJDLl7-`u4953ULLg3Hw@Ht){B$&xmCjt(?i{WY74_c zHrUMSlmtpppo!^@MTHwECLP3eKYgy;VWg)e`-!j>);{luZ^Zc5&ZaVdJBsJC= zp&=Y}$`05iarx-#!GvqNw>`$Qf2s=PF5|m?N$N6^^Zc()y$s#t*;cq~Imm;^pxX&e z0-g-yL9n4oGEZj_bg;nN>$B#Gg?h_~(8=eGmRnI7b&H^%FJTbKm${W7f8VZdzc4+_ zU&h}8T|1oz!gAyae?eMxg^NL`|-+i2p%tIR+B}Piez*hKV(}v_b3)uYgRwaXwZMwC!wAvo$YZYlF z9igQUDwY;CB8vfpiX>jnw=~Qw5a0z3p5F^TuY#mmswMLfZ7(=!JsDH%`{v*2sSBtA zBcriy7;<3bQF5(0Y!Z(LjVLCj2gBQ^vokldM#WT)6LNSd)^Q?Dm`h z@5VU1{3OMz7T3BOR+~2Ijng_Pi=#4;8WmM}wyrQ*%_kXDdI&6)SSZA7dmr-WPXlP{`ci1-ErqV7@OR$ zqU1vWQUHvR=amhb;rOKgF%D$1G=)@Auv0tH`aQ`1%$u)y^V6T zKf;I2Eek#mudECOO}dBTR5^@Cr$BZDFV1#++umXl9r0~c+BBGLrPJ9ivwX&#?xJb^ zT|2HY5n~h4=Z*5(uZ`L%^(QOqs&a;E-Ym6QW&A=+IuU*W zyt~P76j^pt!$zt5Nx$2t;C;MRu(rY~gF|KS%$&qn8xZo`~ z^2X@;_sU=K84QqH-@A^&Y9A`XQ=ipF%h{0b$RPO6{OgjZp#^>zFy#kXU9O`_{5o^* zYzmcp_Z(`HR+3~K4!q0H*zW;;j7ydlSYWxO1$S(vU-z8x=7pN8==}C75a635Gt&^} z{=L-_=bAc7X8flt8?vk~F@R^11QF}QR&g^Q3q!qV=5C8F_A0Kspwzj}z$qmqzJGK! zg7M|!O3i-&cJd}ei0!TYev->&XfT9GSZ8>(JsPQI`W?fpq=6#h6?t0beV+0>r!R!B zD1YQs02P#U!)$+JSbLP8^N0HTEzW;&JW6;b`XK2rP8mG}vQM`TD#DT;FC_pFB)E%S zZrtuIdky1MZ+dX-Sa$A`SjQJt{n8B1hGPw>}9)`66~IiYm^Oniwf6R z3*>%yRfWaAGdKyy7(RWJkGyezXJF|6hV0w-m6u0{`f-j^{;{Zgn`|*nRSX60q#c#X z8aHMgl+~kpzvDyye=Jl?jjyPM{5p((G$; zD8Ny0G2MSp!?CYTjBi~p3t(R4ve{)JiHw=3qQ zGJ%Fhx>p%`!<(AXNB)K^S}IM5j%6hgircjAQ#b7o~l?{EeI)R061}?;)EAnw48bP`e^H( zxANqkjj$rwE8+`(aOtu`IV>dW&`$~>HcB+(5H#!C+*)W|jmZ+Lym8v#OdUOKx^A|1 zXzH(sD1sSDWXV;t;v(dz*0d;FG8kVes1B}?bmj{ zi%g;4NExTH7#xj$9GJg}s-cmn<{AYXmJ`EH4~w6-sw0RdcJ#5u@4Rp?h#(>D8lsKq zm`0aCp3M1%%~yow04vS0x7vf5Ah{N+)yU-yG%q%pYp~f44QxCjV+jzK8iEBHa5`@0 z1$E5hD^!Z<{d&SD-;}eOvWbU{0!0d>g9{_OI#9^2sDDm!?Q)NcPhj1#APtjx zDyRfy8IFeG$O$l_h8a;l^fIecn@6g=rkKQ=W$M)Iy>ByW`ZZ6Snt=L@V%lW_E)dfu zjFd?II-m^X@)DIFayt7Ikhe(>>}AfVU?3&q*ED2QJ^JaO>o=NxLG zX=qluEUBalEk}-exN;AOG*i66wE9uQ4W*6OeEab#Y_Tz$IM2c-@GA$s;Js!49HlGR zO_wo8k#m5shm)Z-QPfMTlP;jw0PVX0vh5}`uTb%&rwg&eCOCr;dYn0hFq!X;{maM; zEd3e26=+Jk#uK94X|F@6D5-tfr#4nl1z&@Iz`r7xDt`ku3IFG0y_b|CC6lFLG%*^eG-##rb#q3(A$?PlY&aI(jF)>0Sj^tqa^6Et3Mcsy6PboICWa*ik){ z!u`f{Q)O19kC(Ni&2-;QRo8(J+$jSHNVl*Bn!OQPrduz3b&(i42DW zePo$yUm{jPTd#*}7Mm9%H|aw6rq$OU#na;#c(F8CsS>zbui;ZdW3;T!&&3*ZxQLpd z#!ZKd4g;S&Th(e*vDLm!RPBqbGJAiJ8k=}mxi_UfFq)E)x2(pO zv>EV<2g)qvV1kCJ9;UlSq+pvB61%IvzzjX6C(YJ4KA5*63HVOS*(_Go-i=)9C7+-d zsg8Q$v4(g2e)9yM$xx9|VhgH+aC}f%#J zu8u@vOpFq-loNRcqmZN>rnfRl&1C{x6TABF>1<&Qq{3I5YUL0v50&WQWTU(@hv?|} z8{aS7gOtf3xw<&E>B&dVYG0f7$_-GxTJZ5(6J=z8JhZf7VEn$Xa^wrt?@N8~rR+m- z@I+zlOHV!?2gFbY+W>&yeOW}MDp4P2OA&*E4hF#VzTIe&zxToMSs~HpXlbgD05xMB zI;_BAXjh)>{_a4mCVOc#X89N%i9|OFk}AJn8cOqOR97BM1`G^q}C*<`rlGf0= z{H5rHgXa&sY|Iqpxr5xt1j{8ELaTJyYe^OF8CKwrVZKDyM_m?rFGZ+Q_ZD~?-_3; zBG#>t01j(fVf&ph_qn49;hHs1FEXdLV%bR&QX;7wgoRykH=%uYEEahwPCS2CA7(fQ zOsm}d*iVqzPhg@42h~CCy*&3_UKR?Jd|dx@q`J#swq(D%UhVnd+kZ3xE;RCH*y>J$ zNw+AKfsi$`k7A#}621EINe2y-(R&IVAR{O$gnQn8nxlUT-aJTEtex<`&Yt08AMs>H z6TNlfx}Zqy;R_!GAe<41)n4{j`Va{O^%~XZ&vAlyY>yqeKFz=Yfi)G{@P|x)uMG-A z7b~|cNf|T#t@d-aV(AcN^Y2)7k2A$r5W&MTqFzy3i%kookHq+)L#acKlT`-hdMKl8 zd?L^M(-PgTKpA44T;G=&z(x2*(~-Izq+NB9%utPKj;4c#xv)iQ2&(WUf$bb`)egu$ z!A`P{m{?lu`1K4wees@xT@MSPOzy?B{~f85qCJG)kS|wz401KC`v7cxc8z0lv869d zMEl^MY^?tP9t+jAL3>s^s&hK&4$gyduP#A)JaD2teovT zow@B0jD=7erbtT*X!3H9`vv6q$PJActZp%jz{lr-Mnr(kWuPEBTJ!%?6ncg8wh^ zUv}i*eg7|FFi$NO^Fq=@Hd;pTUvz(G^DlJ&1;c+A)!jE)iHY0|n}h!LtR={I+3+@p z{%aP0-xa)(3Pz(G=#EJKU-%Jf`nPHO3+W%?zm)jz+W)cm4~_rr8|A?$C>cZPQb($W zEd@{uKn5UTGUT8Fm@&6k=gz4yy#zg+F$+cgNlF@WLE7Rdn3#nCNDdK=sCYF1GdHKL zb0HiqxJp9Yv#aanS9k{dc+L6oF<^L2yFMEES#dDNt0n2zENBo^13L7A;zI30DGt1k zl!A^qOHYj13ZlOoLPJAEPwIbMjO2sG8CcGwM%B&27^X-et)n%LY7vFS+S(%6tGxZ1 zj@_EW5yN85Fa~=qGj9M4I8;Q?|y_h z*E6<>|5}`EA30vbf@inG2OC>tv_A}j%Gh|Me8aMqU868j_?mM_yFfv4`Ze=GJMh)y z&v)65d1dgSi8k+WY5I%Nl{@CLmc)3e8800{cIb3lul%dz*f&%+Ulp^&Tlplt8ZKi) zZ#{O1w6r;NIp*5PS=O?$C*b(WDEitA83a8{K4mEmN+0tYmIRBBTHc_6X>Rw}-N%5X zF>8Eo3q~(JpgYfMgX*{O6FUrEB%QF^D|qtKRhEhDV4-HeEFHyR&Sq7@eB*igqa*zd zgS$7ej*eayg)82l^5K&T)&v>ty^{2DDQB0cVMo>So+Qtwat!WS7zrHSXSMYmw>?fn zi5|+N{ekHXk#<)BQ_lu_a1p87TxbHe6d|+F#xMQ3FCb;*YiENu_doCPbv^+eE;cUg zKBPKpKi|VBOMWNN=C`eUGv{5>_jv2v2QF`QBv*D_TmTGAnHml8Jam?0^8lK*^7_1v zmIKj`0VsrF)xSvRCPB(?&O<_4r}49R2bW>x!_VL@&5hpg@fvh-aMn)!ZdBt>un4o~ zgLWAutt|f#c^vzbXurTrDytY+cMF!heO+9_-cvSqrIrN|ggLF(R*xGp7-%oUMvVbdEM^;FnTH;?0n2c`R zx$sRJLOmbJ;MViuBMdxGp9!1(#JWpc1tOnxU6z-HFYS}7fIA4>&M`=Bij#X1CREQV z?0$fh%PZ1laD#vQS-_F{$6&Kxn3$1AGq1_buhY^uV0m^;6$gFEPs9QfqvA)|$+ZJ{ zbmN^S%Td4KEtnp`D<_&tW@}gPDV8k6+#-;=+DNX=M`4b=SzSsjZ7=L!bYyv^+Gx=Y zDZIhq4y=FA-_F_=VpCS;x#PDD)m9gJ$a`-szRnl@bg`nTJ;NY6t9zMHUKXtU?jJG{Ukd$GJ$(X#LmLNuQEVA;Cul$M*kCt2B(mj zf{-H)(-|s6oaP9@rk{->3yWV&v!Q(>B^IWD;-#Gmjl`4etfqmGuZ^39*PqH=pGR-6 zQS^S|^X@)o$NR{_QW(;g6vFdBatgGwOiN31KrO37#2cZ@;vrmvMXNPb1F;JzkK#w8 zg}*$#?n+@0xK#QjDmpkm@Z}-4=Yw$P85yoocjXrR+Mz$UeU7YA$Ud_EB$4jMYrNyj zVUq?nb;l1p3k>!heQ8T>t71cNb{$2F^)qiO=E53BO{}b^Ey4RF|D9^Gxw7n=p>GeV z;>D=NQAcV@0te)3)1SyYeuYp{i+W;ax4Q+{;KT=hCa)nXC#Jp*QQ1RBK_djgO!)?UJ1C@mj9??Y!y%BnNjQM0l z%iDm^=hW#p`q9x=%4wsbgecef@|E84N!MF_RtMHv{Ld^Z@`w91+Dqhi#Kk zz|co`nM{|8MNc!N*h^2d;fRm3p*HFRMbocwtZODl`1&wr=n(NiX@`oewkcMaf_6*Ik(q_?GbidJ zmOuuNM`l_{&0gr5ql8lHo+%YkOV=c`=g_mPWzAb|)jYk*o+J%*gHYK|5gb`2g96(u zJLNSjx|N>~uECdlS8(d~SPKiHIz)`gLOg`{hTRoRwFZhji^+yJQ;{Y9**vE( zgo!4Rn&H>6B;qSb(z{T^fAz^n_h+Xr5{-4$VcWgr7bQu;`&AKHPc-W}Lhhm`Po7gh zivzok4<|~PE;-qGB3&!(ad7h;u>q}!=5N^c9d%MHLsbm2RsmQLm-DXYJeM67Wg`29 z8K|rJf|w^1?s+98T?JS#b{|1*a4|;dQ8q4r?xX}DZ`2JS<9gGW-Rs))b;naG)dp2| z9WYv)?biiP)WV5|(q{6zI}5=s^A3V@yD3Kei3b}RimLaRtIY+Nm|T#6@G28T^r(_& zjS9#^O=7Z)RA|MJdM~3Zh)x=nbZmxK$3n`bY7)nQ7>pTM%V!VcL7IPUi<=Zt^X# z-w^(mMds((B5TYGi4}{lZ$}$6H;~vo9A2Se2Kv8h`AmWU3Z}|}QSRc!Rul#e?6TvH zz%yIltNmK>Qpeu1aPSVscL6X1-3j4gbAL1i6O?pTx2^w0b^pN?{|j~eFXBJg?0;vH z|NcY&O``wN_#gl8-`fAN_}{oLcGPs@c*K$kT3@gnkf-ddCx4li75sR%JSJ)j1?3;0 z%0LgxVF#N?bYWoiA9uY?xDKd@XydGTyaWrH3eH+b$LVz@zMISz;YT4vPQlq z8?M0;YIBU|@5xDUkdcOW-;Q>pU}_wptVi&6n@qQ` z2OX(7&-mMoo(mB{(z0Pa(N54CG1Iolj0v%}UbUkFdsi=W+RlVN5S_!aR#cRRSp<$2 zOQ>u%_&lulYK(~ok>U`B^*!9lurDzMB4_OR=q=>1y+KZW{#+ zA4;k*T+=WEOR4M+G6ly6uj0`EOzwA-){)T?fi3t$HeU}xDh<&BmcuL86^E5#?DBVWv_#K*uX8vT>5K^0 z{QZcY?oODm97LYSAN?$};id9_Xv@*5(qK(>$0>?2j_i35%=(6&kCHlW&7IZrpo56a zNxyaD)5u68zofm?`DmY#v|jjb+2yQB%zSy@E0;3#&d@f!Dpx;B9Zb=n_8sRmFbT7ujPC?J-Z jdWlOE8oun0CyWpzQTw7TTYp3r89-51Ri;YHH0VD7#XFGS