From 5198725879e003512f0f4d68bf5ff10c384a5301 Mon Sep 17 00:00:00 2001 From: Dimitris Date: Sun, 15 Mar 2026 17:18:02 +0100 Subject: [PATCH] Lanes --- app/build.gradle.kts | 4 +- .../com/kouros/navigation/ui/MainActivity.kt | 20 +- .../navigation/ui/navigation/AppNavGraph.kt | 35 +- .../navigation/ui/search/SearchScreen.kt | 34 +- .../navigation/ui/search/SearchSheet.kt | 2 +- .../navigation/ui/settings/DisplayScreen.kt | 164 +-- .../ui/settings/NavigationScreen.kt | 139 ++- .../kouros/navigation/ui/settings/Settings.kt | 120 ++ .../navigation/ui/settings/SettingsRoute.kt | 3 + .../navigation/ui/settings/SettingsScreen.kt | 124 +- .../com/kouros/navigation/ui/theme/Theme.kt | 32 +- .../kouros/navigation/car/RouteModelTest.kt | 22 +- .../com/kouros/navigation/car/map/MapView.kt | 6 - .../car/navigation/RouteCarModel.kt | 8 +- .../navigation/car/navigation/Simulation.kt | 8 +- .../navigation/car/screen/CategoriesScreen.kt | 10 + .../navigation/car/screen/NavigationScreen.kt | 3 +- .../navigation/car/screen/PlaceListScreen.kt | 15 +- .../car/screen/settings/NavigationSettings.kt | 18 +- common/data/build.gradle.kts | 2 +- .../java/com/kouros/navigation/data/Data.kt | 4 +- .../kouros/navigation/data/NavigationState.kt | 1 + .../kouros/navigation/data/osrm/OsrmRoute.kt | 4 +- .../navigation/data/route/Intersection.kt | 2 +- .../com/kouros/navigation/data/route/Lane.kt | 2 + .../com/kouros/navigation/data/route/Leg.kt | 1 - .../kouros/navigation/data/route/Maneuver.kt | 3 +- .../navigation/data/tomtom/TomTomRoute.kt | 84 +- .../com/kouros/navigation/model/IconMapper.kt | 24 +- .../navigation/model/NavigationViewModel.kt | 5 +- .../navigation/model/RouteCalculator.kt | 2 +- .../com/kouros/navigation/model/RouteModel.kt | 76 +- .../res/drawable/baseline_add_road_24.xml | 26 + .../baseline_directions_boat_filled_24.xml | 21 + .../main/res/drawable/baseline_toll_24.xml | 22 + common/data/src/main/res/drawable/left_x.png | Bin 4927 -> 4929 bytes .../main/res/drawable/left_x_straight_o.png | Bin 5265 -> 5258 bytes common/data/src/main/res/drawable/none.png | Bin 4759 -> 0 bytes common/data/src/main/res/drawable/right_x.png | Bin 4927 -> 4948 bytes .../main/res/drawable/right_x_straight_o.png | Bin 5617 -> 5620 bytes .../main/res/drawable/right_x_straight_x.png | Bin 5240 -> 5323 bytes .../src/main/res/drawable/slight_left_x.png | Bin 5150 -> 5212 bytes .../src/main/res/drawable/slight_right_x.png | Bin 4927 -> 4937 bytes .../data/src/main/res/drawable/straight_x.png | Bin 4954 -> 5387 bytes .../data/src/main/res/raw/tomom_routing.json | 1090 ++--------------- gradle/libs.versions.toml | 30 +- 46 files changed, 840 insertions(+), 1326 deletions(-) create mode 100644 app/src/main/java/com/kouros/navigation/ui/settings/Settings.kt create mode 100644 common/data/src/main/res/drawable/baseline_add_road_24.xml create mode 100644 common/data/src/main/res/drawable/baseline_directions_boat_filled_24.xml create mode 100644 common/data/src/main/res/drawable/baseline_toll_24.xml delete mode 100644 common/data/src/main/res/drawable/none.png diff --git a/app/build.gradle.kts b/app/build.gradle.kts index ebeeb8b..eb1878a 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -13,8 +13,8 @@ android { applicationId = "com.kouros.navigation" minSdk = 33 targetSdk = 36 - versionCode = 66 - versionName = "0.2.0.66" + versionCode = 68 + versionName = "0.2.0.68" base.archivesName = "navi-$versionName" testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" } diff --git a/app/src/main/java/com/kouros/navigation/ui/MainActivity.kt b/app/src/main/java/com/kouros/navigation/ui/MainActivity.kt index 8265b19..6266854 100644 --- a/app/src/main/java/com/kouros/navigation/ui/MainActivity.kt +++ b/app/src/main/java/com/kouros/navigation/ui/MainActivity.kt @@ -10,6 +10,7 @@ import androidx.activity.ComponentActivity import androidx.activity.compose.setContent import androidx.activity.enableEdgeToEdge import androidx.annotation.RequiresPermission +import androidx.appcompat.app.AppCompatDelegate import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth @@ -172,11 +173,13 @@ class MainActivity : ComponentActivity() { } enableEdgeToEdge() setContent { - CheckPermissionScreen(app = { - AppNavGraph( - mainActivity = this - ) - }) + NavigationTheme { + CheckPermissionScreen(app = { + AppNavGraph( + mainActivity = this + ) + }) + } } } @@ -185,9 +188,14 @@ class MainActivity : ComponentActivity() { fun StartScreen( navController: NavHostController ) { - val appViewModel: AppViewModel = appViewModel() val darkMode by appViewModel.darkMode.collectAsState() + + if (darkMode == 1) { + AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES) + } else { + AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO) + } val baseStyle = BaseStyleModel().readStyle(applicationContext, darkMode, darkMode == 1) val locationProvider = rememberDefaultLocationProvider( updateInterval = 0.5.seconds, desiredAccuracy = DesiredAccuracy.Highest diff --git a/app/src/main/java/com/kouros/navigation/ui/navigation/AppNavGraph.kt b/app/src/main/java/com/kouros/navigation/ui/navigation/AppNavGraph.kt index aae796f..1b137c4 100644 --- a/app/src/main/java/com/kouros/navigation/ui/navigation/AppNavGraph.kt +++ b/app/src/main/java/com/kouros/navigation/ui/navigation/AppNavGraph.kt @@ -16,10 +16,37 @@ fun AppNavGraph(mainActivity: MainActivity) { val navController = rememberNavController() NavHost(navController = navController, startDestination = "startScreen") { composable("startScreen") { mainActivity.StartScreen(navController) } - composable("display_settings") { SettingsRoute("display_settings", navController) { navController.popBackStack() } } - composable("nav_settings") { SettingsRoute("nav_settings", navController) { navController.popBackStack() } } - composable("settings") { SettingsRoute("settings", navController) { navController.popBackStack() } } - composable("search") { SearchScreen(navController, navController.context, navigationViewModel, mainActivity.lastLocation) { navController.popBackStack() } + composable("display_settings") { + SettingsRoute( + "display_settings", + navController + ) { navController.popBackStack() } + } + composable("nav_settings") { + SettingsRoute( + "nav_settings", + navController + ) { navController.popBackStack() } + } + composable("settings") { + SettingsRoute( + "settings", + navController + ) { navController.popBackStack() } + } + composable("search") { + SearchScreen( + navController, + navController.context, + navigationViewModel, + mainActivity.lastLocation + ) { navController.popBackStack() } + } + composable("settings_screen") { + SettingsRoute( + "settings_screen", + navController + ) { navController.popBackStack() } } } } diff --git a/app/src/main/java/com/kouros/navigation/ui/search/SearchScreen.kt b/app/src/main/java/com/kouros/navigation/ui/search/SearchScreen.kt index 2c35bc0..2bb1913 100644 --- a/app/src/main/java/com/kouros/navigation/ui/search/SearchScreen.kt +++ b/app/src/main/java/com/kouros/navigation/ui/search/SearchScreen.kt @@ -3,8 +3,10 @@ package com.kouros.navigation.ui.search import android.annotation.SuppressLint import android.content.Context import android.location.Location +import androidx.appcompat.app.AppCompatDelegate import androidx.compose.foundation.clickable import androidx.compose.foundation.horizontalScroll +import androidx.compose.foundation.isSystemInDarkTheme import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.PaddingValues @@ -28,14 +30,17 @@ import androidx.compose.material3.Scaffold import androidx.compose.material3.SearchBar import androidx.compose.material3.SearchBarDefaults import androidx.compose.material3.Text +import androidx.compose.material3.TopAppBar import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.runtime.livedata.observeAsState import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.focus.FocusRequester import androidx.compose.ui.focus.focusRequester @@ -49,8 +54,11 @@ import com.kouros.navigation.data.Place import com.kouros.navigation.data.PlaceColor import com.kouros.navigation.data.nominatim.SearchResult import com.kouros.navigation.model.NavigationViewModel +import com.kouros.navigation.ui.app.AppViewModel +import com.kouros.navigation.ui.app.appViewModel import com.kouros.navigation.ui.theme.NavigationTheme import com.kouros.navigation.utils.location +import kotlin.math.roundToInt @SuppressLint("UnusedMaterial3ScaffoldPaddingParameter") @@ -64,10 +72,18 @@ fun SearchScreen( function: () -> Unit ) { - NavigationTheme(true) { + val appViewModel: AppViewModel = appViewModel() + val darkMode by appViewModel.darkMode.collectAsState() + + if (darkMode == 1 || darkMode == 2 && isSystemInDarkTheme()) { + AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES) + } else { + AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO) + } + //NavigationTheme(darkMode == 1 || darkMode == 2 && isSystemInDarkTheme()) { Scaffold( topBar = { - CenterAlignedTopAppBar( + TopAppBar( title = { Text( stringResource(id = R.string.search_action_title), @@ -92,7 +108,7 @@ fun SearchScreen( Categories(context, navigationViewModel, location, closeSheet = { }) } } - } + // } } @OptIn(ExperimentalMaterial3Api::class) @@ -118,9 +134,6 @@ fun SearchBar( } SearchBar( - colors = SearchBarDefaults.colors( - containerColor = MaterialTheme.colorScheme.secondaryContainer - ), inputField = { SearchBarDefaults.InputField( modifier = Modifier.focusRequester(focusRequester), @@ -206,11 +219,11 @@ private fun SearchPlaces( ) { val color = remember { PlaceColor } LazyColumn( - contentPadding = PaddingValues(horizontal = 16.dp, vertical = 10.dp), + contentPadding = PaddingValues(horizontal = 6.dp, vertical = 10.dp), verticalArrangement = Arrangement.spacedBy(4.dp), ) { items(searchResults, key = { it.placeId }) { place -> - Row { + Row(verticalAlignment = Alignment.CenterVertically) { Icon( painter = painterResource(id = R.drawable.ic_place_white_24dp), "Navigation", @@ -218,8 +231,11 @@ private fun SearchPlaces( modifier = Modifier.size(24.dp, 24.dp), ) ListItem( - headlineContent = { Text(place.displayName) }, + headlineContent = {Text(place.address.road)}, + leadingContent = {Text(place.name)}, + trailingContent = { Text("${(place.distance/1000).roundToInt()} km") }, modifier = Modifier + .animateItem() .clickable { val pl = Place( name = place.name, diff --git a/app/src/main/java/com/kouros/navigation/ui/search/SearchSheet.kt b/app/src/main/java/com/kouros/navigation/ui/search/SearchSheet.kt index 5fe6f5d..10cb952 100644 --- a/app/src/main/java/com/kouros/navigation/ui/search/SearchSheet.kt +++ b/app/src/main/java/com/kouros/navigation/ui/search/SearchSheet.kt @@ -160,7 +160,7 @@ private fun RecentPlaces( Icon( painter = painterResource(id = R.drawable.ic_place_white_24dp), "Navigation", - tint = color.copy(alpha = 1f), + //tint = color.copy(alpha = 1f), modifier = Modifier.size(24.dp, 24.dp), ) ListItem( diff --git a/app/src/main/java/com/kouros/navigation/ui/settings/DisplayScreen.kt b/app/src/main/java/com/kouros/navigation/ui/settings/DisplayScreen.kt index d2bdfa6..b9d7bd5 100644 --- a/app/src/main/java/com/kouros/navigation/ui/settings/DisplayScreen.kt +++ b/app/src/main/java/com/kouros/navigation/ui/settings/DisplayScreen.kt @@ -1,35 +1,41 @@ package com.kouros.navigation.ui.settings +import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer 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.lazy.LazyColumn +import androidx.compose.foundation.lazy.items import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.verticalScroll -import androidx.compose.material3.CenterAlignedTopAppBar import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.OutlinedCard import androidx.compose.material3.Scaffold -import androidx.compose.material3.SegmentedButtonDefaults.Icon import androidx.compose.material3.Text +import androidx.compose.material3.TopAppBar import androidx.compose.runtime.Composable import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp -import androidx.navigation.NavHostController import com.kouros.data.R import com.kouros.navigation.model.SettingsViewModel import com.kouros.navigation.ui.components.RadioButtonSingleSelection import com.kouros.navigation.ui.components.SectionTitle import com.kouros.navigation.ui.components.SettingSwitch -import com.kouros.navigation.ui.theme.NavigationTheme + + @OptIn(ExperimentalMaterial3Api::class) @Composable @@ -37,80 +43,100 @@ fun DisplayScreen(viewModel: SettingsViewModel, navigateBack: () -> Unit) { val darkMode by viewModel.darkMode.collectAsState() val show3D by viewModel.show3D.collectAsState() - NavigationTheme(useDarkTheme = viewModel.darkMode.collectAsState().value == 1) { - Scaffold( - topBar = { - CenterAlignedTopAppBar( - title = { - Text( - stringResource(id = R.string.display_settings), + val showTraffic by viewModel.traffic.collectAsState() + val distanceMode by viewModel.distanceMode.collectAsState() + + Scaffold( + topBar = { + TopAppBar( + title = { + Text( + stringResource(id = R.string.display_settings), + ) + }, + navigationIcon = { + IconButton(onClick = navigateBack) { + Icon( + painter = painterResource(R.drawable.arrow_back_24px), + contentDescription = stringResource(id = R.string.accept_action_title), + modifier = Modifier.size(48.dp, 48.dp), ) - }, - navigationIcon = { - IconButton(onClick = navigateBack) { - Icon( - painter = painterResource(R.drawable.arrow_back_24px), - contentDescription = stringResource(id = R.string.accept_action_title), - modifier = Modifier.size(48.dp, 48.dp), - ) - } - }, - ) - }, - ) - { padding -> - val scrollState = rememberScrollState() - Column( - modifier = Modifier - .fillMaxSize() - .padding(top = 20.dp) - .verticalScroll(scrollState) - ) { + } + }, + ) + }, + ) + { paddingValues -> + val scrollState = rememberScrollState() + Column( + modifier = Modifier + .padding(paddingValues) + .fillMaxSize() + .padding(top = 10.dp) + .verticalScroll(scrollState) + ) { + OutlinedCard(modifier = Modifier.fillMaxWidth()) { + Column( + modifier = Modifier.padding(10.dp), + ) { + SettingSwitch( + title = stringResource(R.string.threed_building), + checked = show3D, + onCheckedChange = viewModel::onShow3DChanged + ) - Text( - text = stringResource(R.string.settings_action_title), - style = MaterialTheme.typography.headlineMedium - ) + SettingSwitch( + title = stringResource(R.string.traffic), + checked = showTraffic, + onCheckedChange = viewModel::onTraffic + ) + } + } - Spacer(modifier = Modifier.height(24.dp)) + Spacer(modifier = Modifier.height(20.dp)) - // Appearance - SectionTitle(stringResource(R.string.threed_building)) + OutlinedCard(modifier = Modifier.fillMaxWidth()) { + Column( + modifier = Modifier.padding(10.dp), + ) { + SectionTitle(stringResource(R.string.dark_mode)) - SettingSwitch( - title = stringResource(R.string.threed_building), - checked = show3D, - onCheckedChange = viewModel::onShow3DChanged - ) + val radioOptions = listOf( + stringResource(R.string.off_action_title), + stringResource(R.string.on_action_title), + stringResource(R.string.use_telephon_settings) + ) + RadioButtonSingleSelection( + modifier = Modifier.padding(), + selectedOption = darkMode, + radioOptions = radioOptions, + onClick = viewModel::onDarkModeChanged + ) + } + } - SectionTitle(stringResource(R.string.dark_mode)) + Spacer(modifier = Modifier.height(20.dp)) - val radioOptions = listOf( - stringResource(R.string.off_action_title), - stringResource(R.string.on_action_title), - stringResource(R.string.use_telephon_settings) - ) - RadioButtonSingleSelection( - modifier = Modifier.padding(), - selectedOption = darkMode, - radioOptions = radioOptions, - onClick = viewModel::onDarkModeChanged - ) + OutlinedCard(modifier = Modifier.fillMaxWidth()) { + Column( + modifier = Modifier.padding(10.dp), + ) { + SectionTitle(stringResource(R.string.distance_units)) + + val radioOptions = listOf( + stringResource(R.string.automatically), + stringResource(R.string.kilometer), + stringResource(R.string.miles) + ) + RadioButtonSingleSelection( + modifier = Modifier.padding(), + selectedOption = distanceMode, + radioOptions = radioOptions, + onClick = viewModel::onDistanceModeChanged + ) + } } } } } - - - - - - - - - - - - - diff --git a/app/src/main/java/com/kouros/navigation/ui/settings/NavigationScreen.kt b/app/src/main/java/com/kouros/navigation/ui/settings/NavigationScreen.kt index 0cd2e64..a54c559 100644 --- a/app/src/main/java/com/kouros/navigation/ui/settings/NavigationScreen.kt +++ b/app/src/main/java/com/kouros/navigation/ui/settings/NavigationScreen.kt @@ -1,7 +1,10 @@ package com.kouros.navigation.ui.settings import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.consumeWindowInsets +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.rememberScrollState @@ -10,9 +13,11 @@ import androidx.compose.material3.CenterAlignedTopAppBar import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Icon import androidx.compose.material3.IconButton +import androidx.compose.material3.OutlinedCard import androidx.compose.material3.Scaffold import androidx.compose.material3.Text import androidx.compose.material3.TextField +import androidx.compose.material3.TopAppBar import androidx.compose.runtime.Composable import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue @@ -38,16 +43,14 @@ import com.kouros.navigation.ui.theme.NavigationTheme @OptIn(ExperimentalMaterial3Api::class) @Composable fun NavigationScreen(viewModel: SettingsViewModel, navigateBack: () -> Unit) { - - NavigationTheme(useDarkTheme = viewModel.darkMode.collectAsState().value == 1) { - Scaffold( - topBar = { - CenterAlignedTopAppBar( - title = { - Text( - stringResource(id = R.string.navigation_settings), - ) - }, + Scaffold( + topBar = { + TopAppBar( + title = { + Text( + stringResource(id = R.string.navigation_settings), + ) + }, navigationIcon = { IconButton(onClick = navigateBack) { Icon( @@ -57,19 +60,17 @@ fun NavigationScreen(viewModel: SettingsViewModel, navigateBack: () -> Unit) { ) } }, - ) - }, - ) { padding -> - val scrollState = rememberScrollState() - Column( - modifier = - Modifier - .consumeWindowInsets(padding) - .verticalScroll(scrollState) - .padding(top = padding.calculateTopPadding()), - ) { - NavigationSettings(viewModel) - } + ) + }, + ) { padding -> + val scrollState = rememberScrollState() + Column( + modifier = + Modifier + .padding(padding) + .verticalScroll(scrollState) + ) { + NavigationSettings(viewModel) } } } @@ -82,49 +83,63 @@ fun NavigationSettings(viewModel: SettingsViewModel) { val routingEngine by viewModel.routingEngine.collectAsState() val tomTomApiKey by viewModel.tomTomApiKey.collectAsState() - SettingSwitch( - title = stringResource(R.string.avoid_highways_row_title), - checked = avoidMotorway, - onCheckedChange = viewModel::onAvoidMotorway - ) + OutlinedCard(modifier = Modifier.fillMaxWidth()) { + Column( + modifier = Modifier.padding(10.dp), + ) { + SettingSwitch( + title = stringResource(R.string.avoid_highways_row_title), + checked = avoidMotorway, + onCheckedChange = viewModel::onAvoidMotorway + ) - SettingSwitch( - title = stringResource(R.string.avoid_tolls_row_title), - checked = avoidTollway, - onCheckedChange = viewModel::onAvoidTollway - ) + SettingSwitch( + title = stringResource(R.string.avoid_tolls_row_title), + checked = avoidTollway, + onCheckedChange = viewModel::onAvoidTollway + ) - SettingSwitch( - title = stringResource(R.string.use_car_location), - checked = carLocation, - onCheckedChange = viewModel::onCarLocation - ) + SettingSwitch( + title = stringResource(R.string.use_car_location), + checked = carLocation, + onCheckedChange = viewModel::onCarLocation + ) + } + } - SectionTitle(stringResource(R.string.routing_engine)) + Spacer(modifier = Modifier.height(20.dp)) - val routingEngineOptions = listOf( - stringResource(R.string.valhalla), - stringResource(R.string.osrm), - stringResource(R.string.tomtom) - ) - RadioButtonSingleSelection( - modifier = Modifier.padding(), - selectedOption = routingEngine, - radioOptions = routingEngineOptions, - onClick = viewModel::onRoutingEngineChanged - ) + OutlinedCard(modifier = Modifier.fillMaxWidth()) { + Column( + modifier = Modifier.padding(10.dp), + ) { + SectionTitle(stringResource(R.string.routing_engine)) - if (routingEngine == RouteEngine.TOMTOM.ordinal) { - var key by remember { mutableStateOf(tomTomApiKey) } - TextField( - value = key, - onValueChange = { - viewModel.onTomTomApiKeyChanged(it) - key = it - }, - label = { Text(stringResource(R.string.tomtom_api_key)) }, - textStyle = TextStyle(color = Color.Green, fontWeight = FontWeight.Bold), - modifier = Modifier.padding(20.dp) - ) + val routingEngineOptions = listOf( + stringResource(R.string.valhalla), + stringResource(R.string.osrm), + stringResource(R.string.tomtom) + ) + RadioButtonSingleSelection( + modifier = Modifier.padding(), + selectedOption = routingEngine, + radioOptions = routingEngineOptions, + onClick = viewModel::onRoutingEngineChanged + ) + + if (routingEngine == RouteEngine.TOMTOM.ordinal) { + var key by remember { mutableStateOf(tomTomApiKey) } + TextField( + value = key, + onValueChange = { + viewModel.onTomTomApiKeyChanged(it) + key = it + }, + label = { Text(stringResource(R.string.tomtom_api_key)) }, + textStyle = TextStyle(color = Color.Green, fontWeight = FontWeight.Bold), + modifier = Modifier.padding(20.dp) + ) + } + } } } \ No newline at end of file diff --git a/app/src/main/java/com/kouros/navigation/ui/settings/Settings.kt b/app/src/main/java/com/kouros/navigation/ui/settings/Settings.kt new file mode 100644 index 0000000..9d125dd --- /dev/null +++ b/app/src/main/java/com/kouros/navigation/ui/settings/Settings.kt @@ -0,0 +1,120 @@ +package com.kouros.navigation.ui.settings + +import androidx.appcompat.app.AppCompatDelegate +import androidx.appcompat.app.AppCompatDelegate.MODE_NIGHT_NO +import androidx.appcompat.app.AppCompatDelegate.MODE_NIGHT_YES +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.RowScope +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.items +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.Icon +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.OutlinedCard +import androidx.compose.material3.Scaffold +import androidx.compose.material3.Text +import androidx.compose.material3.TopAppBar +import androidx.compose.runtime.Composable +import androidx.compose.runtime.collectAsState +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.unit.dp +import androidx.navigation.NavHostController +import com.kouros.data.R +import com.kouros.navigation.model.SettingsViewModel +import com.kouros.navigation.ui.theme.NavigationTheme + +data class Settings(val id: String, val name: String, val icon: Int) + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun Settings( + viewModel: SettingsViewModel, + navController: NavHostController, + navigateBack: () -> Unit +) { + + + val items = listOf( + Settings( + id = "favorites_screen", + name = "Favoriten", + icon = R.drawable.ic_favorite_white_24dp + ), + Settings( + id = "settings_screen", + name = "Einstellungen", + icon = R.drawable.speed_camera_24px + ), + Settings(id = "info_screen", name = "Info", icon = R.drawable.ic_place_white_24dp), + ) + + Scaffold( + topBar = { + TopAppBar( + title = { Text(stringResource(id = R.string.settings_action_title)) }, + ) + }, + ) { paddingValues -> + LazyColumn( + modifier = Modifier + .padding(paddingValues) + .padding(8.dp), + ) { + item { + Spacer(modifier = Modifier.height(10.dp)) + } + items(items) { subItem -> + ScreenItem(item = subItem, onClick = { navController.navigate(subItem.id) }) + Spacer(modifier = Modifier.height(10.dp)) + } + } + + } +} + +@Composable +fun ScreenItem( + item: Settings, + onClick: (Settings) -> Unit, +) { + OutlinedCard(onClick = { onClick(item) }, modifier = Modifier.fillMaxWidth()) { + Row( + modifier = Modifier.padding(10.dp), + horizontalArrangement = Arrangement.spacedBy(10.dp), + verticalAlignment = Alignment.CenterVertically, + ) { + Icon( + painter = painterResource(item.icon), + contentDescription = stringResource(id = R.string.accept_action_title), + modifier = Modifier.align(Alignment.CenterVertically), + ) + Column( + modifier = Modifier.weight(1f), + verticalArrangement = Arrangement.spacedBy(8.dp), + ) { + Text(text = item.name, style = MaterialTheme.typography.titleMedium) + } + IconForward() + } + } +} + +@Composable +fun RowScope.IconForward() { + Icon( + painter = painterResource(R.drawable.arrow_back_24px), + contentDescription = stringResource(id = R.string.on_action_title), + modifier = Modifier.align(Alignment.CenterVertically), + ) +} + + diff --git a/app/src/main/java/com/kouros/navigation/ui/settings/SettingsRoute.kt b/app/src/main/java/com/kouros/navigation/ui/settings/SettingsRoute.kt index cd11265..dce7e9d 100644 --- a/app/src/main/java/com/kouros/navigation/ui/settings/SettingsRoute.kt +++ b/app/src/main/java/com/kouros/navigation/ui/settings/SettingsRoute.kt @@ -34,6 +34,9 @@ fun SettingsRoute(route: String, navController: NavHostController, function: () NavigationScreen (viewModel = viewModel, function) } if (route == "settings") { + Settings(viewModel, navController, function) + } + if (route == "settings_screen") { SettingsScreen(viewModel, navController, function) } } diff --git a/app/src/main/java/com/kouros/navigation/ui/settings/SettingsScreen.kt b/app/src/main/java/com/kouros/navigation/ui/settings/SettingsScreen.kt index 4a22154..2285bb7 100644 --- a/app/src/main/java/com/kouros/navigation/ui/settings/SettingsScreen.kt +++ b/app/src/main/java/com/kouros/navigation/ui/settings/SettingsScreen.kt @@ -1,20 +1,24 @@ package com.kouros.navigation.ui.settings +import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.consumeWindowInsets +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +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.rememberScrollState -import androidx.compose.foundation.verticalScroll -import androidx.compose.material3.Button -import androidx.compose.material3.CenterAlignedTopAppBar +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.items import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Icon -import androidx.compose.material3.IconButton +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.OutlinedCard import androidx.compose.material3.Scaffold import androidx.compose.material3.Text +import androidx.compose.material3.TopAppBar import androidx.compose.runtime.Composable import androidx.compose.runtime.collectAsState +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource @@ -24,49 +28,79 @@ import com.kouros.data.R import com.kouros.navigation.model.SettingsViewModel import com.kouros.navigation.ui.theme.NavigationTheme + +data class Item(val id: String, val name: String, val description: String, val icon: Int) + @OptIn(ExperimentalMaterial3Api::class) @Composable -fun SettingsScreen(viewModel: SettingsViewModel, navController: NavHostController, navigateBack: () -> Unit) { - NavigationTheme(useDarkTheme = viewModel.darkMode.collectAsState().value == 1) { - Scaffold( - topBar = { - CenterAlignedTopAppBar( - title = { - Text( - stringResource(id = R.string.settings_action_title), - ) - }, - navigationIcon = { - IconButton(onClick = navigateBack) { - Icon( - painter = painterResource(R.drawable.arrow_back_24px), - contentDescription = stringResource(id = R.string.accept_action_title), - modifier = Modifier.size(48.dp, 48.dp), - ) - } - }, - ) - }, - ) { padding -> - val scrollState = rememberScrollState() - Column( - modifier = - Modifier - .consumeWindowInsets(padding) - .verticalScroll(scrollState) - .padding(top = padding.calculateTopPadding()), - ) { - Column(modifier = Modifier.padding(16.dp)) { +fun SettingsScreen( + viewModel: SettingsViewModel, + navController: NavHostController, + navigateBack: () -> Unit +) { - Button(onClick = { navController.navigate("display_settings") }) { - Text(stringResource(R.string.display_settings)) - } - Button(onClick = { navController.navigate("nav_settings") }) { - Text(stringResource(R.string.navigation_settings)) - } - } + val items = listOf( + Item( + id = "display_settings", + name = "Display Settings", + description = "", + icon = R.drawable.dark_mode_24px + ), + Item( + id = "nav_settings", + name = "Navigation Settings", + description = "", + icon = R.drawable.navigation_24px + ) + ) + + Scaffold( + topBar = { + TopAppBar( + title = { Text("Settings") }, + ) + }, + ) { paddingValues -> + LazyColumn( + modifier = Modifier + .padding(paddingValues) + .padding(8.dp), + ) { + item { + Spacer(modifier = Modifier.height(10.dp)) + } + items(items) { subItem -> + ScreenItem(item = subItem, onClick = { navController.navigate(subItem.id) }) + Spacer(modifier = Modifier.height(10.dp)) } } } } +@Composable +fun ScreenItem( + item: Item, + onClick: (Item) -> Unit, +) { + OutlinedCard(onClick = { onClick(item) }, modifier = Modifier.fillMaxWidth()) { + Row( + modifier = Modifier.padding(10.dp), + horizontalArrangement = Arrangement.spacedBy(10.dp), + verticalAlignment = Alignment.CenterVertically, + ) { + Icon( + painter = painterResource(item.icon), + contentDescription = stringResource(id = R.string.accept_action_title), + modifier = Modifier.align(Alignment.CenterVertically), + ) + Column( + modifier = Modifier.weight(1f), + verticalArrangement = Arrangement.spacedBy(8.dp), + ) { + Text(text = item.name, style = MaterialTheme.typography.titleMedium) + } + IconForward() + } + } +} + diff --git a/app/src/main/java/com/kouros/navigation/ui/theme/Theme.kt b/app/src/main/java/com/kouros/navigation/ui/theme/Theme.kt index 63ed86f..6f7acee 100644 --- a/app/src/main/java/com/kouros/navigation/ui/theme/Theme.kt +++ b/app/src/main/java/com/kouros/navigation/ui/theme/Theme.kt @@ -84,28 +84,24 @@ private val DarkColors = darkColorScheme( @Composable fun NavigationTheme( useDarkTheme: Boolean = isSystemInDarkTheme(), + dynamicColor: Boolean = true, content: @Composable () -> Unit ) { - val context = LocalContext.current - val colors = run { - if (useDarkTheme) dynamicDarkColorScheme(context) - else dynamicLightColorScheme(context) - } - val view = LocalView.current - if (!view.isInEditMode) { - SideEffect { - val window = (view.context as Activity).window - window.statusBarColor = colors.primary.toArgb() - WindowCompat.getInsetsController(window, view).isAppearanceLightStatusBars = - useDarkTheme + val colorScheme = when { + dynamicColor -> { + val context = LocalContext.current + if (useDarkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context) } - MaterialTheme( - colorScheme = if (useDarkTheme) DarkColors else colorScheme, - typography = typography, - content = content, - shapes = shapes, - ) + useDarkTheme -> DarkColors + else -> LightColors } + MaterialTheme( + + colorScheme = colorScheme, + typography = typography, + content = content, + shapes = shapes, + ) } \ No newline at end of file diff --git a/common/car/src/androidTest/java/com/kouros/navigation/car/RouteModelTest.kt b/common/car/src/androidTest/java/com/kouros/navigation/car/RouteModelTest.kt index b189008..e41a965 100644 --- a/common/car/src/androidTest/java/com/kouros/navigation/car/RouteModelTest.kt +++ b/common/car/src/androidTest/java/com/kouros/navigation/car/RouteModelTest.kt @@ -176,15 +176,21 @@ class RouteModelTest { @Test fun simulate() { for ((index, waypoint) in routeModel.curRoute.waypoints.withIndex()) { - val curLocation = location(waypoint[0], waypoint[1]) if (routeModel.isNavigating()) { + val curLocation = location(waypoint[0], waypoint[1]) if (index in 0..routeModel.curRoute.waypoints.size) { //runBlocking { delay(1000) } val start = System.currentTimeMillis() routeModel.updateLocation(curLocation, NavigationViewModel(TomTomRepository())) val stepData = routeModel.currentStep() - println("${stepData.instruction} ${System.currentTimeMillis() - start}") - // val nextData = routeModel.nextStep() + //println("${stepData.instruction} ${System.currentTimeMillis() - start}") + if (stepData.lane.isNotEmpty()) { + println(stepData.street) + stepData.lane.forEach { + println("${it.indications} ${it.valid}") + } + } + // val nextData = routeModel.nextStep() } } } @@ -192,17 +198,17 @@ class RouteModelTest { @Test fun `leftStepDistance Inglolstädter `() { - val location: Location = location( 11.584578, 48.183653) - routeModel.updateLocation(location, NavigationViewModel(TomTomRepository()) ) + val location: Location = location(11.584578, 48.183653) + routeModel.updateLocation(location, NavigationViewModel(TomTomRepository())) val step = routeModel.currentStep() assertEquals(step.leftStepDistance, 645.0, 1.0) } @Test fun `leftStepDistance Vogelhart `() { - val location: Location = location( 11.578911, 48.185565) - routeModel.updateLocation(location, NavigationViewModel(TomTomRepository()) ) + val location: Location = location(11.578911, 48.185565) + routeModel.updateLocation(location, NavigationViewModel(TomTomRepository())) val step = routeModel.currentStep() - assertEquals(step.leftStepDistance , 34.0, 1.0) + assertEquals(step.leftStepDistance, 34.0, 1.0) } } \ No newline at end of file diff --git a/common/car/src/main/java/com/kouros/navigation/car/map/MapView.kt b/common/car/src/main/java/com/kouros/navigation/car/map/MapView.kt index 0a4dcfe..9410eda 100644 --- a/common/car/src/main/java/com/kouros/navigation/car/map/MapView.kt +++ b/common/car/src/main/java/com/kouros/navigation/car/map/MapView.kt @@ -2,13 +2,11 @@ package com.kouros.navigation.car.map import android.location.Location import androidx.compose.foundation.Canvas -import androidx.compose.foundation.isSystemInDarkTheme import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size -import androidx.compose.foundation.text.BasicText import androidx.compose.material3.Icon import androidx.compose.runtime.Composable import androidx.compose.runtime.remember @@ -17,10 +15,8 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.draw.scale import androidx.compose.ui.geometry.CornerRadius import androidx.compose.ui.geometry.Offset -import androidx.compose.ui.geometry.Size import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.drawscope.scale -import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.res.painterResource import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.drawText @@ -36,7 +32,6 @@ import com.kouros.navigation.data.NavigationColor import com.kouros.navigation.data.RouteColor import com.kouros.navigation.data.SpeedColor import com.kouros.navigation.utils.isMetricSystem -import com.kouros.navigation.utils.location import org.maplibre.compose.camera.CameraPosition import org.maplibre.compose.camera.CameraState import org.maplibre.compose.camera.rememberCameraState @@ -63,7 +58,6 @@ import org.maplibre.compose.sources.Source import org.maplibre.compose.sources.getBaseSource import org.maplibre.compose.sources.rememberGeoJsonSource import org.maplibre.compose.style.BaseStyle -import org.maplibre.spatialk.geojson.BoundingBox import org.maplibre.spatialk.geojson.Position diff --git a/common/car/src/main/java/com/kouros/navigation/car/navigation/RouteCarModel.kt b/common/car/src/main/java/com/kouros/navigation/car/navigation/RouteCarModel.kt index 4ae6c26..dabbbb8 100644 --- a/common/car/src/main/java/com/kouros/navigation/car/navigation/RouteCarModel.kt +++ b/common/car/src/main/java/com/kouros/navigation/car/navigation/RouteCarModel.kt @@ -114,7 +114,11 @@ class RouteCarModel : RouteModel() { ) .setRemainingTimeColor(CarColor.GREEN) .setRemainingDistanceColor(CarColor.BLUE) - .setTripText(CarText.create("$traffic min")) + if (traffic > 0) { + travelBuilder.setTripText(CarText.create("$traffic min")) + travelBuilder.setTripIcon(createCarIcon(carContext, R.drawable.warning_24px)) + } + if (navState.travelMessage.isNotEmpty()) { travelBuilder.setTripIcon(createCarIcon(carContext, R.drawable.warning_24px)) travelBuilder.setTripText(CarText.create(navState.travelMessage)) @@ -143,7 +147,7 @@ class RouteCarModel : RouteModel() { } val laneType = Lane.Builder() - .addDirection(LaneDirection.create(laneDirection, true)) + .addDirection(LaneDirection.create(laneDirection, it.valid)) .build() step.addLane(laneType) } diff --git a/common/car/src/main/java/com/kouros/navigation/car/navigation/Simulation.kt b/common/car/src/main/java/com/kouros/navigation/car/navigation/Simulation.kt index 0021673..bf2f0b1 100644 --- a/common/car/src/main/java/com/kouros/navigation/car/navigation/Simulation.kt +++ b/common/car/src/main/java/com/kouros/navigation/car/navigation/Simulation.kt @@ -4,6 +4,8 @@ import android.location.Location import android.location.LocationManager import android.os.SystemClock import androidx.lifecycle.LifecycleCoroutineScope +import com.kouros.navigation.data.Constants.homeVogelhart +import com.kouros.navigation.utils.location import kotlinx.coroutines.Job import kotlinx.coroutines.delay import kotlinx.coroutines.launch @@ -17,6 +19,10 @@ class Simulation { lifecycleScope: LifecycleCoroutineScope, updateLocation: (Location) -> Unit ) { + // A92 + //updateLocation(location(11.709508, 48.338923 )) + //updateLocation(homeVogelhart) + if (routeModel.navState.route.isRouteValid()) { val points = routeModel.curRoute.waypoints if (points.isEmpty()) return @@ -38,7 +44,7 @@ class Simulation { // Update your app's state as if a real GPS update occurred updateLocation(fakeLocation) // Wait before moving to the next point (e.g., every 1 second) - delay(1000) + delay(500) lastLocation = fakeLocation } routeModel.stopNavigation() diff --git a/common/car/src/main/java/com/kouros/navigation/car/screen/CategoriesScreen.kt b/common/car/src/main/java/com/kouros/navigation/car/screen/CategoriesScreen.kt index 71d2b8a..3432a40 100644 --- a/common/car/src/main/java/com/kouros/navigation/car/screen/CategoriesScreen.kt +++ b/common/car/src/main/java/com/kouros/navigation/car/screen/CategoriesScreen.kt @@ -1,5 +1,6 @@ package com.kouros.navigation.car.screen +import androidx.activity.OnBackPressedCallback import androidx.car.app.CarContext import androidx.car.app.Screen import androidx.car.app.model.Action @@ -40,8 +41,17 @@ class CategoriesScreen( Category(id = CHARGING_STATION, name = carContext.getString(R.string.charging_station)) ) + private val backPressedCallback = object : OnBackPressedCallback(false) { + override fun handleOnBackPressed() { + navigationViewModel.elements.value = emptyList() + invalidate() + } + } + init { + navigationViewModel.elements.value = emptyList() navigationViewModel.elements.observe(this, categoryObserver) + carContext.onBackPressedDispatcher.addCallback(this, backPressedCallback) } override fun onGetTemplate(): Template { diff --git a/common/car/src/main/java/com/kouros/navigation/car/screen/NavigationScreen.kt b/common/car/src/main/java/com/kouros/navigation/car/screen/NavigationScreen.kt index 0cd422b..59f97ef 100644 --- a/common/car/src/main/java/com/kouros/navigation/car/screen/NavigationScreen.kt +++ b/common/car/src/main/java/com/kouros/navigation/car/screen/NavigationScreen.kt @@ -530,8 +530,7 @@ class NavigationScreen( * Pushes the search screen and handles the search result. */ private fun startSearchScreen() { - navigationViewModel.recentPlaces.value = emptyList() - navigationViewModel.previewRoute.value = "" + screenManager .pushForResult( SearchScreen( diff --git a/common/car/src/main/java/com/kouros/navigation/car/screen/PlaceListScreen.kt b/common/car/src/main/java/com/kouros/navigation/car/screen/PlaceListScreen.kt index 303deb6..295358f 100644 --- a/common/car/src/main/java/com/kouros/navigation/car/screen/PlaceListScreen.kt +++ b/common/car/src/main/java/com/kouros/navigation/car/screen/PlaceListScreen.kt @@ -39,11 +39,12 @@ class PlaceListScreen( private val places: List ) : Screen(carContext) { - val routeModel = RouteCarModel() var place = Place() + var mPlaces = mutableListOf() + val previewObserver = Observer { route -> if (route.isNotEmpty()) { val repository = getSettingsRepository(carContext) @@ -70,21 +71,25 @@ class PlaceListScreen( } init { - loadPlaces() + // loadPlaces() + navigationViewModel.recentPlaces.value = emptyList() + navigationViewModel.previewRoute.value = "" + + mPlaces.addAll(places) navigationViewModel.previewRoute.observe(this, previewObserver) } override fun onGetTemplate(): Template { val itemListBuilder = ItemList.Builder() .setNoItemsMessage(carContext.getString(R.string.no_places)) - places.forEach { + mPlaces.forEach { val street = if (it.street != null) { it.street } else { "" } val row = Row.Builder() - // .setImage(contactIcon(it.avatar, it.category)) + .setImage(contactIcon(null, it.category)) .setTitle("$street ${it.city}") .setOnClickListener { place = Place( @@ -155,7 +160,7 @@ class PlaceListScreen( carContext, R.string.recent_Item_deleted, CarToast.LENGTH_LONG ).show() - loadPlaces() + mPlaces.remove(place) invalidate() } .build() diff --git a/common/car/src/main/java/com/kouros/navigation/car/screen/settings/NavigationSettings.kt b/common/car/src/main/java/com/kouros/navigation/car/screen/settings/NavigationSettings.kt index 476b1e4..00918e0 100644 --- a/common/car/src/main/java/com/kouros/navigation/car/screen/settings/NavigationSettings.kt +++ b/common/car/src/main/java/com/kouros/navigation/car/screen/settings/NavigationSettings.kt @@ -3,16 +3,17 @@ package com.kouros.navigation.car.screen.settings import androidx.car.app.CarContext import androidx.car.app.Screen import androidx.car.app.model.Action +import androidx.car.app.model.CarIcon import androidx.car.app.model.Header import androidx.car.app.model.ItemList import androidx.car.app.model.ListTemplate import androidx.car.app.model.Row import androidx.car.app.model.Template import androidx.car.app.model.Toggle +import androidx.core.graphics.drawable.IconCompat import androidx.lifecycle.lifecycleScope import com.kouros.data.R -import com.kouros.navigation.car.screen.settings.PasswordSettings -import com.kouros.navigation.car.screen.settings.RoutingSettings +import com.kouros.navigation.car.navigation.NavigationUtils import com.kouros.navigation.model.NavigationViewModel import com.kouros.navigation.utils.getSettingsViewModel import kotlinx.coroutines.flow.first @@ -61,7 +62,8 @@ class NavigationSettings( listBuilder.addItem( buildRowForTemplate( R.string.avoid_highways_row_title, - highwayToggle + highwayToggle, + NavigationUtils(carContext).createCarIcon(R.drawable.baseline_add_road_24) ) ) @@ -71,7 +73,7 @@ class NavigationSettings( settingsViewModel.onAvoidTollway(checked) tollWayToggleState = !tollWayToggleState }.setChecked(tollWayToggleState).build() - listBuilder.addItem(buildRowForTemplate(R.string.avoid_tolls_row_title, tollwayToggle)) + listBuilder.addItem(buildRowForTemplate(R.string.avoid_tolls_row_title, tollwayToggle, NavigationUtils(carContext).createCarIcon(R.drawable.baseline_toll_24))) // Ferry val ferryToggle: Toggle = @@ -79,7 +81,7 @@ class NavigationSettings( settingsViewModel.onAvoidFerry(checked) ferryToggleState = !ferryToggleState }.setChecked(ferryToggleState).build() - listBuilder.addItem(buildRowForTemplate(R.string.avoid_ferries, ferryToggle)) + listBuilder.addItem(buildRowForTemplate(R.string.avoid_ferries, ferryToggle, NavigationUtils(carContext).createCarIcon(R.drawable.baseline_directions_boat_filled_24))) // CarLocation val carLocationToggle: Toggle = @@ -91,7 +93,8 @@ class NavigationSettings( listBuilder.addItem( buildRowForTemplate( R.string.use_car_location, - carLocationToggle + carLocationToggle, + NavigationUtils(carContext).createCarIcon(R.drawable.ic_place_white_24dp) ) ) @@ -118,10 +121,11 @@ class NavigationSettings( .build() } - private fun buildRowForTemplate(title: Int, toggle: Toggle): Row { + private fun buildRowForTemplate(title: Int, toggle: Toggle, icon: CarIcon): Row { return Row.Builder() .setTitle(carContext.getString(title)) .setToggle(toggle) + .setImage(icon) .build() } diff --git a/common/data/build.gradle.kts b/common/data/build.gradle.kts index 019543e..6d81d16 100644 --- a/common/data/build.gradle.kts +++ b/common/data/build.gradle.kts @@ -62,7 +62,7 @@ dependencies { implementation(libs.kotlinx.serialization.json) implementation(libs.maplibre.compose) - implementation(libs.androidx.compose.material.icons.extended) + //implementation(libs.androidx.compose.material.icons.extended) testImplementation(libs.junit) testImplementation(libs.mockito.core) testImplementation(libs.mockito.kotlin) diff --git a/common/data/src/main/java/com/kouros/navigation/data/Data.kt b/common/data/src/main/java/com/kouros/navigation/data/Data.kt index 77f2128..2894879 100644 --- a/common/data/src/main/java/com/kouros/navigation/data/Data.kt +++ b/common/data/src/main/java/com/kouros/navigation/data/Data.kt @@ -62,7 +62,7 @@ data class StepData ( var icon: Int, var arrivalTime : Long, var leftDistance: Double, - var lane: List = listOf(Lane(location(0.0, 0.0), valid = false, indications = emptyList())), + var lane: List = listOf(Lane(location(0.0, 0.0), valid = false, indications = emptyList(), 0, 0)), var exitNumber: Int = 0, var message: String = "", ) @@ -116,7 +116,7 @@ object Constants { val homeVogelhart = location(11.5793748, 48.185749) val homeHohenwaldeck = location( 11.594322, 48.1164817) - const val NEXT_STEP_THRESHOLD = 1000.0 + const val NEXT_STEP_THRESHOLD = 500.0 const val MAXIMAL_SNAP_CORRECTION = 50.0 diff --git a/common/data/src/main/java/com/kouros/navigation/data/NavigationState.kt b/common/data/src/main/java/com/kouros/navigation/data/NavigationState.kt index 28b3251..d86e805 100644 --- a/common/data/src/main/java/com/kouros/navigation/data/NavigationState.kt +++ b/common/data/src/main/java/com/kouros/navigation/data/NavigationState.kt @@ -15,6 +15,7 @@ data class NavigationState ( val lastLocation: Location = location(0.0, 0.0), val currentLocation: Location = location(0.0, 0.0), val routeBearing: Float = 0F, + // index of current route in the list of routes val currentRouteIndex: Int = 0, val destination: Place = Place(), val carConnection: Int = 0, diff --git a/common/data/src/main/java/com/kouros/navigation/data/osrm/OsrmRoute.kt b/common/data/src/main/java/com/kouros/navigation/data/osrm/OsrmRoute.kt index e301f32..c2fb4cc 100644 --- a/common/data/src/main/java/com/kouros/navigation/data/osrm/OsrmRoute.kt +++ b/common/data/src/main/java/com/kouros/navigation/data/osrm/OsrmRoute.kt @@ -48,7 +48,9 @@ class OsrmRoute { val lane = Lane( location(it2.location[0], it2.location[1]), it3.valid, - it3.indications + it3.indications, + 0, + 0 ) lanes.add(lane) } diff --git a/common/data/src/main/java/com/kouros/navigation/data/route/Intersection.kt b/common/data/src/main/java/com/kouros/navigation/data/route/Intersection.kt index 25b12fb..ae3f272 100644 --- a/common/data/src/main/java/com/kouros/navigation/data/route/Intersection.kt +++ b/common/data/src/main/java/com/kouros/navigation/data/route/Intersection.kt @@ -4,5 +4,5 @@ import java.util.Collections data class Intersection( val location: List = listOf(0.0, 0.0), - val lane : List = Collections.emptyList(), + val lane : List = Collections.emptyList(), ) diff --git a/common/data/src/main/java/com/kouros/navigation/data/route/Lane.kt b/common/data/src/main/java/com/kouros/navigation/data/route/Lane.kt index 38039c2..5686c74 100644 --- a/common/data/src/main/java/com/kouros/navigation/data/route/Lane.kt +++ b/common/data/src/main/java/com/kouros/navigation/data/route/Lane.kt @@ -6,4 +6,6 @@ data class Lane ( val location: Location, val valid: Boolean, var indications: List, + val startIndex: Int, + val endIndex: Int, ) \ No newline at end of file diff --git a/common/data/src/main/java/com/kouros/navigation/data/route/Leg.kt b/common/data/src/main/java/com/kouros/navigation/data/route/Leg.kt index f01462e..27dc4d2 100644 --- a/common/data/src/main/java/com/kouros/navigation/data/route/Leg.kt +++ b/common/data/src/main/java/com/kouros/navigation/data/route/Leg.kt @@ -2,5 +2,4 @@ package com.kouros.navigation.data.route data class Leg( var steps : List = arrayListOf(), - var intersection: List = arrayListOf() ) diff --git a/common/data/src/main/java/com/kouros/navigation/data/route/Maneuver.kt b/common/data/src/main/java/com/kouros/navigation/data/route/Maneuver.kt index 8502f27..ea97162 100644 --- a/common/data/src/main/java/com/kouros/navigation/data/route/Maneuver.kt +++ b/common/data/src/main/java/com/kouros/navigation/data/route/Maneuver.kt @@ -10,5 +10,6 @@ data class Maneuver( val location: Location, val exit: Int = 0, val street: String = "", - val message: String = "" + val message: String = "", + val pointIndex : Int = 0, ) diff --git a/common/data/src/main/java/com/kouros/navigation/data/tomtom/TomTomRoute.kt b/common/data/src/main/java/com/kouros/navigation/data/tomtom/TomTomRoute.kt index d980502..890195e 100644 --- a/common/data/src/main/java/com/kouros/navigation/data/tomtom/TomTomRoute.kt +++ b/common/data/src/main/java/com/kouros/navigation/data/tomtom/TomTomRoute.kt @@ -35,7 +35,6 @@ class TomTomRoute { } var stepDistance = 0.0 var stepDuration = 0.0 - val allIntersections = mutableListOf() val steps = mutableListOf() var lastPointIndex = 0 for (index in 1..() route.sections?.forEach { section -> - val lanes = mutableListOf() - var startIndex = 0 - section.lanes?.forEach { itLane -> - val lane = Lane( - location( - waypoints[section.startPointIndex][0], - waypoints[section.startPointIndex][1] - ), - itLane.directions.first() == itLane.follow, - itLane.directions - ) - startIndex = section.startPointIndex - lanes.add(lane) - } - intersections.add(Intersection(waypoints[startIndex], lanes)) + if (section.sectionType == "LANES" && section.startPointIndex <= lastPointIndex && section.endPointIndex >= lastPointIndex) { + val lanes = mutableListOf() + var startIndex = 0 + var lastLane: Lane? = null + section.lanes?.forEach { itLane -> + val lane = Lane( + location = location( + waypoints[section.startPointIndex][0], + waypoints[section.startPointIndex][1] + ), + valid = itLane.directions.first() == itLane.follow, + indications = itLane.directions, + startIndex = startIndex, + endIndex = section.endPointIndex + ) + startIndex = section.startPointIndex + if (lastLane == null + || (!(lastLane.valid && lane.valid + && lastLane.indications == lane.indications)) + ) { + lanes.add(lane) + } + lastLane = lane + } + intersections.add(Intersection(waypoints[startIndex], lanes)) + } + stepDistance = + route.guidance.instructions[index].routeOffsetInMeters - stepDistance + stepDuration = + route.guidance.instructions[index].travelTimeInSeconds - stepDuration + val step = Step( + index = stepIndex, + street = street, + distance = stepDistance, + duration = stepDuration, + maneuver = maneuver, + intersection = intersections + ) + stepDistance = route.guidance.instructions[index].routeOffsetInMeters.toDouble() + stepDuration = route.guidance.instructions[index].travelTimeInSeconds.toDouble() + steps.add(step) + stepIndex += 1 } - allIntersections.addAll(intersections) - stepDistance = route.guidance.instructions[index].routeOffsetInMeters - stepDistance - stepDuration = route.guidance.instructions[index].travelTimeInSeconds - stepDuration - val step = Step( - index = stepIndex, - street = street, - distance = stepDistance, - duration = stepDuration, - maneuver = maneuver, - intersection = intersections - ) - stepDistance = route.guidance.instructions[index].routeOffsetInMeters.toDouble() - stepDuration = route.guidance.instructions[index].travelTimeInSeconds.toDouble() - steps.add(step) - stepIndex += 1 } - legs.add(Leg(steps, allIntersections)) + legs.add(Leg(steps)) val routeGeoJson = createLineStringCollection(waypoints) val centerLocation = createCenterLocation(createLineStringCollection(waypoints)) val newRoute = com.kouros.navigation.data.route.Routes( @@ -184,8 +196,7 @@ class TomTomRoute { } "TAKE_EXIT" -> { - newType = androidx.car.app.navigation.model.Maneuver.TYPE_TURN_SHARP_RIGHT - + newType = androidx.car.app.navigation.model.Maneuver.TYPE_TURN_SLIGHT_RIGHT } } return newType @@ -195,8 +206,7 @@ class TomTomRoute { private fun exitNumber( instruction: Instruction ): Int { - return if (instruction.exitNumber == null - || instruction.exitNumber.isEmpty() + return if (instruction.exitNumber.isNullOrEmpty() ) { 0 } else { diff --git a/common/data/src/main/java/com/kouros/navigation/model/IconMapper.kt b/common/data/src/main/java/com/kouros/navigation/model/IconMapper.kt index 12e95c4..34c06f8 100644 --- a/common/data/src/main/java/com/kouros/navigation/model/IconMapper.kt +++ b/common/data/src/main/java/com/kouros/navigation/model/IconMapper.kt @@ -65,12 +65,15 @@ class IconMapper() { currentTurnIcon = R.drawable.ic_roundabout_ccw } + Maneuver.TYPE_U_TURN_LEFT -> { currentTurnIcon = R.drawable.ic_turn_u_turn_left } + Maneuver.TYPE_U_TURN_RIGHT -> { currentTurnIcon = R.drawable.ic_turn_u_turn_right } + Maneuver.TYPE_MERGE_LEFT -> { currentTurnIcon = R.drawable.ic_turn_merge_symmetrical } @@ -136,10 +139,12 @@ class IconMapper() { "right_slight", "slight_right" -> { when (stepData.currentManeuverType) { Maneuver.TYPE_TURN_SLIGHT_RIGHT -> LaneDirection.SHAPE_NORMAL_RIGHT + Maneuver.TYPE_KEEP_RIGHT -> LaneDirection.SHAPE_SLIGHT_RIGHT else -> LaneDirection.SHAPE_UNKNOWN } } + else -> { LaneDirection.SHAPE_UNKNOWN } @@ -151,8 +156,7 @@ class IconMapper() { val bitmaps = mutableListOf() stepData.lane.forEach { lane -> if (lane.indications.isNotEmpty()) { - Collections.sort(lane.indications) - val resource = laneToResource(lane.indications, stepData) + val resource = laneToResource(lane.indications.sorted(), stepData) if (resource.isNotEmpty()) { val id = resourceId(resource) val bitMap = BitmapFactory.decodeResource(context.resources, id) @@ -222,11 +226,21 @@ class IconMapper() { "right" -> if (stepData.currentManeuverType == Maneuver.TYPE_TURN_NORMAL_RIGHT) "${direction}_o" else "${direction}_x" "left" -> if (stepData.currentManeuverType == Maneuver.TYPE_TURN_NORMAL_LEFT) "${direction}_o" else "${direction}_x" - "straight" -> if (stepData.currentManeuverType == Maneuver.TYPE_STRAIGHT) "${direction}_o" else "${direction}_x" + "straight" -> if (stepData.currentManeuverType == Maneuver.TYPE_STRAIGHT + || stepData.currentManeuverType == Maneuver.TYPE_KEEP_LEFT + || stepData.currentManeuverType == Maneuver.TYPE_KEEP_RIGHT + ) "${direction}_o" else "${direction}_x" + "right_slight", "slight_right" -> if (stepData.currentManeuverType == Maneuver.TYPE_TURN_SLIGHT_RIGHT - || stepData.currentManeuverType == Maneuver.TYPE_TURN_NORMAL_RIGHT) "slight_right_o" else "slight_right_x" + || stepData.currentManeuverType == Maneuver.TYPE_TURN_NORMAL_RIGHT + || stepData.currentManeuverType == Maneuver.TYPE_KEEP_RIGHT + ) "slight_right_o" else "slight_right_x" + "left_slight", "slight_left" -> if (stepData.currentManeuverType == Maneuver.TYPE_TURN_SLIGHT_LEFT - || stepData.currentManeuverType == Maneuver.TYPE_TURN_NORMAL_LEFT) "slight_left_o" else "slight_left_x" + || stepData.currentManeuverType == Maneuver.TYPE_TURN_NORMAL_LEFT + || stepData.currentManeuverType == Maneuver.TYPE_KEEP_LEFT + ) "slight_left_o" else "slight_left_x" + else -> { "" } diff --git a/common/data/src/main/java/com/kouros/navigation/model/NavigationViewModel.kt b/common/data/src/main/java/com/kouros/navigation/model/NavigationViewModel.kt index d0f02b5..42d67af 100644 --- a/common/data/src/main/java/com/kouros/navigation/model/NavigationViewModel.kt +++ b/common/data/src/main/java/com/kouros/navigation/model/NavigationViewModel.kt @@ -550,14 +550,15 @@ class NavigationViewModel(private val repository: NavigationRepository) : ViewMo val rp = settingsRepository.recentPlacesFlow.first() val places = mutableListOf() if (rp.isNotEmpty()) { - val recentPlaces = + val rPlaces = gson.fromJson(rp, Places::class.java).places.sortedBy { it.lastDate } - for (curPlace in recentPlaces) { + for (curPlace in rPlaces) { if (curPlace.name != place.name || curPlace.category != place.category) { places.add(curPlace) } } settingsRepository.setRecentPlaces(gson.toJson(Places(places))) + recentPlaces.value = places } } catch (e: Exception) { e.printStackTrace() diff --git a/common/data/src/main/java/com/kouros/navigation/model/RouteCalculator.kt b/common/data/src/main/java/com/kouros/navigation/model/RouteCalculator.kt index e8b595e..cbddb81 100644 --- a/common/data/src/main/java/com/kouros/navigation/model/RouteCalculator.kt +++ b/common/data/src/main/java/com/kouros/navigation/model/RouteCalculator.kt @@ -31,7 +31,7 @@ class RouteCalculator(var routeModel: RouteModel) { } } if (nearestDistance < NEAREST_LOCATION_DISTANCE) { - break; + break } } } diff --git a/common/data/src/main/java/com/kouros/navigation/model/RouteModel.kt b/common/data/src/main/java/com/kouros/navigation/model/RouteModel.kt index b4c6f3a..64fdcdf 100644 --- a/common/data/src/main/java/com/kouros/navigation/model/RouteModel.kt +++ b/common/data/src/main/java/com/kouros/navigation/model/RouteModel.kt @@ -11,6 +11,7 @@ import com.kouros.navigation.data.StepData import com.kouros.navigation.data.route.Lane import com.kouros.navigation.data.route.Leg import com.kouros.navigation.data.route.Routes +import com.kouros.navigation.data.route.Step import com.kouros.navigation.utils.location import kotlin.math.absoluteValue @@ -29,6 +30,9 @@ open class RouteModel { val curLeg: Leg get() = navState.route.routes[navState.currentRouteIndex].legs.first() + val currentStep: Step + get() = navState.route.nextStep(0) + fun startNavigation(routeString: String) { navState = navState.copy( route = Route.Builder() @@ -69,42 +73,11 @@ open class RouteModel { navState = navState.copy(lastLocation = navState.currentLocation) } - fun currentStep(): StepData { - val distanceToNextStep = routeCalculator.leftStepDistance() - // Determine the maneuver type and corresponding icon - val currentStep = navState.route.nextStep(0) - var streetName = currentStep.maneuver.street - var curManeuverType = currentStep.maneuver.type - if (navState.nextStep) { - if (distanceToNextStep > NEXT_STEP_THRESHOLD) { - streetName = currentStep.street - curManeuverType = Maneuver.TYPE_STRAIGHT - } - } - val exitNumber = currentStep.maneuver.exit - val maneuverIcon = navState.iconMapper.maneuverIcon(curManeuverType) - navState = navState.copy(maneuverType = curManeuverType) - // Construct and return the final StepData object - return StepData( - instruction = streetName, - street = currentStep.street, - leftStepDistance = distanceToNextStep, - currentManeuverType = navState.maneuverType, - icon = maneuverIcon, - arrivalTime = routeCalculator.arrivalTime(), - leftDistance = routeCalculator.travelLeftDistance(), - lane = currentLanes(), - exitNumber = exitNumber, - message = currentStep.maneuver.message - ) - } - fun nextStep(): StepData { val distanceToNextStep = routeCalculator.leftStepDistance() - val currentStep = navState.route.nextStep(0) val nextStep = navState.route.nextStep(1) var streetName = nextStep.street - var maneuverType = currentStep.maneuver.type + var maneuverType = currentStep.maneuver.type if (distanceToNextStep < NEXT_STEP_THRESHOLD) { streetName = nextStep.maneuver.street maneuverType = nextStep.maneuver.type @@ -128,16 +101,13 @@ open class RouteModel { private fun currentLanes(): List { var lanes = emptyList() if (navState.route.legs().isNotEmpty()) { - navState.route.legs().first().intersection.forEach { + currentStep.intersection.forEach { if (it.lane.isNotEmpty()) { val distance = navState.lastLocation.distanceTo(location(it.location[0], it.location[1])) - val sectionBearing = - navState.lastLocation.bearingTo(location(it.location[0], it.location[1])) - val bearingDeviation = - (navState.routeBearing.absoluteValue - sectionBearing.absoluteValue).absoluteValue - if (distance < NEXT_STEP_THRESHOLD && bearingDeviation < 10) { + if (distance < NEXT_STEP_THRESHOLD) { lanes = it.lane + return@forEach } } } @@ -145,6 +115,36 @@ open class RouteModel { return lanes } + fun currentStep(): StepData { + val distanceToNextStep = routeCalculator.leftStepDistance() + // Determine the maneuver type and corresponding icon + var streetName = currentStep.maneuver.street + var curManeuverType = currentStep.maneuver.type + if (navState.nextStep) { + if (distanceToNextStep > NEXT_STEP_THRESHOLD) { + streetName = currentStep.street + curManeuverType = Maneuver.TYPE_STRAIGHT + } + } + val exitNumber = currentStep.maneuver.exit + val maneuverIcon = navState.iconMapper.maneuverIcon(curManeuverType) + navState = navState.copy(maneuverType = curManeuverType) + val currentLanes = currentLanes() + // Construct and return the final StepData object + return StepData( + instruction = streetName, + street = currentStep.street, + leftStepDistance = distanceToNextStep, + currentManeuverType = navState.maneuverType, + icon = maneuverIcon, + arrivalTime = routeCalculator.arrivalTime(), + leftDistance = routeCalculator.travelLeftDistance(), + lane = currentLanes, + exitNumber = exitNumber, + message = currentStep.maneuver.message + ) + } + fun isNavigating(): Boolean { return navState.navigating } diff --git a/common/data/src/main/res/drawable/baseline_add_road_24.xml b/common/data/src/main/res/drawable/baseline_add_road_24.xml new file mode 100644 index 0000000..48bb585 --- /dev/null +++ b/common/data/src/main/res/drawable/baseline_add_road_24.xml @@ -0,0 +1,26 @@ + + + + + + + + + + diff --git a/common/data/src/main/res/drawable/baseline_directions_boat_filled_24.xml b/common/data/src/main/res/drawable/baseline_directions_boat_filled_24.xml new file mode 100644 index 0000000..0c67f81 --- /dev/null +++ b/common/data/src/main/res/drawable/baseline_directions_boat_filled_24.xml @@ -0,0 +1,21 @@ + + + + + diff --git a/common/data/src/main/res/drawable/baseline_toll_24.xml b/common/data/src/main/res/drawable/baseline_toll_24.xml new file mode 100644 index 0000000..a97a063 --- /dev/null +++ b/common/data/src/main/res/drawable/baseline_toll_24.xml @@ -0,0 +1,22 @@ + + + + + + diff --git a/common/data/src/main/res/drawable/left_x.png b/common/data/src/main/res/drawable/left_x.png index 21d0752d6b797b56d35c9c8b5545d4870f119623..d117cee2184894e2865b5a3097e274d434d2e70c 100644 GIT binary patch delta 1247 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 V{KD#{WkLV|002ovPDHLkV1leEvUvai diff --git a/common/data/src/main/res/drawable/left_x_straight_o.png b/common/data/src/main/res/drawable/left_x_straight_o.png index ac944d63754fb77ff59f70ef9c53b36c6ed37799..7aed0b0814ccfb6c8952f5b2d8aee46bb75eb4c6 100644 GIT binary patch delta 1379 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 X5lkTF$~im$000R9NkvXXu0mjf9u@6^ diff --git a/common/data/src/main/res/drawable/none.png b/common/data/src/main/res/drawable/none.png deleted file mode 100644 index c1248e5c0cfc5867a09b2eef7310f3befeb26425..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4759 zcmeHLc~BHr8gI{0!~>0p2b*??#trmL_w=0Y3@ie2$S8pkCQ2-Nx_gF(ndwPS!;Ij8 zM1w*+fFz^{(G4nEMhO~?K}DkI?iRvkjahH;pmtr41QU%hYpMHsxGSmJvUdHCshZ<` z@B4o5`+nc=z3MmXlIF*b_M74dLD1;DC> zSvvy@OI-}icv&k1c`vSiJ#s@6?0@8b@$$mG>sQ^}G_&-fWcdmS+FFym{lMxU{As=R zy7%ek>pLWq##Hiqv*wQ2v#9=)SxRcEu55$8P!mz&2|qG6=)H`Jk)g9!7Q_Yzr*Y%Y zJ(;n;_lL`o*I@_wLaXD>qMEayKI9N{KBWhoC_rY*bWId{k6-<)GfGk2f_W zoJbnCrg3-U+KaOSEv3i5LBbN5>$}DcT@#e%I?z1xjr2rI+`h2K!;h{DZwV~C7G-|T zobTLUhpr#ew9FsB+0yX0edQ=(8wLatCCzyonFbqb^xak@i9h)#@X#!d08 zOJF%CEMk%tE=Mp(rC=WJ&d=d88hhZK?k*MpA9640l4CMd?r_NaM!1ETTmaIQ&~HY# zO<=#tlNmRc!&6L5F5?tJ`a;lDPrNIKxBJSWDLG?j9Kh8LR>k@)85?g*>WL63u(1x8 zFA9*|&r@Kny=3*vO`P$S(>D5z~v0Hb}+w37Vr=n(+Np zkXoxoiIRv#fvOOtTFoF@ic}&>1!Ki6I#i93sy;4&8A&{l?`)k+mY z<5m<=QVJD9S}+tL)flDJU<{^EGJPNx@GMXzY44kr2t@-ZhQTa48r34S)=C2?8f2qJ zb%<7r(^iz$VisEIgQ6*7F2_4a&`#Dt+8DXZY4c5pgcFfT@dl|vhW1*L?4)1?0S0Lz z>&)@?PMBB+lOm8Jo0vweQ7e>cRIO91z>2+2sSNK1N)$0MREDd4Gh$l^Pz)fJ6m<#! ze0ER^5ydm4!0{%Ivm2yhO0XE&Gi?Ohi6#XyniLoSiYn9uiV>*FgsBJ>N}%dcaoES7 zqgm_H|K=@j9$4Sm@;KHF)?ex~b?&GX=B>_A=g`jjwh|2cHU&Xaoh7)*T!!}L39ve+ zs7%snW5DUrCD(2{`x{cwX>n4c(2@vAYjB_;5=AUJ6^>XfN=AueC~n0%)zpLT=B$E; z8UNwt;gpY;D!;;%H(Ria)l9*f;g)ysd?_D+XB=P012(+19G zaNNs#&igLTKuccat4nS#atRn7Sme3*9iVG~uIFOlxts^oH9*&MG4Nc@1M2#}(dE~B zV`QA*LC*v3gFhbLvJl)Q2V3UFMnll9^&tu{8tICA!wo^BCW!ArP-RsRFiM1YV~phX zpkZSa!&ho6)&kRHA;v63aSrj)0r{SV85;JmLMALe3#Y9bGYMSy-SN?prnoAMBdK#8qAhfbavQ~duvw)mQDHg)vU0u zp&w6AmPCGjh9Jwfs;&)+HKb$9+Jy1-m4DIIUbsAI*DpM7zceg3&!$PfJO0*|bpb6qzAO&CHFVU7&jN0&nQ8q~dW?)2T~qLnW?M#H zNya)HUUYjTBuO`)rv`Tv6Ay|??w9X4-?C`a!=GN-*&cSRd9dW|5a+gP_(p#0-B|}` zjIow4-m3Ul;k3*Zr!FUwg{3D~WXFbI8WQjaeQ1V6LYa=%)(2Pa9_>7I>Xq!PYe;Ki zUT{|F7ex)S2W4AQ>#ppI9G*ISV`;>=V~Lule84tS%?vx$@%;`*{^`3(M%3jg&-)z zSi$O)CMgz^7?PCY8~7&rFdYQJhj3JI_P^1=!7N@l{2zyN{{P+!EjtO*a%msZtSVf8 zFuELF*~@RVS*J^nP7s#)!1GRMJfE*vMqm2It9kCv(~06TlrB_Rm=_gTdtzr^<%U&% z^lpd#q4+^;M`;#`pTs*+8W}dbewxg}A5U|eZk%15o4#rrmt#)xuo*LBN-h&AmE;oP?`{{H`h$Xji{89%YkSp(rp^@w(2}jDPws|UBdI*2?dn+oTeD08 ziUK5LV>C7~Ff%nRF*G@3Ei^ecG%aB__GGaE9`UIv9H#0RiH!?Uk zFf%kYFgKH~1#S&9Ix#g=F*7P(Ix;ajGB>kV1`z?1g$7(C zFk@ygGGjSqEjMFkGA%S=V=yf^Gc#i?H8y24W@9*GH8LP(Ix;aj zGB>kY2W|)iXKq`{lgtk-2?Gxd6&uh3E?$!Y5GQ{EF-b&0R7l6|md|SwK@`V7Z)P|4 z2ihjpBK07&81O21DEJ?Uc=Y05=+%n|f}ppeBK|@sD3)4#X|2?@Np|OX*vVu!Y?|G& z7a{MIeKY&X%=gXv&cZDIvyif+ULwIV2OxiP`Ny2lZnoR&#l0U`~ahWT6 zi$i~TRd=-(5av;k<6QnOYk>N<3<5z3Cdu7(|DBN-G% z9^~E-A{=ulaKM%vj^>#uB~8YjQxru3u*AeDDG41OX=hr=N<%m3Vuue1iej*U!|_)R zYGZYQO%}%Fa}2O3DTbKO(+}7<^eT6}h9iHdAby>e7JKrh4pva@vQ8_MR|8ahF5*aD zRB4A%v~`V}*uqNjWiuE+>B-Y-T_zs0$Zb||xNxkra4NuJ$O{zE<~y7{Ri^YQY&4!*_YF!u{}9d*#g=O*EhzT=&pS5=Hqa+aRhv<^kx*%kqDc zly})FZPq<(xOvBxZ2R$kHYU&N8Ka{Usy{TAPqLqz(MRQZU1MD49yV8Z;fK5{%vh46 z`Zm6bDM)Zugu?mIN!aDW`852jdzY1$Y$Y@%2*`)J*z(o3g6;~O*1vh9344&x2*S7K zu+DpwBW9;Ltd4%k1w@#1muYa+zP3x6!b+~+2GVazgrQLjntd`gvsxQmtNf$sf~!rv xu9?TutO%LPra2K7Nk~xow97qi7PF`p{{V*(ut0oX3a2i>h)$5}LsFV5M3CX};R_(jMv z_PL!xHTUt9=A=o?c$Nw@$*!yfRP~`!&*R!~b4N*Xx{3g$9hbg%!moq`G_IrZ(dfO* zzq`41^u_-3tn>osvvt(;I<&L^00AMB9|0GC#841LPgZe7)=IGuixjq78Z0ba6@s7; z;|fNrY?8&rB!(o*;ve`Y`Y~Gwf*)b2VC{X;!ooBT409M}?w$9bW2aG8E$%~>*QM(Z zg3I8_?!IBsp+}e9Fsh1y=bg|RpRbx$y!4G$``n+g6em@rI#=dVQPyDXiJe(p7*?`> zemnFJ#SdaT&hl9NB;Jm**s$64vveB$Xqsz!Nq%u|`pPX_jyc7{X2g^UDK&l7T%98v zc2r$TLhh-ER8tc5W}2zF5mi!hIS>s+x~9c%v+?ufGLptbTE^jyOq#|I^S|F~A7{;` zwJ|TkGHhB4%FcKU?~kw=z}hpcT(@`_E&qO7%Z$^^{PT@p@5fMRQKG{n000emvrPhu z0wg&zWi?|qIb}8O3J?Mya`ltu5GQ{F07*naR7l6|)=z9yRTRhZ&+W{ccBa#5OQC;6 zTCf!f3PChtNDML2n7DDn1~(=eV&cNR%f`4gF>Z`*P$P*u7bcjPsKEuBVoeMR2t@u> z3Y|hxX`HD6oRaTFjM2-?*N+R5xi}) zyb%N1Oe?x=RwU=Pi#FNet-I$oLPiQrVTz>Phl-Bdt4mR2!bXYMf-@?%d(rD*s+Q1H zS7}yO3Urg~bkwXr9nd4ENn}W`4z%gC(Jt>Shu%qTVJYZ7GlreeByE4tjA^m57Ac8X z#RYRp;m3RHvVUwqmp6Q+%aDgvoHFLNg3E^VOIy^g5I+0wk4>vUdkr}37kh2ft76ix z@}76XL4(?qYXNP@@QG~>Fm_e0Vcw%bmpRi$P9 zrs$-H90?PIB)MWzRl$EZwG-p+*pfc|Zm8IzL(wf~bogSa;1`_J>C&BNSpD2qwc4cD z0Z*wKH)fw7YPx8_%Wj5ajn3OhyMtm8ZW!@`Ps6pEsyv*uhq&=E-e#+_NQ<4+p(h+) zQSR|twnIjz5!{k6Y$;+Dbra!_@>w}G)m6v}p?{^#AXngb5$L^3b zt*RvDjFHB%^$V<3EN@O(;*inxQrn|hT2<8^srv1?7PYZhR>lVN#+Jt9eXCtCBP&R` z?AG!nc|U41Zklk#xT~I6%Nwk|+GkuaX4bsK>Gi{&*Q;AaN{hbr!^Xx;3W6z*t_52^ qw|i`{%U%mU(e@nRwf?}?Zhry$5$aq&k+f$30000E|>ll5EMvt2@E0T9pQ;(6F^5lXdJSYvlOOr*}d?DQ+EMmEd>q@HyfdLwMHDM$_R2 zZ@Ch!1;5_;H5y9!Y!nKV$7I^#gJ`4@ifqJ>P~^4b+taoO-CM0($25yC&e?z_l(Sd( zMaVMtxt&5a_wkhGq)E(pmI^e4tR2&osame7>(SYD!RS1GYj1{a- zX_8_wi6KcTzJYI|57R*qd6bKGSL7|Y8f+>y%DY0DO@e33JPWH~KjGGjL_Ff%eUWHU51VK_85lllaw4Kp=2Ha9aiI5#pl zGBuN~1#k;7HB>P(Ix#XjF*cJa1|thGHB>P(Ix#XjF*dVT2D$^2E(u&DGBY?bWnp7D zEi*Y{V=Xi?G-fS0WHmW0IW#jiV_{@vV=y#fldcI$3o$iRF*7iqP7pMPJ&@_Jh6 zBjwA(bpEDp>zbC-7|XZP2(7-&0O$@e0z`*Rdp9jM4V-Q;gS?e}ez0PN*efh@1bE;L zuCu{syPImxrsK0*CmB}9(c+_zcKNXz;fu3ajvEoyYLf$ut9&dd8dKo9^FqfDoPUmU>^a3}0u+ErR z9cPJJ3f-Rz^I^ljhJZq>&asFa3S#U*cWosLD7N-A5s5cqfrcS_UaEp+unQR-qviQk ztd=Z{8EhY;hQlx%*kj%fa#w8GS{|U-2{pjs5XLWC2KEMXJ$qGux{PPA`;j;HY#D$y z{j`p~AC>3D)>;Zp*hvRs^@zZpj6Oca*lzl4+RMRC&(yH?n7u5(CM1BdV=sbqX#e@J z7u5s-3A-V5qRyMKHU@iIK|>F)F$!)= z0nQwBO4*7T0a~0x2#H}LSI$Hj-lQ9l|DCsugV zm8jME`OeSYkn?vVS0J%Zrqw_2M#8R0d%U>bswk~u>zFUq;Vw)@`g;l1MtiaDq zny}CH5GtvSr!XcBV#K4Ap@}z%La-v%RmyQpDsE|(0}4@ufF(C-YKtfQN=QKC(i`uU z&Sie?=GxH@{PV2v2J0$y+t9gF1^@s7B9k5g9e@3gPKrZu$l%)1P7YRuAXJF4g4HQa zS}f+qkfapfz&Fu{=^zL`grkD9-;E9qX7R$|`*Aqu`|iEaaFfUs3kP7*GWUY+;Bs)~ zF2B*F!742l!>Gu*zTczve7@pXeQ8^-`nf;PQamam>0FgYSzdy(FYe6B%(9BD+kS8) zet!_Vu}Nd`lXy2av1Pj(m}DCLc-q^nkJ5{C+gI)2al$D+E<>hFNGNGbQ+1B%uqXA% z3Av{rk|vDQn`x%xMpVej<51KW={7Zatt!t~%1|B?dD(XN>?~kxCfX!!EyROMv`Te$aiUK4x zWjJPJF=ApZG+{S4Ei_{}Vl6miHexL>FflVSG%#UdGdD7m`vj*AGBh+ZHaImmFgQ6d zFq5wZa0@UpR536*GB`RgHIpg^BMUGxR536*GB`RgHM3X-x&yQI3SbBX5>MT^lUWlk z2m%Ql1rKhrLX(dZCw~J8NklMA z2OAqb)o;$6`Of*B-+9dN?@7$W`*A70iQ{o#lGsBi+a5qlmU zc{I+~$Q$uX{1y?NIP$R2=i^EYOBJ``LcCGme=Uy3iFh#@8odQx6%DwQcq_0_aw!J_05ug8q%%S`X*_kd_~S?c-y8nX&m2%)FYmfW=8Td`v!Y!f|g>>BWO)+-(pGHQP_2@RR#r_>pDWP{p< z25T{n+)JL-)KiF|;3b)PE6$D2gf;1D=qa?cAzEJcTz|dk5HeBc$z7j!JC&-9v4w`B zzxjRMwnL^+=n1(HLWN8+>+7`epuAm^R)71gIN$}lY^-ne-0*Uo9` z=;$et{C}9fjND;KQn}@Hip^t}&D5(^rLLX|EuW;TXxeE?Pvv4-9o=e1$9Cty;T1}i z(hg_SRdC0Owkh*2Y_5Z?Nj~t}N~O|p-S>6i%gFpKOQ$y57}q<&<4QRSRb5qIq;~&A z&&15VJ!|tp2uzt%YWgT$8cLPr2TzA=Y&ql-GJkWHoK5pv8(*WbZ4}xl{oznLv(>3M zI<|MT6f$)EuH5>t9gUss$G4^H^Q~jEG37v8iNen9csm-qNhDR-I=d)`Rc8@u!&XH1cQrN|Txw^WYJQ#@`D%6I?cCe!# qDkbIa4(J>9Pxk*IMe@J=cjOpHh+$FzuN=VCw;DpJK8{3>Jx``k~VTE=)v zYtkZCyh;U{Vf3Zr#P!xv0w4x#v2c?5JWN__hTpX+lK~RXXg4HQa zQYo|GgJlb`qxL!ak%~Rk(hCcsabX zm)>ZzMu#rDL0IN}&pV;bdR<96U5 ziXX&wlxC6mNxU7Ukzupzr^z(@@ie#T#M#BU>8rMIIp!1(n-NncBvkaInL0-}>_}Y- z0`4gZr3tZmGtE@oh)OxR9Eb)Y-KHULv(EFBGLpwcUMAs=RGuae^S|F~A7}NZ)t%=- z5!9`=sx=``Flc}{VmLG{IWuEsEihs-FlI72Wn*G9Vw3#@rVckVH8(djGdVUlH!(Lk zld%PE4Kq41HB>P(Ix;glH93Q1uT5J@P%MmAk0pUqRK~zY`?Uu`K(?Ar4zklMSB$78EA*k9# zp+!QK`U%?Od7KonR9LwMQbLKzC zhO79;LS;#N#Do^Y(bkh({sBG49PmNk=VPPN?9v6v%g01?h*`E{cJ;b0_97C0Z*@YP zZq;MW?sCeIow5VF3aF=6&vKg?30*dLY!7X-o?0s`#fT1;J`sjt^tf-E?CE)!+T;@W zUJ9LVIz7zq4K~RxB;qEWQk4S4sh){`&pV8M_yTKYl2;-%Wn#=@$vo{(73BmzlkoRiXm0gba zDdYqPIDWEIZ-ONyxgmqJDkJ+wL^m5ee^7)yj`b@m_qN44tUtz!B^dL1Nnbs89nM$b zg0%L1Xmx~OM|xNH6B&XX)c^8^U|WmA`g67YH`E%ti&ra>K(|!+)|NbQqA!b48&@`R z6SZ9Ty3Lwk%hc{S=)-Dku2F?+hMhGS#~9+AYfYMAo?T_S`u+cp;x}>WkLJD)xW0!p##EM0;>zyovC9!+@NUqTCT2g=t$@4;_(ehWcq zzlFkq^6X4|d=Nq^;mBtF2uHa>Veud4!2awsbytCs0f&sDTz=aF-g6J`{uj?CJ|;0eDH79i|X_}S?*%%8KlX7oq= z=UM3u@cDJ;UZND%0003alOO>Vf4xe?P!NT`tUoKFHj0H1cVgUi8{ z-G0Mjfi-q%2Vt3aJ@16Z_n&Px^KC?~&ivlBJ zGGa4hVK^;hIAu32G&eChEjc%1W-T;iVq;}EGBhw^GBuO@1f~u)H#RaeIW;&pF)%SX zGn23dZVfRyFf~*$H99jnIx#qtD+VDAF*-0cR53L=GdVgjII~#>*8;N>3SbBX2KEN$ zlXns=2>}HTClXRurQwsU5+{EGa7jc#R7l6|*2{|=RTRhZ {E>6tj__&^0AGD3t| z`Pv!5-GCci1pF5SxBeflb?-WhY|JP@2t+|dMN|+GVGwBprsri(ch#-y!a_5llUSYJ zh&i>Y%dIb`e&@-3`;S@7JIp&bUATPH`328fch$$y+!fH(7oPwkq-y^277$ALl!h0;KSFMJt?_uy>~oBq)L^`enb+VXP#!KIk}bF=6r z!dd$^4V1{TMr|To_q4b2>O$DO=?xFc3PdMFpi&55$F#Qyx%8Un4K-@{R7^r)5~}u%!It<6=#&DYBNcp zl%&qzF!WP=d+S$s3)#MNUQlT|UqhHE9LYjqNPN(@%6_(PN2xaLpvlM;PuWIc=E!?} zi}!|YuNs(|cBvn}ijQ(>TL`TiE=BH#9k}2zwV9bli#6}Z9)^F`t!T8qkK_3t-(K4_ zk4Rdzq?N3?5_{mpk%<)}-}UccFClBYr}C@My8KVM@Vg=|F47p+v|3r)Rn_H~{9RbP zdE?fe_3yA-Dp{+MBt!4U=u|&FTx>d=RY{UYpz5~ZfG#ce1*g4w9$i&!*#);#e xTiP8H?7Z-~f!~}rUFzbSBgg&q|9@nE0^JYqKmHP;+W-py002ovPDHLkV1l6Mc8LH0 diff --git a/common/data/src/main/res/drawable/slight_left_x.png b/common/data/src/main/res/drawable/slight_left_x.png index f968d594ad350f69eb873886c4214fcb7c07e336..c821ad6ec92a62e06419f71b87ba070d60862fa3 100644 GIT binary patch delta 1322 zcmV+_1=aeVDBLKJBmwS`B_DrVj>9kr{P!vL2)u&r<2X$s?G1YTPHfz4Dpgt}k6|D% zCTV~E9_i;aDUxi-#j88PtXh=@r_ivqBa?OPCTryQ&Zl=mKq+n=VU^%_@bEd}8bf&2 z&qmYX25-3%tp&f{`866!`D_#ll*eS+;)7_U5{hiZk5J^b2i;t)T*owvFV5M3 zCX};R_(jMv_PL!xHTUt9=A=o?c$Nw@$*!yfRP~`!&*R!~E0A?bDC7uERF%GX!moq` zG_IrZ(dfO*zq`41^u_-3tn>ow@pas1a4xn000APC9sw7B!%!54zqFzv6$iyZL^8N` zG?Rl>AqW*>tYCFYlN5_d3`t7y4SW-Qm=1#ALpUlp``_r`U>0vU{2zyN{{P-U%TB_y zTsVLVsf`#7sNt<8BJ6hYl;t6JkRyg$O~5Z0ez<+>pmYx(!v8fKh&=AUo;0^~tZVk{h& z`TzhAKC?>#iUK5LWHd5iH90XYH8L_{Ei^VUIW1v1WH~KlI5Rk9GGSveGGbwq`2?p8 zGc`9hI59FdFf}(gHT6VK`$gIbvirEi`2}I4w9ZVlyo@Wi>fAWn(pDWH^)N2TKbvHB>P(Ix#ak zGcvP02yO@jdHdvildcdh2oDSmAmb8WBa`G1D1TN-L_t(Y$L-cVZyZ$=#_`{onYF!6 zvPvR3LO~chUXlhH8ana~!bhY|O+^7h2%!imKm$~yAP^-;IKlD0+(VJ^up7^=*Jc|e zpKeC?{O7snJon7yBK~I)r!B~RZ@cV@5}(e!9f22Snm9U{)vQF`_Ldzp9N6tyP%!Pw zTz`Vi>u%XoODRw}JONy>(;jTT?{zyUrl`3_{#J7f=k6rX`PW~_7!igI-k9j z@ujH{YPH6nqCFfheNsy!H@tR6Y`pMoyZuo4G46TPHnOzu(j{*!5w%pU_}wqZ9)F0( z#Mkb(?#M24t8Lu+SmFmyP#rfl9{NtsSugFl>FO&Z=rkKEX{GpqQs`WBspU1N^sf9%-@r~zQ^HSRO-WiLqBz{xVVvDf95o~<#Z&f!j zwv9HUmZR2WDGmC&a~|x|4jan|=WoG2u&c3UpfNqupLb<-Gn?+THUIkhKVoxaDpg85 zfj3ix_{+FETSxuibEw6@M5U9kry!#dV1Plb5=QvFw?GO6=PHfz4DplH%jA0-! zCTV~E9_i-!=jA#d1+t zWh%uSer3{veQu{vDSbSpIcX9zo}~;;yh|2>6|tdG&STPWOSha*Na6@2DD8_U{7Ohb zV;_x=M(<_*-OaV5FZj>1(hKa-b>59YX?6er0U?tg0T+M7P!L5=c10J_N>LDt6t-KM z%EDD42nsQ-V6@66SzJtFNU|*cfq$YOvxOk|5ta(p-WM$_Oyh%L4#Uj7GY>j;8fDeO zK4f`ay8a-z46f{@H@bA_VY3-VRWb0q6I$QrE0)PGeWTUh_vc%TlPXf4EApr)Yq0jj z&a5sBD_DQO9r}mj2eBPzc`SYs@5EVb*v$G_I*oob%{D7ZesONB4VN>cVI^!|CKf-DNYtOKJ-QpP4`+ZwWkJI%0^TsdS7f@b$VfuJEaAQTOSsRjDq zwlkgS%yl6|3R9RNvoYy!_ujkbd(Zp5bMASp>VKwiuLUuw;01$Dgd?}y&499hnq*kK zGb(vku8i$ySRWsIR9Z=|0WUk@d1*r? z)bxAMpfg^#O@GTrO-wsv%8Sz0DufuHwBI$4+o)SK=8{Kz^sf=kXDiG(CS%-)%OMg@ z`B2hvzxhK-Qr%g*ZhSa@O=AfcT{7vy&Grn}eIn|4($8dLX#f`04t+^mO|j`5fcGs~_E4*A+X51TVC>m9#_a!c95awy?D8|0i)G;Pf9 zw(D)tgcbCb!%9X?nQ>B|uKSv04UG9--8VLyHYeqa@MDX!Yy};Ev)*}m6_e(4w#NjH zJ13!}u57KH?ZTdRP*kxXZAMQEllQLG5o5w$B}p0Ga&5{sW)zjJ)g@)dOq;M_)=x$x zbV-?OJbUlQTXwr*%w^Y2Hm=*`ePPdvie}AAd%AttjY>AFNw{XXeb|Jec~MlBX`i>f n-d8nfk1h7D{>`r#YGwTc+dBHE|0zD(00000NkvXXu0mjf95JZI diff --git a/common/data/src/main/res/drawable/slight_right_x.png b/common/data/src/main/res/drawable/slight_right_x.png index 158a6dacd93772856131b0471461708144c16232..d1f40eb58c8e6c740a2cb75e30388842c290037b 100644 GIT binary patch delta 1113 zcmV-f1g87HCdnp{BmwY|CCf1@kabBYWRML+0g75*JmFVD0vgxR_-OQA=HK01JNjb( zc~*J>??iRuydXLf0003alOF*Uf4@q@P!z_0X+_09I4BO{kfCcw1A>E9AqWaFRmAsiK){cdz{FpC!s-;cvN-*@kYmYsxYIkyjKRu!(_8(a>q z?1eYltg%do-5@OUp68v=cs^gTjJ|Y@SM%JTXFiI{P&!j(VO~^V?TMXff0Y|nv2ok? z55*5+J4&-i{3Px~X=K>!`e`x=e>}}?R^sg9-1Jr3xEynehs}@)V-hO5(oCHr9CoBG z1p)Vzgwljqy{Tp@ZbYS=TnP&Ix{#rGBmSS1`z?1g$7(CGBq_Z zI5Rn7Eo3=lFfBA@H8w3dW;Zb{H)Ak2GdVXhWMyGulLZGz4KpnP&Ix{#rGBmSV z2W|)iV+;s(lgtk-2?GxcCK$Lc3{jH;5GQ{EBS}O-R7l6|mcNVCKorM6-$^dJ_rvup zPS+oYD0qdfU}0$?*a_mlCs--MRW^cxl@?;5jg5NXA=F?pVhu}AZnHEU0N{WT-}UV9*bWK9vIt-|Swr3~!vQHN zFST3vxu85L!@&B42`QlL1%_yG!#agi9K?>Ybc?{Qat2{2lfbfUg^`x?3S+G_*> zLm4T~^Af4=UyXG@RET7kxinJ+mF9mA)7`&gm-Ep{_UsCqGg+vF6%#!y1LCEvqSeYJ&;(}UTK^ReeSPDE1D+2OKy4MzgHSFK{mx@@vfyOgwmJ`n<9S~z;4;)Dda z$Y|OZa<9gWBj9K&2H=w5{pw~DOBH*VU!Qrt(*6DH#%S(S_qSbUY}u z%TSmP4T*4;DCrQ}x*WMysl$^53HMZvjzr(fUVqtGQ@b^SjH+ fV%i?rkxlJ4AES>{vsA2X00000NkvXXu0mjf9tZHp delta 1273 zcmV|RxR#Bme-~04}#0!%I>~l z(V<6|-7u<(f#;pj8lSJ4R=o6$R{Pwau@om&q&ipTQBl@l?TMXPT^N5>vVJ@C55*5+ zJI?Z0{3PCvv)HiN^|N#u{b-tNdP#n9Zu-hCT#h-#!)C;k2`M#w)m)t;9ClP)Nxe--TaybwUMY^WNZ?p0946>U+>3IXi=iWBme*nc(YIfiUK4#Gi5bn zHaTQ1He_WrEi^GQIW1x^IWjFaFf%n_GdVM2H)Cd#{sg8DHa9ahH8?RgH8nOeG&7U3 z1#S&7IxsR+F*70{}@xK~zY`?bc6hR8WkW))8Naa%0#?N?AITmYuvSBNAK^ z>#f+p9}aWWq*?*n%@EwtLa*VXBtUR99(MRtj{J z>~z$uKON8`r%7Z;uMV{7w9zi_EQj7nZDA?sJ~M`$&?Iet(2QxZvKA?cSj7c%O5w+Q z>#~1rK$kasrOS|qRh%;Bwt~xs^h;aRt`I)^?~hHZKzj{1>=%1&)2m|AukxOE!a;-D zl;rL9`MuACzXNV_$ae;G3#MH2vqyX%K1hPc+%)65viC#M7`EF^r&Xn8{if)oha3qL zge19QQdPl!H?nyNgUw1>FyG2UjYvq+1b)S)LFUs3MyTDC(* zrxDw=npZOHl}2}2ee72KHp$DH^rk<;PwRt?oRyblk<^-|lTSz1-q9;y26xfZptSXRac^Tw9O9kr{P!t(1YXAWahxWR_6B?WPGa0Nl~!6L4=^S$ zCTaWq+0)}Nsgf+0>dl>C)~rcKP-)n5B9onXnuJGw7ao}=3F87`@Gtjb=FfC{HA=ZG%5QipQ-CT8V=$85te;eeTIeW zhOGJDZ)=$8)-w?QeB&1=08oUX<3Be501zRwOah7mBx5%>GB!A3GA%VSIAbj|HaKA| zVP-gCEjc+hF=jV4G&wRhGL!lQrVckVH8(UhG%z$UH!?6dldc7B4Kq41HB>P&Ix{so zG%%AX1|bbIIx#g=F)}(cH99mfvsVW60h4G4TqI#QG%_+bVl^#eGh{X`G&3_YEjc(d zHZ3_~IW%ErVlX%~WHFQI2S^PwEipAzF)}(bG&(djlUfIABr#$)GB9N@WGyr_VKgl? zG-5a{VKZSjEi*GSH!)>0W??aAHk0TFNDVVBF*Q^%GCDIgIy5k|JqT_H1eK*RIg_st zEeQh;3nM7wfylU%C4&)te^mm*?+?i+_1D<-~;*;#qv z9O9WB(LSzPB7EPmiuk~9t{52_s!XlNd!2~5;jX@c#7Lnh@zRsHd|@JH9^3YTpr=8m zmHWxp7wSdK6rOq|^|8uWG#XPg)E>rv!8~Hau^{3=E{Ymai!1s*^1H9*5jUiAiD&`)CQ65D1D`tf+M1Pr zKk(m~d+xfK7om*b8SBf{5~X4IGWL8X@$&_zGZwzE>Fr5Ndp11_e|BB7MFWLKjl$mP zzTP%9#8ReIew^{pkDo7$qnMo?p55@ic&!gpmwB|hrO;husG_oYKS}R*Z~&?yt-I5HpMVGSo_kq3%o&Gx-TII}3Op?>PPe{XoCu+r{gV P00000NkvXXu0mjf?9^mP delta 1239 zcmV;|1StE9D%vKHBmwM^B_Dsm4Z|P|{4+&IFt%f09MU9GH|Y31z%)&zO0|SJALM*E zVShf3aBIPtgDxpsF(ZT)EnERIjaYk*q&@dw-%(%qz^flHD8VkHEbROa9yoekyh)t* zr_cnk0xjo3t+|3o>DLo3BXEK=BBX(|g( zg&-)z+zCdj+-?pJvunuiarg$liT5y92!an`sbKBD(Za%QoMo7gVdnpL7YL(DD6@J9 zC~cc08{}8{b+q<|03mujokEy;kfvww#^)=i883aO<)8aAIibvMe6$&chnDlN$i@}5-EvD?j5H4{IY?l$YCz8tzfa~BCtagG!b#+YG>3Ko4< z&CUs8?6W3lP~ZUug0(`)-kdWQwqAA+e3NxHcfcGb0BL_C0fgIkA$E*AMZJwF9o%!b*zW|O{P&bUdYtH}x z4qLNO0*V48Fg9goFfuYXEiy7RW-T;1VKFT@VKX-^G&whBWnnQiW;HZ5ll}yz4mLM4 zG&nafI50CYHZnPrvITApF*-0ZR539+F)=zcIFl^~Aq_D)FfvpzF*-3ZIy5-5TL$z2 zvlR+p2n1(bJcpBc5-kY<0tyQ-B-kI$ldlpee>h1*K~zY`?bf|+9Ay;7@!!mj*E1XM z`Xg}yi4k%r3`h}*P$F)C0-69zMTiCoMWP^dDWHb}N{V#30)!B95sd7OgNb9Z zcJ|XVJ1;dNyWTM+Ira1Y^*l%C=)63y87pKVJ4q8y2Uv4EeDNO;YToyXIDBw=#JcOg zfAPIH!%L?^gt||BXUBmZpM_+SE}DY))Q?(f4kX?(AKj{m2=6*n*f2Ii;ceeW_bLPl z>t5HkY$%pk)^yj+urjlYkod}%mb@y~5iH8>io6qjJcEmngr-~W$eouNh-HE~t44kZ zAI{(+G`*+ryLn4m3Uz&FL>BCeC4P#2e?F-fYw8X)9D8V6M=AA0@Km9+<3?zlgt(+F zm+N{gl?o1&_UvKBvHLzc3DGh#GO*~Ho?V<**LC2k+>&({|M}qGGq>r2W4}bh@VRX- zs0qgUmZO_t&Q+Vfo^`Y`H|#oVLE%Wx6?dZiS;}O}k~N%>daNT=`1RR3EZ>Ml~h`O|{PSknWw>Mq7Mu