From 34d5e3bb59096ce4b4accb896e16234440752cac Mon Sep 17 00:00:00 2001 From: Rodrigo Lazo Paz Date: Tue, 5 May 2026 13:31:24 -0400 Subject: [PATCH 1/5] Add UI for it --- .../geminihybrid/GeminiHybridScreen.kt | 96 ++++++++++++++++++- .../geminihybrid/GeminiHybridViewModel.kt | 7 +- .../src/main/res/values/strings.xml | 3 + 3 files changed, 100 insertions(+), 6 deletions(-) diff --git a/samples/gemini-hybrid/src/main/java/com/android/ai/samples/geminihybrid/GeminiHybridScreen.kt b/samples/gemini-hybrid/src/main/java/com/android/ai/samples/geminihybrid/GeminiHybridScreen.kt index a15fb129..8ed8562a 100644 --- a/samples/gemini-hybrid/src/main/java/com/android/ai/samples/geminihybrid/GeminiHybridScreen.kt +++ b/samples/gemini-hybrid/src/main/java/com/android/ai/samples/geminihybrid/GeminiHybridScreen.kt @@ -19,9 +19,11 @@ package com.android.ai.samples.geminihybrid import androidx.activity.compose.LocalOnBackPressedDispatcherOwner import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.ExperimentalLayoutApi +import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.FlowRow import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxHeight @@ -77,6 +79,7 @@ import com.android.ai.uicomponent.GenerateButton import com.android.ai.uicomponent.SampleDetailTopAppBar import com.android.ai.uicomponent.UndoButton import com.google.firebase.ai.InferenceMode +import com.google.firebase.ai.OnDeviceModelOption import com.google.firebase.ai.type.PublicPreviewAPI @@ -135,6 +138,8 @@ fun GeminiHybridScreen(viewModel: GeminiHybridViewModel = hiltViewModel()) { onTagToggle = viewModel::toggleTag, selectedMode = uiState.selectedMode, onModeSelected = viewModel::setInferenceMode, + selectedModelOption = uiState.selectedModelOption, + onModelOptionSelected = viewModel::setModelOption, onGenerate = { val tagStrings = uiState.selectedTags.map { ContextCompat.getString(context, it) } @@ -185,6 +190,8 @@ fun InitialReviewUi( onTagToggle: (Int) -> Unit, selectedMode: InferenceMode, onModeSelected: (InferenceMode) -> Unit, + selectedModelOption: OnDeviceModelOption, + onModelOptionSelected: (OnDeviceModelOption) -> Unit, onGenerate: () -> Unit, ) { Text( @@ -215,10 +222,27 @@ fun InitialReviewUi( } } Spacer(Modifier.height(50.dp)) - InferenceModeDropdown( - selectedMode = selectedMode, - onModeSelected = onModeSelected, - ) + Row( + modifier = Modifier + .fillMaxWidth() + .padding(start = 8.dp, top = 12.dp, end = 8.dp), + horizontalArrangement = Arrangement.spacedBy(8.dp) + ) { + Box(modifier = Modifier.weight(1f)) { + InferenceModeDropdown( + selectedMode = selectedMode, + onModeSelected = onModeSelected, + modifier = Modifier.fillMaxWidth() + ) + } + Box(modifier = Modifier.weight(1f)) { + ModelOptionDropdown( + selectedOption = selectedModelOption, + onOptionSelected = onModelOptionSelected, + modifier = Modifier.fillMaxWidth() + ) + } + } GenerateButton( text = stringResource(R.string.gemini_hybrid_generate_btn), @@ -402,6 +426,7 @@ fun LanguageDropdown( fun InferenceModeDropdown( selectedMode: InferenceMode, onModeSelected: (InferenceMode) -> Unit, + modifier: Modifier = Modifier, ) { var expanded by remember { mutableStateOf(false) } val modes = listOf( @@ -412,7 +437,7 @@ fun InferenceModeDropdown( ) val selectedText = modes.find { it.first == selectedMode }?.second ?: "" - Box(modifier = Modifier.padding(start = 8.dp, top = 12.dp)) { + Box(modifier = modifier) { SplitButtonLayout( leadingButton = { SplitButtonDefaults.LeadingButton( @@ -457,6 +482,67 @@ fun InferenceModeDropdown( } } +@PublicPreviewAPI +@Composable +fun ModelOptionDropdown( + selectedOption: OnDeviceModelOption, + onOptionSelected: (OnDeviceModelOption) -> Unit, + modifier: Modifier = Modifier, +) { + var expanded by remember { mutableStateOf(false) } + val options = listOf( + OnDeviceModelOption.STABLE to stringResource(R.string.gemini_hybrid_option_stable), + OnDeviceModelOption.PREVIEW to stringResource(R.string.gemini_hybrid_option_preview), + OnDeviceModelOption.PREVIEW_FAST to stringResource(R.string.gemini_hybrid_option_preview_fast), + ) + val selectedText = options.find { it.first == selectedOption }?.second ?: "" + + Box(modifier = modifier) { + SplitButtonLayout( + leadingButton = { + SplitButtonDefaults.LeadingButton( + onClick = { expanded = true }, + colors = ButtonDefaults.buttonColors( + containerColor = MaterialTheme.colorScheme.tertiaryContainer, + contentColor = MaterialTheme.colorScheme.onTertiaryContainer, + ), + ) { + Text(selectedText) + } + }, + trailingButton = { + SplitButtonDefaults.TrailingButton( + onClick = { expanded = true }, + colors = ButtonDefaults.buttonColors( + containerColor = MaterialTheme.colorScheme.tertiaryContainer, + contentColor = MaterialTheme.colorScheme.onTertiaryContainer, + ), + ) { + Icon( + imageVector = Icons.Default.ArrowDropDown, + contentDescription = null, + ) + } + }, + ) + DropdownMenu( + expanded = expanded, + onDismissRequest = { expanded = false }, + ) { + options.forEach { (option, label) -> + DropdownMenuItem( + text = { Text(label) }, + onClick = { + onOptionSelected(option) + expanded = false + }, + ) + } + } + } +} + + @Composable fun StatusText(text: String) { Text( diff --git a/samples/gemini-hybrid/src/main/java/com/android/ai/samples/geminihybrid/GeminiHybridViewModel.kt b/samples/gemini-hybrid/src/main/java/com/android/ai/samples/geminihybrid/GeminiHybridViewModel.kt index 240e99a5..37e6bc6d 100644 --- a/samples/gemini-hybrid/src/main/java/com/android/ai/samples/geminihybrid/GeminiHybridViewModel.kt +++ b/samples/gemini-hybrid/src/main/java/com/android/ai/samples/geminihybrid/GeminiHybridViewModel.kt @@ -58,6 +58,7 @@ data class GeminiHybridUiState( val reviewText: String = "", val reviewInferenceStatus: Int? = null, val selectedLanguage: String = "Korean", + val selectedModelOption: OnDeviceModelOption = OnDeviceModelOption.STABLE, val status: GeminiStatus = GeminiStatus.Initial ) @@ -88,6 +89,10 @@ class GeminiHybridViewModel @Inject constructor() : ViewModel() { _uiState.update { it.copy(selectedMode = mode) } } + fun setModelOption(option: OnDeviceModelOption) { + _uiState.update { it.copy(selectedModelOption = option) } + } + fun toggleTag(tagResId: Int) { _uiState.update { state -> val newTags = if (state.selectedTags.contains(tagResId)) { @@ -133,7 +138,7 @@ class GeminiHybridViewModel @Inject constructor() : ViewModel() { "gemini-2.5-flash-lite", onDeviceConfig = OnDeviceConfig( mode = _uiState.value.selectedMode, - modelOption = OnDeviceModelOption.STABLE) + modelOption = _uiState.value.selectedModelOption) ) model.onDeviceExtension?.checkStatus() model.generateContentStream(prompt).collect { chunk -> diff --git a/samples/gemini-hybrid/src/main/res/values/strings.xml b/samples/gemini-hybrid/src/main/res/values/strings.xml index 82a3fc9e..d8313289 100644 --- a/samples/gemini-hybrid/src/main/res/values/strings.xml +++ b/samples/gemini-hybrid/src/main/res/values/strings.xml @@ -25,4 +25,7 @@ SPACIOUS NATURAL LIGHT Select topics for your review: + STABLE + PREVIEW + PREVIEW FAST From bec3faeda4b97eef94c50d5c8ca675b32cb328ab Mon Sep 17 00:00:00 2001 From: Rodrigo Lazo Paz Date: Wed, 6 May 2026 13:25:13 -0400 Subject: [PATCH 2/5] Initial v4 sample --- app/build.gradle.kts | 1 + .../ai/catalog/domain/SampleCatalog.kt | 11 + app/src/main/res/values/strings.xml | 2 + samples/gemini-hybrid-v4/.gitignore | 1 + samples/gemini-hybrid-v4/README.md | 28 + samples/gemini-hybrid-v4/build.gradle.kts | 75 +++ samples/gemini-hybrid-v4/consumer-rules.pro | 1 + samples/gemini-hybrid-v4/gemini_hybrid.png | Bin 0 -> 157515 bytes samples/gemini-hybrid-v4/proguard-rules.pro | 21 + .../geminihybridv4/GeminiHybridV4Screen.kt | 575 ++++++++++++++++++ .../geminihybridv4/GeminiHybridV4ViewModel.kt | 260 ++++++++ .../src/main/res/values/strings.xml | 31 + settings.gradle.kts | 1 + 13 files changed, 1007 insertions(+) create mode 100644 samples/gemini-hybrid-v4/.gitignore create mode 100644 samples/gemini-hybrid-v4/README.md create mode 100644 samples/gemini-hybrid-v4/build.gradle.kts create mode 100644 samples/gemini-hybrid-v4/consumer-rules.pro create mode 100644 samples/gemini-hybrid-v4/gemini_hybrid.png create mode 100644 samples/gemini-hybrid-v4/proguard-rules.pro create mode 100644 samples/gemini-hybrid-v4/src/main/java/com/android/ai/samples/geminihybridv4/GeminiHybridV4Screen.kt create mode 100644 samples/gemini-hybrid-v4/src/main/java/com/android/ai/samples/geminihybridv4/GeminiHybridV4ViewModel.kt create mode 100644 samples/gemini-hybrid-v4/src/main/res/values/strings.xml diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 8cdfcf69..9b6f29d8 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -94,6 +94,7 @@ dependencies { implementation(project(":samples:gemini-video-metadata-creation")) implementation(project(":samples:gemini-image-chat")) implementation(project(":samples:gemini-hybrid")) + implementation(project(":samples:gemini-hybrid-v4")) testImplementation(libs.junit) androidTestImplementation(libs.androidx.junit) diff --git a/app/src/main/java/com/android/ai/catalog/domain/SampleCatalog.kt b/app/src/main/java/com/android/ai/catalog/domain/SampleCatalog.kt index 1fadf927..56e14ec1 100644 --- a/app/src/main/java/com/android/ai/catalog/domain/SampleCatalog.kt +++ b/app/src/main/java/com/android/ai/catalog/domain/SampleCatalog.kt @@ -32,6 +32,7 @@ import com.android.ai.samples.genai_image_description.GenAIImageDescriptionScree import com.android.ai.samples.genai_summarization.GenAISummarizationScreen import com.android.ai.samples.genai_writing_assistance.GenAIWritingAssistanceScreen import com.android.ai.samples.geminihybrid.GeminiHybridScreen +import com.android.ai.samples.geminihybridv4.GeminiHybridV4Screen import com.android.ai.samples.imagen.ui.ImagenScreen import com.android.ai.samples.imagenediting.ui.ImagenEditingScreen import com.android.ai.samples.magicselfie.ui.MagicSelfieScreen @@ -51,6 +52,16 @@ val sampleCatalog = listOf( keyArt = R.drawable.img_keyart_text, isFeatured = true, ), + SampleCatalogItem( + title = R.string.gemini_hybrid_v4_sample_list_title, + description = R.string.gemini_hybrid_v4_sample_list_description, + route = "GeminiHybridV4Screen", + sampleEntryScreen = { GeminiHybridV4Screen() }, + tags = listOf(SampleTags.GEMINI_NANO, SampleTags.GEMINI_FLASH, SampleTags.ML_KIT, SampleTags.FIREBASE), + needsFirebase = true, + keyArt = R.drawable.img_keyart_text, + isFeatured = false, + ), SampleCatalogItem( title = R.string.gemini_image_chat_list_title, description = R.string.gemini_image_chat_list_description, diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 19e449e6..777e856d 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -29,6 +29,8 @@ Conversational Image generation with Gemini 3 Pro Image Gemini Hybrid Inference with Firebase Hybrid SDK using either Gemini Nano on-device or Gemini Flash in the Cloud. + Gemini Hybrid v4 + Inference with Firebase Hybrid SDK using either Gemini Nano on-device or Gemini Flash in the Cloud (v4 version). Firebase Required This feature requires Firebase to be initialized. Close diff --git a/samples/gemini-hybrid-v4/.gitignore b/samples/gemini-hybrid-v4/.gitignore new file mode 100644 index 00000000..796b96d1 --- /dev/null +++ b/samples/gemini-hybrid-v4/.gitignore @@ -0,0 +1 @@ +/build diff --git a/samples/gemini-hybrid-v4/README.md b/samples/gemini-hybrid-v4/README.md new file mode 100644 index 00000000..650cc640 --- /dev/null +++ b/samples/gemini-hybrid-v4/README.md @@ -0,0 +1,28 @@ +# Gemini Hybrid Sample + +This sample is part of the [AI Sample Catalog](../../). To build and run this sample, you should clone the entire repository. + +## Description + +This sample demonstrates how to use the Firebase Hybrid SDK, utilizing both on-device (Gemini Nano via [ML Kit Prompt API](https://developers.google.com/ml-kit/genai/prompt/android)) and cloud-based models via the [Firebase AI Logic SDK](https://firebase.google.com/docs/ai-logic). + +The sample lets users generate generic user reviews for a hotel based on a few selected topics. + +
+Gemini Hybrid SDK in action +
+ +## How it works + +Here is how the model is instantiated to leverage hybrid inference: +```kotlin +val model = Firebase.ai(backend = GenerativeBackend.googleAI()) + .generativeModel( + "gemini-2.5-flash-lite", + onDeviceConfig = OnDeviceConfig(mode = InferenceMode.PREFER_ON_DEVICE) + ) + +val response = model.generateContent(prompt) +``` + +Read more about the [Firebase Hybrid SDK](https://firebase.google.com/docs/ai-logic/hybrid/android/get-started?api=dev) in the Firebase documentation. diff --git a/samples/gemini-hybrid-v4/build.gradle.kts b/samples/gemini-hybrid-v4/build.gradle.kts new file mode 100644 index 00000000..690d44c1 --- /dev/null +++ b/samples/gemini-hybrid-v4/build.gradle.kts @@ -0,0 +1,75 @@ +/* + * Copyright 2025 The Android Open Source Project + * + * 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 + * + * https://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. + */ + +plugins { + alias(libs.plugins.android.library) + alias(libs.plugins.jetbrains.kotlin.android) + alias(libs.plugins.ksp) + alias(libs.plugins.compose.compiler) +} + +android { + namespace = "com.android.ai.samples.geminihybridv4" + compileSdk = 36 + + buildFeatures { + compose = true + } + + defaultConfig { + minSdk = 26 + + testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" + consumerProguardFiles("consumer-rules.pro") + } + + buildTypes { + release { + isMinifyEnabled = false + proguardFiles( + getDefaultProguardFile("proguard-android-optimize.txt"), + "proguard-rules.pro", + ) + } + } + compileOptions { + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 + } + kotlinOptions { + jvmTarget = "17" + } +} + +dependencies { + implementation(libs.androidx.core.ktx) + implementation(libs.androidx.appcompat) + implementation(libs.androidx.material3) + implementation(libs.androidx.activity.compose) + implementation(platform(libs.androidx.compose.bom)) + implementation(libs.androidx.material.icons.extended) + implementation(libs.hilt.android) + implementation(libs.hilt.navigation.compose) + implementation(libs.androidx.runtime.livedata) + implementation(libs.androidx.lifecycle.runtime.compose) + implementation(platform(libs.firebase.bom)) + implementation(libs.firebase.ai) + implementation(libs.firebase.ai.ondevice) + + implementation(project(":ui-component")) + debugImplementation(libs.ui.tooling) + ksp(libs.hilt.compiler) +} diff --git a/samples/gemini-hybrid-v4/consumer-rules.pro b/samples/gemini-hybrid-v4/consumer-rules.pro new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/samples/gemini-hybrid-v4/consumer-rules.pro @@ -0,0 +1 @@ + diff --git a/samples/gemini-hybrid-v4/gemini_hybrid.png b/samples/gemini-hybrid-v4/gemini_hybrid.png new file mode 100644 index 0000000000000000000000000000000000000000..1324a668e703775e38c1b23b99c29a35df871735 GIT binary patch literal 157515 zcmeFY^L4 z>K=Zt`+2VWemy@tf55e$KHKZu=RE2>;(Z)vxR!<@88Ltu2M32tSxHU@2j{LZ4$eI* zA_D9$ClNhe*e@ULWo5NIbQEPCTX@+!d2-o9aB$wG?Ihc3QO^QGHGMS6Ne4+~Ls`hB zVV`iHZ9Iz$E^H^IQ+O12htK@Y;wU!@v{pn&@C!ft9?ppW z>@h_l4#}hYX_HnwQnmq=I-QNnnOjJZ-$Pr)NY0t?p zp#Gcq(2d15|9OQ~MlKm8SI z63?wPUP|4=nJte$cUZ#sbGfOnJV|nEu3?kpd7_POGaU{n4?sOn;P|leb<-`}9`4qB zGbY)2^*3tK2#O@6%=x|NbodMSrtJ<6`?;8|7%7f55QkAB!1ik&F6t%jYYh8~^vnY; z*?WWnk8y~z@aVFL^nzGN@fF%hjqaE%-(3g-?h~@SdAb~E6nppVG1Z?y@MC7`j}Kfa z^@2d7I4Q3=h4Dl?-n-J7;?=iaVv`z^#aA>y_erb zTuD$$QTFsd@Udrrd5mT2 z0{SU@3d&`=K1o^Aaxf z(^Pgpxs&4UGzRdnPV`g5$36%=tltfT8C}QrZSF@;zguzv;ZyN?ip!!tFGDe}GmsB-nt@oEj zZDp_E&lI5)_LSq4(^_7TQ`z0JE|5>{TW6{rqaBAGG11ECB8{}+x9QpXDE;-4cO_ef zQJZm|JD&WWCY~>j9{FbYHu|RfRvb+o$sV7gPtgH~f6LVe@u`&jWy@nZ3e z_YQu4C7N2^@c2~VnXNibA^uK#`{KC(_wW`qr>my(N-uf{SF48$<~ABDvR$HG+L(u2 zzL{4qi3RxGSlsmCrr>Vkwgi1|uWZk6?|IypHU3z-(tp2h*@Wc>iyI3#duFtKw0AV* zSOZN_o=MDqZ-9ax**;fk-4zw}gPsa#HX}DnUA5^XEGo{{EOM#)$wv;_Et6|vrT#~} zFUaHbVVvUt*Xpo>v3$IOx}u4EqQXeGQ4hoCqb zGi7rrPt(fCR?oJ1gmh56N#26QWuV<)r_8Tj!&bzWaYVt|_p z+}9w|z+`MYzH)SMNO0kX1euJR*l32N!!L4HW58wbGB4-Iy3UDZPs}E*RLG5hZ||zw zp7F`-bmmk*xqU$`t>6l9BdwOU zo2`9IEZeQJuIhg7oJdb}M2p$@z;{!Yc9=l?ll)HnAI>vYTO3c3yGsCLu~z~BUd)@9 zPHOvs7jM?z{_aA=ykg>#SQLq7;(7PvI`^{Zasp#s>+ejJM9i%wtU)S%=y&oij{p2` z{p6CQ`kKTGDV|J&6i5nRiii|i-0!ppL#xa4&76B^?S}inZ-{fqQ0nIGMPK`e{mV(m zYxJ5_#A1K5$ieiP^Ij)Bq+l;@k&BC_Iq`kKU%U(1o4%}=3DxAx%1jPPN9gYL?*r=( zBKGf%|J->(OW7FMk1xml*d6yP$!F8o|MyRq%uVfx>eP6dPhC}fIG5G9`^{vptsmk5 z#&EPcukTFM;Yokhn`Otr3B*yBlh%7@wv8lgrk={~J4j8m?V`!E>`G>$Y$&oPg(61xqO%L}Mi-_lMX#IG`>@4#k6!P)Tn%v(-u_qV#T{~i<|yA%TmSQth(VaVd2+zzA#)H<>)C>- z=h(T;s`|9YFRbb1Y8P-jt5+=2ry@^hW*LN>i$$(KSq|D4WIh;eI|i1aXB~60N?Rnf zXiCcM=G0L(qNc6Mn@9Tw`4g%psTw>5NzpC`Zd>oX7DW9wL^|m2GT7M>18vOysvZ_d zUH!a7o8P>0X}cmkyuR2=zPV_-;Sac6We)bDhb$mx$#DV|^7eWdy%&a$m`4$w+s!|! znWpMN2o0M*LS2JDCp39IHWn?+pvMoZT~}z=!uX@IzPvI9yMPUqf z(UK=h9)nei_&3u_-t*9ctjJ#u^nOxPt@{f zJ*K7%Z9d$CULC`hSue`Xm)@Q*Ne$2DGhj~8_S{XEam#K}H<#7RZf%$?)x&v&IX}Y8 zrD_-jGj*4wr0@8pDLiY&QAe`(`M zv4)6V*d11|X2!d5xAiLg7y{tRIX6d%Gqoq_%n#9)kf)L-X&ape4%v-#yu z^%07Kw}MyT% zy@t4lR|S5k3n3prT_B>pinUGTl5ZmCBtXKZUTw7ppYkg*yf+$^k;M(|EU~aN*^#{% zm#k+N6Fp0-b8h>1c($hHvZC-gwoB?RgMPjZgoH-iCi6u#w*z^>_4kujVy~O4!)>rF zSZ{Grak8rK>PbZ`E4(m1tR>Syl^A$5hw834{=|TTW96`PV7vZ>lV_k|<>IvMhB4A4 zk6Gc1JuZk`Hk6E1CP9`$Y^Jon13rwAu`?wGlPIP16`q~@IueeYhR|kG_$P6Ajnzh< z?q{eKB?qA!{`C+tEMaJ_OmM`P5bOXq&fLv}skO zc{NtIIVckY4UEeyT{{gwXLECKsP^OHyynDbs4j=Y)-EmsZcYR6K$AL$i++1TV!Pt3 z6*O6Eu)RGI4+t@QZi_JckVm*4HT?{UFz-{7T9Z2*tkxt%KF|<@!e?tXLNyvex`~gy zWVJ}<8dCQxXm`*Q0cTL&Zwmm&vfttm3Az?!|FT5RMrrIWEP{s!q&>67LFZan8wqk^ z^V9-TiNzLDOC&IP43g*kny|2)dU(j>k<;~&#b)-NWAel3ZHVZC)Qs`c?~wa8YpQ;! zYbPf2FZqijjEl`ps*gsaZD!rTLthGtmhx?K;}D3U5|Y8hoVc8^Xi;j66Rnn z_Wa)Z)tC2?>P;e7>3^epMtJ7ia{vl;i^rw=SCc_vEby z5w70N3f@h7sIJ7q^zt z<`Sxjx>Fupz(hIP7BM$6rg*n6%d#q_f-=Rc``%rPnig;V>Z4_Ho1bQ!gwzHz*118gUhM^t5a9e6K!sim)%zoKxJ^Bu{1|9G8jtYi^VqcJ=l_Lz zX2S!2Jhmz~uxXgsK-H?@KjvPL;r#>r>`|%S+aTx%;yXw3_B@#QK5i3bx|Lq3oBKzG zQwLj}HsgPwLyi?KfTQRBnB;<2cgVH-`QDY_cF0H9>9?t-58k?^sBUs$fj8&gmUZ_) z;SO35E)4F9k4x+QOBK_oa&pmp=m~P7`Jf45B10agtGU zKFMzLLAM%=y6Yo|S74(GT%w*W(~YWPw&qhpPTk9oKi$i8d`@#knU~Pz`|{!d9YoL@ zL&C)b-M(-8;fu;$hAYe|IY?_JNHz|SJmtZHFdC${x5<=R&RnAd3h091USUI|XuP(u zv*q54aDr!Cd49&c51rH=k3M)MF1GMeYG*^EFe`!D+qkyIs5n5Z!0AD4yHd)>y48s; zwN$WyN1gJIL6rVS8rBnk8Q+W(55pvSs3x0h?LZdh)}!dEZVvpt*lVM}h|Hm7Q%V-h zh9zHkbg$u z(+i5|KfWJIpOS5}AB!v9;MN}WEjZ#y2tDZc9rAlJ(JWYh8rW-1$lx$tkwEF7UR=)N zKvygeTmpR1;}R_y`zyV)0Vf}ec?cIlz+z>&^mbTRw|ukF0BB5!V<(+$#u|!l#X)@>M-{;5q8Rb)$`KZHlDJxWqvp!bM2JZHpY1@i7DOI}KJ-Ya4Zrsg|$jtYx`AZH~RyMJ6b%pD7D{&$b<&krsD{oaGM=j=-Ie2T$wiXzo(aM+3$l z;IJ)^3WHW)fwD^laP~=3N{5H{>;7oHPT~EyKIm8#^{9a2mceuxTBvJO7 z)fbsV{~CJJc6|+@vgY_yT`OTMcak)%&>LV!gF2}(f>SHAaFQ!08M!sqn z!sB-75b)66&AvgeSC!c+M9O@Ey=0fc4KXrBEG*C)&dB?TRRh3W!#lm9yf@I`CjB?h z`60^Q++p@Bm(keKkCT)0FV50cXD*x?OQSDm=gFSI-pYE?*Y@@sEw!Z^Z^oI?UARaE z#M)P*x02WDoc26e~&hQ7RjEPppS|0}tBsD18uQ7dj++o_GX zM8RSa<@A#TxQZ&9BtSSyMmp!y{Msa9%1q@v&>mpyr;WPLMRih3Mth7`dull`!*Bw_ z&DeHdqo#$hWsUwcyCU;q$<;IAvX?U#PSH?3E~rxTd`mOVbH|~W4gr>5yNC5B$1+Rt z0OswS7)b$5$@6!VL@0t+?8~D|ijfQtJ^(|xgpSR@x_Z?=y%Y7tlHB}u0X20*eGhff z>O=c5`ndgd{p^Tdu2C{q({>h2D}v$)vB+eeQ3NQ&E-mzfzcLRHS-RCLJ_pSok0^KZ z1nd1$_pa^s%A9@xELSMQBlltnCwb%vW*Gis>!QoCqkCacf+vx1b>^_+?DCCAj3_oU zy{(9fVFs)T!&MAZ3`=@`Q8#4NHZCubenlWHtEL+BP;*H*XYhw#+1Dyb`(_H`>*c`& zrh!P~9`=cY@}AQ-B|snRiE~+g#o36h>HkQRcoXs9f@J%kk5+h1uBf!PTI+pnu2!aQ zlN!q;xXWythHT4>3M}xYzJ+-{m_j19Dk?#RTmTSC4}TP1x1e<>Pu8ECKx;z2Abu90 zjmq}-bQt!%P3idy>|PszvwosoE~mG}-tN6wa^n&iPIoiV#{lqfwCo*P4z@i;?k-Jj zL12$xgPo?JUAr(CAI{{K-m1(e@Qb6XqCGHlWF8n9pC4<9Kzft<;}948Ff55fm$$K= zxjhBA7h0FL&ad7fA3JR+51Pd2NBBF9A4itsZvTkScO)I^ruC@?7swPzbVC;sgz0nnI-pVL790+I~NA|{XJf?yvXjA08dFWTyX`AKKRn7lqx zW^*b=6nvIDl}C&;_Mv~3nS$M@P2!Y#Q=^I1B^SWe&gOe$?Az2~Y>(r5)pi3Cr|rMF zk`9{odiTb)SJd8T1ILz`YmIsF*F8f48l{HTRZCg5d9oB)^>p*ehr?!`<(s4S&kG=0 z*rJU+OYXR4LX*!B47DC*;GMIUiSU;m;Iq5MQZK`t~9x%(h8xvKL3jjfEMT|b7|wO4IRI@*-#p3?YbYBSQ3ddWnR#|XsW z{}>-MqYFShVmdhys@?w#IjpYs6{zsDNwRNx@3EhI8rtgxD~e3+zd|O9yUqUEA8V6}Ei`N_niGQkwsGYj zs$reCvRP0lfd`xzHu(2)IMiOIs0}>K77XB&WXbrQGAX zT#+x6&%rb0IEr;uh@p^tCw0a&8N;Lubc%<$2~Fl5bx}wWbrRtN-U8!Y&v%>lK5|zB zRv+)4QTq=jd+1WmYzMUGCM?Uojq*Plg+Yty9mj_(^H7G~wX9_)uvGtAp~{#7&}6!i zrK4SQ@WpP^i>kaJ7hFYd;-$$b5Le8kx*FbD?~tb*569tHA2UAgQY`|^IGC@q>E^m! z`B*vZqAc*~tA0CAjFG*VpNFmA{PexaDtd?`s45R zz3e<5x$G&)@(naFsP@ zSmDtelU5MCY04;(iS}81KYveHu1#Z;k2=?G$=8WdR&t0G7e|Q%Ip~O^PUc>?*NWv! zq9NSSkpN0Dx9TEKi_btTbHrIq@S5VZXipn#SxEnxU`KC&^1^LE9;xH`G|f@_bq=L$ znZ$Oi?1%~2-(H8l{73vDFMKv5Z3N=<(o&F_|OsiC-M8f{G%_&8LQx5E~~RoU~#umEs<` zHVZ#l-GfbIt+dYibdbT;GLIz1R@WHP=Jo0t5;Jn&HZQ`@#%aQ)eoFm<9SO?F8FtR& zPHE{Y7t_NAOJeb({ooVZ$Clav$B`@@Zike79v;83B^WJxo!D;k&|*OWJ+kUs6_(c31&G{JixBc1cL72`7KDl5f9Gr0t zt_NP<3h59>U}68ImXEu1ZzFo=_Zqm8L?_LT7>jq)iTSq63owvLvYOW5DV@KuvJ}Ne zQeH?>v<-KrJWJm~WBuK}0$iOgD9T1?n*g?3da>PPyYGX~0`ItItFA>7;}9@{&NnU7 z`JK>qeO%G8Tmi4+_`_;oT^m*I76&3MoYE+R493|+hL>FU-V>gL8-epznW|^xFLVqO zoRB|N--h8UW0NW}fCC`|W2RmD&a>9TI_2IeS;gML?elT5vqT2ZY;Z7z7U_CUBwt?SEY%cHO$t*JGGGWD)l+0`c~z%tTj=^gcaQ@;|H#VCPoXZ@s?v*u#?sJU`Co}>}<kCdV zGA+CIY77KJL13R4U8N|-ggG(D5w$IIyrh!`(b1SL9~;D7xu zi7FEdf7yju5YlZ35z=YU#!sY}cw131CV9?g*4J%eP&pJk4lnJ-Y=!I>9_f+eDt_sS zN+?_Z?%lXqkoG7t>jM@P`)HrJeJoTf{F#165AV_DogVJYh&t^7!eeFqYV~xh)sqTy zLV*W0Ezm|gAwcb1zyC_N=$fae>;KH|ny=^{2# zT9iiQ5xFS)g{TE>6 zX|)mQY|vWjvwyl4gUUEKU85MXj2-(1BBTS`Tunnf0sNy(ypstvY_F##C%vKBe{W0e zw$MmGi`M`T{w(TFm)YcxryzwFKTvTcoDtkYN7mLn@UMuhRPS?(LN7T_(+JnG3Tf|I zcO5F-s7R)CxIT-bse5stESJ&ITCsTsH3qde7WkADC@`ABz26!56aYazQECg6E6APV zle;*&gR_iBw%DK@bNq2nqBcvUa{&5;MW}YahRo{9cpbn?O z$8qQ>ssd8m&0Lf{t98|{f6(OT(i-s*fG?1;^PE&fMnVWI)nH6YE3x5FKRGG*1%&8E zIvF~QS+WbWZM5$@>`>Kr*EV2X=kH#Tl7c|lqYPbiqz=K_6c&&;v992JaT~|EmW>X+ zla*<~A~uiWnqo#rhBv?dSE;~``dt3)#K&cGJ7=wdY4O*~*?MP%b8lN02LrPgr^n5p z!XB>pE%Hy7l&*STxk+KFb*cWx+EM7!ykwcqR}OCX>+Qt+0x-X|mOy@Eo+c{-c6sBL zh019yY=dJ?tlsA;VS-}8-lRFl{kI%4m{eTQ8 z5E^z;)~9u705-jkoqzjKuqS?{MOimI>Vq8eupulXf&t?&i<4wo@Jy7i%u<2FN;c)6 zhSeBubd(#n0~97r{ZP(se>Bzxsyi@Mc^uMGc;O_RkaS%(OD^Cj<5@~6K|$7C1Vx2K zW9x>NEiOT?>SA22F377XBLO=&oGbpYk(J90nFtVB-VbMd^z0{%>kRsjt!=O7-rPz@ z2Nt+teD$k1Lb*mQmCXbD?~sTFJ}3Sa1a{;`ldIBPU{{G8NKbWyV1v$@WkdCCdQzsUO=KhnrioWySoHG4u`Hk(phBkO@v**&tp<P!Z?|KT?-bEva>N;uEeI!Dq&4E!sAQ!?r=FUh% z$~VAVQ?9E~CcAA(@zNiQJtDqOjLDZRZom2m!|Rj;HONCvj)O*j8bjB%DO4K@@v&(M z6gLcNS&LGTV9bkbyZ`>lz3C9mh(&rr z=(}T3;loJyZvOyJ8}$de;wIu10-UVHqKlAxMZ-j*S|`!~2CYC!T35UDU#LXYAyyi0 zhvWOh-8!no#sW$O@X8tfwxZ*30@A6W50BhYn6-bcx0sa}E%<@Hqu!OB)m|IO^Cf`( z<@N);pYk!;eH^Dp3Cbk6iq}W2AAX19cQgQD1C74`b$n0FI#QeM;CkJHXBoMO}$tljct_SzRiiS z0hd~jeloc2U**U3yab4d*$k4;b+tvoFU=^zJ;kRHE{B)E><~p4x_`L_x$)(kp#< zwQM~fdk%ffM{`Drz*Z4PPeIgJSfn@~KJ_@~Z~S~YlX6Q~3t z68@Xhu2q(^Ew6i3KJQFc@IlbCp{9bKpj!(aZkLqR-pM@y*`={5!aa~yU>Hh@qgL|# z;G1-d*$oRRC(P!njuv+h%6> zIQo%%!c792loK=oQq_K{Bs^Bpr7Gk7qxJZpBH)UXNMu;_B60?I6bokwlaOF_j&pz; z29k0(5yB_LMm3llhro4DUCsh4vNm_msdjqw^ut+Kq>YR&QO;vUq}KY#jXJMaDGhX~ z310dY1Msq*j@CG$j|b+e|3n`&gvS$&dwr?eS=-H(=oR)#(yHwrf>pokl4k z1twhZS(4yFsKISUy@KEi`FVn0qF1qX7cqfmRMR=P_gHIhRrS3G}>1y8dhVmlR7mt0R4Y9^;9YCUP{zh@}`&M$JX{O z^yeX+O_Cl)n~l`Wy47Z_eTMhd&@~ksuk`Qc?`#jH{}qyuk{Cf9PUqfr*b#6~eXEh4 zRd-wZgH;9md(uRJ_+?Cx7B3Cn(YzGx1b~xXYQDil1;fefLi_C3GUi#tBGh`=+&P5G z9o5O&{D!16Dzm~~Luz$0tCZXQ2=Gr(U$KG;13;x5#(e(FyWFsxT`yQokfk|K$Z=5# zl(op*@#^}#IJqU_klwnN!_wz5B1LbS8e$nenF60<5^{DJ-IIdsPs-3D3+>J4Y;}N( z?rc^mGm1>m8E%@FZmUg2g4zo(ZYA&8pSrBwp)@g)C!eWY?G&;be)uw-_?Ji{s@?)C zQLr|9*EdZcYHKn_hwkBTg=b`5rF}KSPGqn(%8+~E&)LkcNS8#}HDv!dTu0J6sWxDz z3FN%n@3%CLzkE(iDgX&j9*r^KCCz364N}y|*{|DkGrH}|S{vjcY#3{kd zUZZN-mVlifzC7JSa)l#7;5ra>>n5~lcLg>*4KJUnnzJq`>M~9h?RYIQdiW!gb37^# zkL-m>f3_FkbEGRCPM{k6-W1Q;n6#)@P9B|-r-!K~KM~4C8%agYBqvK=`)B5TB4gU$ zT9`pK77t#)cA&f8XierOpZ%r@!M| z-4i^T#G*t%930o8)KExn+u3iATa+s~71GLg7F~K=(HEaZZ-&%Dvx<|7967!>tq%iD zJT7zy^8Hox0w3&b?zc0wEUM5~;z)Ok7nkYMg*=&K<-#k3N!Bdcf+se!FY}|-%l_Zd z6+AN4pb!FraUM;FJ4MoRf`Ns1$jJ7K+w9Z*J8oTl*84!!<6}g{br`do9dd*zD^?cC zY_sGk%lohZM8M!WN?l-z4Uthcb=nar_F>Ex8seJ`S@Ff#yVwz#|2Fw_y8>rf;tu)$Jp7Lk{>KvjzfS>K_C~w)(9Bh$kgn}Z`{AUfPmRBe zGE*A&E)$dMQ7>#dVx;&qZ<>CJI|gE4r?rbqzM1H&^3#^Z8w>;LFl!{_%J?82h}-9j zLuXU@Gs!b~-(KVmF|B>u`+wPX^X0o>#* zl%qh(r^x7;MMW?vCSfb#GZh5pxh3H@*-Twjvj6IXPk`6>D3836L`mBdTOMZ}DpybA zymUH#_Xdv%W}VY$a6L1l=MJh$+oyUORFa|ro=d6QF?2nN7ZO!qEQvPmE8oc4fNfYw zjl^!rcvB6VT{Z4gnb^b!x?3PX^w9C%yBNfdH~zH!#P$NTHfHY*w=czb0kvcRUcnW$Y64^ z_sy-#e=>+LB_q{o`MWItRMZw5$hKIv2|FBzqvfIjk2atWbUA9Ql^U4RnF{QJG`h8) zwFD>2Z@c&Bit2#J;%;RSm33>PWEFhV1k0RI|6`Flsrh(=+CbS=MLCzz7keVZp1Yj_ zh37h0`e9!ENw+UA1m^VdSkhc7B~rKhU>MMcP654~MI5+cHaOwrY{k<=pXft$)8$Rv z3pJ9#__qvLWLhS8L*j7G)H59~s?FH-?+L?nI~>IC!Skp!y=WP~$$B{^-m?0^V>ww> z1gL_=}uLFZ*TVP_e|2MiUQ?uwjv}qPHfv2 zH~{}_>eh};Al=Etbsu25{l(Gx2yZ&<^2a-Njkp5QD)oN~?b`Wl{_H2D?gzivMwcqu z>lM7cDlXqS#;$su!naN^`i?_{Rk7zzbU9ee)-wk+;b>Vvs*)obHC?r}zBa{FK=9y7B!WhEg*IqT@ zJ@1|a#v6O1{J&oqZ#?qKkvMSs|5?Llzre%=oRbjR!phgWu)NC`;rQksTMj`%^08BqTl1_OU?L|WGV7IJEx*{k8;`~wEAh}?`0!f4neQN1*?m5UJ zRTEnY4p}e8=_WHgKM5=+TF&#DX1$F;xPeZditN5uiN_V(?)fb<+^QDc^XC?Hr^lZhNS5lQFw? z*DeqZrQXm_6je*@*(^*nw~0x)BXIlGH|}37 z#Hq>>G8|JwK5PDG4*|~&>oofK8j`N4-IkO=-;_O(f7RjNDL%~1qelx4D zM^mH>9#;q@mt;7;^6+1>TIX^fc(_#*z*QbYk$NhIo@Mn|MXRsxj-(9o-)tl}-z|=* z%+P19rbYC%mU-Wsx7SCecDwPTo@2hzGLl&fy>BAxzgQ33eRk`#&zT$5UMt0Na_l@^E#(E#8Q+SgNL5=}*&WJrs@8OC zgE@-%nPUg?Vr&yNBW$mc47@Xa>A5#`>g#if3s*B4m$q*&kZ-D4J%%fgS90k-=8os) z62HqwY;O2X>h%Zi`LzuCcS)3gDAeHg>qMGQe!H=I6$VB@QgcsZdyX%EG-c|<=^**ny}|K(+$`gRHkbWbA?Mo@FXtS*4hc(#XhS zL7`+#6quQI*xZ!arEa>9&Leejzu!-(B5Zp8g7c5jsuhXGxXgbTkp0KN|aHueX|3 ztfS%8ymI*_VqxQWjyo?H6Je3ad1M65J^TVKS03lZRcr~PkDLB?cr70_-HZGB4#2MY#F`znk6r+XW-|e4<_STZJuNy&g{Vjf85m^Z=LnrqkIegEzkBL<}kBJ{{A1hN+ zM=msZ%{T5fSdTw){S0dTG{58jZXIrcmJR+KI=^#H;sdSf+oDQ0-1bn9gHs6qh;HN? zS;CcH%`Mx-2829-oxipkXNVxnr zHpztDL+Fnu-JgC>_>V$UE`|d{fG=td(5_FvugA((=Y8sfB@iCcG+H~QT?&A$*U@GtNH5Y6&ZZV(Lzc9Vu|d*zackWb?PcRStzFom-O4f0dw z2g^&f$LHgcH9SMtZz~4YzDm_IMMMvnVFkK6DN;5MhCjaD@2mQMV~RwW8uH0XGI75GC`RMNx4?efY{Vy}*=KmR*D z20N!Uz_N=GAYe$87_$=EMl=sKvpfEaHPg7A<#kCRn(?fkw5+pP)ewg#5kyROt z&wxFZqo+#&=3c4JivuFlDuvd!rxr)k1n5;8$GKpnm4g*2=byr7S|aObnxD_N_h*DY7lk?y)C9FFTj{7}cOxo@l^^Q;ZT9WW^k2f4i#Jp^uBXN- zP%A2lX_+*))TWA>@%KHFh-XEqflo7Ca}5676&xknrvHXbUtXMsavzPJLe4?&_G8p- zS{8Q?el9a2XI~X7OZP=p0PD?0t7iIMq>@i=)2Oc(1vJj9vICFoiG_*Xb_RA^W%4E! zg?gAuG;zabHd3B>QMoo_AwZ4FSem=HiM8Ebd;5-T8p?1kVbF_0 zPcwt8CSWWkuB41Y{Vz=$M(@#%2L}F2`X8%Xy0SgfGJ3Q0Nrlq=5{b+Q2*{(nzb#nC z353!Msh<}o&`S{1FrCi&z}Hk7vo&?wt8VCq)_yeZ#KcON114yCN!;M;q-yGL2 z#(1QzJk$RhHorhczM#1J3FhgU{<&N=G~bQSkT=ato+@sJ+NkMNf= z3ibC4ka~3XF2*8e>V;^3mTi{jjWB7pVwwW=8Kg}rWBtZW(n2O{8~;*F<$Lb>Ppv)oj98~M|BSA_b^ zR};QkmOF9fNDB#O6@gh*R#YCP^baZ`Uu8!yIdK)dVX+sF8nxD}saqTk*u!*lQE>uJ=yy|1$ z>y?j>|46-RN@X)j@-_R`xZcC+v6MJe2AHgsUkSER4WFKvq}#oQWt_JSPLmNUL^tRD zG`%UdLE7#5=9Irol$y2bWzKBeqdRT5h`1&Hp{t$i7~{!6X^iZ!OZkQ!)D-j11Cfg2 zbL2Jh3wR(gtxW~8?fx0IgbAxP=R?acC zbbp4d*?_rPb_@Gt#!b7*PWH%gaZ^LEsjAg>&`&(CPyuupquC!=>R!S1+U`L9fjVpY zZ%C$G&_{UD%te(x$8u~~fZZ#<>rRQfXIsPWD_6VzGGmrp-wDENPZNbiGiNrEoB*uL zvAz@{Uf2rv6 z?0xUO?{eMOzMfyVd1i5l6^+6I=xDNb^Ztim^C8pNA9h zyZpMNu(tXAH^4_zwp_5QJwuAZF`JD8Ez3%(yj#!=0a=2l1aMvM6Y`snq7C!G?gxd=P+fRJjy{s&aYVw5a)d3``c9)%;!I?dxK$v9ammvx14}>6MlGtD92CEG#@4H#c z;@rxEPs;+Y!h>Gl>pIA*$3$!v+7DM!-9NdLvRCrGH_2=rLa_WNS3e-rlG z0pB&L8zZ&~j1enB5uCqkGg*3VmYPr#8P0e7+8cJzJ#;;XDu-Yz`$e(8%c-Z$LfP$Z z_SmxOBtGo#dKULWFl2h7AjcYvdGqO5ptWRoqtojCQBY{s!W}z=rI*WSsH!>?bh{qY z5xn)DBi+;ssU?N@UBS2l2;$O2$*;T2cH<#8zFZFRJz%}4EFU{cO0wdSpNex6YESpW z|1kOb!}8E?DMjN$Tu6>A499<$xVb-=ImX0w{1SPcY@y>w+m5^DBd^Qs`v^Yv6H_S;dA^r;2lGN*S(USj#*rSD$b)wI-e3|}L<1vllc z3LfD*ZSTi3@CV=?A9iwJ9;9p^?x6w+W~BSfL=D4TrGM-9>$AUhsXdJ+K%cjMm)RdS z7WLD4xf#Bylh|oi@5%vm;hJ9Ruvh$2=jd6a%!tjsm&*&SdUPL?n6Yh>LF0M5|IbB;|aH#iu!`?fzRMQ~*;GPXZ4 z5=Kopm)9nRhzSMd{x=H4k{87iS8kUV9u1%t{}+JC%StfAtYOC}ZzeEU08e)BjNY7L z+)Mi#Gznl|JjB6D-DHw3m@quer~VbZeBF4AIc7e3vkVvOJDh#nc;tu22oA6R9RcIFHerw+*lt$RNXWXxs6nt% zw1Cucevs}nk6kn5kJu-QoiY58=vMQ_zY(LX%C8nE$?dJW()B=zoq zgb1>1iPKR1A)=+3n!%hba{~VK4#!7>1!_4pG#GN-1i%9gE{E4lJlE$#VE}Y36U|OB zhe3X$YtHMxfb%545Uqj;p`|_KIC}z&_FbCrnh^^Qa9>PN0WiisdDC>912B}(nsL&3 zR(=+qXr-;q`j1wkK)`taw$&1X<;V4kv>}j{J zN0Dz?O*RI=T)NSN za>vfTzjy8@La9A5MeM4U2@}UcrNLGdTlmK|3vll`r$pJz7dOr_yMB zrSjAM`%+%>7YE$&GHLGQml>%U*%`%qyC&C)cmogHwVkOzH$*j({>?l! zQNVIl+6q#RvmPX2Wgnnvp8)tYo!3f$%mFio46KRd>xy~KXRyVtwV zy9(i?3ConP+@fRm7Y9P!aYyVKZvJ}PqWP!S07)Am5!}7lV>6y3JarVdG(Hx^Bl(#R z_HgdIQ1o4(OJWUsJVI~Jgf7baK$fw$0jN=UkwN=oPpp#0&YcBshmhfZQ5d6!F>qMF zFWa|&28hAj0g!;0fU-?09jkuWkf|VHpJ!e3w6|oR4lQ{yq%2S~dtNI^>`aUzuG;3v zPrb4zr;V$O#H1r}!w+~+8oiA(fA_=WGF8Us&%;XO9r-aCVHuGb@q2gZodsj{o6*)D|6)Rm;ac1lMjD3ho#y&PG%vFG(A$@gKo8P{!E`72?(JCmu+Dtw*n3*-XD9y zl(;vYzL-rOR%pP3wElnbnEy|R$26P(cPkw5Q9Eu(C?NS|K8aXzwDScfP#@j)Tzj5h zvXZw5>hsU2+7HL4RcV%Y?QD42sE!5hg7e+3n$W6K9Qox-pT>aK0C|*jRBArLzoTKd zLr1Uie&OI-l6kJo4=Dn>{WoS)g?kvcqz9@P0$U5 z%qO@@XQOysmg}g2yX0{{FdG6y6zo@8Pdh#(EnmI)%Zu1EM5`ur1pSt5AvHPg2_S4`+- zHl~<14lufG>`T!4s)k+SqcGK5ch)!f ze&gPzV4C~0=+MiG%V-J*X}c3ZUIdRgtzc=8k`Kwd{bSt!8ISW~=uB@bhcEdu8Bkd8 zMi$C2#jN)}7u%#Bkc-Q|bJ%MrIMBzF5dP12q-P?m1r2y?TPE{{kAnLzLw2Lt1cRDI z=tKGFbA(Usl!vr$vk-7G_b;PsnP>FnyMIqFQ^afbm;fxzDn z3IeYG0bCg1*>^WidCcBzR%!mh3(V%?6!2FJMG^D~a4>NY{*RBJ<@P9P>?S*6BysQe zHW5$r_&uE;PtUG84$hbA0_cPs@vU3pko*ytoJ$9G?&rZEF=0F?Sa$Mha5YUpA(fdU&F*l+^$zZZtv;0{WW^dGZ4B6meSqeQBcRIh!kbpp%6Y6? zH$MNtErn(2HXjN$$q0Myc`3j$a|5QFaCxdXnkbT=k@jRdCpgV3ceKP-f+Tqb;c>~> zxw)@A1WH$J=qfJ9+F@xke%-#A8pMoO8&3y&Y!vC6k?;oG^}n>c6} z2@wJT9-%9BuEBfHx9Y= zj~6w!r^2?<+hYyW@2q`QZE>=1jm9X@MAe1L%|#HMZb3!Ng>5wCqX7Og%rXykhqv5G zu_xqUZ)+~yk06QaZoGPwdvVWLyVTZ|fgw)(w+)}~ZXBH{xCpH3r+XUoEc(fYm6$l?>e~Mv@7{9w38zQ}>d`sC` z%zj0J7yodKR+R9hl}#ZQalfgI(wIgOmiYEbf}6GF1)LGJW&JA1rrIk=eCC%in_<_M zduEK%oF;YLwOl?HQBFA67xVt~x?P?omR!>?3`BA3Lof2R;qAyw(61kCdLQlu?=0l2 zP#tOKkDvBy_@Vn3`b$B~(uSuOUh|7$OahMwU*JSv%j!5yY!I`8bp`aBNJ)FL7Qp3d zhA)$|ui%MTK=wc`HvLL8V|zCyh8_L{g^~!j;GF07q)DzNF3H>=k^1L!`2Q-}!*-1T zjjgVMo;$=lHkBEH%VSe%PnGpV`qwQ0)}fEk)K@_LX+zy+uPoA~_HEbq58MjvZ_Ddu z*rAsOXYn@4S4N_ZCuTH77~6+NzI$z&9j~1`nz{}$Y_4ptvwo4c>-)iOUg|^7b4eEf zrA|28&va3c4Dsmu!VQj1jCCtFms3tQP#XGka5^}ZGuq6cZ03#ONF2Zw21X;U#5;LlELq!KRkhLiO&pN`UWj9Gg@ z%Zg7cv|Q&MMO}2+GY2Svb^Dis$BdyosQViQj)Te8@Yf%tNO>Oxv2f3t&Pd2dBww9> z>k&Sl$SJzx9FA-Y_NS$z!dAs+OHq^GZ=@Od3DM5G0g&W%Q6D&ZuzNUqHW?|omWVSMu zKVlX3=cn%BC!q-)qqwV-P{$&LM8Khvrg`7AP z3w|w-A2${|ZBt#~6)YZH99DycZZs(G|FU-q$)YA=nH9L6+w^Z`v#!=`T>}WWa|u0H zyUJyNQal-b47}X9lJVHOeqqFvR+(b}c&fAoZnZ#MkZ#%?rPWVn+-bhsZdLr{5aA9P z53BweE*_67>=XHOWPpboVWebB@v*%8-lS!+yc8IJD*8U``cKvegXnp`CR%#6vK+N! zAbssifpK#tHNDZ+f2xJ1!xO7gTD2H_F5vqZ;ECYk!KGmjEhbS-Sem$#a@M(PxoOF5 zi?>826Rxi-#?6iJ00b8u{1LvP;a7|iaDU3f&n)%2pmERO%ng(mKU9)?N9!Fh|4@6v zu|Ne}B;;33x47-~mwFP|^{ncHf`F4?dRF3;de5z~t76n~MWS){oe}bU;!*IR*v(}s zbvjalYvTkw0dj5zeSSVT1*#JUoc(&GWTlZmbBMvk=Y%{r3eGtY!4kR|w}pHUB%0nG zc;@ZZ0%W3vd(8$p>LJB3(sY~T8T7~#|}rQs@u1; zM0|0g$GhX4j_!WEMZ0Dr3tvDw24>qimiN%JiGvdUtQy{!R`s&zesgB3eINoY)F~sN zhxMWIjU+w@!;d@6TxoP9?pp0wusu-(MwU7lU_bYL+6h&4Rrds6`nY^*P1D~l`^}&; zW4pN%;zxqSOV;AZ)R>3WVzW3F;f3erb!)ft-9+9ATq6K3r(;I^<5?ae&59~RDY}u~ zer(9H*%Kf$D-a_y#}pTXJO5-`Ati_z*E4hM+3|zUeB<|o9Lphgd8LpiR<;d znwItC_P{Q>_!Ei*W*pmq^vchkO+joJNafm*&Ap4lzUHZ%hr5lNDdv)lVe{&@Ylwsi zE^v%`BT7(5`i~}SS-t+%BJ&z+B$rlfJhVB}V0+1(*g);^>_{pyLkPTP{xEFRoQd6Y zEw4r!Vb7Y(#>9lXuTm%=(QX?i^UwXm&L7=37xK=PqCW=Ow^Ks^uW zbmSV9JtT5~%dn0~)lGm(13|=0w6>y3P{Zf4bMpTfSkZ~_#!{v;U61UN#Y}2+vMiq7 z*_x>K=g(UV2+21ndZnyZ1q))-YPVcIv7XcZ4K(%emm`!0 zsK3b(JJcB8?maiuXVj>-Y$`Cv?wtqqd{|1j0W4vRhG6jMm@O8Oe9z?^JFQZ@e%(w@ z&hnoXjLX|{w^9`CMRg{Ip$h?){*~&z>4S5(Idmo@^OpYJ?r)hH+c{gl6j~##rY*kC z2EI-AY1n4v+!o#NlrH$#=$>gbeQvtK#VEZ!9>`Us@fEM&wjQ z=NB#cNg%~}sU4hFXUjTt4KHpc4u~-QxRWsmtUo}02i8BqEBn1JVCT9mLmA?L;>87- z&MoS@$R5~l)7to~vH)6pZxq^|b&&qCXP5+I`>)_FF9B6;cChq(nsr&)`iT#F?^9fw z#ksF7KhL!S8D<&^l5o%_7*cE`;e#hD=Y^@mx4wnqNyQ1gU)(zQJw7)k7cK0w%_+1$ zEnat3=dTeZ(yh3Itp-S(6OyJzO8=ZdzMx~^cWu_!Wv1fjIdOtma)dxYuYUul@@}&T z_qB!Roxx`e$TK&0y)vsyC)<7Nz4}}s${D9TuG}E1OQw3U@p-OXjb62jz4lNGnzZ(TgK~=dYZ)S%;gF zl^XrX%=iMs5r)BF)Z?ybZc5Bcl+Rg4pb104Xte&uTN-@-XWirFG9+0ZZ?aNWvqzX` z$3f!Mf6x=WANNAD@4aErFt`j~o{&>P+st>R;v;g7dblaWQZKrTs@a}5fRFj8b;N*R zm$OFp8rH}+TF?Os&I7q#zfMOSp`BskBL=u!_l=itHdr6Z#6g;5G(no~wo+?T3XKUk zZrdQCM_YgZD|S3&Tdg`J5aM{7nR!)|MfXp|Ahwn_o8={Db*CY@< zt53H4@EaqnE|CfRP_p{{a+*kr<_5J#xd&b`m2=Vk__L|eyDE~(Ys6FtCKR+F1{ua43!ki*diX5()NsgRH(4#vNz`PT+%-pgYx=&B9b zR5ll*=Rn#O7W`jBdGEio{y6aXAINPH5_%l#INRbEAcB%|%s5wagBMz*5{ERqmiFAL zLNHj&HCo1uvw!D*tVFxah}GGW@*YBNKv)yW2E&5$Gdu#u?vguA=YBk&&twY=ey!yF zY~Y-T!sKA|E1RwAE?-MlN=GyxG?cS6WNHaLx7aS!Y*3aQZU-u4=Hqp%gH$NKWdr4c38F84W0+&Ov>MJIt0=H(M;FXZ(M{P3RIwoZkEH^y=FEg` zsIl9oB41~9WXE4>7Am~aC|{QKLj>69j&aBbW2gtP~7R zZ!VLdOBWL@zdzbB_ApTWM7P^WxZMX?Ccd5MQTL9;G43NSsSjtCm;5FLs@+-UeI#mJ zBG+f=$BA4!k)%kFzUgV0Me7HY)BbR?-6|Y)8mORG%!ix&dNITBF^{i6mB{O+suI?P znz*9L>$=AdPv?YH;x@jRUdk;oTz3Axey|&)U14KwW&`uDaNl09*00WmH{6fI{x)mQ zXipDZNv>(qm-k&(7Is*^se{@B!D)nMTa~kax$S-gyv|E!&%RhH>U%!_gpF;1k|IUg ze2mMIkQu8{``461brNK2;bf}-&p2#Z70D^Ry!IrG?xtH`GHAT4I&Ohy&=|A44ts4f zGniLiMHN?giU?XmJq|SOV2B9%*+LrwbV9jGcvTfa9oU~j15Rmwlnm$|u6)k8zPSX4 zL4C#w8-a89b!MjCaiuYL3;LpuKfF1h9;*6A*qw_#P#%!<4O_45coBQNx2;>hx7<}- zAV&nHp|5ahG(4N&F;JhiUQecDFQa)(jXX%Z0Y9nl^aS}NHxu7kLl$t7#ZJ@O<8XXT z(B_!aSY^9zCo_YuqHE%}8?%UUY9O<774jM)=+bD}I?q(^^F?a6@--Te|H_BZ0Bzv6 z>9EZUwQVAlNFj$-!~3?0g}7!3c^fTYH`EFe%?$JfNQL--}+o_W;U;umn0U7@dxS8|2@C^-d9GK zeo1?#h=#KX)Y!t6cD!C=uUECk;E2$|^?Gv_?!^E$L9e?={9tKVlTJA!$HpIhRVSPf zf;Wvu95=KdLf>VftWyf~+Dlg_e62C*LkFn+9ACBe8-={5MYggj1~Mg@+pyhZ?Ob!; z>>?!+mZ~UvV-6wcnhMu?UQih*)%OD6RSS9Em|8M&7+fy3Y<{3prm)g*Sz+;K7`3^; zI^gip`RDuQS*RJC%puA6fhCN1QPzl)SA`OlPD!S@@57Lfrzcy!#^z{VoMvu`89h%s%du4y%n4QBd@ zS**UN-JA5vUlz?I0{(80HU(1?8#&iQEPb1mK}5pFHzbcv+W;Z?kyXaiKvj|0`X z9`rWSpdmENC(z1EJ&Ffb0-BYyQv37tyyAD5;P7}Ae#*zSpsoMDJLHiLj*+8}w*rF5 zDWw27Tql8beCCyjPs2O(>Ap7jAd5vJ1k;r_sD=}qz8{*Mk-)BIOaC7@GNiRBrFXAz zVHUNAcEr9;VJqw`Wje*Fxc<1J%fa{>9Ua|6!z(=A2c*YGnAE>RT$FCMHZ z&5Xn{MBHrny6ewvn_9+UA`ER<*vYuj@Ozovmo)I`6?7n^0x!RAy0&|wn_X!^6BxoE zyIQ^=u&SKJ7o_xy`S!U~p^bjH;N-`Gl{>JmlGpFq+!HNz@1$H}C>9XlfjFTE#ZS;c z!S%TA)Ko#QDbVDDeyOpK>9{r;wa}L-bew#T7CB^g0y$L95K+B1=w6kNfQxDpqM4+; zSJ2hNk}#v+*4TuvpXJhBM=NAs#F6*>awtDjm# zbpWESh;+}n??D;d7`!|%td0jccbtnS-UKWzY;O%)LRo1ASx?>^|0IF>1bDnW>63?h zDf#UQk0>)EQ%ezq)%pb0k*IhDf?f!3$O>2$bRr?X8eOi#nVABCG^O|Z2H?3;@PNSi zn2to_`zCgyhxc2~?=`&ufhIhx_thU(Q*axr{JL5Boyz5Ln zoL|GFhb=?W-R2ess(}Ql)*IosT_E=&0p&;VHWL`^{fWst^Lq0IeaZh~Xa`FcFwn7o zz%hpce^NOl4>H_(dwS~pwWA(~KM=mn#5Z$<@$imm^L_PD`?QOPO%8_lUypi3ouPl{ z3;1;gILe;Wz#aILFLXv-Kw_Y9--1}_7{ z%xT6L#(9m<8G4pByxqmqg8luvSm1Scaf~r^H9wp$t@OnnaQ_4dkv71DLQ9d8H!+mu zdac6+A(~kw0e}{K>{H}-!NoK5e-^qL^|v+7`~N+!=C7B3&z3=5;I|DJZms=onb5Zy zzwN~kb%%-Gx&XHC?CGh)-!NQ`{eS*ZZM9dEEpFnTQM#%cx5lItW_y{d)8QZ)OV@N; zmIa27Vv#qIcS%YY2Y(6|1y=-ESc`8jR7JdgL3CcewfAcIc(!KqthoG=%HXUqH)W4sikgmhvL)m3y|06zX^>$ zNG;cCUxF43`X_$6NDa(#*?BPbbb#PHj9K1s4I{c=Kvwk)L&<<|SV|-DxRwdrgxZm; zeZLC8e`PL4N?^b4uN>@4`~(CR=Nul_pN0QXLQ$Chsu~uk`xZs;R|x#^l2l#qhyS39 z@YLQcmI*oSR;(F+yU?2kG#wNuT^YeDagM;TZP2~H_qFp0#+IF##aJ+4zGV?{uI0BpYH{YL&(D|zUlG~hQwZzcWNYA0w-25hnI8wCiIMMe1ngR zJ%#=DgRNo}!?=yrYXZFSBz@&QW8d-h_-gBrCT}EwgcDVUBE_+{po?f}fG}(_soD&I zHaHt{7-`nq9>D2M2gg{&ePTMm?g&>{1=dbkwSTq;C%;)lT9NGmyVu8SCGUQzjc1D^Uix1YER4iVL#RFOi25>#` zrF6eZNk|xPzoUPubINS44zDFN?g`qKy?-%Md~r66DH9*?N(}p%vl{qF!Aj0ZB)_#p zD&)@J^WvQXuNAb)A7D4pt}oV=nE3WoLNC8N;o;1|fjK#k>Ozi!L(Wh746u)p4=k;o zASa;Lky*FDYHN28g5n!^{!*FSx<618bb9KSsby24F(XHa!Zn1KT}8uJKokXMPWp8w z=2#NVj)^bk#Z{nheE(YGT36oN_qXeTC=gxvk*>YEZE2SZX>j?yRr?cbM+3r6;`W}M zlFpCiy_fgc_4`9#7Ezgy*iMXnjH0}zl+B=6L}AW&IlG7R^>-(%g4Vtf(5(4KJ=WP6 zV>FL|Z;{D|roRwQ2x?+KJY>X31`Q05vkL^x|Jm)^5%A#APRgH_8uoKbUbg8B5hZnN z`U;5_?8!i7Us3en*`qG{w*&N20+;I*5W5q3%!Xb>G34aMGer1MG*huSr*uh|xI{Hj z62pd#dWXc@Jt`PE_H3f`lQ{-+L?O(B^6?Xf75aChDhtrRA^T6ZPP@8j@X{si}sNFtWDjvw@#8DwX21y#vgyyFX3rFG3ZYH93RtjZQa06NOwFN zCAhPtbqx_0>sO6CZG99ahM;{;Yr~E^7;eT5yi}U)4O@F1j@|WIbRzrQ86kMM@5j|p z%+*K*sdhG)9#VM&(z?JZs|#ctAKG?OL~0+4kelEdgiWwYQ|%E@ZYvr|8tGWwTvQ}q2; zhW$6|ox+G%VMY#^fgfeUqZQV&R&~3uVL1OWzKTttLAl2W1D6;VMd_7qy?(!DYMf*= zte-&IIr6k`WvO<=rgKYb;o#s+^s@c(qoO(Q*SYTbb;?OZhoXQmE_}J+F#q`1NcpX% z^U#LX0Mh#;Ye?Z^f6$Ik?XZ!+m;;t;`(XlGO0_0z05H}qLfL*Xty@n4s_baZ-rBdT zw&V4RXQ>E9fj6=z z0=bsey(iHIq(G#gV42OuD$$*H1HgBW-rWdfOcE7z6X1ib|L|8GfoK>jdmv7j_V&xn zMb2Y$12gcpeeg*5z-FD{UPkdIZ}H9M%&8a}ZR{$naFju#gT9y|MX~cNHGs{T()P$t zq{W%}$}&p|W)jV;Gc>Z+lQa)jnpBq__U<}k=V-)N;G4k zUdz+THhoI&mIlGWoK^;=jeDz3F(cPLpc_}U{3k01-}n0Zr1^d*s3LF5*w4A72PX&H zPL|&tSJy_R`j}tvT{xRr+j+m;3>n*5UUxGPsQ#%3<><+PilJcpCnmu9&nKDr{$Bqw z^v;{0rk#+kJf*-*mTl;he1dKluk8*TBY4#;gHp`yo7xSezj&?Uws0j_>BlkQ)O}eR zUR+@gS~#Jo$MqV8`?;`H@~Wv{w0xa5SXp>4o7!7XBc!ej6P00y`z2hc`EAwT-oPMR zy`9uwI8m5(-PWgsw!9UwdxIH`aIT+Jcd6GA92gbJ4*baT9b(zRndr%WqrxTC`H!ie zfv>o_CZU}<%i91u!IT6=3&1A5M*2+abzz8CB2f1sec^JnU4Er#z+2C*S~Dkl1`iME zhw3+-9_gF|XlxI7rZj^?ZgzGWZ?IzMyGE8u-!T&oQ|?qccgX;w+n5QEt$G(zT+2F?_SWMoal3i& z$Ed{dx)UCC4+hmli%vR!&(B&EZ|onFRwH+}cv9R^QF3!tC~?t}vRj1}Not+D-dwQP zgHdGJ^u#src!!h8Q7d`HryW_?qWjxWMzorkQHI~m^lV&y+r4q(P9}!P!@WG|cCNAO z4EMSS`j~9J7G}K?dd2NS{3xXqR4J>=zYn!(FJ|+L%G$OlTIR!g_;?ePCWbe@w)#jxwpo%(?qyWGCb@QH=A&DNo zJ6B9(3K$~dv)o3P+LHE)2noMzul5CF7AJg^R}qYQ5&%-d(!uEk4xkh?7%|tDwBJnx ze18bPH7{mDBAy35zPOwxsR-`~O+#HaHMQmn=I<;dzLTrwy_ zK@HyRMZxT~x}r~o9WUTNwaYX9K$4>Hj&GE$R$|`nbqI65kowfqda_ygA|LFhyOrbm2HyI`k-rHc>70ej~OR(>YRSE z?YKFFWn0<2{VhYwWk1-xj$^i_m-O$^gpJ~39ur2?VuV9LRN5YV@YeuU26Kh$?XtiC5 zu7r)7x(*X8;K3z=xP;f7dA{GU$TKc;7!zN><(wKG+|%U5)oY+}x5(%);=Wvx+WpeO z_C9qbjQ!wwslYSWsW>Rlf%n`*#z947cy$h7`1pp`>PuUld%V~z%ucGPciE+RghoJ9A@83u-(Y?YEo!8*{-^&B57RCc?#Q&(6awrv)`oub+uUoBOh>!rW1vl->r= z@_Dq$QxHAG7C{OFsZm8Z4Q;3aW&@K%ZF8Kn?s z<31@7)BiK8Lz+w$_oQG-zx+RLr!S(FMoyTcbte|}A6cFQ7l}r=CkSEiS$l2db5o~6 zMstNDHY<0HVFwy&xiiQZyFuO;TThsGZ35+mis> zyZ-eh%CP=!!@||(zP3F{VToXIcc5GwuRGTo#|<;qy-nV<{K5)0B&nv~`N_-&R~**n zpVBl5U-XRxGB(lX%GMYH`@omac*nb4}$sJXR8&|yFR-m%OvhJP&^QFgO)}Ioh-`~hJevu z3ox+_KiVmuGpUJqhSUVMe|)mZMrwJsXn9c*w@Y@^c6(+|*ccICz*M%ejvN#}BXeaU zO}rAVcr!g3G*Em_v{13bH!(a0>5E`Ayx_+eGsi{L6Z~vIH!TT z5FC&}Ju5Qx~Xxp9XWo{Oi$nN+nu-94GHRJ>Q#`2RQn|rRM)Z)j%gbPmHi+rl6 zh+L!1B+C#pZrzY@Xz^^4dE6ZPL^%)%GU~=nO5#zF7ck$YG>=j@T0Keb8|9p2UR#i zy7NcBVeS9f*T1a};ox9dWR+y2hd%W7Y=7G@Z_ct(4%yOCo&3)7(=$deI=CKHCysbO`))s^@21$0)?Z${ITbq|i2}ae0HZo24nrj~cYajZB zO#NT=?#aPTqlPSwhRJUp$9n_bK{qB-6tVY47j-;Wzl9wYla=D@57=B4=K;F5m82Jc z20dc{a=^qCUq|ad-+Qv6D8I0AAloQie@;BuO)dAEQc;;-Nf9B4--KjP+n~wo@Lich z=H!wa$-#HhA)-5AQ_3TNmz>mvEeLQ9Q)at2)@pc`HyUy9YbLgu4ih|rY(vlQ+GzLx zYw>+wrwk>sNYT(G**KYw7vA}7rmj$2-E`hGOYQj#c3{Lgkr zP5SCM`7fF=tw-r^oK2Knj)hUb(GzY>e6Z$F?fLMA0@bF=sHGx(ubn~aNa;1%=C|^K zn3c~*z@LL#)VaP75zvPjomM=xtV4h3&j~MgAgRbIN7a3dox54nX>_UC!0XVT z1fT11x0G5KCnimrUeC&fxHupNUA(m_w&czVnaM7LsIV z-xHV!60^f3)9};9$b32bi5vsN%?ji4Y1Lt*^P8hyS;sNF)DnV#Z&$bl( zmTsCsIjsFs+t$i!e`nf{KnSaG09qczM;Kd2J^=u(!8mV~yDSYzJiPd#?rBBPgyd_W zLh6o?_rDuDqfp7n(PWk%xG*Nx+^3$do08^d0^gBHU5fal!@2A#DrPU>>9c>&!JD_r z;br7Y=wsK_xC!FP|8YMg5Wx()HyG#aR<@Ku9stWGBNy4|QPb;8bWv4_y(hl1BkR}B z$r4rHz#R=+D50B?Ln$tXi{(UzM0DkcmZ z24oo0v%<4#{yhuiC(}L4bUZ_a2eI}|@9vn6hY7zB(Dr1|y02m!s(NOjeyPChRT)Zm zX5bF@e#MI-ef2h0@L*UQp)CA_$cmdh(39Z#G#|U7e|EMv*}A4Nfharl_rb8sFoVZE zjE_!g^ywX8fYkj!OKxBK9e4k?m>?d!+Tm`iSSQU`1-vS_Ki3b)QeaAh-3l*Cl5+@Jsq6A$Jd8^I4>asQw!mWJf*l-S=Iy z1nbcmg?F9nAr?SU9`K;XH1S;HMg{KOKP&b?$u;T&ttC>_g3M8LVYb$)9SxA{Vn6ts zaYk0jfv*t#M{+B-AgM-vqsRe%%IHiUi;J4UBeN%iPoc}~)?;BRDOM-A7xn-W)Y{&B zbeWNKclkFZyE4D1e?8g*^6fdncG!q*DoEQz+}LH(hZ-%vVElGCRjuh-yprca{;DEv zq8g}E792}hr$HW-#)XbLKLn>C|2Keb1tB`9GE!wW?hEy|wA!LzH986p6@Vz?_Gy%8=< zi+k@XIp0vA^XEh5^O=N1xGE2_yZw(ZWd(mm;ItIw z0Z*C}EpHi5jrpd(k>y7XdL1xb%u)6EUF2NnMuXW{11$q1@3Q{jx_sNKvlFFtLAeC)n3pME}G}dYg$At z)8Q09c!WPlHevk?D;yp%ny7?DhRfEU+vmNlGx9ADS`|A=vCbufY&C}bjm1`k9WGE~ zRqcm2T+|L|8v)k~-npQPQOtNAJzhAK!3$C1y&f72;H2$$-mfM}Qe9frSF_k& zmVbNN?6Vs2UOTzx$H&|G+G)y^AUbR7h_ULxoE9hmi({?JzCdx`c|`t_d8Zn;J|q@@ z{3osPso@bh=_l4Ea2EvGWj;Vu+x)N=dB1ipzU@u0%|dCB@?v3Bcz%%qg`^8SP|H80 zBvl_qVb|RhsvNP4^Y8nM>FNGo4fuJ#tYo`qw=hj-S-@9J{yxuud#CdZ=$dwEVxFFyJ5x?gNI@cfYmz+Qb}woo4cAt*&Lrev!?SC`OO zVbZ*gCq41dQ*xcY-T1cVtlh!+4P<$@U%XM8pEbRZPOswqFk|*AZ4lpOylvv320D)^ z&w8UBA3Rt;iKnCchfdbSFcCIs?OhnZy$rPz)%mLKOTaJT0Aq z>IF=n-K-DS=xgmaV75M^os^D2X3Cwni;*-r#4+rc`LZf&0tVcvR*4TcggwrD2I{(b zs0bbKw{=p?-5_XMkjF9wj%lafZvIiw^05laJEjzzc;C7)GJF*bgdW8v;~#@7b89!m zJGY=HlN_xN#uvO4+3rIl1KGWd6D_$J-mrUU9(H$tWherUlB(TKl+jv-I(%~eU<2gd znTQw!)&lmQwSbj<`M8!PBJ<0{cQ-UAmr$1KtSsCtB(etN+^42~=>d(sNnWAXzy;Oj! z;P>M>Gc_-4GUl8*d6Dd$Lzkc4Z?Y^$4jPxO^BI5wmktt)^gALh&do0>2>EAdO7F}qAN8(b9K3)B|7&)+mbKC$qeB? zYnn#R0~9x@(Q^zRgXyFsO-+QykQ2`C4sB6t>}s+NUywCRv&SRo9_$6d9YiM+?u)c# zNt7@!jQ_t(PXdTg|F@*6XO>SZS*qyFEL6nS6KfgNvCF0^j$ydqNx!wn7fL zf8hxGNx;S$I%J7I^1H)hyscYH;P)FZ^Ij=%ZQK~SP%XBH6H@?gG=&ATSE~pF7X2^w z-a9DDZtEAs049P;l&GR0K?ycFy{II~p$U>fBqyOkGJ=ANfJz3*xyh-?NdyEWXPTx# zGBl0QK$Dr>aLza9yLWEYR87^?+^V7Y>v{IGdxw?wUca@Ltw=seT&2CDhZJM)9+hq6 zDnAHckoGrctRg^nIJH&}Uxk|N@p>)9Ws9}@5~2sX_!y&gU-x4G9G8squZ85vAULMmK*ndW4q&Ayx8mMyK8y`QEzzvJ(RYDLW<~7zW*En`)mL7o+GCjnT1{g#8PFy z@o=7J9KZ=_G9osjRh2*6fHdR;{QKt%=>{BQ;N}%aQo{K*?_|9X*#U>|7e;q)*Nu#^obY^GefKAk(TRwwzhnpJt1sYot@IHG9 zHxpio>#npbzHHQUFYwmhNW&J?Yh5J(Ay1=7lAC=_F($!rwa2T4m2QYrbjd2cmJz9o zNRroRVq$4#kvO=jiHyN^osW-cdBpQL$Cg)}OtdMrw2^=q_iG`*OC(}^Dhnz`jx8i_ zWEdJ{b(E++dlVid(A2+kl)ikVCb>6P zUEaBc(&~~{=Z6S@paW0{6lcHMNFn~l4>8z-4A;{!?B)6HqUDDFR;=!Q3Nsv)_W-{e{?hMg)fo3W-!?<&F98F1?4eVe$ObR8 zV3^PTMBm|%EP3K?9iz~{sD|tTo~LlS`bmmpAMAKfO$~Z%US@Ad8NL|ICJ7tm*>TUl z0!4{-Jv^B;sV?fS0m-IhWI>?H)%Wzp$1QcAIaN?o)4Ne02^T8*Hp?fST*Pi-l2C`O zSHB#%1nN7yv3rXFNBZ=md2Kq0&);&g!l}mX^{&z=i7n)q`1Dt<7}lhRNs8+_$82C3Y5m@KgR+r-ee66Jp7e4-2iFIeXuY=|ytFC@u9-+q`zq&s{h z7=&`KoX_o4rAC#l37Y57zC}e#G)8(PY#!f8k2#nDO#Z5J^0j7R4)=t2X;pMAcB*EEh*Oe^257RMDayrnQ zH*{o&Zj3FvYl85FgQt=qwPBiF+X&4rb53G7UE|&YLt^> z{1O%h9#CN1hQ?gTsGyJ5UMnKqk+p}Py4tuy3WkV$~_e`ZL=zUK)q)jBl1X! zWZxZY_HOfVsTcbTf>5KOs2awQ38@3f}oZ;i)ZHqj*O@lC>Ae|zG3XzS4 znZ74Aax2Ff=IVPJ?C#zG{~2g6d|a~1y#0%nV)4Wn7D{ypKVU<{J1YgjUPi$L(jHdY z9|>ak)dNQy@(SvdeNH~~FB}!a8RZ#z!x85h?MFbax20Dv&odJ4>=v7|E2T0ipg37M z!w2^C8(fry{3%zp)kk$h=e330V~m#V;d_X?(mWs z&UhO;s`@Lgj?-Em`S53X-_O&e!`jPNxwyO94D-5&;#$NFOAl={?V(G^DiT2!0(Rw#?3UX^H^p60U{U`|oBI0g zn8KVAI!#|>)#y)*P!P*>&eQsb7z8{_!3AG6Pl#eFo?N7mIT-I{ztq*r!dXd(SL|Yn zy3pO8nxde0J0sMVDvuLQ;$asp7G!jukt3`z^g!-20vik*!4saot&~$ zu1v3`YK0NF63P_WBkHo)18}ZUSD@bJ^zRAb8} zP5?#hHbA&W0m5TtpkNC=1d7dG<*%yDhnq~m5|4J!wPk36GmyIoJ_NvyD*?8^9giN! zgj)r!sLLsAr@Hv0!2C@kCEXfCa?(Ei&5of?M^eBOVgJ)%OT*%|Jh3a)K>)4mE?$ zBX5Uyzed^t2oUVhep1Q|7NTH^6gZYB+4Y`ZgEk@U+nrX2RM8> zg#NYX1!!iDSV0ht6*B%xLk~Bo3rJ-Vbv`Dzld?Advy|TaD}6ggZa?7k+q8(srP|$n z424I^Qq%g7H9b0Agd=m@d?iCc48by-QY&rI1NcNb`$+Q*Tyai}G!L9}i{Mg*+0Fua zn6hR-y3-(7(_R(gOi4EJa*N6hQ`5?Dm3b*WUX0h|-|C#)X%=1}xs z6aLuIk_BfQb~z|8Av2jl#a1`R)6k!X+XOyAqZ|Xp1-tCato>PsH!7V^KB&W_ zFfvdPtJS@JS^xA?^8gAC{xd;Otaf0v`3Yj!ssdbbdBmT+s4E8+;r!63N5C09(^JMx zrzY#ThH~rTBtBN!RA%)4b*@>Ex`~jK6*o@++!q@df`TRNNv__-XVWt@t}gD1w_@cn zD_0M@&qk5(+BjFOb?Ol%)S;_xYak?XwiJMi3Z)ah>9oM`&ZrrLS@%hYYQ#K}1sE8+ zx(*T#M}CGSC)em7OlXVrsqwpxpHO%fRyz|*t`16?jik_lP)jeo`-9J^LtlXR!+j-Gz7uTc{E^vZ`W!8R6(o#Rkgh!@MVY|3u0OL z2oSp-=iz#m{bcS(@egV_lYuOQTyffUp#wK@6ZzoGp=JT3{E89$l2PIYBJB>@3sq{v z+I%>_dIo%8I1fMN#e+sn#jC<&si(3_a8Ls8+7wS%)Q{QY*XiX~+_1-mx26p5%O!`D zt%~rC&VZndj+hg0#wL%BQkPMAYW0b;)P7zj3dpXXbEcXE+u0IXi>c8puKV2fp|&X! zU`H87Q&c3uMU-|U2zi#Maai@dR`^e`A=Txx z%tV&NyI#Uq?flsw7=`oRl(fPQ&_5E>(T|dcESh z59hD=J}u4H;Kdh;iCK^6x@? z|LLfb#820C^>E?Z$xW|!gbV`Bn-1+pSt~$9O}GQ!e{L8M)QqnF=|ESJ5^tb#Bec}QRwKZtud~{It?$dVwgnerH?>6_TMaJxyco#rXGl_@j?#MQx7ooiy1Ep?dAtcQqzV6`0{HFP&in=8 zF+w07LI{z(b3qvMwBXGcdZO~I*v!vaQEREgzkxrTv!PKR z??R&rjh0i_{3J|vFxLVg4M1y)#p)L2YouT&j=x{Fh+0jdF5^#mB=m5aOIEtT`WvwQx^SxAq@o_PpSz2_Xk=z-C zRRbk>j9gFaJ?}2I!w(&Opgr7>7fq`Gvx`P9qyVA=N_`LcshVX)a(%!yR(^0V2Z)7V zAmLd=_Mqa`VJ^&Tx#l~xx~^yR5;?NH)%R?~V($u3Y!y%icRa7HvyI=Z?;u3*;CMW& zswMrh95x7Hr0k*e;}(v(@H775u(}*{lH!Niki&UAnp{M&bsyB%)#kCyYFIpT%-kZ$ zgKOL-WQMRbB2^Dm_Q?7a5uJCV1{ksJbv~nXmh}%Skfo6EgaNMWsck{}jZ5~qJUgOR zzZ)1@c;H`7@2^U`9!Bwuih!I`3+Asr>&%yd*d^=7JT(f2VfO_o5;R6l8~IYN76y?q zIZZc=JB}D~6-{$Wg@ZVl0-8-8@d?0Bsm>a zy5zl;&@W0ql73JN^gt0Lo_m<(i8j^r7sLvGeBjUCK|-v3#yKJ-J66i+FNyBscap6J z{Hi{3ieP6jIp;^#^znRASGckUq5?RjeOr~amit8Z%#8((4MH!Tkg4lsHmNh$sG>yR z?{faw;M5}GtM9Nw4~-~$@a9wNAp%>o9sjkFZ>!ZKBu z1qCcBqS}f22l1->N0f`?ho3zA}(2UdF^?@G9J zd9Hen?3V;&`w4JkVWS*=`KJrDUwye`G}CCW9j6i4>A<=$7NTXCt_wXVd|`BNxJ1ua zl}_=Xd^xjwR@LJq=czGf<3bhGRS8SW2O#5uKjy*3(4_=t8cG+~!bTc%zv-m-)zx-UfeYD_w zX62nmU7reZd!@{#?5iKyP&h6+N$71Y>ptB#wGOwSjP~Tbyz8cUX(jpQ>7f4IHnMA_VSQTX1lKu!b&$e%LYOBB3x zoH0YTV{~U931DK;csh+20&_$6(Zn2p+0rT};)ja!53PKo7~IbW(Y?scy_rD+k(E?azvQcZR!gy~ zcc>YrzYM`{HOfWzJa)UO36_TMH3)f+nwHqv_c_*nIU-`|Gx%3jfN<3=_LQdW8Zr5n z%s0KVp5Kd=eJ3$6;zC73m_UPJsJ6Yaxf_1cwz4p^-!#?X$4Y#H@rh%YVT+j-vw;q( zh9rtM{Y9A2)XP9``A?l04Q9^Zu(w$`8^OuuzxGOPl;|ismDRI0?~2^^!tSqh@>R&| z_yJw-C>Ff_04)#1?w|T_yBD9O0_*&&9%L+JJN2eVs7R*r;5QISELUGFQFg5yrB1!6 zcTm+Bw%GdIzy6|eeOqW3R?JA`|CQ`4ES*UBe}@R||2r-aLcI9@yK&?H4@Y$~!M)++ z7ma~cgI#|sO=t9OzuCBkB2V1Ivftrl^U?Z(`p&KMWl(J+js$*>D7#?}&C+3QLxF>F zo4nH~{kC%z8$%d}bwQMOvw(z#`0HEMO z9^tDtF7UNn!JLUSn}`WF&L;rN^SiMr!|vmKnyy;P{>Oww#zediY$D+Ha6$M49C7r? z@y7ID?()BfdsPoYqe_Xd5?c71rU>Ru6xy^*xUD@CoVmcbf-Tz;A`UcVKYgp7j>w-V zw?R(qV4pp{bAd6wT!ykLzi!}@^bFxWkqK39XdVLlEHTIR39&HE@eD<&Ma+?c+U3)? zbgDx;P=+x?NPF3p8`HXnjlQu3<$ta=6Q3q!FcH_(*R?l8sxXJQ+568PL>x;T^oP?} zI)LO2L_|&RvJoW{zihaWJGeJ|SYR(CZ#nM8rbDxpt~`2h+C635M5oQ;yfr)IV7h|= zvGA7iG>0bJC0asRR8Sf~&fc;j)xOp>_ zs@*21_boySdm?|qobCcdrF}R>Ex18d0;!+IReeiczigcT-ffAB=3$!3HJquy&2wQg zG={tzfJvY_h6rgYZn z@cfT884T@VNM9D3r{GpZ=#9PX+4E-21QcR6xVev5`1qhP<}yDaw>L=mLs5aU_wwxv zVr$wsQaFizSc_2&P&y?Kq82&(Hx={t2fk`Sz%7nGJc%o)x{I?v{f_tfP_$0M6ash2 zboyKvF>QRfYSUS|iL{|C9pn@Oe_)?-E{x?-=|5liPQNhO$_aHu9vwl7PuH93C83Be z$dX}bvx?mDYb3qbR5JEQij@D9O^7~AR_xg`rm*ZYPwRf4K5IG~KXEL`+=pl&5nif3 zZPxU&Z#{|8=b97m;#m_)MXANPB}M$93@SSH6lFzLmYR9u5=_CGp>bk^!x`#w;%&nKHxQaIdwO(9v z;wG$9#MxLWW6X#JeZM^CIu~|wyna6*;IGRd+rClSwbT&z~7&B5qteV{H>HNI}J*b1SPJhCGilZ$>H!xJ{ zrw!xubRMB*1QF5gOrr+)z=NierYAU(OK1J|akVnqG2WF;Cu-m9I)VVNJ>k#ah-1NI z_Bx-2Fb*HaD>)7hJ-XP1N< zq?Xa81ZEbyc=}3SneTGzT|4hIC0GAhImJiJ7(#gwkk$R+Cz-L`XK^`alR>ORax{mX zz9E`O(|u=lQ&2_y9%s`=I!nLT8Od)Y|N9}I#U^L-Y2p_ECdi6?7ph5>OX|dZY%E18 zc=ouD=acf-oA=OL)4JBJ)B$Aw>LJ98Qqag0M&`tQ*urv=;cQu#gjyfiP2Lr4D#4UW zT@gInnm)%*>SI+hTK?u!d&jI}bf+V0N_n7dI!hY`c;{h9w(04bHvLu0|Ix<73CN?U z+^qP{X+wW$b;83M5@CWiGGRTNS727``%v>p65(hg&kmEbArbx8!z?AE`(tMfn^HJl zna|<*H^=UJrpincq%yVG`wWxbW9H))wrgy_n4*h^iETjie z4Z1Dt*e&1_1wR*gjHaVf7ek2~8v*;jLDA1Ks=NW+^mCK(^^!BmK zLWgv`shq!gRqkk{pU<;K#RPJqc=>I{StDYiCac>`Gf-`S&49IR$ApW71F(dE{9_Er z&;)W(`EXc4ejOhi-I2dW1k3acb1phhWCFQ9{g;yyU`U=r`53`{Ws7Y@s1ParwI1v2 zr0W@%{6W><_9~roM7*Mxo!(GWpJz1B%%LNvKNmIwGS%{2BqJHOT$czeCsNaaFVf*T z0sQE~Ir5^kC;sO7TGTDlK!HzgK@$Lmq?>Ql3Okt);7Zi@>_3~9{zub8a>+N1n1zI* zPKIliiLe(tjd?}P8Rz`jG$hXkad_G3`t*aSQZwNP1VGpydWv44<*$RhPv^okW>d}N z{Vx!)6>j`x1QKZDw5l~6WBW2gC2(VWN5kS4`T;N@0!`*UTm6y}FpH9^rGkvd^`VYPf7C9~uJ z=Wra7d?J2uR48T{%;IL^wyDtmiaq)8VM5{a6>;R>%2m8KQ-QM3_=B+p&(&i#d3Hf` zq#THs&c(@%=y|P8d;8*6VF9i(zS~_VWFu?BCds#bZp|kf`gIHVQfJtG!6?{%-Qgkiz~1tUGOnF3$}s&D;%U&1uHf5`%Vfui2%_EL0q z&uqZ8o=$wWS0?tMdrvi=6vBFtytnJKVYW`7B%TV-8#O)~E+tyKoRzm}?z@^`J}OcN zGUbiu6-h=(Z2a^YAJ#Gc#sr&7-8;tYYfB}Y3;KE1>r`ypRbrfD42z^AtpPBHwTbWN zxv&;;30|+Q{@QlCyiYwd8I<8y5YT7Tw`zThv^FXZI9_9fLhlPiV)iYjeDJN9(ymAO zhR*Aj{w>(mEQX@rCxs+a_tB8!sW(S)({;}xhR5xxt+{%&;!c**N89qfHd+@+mRG9& zd=gHN?knIS=%{46R%RKr}r7F zlBFWpMYNn2fVbs0L+Vad$PyQ2CEC{z(b2}>hA3&?Ukz2jZL}9%I{4VXu)RILx#Zj4 za|La1i5P}C<|bw-tU62^Xfk2~OQwY{ULI8a{75lPwz0Dw6;_no%aD`b^+nn>{l%}i=ewg^rwRaP);HLMmMXj(gl z`AW9i9S_y(Y_ER$|fmOMpXzcn!HF}oisl=M(S2xf^zup}4M4HtYFF~w&-pSzWv ze3P+pxWKuuNPLh%yjG>Mbg;YO0CUT3^+O8<`GANZdc`2!cRoDFGN7>sWb3|Y{YiYc z={w)*xF1>opJkGG7o>bYh~0)%`YT6#3VO11Y=-Ml|MJ8U7gbb4n;RUaj2qGKwbZOc zXJr$~Nfi~Dcct5s8o6|5HoKWy@3u16uG3@k5==$#M(d_4-I^l@&+5NGlw=w|1~b<@ zmZC*!cB&Wg*4i&Gy8K>HP4t3FC9g$GV5Oq~`R~6a3dT1vyMl_|AHm&bbIRR2z%ish zbs1{jY%FJP7B0T|1v8>w)WJaRCJ{QQ-P(y&giLAAdUfUeS$0%Jy|<^gZD&GU+nMbt z$oZC6(zf#45EAWCrYzKxmF)J!G*c}qFAc`6Uy`SJr=SblHBb%z>Ny*#GiUyriOdwl zX!tI~E$FHRcBmHljJfg^R(slI>diMI)vhKumW<{k@3Af>dF{t;*?3PtqI>j_u+^NI zG*cGefBrGtS@V?H(%->LxwI)r$5SED4hcU@FWNXQOJ11uUMe>_~*}3I(c_t{_Kd^G^1IY`Q5B3<#_B0obhI=h*EyBI7 zThJ5v``&4qk;n&5E49pi?c3#rY{X^OAvuawQ~u-jIbJ2o46O4Q99->vJIu@IYbnTc zlQh(gZWz~Wt;k1~0&Xgy9$>>XFbX`{qAAC|`*PItSebhicm8dM-qH zwrr2aD|A0m%0_6(qm}8 z!@5#%L+lQGYAmmRVFap5?xzpJ1bvN-+oR=}8dtkFj}LejBWxyM+{)g$9o%ah?IdKu z^OK2LcwXfm(wHl60;y`yO=^9QBf=o4AhX(p0kh9Fs2n=Qn^fZMHv`)aS zPG_fEQ_24+*&U>RKk}TVE~&lzS#v4#Lu})}n%f#Kv4D6C8m`zrLpl z>9-mlC9}itx%9-voft+`?fXgLes4ZHe*LTYAY1`Gaxs0MKd76+r~A&1GAQ4;)h%R9 zZKnu=djqQ)!XAc)1yKxL;mnN*v5$nuXhCOv$9n3QC6q9Gt7z!rdV&aUj&GIA7%}EO zc-v4YH_6-#m%UsMNB6pUO{z3@LWqctKmyk3)Z`aR{w*q-IYlop6*^@Kbz51jVaa|A zahC#Jp&C%qwR;{bFOcQ>c_?jY5rck7NbVOw3;g}XL@(JdkAft6edprnGZFaKC>@$` zFCO!*&ozNaNOX5)tnJI*T$VjnH-$vchNIc9=dvAb z1JuR1pP&ZJg9NGik;m-Gb$-8h?y)tQ7AZ6q7L9E#F?#*JAw9wAM(3|2wqJZZv6p#0 zr5DW8uF#7nmSTu}X8Z$p=~wo{epdnzwJ63I0` zm8yUf1=qMDg<9eTrn2&8zw12?oEEgY*{NL^px&t!$L{oq}+D(D$%){ zPaWkAUvwK!N9S$;N5km#>$S(RM^mD1jr5d`gImv0)Ao{9zQhTIVQI_~AX|yFk#Whq zdOT^Ulj{k-GSlNZz#gCN<i$}NVQXul=w@)&$C z_M`-Qk9s#tXc3WLLLt)c29R{{FFQZ5dCS?_m*Ol}OsBIWeDEkPd7Zhwr|sm<>LQO4 z@_Q|D1uOJq{C->RQi*LDNJyAu`cn3Af`Q*&3k6!>)n1sG9gAH{i3Ha~tceyS>Hb0A z_TarhK|@2gZ|!iR_S;2ZM<1{!2|tvl?P_RV-x>aHDuxGKsz|Agts5MgUgyJBnPV)W z+w&9&SqW6VeC-RW!}T`0gpJHJK9y0dKt^QN8V9#T*wi=snYVAGM zDI0$Tm2V_CLPxE>?3uIEL-8I7Z6z(w?R5MEo*&Lzv=|O4pR`aIGejVIs`hjd?$3Yg zn3-9{Ml_=SJ*OO5>)&%p77kUWn2QlBUrh#uTime9>!uvzua_mA>A6~6ewXZN;*A7y zENdmOzR?5?214BNUkd4y8N0N9fkZaR1Rk7cSwp9(%NeixgC* zq|cgmtmJIs`?bo25Ffdf1)baMvIIHfHpjD^gVrYVd_$CTObbM;!lbY{hh5x+^M+2m zW1Cc#`c)^=t}+y}yHpoJxXAjqc*#?6HksBYXYXSswwBq$#tZ3F1eJw2ID>X3CsMA~ z{SQ+?+9R&MI?^$p%)&AG45gY6`pg19+p=<>N|X3`U41bI=_yY18g}H1 zUfAfHyw$1b<(O+5d7g2{lY9j*Pi^x5NnDqx0X&UXs(!6=YN*^gI2?>x^K8^S^;^5K z_T|!1^0mh8+88)zM!kGRk@RAm`2G?K3|A4vh?ZG2^i+>_S4uC2ON@3ss=qCte&?QV z`~j9rl3jUgF&pVyYaaj^HSk%Eh1e_ca?+}#b2UaTS%-gmi0;UY>=`xAhi^Y*^&iFEL>D%W>FbcO)&NI5*th&}C#U(%Zv!tC_ zGFYvZdE-jBwG1f%=l=vuk4$LmBlW+ax5E-= zzbmMG>u^))+1{VsyI!R9`di0Keq6`P&6(h}y_&3y>~8GhlfmDcOk}HE?`&{UeCzO_I3Lv$dYJ`cnv9 zlPHCKmhQY>wmz|KqaXj#{c~u>SFiQBq3A~rQ`hjz?$nf@KZ)rCI?9zs#8FI+B2eW- za+SSBjvh+0gSRl*=8b;)5~lyOP9+^1j|MeN^V!UOn2wh+qHbvT_AEKBqH;$6QCC&? zt%kkXq+SOU(g!aVs!A|E94-pyC%9IgXSA^!Ti(_a7gA=%4w|Bi?nd_Wx`uaGI=|2w zC#$_@aim>|`#o0grHJ&k8^5{c9fCZkzUMx8Z=<`?VKH2+KS$JF#~5azAOHBmXr12i zdS~ z4Amhm&}q98S$lVRU}?MXu7^gO&+ot8EO>9X)hTQ(+S;37JHS|*Oi>xN)YH0^*lNU9 zA{)cjJh{Ce3a7tq0U;GoPpuElnCXW;s5Onr$VacZCEarQUS#ZjyeHRupVoLNT9#Fy zLG%E`g)R?f%Oaw{;(Ollo4g{jUcES~pxZtvI3SWVdUq8`C0{&9QNPzV!jCsRfCCB~ z&P%pFD8MbMlIR#5*UF0;AM9YQkPa{W(~*m#mEu_&%cMJ9CB}ZJFNn1dMqVz17BgW1 zRvP~4I@q;_gT%IMz&L6^NscJjfK>kO9~zau3qShS(@r*O%;3@5=k7pjA$Fy@3VF{oPx~hV3WE zRijl4^t@eL){0(nlFLhlj)MlYZu`X77RsZSsfoSxpn!r7Dk$|z?;*2&#roM@QN1f! z#KJW`zCK;%^vOLYjf@B7))B1j8wXsE!ZQ@pi4ydE28oDl*~nquA$HldH7oR%1+kDJO|O`0 zEx}-!Cu0JBuh^0*KTszKMN5}^;8 z9_GyGbE6FvodF;m35!Ci9!h_Zw=luxqwjw1xV`)(pd&YMUtNL{KRyYhQU)z{&0|OG zqa%TpRZdx1=mMkX`B}+j^+kDOJ*Ppo{(}?G6&pu6)i;}vFX?10_Fo}-KbaC5NN13G zgM;Gk+P=z8%UNZo`?BB&d)#ZcGL|I1=ZoBrPk7q3#rgio-REY%p;U&RnGuQJ~AdbuIoopex_eJtl17;`o8;luFvu{DlGOZ zNYT`dOUM>y+gKyrX(_3VnEkZQu2JcTrW+5UqHvn44Od7y{3wBc*Wl)VF#^U=*?Mb#ONbAVB~oGltNbaqn1c>j&j6LUtISx0!;%eqE(@TG4ZgsH4K{Q5^DNmgSyTK4| zGHA&D(Mw4NWSvEnE~r+nuX*5WBg9Wt%Jox~Lhke{j49!6D4qJES_*E3em1 zW&g+;L=lpoAoJCG^%W2-)uMOe(|;Z(1W;i;orgBcMv--UtEj0@GC*Yu5KZ>Yy|F)^Yszzpv7b>McSVW4tz5Zk6=I+iQD9{FZHeuKsBP#E5 zf{%X|PY|Q<-2#U>Jw|1XC5J)njJNvt&^2*Vo@6akL6YgVy$Wgnd@<5@9u1lGefUJR z7R57WrE<-^o>O9fsSa^Fnqrs%fVaD1m_A(;u5tDaiEJ145DPqV!d&&OJ}`f}6_DYS8;U>3N4y9gPdaQA51t%MotanDeFkyR+`~Q-+TS`m7TTf86^gU-$Us%UVH+ z4foLw_-F4`+2g9BFI8Lcpjwr0?r0CLQnG|~MpBDD%K5-c?s7Z#Kdy)!xcrbg&nTO? zSowL=ifiJQ-DXKJlCdgy_afc1w@*Te_>fLgF?NdAoML^PMpy4GOKQs=yUtD-wfQ1H z5tbbO>~zUB{GU41Z0wt$CZIE#k1A6s7xT(kf|Fwz1ztaUsV~FrX(sJ7_v`o3j(+i^I`(s>J+D9T~udf42y!2HuWhD!Ryy5ft~ z)_GRToPx>M$S*+Xe;iJ_^uAkP&r;SN34y#bZuv5NYv_=8f92iO!;zBZM2q^s! zLsc(PvWJCmw-m)ks=_HJPu=rNK7pc5eBcz+oU17pu-+gx*e`_sPCioTBe>gra^UxM zTfHE7v6q~N$M;?=?{V)}i~2vnB}>}Xp{YnC8$F|={43k`_zl!)Yw7JY(ou@c$iu?$ zr3j!i|0zMM3ydy{;@3%dlx~}!949uHT7(1=Q}mK285~*_FVE8k^h%xNR2ZDBJ6ssv zpNqi%s57qK%>7kr)c-Ebi^uAT$%H0a_5i09H05o+8bv9Q3yW#@ z*y{HvfCZ7)^Oa7zjc3NZ!@YU%V6%+@8IUL@6dVyb1zX&ee=%C+Y#uO9aeTk(piu0E zU#-vYoayE4c=i9+1TIF@}P=9=#T@|S4Z zsIOqYF%xF0{{EZQg3*8ieW797{d3KGySn@Ahxp8d69$`snym7b}wY z6JwEbI~?3&)zwIf2aOv0-Pdh6?D$K~9+CAs>;N3jsF)LE={V3km~nc$$tt$klfJ+i zzUpN|Z1p$(PSyBQTnGNp`uy~z6P-t;-rMrVuhl22bOx1}RpPuYU&bj~_$@S5{QU-N z5VZH?BZ#dmoW(9P4_GDtH-V3uQhP$LXy>G3AB8{O>8&I#vNK|+Sb`F>1SBDlaY_T_IH_=DN`unCW z4$NZ<3}4ZFJ>H(&RhBELujoD8S~{zB#H8T<$S=W+r49P47r#q*-7RhKKHARJK0a`r zjQIUsMB+CCzLKY}!CimBe>u*yUME9)13L@-cL zVccbzK5rFV-Yj`KkGjlD7!mefoiM47Pm~u;31{Xmoabgo`-{Yma4BCQUEJZOQ)EjT zJIzHTqJhedvqC9xOhx);Z6JJmdp%rIlu@$P59s6RiZz}}SdezU?#*#o=t0J)Or#2O zuo3Nh5c=gP;|1%5-?sM`dy`4UzR*{sW)IqqD#Ili|}R=zA>xBH`$ zw;Kp+x#()|^NbcuMOW@wZM|KHc8X!R%DA+BuB4q_hbwf`v zeVx~MwWpxzCK=^?Om(pjE#|W(fAD&LtDiSiR%ntW|)LO5%)UcHjC0_jou3mi$ z2_|&)>afn}`^6{CpH+|?uj>dbH32U5q91}|W<@?Fe)w8_scH1GBK68=_PyyqV^J>8 z_9>LmVD-RvN-Tej$G3d;Wo#+uj)Lk8$|BL~-jAcf^()L?Td5`+f8-{X6d5L_s$G(w zjks8lIYq+i;*}7C9%1QW@VKXU3TKnrl5}Ce?O9p74C$#&2HJh*)BkJS940T>nujNO zuGe=+jk(~$Koer0*hA5bg29kIU|M=^Qlg)YQ7>|Ox zg-?Zi_J>CJS@yqY)jDRmWOj(5EDA@ByhM}^+8FxDZKw~ZMrT}>p~RU&s`!Y=TLe<3 zj^yha5`p=#hp`bu_p8FZ=nwGUjBv$qb%{cbuU_iu&9f_y?`e(fMKFm8zVF&ucT6sO zlI$u#naJkDESg(~Jh&zF3`zCCyj<>G`wu zi%G_nltLaa86TV7CM@36dd40j>3qqpD;v}K2drIXNn8q?r_5E_lPu0V8#8K3b-9#X zQb78a^0WA@eH9JniG#TEP>98uLNGpEIa`Qovyh!GlOD62aXK&4O`#^|)eojjF?-Kf z46470=Ag#1;~r-RFAtqdPW)^ATPB40%3###atSnk8du5Yi5deLP!(84omI`(0VV-; zLk~+23?Q!hhK4_ndOZv8Fup$A)$4?90|kvU28SJ!;y838C}Iq})&I8AHhL!sFH@F& ziwvIVRXwRpPB8MlQ#xtU5c!&L%ya7$KUGAHbdl%6L|mAyU{zZ$b)KG}AVEgNyw0xm8KZFyRSJ{Y?}M zC_Bp~|A;}zK?%sSQ5JB;H^52j%!KELbYzU^G~Dwc(JIpRIo{(*+zbREE;XE_e&8|JX zkunR_;A1Xq(`4wt1+>QR{zZ3!Rm!KqOi$9D=Zl$QQx`*9nLen z;m|yL8L}?(pVNG-H4>>p`6=Ff_+o~GsLl&!BV2pli?>2+UBEUEjZ`MSuX3{PZ7sF+ z;(Kjk=^0DjCU?q7PgD6UN)aS#1CE2FV)qx8^F--Zz9&GOL2Q_?vw{BYXNf67=t*V_ zvX&Jy9oLxGlHHS9+mHb(w9^yKfvrb)Y_}hd6pk8SLD+k7yuqU?fB4Jpmiu91d95-a z3ywmlH$E@*)3&>JB1Z@&i2MtV**EhI)|xMv6xE%i+q3FMYW5QvuBP?+tVA&$`2hh= z+wpT@*Q`-=W+jgV=3$RQ_DT6pIIoKjGWFnT3n0C zg&-^$-V!Bz4wf2dh7Z{(XC>`hBXLmML~+7NHaiCX-0*BGywx5AU)I& z6RKr{pn!mYQl&@~fzZoc=-$usyzf2ZoKNS&z2oIe7z~ns z)>?DT_WR8_Ls~D)J>H5*_YZRg+P--*CVL5Ch1~0z$(w?B97{%n)~{7LAD!5@b7_|x zSzXz4X`g~giQymIt*xfsQ_B&tCvLb)$E$4K=Bi0ol1m!(0ck`k#qSi(eeK1LkRLjA z9Xr(aha7UHK-9x%CqX7|$yj@?`v`rg#_Q?j(vbZW>xnX(;~eR3dQbMt-})A`SxfBQ4^DoPA8yAUYxh3K~##!j0?pffYP>&8^_L z_kQy`WubfXU!FdwetI=%Zf~0IHMIn+&vlC-%Y!>1z5`S6?d*OLCnH2t(&M$EAEoJc zs;Si@w~Yzf7co7NOwX z+=ulprfu;IiYTLXb?ZjIkNKa_{eQ9-|C7Ms`oFXVsk`RJT6qK#Rmd`hk6MD)0`4Ti zDx<~_s(~8Oi>cL*o4MMvy;)XCMb~(B(z&@?ue8&_bmnh$f`0oqRtGnu0pXe)x)uOt zK8obZgu!NST47Znuj=~QfVw*S;CpRIb-0k`Qx2QP9fQQ#o#XM}7||11P21^MiA$QG zNi(vS85h@aiBEj5`={>Op@6?~GOYYSsz&Jk)+EIfQ1`JrR@ev-pI7EqR25*||MmVU zO?6gpb)M186?Du5u~COGhA~qI1l76ypAK#dg-kL?@(Lc~I+}H%0!}1Xz%2F(myu)G z_7A?UzBhEB))Gt{a3)tq5jL0VRL(XWS1CyeD;=@xn%`_zq)+EU>RHa@16uat zJ7SXu(mK;$JLjQoJ8*75akPoFDML=#7Qj zy-^UZ^@a@X1vot{xU29Pk}zPylBA%clFeFXDuTyM8O&4ug4<6k`*UgfKKuEWtur#F zGD|gmW?2`y699awOI6eDan7M>+uDCC5&KX$u}$Zu_(y7LCtE|DZ?uZWXMO5&6XI>xS12lnr+5xC|rME><%adX_46qdUh zV0NpAIgXoIq&s>6v!b!i=p-91UogDWAG-ZCnLHf)V66FFb;SAGPpzAxcY^5Y#47=1 zXhDb)KuV8GU}qb?bsL%O@to>AHrbyqbdTiebZPDh_tV>wRSFSRci?sLUpX}72?_7X z{2y~Ru@z{VzgC>w&X*^oB%OATy*{J*wC7HejQ7~ zoAMb0Dz8CTp%zt?fX;zte6I`HC;UoKFx{ONLf1%KZT_M-z1uL?NvWTBLB|Q>3n9xc%S{dza5bkV{$UJ9F#avSX z9ghCM?{~@k<7aK6ay5B_Lns-%q#dtt7*vZq3wN@pMo2?0Ui z#U+sFJ4=`GNL-wupOEY8=QQa*LKgdNZj@9s^tp6nulj9xJ0PP7fI=h_(Q1h1zx$}_ zB#c&tFfr<0HS<)~Rad54q0GeZW&T+M8a4KKPO^58*tJeI8EgG2KfG4dHD6x4-&nW1#U`VRE)=`7Jd^x2BdFRayMojOUy+J2_*Vi4~w*Cs-?@Z8p zQRGiBs~wqk#3So-7v&np3oc+@cB%6aJOwc%2AM`>Tv&X|7BRT_^kxPIljFDWlcOG4_z1G^Itl&9F#6uYKSLAedm(CO+1OhRx|}9KUS|>yh$@=z zPjVO($;}P*ks>8#rX)H^%M-!`RRpf0M6Y~GX6qnGk=w^h2;8mj`g8$6XI)Z5LxDK=24NK^}%F!XX$CT7|88~D)G(@ z(CqX1sv?@z^`LR2q55f{;DNw(yPlK;xhx@j%8%=-Wt^hnA44@3Ra)ekst2Gfa)|e_ z`pQ&X^nyBe9M$$AG@Nm{8+(6lZO($*wO*F?oFI~U{U?~3STa>dy=Z!tek5uqy3A>& zVosaBL23%o71Gow5vlYvn_Pc)*;0c&fuuTEjy6VxI~`B9=y(LiZ%w<)jc)1)OmYo{PfP7`rMiMtNqsmGXJpX%=(%A z?FlLOEFPaN6K`T&mqsg|OwZ5Kv+iW!ZjoBE^HlMTCw`VM_;fci)F~x;bE?4Adhv@g|mv=I8pl=QHwkrR*ylH0LQJjTIl>O3u+C9Sv;h`1lL~gfu zemlzg=Joxg(6_P~ z*wNM#zXZIidlkD91>7+P#?J}a(Or~IzJk=>Lu zllR*t??#b{>=- znZ+mY>^u33Nql+AYWG7#87}=Yl4$DWV7uOK@ZGAOOr+|uVTT&G_45Z2~n$%wmVl%@PN(MqR znrWbkegS1yCz41JJGn zErLiq4d-3tgGE{^liaKwltD{MN%;*K58I_%0nq$(N}R?_VXTu)$aoBPZe{@+jwggD zO$KSmrqm!&m^E1|6F-WB*qE~`;ic_OJa&ap(ZLo*#p0V@D>q~ z&||DHGds_XNmcNhzVBcaB5YmEJf`C-5|VRh5qE)fAWO)+z-+b3jy-@$8mu=(9L=?f zYi@NwZcfii6hsT|6wrb;%tB(4*~ZM;95!fnUn>1Zmdq$Zk38FQ3OPT!|7ADMeoD42 zAXV^^C~su$o6YPW>#Z91Q7g<#>71UeC>U^=@>sd%QuhqqvsqetPAX zuCymX(P9OyNX)=QG1(yMOq8FZPv5zC>+Hh3yNBRdC+cNHUx{OH=L-Fx%G&Ftz9?N_ zL^Z3dDi?ex)XEtcIlMISf=Qd)y964XH%Pd_#Fv_5Ou3ao+vp#)lR*R;4~l&1aJIhS z;7hV&*K=>3IqQ3N>c35ZP}7%}0gr;@*merVr2clj7i#Zpy0CS*$5>jh_9={p!KBq6XUgW)REWf-Y7AsSUO)K}tU>Oh{?&L{b9 z-3bOv>Mo}^JW#nK6Vg8IHfQoe<9g4w?d}pd{PzQeC46!#{duGo?M6w}=`Xgi544hA z85sY-9*HtypreFwg@aL4alNsLxD7>{2c&7JD?U$^RUeZ>t8(rmE>?za$vxYWc-2rSKS|3k+8)HtD1r#5^ZN4mOGwz3nvJpJYzCC9I%gklDIU4`|LBNg+d?yTL7~&L+LMDunqQEp9$wz#$?(_xPRuAyfWr(4f}5U|iQPlbAp{99 zMjvi!bSnO^ZdVJ#IekiefBB_Ef}ZHy*x=OMSp&ep?BNX|y1%d$I6}D&b6tB(m##ef zkm_!3(e60)$T(w%gtnN@)$=z?=GIO&JGFxrb z;hnv*#LArU_XX`Ux%H2HyIH>6Q(#!;sb*ZG#ISHRj_M6>zyT0sry(I!0#Rt&kwRTb z(Ae8U4rhw$9>?`L`Q5n7U&pWLxuYo&D}3+AkDBV5Ii_Y$jeDV|_U+1L%yPv|>bk3S zrQED^(vv7x%es;cRNLUU1N&$vjGF6T0Qa=a>D{v0eQW~PN5hu5j<+WpLggRC{{`FQL|xqLa&9s*dyjQJv&Xx$Z+7gzD=FJs{;D(2<2)dJ z(~>g*IdwUF&*2%Z@g$`X<+)8idt%oINg9igHOsB^ArxKm=^yp%@h_?sjMQhk{NS0o@ zT6Eo2mv7e`5^@qKMAQa4{zL;&B`=4jVV0ffQ&Q|Rx7@9AA8TEA7^2a?``{{?KfxWt zpURR>3;%QF#;ev0jkvNVq}b}C8wL$mp7$HrAZ4cpyyzNV-j-jHnf)s8VHVU{sZ-Iw z2zP}8BOHt9Goxcn2Ywr2>>RXBZZh0=uM$2KO*xmZvUtZ|h@QWkRy^AB-<5Cs8?L~_ z{e9-U%IYl3@MbFQOkN*q!&&xa`l{rLeoS0?WY8bWmISXwBu2fnJ}KaU)9sid6-m;z z1yG3o)zcBMtA-6B4YMOio~v_xPt(xaA^&x~3na#vo(sg#exs!Kon_6(z&pH6*-J2Jx9Hg(w;=rGM67?dDVmT3G*w@dv50;F6FynKfl*#Cu zBks50R!%3y)qV`DM7&eFTG0OienATdXU9X(18ZyrDcGvzTsbhbfLolQ@296zvzGpp ze;WGG&w4a947PXk5!ohBqns3~i#}CguGpC^qG#vE=~6bNXCGh{L4jc5y_qJ{4dg7n%}fCna;YeH8Pp*8X}-fyy!2w zL!p2_4q;+C;QhluW{{;&h5yH)*V^l1J2=_fgD#}S9O?H65HsZ|mIHBL19ZsjIIM1R zniSA3qO5Cxt33Ol)4`W31mZ&J?fSDb(xfmn6p5H%cmzPDG7t&&A2vbza7*U>OQHPA zeYdh{SGqbdl#{XT9lj6=<3SYxce+z9dYcZOicC=vpuno`q=!T`tW)=4Az`QXx#@ z-l63X5dHjluiVZ0q!ulQ`jUIr9u&+tEvNcrv;@6hFIw7h%@7-V@G5CZkmT(-W+t;77A^8-S!_^C+~Z9!QU=ldl5ji#w*s%d?7qZ)JP;o zQGN94BlO#;U3VZn=HA{zpX)s%Y(Hz3o{3UJJhp-TxMrp33b^5zb?xT+`)yj|gCwp* zp7BhH%Or(f6h+{sDn@z*8{D0iaGvieEP=SRO8jNN_e0c%3ehUb7M*H8)#BjEq>;k zPY!dPz^(A#c7hm22>!hjI&a7J=-AvrT;Ozb@gU5L?G2Wm7c>uz8R#(8I({;_p?~#Z zlx1nI<558cs3Wo8uch)1`wBJL$VA%KZ_ZjbeU`GbmAGl=LRns9*|`9Tq8W}sef8&~BPvl$ek4ggAH|M8DMeuCx}+>s)PP)C4z zoBn#n`t$K6}oax*UalSgvx$2bn zk818}kdC||=(}XGB!7{#oE~?UedZMUvj4d)!`QuN3u=X%-oV|MkaQ;21Rm6cbjsilXN(u?pcx!6N2oN4frl(F&&?CtAHW1*yY!Iviv+gqy0ZQBH6MWdWwNxl_i@}U zCnH`;p&az*o*#n;o9zP!B;;HvtKd0#5A?!Oc|PZ|zjAr(_cOkK;Nk0I1&N%H3Qe2% z;_Ec7vbl`7G*&`CYj>%PHJrlPed#U0_*wrPMqU!1<+Ci^kYVt-UC3@cNT`ILT&UoS z75S_csb;>h#8lbrHSwzn;0c#Gc^~cCf!lGi8QfIPnNPYx&^;U$v$p4fy?$z03T-=( zQT%R~JBdo=xuI|6oJ`JXL44$>IRr!_vFY-Ee>gnh`JlP0`X%?VW|tcU|0lG|cf)zA zuXw0=#Xi1#!pC;DNFAYc7{;KNyW4!Ud_Z4WtSEPXwiPLy^MN=aW-h&~bjGzA*IoAQ zFMIHL>1Q~co=2W0QgH#oEmxLvMZ&kTWuA+v`};Ef3R6B_)s{CjK-%TlS0s9`;&DFh z&{~=0Dy2QyDTGC@dli%xo`#v46H8U%DIbfa!d9Yrk>Zb#rKdga*24I(O_>*Gu68o@ zTX49~(~>b|vv=oB0Nc1=j`Is3tI#u9T$@;8{sWG!W5<*4niJkWuHy$h`Ru1t^ipmT zX68T%iUG+1s;|uF{hUn(W%u5~Fu3(~u%EVqEpnR+rDXMJo1b8=ZfVKSsnS7>Xc5uu zjY2eZ22fZHE@4{Z0a>|i2ZHBK4fmtw@y9+d7PLn7M^z^be9=zj6Zc(dhP`Ol-esz2 zTPG{K%<`g1*izZh3j2z4oA$qUmxb z)2nl?{}RMoqvSPJO#8g>SKleTB8T-MA@uLd5X?T88R28umiP_yQ!_FZYEW<;R{Qbo z*+Ecm`?W0JvZi5mexrzlR8@UzL@-Mz|7mSYmi-y8s7Thj(nK&P!}Pvaf1DeaS_F>d zeRSmd&pJVRNp9=Q%0$%h=@}bYY-d`%@6qIp``p}6iP-kG^^2;^k@dyFwt2dGXl`>x zkacv16TPIE`qM|+(Jw-m7E0_u=*>?v<7~-kPbV>l^r4(T!Ed(+!L(GeUw%>EV-veP8bhB}*+S!c_5_Iw z-6Dhrb(WDGj+U2nvWD@qEiHvP9D{I6PA^%z3XBP%E{wG$2^youi5Nd~0ppKNh?js{fc5_}_;K|XWkQS)xVnmg88vd-6d zh4+ETr)^yTJP+yLRgoCqbEO}Xa_g?H%|F1JKiq9{yrY>vcPm7I9rh^g2ogSc$6ltd zzqaS+_c|eZq?yC3Q@4|FV)@m>ojgllPnf-MtTP4bC}MYNQ@otfxGzdVOc1^3H?(qXCKS zB(fCb$+vV_kHJe5`p#udAz&xP_HE6ov-0UixK=;zF2>w>3&6xvtjxhzVolft@)^jn zBbdD5akQjQMBLxfUBCx{oW+15g&%{#8xHs40vAG?Emar&J&@t ztS%7HmY|ii+sAK1fi*Ot$%fT)S=&xv96?Q&gQ+?HN%@eYr=Sk{28mTm!!Hd@(C_<4 zUFVOzp!#~xY1JJtGFX)WDy(IowBV1^uA?7-4c25t$$v`DupMi#q^>uZ45*FPni^30OIG_m#!c^V5A|CMS#%oU!b` zHPlD*{9Cbq8WGTMJs_7KX}n3e>Mjf}#0%xZ&^%sDxqdITx6lS5UitI~pE*u6NAu|E zj&;p#bmp#5^XeUu+D}ea9vDYuXF^%>S;Tl6hwn&?4WuR0DNc#PNjq#cHjs+D)zkt7 zE`T(Uadv|PvEP9hyp|pn{4qNKzBF6 zu7str)AE9*@Dd1!3954Z9y|J7PWxyM4;K%S@#SZDF;?<%=lpVp*M|FS(g0 z?(AP9L-bkDq~W2LKt*w5z`E5OaM{8a@lIkkj~;51_NV_o1d4r$F(`A)TWPx(h?gHW zlhRqY9n9X3#>v_Q#F;(0TS|E+6BxK^y5PLj^%Xr57iWWIiKp5s4}Y(+qnMBZE^e+K z&6l(}V{p9tG6b7?SV7o%xG`Wn@j=P@jlhgWgJSg2FCW>vcJ}tm1S||5xFz~&I9)U) zZzVU>&8*J(MIOK6`w!6bcCDg2ko-W34rF-GqEm75`*0NEfK_Z(a864g6vcc0ksb{@AfEs5#HrtMz+1(h}6TWjCW z_N_%jUK>N|?8kfRvNK+wVzq;1@ILy+iSuvT(kOn4AFg!u6!KldXvd#~3mHrozR}f~ za*ydc>B+g}pBlWin?f@i7e0-pyS~%io^n_dr{1W=YqPnr?z9?1-DZpXwSU?Kx15Y| z6hyUYGMPopi~Lh&FX(dY&jnYzt}h(dO>tFhe`mNWe+YNrr2SyQBM4f#XHatVYe6(h|k(@ z5mxGvW@<=|DgS6i=-{C!fZC>r{#1H)Rh~eu? zEEipMG@ne)NJu;~-VhV;Gt2b>gx7BV4Z~4>s-dh0n`TU4jKWyjCjZH0KEQV_OJI6KKpvY`LbL6z=99+>j3{5I=1x~u)N7e5U}5{%iB zi}Ib&81!V=09oEn{rV+&EfM>9v7{EF9lyUYh-z~-C+G4Gp8)IvBsues5pZM)uZvdd z>rGG4qMWU}j5IoMUt$2Nq~TH4G}THrOKu;Wj}2Zn?Ps5bnhWdXDRpfDiDGon@0r>Ek}ZwEK5VqiO6>=Cn=l**JM~`z(>56pkf=axQp|Vmfvd`L z61@_qUJe+`vsrt}Nd4V*N91Cl!H-DBkTg$$^2o$xeD>u>Os`HaazYR9U}ceoDgc8k zLhJ#3M8wOM&{|3APd|_aCoOzE0lb4yAIa2Lv78Tf+YTgox0qny16-O0t00z&H8kOQ zhKb3@7x{FD#dS0CkJD(X-vX87&t^CeJ@6gJlmBXLDbzG+qE6 zR{?+&u;@+2G=Dc((TdR z&geIDP+!%=;%PGE{IDd#GR6i(2gM*ds7Ki@-LbXubG^ZPth|XweOU`yIhV1AR?zv} z@DU@;_$w09nF;6P$Xr{UC{W>!{SeNeHh?<8dN)C|Z0)2sBDTDe7fo$hfk$Tb&dT9^ zx^xp|zV!IUQYYXQiM~BGQJNqp0N10zVz%xO4YZy)hK?EGanUbUSunaO6P~<ahfj!r^yEhYU$_U7wCyq&keNnwsSCML}+M5-t99E_F;-`Xc19G=+D|?A2 z|Bv^sphr*DkB3y?-25#8wR(TZ8)L8{u@0YdGWe27{Vsz)n{LmXA<;{+RMUsvE{qr% zr!nTgW<*A&=hfazv}lp@nW)YooavsPRlxFf!&qX7854xDqSjJ6LO)gpt{TAZVM;e< zuA6G@bJ5qu1R<)Z`6>&P&7x)1;c0j05B?y0i~}tpwYHd>QY^zVIHU z5nCoBAE+T=FY`nrzKk86{4dd8SA;!5*9cq(J##aU1HR>`PeeqqE((!%Pwj4vHSns! z+Z?Q;M$-qs)V@ua0i6mwtgsNR=Z~mu(4cz*8pUDI#>jDAl(19A_fnAsobZ{je-;RG zJ<5N{vC-n6bk!N>Vaq0U8$H&dh*WHB_to1#wvPceh9u4L%0!k`#3{e!+YWqgAU~nj z*=4-B{O0h)D3>O3c`VKb9!wRglG%KIsyt(P-{fot7`g;Ca(~o@y68^G08seH;^uXk zyj)gEsI4})kD2~99?+#>CnjP+HjKzBg*IaO$!8X3E?gUxM5Ic6d;oVA9Nk>-y z)PMyJ^-M+7eK&w0hCSi)HZ^<+gGFc9FDpy$6Hy8;~sJ$O;3kE-5K z?4IgorRG{vlmsa=B&qm1OOU1*I-xZgB-OIPI>wD*RIu=atP4!^T0jpu(ppopLopvx;nZ2Hi;(zaN!2nLep zdC#>Jo>OOsE6wAgmRkUGxq^e)IR$2dNXz7BlFa(ue9qDw#qA>Mp?y%hEq18O?48xy z(kqj$8DN&$zppx*yVw}8B98uf1u#H27WB@{5tICjo4yF`JFZ48?*3)s zv|Q{;lG$|RK+w9R!nROHJ0}21sdQ6#`+ubpv?9LOg_UkCUf&wzta1lqL~>d!4EL73 z%k1Eg5~eFbJsbCTCqW|Z|Z zG&(&}EPOx~2~~&g*SH@o0wG$&4!w`?pvcdT>BbWwmY!8Fg(KW39@4Jg`uo@eJ~U*w zjBEc~crmyIjk6;wQ_jA;-XQ+X{r>KfulEwB<%09V^^lK68SUsv_fyvTGt03~1nEnz-}_kU+M`AP3lW$s7ibnq zo=V<`Q>^H;Cf~4tEjy@W(QNKA&|j33tV_`{ZeP+UAi|ziz@sQu_r=vUcS@f?z0RUAFlml)E*bw1gIv_UH*CLM1T0 zGybmB>Z>3J0VQlK@uMOUKI2R5*SJU3C?<$CMLJ}2zO=~PG%pIM zW^wjo5)=$(w)P;zt&k_=-o*T0dsUMz6!F#G@E0=O(g!crjjw5WW(M&vWb!&vHMzG4 zs}9O=7FYfEeJ28{WqHD?Ha9>~;#@&eXK;W)oz_uS=Sb+I7v#)o1g`JC(K^#>!0YO# zGr;x!M_dpxc=j#7LN;QjA|RS=GzKrvMk~)ehfe&KnN{>&q~+delHL84`7Gm%W+N%~ zKZ;?T4ft)d7UM1Qnw@3(VBj9y#2@tHksVgG78M=e{-?l}rEs!cl4*42zkGHH@I6c> z(BEA}oQkrlfpHov>SPDdv$lDIEPx@DXQGg-IMZom?5Tghpn~}h+@@FMQzg_AnRo-9 z^YU88{ob@o@GE=IgmyaE=E4aW2cU_yXz*~2;)9E2jS>h+QheNE>Qrr=xwPjReqhUf zF4R@QJe)4P7)s|=hRcI;>$bfsI}|{k5gXQRhoBIWiHahwP!*UH{ka1p2q41GP~rDVm&seNiGQrr4rkM5ky{Y7l^Inm8T zLF2qy3=Dqh;)5<-;s5C3n5Un{FwPZ20fx(6uyd6r7Sn=_A|Ynd*B>J5TxlP5txfCr zPks1PwR2r#aj)XHyYr{}Knfu3S2R;U0*~x#O`4Sw%SHeOZ2Ayk z8i$>j2uuLbDFhmO;voo{EjH|i$(3GD9$hY z^6OD}jP58b#z4co?_+W?zwUR(#c9TnY?@mNyua&`bY1dLFDvccJZxKN3hl;#ZNYKg?fK<) zPNtmHAm@7S*M6){~LCzJP6!c!_7r zr?TA4XwEO5nk*1k_`7BM0?f`=I)8&ac6=X?hg}v@Mdw~9v!=rLx_n{qQmd98p5ym# z1u$}b;Y2NqMn%W7IS}U@e@xa?i2>5t%;;+Bc~O{2}ydIcRqnHfyp>HLXymt}{c`0KK(CafiOW z7MlYOW9QK<&DpzLt?T@7FQnzh+HyGh9PkxVDU}yOolP~-WaT~f$HfF!ymLvp()z~a zUc3Yoy%8|J?K%G)IOvVH-4a(>w{}tVQ!+RIw4AEUpU3qPEQSw~$<%k@WPV zH_Y1TtxBZ;`3tL$kvQD@fTt+j^osGDj?4uE18jj^k8MAeX!-g1%{~cy53`^tGZ8vr zhwFD0zm^&rz1(aO&ZQad0D#@qE+mXR6iFQ`RFOowZ)F%2ws?2hH&Ptd$2(G;+)J&d zxGp5D%jm9tfPT?3dIL4scU)rkoaFQxpGmsqE3esj^(ao#4AvPCtMHBTgG#sc(gYfA z3r+VAhL`VgKi>cfT^=r8o{ADrZ7d1B0~qX=ew-`~3z$X{1DCzEmBR2Wd2m}QrUwuC zScpulP0XzDToJrUZrs!=_>gfDvi+V_+`8I~h{Mt9xj#ZgM@flt6Q?s(TBmD3I2= zl$D&@XH#KEdUo0s2Bg9styeBv zCGb&OKkvb)l3&00*KY09*3bWU{z;~5euDB0bXYpLZa3au@8+RNPKxnWf$cxAMH9Vb zv9|Or89Qe(+Pn0<8+4mA3Sl=Q$V?-$pt;Bvj?BN9V_bN)NjDV5oC9c^5C?TQA=Kid znP#3DX;4xFlJr2ec&u4dlWftgmpY0aXwJr#N_np&MHOGP-}zdmU|cwXtK$ zdjlXs|9)_ZS;fB}d>b^TJQc5kk@Nr+bRhoI@2||Fwe1@{@Qg836DSl?fs$VezHg*X zm=K4{dAjh|uv_0tCdoNjmuw3jjeXw?uQ2Z?6o=@p-)8FV-hgS=r?w4~YyW$#Yh)$^90WwjgKIQKPLAy_>P93vS3`mFaQyBIIAN zvwL1^6d)y5VB;{#HO!?II01bjII+&G+hKXKGjscDY$f{3i<_|$ThEwht>=m2)}hy> zx1UDdov*#xBR*}`&1U}P7FhiodH#XKOcjUAFmy(UVmK}NaD;D21&0dyyINL*@cvM1 z&1Jws6taUh#Fb)cSGG=W-Xt((96M=H8Qmti{q*Q~H(18}3i_$l?`61uH0rO!|2`jM zg4{nRBNkx}`?X20h@k}9|K-oHcUec%{+g-koz9nx|6Sb}Fp%tCDgr;YI728qyG2r0 zc+w8A>CevQVZX~TJtX!CdP%HJCJKA@$v&W@YgG?_eOVc>(Kq>heJipu9teVM+EAP3 zF3tpD`VZ%r6pQs#`FMTwTAY5srsh@yt@Dh|4UzSP`l$A=Y4<(9!+=V;u4MJ|@X1Ns zn;nU)&L&hGnd~bT?}4;TRb0bZ0j2iB_Jw^P}q%*6%^>t1k|~gt{fqCeMpW5knas)-S|20nel; zw;D`Qxg0JN@j*bW@QyIjsHDsioa>2XxBT%Sk*DCh>`=r^@Auq4@?=BVdAk4GK_jzM zaP#P#W$T{N#b2=Y{n@dpFHc%~Kw|!<83LNXJNVr_%2=-Hy;5i8LNU#3-=gL!2f)>V zgoC{k!5$ZaqrX-#RVI{(c$gG=#GRkD+eN?EEF9lfES|R(jU|hdv<_#44!-s+*M1Eu z0(m?vdZJ#1HAD)lmx#C?D;4vXUzpqj$}s)ZON2zU<97=H+Dm;&L3`w@XWR#NsBEse zTajwF4)GhngJ;Ux5_;%5)@;ccD^O&X%Ng)povNSJo+fW@xCr}THl1SMw(4gRC*Fbf z*%KMzQ@t#h6nrJQKZH+FPU(c0>9l9aR?TNMt2_G&Bh9C`ZZ+nKTk3(zrxw#T)6tSh zh%wDzR92tkT(UB8dIRVh}55v2jKj?h)30pg>aYma#449|?x^wnAX~Pp9mr2J;6VGiGO^0)kME_%l(WNm~6MBfRo>oxZ^PJ&*QfeiX zph{HKwgZeiN3SFBYEI?{`6|9^k4G?| z6O=CJ@BR2QjHDa`*$Y`u%P`PPXxsRzTT?)xaFl}IY-;mV;TzMukmXGwXY0BqVi#m5 zrb*Ap;O2x+VN&+Mnhuy*w@4+;tIx_2w3+9cHi&q!IP8R|<6V)qbeamF=MXS~Da{4< zM%FOUu)7>=mQWHK5NlE!7lQSD@*=^)6Q(f+C8De~o{2j4jt1cOKLDQY5brL!;!FjF z=rTW2jYPeIT$L_R&KF1V3_`sY9G9Gxv)W&a^N=X76?uBQX2c&lnty^sFi}@Rp{NC1 zFpdmB>7h%~$Tg5(IB;BP%|&5NTUBAI2={OpB^6p3im;yH`*x3vLXO<)()D`0vGxOf zOg;SAkb$}_>S?19kIfbK5-7G;k}{Sl@3*5b;E3hpYj*P?0mdxvto;H2AD&A8urBVt zl;KnH8~%l&(BHP;-_Gn)(?kt^noc*_f>{H~0c$pWH`f=3ic{v4y%r{2>X|l4KGc#a z*)|oy1H3eHO*n*51u=65D8=6BgKu_X&UKeOk^a8nY34fW2Xc%~vgOmv3B2b()%AIG5ki4DN;Sc| zvSuv(SmlY$Up16s5Am2C3=cVV(v*cat3OOYRxF}b$7T50L743aqw_GpqPq!=@!4;% zQ^6xL|99!(Y*m8R-1bb1v?5Qn0g+D@Gp6sDA6^K*QEYkgkhwfiI&dpK<2sXy8fT%o z<+aSFf8w8{)mO#kMMCqEp}sXO+#yy$aTqn8V!%)<{SbA)tWR4 z77<&MnpqK4drW;nok|(t`@O3clCp0Epvi7HoT?oI=rwG_=0tw3@Q(S$WU|JT7s%+-~+` zm=WTT%)9q-!f+sc{A7y^gYpiFu6#(c^}=htgoA*Ma9V5Ok^Dj@)I)v5>BrUCx=Hgs4j9SB!AqN(HeV))~E-J|D&&jtsd&Abp?sL-bH zjtv6%_#QVByvN=b^*g8rjgJ zS$SV2nU@!^@bI=P=MDXZG`!RwCb3KU24?OSiQb)5edK=iy6pDTSJIk7^c>I7>Yw8~ zc1QE$KP>95kA|Wylx!OkKJNIi`sq$pwzihmvCexXM}iK2Yxg303KH{|>>4wryOv$q zV1(hr_?SH5bs}YRwwrElc3%C|t^9-e%kK}L*lS#F(FhB_j!5BH7s-rZkn}JkX@g4aOhZP{lb3`84%d9kMc8J5Lduo^8NXw9K zP!yf!N~UnylcKwsj#46-8vd36MU}45lHmjI95oAlNF14lbSaRO zAh-`!E*_xLD8_!}qn+?qptasbw~0ir>GIFBP)I9_dhlOMqc1~_`UYXeZo6_w zgx;w|yUu$U$5=|eIXSm|ZLii$Q4762M^WnK zTfYwQNbEtmPswg9sYNX}f1$6kr((&5G->`@=%RpCI$^JDN0DBynyv3&>6pGTCpsrS zy)JIJZ@)#DNSf+Ij|fu>dNlv0@BKxZw8T-0cAZ2tdv6o_ zKDyiB8ArA{gWT^naqb*w7Eyz$1OunmFFJ`)_DNN^eDuBPFI0=2z#~BW6DME#1uaEL zYT5-=(S8!7vQ& znb~ky-Oo7LXLCSzubW`r*$#ta-yW}XXxtNh9*KGJsGH@SXWLI7ZoO^fL!6$6hO49% ziuzFc2R-?m{0RTcub$qj%#2TN1C@*nOfTq+9M{%RFw4p=%r?ro@R7hRH>EFkIMzIQ zBB2CJ_f*c9_?ccNr`q$~PI@sEQ8!E_uyoRh$LWc6J(Do*ZXz7uGjT?E-%jD8X7PYho<w!!yxI~q5bZM71CLmoD30k}$_ABrce>wd3 z*ypVM-e5O|zm5lJctuHFC#e3U&8*{J5k@xwzx<#hJYaj}3ca2LVknY6cdP4usDQba zp&hw1?eAyc!Orv|0kb}Ms-Tit|2pfB+@a9t(;r<9KgxM0tWG7)ndhY#)W8{c1K#fs zp31JHk2pVrC;Nz{9O}Szuj-QOTx-+&Rv#N$rU(sA_>jInR@vF2f^pt4^9 zoI{c$qyL;k{}EDQFprGKAK28-iOUQ}W#qMI%_4{3U5aa?YCM#Wvnfxt{Yk~urxK#e zR$pr$plBHfP0nOT@$hu=Tp8}}&D9R2s|TH1Tq-A~Z8K7uULJ95b;zG&X$C5E)2axj z2636-eRl^W>FkXHM~=g`Ijqf_Gq6F{&jx#~Ot{mFBJ+O@sdi8T&Omx7XTfo^DM43( z4Q6Wj{rzIX1JZB({bJVb{#p$bxjb?bY{K&T)VE+1@tIzF^K4H3yM;@7H&wYV{Onsl zs|I~2oqz6?EZ+p}(;!VycDK;+WR7ytMxj#N-@89g zX0u~~{IsCpgXe@TutSa`>L|wRJfAjoK%ia6t6*JhSI38l5A%VEhkprdOqDEv3J{81 z3SzDannN-FTM^L-oR$)OKhYqKP2AR&W-4yru~~=Dpu(bW>Fz^|wy_Bx0T1oD>4;`O z+zCXtxxtE-O~4<M39 z+e($rqiCEai)z!Yr9{L(&+dWJ_#9;oMjNn6yppRXX9@ep$8_sZv zKArk{B;%ec&)K5pyo}}=|H8 zk9l@f>5|~BuZN>GBribRW~n*b2whCL#W{YLskf*?kGE?_6D2GXU_Qfk!^yJFWq?lc z2Rr$7r8DtVm8pg_9|>-}T>taKkzpWEp)NRQL5(4cW~*+g>0<9z;d_r>-KnF7q;I_t zyCs_-^Fck9f3|w~whrTN-Q(r2I}CxZGi`A6-K7OMT4MlA3{O6oo^bK*YgB_ayHr{= zD>XTgsJhCPCG}M!+g9Q?&xF@UgPr2K_h^{kKRGPd`SWyII+!Qrzqv4X2o!}=v`YUE zd39w}!P(Q*uA~H2W$VBuuHc2x|40g)h`P(T4!M&?j~K`JQ|5jjkVVw5X3b7CI1OOx zMr+YSt>=^*A5o0p7Ytx8VXU+MgknaMileWb@~WQDm;y}#gmV|17Y3q?bh3ahB-+X* z3|!dGMPZKH^KhV)(^2+WW|YXkh%>FLg!A&+_yJOWEwgw)7z}7t0`ZHPDfx?EAu2rw#W5BGBjfx z0;d$u*O=3+WVnfso7Kbc%BBuYgaT}C;s;a$wz>bvsRw|2bQ&*Q>l3qEJq)m}9U`vL z^39dQ!3~eHe1|oefjtw(+%;=U>WZuaLXzAQhP1MoE+0{)&(MKx>ZVEpGvx#+}9Lv)PC5M3k2e-nf?Be*0sy%qmnq zHG}`rH6fVk;e;93t@<|Km0Kc_sb6IPu%sY!WP4?{xdMd86f&P{gUVfeE~y1@~{7z z4k9cF#DoWSCztF=3IH4q9z->9zk-ZoJYg45xkNqm=w5~=Xlan8&E%)L=8B+s8>HV^ zxJo;~T!zpyoN;pZj@y79`EY-~(ft(GSAyPvPhiB+s>c$#vX9$<>^2OHe9Dgiognzv z`04E5$PD5i_-01=ANXeNANXecANb~f&^Pq~U?Ub*Z*$XaxXdniYEQE*kN`d_ql2As zq;2n?2j{jxlw?Hogi`5X*t-n;cYyl`nc z4dFxwjQ$#+&E-z84Zu&s`XZl*SzhjEZ-h%zX(=CcMB80ukB&&mS~| zUj2(D1}b+aFy&CHzKhE(-2X3szl=@qY%)=YSE{jr^{ldB=^uJE=;dwN8{jlI*_c2A zyUxaCI??mu7I-+Q)8zgnM_Ah_jQ(1LTnaV#%7AJtLb-SMS;F|dR(pm$S_D!U!+Q^& zOi0Xi#dm^JwNIfq8%*AA_!ct%{-9tx>oz(M%m6xekW(vD!@KqBM88zD!dYD0>V~kZ zFO}M5AFZRN?nv29CxCAOQ>p045s;H8{~7rYPs4m0pu6%Pst-T7pigEjU{^ zF`Ka1qsbo+^Vpag9;tLLw*ZR7+Br;NSszrfxFqt5`Gk}p;WBwCV`t0$zMsSJX>^jG z{!LPY+b5dmWTd>DWa#A;JZ5=CHI(o5KS%ub&epq?Ru@p<1-r`Jr!ol~ko=Omi>|F+ z6*KeY)XiCNUGKC4b4upFh-9>by4u30**kkQPx0Ok^I!|wmR2n-IayR&xUw1-3+<$Q zW96}(F^sdE+h`K(lC1HNF7ISfo5mSU7+^(w6jBRnPpw3#C{U5U4bq1S-EhwUm z)D#6!KFT;PemF-=R?FwJEJJrvunWK$vUt_kA#KXkQhz7`N^Y^PE-0XONx4W>CHY+r zPd9T-8Lz^!p=zT#X&KwA=#i}#uZY<^QkYmP#B$R-OXzoBaRoGK?FfvqrNbo56(?0Y zgN*E#=(Wr<6HHfgu~@7}{-4ndVD*GF8nQCIJh!Othw+bJslZ@2)+eLOff&55q+Zyj z-)>NW^8K`3TNpe#(F42}qN#n8wbe8*&JZkG3sv39EEzpqz?pj5Z#FUV+)-5}j#VT$ zIn+tUdIQ$-A^ICx6U@0noJm41xb(?b_|XP}7mQ0O(F~=Qi-tlrmeQ`D@`=SBRkB6; zMB6tp6%BFB-*YsB&K)=Q!QRU^pe8DYKiv$zv--RXO0k5%v|xD9KRlUWLC)~Cq63X{ zeF^hQawGZeK8}knE$EO!p1kc%%%2)#e-gGgDf9E?onkKbd?#-TRCejhJXroPv0Vjk zf&++=i{~%n@$m-bnLO+a>N6eQSBF0vH2o636}V3~(gv2YO%H&$gUi9=`~HJDGB(NK zZ@3#OqadoC%jovfPt^x2tkJqsO=;n<=fTRD`uR;o>#C!z^pbF_Qq=vHc+ zFp@n~ER{#K-r$sOJYKj+r+SYUpiik#Of?wB$^s{zzc5a`I*EO08z-^5ykxMvN3Kfg zKpJ$l(*7)6>6PLIkl#fY?T2EGrNG;69$XVe`JiW%D~T7RQMK7av}R7VBFd$67S(ya>IQ&#leqgKL14wF{ufllq&s?#1<#7w3V zzA)TE{nEhwcU};V{(HW&{ZG1Myipb~rAIr=XA3V^pwJLn-1U#|;dc%nQr`OcabwAp zXY6z3XmMY~_uik1wNk}sCS`X13_2ry3A$H2jWZ|lvrZF>5FnUS8rL`!e| zs`aNwR$eJ@A_1v;qR94VHTk9A7CkTfLXE$9@c~@Oo;j}H>jU2&p!PwPWfi!ic%--G z_;XDgyAdFlg{*wBUd)f3a%XLs`vsR>h;2A#qE%~GrdvDDHjZMc9wBQ)%#%2RZ~p9@ zB8Wc#LsvTn&d44|rDQ*s2{1^@)~Cg$k777y=0p~(Bc-@k^|u`k6zy8x zmRj)Gnvz1D*|M;MK_jeDOj_!Q*f#$1&}5At9_!U|(!hl4$IoiTaG3ol`Krd7!^f$~ zY+Qv_iKkuJ#dA_aa^MBZ(OpX|^1Na)h~hT}!$%F2&4m`Dqh z>~JUzq9R!j(KMFF47IqsL$KL-8e!uDt9{`i27s7>;w#FY5V%t3_gdtH_ABmfT9)&5 z_S33q*(-Ix==nM#KIJd%gp<9Iy|OshKn0Ruql{DY+Geu~Mfmhl3Y|#+>jdfKsjN1a% zW>VxI5U%vdF?Y@^;Mcx7CN9;4+ZUhVL|C6SSdP|IYr`1T2QCb)!Z#iSr`i`f|623o z9*bV?23Yn;6hJFO`E8&q}Fv9>qN34;;E5EaP;i_URh9>`bHjI4_#Mm8nT1 zMCAwJWe^6_QryPZs`Mz1FEg6$c`vXG2te=tbG0^FBz@a+qBUOp;4E;s{s>@_ID5C+ z@EeKyp9j$M&g+Ot)*H_BQo^)jGy9#)YQ5fyuo?58CHWalmS8f!&D28``w318_vR

HObiT&EAMJ|BZh-zL@xXoBG8 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 pTD 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 + Box( + Modifier + .padding(innerPadding) + .fillMaxSize() + .clip(RoundedCornerShape(40.dp)) + .background(color = surfaceContainerHighestLight) + .padding(top = 16.dp, start = 16.dp, end = 16.dp, bottom = 32.dp), + contentAlignment = Alignment.Center, + ) { + val scrollState = rememberScrollState() + + Column( + Modifier + .padding(top = 16.dp) + .imePadding() + .widthIn(max = 646.dp) + .fillMaxHeight() + .verticalScroll(scrollState), + ) { + Text( + text = stringResource(R.string.gemini_hotel_review), + style = MaterialTheme.typography.titleLarge, + modifier = Modifier.padding(8.dp), + ) + + val status = uiState.status + when { + status is GeminiStatus.Initial -> { + InitialReviewUi( + tags = viewModel.tags, + selectedTags = uiState.selectedTags, + onTagToggle = viewModel::toggleTag, + selectedMode = uiState.selectedMode, + onModeSelected = viewModel::setInferenceMode, + selectedModelOption = uiState.selectedModelOption, + onModelOptionSelected = viewModel::setModelOption, + onGenerate = { + val tagStrings = + uiState.selectedTags.map { ContextCompat.getString(context, it) } + viewModel.generateReview(tagStrings) + }, + ) + } + + status is GeminiStatus.Generating && !status.isTranslation -> { + GeneratingUi(status) + } + + status is GeminiStatus.Error -> { + ErrorUi(status.message, onReset = viewModel::reset) + } + + else -> { + SuccessReviewUi( + reviewText = uiState.reviewText, + reviewInferenceStatus = uiState.reviewInferenceStatus, + onReviewTextChanged = viewModel::updateReviewText, + languageKeys = viewModel.languageMap.keys.toList(), + languageMap = viewModel.languageMap, + selectedLanguage = uiState.selectedLanguage, + onLanguageSelected = viewModel::setSelectedLanguage, + onTranslate = { + viewModel.translate( + uiState.reviewText, + uiState.selectedLanguage + ) + }, + onReset = viewModel::reset, + generationStatus = status, + ) + } + } + } + } + } + } +} + +@OptIn(ExperimentalLayoutApi::class) +@Composable +fun InitialReviewUi( + tags: List, + selectedTags: List, + onTagToggle: (Int) -> Unit, + selectedMode: InferenceMode, + onModeSelected: (InferenceMode) -> Unit, + selectedModelOption: OnDeviceModelOption, + onModelOptionSelected: (OnDeviceModelOption) -> Unit, + onGenerate: () -> Unit, +) { + Row( + modifier = Modifier + .fillMaxWidth() + .padding(start = 8.dp, top = 4.dp, end = 8.dp, bottom = 12.dp), + horizontalArrangement = Arrangement.spacedBy(8.dp) + ) { + Box(modifier = Modifier.weight(1f)) { + InferenceModeDropdown( + selectedMode = selectedMode, + onModeSelected = onModeSelected, + modifier = Modifier.fillMaxWidth() + ) + } + Box(modifier = Modifier.weight(1f)) { + ModelOptionDropdown( + selectedOption = selectedModelOption, + onOptionSelected = onModelOptionSelected, + modifier = Modifier.fillMaxWidth() + ) + } + } + + Spacer(Modifier.height(16.dp)) + + Text( + stringResource(R.string.select_topics_for_your_review), + modifier = Modifier.padding(horizontal = 8.dp, vertical = 4.dp), + style = MaterialTheme.typography.titleMedium, + ) + FlowRow( + modifier = Modifier + .padding(8.dp), + ) { + tags.forEach { tagResId -> + val isSelected = selectedTags.contains(tagResId) + OutlinedToggleButton( + checked = isSelected, + onCheckedChange = { onTagToggle(tagResId) }, + colors = ToggleButtonDefaults.outlinedToggleButtonColors( + contentColor = MaterialTheme.colorScheme.tertiary, + ), + modifier = Modifier.padding(horizontal = 6.dp), + ) { + Text( + stringResource(tagResId), + style = MaterialTheme.typography.labelLarge, + fontWeight = FontWeight.Bold, + ) + } + } + } + Spacer(Modifier.height(24.dp)) + + GenerateButton( + text = stringResource(R.string.gemini_hybrid_generate_btn), + icon = painterResource(id = com.android.ai.uicomponent.R.drawable.ic_ai_text), + modifier = Modifier + .fillMaxWidth() + .padding(top = 12.dp, start = 8.dp, end = 8.dp), + enabled = selectedTags.isNotEmpty(), + onClick = onGenerate, + ) +} + +@Composable +fun GeneratingUi(status: GeminiStatus.Generating) { + val statusText = if (status.isCloud) { + stringResource(R.string.gemini_hybrid_status_generating_cloud) + } else { + stringResource(R.string.gemini_hybrid_status_generating_on_device) + } + Column(modifier = Modifier.fillMaxWidth()) { + StatusText(statusText) + if (status.partialOutput.isNotEmpty()) { + OutputText(status.partialOutput) + } + } +} + +@Composable +fun SuccessReviewUi( + reviewText: String, + reviewInferenceStatus: Int?, + onReviewTextChanged: (String) -> Unit, + languageKeys: List, + languageMap: Map, + selectedLanguage: String, + onLanguageSelected: (String) -> Unit, + onTranslate: () -> Unit, + onReset: () -> Unit, + generationStatus: GeminiStatus, +) { + Column(modifier = Modifier.fillMaxWidth()) { + reviewInferenceStatus?.let { + StatusText(stringResource(it)) + } + OutlinedTextField( + value = reviewText, + onValueChange = onReviewTextChanged, + modifier = Modifier + .padding(4.dp) + .fillMaxWidth() + .heightIn(max = 200.dp), + ) + + Spacer(modifier = Modifier.height(20.dp)) + + Box(modifier = Modifier.padding(start = 8.dp, top = 12.dp)) { + LanguageDropdown( + languageKeys = languageKeys, + languageMap = languageMap, + selectedLanguage = selectedLanguage, + onLanguageSelected = onLanguageSelected, + ) + } + + GenerateButton( + text = stringResource(R.string.gemini_hybrid_translate_btn), + icon = painterResource(id = com.android.ai.uicomponent.R.drawable.ic_ai_text), + modifier = Modifier + .fillMaxWidth() + .padding(top = 12.dp, start = 8.dp, end = 8.dp), + enabled = reviewText.isNotBlank() && generationStatus !is GeminiStatus.Generating, + onClick = onTranslate, + ) + + Spacer(modifier = Modifier.height(20.dp)) + when (generationStatus) { + is GeminiStatus.Generating -> { + if (generationStatus.isTranslation) { + val statusText = if (generationStatus.isCloud) { + stringResource(R.string.gemini_hybrid_status_generating_cloud) + } else { + stringResource(R.string.gemini_hybrid_status_generating_on_device) + } + StatusText(statusText) + if (generationStatus.partialOutput.isNotEmpty()) { + OutputText(generationStatus.partialOutput) + } + } + } + + is GeminiStatus.Success -> { + if (generationStatus.isTranslation) { + val inferenceStatus = if (generationStatus.isCloud) { + R.string.gemini_hybrid_generated_cloud + } else { + R.string.gemini_hybrid_generated_on_device + } + + StatusText(stringResource(inferenceStatus)) + OutputText(generationStatus.output) + } + } + + else -> {} + } + + UndoButton( + modifier = Modifier.padding(start = 8.dp, top = 8.dp), + onClick = onReset, + ) + } +} + +@Composable +fun ErrorUi(message: String, onReset: () -> Unit) { + Column { + StatusText(message) + UndoButton( + modifier = Modifier.padding(start = 8.dp, top = 8.dp), + onClick = onReset, + ) + } +} + +@Composable +fun LanguageDropdown( + languageKeys: List, + languageMap: Map, + selectedLanguage: String, + onLanguageSelected: (String) -> Unit, +) { + var expanded by remember { mutableStateOf(false) } + + Box { + SplitButtonLayout( + leadingButton = { + SplitButtonDefaults.LeadingButton( + onClick = { expanded = true }, + colors = ButtonDefaults.buttonColors( + containerColor = MaterialTheme.colorScheme.tertiaryContainer, + contentColor = MaterialTheme.colorScheme.onTertiaryContainer, + ), + ) { + Text(stringResource(languageMap[selectedLanguage] ?: R.string.gemini_hybrid_lang_korean).uppercase()) + } + }, + trailingButton = { + SplitButtonDefaults.TrailingButton( + onClick = { expanded = true }, + colors = ButtonDefaults.buttonColors( + containerColor = MaterialTheme.colorScheme.tertiaryContainer, + contentColor = MaterialTheme.colorScheme.onTertiaryContainer, + ), + ) { + Icon( + imageVector = Icons.Default.ArrowDropDown, + contentDescription = null, + ) + } + }, + ) + DropdownMenu( + expanded = expanded, + onDismissRequest = { expanded = false }, + ) { + languageKeys.forEach { key -> + DropdownMenuItem( + text = { Text(stringResource(languageMap[key]!!)) }, + onClick = { + onLanguageSelected(key) + expanded = false + }, + ) + } + } + } +} + +@PublicPreviewAPI +@Composable +fun InferenceModeDropdown( + selectedMode: InferenceMode, + onModeSelected: (InferenceMode) -> Unit, + modifier: Modifier = Modifier, +) { + var expanded by remember { mutableStateOf(false) } + val modes = listOf( + InferenceMode.ONLY_ON_DEVICE to stringResource(R.string.gemini_hybrid_mode_only_on_device), + InferenceMode.ONLY_IN_CLOUD to stringResource(R.string.gemini_hybrid_mode_only_cloud), + InferenceMode.PREFER_ON_DEVICE to stringResource(R.string.gemini_hybrid_mode_prefer_on_device), + InferenceMode.PREFER_IN_CLOUD to stringResource(R.string.gemini_hybrid_mode_prefer_cloud), + ) + val selectedText = modes.find { it.first == selectedMode }?.second ?: "" + + Box(modifier = modifier) { + SplitButtonLayout( + leadingButton = { + SplitButtonDefaults.LeadingButton( + onClick = { expanded = true }, + colors = ButtonDefaults.buttonColors( + containerColor = MaterialTheme.colorScheme.tertiaryContainer, + contentColor = MaterialTheme.colorScheme.onTertiaryContainer, + ), + ) { + Text(selectedText) + } + }, + trailingButton = { + SplitButtonDefaults.TrailingButton( + onClick = { expanded = true }, + colors = ButtonDefaults.buttonColors( + containerColor = MaterialTheme.colorScheme.tertiaryContainer, + contentColor = MaterialTheme.colorScheme.onTertiaryContainer, + ), + ) { + Icon( + imageVector = Icons.Default.ArrowDropDown, + contentDescription = null, + ) + } + }, + ) + DropdownMenu( + expanded = expanded, + onDismissRequest = { expanded = false }, + ) { + modes.forEach { (mode, label) -> + DropdownMenuItem( + text = { Text(label) }, + onClick = { + onModeSelected(mode) + expanded = false + }, + ) + } + } + } +} + +@PublicPreviewAPI +@Composable +fun ModelOptionDropdown( + selectedOption: OnDeviceModelOption, + onOptionSelected: (OnDeviceModelOption) -> Unit, + modifier: Modifier = Modifier, +) { + var expanded by remember { mutableStateOf(false) } + val options = listOf( + OnDeviceModelOption.STABLE to stringResource(R.string.gemini_hybrid_option_stable), + OnDeviceModelOption.PREVIEW to stringResource(R.string.gemini_hybrid_option_preview), + OnDeviceModelOption.PREVIEW_FAST to stringResource(R.string.gemini_hybrid_option_preview_fast), + ) + val selectedText = options.find { it.first == selectedOption }?.second ?: "" + + Box(modifier = modifier) { + SplitButtonLayout( + leadingButton = { + SplitButtonDefaults.LeadingButton( + onClick = { expanded = true }, + colors = ButtonDefaults.buttonColors( + containerColor = MaterialTheme.colorScheme.tertiaryContainer, + contentColor = MaterialTheme.colorScheme.onTertiaryContainer, + ), + ) { + Text(selectedText) + } + }, + trailingButton = { + SplitButtonDefaults.TrailingButton( + onClick = { expanded = true }, + colors = ButtonDefaults.buttonColors( + containerColor = MaterialTheme.colorScheme.tertiaryContainer, + contentColor = MaterialTheme.colorScheme.onTertiaryContainer, + ), + ) { + Icon( + imageVector = Icons.Default.ArrowDropDown, + contentDescription = null, + ) + } + }, + ) + DropdownMenu( + expanded = expanded, + onDismissRequest = { expanded = false }, + ) { + options.forEach { (option, label) -> + DropdownMenuItem( + text = { Text(label) }, + onClick = { + onOptionSelected(option) + expanded = false + }, + ) + } + } + } +} + + +@Composable +fun StatusText(text: String) { + Text( + text = text, + style = MaterialTheme.typography.bodySmall, + modifier = Modifier.padding(8.dp), + ) +} + +@Composable +fun OutputText(text: String, modifier: Modifier = Modifier) { + TextField( + value = text, + onValueChange = {}, + readOnly = true, + colors = TextFieldDefaults.colors( + focusedContainerColor = Color.Transparent, + unfocusedContainerColor = Color.Transparent, + focusedIndicatorColor = Color.Transparent, + unfocusedIndicatorColor = Color.Transparent, + disabledIndicatorColor = Color.Transparent, + disabledTextColor = MaterialTheme.colorScheme.onSurface, + ), + modifier = modifier.fillMaxWidth(), + textStyle = MaterialTheme.typography.bodyLarge.copy(fontSize = 16.sp), + ) +} diff --git a/samples/gemini-hybrid-v4/src/main/java/com/android/ai/samples/geminihybridv4/GeminiHybridV4ViewModel.kt b/samples/gemini-hybrid-v4/src/main/java/com/android/ai/samples/geminihybridv4/GeminiHybridV4ViewModel.kt new file mode 100644 index 00000000..39798ecb --- /dev/null +++ b/samples/gemini-hybrid-v4/src/main/java/com/android/ai/samples/geminihybridv4/GeminiHybridV4ViewModel.kt @@ -0,0 +1,260 @@ +/* + * Copyright 2025 The Android Open Source Project + * + * 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 + * + * https://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.ai.samples.geminihybridv4 + +import android.util.Log +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import com.google.firebase.Firebase +import com.google.firebase.ai.InferenceMode +import com.google.firebase.ai.InferenceSource +import com.google.firebase.ai.OnDeviceConfig +import com.google.firebase.ai.OnDeviceModelOption +import com.google.firebase.ai.ai +import com.google.firebase.ai.type.GenerativeBackend +import com.google.firebase.ai.type.PublicPreviewAPI +import dagger.hilt.android.lifecycle.HiltViewModel +import javax.inject.Inject +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.update +import kotlinx.coroutines.launch + +sealed interface GeminiStatus { + data object Initial : GeminiStatus + data class Generating( + val isCloud: Boolean, + val partialOutput: String = "", + val isTranslation: Boolean = false + ) : GeminiStatus + + data class Success( + val output: String, + val isCloud: Boolean, + val isTranslation: Boolean = false + ) : GeminiStatus + + data class Error(val message: String) : GeminiStatus +} + +@OptIn(PublicPreviewAPI::class) +data class GeminiHybridUiState( + val selectedMode: InferenceMode = InferenceMode.ONLY_ON_DEVICE, + val selectedTags: List = emptyList(), + val reviewText: String = "", + val reviewInferenceStatus: Int? = null, + val selectedLanguage: String = "Korean", + val selectedModelOption: OnDeviceModelOption = OnDeviceModelOption.STABLE, + val status: GeminiStatus = GeminiStatus.Initial +) + +@PublicPreviewAPI +@HiltViewModel +class GeminiHybridV4ViewModel @Inject constructor() : ViewModel() { + private val _uiState = MutableStateFlow(GeminiHybridUiState()) + val uiState: StateFlow = _uiState.asStateFlow() + + val tags = listOf( + R.string.location, + R.string.view, + R.string.service, + R.string.comfort, + R.string.food, + R.string.spacious, + R.string.natural_light, + ) + + val languageMap = mapOf( + "Korean" to R.string.gemini_hybrid_lang_korean, + "Spanish" to R.string.gemini_hybrid_lang_spanish, + "French" to R.string.gemini_hybrid_lang_french, + "German" to R.string.gemini_hybrid_lang_german + ) + + fun setInferenceMode(mode: InferenceMode) { + _uiState.update { it.copy(selectedMode = mode) } + } + + fun setModelOption(option: OnDeviceModelOption) { + _uiState.update { it.copy(selectedModelOption = option) } + } + + fun toggleTag(tagResId: Int) { + _uiState.update { state -> + val newTags = if (state.selectedTags.contains(tagResId)) { + state.selectedTags - tagResId + } else { + state.selectedTags + tagResId + } + state.copy(selectedTags = newTags) + } + } + + fun updateReviewText(text: String) { + _uiState.update { it.copy(reviewText = text) } + } + + fun setSelectedLanguage(language: String) { + _uiState.update { it.copy(selectedLanguage = language) } + } + + fun generateReview(tagStrings: List) { + if (tagStrings.isEmpty()) { + _uiState.update { it.copy(status = GeminiStatus.Error("Please select at least one tag")) } + return + } + + viewModelScope.launch { + _uiState.update { + it.copy( + status = GeminiStatus.Generating( + isCloud = it.selectedMode == InferenceMode.ONLY_IN_CLOUD, + isTranslation = false + ) + ) + } + try { + val prompt = + "Write a simple, short and generic hotel review positively covering the following themes: ${ + tagStrings.joinToString(", ") + }. Generate a generic review strictly from themes, don't hallucinate a hotel name or a location. Return only the review text." + + val model = Firebase.ai(backend = GenerativeBackend.googleAI()) + .generativeModel( + "gemini-2.5-flash-lite", + onDeviceConfig = OnDeviceConfig( + mode = _uiState.value.selectedMode, + modelOption = _uiState.value.selectedModelOption) + ) + model.onDeviceExtension?.checkStatus() + model.generateContentStream(prompt).collect { chunk -> + val isCloud = chunk.inferenceSource == InferenceSource.IN_CLOUD + _uiState.update { state -> + val currentStatus = state.status + val newStatus = if (currentStatus is GeminiStatus.Generating) { + currentStatus.copy( + isCloud = isCloud, + partialOutput = currentStatus.partialOutput + (chunk.text ?: "") + ) + } else { + GeminiStatus.Generating( + isCloud = isCloud, + partialOutput = chunk.text ?: "", + isTranslation = false + ) + } + state.copy(status = newStatus) + } + } + + val finalState = _uiState.value + val finalStatus = finalState.status + if (finalStatus is GeminiStatus.Generating) { + val output = finalStatus.partialOutput.trimEnd() + val inferenceStatusResId = if (finalStatus.isCloud) { + R.string.gemini_hybrid_generated_cloud + } else { + R.string.gemini_hybrid_generated_on_device + } + _uiState.update { + it.copy( + reviewText = output, + reviewInferenceStatus = inferenceStatusResId, + status = GeminiStatus.Success(output, finalStatus.isCloud, isTranslation = false) + ) + } + } + } catch (e: Exception) { + Log.e("GeminiHybrid", "Inference failed", e) + _uiState.update { + it.copy(status = GeminiStatus.Error(e.localizedMessage ?: "Unknown error occurred")) + } + } + } + } + + fun translate(text: String, language: String) { + if (text.isBlank()) { + _uiState.update { it.copy(status = GeminiStatus.Error("Text to translate cannot be empty")) } + return + } + + viewModelScope.launch { + _uiState.update { + it.copy( + status = GeminiStatus.Generating( + isCloud = it.selectedMode == InferenceMode.ONLY_IN_CLOUD, + isTranslation = true + ) + ) + } + try { + val prompt = + "Translate the following text to $language. Return ONLY the translated text, no explanations:\n\n$text" + + val model = Firebase.ai(backend = GenerativeBackend.googleAI()) + .generativeModel( + "gemini-2.5-flash-lite", + onDeviceConfig = OnDeviceConfig(mode = _uiState.value.selectedMode) + ) + + model.generateContentStream(prompt).collect { chunk -> + val isCloud = chunk.inferenceSource == InferenceSource.IN_CLOUD + _uiState.update { state -> + val currentStatus = state.status + val newStatus = if (currentStatus is GeminiStatus.Generating) { + currentStatus.copy( + isCloud = isCloud, + partialOutput = currentStatus.partialOutput + (chunk.text ?: "") + ) + } else { + GeminiStatus.Generating( + isCloud = isCloud, + partialOutput = chunk.text ?: "", + isTranslation = true + ) + } + state.copy(status = newStatus) + } + } + + val finalState = _uiState.value + val finalStatus = finalState.status + if (finalStatus is GeminiStatus.Generating) { + _uiState.update { + it.copy( + status = GeminiStatus.Success( + finalStatus.partialOutput, + finalStatus.isCloud, + isTranslation = true + ) + ) + } + } + } catch (e: Exception) { + Log.e("GeminiHybrid", "Inference failed", e) + _uiState.update { + it.copy(status = GeminiStatus.Error(e.localizedMessage ?: "Unknown error occurred")) + } + } + } + } + + fun reset() { + _uiState.value = GeminiHybridUiState() + } +} diff --git a/samples/gemini-hybrid-v4/src/main/res/values/strings.xml b/samples/gemini-hybrid-v4/src/main/res/values/strings.xml new file mode 100644 index 00000000..d8313289 --- /dev/null +++ b/samples/gemini-hybrid-v4/src/main/res/values/strings.xml @@ -0,0 +1,31 @@ + + + Hybrid Inference + Inference with Firebase Hybrid SDK using either Gemini Nano on-device or Gemini Flash in the Cloud. + Hotel review generation + Generate Review + Translate + Korean + Spanish + French + German + Generating on-device… + Generating in cloud… + Generated in the cloud + Generated on device + PREFER ON-DEVICE + PREFER CLOUD + ONLY ON-DEVICE + ONLY CLOUD + LOCATION + VIEW + SERVICE + COMFORT + FOOD + SPACIOUS + NATURAL LIGHT + Select topics for your review: + STABLE + PREVIEW + PREVIEW FAST + diff --git a/settings.gradle.kts b/settings.gradle.kts index c21b7a73..8619decd 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -52,4 +52,5 @@ include(":samples:gemini-live-todo") include(":samples:gemini-video-metadata-creation") include(":samples:gemini-image-chat") include(":samples:gemini-hybrid") +include(":samples:gemini-hybrid-v4") include(":ui-component") From 7eb6a3d8929954d9626e83127302bb945dac6ce3 Mon Sep 17 00:00:00 2001 From: Rodrigo Lazo Paz Date: Wed, 6 May 2026 15:54:39 -0400 Subject: [PATCH 3/5] UI mostly done --- app/src/main/res/values/strings.xml | 2 +- samples/gemini-hybrid-v4/build.gradle.kts | 2 + .../src/main/AndroidManifest.xml | 4 + .../ai/samples/geminihybridv4/Expense.kt | 10 + .../geminihybridv4/GeminiHybridV4Screen.kt | 728 +++++++----------- .../geminihybridv4/GeminiHybridV4ViewModel.kt | 291 +++---- .../src/main/res/values/strings.xml | 34 +- 7 files changed, 412 insertions(+), 659 deletions(-) create mode 100644 samples/gemini-hybrid-v4/src/main/AndroidManifest.xml create mode 100644 samples/gemini-hybrid-v4/src/main/java/com/android/ai/samples/geminihybridv4/Expense.kt diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 777e856d..b7c1e408 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -30,7 +30,7 @@ Gemini Hybrid Inference with Firebase Hybrid SDK using either Gemini Nano on-device or Gemini Flash in the Cloud. Gemini Hybrid v4 - Inference with Firebase Hybrid SDK using either Gemini Nano on-device or Gemini Flash in the Cloud (v4 version). + Inference with Firebase Hybrid SDK (nano v4 version). Firebase Required This feature requires Firebase to be initialized. Close diff --git a/samples/gemini-hybrid-v4/build.gradle.kts b/samples/gemini-hybrid-v4/build.gradle.kts index 690d44c1..65abb15a 100644 --- a/samples/gemini-hybrid-v4/build.gradle.kts +++ b/samples/gemini-hybrid-v4/build.gradle.kts @@ -17,6 +17,7 @@ plugins { alias(libs.plugins.android.library) alias(libs.plugins.jetbrains.kotlin.android) + alias(libs.plugins.jetbrains.kotlin.serialization) alias(libs.plugins.ksp) alias(libs.plugins.compose.compiler) } @@ -68,6 +69,7 @@ dependencies { implementation(platform(libs.firebase.bom)) implementation(libs.firebase.ai) implementation(libs.firebase.ai.ondevice) + implementation(libs.kotlinx.serialization.json) implementation(project(":ui-component")) debugImplementation(libs.ui.tooling) diff --git a/samples/gemini-hybrid-v4/src/main/AndroidManifest.xml b/samples/gemini-hybrid-v4/src/main/AndroidManifest.xml new file mode 100644 index 00000000..cfe0da1d --- /dev/null +++ b/samples/gemini-hybrid-v4/src/main/AndroidManifest.xml @@ -0,0 +1,4 @@ + + + + diff --git a/samples/gemini-hybrid-v4/src/main/java/com/android/ai/samples/geminihybridv4/Expense.kt b/samples/gemini-hybrid-v4/src/main/java/com/android/ai/samples/geminihybridv4/Expense.kt new file mode 100644 index 00000000..cad9c65b --- /dev/null +++ b/samples/gemini-hybrid-v4/src/main/java/com/android/ai/samples/geminihybridv4/Expense.kt @@ -0,0 +1,10 @@ +package com.android.ai.samples.geminihybridv4 + +import kotlinx.serialization.Serializable + +@Serializable +data class Expense( + val name: String, + val price: Double, + val inferenceMode: String = "" +) diff --git a/samples/gemini-hybrid-v4/src/main/java/com/android/ai/samples/geminihybridv4/GeminiHybridV4Screen.kt b/samples/gemini-hybrid-v4/src/main/java/com/android/ai/samples/geminihybridv4/GeminiHybridV4Screen.kt index d448aad5..18052c1e 100644 --- a/samples/gemini-hybrid-v4/src/main/java/com/android/ai/samples/geminihybridv4/GeminiHybridV4Screen.kt +++ b/samples/gemini-hybrid-v4/src/main/java/com/android/ai/samples/geminihybridv4/GeminiHybridV4Screen.kt @@ -1,5 +1,5 @@ /* - * Copyright 2025 The Android Open Source Project + * Copyright 2025 The Android Open Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,96 +13,165 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -@file:OptIn(PublicPreviewAPI::class, ExperimentalMaterial3ExpressiveApi::class) +@file:OptIn(PublicPreviewAPI::class) package com.android.ai.samples.geminihybridv4 +import android.Manifest +import android.content.Context +import android.content.pm.PackageManager +import android.graphics.BitmapFactory +import android.net.Uri +import android.util.Log import androidx.activity.compose.LocalOnBackPressedDispatcherOwner +import androidx.activity.compose.rememberLauncherForActivityResult +import androidx.activity.result.contract.ActivityResultContracts import androidx.compose.foundation.background import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.ExperimentalLayoutApi import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.FlowRow import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxHeight import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.heightIn -import androidx.compose.foundation.layout.imePadding import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.width import androidx.compose.foundation.layout.widthIn -import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.items import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.foundation.verticalScroll -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.ArrowDropDown -import androidx.compose.material3.ButtonDefaults +import androidx.compose.foundation.Image +import androidx.compose.material3.Button import androidx.compose.material3.DropdownMenu import androidx.compose.material3.DropdownMenuItem +import androidx.compose.material3.TextButton +import androidx.compose.material3.HorizontalDivider +import androidx.compose.ui.graphics.asImageBitmap +import androidx.compose.ui.layout.ContentScale +import androidx.compose.ui.window.Dialog +import android.graphics.Bitmap +import androidx.compose.material.icons.filled.Image +import androidx.compose.material.icons.filled.ArrowDropDown +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.CameraAlt +import androidx.compose.material.icons.automirrored.filled.ReceiptLong +import androidx.compose.material3.Card +import androidx.compose.material3.CardDefaults +import androidx.compose.material3.CircularProgressIndicator import androidx.compose.material3.ExperimentalMaterial3Api -import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi +import androidx.compose.material3.FloatingActionButton import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.OutlinedTextField -import androidx.compose.material3.OutlinedToggleButton import androidx.compose.material3.Scaffold -import androidx.compose.material3.SplitButtonDefaults -import androidx.compose.material3.SplitButtonLayout import androidx.compose.material3.Text -import androidx.compose.material3.TextField -import androidx.compose.material3.TextFieldDefaults -import androidx.compose.material3.ToggleButtonDefaults import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue +import androidx.compose.runtime.LaunchedEffect import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp -import androidx.compose.ui.unit.sp import androidx.core.content.ContextCompat +import androidx.core.content.FileProvider import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.android.ai.theme.AISampleCatalogTheme import com.android.ai.theme.surfaceContainerHighestLight -import com.android.ai.uicomponent.GenerateButton import com.android.ai.uicomponent.SampleDetailTopAppBar -import com.android.ai.uicomponent.UndoButton -import com.google.firebase.ai.InferenceMode -import com.google.firebase.ai.OnDeviceModelOption import com.google.firebase.ai.type.PublicPreviewAPI +import java.io.File +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext - -@OptIn(ExperimentalMaterial3Api::class, ExperimentalLayoutApi::class) +@OptIn(ExperimentalMaterial3Api::class) @Composable fun GeminiHybridV4Screen(viewModel: GeminiHybridV4ViewModel = hiltViewModel()) { - val uiState by viewModel.uiState.collectAsStateWithLifecycle() - val context = LocalContext.current val backDispatcher = LocalOnBackPressedDispatcherOwner.current?.onBackPressedDispatcher + var tempUri by remember { mutableStateOf(null) } + var showImageDialog by remember { mutableStateOf(false) } + + val cameraLauncher = rememberLauncherForActivityResult( + contract = ActivityResultContracts.TakePicture(), + onResult = { success -> + if (success) { + tempUri?.let { uri -> + try { + val bitmap = BitmapFactory.decodeStream(context.contentResolver.openInputStream(uri)) + bitmap?.let { viewModel.scanReceipt(it, uri.toString()) } + } catch (e: Exception) { + Log.e("GeminiHybridV4Screen", "Failed to load image", e) + } + } + } + } + ) + + fun launchCamera() { + try { + val uri = getTmpFileUri(context) + tempUri = uri + cameraLauncher.launch(uri) + } catch (e: Exception) { + Log.e("GeminiHybridV4Screen", "Failed to create temp file", e) + } + } + + val permissionLauncher = rememberLauncherForActivityResult( + ActivityResultContracts.RequestPermission() + ) { isGranted -> + if (isGranted) { + launchCamera() + } + } AISampleCatalogTheme { Scaffold( modifier = Modifier.fillMaxSize(), topBar = { SampleDetailTopAppBar( - sampleName = stringResource(R.string.gemini_hybrid_title), - sampleDescription = stringResource(R.string.gemini_hybrid_description), + sampleName = stringResource(R.string.gemini_hybrid_v4_title), + sampleDescription = stringResource(R.string.gemini_hybrid_v4_description), sourceCodeUrl = "https://github.com/android/ai-samples/tree/main/samples/gemini-hybrid-v4", onBackClick = { backDispatcher?.onBackPressed() }, ) }, + floatingActionButton = { + FloatingActionButton( + onClick = { + val permissionCheckResult = + ContextCompat.checkSelfPermission(context, Manifest.permission.CAMERA) + if (permissionCheckResult == PackageManager.PERMISSION_GRANTED) { + launchCamera() + } else { + permissionLauncher.launch(Manifest.permission.CAMERA) + } + }, + containerColor = MaterialTheme.colorScheme.primary, + contentColor = MaterialTheme.colorScheme.onPrimary + ) { + if (uiState.isScanning) { + CircularProgressIndicator( + modifier = Modifier.size(24.dp), + color = MaterialTheme.colorScheme.onPrimary, + strokeWidth = 2.dp + ) + } else { + Icon(Icons.Default.CameraAlt, contentDescription = stringResource(R.string.scan_receipt)) + } + } + } ) { innerPadding -> Box( Modifier @@ -113,463 +182,242 @@ fun GeminiHybridV4Screen(viewModel: GeminiHybridV4ViewModel = hiltViewModel()) { .padding(top = 16.dp, start = 16.dp, end = 16.dp, bottom = 32.dp), contentAlignment = Alignment.Center, ) { - val scrollState = rememberScrollState() - Column( Modifier .padding(top = 16.dp) - .imePadding() .widthIn(max = 646.dp) - .fillMaxHeight() - .verticalScroll(scrollState), + .fillMaxHeight(), ) { + // Model Status Card + Card( + modifier = Modifier.fillMaxWidth(), + colors = CardDefaults.cardColors( + containerColor = MaterialTheme.colorScheme.secondaryContainer + ) + ) { + Row( + modifier = Modifier.padding(12.dp), + verticalAlignment = Alignment.CenterVertically + ) { + Icon( + Icons.AutoMirrored.Filled.ReceiptLong, + contentDescription = null, + tint = MaterialTheme.colorScheme.onSecondaryContainer + ) + Spacer(modifier = Modifier.size(12.dp)) + Column { + Text( + stringResource(R.string.hybrid_v4_ai_status), + style = MaterialTheme.typography.labelMedium, + color = MaterialTheme.colorScheme.onSecondaryContainer + ) + Text( + uiState.modelStatus, + style = MaterialTheme.typography.bodyMedium, + fontWeight = FontWeight.Bold, + color = MaterialTheme.colorScheme.onSecondaryContainer + ) + } + } + } + + Spacer(modifier = Modifier.height(16.dp)) + Text( - text = stringResource(R.string.gemini_hotel_review), - style = MaterialTheme.typography.titleLarge, - modifier = Modifier.padding(8.dp), + stringResource(R.string.section_title), + style = MaterialTheme.typography.headlineSmall, + fontWeight = FontWeight.Bold ) - val status = uiState.status - when { - status is GeminiStatus.Initial -> { - InitialReviewUi( - tags = viewModel.tags, - selectedTags = uiState.selectedTags, - onTagToggle = viewModel::toggleTag, - selectedMode = uiState.selectedMode, - onModeSelected = viewModel::setInferenceMode, - selectedModelOption = uiState.selectedModelOption, - onModelOptionSelected = viewModel::setModelOption, - onGenerate = { - val tagStrings = - uiState.selectedTags.map { ContextCompat.getString(context, it) } - viewModel.generateReview(tagStrings) - }, - ) - } + Spacer(modifier = Modifier.height(8.dp)) - status is GeminiStatus.Generating && !status.isTranslation -> { - GeneratingUi(status) + if (uiState.expense == null) { + Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) { + Text(stringResource(R.string.no_expenses), color = Color.Gray) } + } else { + ExpenseResultUi( + expense = uiState.expense!!, + hasImage = uiState.receiptImageUri != null, + onShowImage = { showImageDialog = true } + ) + } - status is GeminiStatus.Error -> { - ErrorUi(status.message, onReset = viewModel::reset) + if (showImageDialog && uiState.receiptImageUri != null) { + val imageUri = Uri.parse(uiState.receiptImageUri) + var bitmap by remember(imageUri) { mutableStateOf(null) } + LaunchedEffect(imageUri) { + withContext(Dispatchers.IO) { + try { + bitmap = BitmapFactory.decodeStream(context.contentResolver.openInputStream(imageUri)) + } catch (e: Exception) { + Log.e("GeminiHybridV4Screen", "Failed to load image for dialog", e) + } + } } - else -> { - SuccessReviewUi( - reviewText = uiState.reviewText, - reviewInferenceStatus = uiState.reviewInferenceStatus, - onReviewTextChanged = viewModel::updateReviewText, - languageKeys = viewModel.languageMap.keys.toList(), - languageMap = viewModel.languageMap, - selectedLanguage = uiState.selectedLanguage, - onLanguageSelected = viewModel::setSelectedLanguage, - onTranslate = { - viewModel.translate( - uiState.reviewText, - uiState.selectedLanguage - ) - }, - onReset = viewModel::reset, - generationStatus = status, - ) + if (bitmap != null) { + Dialog(onDismissRequest = { showImageDialog = false }) { + Card( + modifier = Modifier + .fillMaxWidth() + .height(400.dp), + shape = RoundedCornerShape(16.dp), + ) { + Box(modifier = Modifier.fillMaxSize()) { + Image( + bitmap = bitmap!!.asImageBitmap(), + contentDescription = "Receipt Image", + modifier = Modifier.fillMaxSize(), + contentScale = ContentScale.Fit + ) + } + } + } + } else { + Dialog(onDismissRequest = { showImageDialog = false }) { + Box(modifier = Modifier.size(100.dp), contentAlignment = Alignment.Center) { + CircularProgressIndicator() + } + } } } + + if (uiState.errorMessage != null) { + Spacer(modifier = Modifier.height(8.dp)) + Text( + text = uiState.errorMessage!!, + color = MaterialTheme.colorScheme.error, + style = MaterialTheme.typography.bodySmall + ) + } } } } } } -@OptIn(ExperimentalLayoutApi::class) @Composable -fun InitialReviewUi( - tags: List, - selectedTags: List, - onTagToggle: (Int) -> Unit, - selectedMode: InferenceMode, - onModeSelected: (InferenceMode) -> Unit, - selectedModelOption: OnDeviceModelOption, - onModelOptionSelected: (OnDeviceModelOption) -> Unit, - onGenerate: () -> Unit, +fun ExpenseResultUi( + expense: Expense, + hasImage: Boolean, + onShowImage: () -> Unit ) { - Row( - modifier = Modifier - .fillMaxWidth() - .padding(start = 8.dp, top = 4.dp, end = 8.dp, bottom = 12.dp), - horizontalArrangement = Arrangement.spacedBy(8.dp) + Card( + modifier = Modifier.fillMaxWidth(), + elevation = CardDefaults.cardElevation(defaultElevation = 4.dp) ) { - Box(modifier = Modifier.weight(1f)) { - InferenceModeDropdown( - selectedMode = selectedMode, - onModeSelected = onModeSelected, - modifier = Modifier.fillMaxWidth() - ) - } - Box(modifier = Modifier.weight(1f)) { - ModelOptionDropdown( - selectedOption = selectedModelOption, - onOptionSelected = onModelOptionSelected, - modifier = Modifier.fillMaxWidth() + Column( + modifier = Modifier + .padding(16.dp) + .fillMaxWidth(), + verticalArrangement = Arrangement.spacedBy(12.dp) + ) { + Text( + text = expense.name, + style = MaterialTheme.typography.titleLarge, + fontWeight = FontWeight.Bold ) - } - } - - Spacer(Modifier.height(16.dp)) - Text( - stringResource(R.string.select_topics_for_your_review), - modifier = Modifier.padding(horizontal = 8.dp, vertical = 4.dp), - style = MaterialTheme.typography.titleMedium, - ) - FlowRow( - modifier = Modifier - .padding(8.dp), - ) { - tags.forEach { tagResId -> - val isSelected = selectedTags.contains(tagResId) - OutlinedToggleButton( - checked = isSelected, - onCheckedChange = { onTagToggle(tagResId) }, - colors = ToggleButtonDefaults.outlinedToggleButtonColors( - contentColor = MaterialTheme.colorScheme.tertiary, - ), - modifier = Modifier.padding(horizontal = 6.dp), + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.SpaceBetween, + verticalAlignment = Alignment.CenterVertically ) { Text( - stringResource(tagResId), - style = MaterialTheme.typography.labelLarge, + text = "Total:", + style = MaterialTheme.typography.bodyMedium, + color = Color.Gray + ) + Text( + text = "$${String.format("%.2f", expense.price)}", + style = MaterialTheme.typography.headlineMedium, fontWeight = FontWeight.Bold, + color = MaterialTheme.colorScheme.primary ) } - } - } - Spacer(Modifier.height(24.dp)) - - GenerateButton( - text = stringResource(R.string.gemini_hybrid_generate_btn), - icon = painterResource(id = com.android.ai.uicomponent.R.drawable.ic_ai_text), - modifier = Modifier - .fillMaxWidth() - .padding(top = 12.dp, start = 8.dp, end = 8.dp), - enabled = selectedTags.isNotEmpty(), - onClick = onGenerate, - ) -} - -@Composable -fun GeneratingUi(status: GeminiStatus.Generating) { - val statusText = if (status.isCloud) { - stringResource(R.string.gemini_hybrid_status_generating_cloud) - } else { - stringResource(R.string.gemini_hybrid_status_generating_on_device) - } - Column(modifier = Modifier.fillMaxWidth()) { - StatusText(statusText) - if (status.partialOutput.isNotEmpty()) { - OutputText(status.partialOutput) - } - } -} - -@Composable -fun SuccessReviewUi( - reviewText: String, - reviewInferenceStatus: Int?, - onReviewTextChanged: (String) -> Unit, - languageKeys: List, - languageMap: Map, - selectedLanguage: String, - onLanguageSelected: (String) -> Unit, - onTranslate: () -> Unit, - onReset: () -> Unit, - generationStatus: GeminiStatus, -) { - Column(modifier = Modifier.fillMaxWidth()) { - reviewInferenceStatus?.let { - StatusText(stringResource(it)) - } - OutlinedTextField( - value = reviewText, - onValueChange = onReviewTextChanged, - modifier = Modifier - .padding(4.dp) - .fillMaxWidth() - .heightIn(max = 200.dp), - ) - Spacer(modifier = Modifier.height(20.dp)) - - Box(modifier = Modifier.padding(start = 8.dp, top = 12.dp)) { - LanguageDropdown( - languageKeys = languageKeys, - languageMap = languageMap, - selectedLanguage = selectedLanguage, - onLanguageSelected = onLanguageSelected, - ) - } - - GenerateButton( - text = stringResource(R.string.gemini_hybrid_translate_btn), - icon = painterResource(id = com.android.ai.uicomponent.R.drawable.ic_ai_text), - modifier = Modifier - .fillMaxWidth() - .padding(top = 12.dp, start = 8.dp, end = 8.dp), - enabled = reviewText.isNotBlank() && generationStatus !is GeminiStatus.Generating, - onClick = onTranslate, - ) - - Spacer(modifier = Modifier.height(20.dp)) - when (generationStatus) { - is GeminiStatus.Generating -> { - if (generationStatus.isTranslation) { - val statusText = if (generationStatus.isCloud) { - stringResource(R.string.gemini_hybrid_status_generating_cloud) - } else { - stringResource(R.string.gemini_hybrid_status_generating_on_device) - } - StatusText(statusText) - if (generationStatus.partialOutput.isNotEmpty()) { - OutputText(generationStatus.partialOutput) - } - } + if (expense.inferenceMode.isNotEmpty()) { + Text( + text = "Extracted via ${expense.inferenceMode}", + style = MaterialTheme.typography.labelSmall, + color = Color.Gray + ) } - is GeminiStatus.Success -> { - if (generationStatus.isTranslation) { - val inferenceStatus = if (generationStatus.isCloud) { - R.string.gemini_hybrid_generated_cloud - } else { - R.string.gemini_hybrid_generated_on_device - } - - StatusText(stringResource(inferenceStatus)) - OutputText(generationStatus.output) + if (hasImage) { + Button( + onClick = onShowImage, + modifier = Modifier.fillMaxWidth() + ) { + Icon(Icons.Default.Image, contentDescription = null) + Spacer(modifier = Modifier.width(8.dp)) + Text("Show Receipt Image") } } - else -> {} - } + HorizontalDivider(modifier = Modifier.padding(vertical = 8.dp)) - UndoButton( - modifier = Modifier.padding(start = 8.dp, top = 8.dp), - onClick = onReset, - ) - } -} - -@Composable -fun ErrorUi(message: String, onReset: () -> Unit) { - Column { - StatusText(message) - UndoButton( - modifier = Modifier.padding(start = 8.dp, top = 8.dp), - onClick = onReset, - ) - } -} + Text( + text = "Split Check", + style = MaterialTheme.typography.titleMedium, + fontWeight = FontWeight.Bold + ) -@Composable -fun LanguageDropdown( - languageKeys: List, - languageMap: Map, - selectedLanguage: String, - onLanguageSelected: (String) -> Unit, -) { - var expanded by remember { mutableStateOf(false) } + var expanded by remember { mutableStateOf(false) } + var numPeople by remember(expense) { mutableStateOf(1) } - Box { - SplitButtonLayout( - leadingButton = { - SplitButtonDefaults.LeadingButton( - onClick = { expanded = true }, - colors = ButtonDefaults.buttonColors( - containerColor = MaterialTheme.colorScheme.tertiaryContainer, - contentColor = MaterialTheme.colorScheme.onTertiaryContainer, - ), - ) { - Text(stringResource(languageMap[selectedLanguage] ?: R.string.gemini_hybrid_lang_korean).uppercase()) - } - }, - trailingButton = { - SplitButtonDefaults.TrailingButton( - onClick = { expanded = true }, - colors = ButtonDefaults.buttonColors( - containerColor = MaterialTheme.colorScheme.tertiaryContainer, - contentColor = MaterialTheme.colorScheme.onTertiaryContainer, - ), - ) { - Icon( - imageVector = Icons.Default.ArrowDropDown, - contentDescription = null, - ) + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.SpaceBetween, + verticalAlignment = Alignment.CenterVertically + ) { + Text("Number of people:") + Box { + TextButton(onClick = { expanded = true }) { + Text("$numPeople") + Spacer(modifier = Modifier.width(4.dp)) + Icon(Icons.Default.ArrowDropDown, contentDescription = null) + } + DropdownMenu( + expanded = expanded, + onDismissRequest = { expanded = false } + ) { + (1..10).forEach { count -> + DropdownMenuItem( + text = { Text("$count") }, + onClick = { + numPeople = count + expanded = false + } + ) + } + } } - }, - ) - DropdownMenu( - expanded = expanded, - onDismissRequest = { expanded = false }, - ) { - languageKeys.forEach { key -> - DropdownMenuItem( - text = { Text(stringResource(languageMap[key]!!)) }, - onClick = { - onLanguageSelected(key) - expanded = false - }, - ) } - } - } -} -@PublicPreviewAPI -@Composable -fun InferenceModeDropdown( - selectedMode: InferenceMode, - onModeSelected: (InferenceMode) -> Unit, - modifier: Modifier = Modifier, -) { - var expanded by remember { mutableStateOf(false) } - val modes = listOf( - InferenceMode.ONLY_ON_DEVICE to stringResource(R.string.gemini_hybrid_mode_only_on_device), - InferenceMode.ONLY_IN_CLOUD to stringResource(R.string.gemini_hybrid_mode_only_cloud), - InferenceMode.PREFER_ON_DEVICE to stringResource(R.string.gemini_hybrid_mode_prefer_on_device), - InferenceMode.PREFER_IN_CLOUD to stringResource(R.string.gemini_hybrid_mode_prefer_cloud), - ) - val selectedText = modes.find { it.first == selectedMode }?.second ?: "" - - Box(modifier = modifier) { - SplitButtonLayout( - leadingButton = { - SplitButtonDefaults.LeadingButton( - onClick = { expanded = true }, - colors = ButtonDefaults.buttonColors( - containerColor = MaterialTheme.colorScheme.tertiaryContainer, - contentColor = MaterialTheme.colorScheme.onTertiaryContainer, - ), - ) { - Text(selectedText) - } - }, - trailingButton = { - SplitButtonDefaults.TrailingButton( - onClick = { expanded = true }, - colors = ButtonDefaults.buttonColors( - containerColor = MaterialTheme.colorScheme.tertiaryContainer, - contentColor = MaterialTheme.colorScheme.onTertiaryContainer, - ), - ) { - Icon( - imageVector = Icons.Default.ArrowDropDown, - contentDescription = null, - ) - } - }, - ) - DropdownMenu( - expanded = expanded, - onDismissRequest = { expanded = false }, - ) { - modes.forEach { (mode, label) -> - DropdownMenuItem( - text = { Text(label) }, - onClick = { - onModeSelected(mode) - expanded = false - }, + val perPersonAmount = expense.price / numPeople + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.SpaceBetween, + verticalAlignment = Alignment.CenterVertically + ) { + Text("Per person:") + Text( + text = "$${String.format("%.2f", perPersonAmount)}", + style = MaterialTheme.typography.titleLarge, + fontWeight = FontWeight.Bold, + color = MaterialTheme.colorScheme.secondary ) } } } } -@PublicPreviewAPI -@Composable -fun ModelOptionDropdown( - selectedOption: OnDeviceModelOption, - onOptionSelected: (OnDeviceModelOption) -> Unit, - modifier: Modifier = Modifier, -) { - var expanded by remember { mutableStateOf(false) } - val options = listOf( - OnDeviceModelOption.STABLE to stringResource(R.string.gemini_hybrid_option_stable), - OnDeviceModelOption.PREVIEW to stringResource(R.string.gemini_hybrid_option_preview), - OnDeviceModelOption.PREVIEW_FAST to stringResource(R.string.gemini_hybrid_option_preview_fast), - ) - val selectedText = options.find { it.first == selectedOption }?.second ?: "" - - Box(modifier = modifier) { - SplitButtonLayout( - leadingButton = { - SplitButtonDefaults.LeadingButton( - onClick = { expanded = true }, - colors = ButtonDefaults.buttonColors( - containerColor = MaterialTheme.colorScheme.tertiaryContainer, - contentColor = MaterialTheme.colorScheme.onTertiaryContainer, - ), - ) { - Text(selectedText) - } - }, - trailingButton = { - SplitButtonDefaults.TrailingButton( - onClick = { expanded = true }, - colors = ButtonDefaults.buttonColors( - containerColor = MaterialTheme.colorScheme.tertiaryContainer, - contentColor = MaterialTheme.colorScheme.onTertiaryContainer, - ), - ) { - Icon( - imageVector = Icons.Default.ArrowDropDown, - contentDescription = null, - ) - } - }, - ) - DropdownMenu( - expanded = expanded, - onDismissRequest = { expanded = false }, - ) { - options.forEach { (option, label) -> - DropdownMenuItem( - text = { Text(label) }, - onClick = { - onOptionSelected(option) - expanded = false - }, - ) - } - } +private fun getTmpFileUri(context: Context): Uri { + val tmpFile = File.createTempFile("tmp_image_file", ".jpg", context.cacheDir).apply { + createNewFile() + deleteOnExit() } -} - - -@Composable -fun StatusText(text: String) { - Text( - text = text, - style = MaterialTheme.typography.bodySmall, - modifier = Modifier.padding(8.dp), - ) -} - -@Composable -fun OutputText(text: String, modifier: Modifier = Modifier) { - TextField( - value = text, - onValueChange = {}, - readOnly = true, - colors = TextFieldDefaults.colors( - focusedContainerColor = Color.Transparent, - unfocusedContainerColor = Color.Transparent, - focusedIndicatorColor = Color.Transparent, - unfocusedIndicatorColor = Color.Transparent, - disabledIndicatorColor = Color.Transparent, - disabledTextColor = MaterialTheme.colorScheme.onSurface, - ), - modifier = modifier.fillMaxWidth(), - textStyle = MaterialTheme.typography.bodyLarge.copy(fontSize = 16.sp), - ) + return FileProvider.getUriForFile(context, "${context.packageName}.provider", tmpFile) } diff --git a/samples/gemini-hybrid-v4/src/main/java/com/android/ai/samples/geminihybridv4/GeminiHybridV4ViewModel.kt b/samples/gemini-hybrid-v4/src/main/java/com/android/ai/samples/geminihybridv4/GeminiHybridV4ViewModel.kt index 39798ecb..25fb63fe 100644 --- a/samples/gemini-hybrid-v4/src/main/java/com/android/ai/samples/geminihybridv4/GeminiHybridV4ViewModel.kt +++ b/samples/gemini-hybrid-v4/src/main/java/com/android/ai/samples/geminihybridv4/GeminiHybridV4ViewModel.kt @@ -15,17 +15,21 @@ */ package com.android.ai.samples.geminihybridv4 +import android.graphics.Bitmap import android.util.Log import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.google.firebase.Firebase +import com.google.firebase.ai.DownloadStatus import com.google.firebase.ai.InferenceMode import com.google.firebase.ai.InferenceSource import com.google.firebase.ai.OnDeviceConfig import com.google.firebase.ai.OnDeviceModelOption +import com.google.firebase.ai.OnDeviceModelStatus import com.google.firebase.ai.ai import com.google.firebase.ai.type.GenerativeBackend import com.google.firebase.ai.type.PublicPreviewAPI +import com.google.firebase.ai.type.content import dagger.hilt.android.lifecycle.HiltViewModel import javax.inject.Inject import kotlinx.coroutines.flow.MutableStateFlow @@ -33,228 +37,135 @@ import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch - -sealed interface GeminiStatus { - data object Initial : GeminiStatus - data class Generating( - val isCloud: Boolean, - val partialOutput: String = "", - val isTranslation: Boolean = false - ) : GeminiStatus - - data class Success( - val output: String, - val isCloud: Boolean, - val isTranslation: Boolean = false - ) : GeminiStatus - - data class Error(val message: String) : GeminiStatus -} - -@OptIn(PublicPreviewAPI::class) -data class GeminiHybridUiState( - val selectedMode: InferenceMode = InferenceMode.ONLY_ON_DEVICE, - val selectedTags: List = emptyList(), - val reviewText: String = "", - val reviewInferenceStatus: Int? = null, - val selectedLanguage: String = "Korean", - val selectedModelOption: OnDeviceModelOption = OnDeviceModelOption.STABLE, - val status: GeminiStatus = GeminiStatus.Initial +import kotlinx.serialization.json.Json + +data class GeminiHybridV4UiState( + val expense: Expense? = null, + val isScanning: Boolean = false, + val modelStatus: String = "Checking model status...", + val errorMessage: String? = null, + val receiptImageUri: String? = null ) -@PublicPreviewAPI +@OptIn(PublicPreviewAPI::class) @HiltViewModel class GeminiHybridV4ViewModel @Inject constructor() : ViewModel() { - private val _uiState = MutableStateFlow(GeminiHybridUiState()) - val uiState: StateFlow = _uiState.asStateFlow() - - val tags = listOf( - R.string.location, - R.string.view, - R.string.service, - R.string.comfort, - R.string.food, - R.string.spacious, - R.string.natural_light, + private val _uiState = MutableStateFlow( + GeminiHybridV4UiState() ) + val uiState: StateFlow = _uiState.asStateFlow() - val languageMap = mapOf( - "Korean" to R.string.gemini_hybrid_lang_korean, - "Spanish" to R.string.gemini_hybrid_lang_spanish, - "French" to R.string.gemini_hybrid_lang_french, - "German" to R.string.gemini_hybrid_lang_german + private val model = Firebase.ai(backend = GenerativeBackend.googleAI()).generativeModel( + modelName = "gemini-3.1-flash-lite-preview", + onDeviceConfig = OnDeviceConfig( + mode = InferenceMode.PREFER_ON_DEVICE, + modelOption = OnDeviceModelOption.PREVIEW) ) - fun setInferenceMode(mode: InferenceMode) { - _uiState.update { it.copy(selectedMode = mode) } - } - - fun setModelOption(option: OnDeviceModelOption) { - _uiState.update { it.copy(selectedModelOption = option) } + init { + checkAndDownloadModel() } - fun toggleTag(tagResId: Int) { - _uiState.update { state -> - val newTags = if (state.selectedTags.contains(tagResId)) { - state.selectedTags - tagResId - } else { - state.selectedTags + tagResId - } - state.copy(selectedTags = newTags) - } - } - - fun updateReviewText(text: String) { - _uiState.update { it.copy(reviewText = text) } - } - - fun setSelectedLanguage(language: String) { - _uiState.update { it.copy(selectedLanguage = language) } - } - - fun generateReview(tagStrings: List) { - if (tagStrings.isEmpty()) { - _uiState.update { it.copy(status = GeminiStatus.Error("Please select at least one tag")) } - return - } - + private fun checkAndDownloadModel() { viewModelScope.launch { - _uiState.update { - it.copy( - status = GeminiStatus.Generating( - isCloud = it.selectedMode == InferenceMode.ONLY_IN_CLOUD, - isTranslation = false - ) - ) - } try { - val prompt = - "Write a simple, short and generic hotel review positively covering the following themes: ${ - tagStrings.joinToString(", ") - }. Generate a generic review strictly from themes, don't hallucinate a hotel name or a location. Return only the review text." - - val model = Firebase.ai(backend = GenerativeBackend.googleAI()) - .generativeModel( - "gemini-2.5-flash-lite", - onDeviceConfig = OnDeviceConfig( - mode = _uiState.value.selectedMode, - modelOption = _uiState.value.selectedModelOption) - ) - model.onDeviceExtension?.checkStatus() - model.generateContentStream(prompt).collect { chunk -> - val isCloud = chunk.inferenceSource == InferenceSource.IN_CLOUD - _uiState.update { state -> - val currentStatus = state.status - val newStatus = if (currentStatus is GeminiStatus.Generating) { - currentStatus.copy( - isCloud = isCloud, - partialOutput = currentStatus.partialOutput + (chunk.text ?: "") - ) - } else { - GeminiStatus.Generating( - isCloud = isCloud, - partialOutput = chunk.text ?: "", - isTranslation = false - ) + val status = model.onDeviceExtension?.checkStatus() ?: return@launch + + updateStatus(status) + + if (status == OnDeviceModelStatus.DOWNLOADABLE) { + model.onDeviceExtension?.download()?.collect { downloadStatus -> + when (downloadStatus) { + is DownloadStatus.DownloadStarted -> { + _uiState.update { it.copy(modelStatus = "Downloading model...") } + } + + is DownloadStatus.DownloadInProgress -> { + val progress = downloadStatus.totalBytesDownloaded + _uiState.update { it.copy(modelStatus = "Downloading: $progress bytes downloaded") } + } + + is DownloadStatus.DownloadCompleted -> { + _uiState.update { it.copy(modelStatus = "Model ready") } + } + + is DownloadStatus.DownloadFailed -> { + _uiState.update { + it.copy( + modelStatus = "Download failed", errorMessage = "Model download failed" + ) + } + } } - state.copy(status = newStatus) - } - } - - val finalState = _uiState.value - val finalStatus = finalState.status - if (finalStatus is GeminiStatus.Generating) { - val output = finalStatus.partialOutput.trimEnd() - val inferenceStatusResId = if (finalStatus.isCloud) { - R.string.gemini_hybrid_generated_cloud - } else { - R.string.gemini_hybrid_generated_on_device - } - _uiState.update { - it.copy( - reviewText = output, - reviewInferenceStatus = inferenceStatusResId, - status = GeminiStatus.Success(output, finalStatus.isCloud, isTranslation = false) - ) } } } catch (e: Exception) { - Log.e("GeminiHybrid", "Inference failed", e) - _uiState.update { - it.copy(status = GeminiStatus.Error(e.localizedMessage ?: "Unknown error occurred")) - } + _uiState.update { it.copy(modelStatus = "Error checking status", errorMessage = e.message) } } } } - fun translate(text: String, language: String) { - if (text.isBlank()) { - _uiState.update { it.copy(status = GeminiStatus.Error("Text to translate cannot be empty")) } - return + private fun updateStatus(status: OnDeviceModelStatus) { + val statusText = when (status) { + OnDeviceModelStatus.AVAILABLE -> "Model available" + OnDeviceModelStatus.DOWNLOADABLE -> "Model downloadable" + OnDeviceModelStatus.DOWNLOADING -> "Model downloading..." + OnDeviceModelStatus.UNAVAILABLE -> "On-device model unavailable" + else -> "Unknown" } + _uiState.update { it.copy(modelStatus = statusText) } + } + fun scanReceipt(bitmap: Bitmap, uriString: String) { viewModelScope.launch { - _uiState.update { - it.copy( - status = GeminiStatus.Generating( - isCloud = it.selectedMode == InferenceMode.ONLY_IN_CLOUD, - isTranslation = true - ) - ) - } + _uiState.update { it.copy(isScanning = true, errorMessage = null, receiptImageUri = uriString) } try { - val prompt = - "Translate the following text to $language. Return ONLY the translated text, no explanations:\n\n$text" - - val model = Firebase.ai(backend = GenerativeBackend.googleAI()) - .generativeModel( - "gemini-2.5-flash-lite", - onDeviceConfig = OnDeviceConfig(mode = _uiState.value.selectedMode) + val prompt = content { + image(bitmap) + text( + """ + Extract the store name and the total price from this receipt. Store names are usually at the top of the document, first thing in them. Total price is usually indicated by text like "Total" or "Total Due" + Output only in JSON format containg 2 fields '{name,price}'. + Do not include any currency signs or backticks or any text around it. + Use dots for decimals. + Examples: + - {"name": "FakeStore", "price": "2.0"} + - {"name": "SomeMarket", "price": "3.5"} + """.trimIndent() ) - - model.generateContentStream(prompt).collect { chunk -> - val isCloud = chunk.inferenceSource == InferenceSource.IN_CLOUD - _uiState.update { state -> - val currentStatus = state.status - val newStatus = if (currentStatus is GeminiStatus.Generating) { - currentStatus.copy( - isCloud = isCloud, - partialOutput = currentStatus.partialOutput + (chunk.text ?: "") - ) - } else { - GeminiStatus.Generating( - isCloud = isCloud, - partialOutput = chunk.text ?: "", - isTranslation = true - ) - } - state.copy(status = newStatus) - } } - val finalState = _uiState.value - val finalStatus = finalState.status - if (finalStatus is GeminiStatus.Generating) { - _uiState.update { - it.copy( - status = GeminiStatus.Success( - finalStatus.partialOutput, - finalStatus.isCloud, - isTranslation = true - ) - ) - } + val response = model.generateContent(prompt) + val text = response.text + val inferenceMode = if (response.inferenceSource == InferenceSource.ON_DEVICE) { + "On-device" + } else { + "Cloud" } - } catch (e: Exception) { - Log.e("GeminiHybrid", "Inference failed", e) - _uiState.update { - it.copy(status = GeminiStatus.Error(e.localizedMessage ?: "Unknown error occurred")) + Log.d("HybridVM", "$inferenceMode response: $text") + if (text != null) { + parseAndAddExpense(text, inferenceMode) + } else { + _uiState.update { it.copy(errorMessage = "Could not extract data") } } + } catch (e: Exception) { + _uiState.update { it.copy(errorMessage = "Error: ${e.message}") } + } finally { + _uiState.update { it.copy(isScanning = false) } } } } - fun reset() { - _uiState.value = GeminiHybridUiState() + private fun parseAndAddExpense(text: String, inferenceMode: String) { + val json = text + // The on-device model sometimes outputs backticks, so we remove those + .replace("```json", "") + .replace("```", "") + try { + val newExpense = Json.decodeFromString(json).copy(inferenceMode = inferenceMode) + _uiState.update { it.copy(expense = newExpense) } + } catch (e: Exception) { + _uiState.update { it.copy(errorMessage = e.localizedMessage) } + } } } diff --git a/samples/gemini-hybrid-v4/src/main/res/values/strings.xml b/samples/gemini-hybrid-v4/src/main/res/values/strings.xml index d8313289..1b9c8b52 100644 --- a/samples/gemini-hybrid-v4/src/main/res/values/strings.xml +++ b/samples/gemini-hybrid-v4/src/main/res/values/strings.xml @@ -1,31 +1,9 @@ - Hybrid Inference - Inference with Firebase Hybrid SDK using either Gemini Nano on-device or Gemini Flash in the Cloud. - Hotel review generation - Generate Review - Translate - Korean - Spanish - French - German - Generating on-device… - Generating in cloud… - Generated in the cloud - Generated on device - PREFER ON-DEVICE - PREFER CLOUD - ONLY ON-DEVICE - ONLY CLOUD - LOCATION - VIEW - SERVICE - COMFORT - FOOD - SPACIOUS - NATURAL LIGHT - Select topics for your review: - STABLE - PREVIEW - PREVIEW FAST + Hybrid Inference (Receipt Scanner) + Scan receipts and extract expenses using either Gemini Nano on-device or Gemini Flash in the Cloud. + Hybrid AI Status + Scanned Receipts + No expenses yet. Scan a receipt to add one. + Scan Receipt From d4888606200efab415a535b1858af24821833dfc Mon Sep 17 00:00:00 2001 From: Rodrigo Lazo Paz Date: Wed, 6 May 2026 16:04:26 -0400 Subject: [PATCH 4/5] Added UI with model selection --- .../geminihybridv4/GeminiHybridV4Screen.kt | 46 +++++++++++++++++++ .../geminihybridv4/GeminiHybridV4ViewModel.kt | 28 ++++++++--- .../src/main/res/values/strings.xml | 3 ++ 3 files changed, 71 insertions(+), 6 deletions(-) diff --git a/samples/gemini-hybrid-v4/src/main/java/com/android/ai/samples/geminihybridv4/GeminiHybridV4Screen.kt b/samples/gemini-hybrid-v4/src/main/java/com/android/ai/samples/geminihybridv4/GeminiHybridV4Screen.kt index 18052c1e..ddc33740 100644 --- a/samples/gemini-hybrid-v4/src/main/java/com/android/ai/samples/geminihybridv4/GeminiHybridV4Screen.kt +++ b/samples/gemini-hybrid-v4/src/main/java/com/android/ai/samples/geminihybridv4/GeminiHybridV4Screen.kt @@ -89,6 +89,7 @@ import com.android.ai.theme.AISampleCatalogTheme import com.android.ai.theme.surfaceContainerHighestLight import com.android.ai.uicomponent.SampleDetailTopAppBar import com.google.firebase.ai.type.PublicPreviewAPI +import com.google.firebase.ai.OnDeviceModelOption import java.io.File import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext @@ -218,6 +219,11 @@ fun GeminiHybridV4Screen(viewModel: GeminiHybridV4ViewModel = hiltViewModel()) { color = MaterialTheme.colorScheme.onSecondaryContainer ) } + Spacer(modifier = Modifier.weight(1f)) + ModelOptionDropdown( + selectedOption = uiState.selectedModelOption, + onOptionSelected = viewModel::setModelOption + ) } } @@ -421,3 +427,43 @@ private fun getTmpFileUri(context: Context): Uri { } return FileProvider.getUriForFile(context, "${context.packageName}.provider", tmpFile) } + +@Composable +fun ModelOptionDropdown( + selectedOption: OnDeviceModelOption, + onOptionSelected: (OnDeviceModelOption) -> Unit, + modifier: Modifier = Modifier, +) { + var expanded by remember { mutableStateOf(false) } + val options = listOf( + OnDeviceModelOption.STABLE to stringResource(R.string.gemini_hybrid_option_stable), + OnDeviceModelOption.PREVIEW to stringResource(R.string.gemini_hybrid_option_preview), + OnDeviceModelOption.PREVIEW_FAST to stringResource(R.string.gemini_hybrid_option_preview_fast), + ) + val selectedText = options.find { it.first == selectedOption }?.second ?: "" + + Box(modifier = modifier) { + TextButton(onClick = { expanded = true }) { + Text(selectedText) + Spacer(modifier = Modifier.width(4.dp)) + Icon( + imageVector = Icons.Default.ArrowDropDown, + contentDescription = null + ) + } + DropdownMenu( + expanded = expanded, + onDismissRequest = { expanded = false } + ) { + options.forEach { (option, label) -> + DropdownMenuItem( + text = { Text(label) }, + onClick = { + onOptionSelected(option) + expanded = false + } + ) + } + } + } +} diff --git a/samples/gemini-hybrid-v4/src/main/java/com/android/ai/samples/geminihybridv4/GeminiHybridV4ViewModel.kt b/samples/gemini-hybrid-v4/src/main/java/com/android/ai/samples/geminihybridv4/GeminiHybridV4ViewModel.kt index 25fb63fe..7d8081a4 100644 --- a/samples/gemini-hybrid-v4/src/main/java/com/android/ai/samples/geminihybridv4/GeminiHybridV4ViewModel.kt +++ b/samples/gemini-hybrid-v4/src/main/java/com/android/ai/samples/geminihybridv4/GeminiHybridV4ViewModel.kt @@ -13,6 +13,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +@file:OptIn(PublicPreviewAPI::class) + package com.android.ai.samples.geminihybridv4 import android.graphics.Bitmap @@ -37,6 +39,7 @@ import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch +import kotlinx.coroutines.Job import kotlinx.serialization.json.Json data class GeminiHybridV4UiState( @@ -44,7 +47,8 @@ data class GeminiHybridV4UiState( val isScanning: Boolean = false, val modelStatus: String = "Checking model status...", val errorMessage: String? = null, - val receiptImageUri: String? = null + val receiptImageUri: String? = null, + val selectedModelOption: OnDeviceModelOption = OnDeviceModelOption.PREVIEW ) @OptIn(PublicPreviewAPI::class) @@ -55,19 +59,23 @@ class GeminiHybridV4ViewModel @Inject constructor() : ViewModel() { ) val uiState: StateFlow = _uiState.asStateFlow() - private val model = Firebase.ai(backend = GenerativeBackend.googleAI()).generativeModel( + private fun getModel(option: OnDeviceModelOption) = Firebase.ai(backend = GenerativeBackend.googleAI()).generativeModel( modelName = "gemini-3.1-flash-lite-preview", onDeviceConfig = OnDeviceConfig( mode = InferenceMode.PREFER_ON_DEVICE, - modelOption = OnDeviceModelOption.PREVIEW) + modelOption = option) ) init { - checkAndDownloadModel() + checkAndDownloadModel(_uiState.value.selectedModelOption) } - private fun checkAndDownloadModel() { - viewModelScope.launch { + private var downloadJob: Job? = null + + private fun checkAndDownloadModel(option: OnDeviceModelOption) { + downloadJob?.cancel() + downloadJob = viewModelScope.launch { + val model = getModel(option) try { val status = model.onDeviceExtension?.checkStatus() ?: return@launch @@ -116,6 +124,13 @@ class GeminiHybridV4ViewModel @Inject constructor() : ViewModel() { _uiState.update { it.copy(modelStatus = statusText) } } + fun setModelOption(option: OnDeviceModelOption) { + if (_uiState.value.selectedModelOption != option) { + _uiState.update { it.copy(selectedModelOption = option) } + checkAndDownloadModel(option) + } + } + fun scanReceipt(bitmap: Bitmap, uriString: String) { viewModelScope.launch { _uiState.update { it.copy(isScanning = true, errorMessage = null, receiptImageUri = uriString) } @@ -135,6 +150,7 @@ class GeminiHybridV4ViewModel @Inject constructor() : ViewModel() { ) } + val model = getModel(_uiState.value.selectedModelOption) val response = model.generateContent(prompt) val text = response.text val inferenceMode = if (response.inferenceSource == InferenceSource.ON_DEVICE) { diff --git a/samples/gemini-hybrid-v4/src/main/res/values/strings.xml b/samples/gemini-hybrid-v4/src/main/res/values/strings.xml index 1b9c8b52..655644ba 100644 --- a/samples/gemini-hybrid-v4/src/main/res/values/strings.xml +++ b/samples/gemini-hybrid-v4/src/main/res/values/strings.xml @@ -6,4 +6,7 @@ Scanned Receipts No expenses yet. Scan a receipt to add one. Scan Receipt + STABLE + PREVIEW + PREVIEW FAST From 3ea9cde6631fb45e4f5f37294453d7f5e63163e3 Mon Sep 17 00:00:00 2001 From: Rodrigo Lazo Paz Date: Fri, 8 May 2026 14:38:27 -0400 Subject: [PATCH 5/5] make source more clear --- .../geminihybridv4/GeminiHybridV4Screen.kt | 29 +++++++++++++++---- .../geminihybridv4/GeminiHybridV4ViewModel.kt | 4 +-- 2 files changed, 26 insertions(+), 7 deletions(-) diff --git a/samples/gemini-hybrid-v4/src/main/java/com/android/ai/samples/geminihybridv4/GeminiHybridV4Screen.kt b/samples/gemini-hybrid-v4/src/main/java/com/android/ai/samples/geminihybridv4/GeminiHybridV4Screen.kt index ddc33740..868667ba 100644 --- a/samples/gemini-hybrid-v4/src/main/java/com/android/ai/samples/geminihybridv4/GeminiHybridV4Screen.kt +++ b/samples/gemini-hybrid-v4/src/main/java/com/android/ai/samples/geminihybridv4/GeminiHybridV4Screen.kt @@ -55,6 +55,8 @@ import androidx.compose.ui.window.Dialog import android.graphics.Bitmap import androidx.compose.material.icons.filled.Image import androidx.compose.material.icons.filled.ArrowDropDown +import androidx.compose.material.icons.filled.Cloud +import androidx.compose.material.icons.filled.Smartphone import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.CameraAlt import androidx.compose.material.icons.automirrored.filled.ReceiptLong @@ -90,6 +92,8 @@ import com.android.ai.theme.surfaceContainerHighestLight import com.android.ai.uicomponent.SampleDetailTopAppBar import com.google.firebase.ai.type.PublicPreviewAPI import com.google.firebase.ai.OnDeviceModelOption +import com.google.firebase.ai.type.Content +import com.google.firebase.ai.type.asTextOrNull import java.io.File import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext @@ -344,11 +348,26 @@ fun ExpenseResultUi( } if (expense.inferenceMode.isNotEmpty()) { - Text( - text = "Extracted via ${expense.inferenceMode}", - style = MaterialTheme.typography.labelSmall, - color = Color.Gray - ) + val isCloud = expense.inferenceMode.equals("Cloud", ignoreCase = true) + val icon = if (isCloud) Icons.Default.Cloud else Icons.Default.Smartphone + val tint = if (isCloud) MaterialTheme.colorScheme.secondary else MaterialTheme.colorScheme.tertiary + Row( + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(6.dp) + ) { + Icon( + imageVector = icon, + contentDescription = null, + tint = tint, + modifier = Modifier.size(18.dp) + ) + Text( + text = "Extracted via ${expense.inferenceMode}", + style = MaterialTheme.typography.bodyMedium, + fontWeight = FontWeight.SemiBold, + color = tint + ) + } } if (hasImage) { diff --git a/samples/gemini-hybrid-v4/src/main/java/com/android/ai/samples/geminihybridv4/GeminiHybridV4ViewModel.kt b/samples/gemini-hybrid-v4/src/main/java/com/android/ai/samples/geminihybridv4/GeminiHybridV4ViewModel.kt index 7d8081a4..6917606c 100644 --- a/samples/gemini-hybrid-v4/src/main/java/com/android/ai/samples/geminihybridv4/GeminiHybridV4ViewModel.kt +++ b/samples/gemini-hybrid-v4/src/main/java/com/android/ai/samples/geminihybridv4/GeminiHybridV4ViewModel.kt @@ -60,9 +60,9 @@ class GeminiHybridV4ViewModel @Inject constructor() : ViewModel() { val uiState: StateFlow = _uiState.asStateFlow() private fun getModel(option: OnDeviceModelOption) = Firebase.ai(backend = GenerativeBackend.googleAI()).generativeModel( - modelName = "gemini-3.1-flash-lite-preview", + modelName = "gemini-3.1-flash-lite", onDeviceConfig = OnDeviceConfig( - mode = InferenceMode.PREFER_ON_DEVICE, + mode = InferenceMode.PREFER_IN_CLOUD, modelOption = option) )