settings ui
This commit is contained in:
+15
@@ -0,0 +1,15 @@
|
|||||||
|
*.iml
|
||||||
|
.gradle
|
||||||
|
/local.properties
|
||||||
|
/.idea/caches
|
||||||
|
/.idea/libraries
|
||||||
|
/.idea/modules.xml
|
||||||
|
/.idea/workspace.xml
|
||||||
|
/.idea/navEditor.xml
|
||||||
|
/.idea/assetWizardSettings.xml
|
||||||
|
.DS_Store
|
||||||
|
/build
|
||||||
|
/captures
|
||||||
|
.externalNativeBuild
|
||||||
|
.cxx
|
||||||
|
local.properties
|
||||||
Generated
+3
@@ -0,0 +1,3 @@
|
|||||||
|
# Default ignored files
|
||||||
|
/shelf/
|
||||||
|
/workspace.xml
|
||||||
Generated
+6
@@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="CompilerConfiguration">
|
||||||
|
<bytecodeTargetLevel target="21" />
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
Generated
+10
@@ -0,0 +1,10 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="deploymentTargetSelector">
|
||||||
|
<selectionStates>
|
||||||
|
<SelectionState runConfigName="app">
|
||||||
|
<option name="selectionMode" value="DROPDOWN" />
|
||||||
|
</SelectionState>
|
||||||
|
</selectionStates>
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
Generated
+20
@@ -0,0 +1,20 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="GradleMigrationSettings" migrationVersion="1" />
|
||||||
|
<component name="GradleSettings">
|
||||||
|
<option name="linkedExternalProjectsSettings">
|
||||||
|
<GradleProjectSettings>
|
||||||
|
<option name="testRunner" value="CHOOSE_PER_TEST" />
|
||||||
|
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
||||||
|
<option name="gradleJvm" value="#GRADLE_LOCAL_JAVA_HOME" />
|
||||||
|
<option name="modules">
|
||||||
|
<set>
|
||||||
|
<option value="$PROJECT_DIR$" />
|
||||||
|
<option value="$PROJECT_DIR$/app" />
|
||||||
|
</set>
|
||||||
|
</option>
|
||||||
|
<option name="resolveExternalAnnotations" value="false" />
|
||||||
|
</GradleProjectSettings>
|
||||||
|
</option>
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
+57
@@ -0,0 +1,57 @@
|
|||||||
|
<component name="InspectionProjectProfileManager">
|
||||||
|
<profile version="1.0">
|
||||||
|
<option name="myName" value="Project Default" />
|
||||||
|
<inspection_tool class="ComposePreviewDimensionRespectsLimit" enabled="true" level="WARNING" enabled_by_default="true">
|
||||||
|
<option name="composableFile" value="true" />
|
||||||
|
<option name="previewFile" value="true" />
|
||||||
|
</inspection_tool>
|
||||||
|
<inspection_tool class="ComposePreviewMustBeTopLevelFunction" enabled="true" level="ERROR" enabled_by_default="true">
|
||||||
|
<option name="composableFile" value="true" />
|
||||||
|
<option name="previewFile" value="true" />
|
||||||
|
</inspection_tool>
|
||||||
|
<inspection_tool class="ComposePreviewNeedsComposableAnnotation" enabled="true" level="ERROR" enabled_by_default="true">
|
||||||
|
<option name="composableFile" value="true" />
|
||||||
|
<option name="previewFile" value="true" />
|
||||||
|
</inspection_tool>
|
||||||
|
<inspection_tool class="ComposePreviewNotSupportedInUnitTestFiles" enabled="true" level="ERROR" enabled_by_default="true">
|
||||||
|
<option name="composableFile" value="true" />
|
||||||
|
<option name="previewFile" value="true" />
|
||||||
|
</inspection_tool>
|
||||||
|
<inspection_tool class="GlancePreviewDimensionRespectsLimit" enabled="true" level="WARNING" enabled_by_default="true">
|
||||||
|
<option name="composableFile" value="true" />
|
||||||
|
</inspection_tool>
|
||||||
|
<inspection_tool class="GlancePreviewMustBeTopLevelFunction" enabled="true" level="ERROR" enabled_by_default="true">
|
||||||
|
<option name="composableFile" value="true" />
|
||||||
|
</inspection_tool>
|
||||||
|
<inspection_tool class="GlancePreviewNeedsComposableAnnotation" enabled="true" level="ERROR" enabled_by_default="true">
|
||||||
|
<option name="composableFile" value="true" />
|
||||||
|
</inspection_tool>
|
||||||
|
<inspection_tool class="GlancePreviewNotSupportedInUnitTestFiles" enabled="true" level="ERROR" enabled_by_default="true">
|
||||||
|
<option name="composableFile" value="true" />
|
||||||
|
</inspection_tool>
|
||||||
|
<inspection_tool class="PreviewAnnotationInFunctionWithParameters" enabled="true" level="ERROR" enabled_by_default="true">
|
||||||
|
<option name="composableFile" value="true" />
|
||||||
|
<option name="previewFile" value="true" />
|
||||||
|
</inspection_tool>
|
||||||
|
<inspection_tool class="PreviewApiLevelMustBeValid" enabled="true" level="ERROR" enabled_by_default="true">
|
||||||
|
<option name="composableFile" value="true" />
|
||||||
|
<option name="previewFile" value="true" />
|
||||||
|
</inspection_tool>
|
||||||
|
<inspection_tool class="PreviewDeviceShouldUseNewSpec" enabled="true" level="WEAK WARNING" enabled_by_default="true">
|
||||||
|
<option name="composableFile" value="true" />
|
||||||
|
<option name="previewFile" value="true" />
|
||||||
|
</inspection_tool>
|
||||||
|
<inspection_tool class="PreviewFontScaleMustBeGreaterThanZero" enabled="true" level="ERROR" enabled_by_default="true">
|
||||||
|
<option name="composableFile" value="true" />
|
||||||
|
<option name="previewFile" value="true" />
|
||||||
|
</inspection_tool>
|
||||||
|
<inspection_tool class="PreviewMultipleParameterProviders" enabled="true" level="ERROR" enabled_by_default="true">
|
||||||
|
<option name="composableFile" value="true" />
|
||||||
|
<option name="previewFile" value="true" />
|
||||||
|
</inspection_tool>
|
||||||
|
<inspection_tool class="PreviewPickerAnnotation" enabled="true" level="ERROR" enabled_by_default="true">
|
||||||
|
<option name="composableFile" value="true" />
|
||||||
|
<option name="previewFile" value="true" />
|
||||||
|
</inspection_tool>
|
||||||
|
</profile>
|
||||||
|
</component>
|
||||||
Generated
+6
@@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="KotlinJpsPluginSettings">
|
||||||
|
<option name="version" value="1.9.22" />
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
Generated
+10
@@ -0,0 +1,10 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="ProjectMigrations">
|
||||||
|
<option name="MigrateToGradleLocalJavaHome">
|
||||||
|
<set>
|
||||||
|
<option value="$PROJECT_DIR$" />
|
||||||
|
</set>
|
||||||
|
</option>
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
Generated
+10
@@ -0,0 +1,10 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="ExternalStorageConfigurationManager" enabled="true" />
|
||||||
|
<component name="ProjectRootManager" version="2" languageLevel="JDK_21" default="true" project-jdk-name="jbr-21" project-jdk-type="JavaSDK">
|
||||||
|
<output url="file://$PROJECT_DIR$/build/classes" />
|
||||||
|
</component>
|
||||||
|
<component name="ProjectType">
|
||||||
|
<option name="id" value="Android" />
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
Generated
+17
@@ -0,0 +1,17 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="RunConfigurationProducerService">
|
||||||
|
<option name="ignoredProducers">
|
||||||
|
<set>
|
||||||
|
<option value="com.intellij.execution.junit.AbstractAllInDirectoryConfigurationProducer" />
|
||||||
|
<option value="com.intellij.execution.junit.AllInPackageConfigurationProducer" />
|
||||||
|
<option value="com.intellij.execution.junit.PatternConfigurationProducer" />
|
||||||
|
<option value="com.intellij.execution.junit.TestInClassConfigurationProducer" />
|
||||||
|
<option value="com.intellij.execution.junit.UniqueIdConfigurationProducer" />
|
||||||
|
<option value="com.intellij.execution.junit.testDiscovery.JUnitTestDiscoveryConfigurationProducer" />
|
||||||
|
<option value="org.jetbrains.kotlin.idea.junit.KotlinJUnitRunConfigurationProducer" />
|
||||||
|
<option value="org.jetbrains.kotlin.idea.junit.KotlinPatternConfigurationProducer" />
|
||||||
|
</set>
|
||||||
|
</option>
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
Generated
+6
@@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="VcsDirectoryMappings">
|
||||||
|
<mapping directory="" vcs="Git" />
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
@@ -88,4 +88,5 @@ dependencies {
|
|||||||
implementation("androidx.compose.material:material-icons-core:1.6.2")
|
implementation("androidx.compose.material:material-icons-core:1.6.2")
|
||||||
implementation("androidx.compose.material:material-icons-extended:1.6.2")
|
implementation("androidx.compose.material:material-icons-extended:1.6.2")
|
||||||
implementation("androidx.compose.material3:material3:1.2.0")
|
implementation("androidx.compose.material3:material3:1.2.0")
|
||||||
|
implementation("androidx.core:core-ktx:1.12.0")
|
||||||
}
|
}
|
||||||
@@ -18,13 +18,26 @@ class SettingsDataStore(private val context: Context) {
|
|||||||
val RADIUS = intPreferencesKey("radius")
|
val RADIUS = intPreferencesKey("radius")
|
||||||
val API_KEY = stringPreferencesKey("api_key")
|
val API_KEY = stringPreferencesKey("api_key")
|
||||||
val FAVORITE_STATIONS = stringPreferencesKey("favorite_stations")
|
val FAVORITE_STATIONS = stringPreferencesKey("favorite_stations")
|
||||||
|
val LOCATION_MODE_KEY = stringPreferencesKey("location_mode")
|
||||||
|
val STATIC_LAT_KEY = doublePreferencesKey("static_lat")
|
||||||
|
val STATIC_LNG_KEY = doublePreferencesKey("static_lng")
|
||||||
|
val GPS_LAT_KEY = doublePreferencesKey("gps_lat")
|
||||||
|
val GPS_LNG_KEY = doublePreferencesKey("gps_lng")
|
||||||
}
|
}
|
||||||
|
|
||||||
val selectedLocation: Flow<Pair<Double, Double>> = context.dataStore.data.map { preferences ->
|
val selectedLocation: Flow<Pair<Double, Double>> = context.dataStore.data.map { preferences ->
|
||||||
Pair(
|
val mode = preferences[PreferencesKeys.LOCATION_MODE_KEY] ?: "static"
|
||||||
preferences[PreferencesKeys.LAT_KEY] ?: 52.520008, // Default to Berlin
|
when (mode) {
|
||||||
preferences[PreferencesKeys.LNG_KEY] ?: 13.404954
|
"static" -> Pair(
|
||||||
|
preferences[PreferencesKeys.STATIC_LAT_KEY] ?: 52.520008,
|
||||||
|
preferences[PreferencesKeys.STATIC_LNG_KEY] ?: 13.404954
|
||||||
)
|
)
|
||||||
|
"gps" -> Pair(
|
||||||
|
preferences[PreferencesKeys.GPS_LAT_KEY] ?: preferences[PreferencesKeys.STATIC_LAT_KEY] ?: 52.520008,
|
||||||
|
preferences[PreferencesKeys.GPS_LNG_KEY] ?: preferences[PreferencesKeys.STATIC_LNG_KEY] ?: 13.404954
|
||||||
|
)
|
||||||
|
else -> Pair(52.520008, 13.404954)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val selectedFuelType: Flow<String> = context.dataStore.data.map { preferences ->
|
val selectedFuelType: Flow<String> = context.dataStore.data.map { preferences ->
|
||||||
@@ -44,10 +57,23 @@ class SettingsDataStore(private val context: Context) {
|
|||||||
if (favoritesString.isEmpty()) setOf() else favoritesString.split(",").toSet()
|
if (favoritesString.isEmpty()) setOf() else favoritesString.split(",").toSet()
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun saveLocation(latitude: Double, longitude: Double) {
|
val locationMode: Flow<String> = context.dataStore.data.map { preferences ->
|
||||||
|
preferences[PreferencesKeys.LOCATION_MODE_KEY] ?: "static" // Default to static mode
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun saveLocation(latitude: Double, longitude: Double, mode: String? = null) {
|
||||||
context.dataStore.edit { preferences ->
|
context.dataStore.edit { preferences ->
|
||||||
preferences[PreferencesKeys.LAT_KEY] = latitude
|
val currentMode = mode ?: preferences[PreferencesKeys.LOCATION_MODE_KEY] ?: "static"
|
||||||
preferences[PreferencesKeys.LNG_KEY] = longitude
|
when (currentMode) {
|
||||||
|
"static" -> {
|
||||||
|
preferences[PreferencesKeys.STATIC_LAT_KEY] = latitude
|
||||||
|
preferences[PreferencesKeys.STATIC_LNG_KEY] = longitude
|
||||||
|
}
|
||||||
|
"gps" -> {
|
||||||
|
preferences[PreferencesKeys.GPS_LAT_KEY] = latitude
|
||||||
|
preferences[PreferencesKeys.GPS_LNG_KEY] = longitude
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -85,4 +111,10 @@ class SettingsDataStore(private val context: Context) {
|
|||||||
preferences[PreferencesKeys.FAVORITE_STATIONS] = currentFavorites.joinToString(",")
|
preferences[PreferencesKeys.FAVORITE_STATIONS] = currentFavorites.joinToString(",")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
suspend fun saveLocationMode(mode: String) {
|
||||||
|
context.dataStore.edit { preferences ->
|
||||||
|
preferences[PreferencesKeys.LOCATION_MODE_KEY] = mode
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,7 +1,13 @@
|
|||||||
package com.example.tank.ui.screens
|
package com.example.tank.ui.screens
|
||||||
|
|
||||||
|
import android.Manifest
|
||||||
|
import android.content.pm.PackageManager
|
||||||
|
import androidx.activity.compose.rememberLauncherForActivityResult
|
||||||
|
import androidx.activity.result.contract.ActivityResultContracts
|
||||||
import androidx.compose.foundation.layout.*
|
import androidx.compose.foundation.layout.*
|
||||||
import androidx.compose.material3.*
|
import androidx.compose.material3.*
|
||||||
|
import androidx.compose.material.icons.filled.LocationOn
|
||||||
|
import androidx.compose.material.icons.filled.MyLocation
|
||||||
import androidx.compose.runtime.*
|
import androidx.compose.runtime.*
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
@@ -11,7 +17,16 @@ import androidx.navigation.NavController
|
|||||||
import androidx.navigation.compose.rememberNavController
|
import androidx.navigation.compose.rememberNavController
|
||||||
import com.example.tank.data.SettingsDataStore
|
import com.example.tank.data.SettingsDataStore
|
||||||
import com.example.tank.di.NetworkModule
|
import com.example.tank.di.NetworkModule
|
||||||
|
import com.google.android.gms.location.LocationServices
|
||||||
|
import com.google.android.gms.location.Priority
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
import androidx.compose.material.icons.Icons
|
||||||
|
import androidx.core.content.ContextCompat
|
||||||
|
import androidx.compose.foundation.background
|
||||||
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
|
import androidx.compose.ui.draw.clip
|
||||||
|
import androidx.compose.foundation.verticalScroll
|
||||||
|
import androidx.compose.foundation.rememberScrollState
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@Composable
|
@Composable
|
||||||
@@ -20,21 +35,58 @@ fun SettingsScreen(navController: NavController) {
|
|||||||
val settingsDataStore = remember { SettingsDataStore(context) }
|
val settingsDataStore = remember { SettingsDataStore(context) }
|
||||||
val scope = rememberCoroutineScope()
|
val scope = rememberCoroutineScope()
|
||||||
|
|
||||||
var fuelExpanded by remember { mutableStateOf(false) }
|
|
||||||
var selectedFuelType by remember { mutableStateOf("") }
|
var selectedFuelType by remember { mutableStateOf("") }
|
||||||
var selectedLocation by remember { mutableStateOf<Pair<Double, Double>?>(null) }
|
var selectedLocation by remember { mutableStateOf<Pair<Double, Double>?>(null) }
|
||||||
var radiusExpanded by remember { mutableStateOf(false) }
|
|
||||||
var selectedRadius by remember { mutableStateOf(5) }
|
var selectedRadius by remember { mutableStateOf(5) }
|
||||||
var apiKey by remember { mutableStateOf("") }
|
var apiKey by remember { mutableStateOf("") }
|
||||||
|
var locationMode by remember { mutableStateOf("static") }
|
||||||
|
|
||||||
val fuelTypes = listOf(
|
val fuelTypes = listOf(
|
||||||
|
"DIESEL" to "Diesel",
|
||||||
"E5" to "Super E5",
|
"E5" to "Super E5",
|
||||||
"E10" to "Super E10",
|
"E10" to "Super E10"
|
||||||
"Diesel" to "Diesel"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
val radiusOptions = listOf(5, 10, 15, 20)
|
val radiusOptions = listOf(5, 10, 15, 20)
|
||||||
|
|
||||||
|
// Add location services client
|
||||||
|
val fusedLocationClient = remember {
|
||||||
|
LocationServices.getFusedLocationProviderClient(context)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Permission launcher
|
||||||
|
val locationPermissionLauncher = rememberLauncherForActivityResult(
|
||||||
|
contract = ActivityResultContracts.RequestMultiplePermissions()
|
||||||
|
) { permissions ->
|
||||||
|
val locationGranted = permissions.entries.all { it.value }
|
||||||
|
if (locationGranted) {
|
||||||
|
scope.launch {
|
||||||
|
settingsDataStore.saveLocationMode("gps")
|
||||||
|
locationMode = "gps"
|
||||||
|
try {
|
||||||
|
fusedLocationClient.getCurrentLocation(Priority.PRIORITY_HIGH_ACCURACY, null)
|
||||||
|
.addOnSuccessListener { location ->
|
||||||
|
if (location != null) {
|
||||||
|
scope.launch {
|
||||||
|
settingsDataStore.saveLocation(location.latitude, location.longitude)
|
||||||
|
selectedLocation = Pair(location.latitude, location.longitude)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e: SecurityException) {
|
||||||
|
// Handle permission denial
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Collect location mode
|
||||||
|
LaunchedEffect(Unit) {
|
||||||
|
settingsDataStore.locationMode.collect { mode ->
|
||||||
|
locationMode = mode
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
LaunchedEffect(Unit) {
|
LaunchedEffect(Unit) {
|
||||||
settingsDataStore.selectedFuelType.collect { fuelType ->
|
settingsDataStore.selectedFuelType.collect { fuelType ->
|
||||||
selectedFuelType = fuelType.uppercase()
|
selectedFuelType = fuelType.uppercase()
|
||||||
@@ -57,132 +109,280 @@ fun SettingsScreen(navController: NavController) {
|
|||||||
|
|
||||||
LaunchedEffect(Unit) {
|
LaunchedEffect(Unit) {
|
||||||
settingsDataStore.apiKey.collect { key ->
|
settingsDataStore.apiKey.collect { key ->
|
||||||
apiKey = key ?: NetworkModule.DEFAULT_API_KEY
|
apiKey = key
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LaunchedEffect(locationMode) {
|
||||||
|
if (locationMode == "gps" &&
|
||||||
|
ContextCompat.checkSelfPermission(
|
||||||
|
context,
|
||||||
|
Manifest.permission.ACCESS_FINE_LOCATION
|
||||||
|
) == PackageManager.PERMISSION_GRANTED
|
||||||
|
) {
|
||||||
|
try {
|
||||||
|
fusedLocationClient.getCurrentLocation(Priority.PRIORITY_HIGH_ACCURACY, null)
|
||||||
|
.addOnSuccessListener { location ->
|
||||||
|
if (location != null) {
|
||||||
|
scope.launch {
|
||||||
|
settingsDataStore.saveLocation(
|
||||||
|
location.latitude,
|
||||||
|
location.longitude,
|
||||||
|
"gps"
|
||||||
|
)
|
||||||
|
selectedLocation = Pair(location.latitude, location.longitude)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e: SecurityException) {
|
||||||
|
// Handle permission denial
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxSize()
|
.fillMaxSize()
|
||||||
|
.background(MaterialTheme.colorScheme.background)
|
||||||
|
.verticalScroll(rememberScrollState())
|
||||||
.padding(16.dp),
|
.padding(16.dp),
|
||||||
horizontalAlignment = Alignment.CenterHorizontally,
|
|
||||||
verticalArrangement = Arrangement.spacedBy(16.dp)
|
verticalArrangement = Arrangement.spacedBy(16.dp)
|
||||||
) {
|
) {
|
||||||
|
// Header
|
||||||
Text(
|
Text(
|
||||||
text = "Settings",
|
text = "Settings",
|
||||||
style = MaterialTheme.typography.headlineMedium
|
style = MaterialTheme.typography.headlineMedium,
|
||||||
|
color = MaterialTheme.colorScheme.primary,
|
||||||
|
modifier = Modifier.padding(bottom = 8.dp)
|
||||||
)
|
)
|
||||||
|
|
||||||
// Add API Key TextField
|
// Location Section
|
||||||
|
SettingsSection(title = "Location") {
|
||||||
|
Column(
|
||||||
|
verticalArrangement = Arrangement.spacedBy(16.dp)
|
||||||
|
) {
|
||||||
|
// Location Mode Selection
|
||||||
|
Row(
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
horizontalArrangement = Arrangement.spacedBy(8.dp)
|
||||||
|
) {
|
||||||
|
FilterChip(
|
||||||
|
selected = locationMode == "static",
|
||||||
|
onClick = {
|
||||||
|
scope.launch {
|
||||||
|
settingsDataStore.saveLocationMode("static")
|
||||||
|
locationMode = "static"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
label = { Text("Static") },
|
||||||
|
leadingIcon = {
|
||||||
|
Icon(
|
||||||
|
Icons.Default.LocationOn,
|
||||||
|
contentDescription = "Static Location"
|
||||||
|
)
|
||||||
|
},
|
||||||
|
colors = FilterChipDefaults.filterChipColors(
|
||||||
|
selectedContainerColor = MaterialTheme.colorScheme.primaryContainer,
|
||||||
|
containerColor = MaterialTheme.colorScheme.surfaceVariant
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
FilterChip(
|
||||||
|
selected = locationMode == "gps",
|
||||||
|
onClick = {
|
||||||
|
if (ContextCompat.checkSelfPermission(
|
||||||
|
context,
|
||||||
|
Manifest.permission.ACCESS_FINE_LOCATION
|
||||||
|
) == PackageManager.PERMISSION_GRANTED
|
||||||
|
) {
|
||||||
|
scope.launch {
|
||||||
|
settingsDataStore.saveLocationMode("gps")
|
||||||
|
locationMode = "gps"
|
||||||
|
try {
|
||||||
|
fusedLocationClient.getCurrentLocation(Priority.PRIORITY_HIGH_ACCURACY, null)
|
||||||
|
.addOnSuccessListener { location ->
|
||||||
|
if (location != null) {
|
||||||
|
scope.launch {
|
||||||
|
settingsDataStore.saveLocation(
|
||||||
|
location.latitude,
|
||||||
|
location.longitude,
|
||||||
|
"gps"
|
||||||
|
)
|
||||||
|
selectedLocation = Pair(location.latitude, location.longitude)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e: SecurityException) {
|
||||||
|
// Handle permission denial
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
locationPermissionLauncher.launch(
|
||||||
|
arrayOf(
|
||||||
|
Manifest.permission.ACCESS_FINE_LOCATION,
|
||||||
|
Manifest.permission.ACCESS_COARSE_LOCATION
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
label = { Text("GPS") },
|
||||||
|
leadingIcon = {
|
||||||
|
Icon(
|
||||||
|
Icons.Default.MyLocation,
|
||||||
|
contentDescription = "GPS Location"
|
||||||
|
)
|
||||||
|
},
|
||||||
|
colors = FilterChipDefaults.filterChipColors(
|
||||||
|
selectedContainerColor = MaterialTheme.colorScheme.primaryContainer,
|
||||||
|
containerColor = MaterialTheme.colorScheme.surfaceVariant
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only show location card for static mode
|
||||||
|
if (locationMode == "static") {
|
||||||
|
Card(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.height(48.dp),
|
||||||
|
colors = CardDefaults.cardColors(
|
||||||
|
containerColor = MaterialTheme.colorScheme.surfaceVariant,
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
Row(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxSize()
|
||||||
|
.padding(horizontal = 12.dp),
|
||||||
|
horizontalArrangement = Arrangement.SpaceBetween,
|
||||||
|
verticalAlignment = Alignment.CenterVertically
|
||||||
|
) {
|
||||||
|
Row(
|
||||||
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
horizontalArrangement = Arrangement.spacedBy(8.dp)
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
imageVector = if (locationMode == "static")
|
||||||
|
Icons.Default.LocationOn
|
||||||
|
else Icons.Default.MyLocation,
|
||||||
|
contentDescription = null,
|
||||||
|
tint = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||||
|
modifier = Modifier.size(20.dp)
|
||||||
|
)
|
||||||
|
Text(
|
||||||
|
text = if (selectedLocation != null) {
|
||||||
|
String.format(
|
||||||
|
"%.4f, %.4f",
|
||||||
|
selectedLocation!!.first,
|
||||||
|
selectedLocation!!.second
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
"No location selected"
|
||||||
|
},
|
||||||
|
style = MaterialTheme.typography.bodyMedium,
|
||||||
|
color = MaterialTheme.colorScheme.onSurfaceVariant
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
TextButton(
|
||||||
|
onClick = { navController.navigate("map") },
|
||||||
|
contentPadding = PaddingValues(horizontal = 8.dp)
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
"Map",
|
||||||
|
style = MaterialTheme.typography.bodyMedium
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fuel Preferences Section
|
||||||
|
SettingsSection(title = "Fuel Preferences") {
|
||||||
|
Column(
|
||||||
|
verticalArrangement = Arrangement.spacedBy(16.dp)
|
||||||
|
) {
|
||||||
|
// Fuel Type Selection
|
||||||
|
Text(
|
||||||
|
"Fuel Type",
|
||||||
|
style = MaterialTheme.typography.bodyMedium,
|
||||||
|
color = MaterialTheme.colorScheme.onSurfaceVariant
|
||||||
|
)
|
||||||
|
Row(
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
horizontalArrangement = Arrangement.spacedBy(8.dp)
|
||||||
|
) {
|
||||||
|
fuelTypes.forEach { (type, label) ->
|
||||||
|
FilterChip(
|
||||||
|
selected = selectedFuelType.uppercase() == type,
|
||||||
|
onClick = {
|
||||||
|
selectedFuelType = type
|
||||||
|
scope.launch {
|
||||||
|
settingsDataStore.saveFuelType(type.lowercase())
|
||||||
|
}
|
||||||
|
},
|
||||||
|
label = { Text(label) },
|
||||||
|
colors = FilterChipDefaults.filterChipColors(
|
||||||
|
selectedContainerColor = MaterialTheme.colorScheme.primaryContainer,
|
||||||
|
containerColor = MaterialTheme.colorScheme.surfaceVariant
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Radius Selection
|
||||||
|
Column(
|
||||||
|
verticalArrangement = Arrangement.spacedBy(8.dp)
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
"Search Radius",
|
||||||
|
style = MaterialTheme.typography.bodyMedium,
|
||||||
|
color = MaterialTheme.colorScheme.onSurfaceVariant
|
||||||
|
)
|
||||||
|
Row(
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
horizontalArrangement = Arrangement.spacedBy(8.dp)
|
||||||
|
) {
|
||||||
|
radiusOptions.forEach { radius ->
|
||||||
|
FilterChip(
|
||||||
|
selected = selectedRadius == radius,
|
||||||
|
onClick = {
|
||||||
|
selectedRadius = radius
|
||||||
|
scope.launch {
|
||||||
|
settingsDataStore.saveRadius(radius)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
label = { Text("${radius}km") },
|
||||||
|
colors = FilterChipDefaults.filterChipColors(
|
||||||
|
selectedContainerColor = MaterialTheme.colorScheme.primaryContainer
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// API Settings Section
|
||||||
|
SettingsSection(title = "API Settings") {
|
||||||
OutlinedTextField(
|
OutlinedTextField(
|
||||||
value = apiKey,
|
value = apiKey,
|
||||||
onValueChange = { apiKey = it },
|
onValueChange = { apiKey = it },
|
||||||
label = { Text("API Key") },
|
label = { Text("API Key") },
|
||||||
modifier = Modifier.fillMaxWidth(),
|
modifier = Modifier.fillMaxWidth(),
|
||||||
singleLine = true,
|
singleLine = true,
|
||||||
supportingText = { Text("Leave empty to use default key") }
|
supportingText = { Text("Leave empty to use default key") },
|
||||||
|
colors = OutlinedTextFieldDefaults.colors(
|
||||||
|
focusedBorderColor = MaterialTheme.colorScheme.primary,
|
||||||
|
unfocusedBorderColor = MaterialTheme.colorScheme.outline
|
||||||
)
|
)
|
||||||
|
|
||||||
// Location Selection
|
|
||||||
Text(
|
|
||||||
text = "Select Location",
|
|
||||||
style = MaterialTheme.typography.titleMedium
|
|
||||||
)
|
|
||||||
|
|
||||||
Button(
|
|
||||||
onClick = { navController.navigate("map") },
|
|
||||||
modifier = Modifier.fillMaxWidth()
|
|
||||||
) {
|
|
||||||
Text(if (selectedLocation != null) {
|
|
||||||
"Selected: ${String.format("%.4f", selectedLocation!!.first)}, ${String.format("%.4f", selectedLocation!!.second)}"
|
|
||||||
} else {
|
|
||||||
"Select Location on Map"
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fuel Type Selection
|
|
||||||
Text(
|
|
||||||
text = "Select Fuel Type",
|
|
||||||
style = MaterialTheme.typography.titleMedium
|
|
||||||
)
|
|
||||||
|
|
||||||
ExposedDropdownMenuBox(
|
|
||||||
expanded = fuelExpanded,
|
|
||||||
onExpandedChange = { fuelExpanded = !fuelExpanded }
|
|
||||||
) {
|
|
||||||
TextField(
|
|
||||||
value = when (selectedFuelType.uppercase()) {
|
|
||||||
"E5" -> "Super E5"
|
|
||||||
"E10" -> "Super E10"
|
|
||||||
"DIESEL" -> "Diesel"
|
|
||||||
else -> ""
|
|
||||||
},
|
|
||||||
onValueChange = {},
|
|
||||||
readOnly = true,
|
|
||||||
placeholder = { Text("Select fuel type") },
|
|
||||||
trailingIcon = { ExposedDropdownMenuDefaults.TrailingIcon(expanded = fuelExpanded) },
|
|
||||||
modifier = Modifier.menuAnchor()
|
|
||||||
)
|
|
||||||
|
|
||||||
ExposedDropdownMenu(
|
|
||||||
expanded = fuelExpanded,
|
|
||||||
onDismissRequest = { fuelExpanded = false }
|
|
||||||
) {
|
|
||||||
fuelTypes.forEach { (type, label) ->
|
|
||||||
DropdownMenuItem(
|
|
||||||
text = { Text(label) },
|
|
||||||
onClick = {
|
|
||||||
selectedFuelType = type
|
|
||||||
fuelExpanded = false
|
|
||||||
scope.launch {
|
|
||||||
settingsDataStore.saveFuelType(type.lowercase())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Radius Selection
|
Spacer(modifier = Modifier.height(16.dp))
|
||||||
Text(
|
|
||||||
text = "Search Radius",
|
|
||||||
style = MaterialTheme.typography.titleMedium
|
|
||||||
)
|
|
||||||
|
|
||||||
ExposedDropdownMenuBox(
|
|
||||||
expanded = radiusExpanded,
|
|
||||||
onExpandedChange = { radiusExpanded = !radiusExpanded }
|
|
||||||
) {
|
|
||||||
TextField(
|
|
||||||
value = "$selectedRadius km",
|
|
||||||
onValueChange = {},
|
|
||||||
readOnly = true,
|
|
||||||
placeholder = { Text("Select radius") },
|
|
||||||
trailingIcon = { ExposedDropdownMenuDefaults.TrailingIcon(expanded = radiusExpanded) },
|
|
||||||
modifier = Modifier.menuAnchor()
|
|
||||||
)
|
|
||||||
|
|
||||||
ExposedDropdownMenu(
|
|
||||||
expanded = radiusExpanded,
|
|
||||||
onDismissRequest = { radiusExpanded = false }
|
|
||||||
) {
|
|
||||||
radiusOptions.forEach { radius ->
|
|
||||||
DropdownMenuItem(
|
|
||||||
text = { Text("$radius km") },
|
|
||||||
onClick = {
|
|
||||||
selectedRadius = radius
|
|
||||||
radiusExpanded = false
|
|
||||||
scope.launch {
|
|
||||||
settingsDataStore.saveRadius(radius)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Spacer(modifier = Modifier.weight(1f))
|
|
||||||
|
|
||||||
|
// Save Button
|
||||||
Button(
|
Button(
|
||||||
onClick = {
|
onClick = {
|
||||||
scope.launch {
|
scope.launch {
|
||||||
@@ -192,19 +392,45 @@ fun SettingsScreen(navController: NavController) {
|
|||||||
if (selectedFuelType.isNotEmpty()) {
|
if (selectedFuelType.isNotEmpty()) {
|
||||||
settingsDataStore.saveFuelType(selectedFuelType.lowercase())
|
settingsDataStore.saveFuelType(selectedFuelType.lowercase())
|
||||||
}
|
}
|
||||||
// Save API key
|
|
||||||
settingsDataStore.saveApiKey(apiKey)
|
settingsDataStore.saveApiKey(apiKey)
|
||||||
// Update NetworkModule
|
|
||||||
NetworkModule.updateApiKey(apiKey)
|
NetworkModule.updateApiKey(apiKey)
|
||||||
navController.popBackStack()
|
navController.popBackStack()
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.padding(bottom = 16.dp),
|
.padding(bottom = 8.dp),
|
||||||
enabled = selectedLocation != null && selectedFuelType.isNotEmpty()
|
enabled = selectedLocation != null && selectedFuelType.isNotEmpty(),
|
||||||
|
colors = ButtonDefaults.buttonColors(
|
||||||
|
containerColor = MaterialTheme.colorScheme.primary,
|
||||||
|
contentColor = MaterialTheme.colorScheme.onPrimary,
|
||||||
|
disabledContainerColor = MaterialTheme.colorScheme.surfaceVariant,
|
||||||
|
disabledContentColor = MaterialTheme.colorScheme.onSurfaceVariant
|
||||||
|
)
|
||||||
) {
|
) {
|
||||||
Text("Save and Return")
|
Text("Save and Return")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun SettingsSection(
|
||||||
|
title: String,
|
||||||
|
content: @Composable () -> Unit
|
||||||
|
) {
|
||||||
|
Column(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.clip(RoundedCornerShape(12.dp))
|
||||||
|
.background(MaterialTheme.colorScheme.surface)
|
||||||
|
.padding(16.dp),
|
||||||
|
verticalArrangement = Arrangement.spacedBy(16.dp)
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = title,
|
||||||
|
style = MaterialTheme.typography.titleMedium,
|
||||||
|
color = MaterialTheme.colorScheme.primary
|
||||||
|
)
|
||||||
|
content()
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user