From c66162659b4673d116f5afaf2be60428a2e3a9f5 Mon Sep 17 00:00:00 2001 From: timklge <2026103+timklge@users.noreply.github.com> Date: Thu, 6 Mar 2025 22:40:15 +0100 Subject: [PATCH] Add touchable back button, autosave (#23) --- .../de/timklge/karoopowerbar/MainActivity.kt | 2 +- .../de/timklge/karoopowerbar/Settings.kt | 6 - .../karoopowerbar/screens/MainScreen.kt | 472 ++++++++++-------- app/src/main/res/drawable/back.png | Bin 0 -> 28042 bytes 4 files changed, 274 insertions(+), 206 deletions(-) create mode 100644 app/src/main/res/drawable/back.png diff --git a/app/src/main/kotlin/de/timklge/karoopowerbar/MainActivity.kt b/app/src/main/kotlin/de/timklge/karoopowerbar/MainActivity.kt index 6924903..be19d92 100644 --- a/app/src/main/kotlin/de/timklge/karoopowerbar/MainActivity.kt +++ b/app/src/main/kotlin/de/timklge/karoopowerbar/MainActivity.kt @@ -20,7 +20,7 @@ class MainActivity : ComponentActivity() { setContent { AppTheme { - MainScreen() + MainScreen(::finish) } } diff --git a/app/src/main/kotlin/de/timklge/karoopowerbar/Settings.kt b/app/src/main/kotlin/de/timklge/karoopowerbar/Settings.kt index d8ba1c6..2abcbd1 100644 --- a/app/src/main/kotlin/de/timklge/karoopowerbar/Settings.kt +++ b/app/src/main/kotlin/de/timklge/karoopowerbar/Settings.kt @@ -38,12 +38,6 @@ data class PowerbarSettings( } } -suspend fun saveSettings(context: Context, settings: PowerbarSettings) { - context.dataStore.edit { t -> - t[settingsKey] = Json.encodeToString(settings) - } -} - fun Context.streamSettings(): Flow { return dataStore.data.map { settingsJson -> try { diff --git a/app/src/main/kotlin/de/timklge/karoopowerbar/screens/MainScreen.kt b/app/src/main/kotlin/de/timklge/karoopowerbar/screens/MainScreen.kt index 60b0405..a127ff1 100644 --- a/app/src/main/kotlin/de/timklge/karoopowerbar/screens/MainScreen.kt +++ b/app/src/main/kotlin/de/timklge/karoopowerbar/screens/MainScreen.kt @@ -2,8 +2,13 @@ package de.timklge.karoopowerbar.screens import android.content.Intent import android.provider.Settings +import android.util.Log +import androidx.activity.compose.BackHandler +import androidx.compose.foundation.Image import androidx.compose.foundation.background +import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer @@ -12,15 +17,13 @@ import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.foundation.verticalScroll import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Build -import androidx.compose.material.icons.filled.Done -import androidx.compose.material3.AlertDialog -import androidx.compose.material3.Button import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.FilledTonalButton import androidx.compose.material3.Icon @@ -30,6 +33,7 @@ import androidx.compose.material3.Switch import androidx.compose.material3.Text import androidx.compose.material3.TopAppBar import androidx.compose.runtime.Composable +import androidx.compose.runtime.DisposableEffect import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableIntStateOf @@ -39,15 +43,22 @@ import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.focus.FocusState +import androidx.compose.ui.focus.onFocusEvent import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.painterResource import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import androidx.core.content.ContextCompat.startActivity +import androidx.datastore.preferences.core.edit import androidx.lifecycle.compose.LifecycleResumeEffect import de.timklge.karoopowerbar.CustomProgressBarSize +import de.timklge.karoopowerbar.KarooPowerbarExtension import de.timklge.karoopowerbar.PowerbarSettings -import de.timklge.karoopowerbar.saveSettings +import de.timklge.karoopowerbar.R +import de.timklge.karoopowerbar.dataStore +import de.timklge.karoopowerbar.settingsKey import de.timklge.karoopowerbar.streamSettings import de.timklge.karoopowerbar.streamUserProfile import io.hammerhead.karooext.KarooSystemService @@ -56,6 +67,9 @@ import kotlinx.coroutines.delay import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.launch +import kotlinx.coroutines.runBlocking +import kotlinx.serialization.encodeToString +import kotlinx.serialization.json.Json import kotlin.math.roundToInt enum class SelectedSource(val id: String, val label: String) { @@ -74,7 +88,7 @@ enum class SelectedSource(val id: String, val label: String) { @OptIn(ExperimentalMaterial3Api::class) @Composable -fun MainScreen() { +fun MainScreen(onFinish: () -> Unit) { var karooConnected by remember { mutableStateOf(false) } val ctx = LocalContext.current val coroutineScope = rememberCoroutineScope() @@ -83,7 +97,6 @@ fun MainScreen() { var bottomSelectedSource by remember { mutableStateOf(SelectedSource.POWER) } var topSelectedSource by remember { mutableStateOf(SelectedSource.NONE) } - var savedDialogVisible by remember { mutableStateOf(false) } var showAlerts by remember { mutableStateOf(false) } var givenPermissions by remember { mutableStateOf(false) } @@ -109,6 +122,46 @@ fun MainScreen() { var profileMinPower by remember { mutableIntStateOf(0) } var profileMaxPower by remember { mutableIntStateOf(0) } + var anyFieldHasFocus by remember { mutableStateOf(false) } + + suspend fun updateSettings(){ + Log.d(KarooPowerbarExtension.TAG, "Saving settings") + + val minSpeedSetting = (minSpeed.toIntOrNull()?.toFloat()?.div((if(isImperial) 2.23694f else 3.6f))) ?: PowerbarSettings.defaultMinSpeedMs + val maxSpeedSetting = (maxSpeed.toIntOrNull()?.toFloat()?.div((if(isImperial) 2.23694f else 3.6f))) ?: PowerbarSettings.defaultMaxSpeedMs + + val newSettings = PowerbarSettings( + source = bottomSelectedSource, topBarSource = topSelectedSource, + onlyShowWhileRiding = onlyShowWhileRiding, showLabelOnBars = showLabelOnBars, + useZoneColors = colorBasedOnZones, + minCadence = minCadence.toIntOrNull() ?: PowerbarSettings.defaultMinCadence, + maxCadence = maxCadence.toIntOrNull() ?: PowerbarSettings.defaultMaxCadence, + minSpeed = minSpeedSetting, maxSpeed = maxSpeedSetting, + minPower = customMinPower.toIntOrNull(), + maxPower = customMaxPower.toIntOrNull(), + minHr = customMinHr.toIntOrNull(), + maxHr = customMaxHr.toIntOrNull(), + barSize = barSize, + useCustomPowerRange = useCustomPowerRange, + useCustomHrRange = useCustomHrRange, + ) + + ctx.dataStore.edit { t -> + t[settingsKey] = Json.encodeToString(newSettings) + } + } + + fun updateFocus(focusState: FocusState){ + val fieldGotFocus = focusState.isFocused + // Only save settings when truly losing focus (not because another field gained focus) + if (!fieldGotFocus && anyFieldHasFocus) { + anyFieldHasFocus = false + coroutineScope.launch { updateSettings() } + } else if (fieldGotFocus) { + anyFieldHasFocus = true + } + } + LaunchedEffect(Unit) { karooSystem.streamUserProfile().distinctUntilChanged().collect { profileData -> isImperial = profileData.preferredUnit.distance == UserProfile.PreferredUnit.UnitType.IMPERIAL @@ -118,7 +171,6 @@ fun MainScreen() { profileMaxPower = profileData.powerZones.last().min + 50 } } - LaunchedEffect(isImperial) { givenPermissions = Settings.canDrawOverlays(ctx) @@ -164,244 +216,266 @@ fun MainScreen() { } - Column(modifier = Modifier + Box(modifier = Modifier.fillMaxSize()){ + Column(modifier = Modifier .fillMaxSize() .background(MaterialTheme.colorScheme.background)) { - TopAppBar(title = { Text("Powerbar") }) - Column(modifier = Modifier - .padding(5.dp) - .verticalScroll(rememberScrollState()) - .fillMaxWidth(), verticalArrangement = Arrangement.spacedBy(10.dp)) { + TopAppBar(title = { Text("Powerbar") }) + Column(modifier = Modifier + .padding(5.dp) + .verticalScroll(rememberScrollState()) + .fillMaxWidth(), verticalArrangement = Arrangement.spacedBy(10.dp)) { - apply { - val dropdownOptions = SelectedSource.entries.toList().map { unit -> DropdownOption(unit.id, unit.label) } - val dropdownInitialSelection by remember(bottomSelectedSource) { - mutableStateOf(dropdownOptions.find { option -> option.id == bottomSelectedSource.id }!!) - } - Dropdown(label = "Bottom Bar", options = dropdownOptions, selected = dropdownInitialSelection) { selectedOption -> - bottomSelectedSource = SelectedSource.entries.find { unit -> unit.id == selectedOption.id }!! - } - } - - apply { - val dropdownOptions = SelectedSource.entries.toList().map { unit -> DropdownOption(unit.id, unit.label) } - val dropdownInitialSelection by remember(topSelectedSource) { - mutableStateOf(dropdownOptions.find { option -> option.id == topSelectedSource.id }!!) - } - Dropdown(label = "Top Bar", options = dropdownOptions, selected = dropdownInitialSelection) { selectedOption -> - topSelectedSource = SelectedSource.entries.find { unit -> unit.id == selectedOption.id }!! - } - } - - apply { - val dropdownOptions = CustomProgressBarSize.entries.toList().map { unit -> DropdownOption(unit.id, unit.label) } - val dropdownInitialSelection by remember(barSize) { - mutableStateOf(dropdownOptions.find { option -> option.id == barSize.id }!!) - } - Dropdown(label = "Bar Size", options = dropdownOptions, selected = dropdownInitialSelection) { selectedOption -> - barSize = CustomProgressBarSize.entries.find { unit -> unit.id == selectedOption.id }!! - } - } - - if (topSelectedSource == SelectedSource.SPEED || topSelectedSource == SelectedSource.SPEED_3S || - bottomSelectedSource == SelectedSource.SPEED || bottomSelectedSource == SelectedSource.SPEED_3S){ - - Row(verticalAlignment = Alignment.CenterVertically, modifier = Modifier.fillMaxWidth()) { - OutlinedTextField(value = minSpeed, modifier = Modifier - .weight(1f) - .absolutePadding(right = 2.dp), - onValueChange = { minSpeed = it }, - label = { Text("Min Speed") }, - suffix = { Text(if (isImperial) "mph" else "kph") }, - keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number), - singleLine = true - ) - - OutlinedTextField(value = maxSpeed, modifier = Modifier - .weight(1f) - .absolutePadding(left = 2.dp), - onValueChange = { maxSpeed = it }, - label = { Text("Max Speed") }, - suffix = { Text(if (isImperial) "mph" else "kph") }, - keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number), - singleLine = true - ) - } - } - - if (topSelectedSource.isPower() || bottomSelectedSource.isPower()){ - Row(verticalAlignment = Alignment.CenterVertically) { - Switch(checked = useCustomPowerRange, onCheckedChange = { useCustomPowerRange = it}) - Spacer(modifier = Modifier.width(10.dp)) - Text("Use custom power range") + apply { + val dropdownOptions = SelectedSource.entries.toList().map { unit -> DropdownOption(unit.id, unit.label) } + val dropdownInitialSelection by remember(bottomSelectedSource) { + mutableStateOf(dropdownOptions.find { option -> option.id == bottomSelectedSource.id }!!) + } + Dropdown(label = "Bottom Bar", options = dropdownOptions, selected = dropdownInitialSelection) { selectedOption -> + bottomSelectedSource = SelectedSource.entries.find { unit -> unit.id == selectedOption.id }!! + coroutineScope.launch { updateSettings() } + } } - if(useCustomPowerRange){ + apply { + val dropdownOptions = SelectedSource.entries.toList().map { unit -> DropdownOption(unit.id, unit.label) } + val dropdownInitialSelection by remember(topSelectedSource) { + mutableStateOf(dropdownOptions.find { option -> option.id == topSelectedSource.id }!!) + } + Dropdown(label = "Top Bar", options = dropdownOptions, selected = dropdownInitialSelection) { selectedOption -> + topSelectedSource = SelectedSource.entries.find { unit -> unit.id == selectedOption.id }!! + coroutineScope.launch { updateSettings() } + } + } + + apply { + val dropdownOptions = CustomProgressBarSize.entries.toList().map { unit -> DropdownOption(unit.id, unit.label) } + val dropdownInitialSelection by remember(barSize) { + mutableStateOf(dropdownOptions.find { option -> option.id == barSize.id }!!) + } + Dropdown(label = "Bar Size", options = dropdownOptions, selected = dropdownInitialSelection) { selectedOption -> + barSize = CustomProgressBarSize.entries.find { unit -> unit.id == selectedOption.id }!! + coroutineScope.launch { updateSettings() } + } + } + + if (topSelectedSource == SelectedSource.SPEED || topSelectedSource == SelectedSource.SPEED_3S || + bottomSelectedSource == SelectedSource.SPEED || bottomSelectedSource == SelectedSource.SPEED_3S){ + Row(verticalAlignment = Alignment.CenterVertically, modifier = Modifier.fillMaxWidth()) { - OutlinedTextField(value = customMinPower, modifier = Modifier + OutlinedTextField(value = minSpeed, modifier = Modifier .weight(1f) - .absolutePadding(right = 2.dp), - onValueChange = { customMinPower = it }, - label = { Text("Min Power", fontSize = 12.sp) }, - suffix = { Text("W") }, - placeholder = { Text("$profileMinPower") }, + .absolutePadding(right = 2.dp) + .onFocusEvent(::updateFocus), + onValueChange = { minSpeed = it }, + label = { Text("Min Speed") }, + suffix = { Text(if (isImperial) "mph" else "kph") }, keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number), singleLine = true ) - OutlinedTextField(value = customMaxPower, modifier = Modifier + OutlinedTextField(value = maxSpeed, modifier = Modifier .weight(1f) - .absolutePadding(left = 2.dp), - onValueChange = { customMaxPower = it }, - label = { Text("Max Power", fontSize = 12.sp) }, - suffix = { Text("W") }, - placeholder = { Text("$profileMaxPower") }, + .absolutePadding(left = 2.dp) + .onFocusEvent(::updateFocus), + onValueChange = { maxSpeed = it }, + label = { Text("Max Speed") }, + suffix = { Text(if (isImperial) "mph" else "kph") }, keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number), singleLine = true ) } } - } - if (topSelectedSource == SelectedSource.HEART_RATE || bottomSelectedSource == SelectedSource.HEART_RATE){ - Row(verticalAlignment = Alignment.CenterVertically) { - Switch(checked = useCustomHrRange, onCheckedChange = { useCustomHrRange = it}) - Spacer(modifier = Modifier.width(10.dp)) - Text("Use custom HR range") + if (topSelectedSource.isPower() || bottomSelectedSource.isPower()){ + Row(verticalAlignment = Alignment.CenterVertically) { + Switch(checked = useCustomPowerRange, onCheckedChange = { + useCustomPowerRange = it + coroutineScope.launch { updateSettings() } + }) + Spacer(modifier = Modifier.width(10.dp)) + Text("Use custom power range") + } + + if(useCustomPowerRange){ + Row(verticalAlignment = Alignment.CenterVertically, modifier = Modifier.fillMaxWidth()) { + OutlinedTextField(value = customMinPower, modifier = Modifier + .weight(1f) + .absolutePadding(right = 2.dp) + .onFocusEvent(::updateFocus), + onValueChange = { customMinPower = it }, + label = { Text("Min Power", fontSize = 12.sp) }, + suffix = { Text("W") }, + placeholder = { Text("$profileMinPower") }, + keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number), + singleLine = true + ) + + OutlinedTextField(value = customMaxPower, modifier = Modifier + .weight(1f) + .absolutePadding(left = 2.dp) + .onFocusEvent(::updateFocus), + onValueChange = { customMaxPower = it }, + label = { Text("Max Power", fontSize = 12.sp) }, + suffix = { Text("W") }, + placeholder = { Text("$profileMaxPower") }, + keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number), + singleLine = true + ) + } + } } - if (useCustomHrRange){ + if (topSelectedSource == SelectedSource.HEART_RATE || bottomSelectedSource == SelectedSource.HEART_RATE){ + Row(verticalAlignment = Alignment.CenterVertically) { + Switch(checked = useCustomHrRange, onCheckedChange = { + useCustomHrRange = it + coroutineScope.launch { updateSettings() } + }) + Spacer(modifier = Modifier.width(10.dp)) + Text("Use custom HR range") + } + + if (useCustomHrRange){ + Row(verticalAlignment = Alignment.CenterVertically, modifier = Modifier.fillMaxWidth()) { + OutlinedTextField(value = customMinHr, modifier = Modifier + .weight(1f) + .absolutePadding(right = 2.dp) + .onFocusEvent(::updateFocus), + onValueChange = { customMinHr = it }, + label = { Text("Min Hr") }, + suffix = { Text("bpm") }, + placeholder = { if(profileRestHr > 0) Text("$profileRestHr") else Unit }, + keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number), + singleLine = true + ) + + OutlinedTextField(value = customMaxHr, modifier = Modifier + .weight(1f) + .absolutePadding(left = 2.dp) + .onFocusEvent(::updateFocus), + onValueChange = { customMaxHr = it }, + label = { Text("Max Hr") }, + suffix = { Text("bpm") }, + placeholder = { if(profileMaxHr > 0) Text("$profileMaxHr") else Unit }, + keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number), + singleLine = true + ) + } + } + } + + if (bottomSelectedSource == SelectedSource.CADENCE || topSelectedSource == SelectedSource.CADENCE || + bottomSelectedSource == SelectedSource.CADENCE_3S || topSelectedSource == SelectedSource.CADENCE_3S){ + Row(verticalAlignment = Alignment.CenterVertically, modifier = Modifier.fillMaxWidth()) { - OutlinedTextField(value = customMinHr, modifier = Modifier + OutlinedTextField(value = minCadence, modifier = Modifier .weight(1f) - .absolutePadding(right = 2.dp), - onValueChange = { customMinHr = it }, - label = { Text("Min Hr") }, - suffix = { Text("bpm") }, - placeholder = { if(profileRestHr > 0) Text("$profileRestHr") else Unit }, + .absolutePadding(right = 2.dp) + .onFocusEvent(::updateFocus), + onValueChange = { minCadence = it }, + label = { Text("Min Cadence") }, + suffix = { Text("rpm") }, keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number), singleLine = true ) - OutlinedTextField(value = customMaxHr, modifier = Modifier + OutlinedTextField(value = maxCadence, modifier = Modifier .weight(1f) - .absolutePadding(left = 2.dp), - onValueChange = { customMaxHr = it }, - label = { Text("Max Hr") }, - suffix = { Text("bpm") }, - placeholder = { if(profileMaxHr > 0) Text("$profileMaxHr") else Unit }, + .absolutePadding(left = 2.dp) + .onFocusEvent(::updateFocus), + onValueChange = { maxCadence = it }, + label = { Text("Min Cadence") }, + suffix = { Text("rpm") }, keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number), singleLine = true ) } } - } - if (bottomSelectedSource == SelectedSource.CADENCE || topSelectedSource == SelectedSource.CADENCE || - bottomSelectedSource == SelectedSource.CADENCE_3S || topSelectedSource == SelectedSource.CADENCE_3S){ - - Row(verticalAlignment = Alignment.CenterVertically, modifier = Modifier.fillMaxWidth()) { - OutlinedTextField(value = minCadence, modifier = Modifier - .weight(1f) - .absolutePadding(right = 2.dp), - onValueChange = { minCadence = it }, - label = { Text("Min Cadence") }, - suffix = { Text("rpm") }, - keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number), - singleLine = true - ) - - OutlinedTextField(value = maxCadence, modifier = Modifier - .weight(1f) - .absolutePadding(left = 2.dp), - onValueChange = { maxCadence = it }, - label = { Text("Min Cadence") }, - suffix = { Text("rpm") }, - keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number), - singleLine = true - ) + Row(verticalAlignment = Alignment.CenterVertically) { + Switch(checked = colorBasedOnZones, onCheckedChange = { + colorBasedOnZones = it + coroutineScope.launch { updateSettings() } + }) + Spacer(modifier = Modifier.width(10.dp)) + Text("Color based on HR / power zones") } - } - Row(verticalAlignment = Alignment.CenterVertically) { - Switch(checked = colorBasedOnZones, onCheckedChange = { colorBasedOnZones = it}) - Spacer(modifier = Modifier.width(10.dp)) - Text("Color based on HR / power zones") - } + Row(verticalAlignment = Alignment.CenterVertically) { + Switch(checked = showLabelOnBars, onCheckedChange = { + showLabelOnBars = it + coroutineScope.launch { updateSettings() } + }) + Spacer(modifier = Modifier.width(10.dp)) + Text("Show value on bars") + } - Row(verticalAlignment = Alignment.CenterVertically) { - Switch(checked = showLabelOnBars, onCheckedChange = { showLabelOnBars = it}) - Spacer(modifier = Modifier.width(10.dp)) - Text("Show value on bars") - } + Row(verticalAlignment = Alignment.CenterVertically) { + Switch(checked = onlyShowWhileRiding, onCheckedChange = { + onlyShowWhileRiding = it + coroutineScope.launch { updateSettings() } + }) + Spacer(modifier = Modifier.width(10.dp)) + Text("Only show while riding") + } - Row(verticalAlignment = Alignment.CenterVertically) { - Switch(checked = onlyShowWhileRiding, onCheckedChange = { onlyShowWhileRiding = it}) - Spacer(modifier = Modifier.width(10.dp)) - Text("Only show while riding") - } + Spacer(modifier = Modifier.padding(30.dp)) - FilledTonalButton(modifier = Modifier - .fillMaxWidth() - .height(50.dp), onClick = { - val minSpeedSetting = (minSpeed.toIntOrNull()?.toFloat()?.div((if(isImperial) 2.23694f else 3.6f))) ?: PowerbarSettings.defaultMinSpeedMs - val maxSpeedSetting = (maxSpeed.toIntOrNull()?.toFloat()?.div((if(isImperial) 2.23694f else 3.6f))) ?: PowerbarSettings.defaultMaxSpeedMs - - val newSettings = PowerbarSettings( - source = bottomSelectedSource, topBarSource = topSelectedSource, - onlyShowWhileRiding = onlyShowWhileRiding, showLabelOnBars = showLabelOnBars, - useZoneColors = colorBasedOnZones, - minCadence = minCadence.toIntOrNull() ?: PowerbarSettings.defaultMinCadence, - maxCadence = maxCadence.toIntOrNull() ?: PowerbarSettings.defaultMaxCadence, - minSpeed = minSpeedSetting, maxSpeed = maxSpeedSetting, - minPower = customMinPower.toIntOrNull(), - maxPower = customMaxPower.toIntOrNull(), - minHr = customMinHr.toIntOrNull(), - maxHr = customMaxHr.toIntOrNull(), - barSize = barSize, - useCustomPowerRange = useCustomPowerRange, - useCustomHrRange = useCustomHrRange, - ) - - coroutineScope.launch { - saveSettings(ctx, newSettings) - savedDialogVisible = true + if (showAlerts){ + if(!karooConnected){ + Text(modifier = Modifier.padding(5.dp), text = "Could not read device status. Is your Karoo updated?") } - }) { - Icon(Icons.Default.Done, contentDescription = "Save") - Spacer(modifier = Modifier.width(5.dp)) - Text("Save") - } - if (showAlerts){ - if(!karooConnected){ - Text(modifier = Modifier.padding(5.dp), text = "Could not read device status. Is your Karoo updated?") - } + if (!givenPermissions) { + Text(modifier = Modifier.padding(5.dp), text = "You have not given permissions to show the power bar overlay. Please do so.") - if (!givenPermissions) { - Text(modifier = Modifier.padding(5.dp), text = "You have not given permissions to show the power bar overlay. Please do so.") - - FilledTonalButton(modifier = Modifier - .fillMaxWidth() - .height(50.dp), onClick = { - val myIntent = Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION) - startActivity(ctx, myIntent, null) - }) { - Icon(Icons.Default.Build, contentDescription = "Give permission") - Spacer(modifier = Modifier.width(5.dp)) - Text("Give permission") + FilledTonalButton(modifier = Modifier + .fillMaxWidth() + .height(50.dp), onClick = { + val myIntent = Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION) + startActivity(ctx, myIntent, null) + }) { + Icon(Icons.Default.Build, contentDescription = "Give permission") + Spacer(modifier = Modifier.width(5.dp)) + Text("Give permission") + } } } } } - } - if (savedDialogVisible){ - AlertDialog(onDismissRequest = { savedDialogVisible = false }, - confirmButton = { Button(onClick = { - savedDialogVisible = false - }) { Text("OK") } }, - text = { Text("Settings saved successfully.") } + Image( + painter = painterResource(id = R.drawable.back), + contentDescription = "Back", + modifier = Modifier + .align(Alignment.BottomStart) + .padding(bottom = 10.dp) + .size(54.dp) + .clickable { + onFinish() + } ) } + + DisposableEffect(Unit) { + onDispose { + runBlocking { + updateSettings() + } + } + } + + DisposableEffect(Unit) { + onDispose { + karooSystem.disconnect() + } + } + + BackHandler { + coroutineScope.launch { + updateSettings() + onFinish() + } + } } \ No newline at end of file diff --git a/app/src/main/res/drawable/back.png b/app/src/main/res/drawable/back.png new file mode 100644 index 0000000000000000000000000000000000000000..38d43cfe8554b8c2e760cbad3c7206c21064c879 GIT binary patch literal 28042 zcmeFXWl&tfx;Bct1`np=Ov_tNZQl=Y3x5UT@S?$UMFWG8pv#u%ON`TDMQ7)JMN4q66=eA1mNSqrxQd?xg% zmRV^XxuUZEVl(o?^yp{##FfHo6U>U{+5qH!SyIZHcl06pNWmQv_!D<}E&R)3xz)MX z80V=dhi`>NhPzfFb-R@2mqFJ^wB(3;*rsd>JCwyo0N=vILNK5~7LBKMy{ojhd>{TNSSAG3~PD@fGO3(tPLM$Uo(SRWf!?Ow3 zrc^S%K~;X5L`Wjv)3CNSZa&k~ucw2mBZ>DL-kBtu(5R^IHc2Xml=H>Pp!X%kN*gZT z9;8^0axo z(-T5x%^A005^k!p>?T3m?<}?}$z5F8Q#xraDz>EPZ06`sWS+wc!np1FtmhPJ5)00n z69R|M6}L2ov=HbA5m+W@+5(QnuS5-4#f8=x!#5?iV>bO_;qs00)LpZWz=P0ixE`yu zSB}@L?xsJIf9Wg6P{I2zb@#g~zP<<=p)@`IoT$8z8d4wTN9846=s1jk*d%e8DbVyo zNQF9xca+5&k%9uD5P8`6(%Vz#_S>IB=-3hJOs`Kf;x!O9kq4Q(LgOk>2&y%xs;uxA=c2BJ}!{g zJ}R1EA6u}H1)aDU#tScDK!82O&5Xv&-p;{Q*h`e|QLZrX{oyhv9nE7CH(OCUU1c>I zX-5|b4KD{T2N%1n7u18BP7H(Qg^Puyu!fA>KSTh(MCq*E+?<3tIXyi+IXrnd99^t9 zK|(@8oLtb_ZAb2N8c|$Ut1dE>I^osG|eTgG@7XM|U?-Iy&G! z%|GX7@1(5!FYykp|3Cr2gVW2*i4(-Z#c6NP`R_ek-DEugA^(`r|Fws!CSa$W8W2}U zcNZ{3)&t_;M*r_3EWrQj@8s@c_qZGjFek(gVh=QR1x5w^$B^<0%4+}W@c@Ap)ZXc_ z7XbEuv~+`7{ufyPF}H_1kIVV@i2&XICHFsC|8wn+je%Cm%EB^^VE2dVDaeS@J&Z4G z;RuFW2tR)1vosSDG85uw=LVaD*?D<*EZBw2%mvvkxVU+Fz~QS z40#X*2nVr;uZoyxXc9vg&sv&fQ988 zUF^*ObwcgUtRS3D4pxsh9sn1XR8tV8L^gFCY(U5e60m0BiQZQ$WCD4`40A z(k>7)H%AvuM@KtRx`!#zJoNn6ZDqimEX>@@WX#+kfKV=OK4C5%VJ>b>5by;g%)`qH zd~*F;dq)eXrT70^>j&eZdGS}}FQKl$_}-6~{#w*)i1XiHe}CFRA5Dpd=FupG&A@+` z;A-Xpv3Q&(AnWffu(g?k6$J1e|DfwXuS5SYRNxX22HZX8zx@3_FauQbKl%Cx z-To(sprQHSLH=9#{x4kr3)g=Of&W(H|7F+z!u8)m;J?-Qf7$i_8(bLwRWU*wfJDy| zs0VXxIq-p662)9mUIy;|;V-YFBpJAZ?(|a66(~Ua9^UY&tT^PrMN~HhWm(i+Ltdn%s#Ve9>Vu92ea zXEQA_7}TbCVDpB^(9@YGU$|lTeerzK5J-n|B}9=4pey z&fcf@OS99qoe6{SM?Ps(T;w7HUKhmVhf=qt-tju?OKasVe?*UoBLo9}!;|Wg3SXVf zj{p9SdnX%!vC3BMXZ$udJ$)6nXfJMi=!|La?>C)W72dcGo;_)B&36!WXK?*@D1t&q0L~yxV$#y*l2xVF%_LB590wZ+qxD&~wKmnb*cj1XM zGCylR@|YltIQ`p@1IU}?klCEVWshGz#2#}6i;NV@6@tIutL+i422mS3a1${n{CP6h7w?=My*rb=#UXZjt8;L2jqv^U#s8Q^Rs-plueja=dNIEYBtcxAtY zOSgHmrbYo@4QYOlk>A2bJ4{3o@gx;NI{fXEw*~N~Z$JcnvG~kf`ZRAM`||Y@7edPU zm_{s%DxNI4Z#7yxW6sA^p?sfXi=aQQZ?O#X+u^JUV(_FTqaS}p-ShPJDX*__Nlxnp zytX;-t+fgdKW=nuP@-NNMZq-cnC4Tj&loV;MfVX-=^2SpJ}#Wg7-G{`i74F~1^VWF z@C014L6s?9%soQoA7S;~;{z$3-6=%eEGrvR8k3UX1*X*=vTw``*MVw)S+CoINle&i^LpeEg~O zS2nL{C;R7(=pxsje2e@}^L0p;IOaAiTn&)%IVBXs(z@eevpxF9%5ovX^(O{ekP zRO{|h8TdPlD+y|3=B1$2ST%v7SI^w0K!=M`mRD3Oyu|(fUk~V#;=UfxquBi>kP(eI z!}7MPvf7m0b)nSNN;jbeD?JI837Zuzt^h^b1kWLm(sLsS$b83W?i)gzQ7kute7LWt~4|2@L!6`7X`*54l9$LqWI~zhkgs(sFawG~%4PDE6sNiXZ3^VzJaY#<&0ithsx?PjVH97f_AZZSIOats6lFZ$<>F?j-0HqtOG4OGT)9xR8~lo_Kk|w{a~MQt zL2p`EK-KVz2M43|tuHI>22nrwsXT2;AI!$p=n1}p3oYW&K53`ZY^T(@*3RO!xxV>Dfk=U# z77FPa{Lq;CT~4-J&R-h8;KjmRRaTeMYa*tmKu#jHRSSml$xrq0ytUPMMRmHonEVL0 zQL%V9>Vff>R;DPV;&8qZDm)lqp)z^5R^(YdefMZ;FP5}XO9%Pdn3PI!^7fc`vn9%m z?XNMk#ZcKhK9Q9=G2NZeF1sD)LUM^Tkt~6GMTvU+OgB!lLg5}BnJ;)~yGBDo3O*4G z+qyh`mIO%_O04AcyRi(E$p?g3t&HrY1H#SQq4#ASBKc+?#4$Dmn!0bM1}{>dVJ(sN z$lXEkS6#!)*%V#Rr*J^Ehexz!x0=T=Qm|_~D%hpZQ@tzZ6|#HqFyo3zu*l$N#>Uoy z`t%f+@S^m3AN$tNQ6FP>J=gv;zDa9B968pxa(;{0*fL68L$>k)uR)A(S4?Y2u3Jua zC}2j=Nc{Rw|7vg5DtRgSQ_P+i3HfRI3CxvX$3+eoG7QpE|31U@28E*yip{o^OnR+| zEZvpjH0Y|iZ(Rdb19o=+G71-|-^rKIRm8cHN`-E6x+9KW}}@ zLw(msh~UYI&an<6m~NJL0`V%nwD61Q&4Qq~GsXH!nC*tNSRW6G26AbALXru7+>low zEh>EO8K_6Tq)N4ED zMUijeR~kOP8abahy}ypF*D@_jvS=#uh@Z`C<&!_aT|mkeA9{hn&%&}d9&Pr$aubhc zq!j&=WtEd=xLUKs%SNIa*bmrJziqA$|5|ZCzgvJ33%wmT1H0VTatJr8^>;9eCV8UK z{^I!unp?xrjYp%xYYEu8xw({u59O6+6fK6xaEDRR-kOrAq6gp}q3U)eTt*7IhEyb| ziw?HYvV+M!f+tQ4&E}$s4mvJ+s3)py({nNH9?U)m&)O{2sx1?vCKIz}#YjghD^KgB z0D*z7mH@%1KMJe+m(zjyo@n3C^`epXF=w%o_G-bSydXWj$)BVusQY)=KRX8uUK4B2 ziH}oU5nT;d3<*3gRdq_PlP%^8QU~()Hn5|Va%B=jF&J=Qh)J7#@_U3P+kzrNrHV%1 zl@5s-;%g2;{%dYHp@@T*S(|pV>#g4YB8DVzck2WJb0vGr4~!NCWl0=KxEW1;Y11<_ zCck=KR*6=d{icvi4h3fUoXnc~EB!R(=0g7{s^$-*R2~hhcZR~PzHBI4FYrPudoLte z*QpI0Zr9(nJK|TXtE(c{;x-BDw)K4DBMLK^X$dLt`oL%w1;!=K^NG3yhxsQ`#Z$~+ zW$E_`CKVMyB8$8-#p+@#L0!LHNRq_)pIy;y5RV#VhI3r@E`M8e-*J$*ezvy#>?)_< zk`s`w>mu&Z;);G$Z7M(k{OxYQ6&gVqkZnnskA#;3J!cv#_6Vg39CJ zl&#?qTcsEN!NH$&^AJb>Qp@vhbL>UUd<+f18qRtgG048`x(?SmiiDHIx=J5 zyehoUYKQ8#9G!cw_gRw`$h7velU%tYraMB~d4feKTlv?X!rj*yL>{<~$HfK3Mf=D( zAN+7%{zXDu_et|;W!yZkFoaHSC)ffOk%J<4!B18*3wY8AWx1TDAT zJQyp{xsFWDP0e!;^f{E)EcYf)RPG(_T4g))3$=!*ttgADMGYG{jSHAPiM!}Zg3ko` z53$IlcQe;`u?KDgU2wtjItsALk*vSuk}S+&oZmv=b@VcE7q6NNqR%pQZBdjXg5i;M z^un-C$X=svADO1aS09F)o<{}hjc{F|KWk#?c96c<&JFay9QyOyHTyFW09ZPL7FE!` z(S*aLZPSa$Z^&-RlSDV;(Feho67j86spD$OEiHD*7S9+e6!#`iFOH-;OAv;amYV{g zG`E=5IkV>}Jx?Fpp+j#LQGhp_63$iu2a0?>*!FCAeV@9l4k;WjZV=q*? zBvQ9Q{V7E52`qOZ751p!QN;n0_)Aws4I%9XWHb1KxpQ6O9D>I@B!|eN>HUpk=z;#* zif?fi?)z8$=aHB&=u=aagWvS7q_hZv zDb+ptOr7`e*hTSHmbh^E=5xf~ME=As#&d28(hyu#&W+bD zkGG7T7W{MF50~Dkp2}27#Xdii*kYNCVBlb7*Qgrdz`(TG5U(y5LGOz8#UGuuL6czW z`(J4M7j#k4(XG_OH$qEU4IeR4s@AeK=BTzC2o4bZM>FR9*%oFiTV1zQ>qYDXV^sKYiIx!{y zLvUxXj-I-yA(0jYE1G1uGrLa}$P!(?i2bYq%8qss;zOoM9*ja{W|K0rJ(33iJx8$ zRVvoO;r05QNvlnlMd6ipPV1FBPcyEu!#*@en3V((>%Q`7DwuEUN5}Ok-A}yZIWv3k zAqAb$#G*J-dKI;=K#BcGOKExRk~|cqeHwGb3DTuW=7J20EJ+SM$noi;! zN&aVY*KZE7+wbkF9|EeZ|H<@UB&tS$^~RkGjfR~-n~EjBagX5K_{J)yVm-LTIh3LWs7!}*_WK(*5I9| zwSo$(T$6ZlKQwm3c;s9Z(-)r9ihnBjzRn3}Mt4}a!%mKgT>z{>P+R(UYb(Vf!4CJZ zkrI}{*zeOtsRcw)nY-2QJ4%6jk@ExJy=&Sp#_ELrOjA_7bOzvbWHxaG@g7{e_}hRp zx%4Su;ZqP}(F!Un=Mc z#p)N0k!nxurcO`^EFf2&pi+o`v%xzfICe#{)6|@P9loy(o9uo~g%&aQGxc20G%0@( zvB3*IyVnb57*X2as94NRXMaQVEAt>Ecb4Wh;fGJ`gBEjM7^bHp8_<-U27RC@8^rJ; z%qGDXrgQ5nL*ACiLy>v4w~%r?#-K*B6ec4m5z&V(_)6&Q@#2LwlBW^M?j@e)d6Lhw{ENTx31IAx7O| zdtHMqM<4g+GzB6J+x`1@+!Aaie?-U1c7+(I+dYzkip+W;P3K$>x678~n{1^6xTcci z8Cuj9nlFsFxfN>xncLDsOXGu127dbsmkEPd#QP$2v`5erM#QcdYHMGST7z_zd;F z_=u%Ee*XR0WC{pV??QJ;jfv}n5aEU&f*EWOdj>m8qVv=EeBZu(?8xLtJj|`Aw7fRK z%coi-mj2LiV){vFPc`CMTU|Sxo8q|cwNzZ|BYy(KEyP6@?~v;EEL|%YtsUJrFwSr_ zWr`R}MP)pD1Z^B^D*NUY{T4ouVk=Cmdyw3Lm&fT;?!zp5V=Y3wX`cQhQA}O3QaStcCY8TED=Z~33CW)fkGb`&h2x<05 zZ;fI-jl9umwxL$QVU7X8y?%Y1Mt08#_lx9ej~xuAdN5^rxl%E&5t~Yq=b1En9BTAZV>~b&>n6NSgGV?t6L|^wEHm-+s&bP`&3&6VnqNGc+4Y(*N{u z!;g9$PdeBWIl%ApsC&2*h)xTOMt6Os9XY(a2*JzxjD%^_t z8SI&dGnnDSUCb5l;}o9(f)3sx^w0hJ8!2*dboCl|40#BMNwhD5-%`vwo|;i^3{IaT z4e(5&OoXcR;HGr>^`ERf3RfNDPP8|*|7m*=A|vCgj42bjgH}-mgTxXtPFcu_Q*-Z( zT~R4g59+|#e|ZS|KEF&m4hQ$-3yiArUWMeNPRh@t21ZrkIG>AE?(kH(=qJN5f*FAW+-M~Pe33&o4*xJTWcNgVlXR`S95 z&h$n7I@XsGmGhG23zT(t9U4U)lo>qsQEK!XnpNj{T-h$g-VxLuhGAXR()H<@ zwf;q6bBl$su(e)kTH)m+B4gJ^=k&MAzfnSAEZ|gH(|PR8WdQ2YWlz^Y+uZbbe614< z@m-#-&!k{$K4UhM0hYfx<2R+5hALl#c@bi}ox1P>FRgZ+Y*#qMZ1Nzg{F|pH)EeWc zDVy1KHH`g}pH5kiG25?0A@yG$0kzbx-<~xHuF9jKn;dB4D7ZzFZWoe{;rL_m#c`~M zMi)`Mhq?j%(UW)PTkGP}3k}1~(919}Tu}51bw2HBXBbR23v|frk$ET{S!`?>SUnFS zCQ2kcBrnx*dX6+&;OYE-{5sxrTe5x!Y)Ao#K;he6=|m%=>F#e>yb{_Lv8NbTA8g-BLZ?F@AhjV7mY0ZE{1r#y5)bF(oK~M&Blb3>qX@8+G@wdU(t>d ztm4}6C9e*(Bz&R{5F^)czxt$??s2~uB{h?8zmxulYwV2U@^r4*eMa7?fHFCF>n{@# zMYgd|Fff&UGGiwk(1JHxHJC052Ae96EqR^>&+HoZ|i&>D@Fi&m0Z3#(n25z8hIG4nLDARpu zCHE*!-&904pnJ<(SdLR4P`R4wE5SjCR3dPoF4RgAKG#|kK$ zkTKu_ZgvYXjURcwnxRwuTo{e&z(uMe5#C7CQ=R=J0O^LR#2$m&g<_3-s8#hd)8C-i zSZIpPAAD)7G!#R*RxfkFF?Q~xHD-~sX~4)=2_3pq2Ne8Q<_imCwJK8vRCaau=F&)3 z-xNqf!7$9gyUs=nzZN}6%lM%2A2#ne&ZO3n-0%S-YRe0|vvhd2C2NudMlecofm%+~H5<~ShJ)F;}AlK0+ThW@7_Bz~OMq8DuiCbD9-3YAT zJl(CAYOZa#lts>Z zvmvq>KAwtvnLbFQZ`$%9-mG~zPnM{(u1&Ffba#l^m2X7@ zFY_MFr`;@hLRauBN9+5dhReNeOYabHIb47}X<*_NlElP3ozrxSy}KNHmtSopght+( z2LB2!asCTdF%eB!)zU|Ogs7nnsfbJaV_&bP$jdgRGWdJ`gOjxE6S?$?is(lV))cky zBhW}6u@>Gel!b0W8pu3KJbW{}b4%wTXwz>B=lq=(B_HF50FM50HEPit7cC;tr0vg) zpq~W}2@BQ|yDUkS;Fq7_JgFb6>4Hx05eWgbrTRd1wJJL6h)EP5uhkM36bARt({G9sQUKz%4jR- z@<-B2gSMhB)Vjzu8VwU_ZC(>MWjVF|)lPzf4yGw-a^V(j$ax#-Vd%)m9|q4TWFAY7 zkzeiGr+dUkV=v%j*fOwYfD;Ne0SR9Ew<1nPj(wDmZodM#I7SMAI7V;j$fOCk8dfYp zJukZ(x&<4hy3qqY?uca}A6jCu8Wb#4?|ml%^)T(My9b>k*8ePUZzP7bKKg6fW!4rj zMLDg&;7e*Z5>;HC?JqyiUkINctrLq0z|1HOPZ#C=L^Q$3y4MZc&CmHM&FW!WnP*=Km?8P{mL^kJzje<;7G zE)->{csT_M9m9HVj&#eG&|q>4QdeKCY-dps9rJ}hED%PU!ujF9r=ISe>Kg4&z(gz4 zEk~oYf1tiprX}eHzzk2JXmLDV7oY9nuYxE;4@g_><<`y{G+g~rmGFVZVil#accy4ZaVPwMr%iXpApgxj*$ zrF=twK;`nXcl}Pw#EQ>}k9b}PN#QcT&hA6I@J7(;oaAV@uFH0vc8_zVof+3x%q%7a z_bTEWNmt~>>_07BfLR@@+hz{%2g^j*#9s;bpsivE&(c!9mkY zGn1))2lcVy$n1%ntD{=Bc}+k-OMdPqZaMjD4ndRMn*7`-`-m(x-dHaQLZ*;Z&W2cz zUQVx3nDe$si25uVO{i3Kf~r!*IdfgXTk&%sE#Ngo*^WfduCo{H(! zIi1F8>gsw|R;WUIg5C$@_NZLtG$hSL2g$vr0m{IJg%8N*7z28mOJ0klxjdZQ0~YO& z3q)Q2Li}oH&lc7xhKAXOlI(G^)o)xBIM>JPi1&Yv?&EN&#ng0&0Eu^Z{vIZsTRS^w ztPv_Dq0olR8~f#_J;OS2ZG{D$e-_NIp0Nj0@S^VKpT%Cg%iYS}Bpf;4d&R^A#kE2( zy|QsjB+j7tcEK^292^{icgMlWxYU9H_c^6(?^Kw+Sn*iON@nWbh={b4VhtHo92-t5 zEncDR&|4JdtZd7itboHQ&NMP=W ziVI2c@9o0#_tivMpa~V&CvtTDGML-ZU3mca8 zk8^uO`!KudQUAjPIISa*mm<6CdeJb5U*XNsHa!#XeNB7AG_Jma&eF zs|fu$4)%0hb@@FWs4-@>4l|kw(iq1mHGicwwy?Ae=54q1<0)7=q$?=W8>W-@gkM0L zf^9$PLGU7Mi!!gcm?({8p#RoRb0`zD2Bns6%Ka%$*s-!=++m_^HKWyU?KRrJKxSoS zl}$zM>ftkd{++Sk8c9D?K?-Y8)7cEmyF_}$;p;1cC$=Evh8VLBYhoEj04#5R1R^dA z*yoQ8aaR2}fm1xRx!`K#Fmia#F_PY(caj$P|#ICM%Z1&z>X6L?l_V*w4 zMwqzE$6KiX0JNS8c(schzR9;@b-S0Z-{d+2CrddVDOpF9RpW+a3YGWTG z(c4)*U_R8n-P{@n-LAKnBmVRjR-!bAY?0M^Z~wLIn`=D%KwjxcY|uV{*x1EkB zdO_PIC5G9#JT0A4`#zvVv?nq4r41;Y#>c1M6>)G8O)oDl=65eYpW#5$*f(ZocEByJ zhEdTzF5A(bm+M?gDC*>s^_gz$?;u?9#67NQ-qf+8Z{x3_RQId_Lm(E zO^mo%F$}dLm^hXDFeA!mECJ!h9l~pCG=7v+lqaV%thME`nU+1o6S7C;iEw4)dN{!L zrI0z4rtOL;h&0ArJvU!W*6i}|7 zo8YnWv1T%CW-H_?miksgJloHw{Ca6$ZJLB`9zct1_c@R5Nca79>#tuy0k(^aIxh`* zX&pi9!uf(;sgUY&`v=d3*4JVxL+%ucbD>4J*-W%~N95WVrQpwAU;3|wj#lpn93599 zUd*lYP3;z)=qG=d%1|a~1w7feC9tDsLTDkqap0u@4i3(4mzU(q7-(Qa^iAeK+05um zzE8Y+A3wR{H}8d-RfL!ZtJ&p^N~K>C6Nt`Rl_!=wuX8+ZE1HqFs1Xp}dEMIbee719|N z=CBzG)HR9Y=)qyVB+tiWl2=0c8tU&d@q0(5c3wj|*=}3TXOZQ_puh!vWzK%bM6|zloLD$lwcy(_9nRDA=4}; zx}Gq1_(LY90jq^vu6$Oc+G~`He{i0E))ExrAAQvMqFq5y8Q1AO3D1u+p{d<4V6gbr zz_qO_#1g=ejE%W-iPmgni12%2XrT>&63yZI2n82rLg!^s~%AN*t}G9 zm7L+p9d9O!BQ-X0{k<-F|9d;|_|LLPB?m{wxMlMKn}nYGyXaH~blf8yKo&gEK(D0i?cB+H;+FwsYPV}W0-`u%-c!X``5 zlWCD*CKpO$lW>fRWN>_z(^sq|^5<6Ns$FKfBC5 zJxQ_{_B3Vtc+XjF-yzsK#nHt1oYssWroQ!QJC{H1XOmlua>M5)jHw3*UxfQ{zk~|` z$VA4rzhr?p0Y5}R9k_O|6;_u_;NT!P{oOBhwKQ5+n)N7gTWHxK{Z0p!f|AnN$*Ftg zCsoG^PK7q>RmCWGH$tV)NJ{hqqWU;dYb$nb%(#>_%9Chp8cgNpWI?Tq1zp6ry;AeZ zB6A}nL#J=N_Ci8`x<{S*y!@mO_p}1aS!Q)xmpM3r%;5VUu__##>!0Vg0M)%t%mq@q zilG^jMKN;ho^dd=X_(_N{h+Vq8*~x9_PZ{Ec3FXB8c{MbeRIwVlv6m01 zmLsb9JH)0pW(sUFzX6X8m0YG(e*fW}P{z`ZtB1JxHELu8^C6l-Ga)8MhFl%34weSI zjS@+wQ*1@%=brORtGFL^SHt_udrZ?;#&8Z_Sn0iMu*>e?%A+$X?6hBg#e(Lf>=$7Z zfjNo>MC#++=u5Nm3p@V8=a$*;-yg5vIc8HtzL?t|Q5;@~HHZV(K!@{IOIDB0PQQ-; zHj|hR#G6}A>uvAnG`b#j*2eva->ZNWeQ#TXggDqHA{{GRc19x=AkYyd_NJ~(o*eT? z^oiFOYr(BwTVX&9{l2{9X=t3y!)e*x^XA6Wqut^)B*IEv~~$FJx!9L;w`JOi8L0PmZd z1#x-mRa9C8MZkUyu!+-=UF2E)NjBhQNnSoOpLKRbN4fU^UT?Nki$M~LuAuc`u>ikP zUEM%DB?j4)a|8WrnawZL+>N)jm9~IQYYA;z3StrI4^oG)JNXPd30jD;;!&@aDyM3< zDfI3g9`H7HtM9NLyX3xqPpN-MJWGlPcCVS@MKt~uJV)S7evTbTk(E5$-^?Vdr2CET zgt!z?Cet>BEC|9)=IZS{_W3h<*WE?@LtbGiXfFItEnN#yJ5yLp`k5mDHWlC^1O;EL z9Un)9>Qv1yFMU`Z`&>(Gj%CJ03wxQoU(%F5%Wo7aPc}a+FHQ;^Ga!wtt{A6(J-DGF zhQEz!fW>GSNmf>}6epwimT?*10*>iu^={y#>t4Pt4_RNU=P4H}&5y(Ej%6bt=)!7{ zFT7jslL7*RKnk!ejRenB7{^n~k*B)pwiX7o#kOydZ%XdtG8IqNFct>V@N0qb7JMB| z_7MwNhDuvHkb&J0)qxfDz1D%2%+^XW97Nr5^5&fP@44ICi<-%Tt0a=X|E@rG>q9fw zoi!8M27J`FZ{OAoU#_0Uru5LoqS{r2o!SB%T-ta>@c#UQ@a}6;G-gW!*5WN5xukL)uR8?C#Tg|nVdh;Y# zi?rDb6!5EwZLzepoLyae7j~I}V&kk`I^Y{RqJ;>Gr^=o~vjdkCJ=i_NE$BzX~yN_CM_mKA5 ziJ26ev!5EJf3<$S{^2s$cC`WJm#WyM0}jFpYlU_U38#lQh?L9gBqX3ChA+*dOgV{R zB+BpXL;p+*Y!$y{*n@_XB)kLlocKtZIJgIJLX%CeJ2tdnd(i+M=QE5Y&w%P7aP9X& zY*`E@Q*YUTqt@|$g+C#G`-LeFp=@vgVVybAE`KjZ6F*vOleyxx%%1>r!<%=`5n;pJ zM=2gTkx;BLvsl_u?$%|Tz2$*A)~~*CH|V(rc?S9{c zf-l>y4h{~PLi$HSVq$^&mQ%B7FxC9LzQp|&n%p~x%U*zZYe4v5rFDK zZ2E_;esX-?Wt|`riZLc+c$neg3sk%=Vuc*6euuOAI}!6*{Fk>dSXCsXZgGUpl$zP} zJy4pD+G%sOk~s%>jGZ`kfm+((z?m5AT?^T#{Pp$kM!)yaPHQYjzsSwRWhQUN%1P&D zFaBbQ4AIk=VH|6T>v~g88L(AXLq&96(@jHh6Dn)^X*d6k9#X#(?v|W45ijnt=9h8v29v6LU$KxwF!p$sR*4O6oqAY@o0-A2D7YoP4>1K zWdOfe-Q@Jl+l2ln6J#Vm3^|Aw;X<8?C@z?x#uR&5#X7>s?_NNY^4T3F2gg{;3-OZ5 zSAC(;E4Bo7R*MzTce%*2cLiPh+wj^tsNvxaU%e_uW=~!AKMb=t5Z%2WOo73CFTO$| zE#4Ma8ZMuX0UHm_9o{48Y_uVKboB;gBFgu0Iy$84hDM-{7Bk`Z8y#(kbEBlLcba;c zG-_$|;DEqBW)nZFJ3$MQz`VrkHBnvL>e;ZabWp|;kx}acHXBfV*&bBY)&y2IMg{rD zKIzd7g`ed8uyj}rR8ud`9}=r6^bN|hf^IEF2hc}T?5bK1#mo;ydUBb?MG7Gx;w;@* z(CDjMtL)-?W|1mh*1Dz<3WhDzn6>PZ_nuzodqlpzoI6Jc6T4d$&}e+uXP?>hk=0GC za+hJ2ZfH2_Bwl4pE%=`1!!= zBpp=LUmhMdiBGgh{Pe!)&t);^ zL^b3m*jsBC0-@IAJjHc165C4K!M_YJW$S%5m4!K$#CrJR24?S)7*CEO+f+sfnGb%f zqeBJ}azF2{XS%X}YJ{#FoK@8duMSgdT};^;=8BewlX(?u_}>-k)6B!MwT$B| z7JMW493gZTbNh56Mz#^&!#$U-R&Q!n<~#X^__y`yU5GHBHrH<~tOj3B2cGnWSE0?z zd&o>3Yf-~_N1Fx3zyChSF0FJvFEE_W!${suNv?A(J`+lY z_OzF);j?TnEbLAlp6^^Fr71rtTRb44Yohql_6|Mp;PQ@FM>8vFPDEVqXhC0lEMxdk zV-0N%CVoX`gd(-xI%KYr6GR&MQsH$x75uF(Z5y^6id|BYC)S~dN@npWx$RJGEQ7-H zvdmq#S6bR$I-J|cquZ|8XJ0H`ONZZDurI!+;0tBR!{LK6du22zs$qWM z>|;~C^l6(!{G`N)&Vi9I(310kyX)97B)q!g8=EK@9n z%xI8uTgn83tQeK(9~JVL$Q<3DaXv?lnkm>Zvea<4Uznn9Hbs6e>dOqVa;m?kO^Fh2 zzr?Rj(7lv1#4wiVPi)k8^Yq{v(9q&L^Cp|DU9^wr-RC;YE7$#zK$qe^>f{z$Gx((} zv({tUSRG6@9k1P{%#p^c_%us`pJeUNsl^@X8#z(_t>};l3SA^k3JahnD?Kd*jvgBp zixgf}P4zD(8m`(jST{-c?(TX?tyg7bjE1mumIBoh50z>&)v}tl*-P`JBD4uq{R$7k zTRklC>CqpHpb}mdn_hmWAs6Sez8L8l*>16AdM0x=CL#t*$C|U8*2>;1`>@tZ(m1&< z(afEd(g_Q#a%GvN9-779U2xi2yJjleh~UEx&q1sWe+f(3cQu)9%Bjy@Gn=MjxOGqBB-L+#FhAkYAT?c1 z8+U)Ey#+2OKmRh{svBcm46%Ll@{L9iJLu_|X|9+tgnnD72EB1Af7u~?m=i>53vJTZ zwSAY)n0%69BZdj7dFn&{xphyR2LFEy{8n#^K3cT$VA`snn{n$eeV0XK=k2If6ac==jCkx3BxN{Adyg3|i*yZsoL;_z*!1-ZJw zs}5*w%^RQ0(>-Hy@`1Anpkhr`#!^T!Qwp+KpjMkZHz$SD(gM9#$s_YO(jlrWJ=T)d zo1;NSVRUTze8&H#0XR26PQ3+UxdWKxd{lP*Q8Gg$!FrM8Vm8)K6?s zAlb02v|#4v#Ls$Y!(v&>F~ZmExSL`zCu1YsZ0%7$fHgLCgaFpVl=Un?ywc5?_%^mW8 z_Zw@^(9k)XIK@MLrZgb%z&qWT%be0uE+&f3Qx|AkVfZ1{ z#;u5cSq+7Zj^@*Cue21xi4ttftc^HTDv0oj`N*el-wr5?a6EpXgjFv*Pu%WME?g^y zs=aj>oD;vEa~tjg3SU8StJmgvuZ(g*w{x*t{sX&Bj@cF3Xf-EFfr3Tiq#NzMA|J5L zLFFeGl;l}3&kU z6YY&d4W(CIs*mWRJDBvevb|8@uN>SACvwtqr#Sr-ZYPYx4WzxFR_aMhuXU(b9ZrP*NHxr5zzJL=^l%$u9v z+_nVi__cCl#}7O5=g1YxmBtN7P5<%qOgihn_x=huhAGkuug<`gGSm zGoZv-XbmiMTxp*k{*jTLt({_ez9G#H38U04TwrkV_Et`@TBIx8NzFH4$67h*_Y4St zFA+j<6DVCl+ng?-c_e=|{;8y5!)z7R$dpC9k7m}{ft2D8!>p10v#%Lf(%Xn{+yGnJ zknZ=;Pu~Tje`WmJflaqr*Ey;M@h@&}#=jVHRf%S4|7HU<@NM_bbZfbcD`l($X)*Fn z{zS;WT<|4rE)(k5`&2@UU#tr2-h10(!myDfQio~B-mER>E$twyxNq%cTL8sxX1*~B zJw7STYk}vORG=>g*h`&@GO~a5Khnh;^qTc*vKC@9TuH`ZYCw2NimJTKU@3D7cLy;V z${LM*FhM$2Q-1^Q#(BWCxXBa(OJ zW%avpirtSd0y5ldn)GHvWYVmETi>o`Cz5Yp(6PF>cy|?+^D3XcB`cz42dfeC~m8TqgCUniV$MqHZ7dKkn190&QP9$AJRXs^rDWhMFEmLW3^Xr~f2H8SYZ_+)(tTW~KH(V0`*C9>}MegX>z zW?j*75S&la<9~Vjm~`{ZmyV9kL;P)=`%~Af4>o5he;owibk~m5d~(!&7!6a=9qgC^$%}qOTa~R*ye9<&NtiUUDh`*!>V# z(#gspyS)BzgRZ#9xPh~o&3>AkfwsHsA{6d+Vl9itLR8df<>s>4s+0eEPg;H$jMSr8 zMKH#AxZ8|#v@z??_S_-fF(<@fxYpgO0b4zpYb=aa-32K zEWFGquX+I5rd^iUlZY1Xpdm>vcZi_Ge!d|(U*@7qBhRS=7{+1;@!D11BoRz{qt*U& zy>IsXy0>%IXgoq+pjasHsRGMdP6UmXQ^C<6l^?nC{N^X>G$8cY9}KB*bw50+Y*c%l zXDF+z)~rw+VNOkwxQFmjN_{$JJ3})8*Z@EfEL#(3hZeY%xIrg>j9;H8eH|btL;}Z^ zYZyyC^_W-{qL&bnc^q7bPt?ipG(Zip#s^}9q9pOd0G>tgX7KZ8y_ZFnEx3zkz>NDh zoOwEmwhMk32Glfux`4fqWIZcl8DC8w5ANJHd)&$Yqfx-yUJ{g~e#VVG2;=}7G8rug z^pOh3rp;UJdD8z%yC1|#`+*%y_z<+UT8hI-s6&|XoHaR`5d?`dQNOdoOQCk>hK@=2 zCr93q!R@x%zq+u`W)-gn_=#5ZTAb8nO_aWK0-%5icw>7bnYyZw_yWw{Pq$W2!Hhbh zh8bCJX4Sx$p-g{NB@uUv04-gtW09AeQ+HRm?c&#FnE~<3Wm3f&WUk4Q0T@PKYu^7T zJY6Z=G|w~}0VrS!6W>hIThBDxsW@&I=Ek$9j(GSs6Tb=f=%@9JfZSt8C>n=J5Mr=g zmse=5xi^808Q?0(F}xeU%SIKM!->L5Eq5_mgo#_?a26FbF~6&X{GfnhK0v(~pA3zg zfrYb7iT0P}R149AIA9MT#%YMVo3`tKo1q5CgESA_(?K--F&Xp`UusZpPDm735pxkGmmlFr!crr`nGPAH! z9UX(|?9(Px=`a&UGxLt)2>lx9F7U_03YXWxpPCv%9P}Rxd7<*|WYz0keZzkB2(PUs5RS~vcOCiwwOe7c+B zysRW!z`^By#F5;x>yfXt4Tp!+h%Z3d#n6k^5iSD|CP4domByAHnap0YV zo0~R0-6s&ar{DH;_3#OG*x{Cne0<_Gd-f`}7>hdLG1{G0$Ng4DGT8F;s2@{U`|D^A zMHr=Jpk#ec!0#3#;z|CPvs{Blgft@o`1JjzO>(MUc1mUQQb z0lmIr=a)NRNC@c>>lA+)d)H#Rh>8)yh{g5#TqRae1 zss?#XWJA;_S3^TcI$;kV*KiL6R6%$!AnHy}E%Rss$b*V}p@=s+8{R>3uvdM)I4z-L zPRPl5Sb0zH9XPrNC|30JQ-FIZws0Pv;2iBrDDaz@WCuIB?|ImjnOqhXygJ3+PuMLW z#^62zW{pl~EdYHAPNWW}++5*=n{fkRu<=2zj1?6AWH39Pq=!iA)z*o|lW(B7xmWk; z<4AfcM*Yyv>xz>}4216p?ZvXha*7#ZJHC8#6Ihp1c#{=tcqbE+8kI&?U(}82rbq{F zk^zjBskj%X`oT5U1y1&>!>thWAeHWk-g=+@e9=26)F#o$tlqpsrBpj8R7RJ3IVOYM z$3LrFePPL5`i_0-Z}Z+Evja>IN~23NpcG(NZ7vlJWj}Uwr+D811?p8a>g~~;ZaSv`mYr;p z?N55?MhHron`u|Z{tM(^+O(DB4&Sl)Bvlc&45hn0yO&TpCS9A+10SjU&R zTM-LoS-D>ab`sY2wEX@W1wnp>j!^ffME-{Um*(GL!+uO+bM^deK(-uF>d} zMp)el+%X1$qG$o~*7z3kZQwp?S3v=urPJMN`>ec>dpmcMb|hkU`yb0=>$ZTyLd0^- zVlMvUrBAS87W$fCssmYgwV1{1cCVuMYn(eQP;C74AHkq%nK_CP^6}3jmhvC?zb%xB zU1`<4P5I@nSaly6(^XXmNox1Xkm4LpDjDdJyBS#--dvJNkj6#s>f5SiTCNBrRejd$ z;``Ap5M)PLab79V%l_BXDkf$Z3n9ZrUrZSnV?6y^m0Wh0FR47pGJR}t*fYb4DR*#~ zU_9A~c%B7B;O99(pNlGH)<{rxC5F3i?+I%X4zc1v4)BZg>xw-Bu>6kamp#^mh%l*Z zY%*}4PnAhD26`Pi++~<^T($jFy|+c2{~dPl9Q~kX~G`-Wa%n0 zP-Lkg&K$smj7zy?MYIQIlknJ2fgPFIyb~9H zZ!t1K0N=cfPR-_-`=a2oRQ`FkWheQ+8!XXh4P6)IO?7uQxS1F_A5R%+{}u|IaXQm` zrk%+o@pFrTWtxE~=pxiKf}A)p`lv&^iN5y#6??UGbj|n|W3PVv6Hbfq0L5z?-)n-P z%eut|hmMoU&_8j3LiZ7oIi@-7*IR~EI7gd3{ zE;y$Y3RalI<+8}2&A$=!*%wp^()dulnxk*3f_F7M%6;czJ^`KwJLci(%?{!HWC;YE zrZP8OMHaDvS;No2tjwforo+k^OoZ=KKfDm{gS37ET&>KJGoGWAJ@SS?;3q42t;66! z^`aP;os)Bh<6T0n&M4F}S!krni=!(<4XU$ly>c-f=I0$jFG9U!3=gTND?lDuP5(f zrv2A6i8$VX6CBbVCm5ecdxG(9_Td(O_Epr5x)=>FnuDjfq{*#Z|05>nOzem*bzlL3 zYY|R1dSK-YvK^gATHxO@J(0Be(>9}5f_;l&p`EO4#Do_a|3XB`=AM+y4orHZ`p>zf zOv)h65;v%w>>JVuE`~!BdC)$6)EQNn3mJPmLhLhG{g&nAKc0AcUULH%4V>qMMt}Ah z#XezdO73#!yB)uH%IkOh#^W5GwMQBuiN{tP;zc8)s?-0){Y1MOP0D*MC-KGAyV;DN zTl>Ay%pR_ZBes2ra12rv-h*TSPj4jHpZUCq1x$RQ)DrDx$Hv0#`L56 zV2M~>ID@FuEC%#JK3!KKc~dS!)KU%~Yg2m|R>hyruI+j>nXK^gkT$zB^4k6(Euft(EM zIz#~t5Kuy4t1F7pCw{YNzLn$S>pgk{tII=08JId6VG!K>KC`vpnaGPni%a{Yr&()_ zrZy~c2BHI$@`(TV4sePrV5@>@0c>j4=%u6%m+pnpkd(9zjhzmCh8Bzl5_dw!u$qto zLw)sE@$&NCKQ>I_r<}BwZ8VOFEF*fUmCkl3AqzZh(I?h^XN_3%}0*w zPF8W&fNh?})~LE*j6|}I9l>LgdFOb~mR;+*3yPbntaaXwUho5;JDTo*ZX;vZ+QAb(e5n-QD3PlLKIS(s3ByUhAaYivW31En|Wnp)_}g}(Ko zCdVo$+?JBvPWzYQv=fCWsbzK_iIh8f2|Jtj2^#j0HPk+VYc|#NyN-Hw_bkvEO{xUU z+Cyh&7A5TH>R4uyN7C2mXu~6|3?vlS1Y5Z}`*DNZ&`V(}D+G^9c^44xf~55aI%v8{ zQti=)O8)|^G~Cv{PhqpZXo7a;zvA;_Ez5+gm8WOc%g<3HPt~#R#JQYfXa6DCf)T!N z9T{$78@GQQz%_zlwYRa`W9MU6)M5KOT*klR+NqaHRsSel9j2NYi^;&_m|M3#r{n3g zEnY=>9DME6#P~7<$Vog3C|q8duWL`IxWj$xhR0=-+_vAYdCN7WB@gOy-om>*pAh-9 zz@t#V=1-2v16CeZ)!z-viel1CTLFyH)Q1iEq%S!i7`=}WYF>K(5JTE#g1&+83q7YT z_+XlJ3qr^PknB3RNNjzPEs}xJDQq9}R9XW45L&*5b!3+Yxh{F#je)}9@aD<1)srm6 zT2w>$L9hiNb(iqNK-DTy0d`7pMvha&CblO#eEv(FDBF%^pA@&sSUsdF`b4p@_1E#8 z2A;A`+C0mJecTlLOLHKV*V4n-LtokS#|RP21N=zFiz!J?AqY~b&m0dIXx*>OyFV^v z#2Fhcu6wk9V$i{?E@BusP(@N|seTJblW}2Aex1zo&nlBnoCGS}8k5#DmNPyilu;J{ z27V2GE_;o=(F4EI^mqQbbQm(9v$&$cUf!a;;}>6wi9+VpK`d003(0P~}RNR_-GH+2MXZQ#JEwL4A#mecFl$+Ip@tA3yloGd`+r?00Q?o{_ycP{1>n zy`*-!5iw7hMAFKjkRh8@Ja^a)EF+o(`J=hE@oFE1x6ikMv3@=?+F3NgAK9|7BZXn; zL3wC z>`__G*KD#j$kz-#>Dk}Ze`i7YK=fiBaW1zfU;WcAMj86cfuu@cz8WC_vzjba*aFfcB>cj=5GxT zOOuIGRiR}QlkPYd=VTsrc;(A)KKz_rHH%lnk9OI@6%TAQ`1`399 zAH9`i3yeLinYA*it+Yt~b0ABK#i-Lx=b;iWzTAyXADX`So!pn9`)U{C9L@FrJgfWa ze?H&#wZi025gDvEk&>d^8osL^#A>o4Zs%Fq5 z99iqgti?H^)K~4Au3I5Oe9GTh?M(hLmWb!%<*eH}zm-kAN0{`ae=)>agKsX)7G8r` z9LzUOO^I^&^9zfrr@=qGGD~-ku1K)!vmJ`cUxIl{v-jdnL7OFixflzt89i$HqpRgzr#2mDAwG)7vTZM|nPyr{3cyU>4v9|V0@ojcx0fpKp zEurFDX0kBd;}UcoM+?G!)64>f>FK(?pXrjQ?`!-KJ9o;Q{*%Rf>M*HS3y=dg|H zB|CA?NMAzHsZ+8TLaha1b-{ssP-%s+sT0nHzdsQ*$J=MXIr?Q`3?N-ea{KmpON$a$ znMGcOq1%R&4G118xP-~fcE2tItBr#eDT@_rB)sHivWFSCd;Q!6IJAwKTA6l)SB1vQ z#pbt=h|b4ucQYMk%KjxEIAQDC+a<{N5HK#n^+3h20$KJDfY-~b6sxtX{aZ|mmD3?z znehxmz0$nsaM&ZxcheK+Yyf)5CnMAckN5BHOhXMOT~oG{u-#sgV=qk(AMf=)oEAdp zB@p6UQE(MSQbeW{zr}Kwr?Pzwc6!p5l2l;$;#%{HtgvAEZ|ls*6#`Cd2P(`qMD_5o zHLH}$>P5hNVn&Blg9@rD)nOb?99ax+Nxw1%#%fZ+Jyd5*fiN-^PKaJ&6Ugv&q76uC zoG91;>AL~pPQ-ARKkG};+i@7Jv;GG@736tNVu?qKp+em!qo!~!g9mSPs9Y^SrRPdK zl4fP#?u&SX%Rp7Y*gx@RtSC2kOSRJMmIav^Uj7lYrYOg>!0-l0wtN*9nI*dtm2%fk|9f^)LReF2<>^HO4ik%JEq5;iGcCxxvrAS69hJI8!^!ql<8ktEHbc6o ziYc|LU+4LRtLd}6PO$qW%6V+$JlVw3#EhiPGdPpv!0yCQXoDyiBI!dy(rUP*n%D_! z(lg%S&(DNBw3`(SW74oq#xel^CT(MJ#o^6P_~t(9FFV7hjH@OU0a(zhb5zHFY+jv- z3oqL`Nz7VqJL?EvV4X4>f*wZ1&sLJWJ*r%N*uWkV#zkNDj#n6Hi|_at5QZ!FS@|Wt zr`NVAR7cE^>1(=CI-}~?@ia5hVC_82cK`eM=yrh8#6(r)*aMIp zbc_|2EM~ei1A^(no7oOQCDN<|T)t&#brl?!k2|ReomEaXvfc(${<+)BYgi|CN~30O zGUxKJ(lHKr4-QKMA}UJVRJlu{eRPwf!LFQTs!!fvxkZnWUBSAN3sh86^y{{wzd3HQ zKi;+H!+T%H)a zbhO!Pn+u3jezBISVFJaK3)M1JEE_#BN`9lG7LS$83MZ?=+1AwNIM|~oy4#RlFFKB3 zy)Ia`XnQw8aE$I==kzj0&aqBpHY6{E`sG;CdvSYwwcO`005L|g0%DMCQx&k{O6cju z1+NljYmU4?CjLu^KQhQ7Ke0`Td#Rlez%RvO{0i{XB8I1q ztede)K4?5fy(2dkOghO+WOCIr$M;OKC?+*l$`fYd;n7Zm(o9VKi{p(eeVtp0YrIsa zSn#b-9t9`@p-XqH>#-^}8zTs#&T=amM`(^S#*x?+2e}=E!lUE<&!soAh5QlM$2Vs# zN~3$y*(Y{CCc`v&462socy!I0Va39g8y_{4bEh-BkGvZz>`K7n#UkL1 z{{V%!rB3Cl`l;Kh{qA$UzE6xi*eR+ks9Z0{U{M}5H<8^ zpTB+UWrzanpMMn6r2b=6cajvMa2!;odUN_Tjc1{y#?&S>^3#ZIYNyU;IsDv*cFV*i zTA?C2f2iO*a&qN=CTPL#WjuojNn3&~vqo?G9c_QkW?lb{TNPnx=C-y+ zxVd?zLUa%7c<2^RnVKt{PE24klnbWXfmUt-vG}LXZP%KuH~RaIFEWlQ2BNM%EKs2K zf7sDvxZ92`x+_fx9qMPA1WMwxdfvi*@fpJ-%9~0){eTid#^N7Q*>(C%4&z-&!Z&+x zC-7umzEqoStYe%x`Qdd;d->o6o2tK^q>*|h$;#~`ZMmd&d|R#>%x&WjR1`GiE9J~W F{s#g`(+L0o literal 0 HcmV?d00001