supi
This commit is contained in:
@@ -5,12 +5,20 @@ import androidx.datastore.core.DataStore
|
|||||||
import androidx.datastore.preferences.core.*
|
import androidx.datastore.preferences.core.*
|
||||||
import androidx.datastore.preferences.preferencesDataStore
|
import androidx.datastore.preferences.preferencesDataStore
|
||||||
import com.example.tank.di.NetworkModule
|
import com.example.tank.di.NetworkModule
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.*
|
||||||
import kotlinx.coroutines.flow.map
|
import android.Manifest
|
||||||
|
import android.content.pm.PackageManager
|
||||||
|
import androidx.core.content.ContextCompat
|
||||||
|
import com.google.android.gms.location.LocationServices
|
||||||
|
import com.google.android.gms.location.Priority
|
||||||
|
import kotlinx.coroutines.suspendCancellableCoroutine
|
||||||
|
import kotlin.coroutines.resume
|
||||||
|
|
||||||
private val Context.dataStore by preferencesDataStore(name = "settings")
|
private val Context.dataStore by preferencesDataStore(name = "settings")
|
||||||
|
|
||||||
class SettingsDataStore(private val context: Context) {
|
class SettingsDataStore(private val context: Context) {
|
||||||
|
private val fusedLocationClient = LocationServices.getFusedLocationProviderClient(context)
|
||||||
|
|
||||||
private object PreferencesKeys {
|
private object PreferencesKeys {
|
||||||
val LAT_KEY = doublePreferencesKey("lat")
|
val LAT_KEY = doublePreferencesKey("lat")
|
||||||
val LNG_KEY = doublePreferencesKey("lng")
|
val LNG_KEY = doublePreferencesKey("lng")
|
||||||
@@ -49,7 +57,7 @@ class SettingsDataStore(private val context: Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
val apiKey = context.dataStore.data.map { preferences ->
|
val apiKey = context.dataStore.data.map { preferences ->
|
||||||
preferences[PreferencesKeys.API_KEY] ?: NetworkModule.DEFAULT_API_KEY
|
preferences[PreferencesKeys.API_KEY] ?: ""
|
||||||
}
|
}
|
||||||
|
|
||||||
val favoriteStations = context.dataStore.data.map { preferences ->
|
val favoriteStations = context.dataStore.data.map { preferences ->
|
||||||
@@ -92,6 +100,7 @@ class SettingsDataStore(private val context: Context) {
|
|||||||
suspend fun saveApiKey(apiKey: String) {
|
suspend fun saveApiKey(apiKey: String) {
|
||||||
context.dataStore.edit { preferences ->
|
context.dataStore.edit { preferences ->
|
||||||
preferences[PreferencesKeys.API_KEY] = apiKey
|
preferences[PreferencesKeys.API_KEY] = apiKey
|
||||||
|
NetworkModule.updateApiKey(apiKey)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -117,4 +126,56 @@ class SettingsDataStore(private val context: Context) {
|
|||||||
preferences[PreferencesKeys.LOCATION_MODE_KEY] = mode
|
preferences[PreferencesKeys.LOCATION_MODE_KEY] = mode
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
suspend fun getCurrentLocation(): Pair<Double, Double>? {
|
||||||
|
return if (ContextCompat.checkSelfPermission(
|
||||||
|
context,
|
||||||
|
Manifest.permission.ACCESS_FINE_LOCATION
|
||||||
|
) == PackageManager.PERMISSION_GRANTED) {
|
||||||
|
try {
|
||||||
|
suspendCancellableCoroutine { continuation ->
|
||||||
|
fusedLocationClient
|
||||||
|
.getCurrentLocation(Priority.PRIORITY_HIGH_ACCURACY, null)
|
||||||
|
.addOnSuccessListener { location ->
|
||||||
|
if (location != null) {
|
||||||
|
continuation.resume(Pair(location.latitude, location.longitude))
|
||||||
|
} else {
|
||||||
|
continuation.resume(null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.addOnFailureListener {
|
||||||
|
continuation.resume(null)
|
||||||
|
}
|
||||||
|
}?.also { (lat, lng) ->
|
||||||
|
saveLocation(lat, lng, "gps")
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun getLocation(): Pair<Double, Double> {
|
||||||
|
val mode = context.dataStore.data.map { it[PreferencesKeys.LOCATION_MODE_KEY] ?: "static" }
|
||||||
|
.first()
|
||||||
|
|
||||||
|
return when (mode) {
|
||||||
|
"gps" -> {
|
||||||
|
getCurrentLocation() ?: context.dataStore.data.map { preferences ->
|
||||||
|
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
|
||||||
|
)
|
||||||
|
}.first()
|
||||||
|
}
|
||||||
|
else -> context.dataStore.data.map { preferences ->
|
||||||
|
Pair(
|
||||||
|
preferences[PreferencesKeys.STATIC_LAT_KEY] ?: 52.520008,
|
||||||
|
preferences[PreferencesKeys.STATIC_LNG_KEY] ?: 13.404954
|
||||||
|
)
|
||||||
|
}.first()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,28 +1,21 @@
|
|||||||
package com.example.tank.di
|
package com.example.tank.di
|
||||||
|
|
||||||
import com.example.tank.BuildConfig
|
|
||||||
import com.example.tank.data.api.TankerkoenigApi
|
import com.example.tank.data.api.TankerkoenigApi
|
||||||
import okhttp3.OkHttpClient
|
|
||||||
import okhttp3.logging.HttpLoggingInterceptor
|
|
||||||
import retrofit2.Retrofit
|
import retrofit2.Retrofit
|
||||||
import retrofit2.converter.gson.GsonConverterFactory
|
import retrofit2.converter.gson.GsonConverterFactory
|
||||||
|
import okhttp3.OkHttpClient
|
||||||
|
import okhttp3.logging.HttpLoggingInterceptor
|
||||||
|
|
||||||
object NetworkModule {
|
object NetworkModule {
|
||||||
private const val BASE_URL = "https://creativecommons.tankerkoenig.de/json/"
|
private const val BASE_URL = "https://creativecommons.tankerkoenig.de/json/"
|
||||||
const val DEFAULT_API_KEY = "9336f42d-2ebe-3a41-7100-11861d00ad04"
|
private var apiKey: String = ""
|
||||||
|
|
||||||
private var currentApiKey: String = DEFAULT_API_KEY
|
private val loggingInterceptor = HttpLoggingInterceptor().apply {
|
||||||
|
level = HttpLoggingInterceptor.Level.BODY
|
||||||
fun updateApiKey(newKey: String) {
|
|
||||||
currentApiKey = newKey
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getApiKey(): String = currentApiKey
|
|
||||||
|
|
||||||
private val okHttpClient = OkHttpClient.Builder()
|
private val okHttpClient = OkHttpClient.Builder()
|
||||||
.addInterceptor(HttpLoggingInterceptor().apply {
|
.addInterceptor(loggingInterceptor)
|
||||||
level = HttpLoggingInterceptor.Level.BODY
|
|
||||||
})
|
|
||||||
.build()
|
.build()
|
||||||
|
|
||||||
private val retrofit = Retrofit.Builder()
|
private val retrofit = Retrofit.Builder()
|
||||||
@@ -32,4 +25,10 @@ object NetworkModule {
|
|||||||
.build()
|
.build()
|
||||||
|
|
||||||
val tankerkoenigApi: TankerkoenigApi = retrofit.create(TankerkoenigApi::class.java)
|
val tankerkoenigApi: TankerkoenigApi = retrofit.create(TankerkoenigApi::class.java)
|
||||||
|
|
||||||
|
fun getApiKey(): String = apiKey
|
||||||
|
|
||||||
|
fun updateApiKey(newApiKey: String) {
|
||||||
|
apiKey = newApiKey
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,79 @@
|
|||||||
|
package com.example.tank.ui.screens
|
||||||
|
|
||||||
|
import androidx.compose.foundation.layout.*
|
||||||
|
import androidx.compose.material3.*
|
||||||
|
import androidx.compose.runtime.*
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.compose.ui.platform.LocalContext
|
||||||
|
import com.example.tank.data.SettingsDataStore
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
|
@Composable
|
||||||
|
fun ApiKeyScreen(onApiKeySubmitted: () -> Unit) {
|
||||||
|
var apiKey by remember { mutableStateOf("") }
|
||||||
|
var isError by remember { mutableStateOf(false) }
|
||||||
|
val context = LocalContext.current
|
||||||
|
val settingsDataStore = remember { SettingsDataStore(context) }
|
||||||
|
val scope = rememberCoroutineScope()
|
||||||
|
|
||||||
|
Column(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxSize()
|
||||||
|
.padding(16.dp),
|
||||||
|
horizontalAlignment = Alignment.CenterHorizontally,
|
||||||
|
verticalArrangement = Arrangement.Center
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = "Welcome to Tank App",
|
||||||
|
style = MaterialTheme.typography.headlineMedium,
|
||||||
|
textAlign = TextAlign.Center,
|
||||||
|
modifier = Modifier.padding(bottom = 32.dp)
|
||||||
|
)
|
||||||
|
|
||||||
|
Text(
|
||||||
|
text = "Please enter your Tankerkoenig API key to continue",
|
||||||
|
style = MaterialTheme.typography.bodyLarge,
|
||||||
|
textAlign = TextAlign.Center,
|
||||||
|
modifier = Modifier.padding(bottom = 24.dp)
|
||||||
|
)
|
||||||
|
|
||||||
|
OutlinedTextField(
|
||||||
|
value = apiKey,
|
||||||
|
onValueChange = {
|
||||||
|
apiKey = it
|
||||||
|
isError = false
|
||||||
|
},
|
||||||
|
label = { Text("API Key") },
|
||||||
|
isError = isError,
|
||||||
|
singleLine = true,
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(bottom = 16.dp),
|
||||||
|
supportingText = if (isError) {
|
||||||
|
{ Text("API key is required") }
|
||||||
|
} else {
|
||||||
|
{ Text("Get your API key from https://creativecommons.tankerkoenig.de") }
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
Button(
|
||||||
|
onClick = {
|
||||||
|
if (apiKey.isBlank()) {
|
||||||
|
isError = true
|
||||||
|
} else {
|
||||||
|
scope.launch {
|
||||||
|
settingsDataStore.saveApiKey(apiKey)
|
||||||
|
onApiKeySubmitted()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
modifier = Modifier.fillMaxWidth()
|
||||||
|
) {
|
||||||
|
Text("Continue")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,87 @@
|
|||||||
|
package com.example.tank.ui.screens
|
||||||
|
|
||||||
|
import androidx.compose.foundation.layout.*
|
||||||
|
import androidx.compose.material3.*
|
||||||
|
import androidx.compose.runtime.*
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.platform.LocalContext
|
||||||
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import com.example.tank.data.SettingsDataStore
|
||||||
|
import com.example.tank.di.NetworkModule
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
|
@Composable
|
||||||
|
fun LoginScreen(onLoginSuccess: () -> Unit) {
|
||||||
|
var apiKey by remember { mutableStateOf("") }
|
||||||
|
var isError by remember { mutableStateOf(false) }
|
||||||
|
var errorMessage by remember { mutableStateOf("") }
|
||||||
|
val context = LocalContext.current
|
||||||
|
val settingsDataStore = remember { SettingsDataStore(context) }
|
||||||
|
val scope = rememberCoroutineScope()
|
||||||
|
|
||||||
|
Column(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxSize()
|
||||||
|
.padding(16.dp),
|
||||||
|
horizontalAlignment = Alignment.CenterHorizontally,
|
||||||
|
verticalArrangement = Arrangement.Center
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = "Welcome to Tank App",
|
||||||
|
style = MaterialTheme.typography.headlineMedium,
|
||||||
|
textAlign = TextAlign.Center,
|
||||||
|
modifier = Modifier.padding(bottom = 32.dp)
|
||||||
|
)
|
||||||
|
|
||||||
|
OutlinedTextField(
|
||||||
|
value = apiKey,
|
||||||
|
onValueChange = {
|
||||||
|
apiKey = it
|
||||||
|
isError = false
|
||||||
|
},
|
||||||
|
label = { Text("API Key") },
|
||||||
|
isError = isError,
|
||||||
|
singleLine = true,
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(bottom = 8.dp),
|
||||||
|
)
|
||||||
|
|
||||||
|
if (isError) {
|
||||||
|
Text(
|
||||||
|
text = errorMessage,
|
||||||
|
color = MaterialTheme.colorScheme.error,
|
||||||
|
style = MaterialTheme.typography.bodySmall,
|
||||||
|
modifier = Modifier.padding(bottom = 16.dp)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
Text(
|
||||||
|
text = "Get your API key from https://creativecommons.tankerkoenig.de",
|
||||||
|
style = MaterialTheme.typography.bodySmall,
|
||||||
|
textAlign = TextAlign.Center,
|
||||||
|
modifier = Modifier.padding(bottom = 24.dp)
|
||||||
|
)
|
||||||
|
|
||||||
|
Button(
|
||||||
|
onClick = {
|
||||||
|
if (apiKey.isBlank()) {
|
||||||
|
isError = true
|
||||||
|
errorMessage = "API key is required"
|
||||||
|
} else {
|
||||||
|
scope.launch {
|
||||||
|
settingsDataStore.saveApiKey(apiKey)
|
||||||
|
NetworkModule.updateApiKey(apiKey)
|
||||||
|
onLoginSuccess()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
modifier = Modifier.fillMaxWidth()
|
||||||
|
) {
|
||||||
|
Text("Login")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,18 +1,57 @@
|
|||||||
package com.example.tank.ui.screens
|
package com.example.tank.ui.screens
|
||||||
|
|
||||||
|
import androidx.compose.foundation.layout.Box
|
||||||
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.filled.Settings
|
import androidx.compose.material.icons.filled.Settings
|
||||||
import androidx.compose.material3.*
|
import androidx.compose.material3.*
|
||||||
import androidx.compose.runtime.*
|
import androidx.compose.runtime.*
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.navigation.compose.NavHost
|
import androidx.navigation.compose.NavHost
|
||||||
import androidx.navigation.compose.composable
|
import androidx.navigation.compose.composable
|
||||||
import androidx.navigation.compose.rememberNavController
|
import androidx.navigation.compose.rememberNavController
|
||||||
|
import com.example.tank.data.SettingsDataStore
|
||||||
|
import com.example.tank.di.NetworkModule
|
||||||
|
import kotlinx.coroutines.flow.first
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun MainScreen() {
|
||||||
|
val context = LocalContext.current
|
||||||
|
val settingsDataStore = remember { SettingsDataStore(context) }
|
||||||
|
var hasApiKey by remember { mutableStateOf<Boolean?>(null) }
|
||||||
|
val scope = rememberCoroutineScope()
|
||||||
|
|
||||||
|
// Check if API key exists and initialize NetworkModule
|
||||||
|
LaunchedEffect(Unit) {
|
||||||
|
val apiKey = settingsDataStore.apiKey.first()
|
||||||
|
if (apiKey.isNotBlank()) {
|
||||||
|
NetworkModule.updateApiKey(apiKey)
|
||||||
|
hasApiKey = true
|
||||||
|
} else {
|
||||||
|
hasApiKey = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
when (hasApiKey) {
|
||||||
|
true -> MainContent()
|
||||||
|
false -> LoginScreen(
|
||||||
|
onLoginSuccess = {
|
||||||
|
scope.launch {
|
||||||
|
hasApiKey = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
null -> LoadingScreen()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun MainScreen() {
|
private fun MainContent() {
|
||||||
val navController = rememberNavController()
|
val navController = rememberNavController()
|
||||||
|
|
||||||
Scaffold(
|
Scaffold(
|
||||||
@@ -42,3 +81,13 @@ fun MainScreen() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun LoadingScreen() {
|
||||||
|
Box(
|
||||||
|
modifier = Modifier.fillMaxSize(),
|
||||||
|
contentAlignment = Alignment.Center
|
||||||
|
) {
|
||||||
|
CircularProgressIndicator()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -39,6 +39,10 @@ import android.content.pm.PackageManager
|
|||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
import com.google.android.gms.location.LocationServices
|
import com.google.android.gms.location.LocationServices
|
||||||
import com.google.android.gms.location.Priority
|
import com.google.android.gms.location.Priority
|
||||||
|
import com.google.android.gms.location.LocationRequest
|
||||||
|
import com.google.android.gms.location.LocationCallback
|
||||||
|
import com.google.android.gms.location.LocationResult
|
||||||
|
import android.os.Looper
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterialApi::class)
|
@OptIn(ExperimentalMaterialApi::class)
|
||||||
@Composable
|
@Composable
|
||||||
@@ -71,6 +75,28 @@ fun PricesScreen(viewModel: StationsViewModel = viewModel()) {
|
|||||||
LocationServices.getFusedLocationProviderClient(context)
|
LocationServices.getFusedLocationProviderClient(context)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val locationRequest = remember {
|
||||||
|
LocationRequest.Builder(Priority.PRIORITY_HIGH_ACCURACY, 1000)
|
||||||
|
.setWaitForAccurateLocation(true)
|
||||||
|
.setMinUpdateIntervalMillis(500)
|
||||||
|
.setMaxUpdateDelayMillis(1000)
|
||||||
|
.build()
|
||||||
|
}
|
||||||
|
|
||||||
|
val locationCallback = remember {
|
||||||
|
object : LocationCallback() {
|
||||||
|
override fun onLocationResult(result: LocationResult) {
|
||||||
|
result.lastLocation?.let { location ->
|
||||||
|
Log.d("PricesScreen", "Location update received: ${location.latitude}, ${location.longitude}")
|
||||||
|
scope.launch {
|
||||||
|
settingsDataStore.saveLocation(location.latitude, location.longitude)
|
||||||
|
viewModel.loadStations(location.latitude, location.longitude, selectedRadius)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Add radius collection
|
// Add radius collection
|
||||||
LaunchedEffect(Unit) {
|
LaunchedEffect(Unit) {
|
||||||
settingsDataStore.selectedRadius.collect { radius ->
|
settingsDataStore.selectedRadius.collect { radius ->
|
||||||
@@ -81,29 +107,26 @@ fun PricesScreen(viewModel: StationsViewModel = viewModel()) {
|
|||||||
// Add location mode collection
|
// Add location mode collection
|
||||||
LaunchedEffect(Unit) {
|
LaunchedEffect(Unit) {
|
||||||
settingsDataStore.locationMode.collect { mode ->
|
settingsDataStore.locationMode.collect { mode ->
|
||||||
|
Log.d("PricesScreen", "Location mode changed to: $mode")
|
||||||
locationMode = mode
|
locationMode = mode
|
||||||
// Reload stations when location mode changes
|
// Reload stations when location mode changes
|
||||||
when {
|
when {
|
||||||
mode == "gps" &&
|
mode == "gps" &&
|
||||||
ContextCompat.checkSelfPermission(context, Manifest.permission.ACCESS_FINE_LOCATION) ==
|
ContextCompat.checkSelfPermission(context, Manifest.permission.ACCESS_FINE_LOCATION) ==
|
||||||
PackageManager.PERMISSION_GRANTED -> {
|
PackageManager.PERMISSION_GRANTED -> {
|
||||||
|
Log.d("PricesScreen", "GPS mode and permission granted")
|
||||||
try {
|
try {
|
||||||
fusedLocationClient.getCurrentLocation(Priority.PRIORITY_HIGH_ACCURACY, null)
|
fusedLocationClient.requestLocationUpdates(
|
||||||
.addOnSuccessListener { location ->
|
locationRequest,
|
||||||
if (location != null) {
|
locationCallback,
|
||||||
scope.launch {
|
context.mainLooper
|
||||||
settingsDataStore.saveLocation(location.latitude, location.longitude)
|
).addOnSuccessListener {
|
||||||
viewModel.loadStations(location.latitude, location.longitude, selectedRadius)
|
Log.d("PricesScreen", "Location updates requested successfully")
|
||||||
}
|
}.addOnFailureListener { exception ->
|
||||||
} else {
|
Log.e("PricesScreen", "Failed to request location updates", exception)
|
||||||
// Fallback to saved location if GPS returns null
|
|
||||||
scope.launch {
|
|
||||||
val savedLocation = settingsDataStore.selectedLocation.first()
|
|
||||||
viewModel.loadStations(savedLocation.first, savedLocation.second, selectedRadius)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} catch (e: SecurityException) {
|
} catch (e: SecurityException) {
|
||||||
|
Log.e("PricesScreen", "Security exception when getting location", e)
|
||||||
// Fallback to saved location
|
// Fallback to saved location
|
||||||
scope.launch {
|
scope.launch {
|
||||||
val savedLocation = settingsDataStore.selectedLocation.first()
|
val savedLocation = settingsDataStore.selectedLocation.first()
|
||||||
@@ -112,9 +135,11 @@ fun PricesScreen(viewModel: StationsViewModel = viewModel()) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
|
Log.d("PricesScreen", "Using static mode or GPS permission denied")
|
||||||
// Use saved location for static mode or when GPS is not available
|
// Use saved location for static mode or when GPS is not available
|
||||||
scope.launch {
|
scope.launch {
|
||||||
val savedLocation = settingsDataStore.selectedLocation.first()
|
val savedLocation = settingsDataStore.selectedLocation.first()
|
||||||
|
Log.d("PricesScreen", "Using saved location: ${savedLocation.first}, ${savedLocation.second}")
|
||||||
viewModel.loadStations(savedLocation.first, savedLocation.second, selectedRadius)
|
viewModel.loadStations(savedLocation.first, savedLocation.second, selectedRadius)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -122,6 +147,13 @@ fun PricesScreen(viewModel: StationsViewModel = viewModel()) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Add cleanup
|
||||||
|
DisposableEffect(Unit) {
|
||||||
|
onDispose {
|
||||||
|
fusedLocationClient.removeLocationUpdates(locationCallback)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
val pullRefreshState = rememberPullRefreshState(
|
val pullRefreshState = rememberPullRefreshState(
|
||||||
refreshing = isLoading,
|
refreshing = isLoading,
|
||||||
onRefresh = {
|
onRefresh = {
|
||||||
|
|||||||
Reference in New Issue
Block a user