Gasolinera app

This commit is contained in:
2026-08-11 08:51:44 +02:00
parent 1e1fa640e7
commit 2b39f873b3
3 changed files with 61 additions and 60 deletions
@@ -1,7 +1,6 @@
package com.example.prueba
import android.Manifest
import android.annotation.SuppressLint
import android.content.Intent
import android.content.pm.PackageManager
import android.location.Geocoder
@@ -28,7 +27,6 @@ import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.core.content.ContextCompat
import com.example.prueba.data.GasStationInfo
import com.example.prueba.data.GasStationRepository
@@ -74,7 +72,6 @@ fun GasStationApp(repository: GasStationRepository) {
var destinationQuery by remember { mutableStateOf("") }
var isRouteActive by remember { mutableStateOf(false) }
var isReserveFilterActive by remember { mutableStateOf(false) }
var destinationLocation by remember { mutableStateOf<android.location.Location?>(null) }
var hasPermission by remember {
@@ -96,6 +93,7 @@ fun GasStationApp(repository: GasStationRepository) {
withContext(Dispatchers.IO) {
try {
val geocoder = Geocoder(context, Locale.getDefault())
@Suppress("DEPRECATION")
val addresses = geocoder.getFromLocation(location.latitude, location.longitude, 1)
if (!addresses.isNullOrEmpty()) {
val address = addresses[0]
@@ -107,29 +105,14 @@ fun GasStationApp(repository: GasStationRepository) {
}
}
LaunchedEffect(hasPermission, destinationLocation, selectedTab, isReserveFilterActive) {
// Registro del rastreador de GPS (Se ejecuta solo al inicio o si cambia el permiso)
LaunchedEffect(hasPermission) {
if (hasPermission) {
val loc = lastLocation ?: LocationStorage.getLastLocation(context)
loc?.let {
// Primero recta para rapidez
stations = repository.getCheapestAndNearest(
userLocation = it,
maxDistanceKm = if (isReserveFilterActive) 40.0 else null,
destinationLocation = if (isRouteActive) destinationLocation else null,
getRealDistances = false
)
// Luego real para precisión
scope.launch {
stations = repository.getCheapestAndNearest(
userLocation = it,
maxDistanceKm = if (isReserveFilterActive) 40.0 else null,
destinationLocation = if (isRouteActive) destinationLocation else null,
getRealDistances = true
)
}
}
if (selectedTab == 0) isLoading = true
val fusedLocationClient = LocationServices.getFusedLocationProviderClient(context)
// Cargar ubicación inicial guardada para no empezar de cero
LocationStorage.getLastLocation(context)?.let { lastLocation = it }
val locationRequest = LocationRequest.Builder(Priority.PRIORITY_HIGH_ACCURACY, 15000)
.setMinUpdateDistanceMeters(500f)
.build()
@@ -139,24 +122,10 @@ fun GasStationApp(repository: GasStationRepository) {
val location = result.lastLocation ?: return
lastLocation = location
LocationStorage.saveLocation(context, location)
if (selectedTab == 0) {
scope.launch {
updateAddress(location)
stations = repository.getCheapestAndNearest(
userLocation = location,
maxDistanceKm = if (isReserveFilterActive) 40.0 else null,
destinationLocation = if (isRouteActive) destinationLocation else null,
getRealDistances = true
)
isLoading = false
}
} else {
isLoading = false
}
scope.launch { updateAddress(location) }
}
}
val fusedLocationClient = LocationServices.getFusedLocationProviderClient(context)
try {
if (ContextCompat.checkSelfPermission(context, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
fusedLocationClient.requestLocationUpdates(locationRequest, locationCallback, android.os.Looper.getMainLooper())
@@ -167,6 +136,36 @@ fun GasStationApp(repository: GasStationRepository) {
}
}
// Actualizador de la lista de gasolineras (Reacciona al movimiento del GPS)
LaunchedEffect(hasPermission, destinationLocation, selectedTab, isRouteActive, lastLocation) {
if (hasPermission) {
val loc = lastLocation ?: LocationStorage.getLastLocation(context)
loc?.let {
if (selectedTab == 0) {
// 1. Carga ultra-rápida (recta)
stations = repository.getCheapestAndNearest(
userLocation = it,
destinationLocation = if (isRouteActive) destinationLocation else null,
getRealDistances = false
)
// 2. Carga de precisión (carretera)
val realStations = repository.getCheapestAndNearest(
userLocation = it,
destinationLocation = if (isRouteActive) destinationLocation else null,
getRealDistances = true
)
if (realStations.isNotEmpty()) {
stations = realStations
}
isLoading = false
}
}
if (selectedTab == 0 && stations.isEmpty()) isLoading = true
}
}
Scaffold(
modifier = Modifier.fillMaxSize(),
topBar = {
@@ -187,6 +186,7 @@ fun GasStationApp(repository: GasStationRepository) {
withContext(Dispatchers.IO) {
try {
val geocoder = Geocoder(context, Locale.getDefault())
@Suppress("DEPRECATION")
val results = geocoder.getFromLocationName(destinationQuery, 1)
if (!results.isNullOrEmpty()) {
destinationLocation = android.location.Location("").apply {
@@ -257,8 +257,8 @@ fun GasStationApp(repository: GasStationRepository) {
scope.launch {
stations = repository.getCheapestAndNearest(
userLocation = loc, forceRefresh = true,
maxDistanceKm = if (isReserveFilterActive) 40.0 else null,
destinationLocation = if (isRouteActive) destinationLocation else null
destinationLocation = if (isRouteActive) destinationLocation else null,
getRealDistances = true
)
isRefreshing = false
}
@@ -269,7 +269,7 @@ fun GasStationApp(repository: GasStationRepository) {
if (isLoading && stations.isEmpty()) {
CircularProgressIndicator(modifier = Modifier.align(Alignment.Center))
} else if (stations.isEmpty()) {
Text(if (isReserveFilterActive) "No hay nada a menos de 40km" else "Sin resultados", modifier = Modifier.align(Alignment.Center))
Text("Sin resultados", modifier = Modifier.align(Alignment.Center))
} else {
GasStationList(stations = stations, onLogClick = { showFuelDialog = it })
}
@@ -454,10 +454,15 @@ fun GasStationItem(station: GasStationInfo, onClick: () -> Unit, onLogClick: ()
Text(text = "Premium: $premium €/L", style = MaterialTheme.typography.bodyMedium, color = MaterialTheme.colorScheme.secondary, fontWeight = FontWeight.Medium)
}
}
Button(onClick = onLogClick, contentPadding = PaddingValues(horizontal = 12.dp, vertical = 4.dp), shape = MaterialTheme.shapes.small) {
Icon(Icons.Default.Add, null, modifier = Modifier.size(16.dp))
Spacer(modifier = Modifier.width(4.dp))
Text("Registrar")
FilledTonalIconButton(
onClick = onLogClick,
modifier = Modifier.size(40.dp)
) {
Icon(
imageVector = Icons.Default.Add,
contentDescription = "Registrar repostaje",
modifier = Modifier.size(24.dp)
)
}
}
}
@@ -24,7 +24,6 @@ class MainScreen(carContext: CarContext) : Screen(carContext) {
private var gasStations: List<GasStationInfo> = emptyList()
private var currentAddress: String? = null
private var isLoading = true
private var isReserveFilterActive = false
private val repository = GasStationRepository()
init {
@@ -54,17 +53,13 @@ class MainScreen(carContext: CarContext) : Screen(carContext) {
LocationStorage.getLastLocation(carContext)?.let { lastLoc ->
lifecycleScope.launch {
updateAddress(lastLoc)
// Primero recta
gasStations = repository.getCheapestAndNearest(
userLocation = lastLoc,
maxDistanceKm = if (isReserveFilterActive) 40.0 else null,
getRealDistances = false
)
invalidate()
// Luego real
gasStations = repository.getCheapestAndNearest(
userLocation = lastLoc,
maxDistanceKm = if (isReserveFilterActive) 40.0 else null,
getRealDistances = true
)
invalidate()
@@ -85,12 +80,15 @@ class MainScreen(carContext: CarContext) : Screen(carContext) {
gasStations = repository.getCheapestAndNearest(
userLocation = location,
forceRefresh = forceRefresh,
maxDistanceKm = if (isReserveFilterActive) 40.0 else null,
getRealDistances = true
)
isLoading = false
invalidate()
}
if (forceRefresh) {
fusedLocationClient.removeLocationUpdates(this)
fetchLocationAndGasStations(false)
}
}
}
@@ -100,13 +98,12 @@ class MainScreen(carContext: CarContext) : Screen(carContext) {
override fun onGetTemplate(): Template {
val listBuilder = ItemList.Builder()
val reserveAction = Action.Builder()
.setTitle(if (isReserveFilterActive) "Todo" else "Reserva")
val refreshAction = Action.Builder()
.setTitle("Refrescar")
.setOnClickListener {
isReserveFilterActive = !isReserveFilterActive
isLoading = true
invalidate()
fetchLocationAndGasStations(forceRefresh = false)
fetchLocationAndGasStations(forceRefresh = true)
}
.build()
@@ -119,7 +116,7 @@ class MainScreen(carContext: CarContext) : Screen(carContext) {
if (gasStations.isEmpty()) {
return MessageTemplate.Builder("Sin resultados")
.setHeader(Header.Builder().setTitle("Gasolineras").addEndHeaderAction(reserveAction).build())
.setHeader(Header.Builder().setTitle("Gasolineras").addEndHeaderAction(refreshAction).build())
.addAction(Action.Builder().setTitle("Reintentar").setOnClickListener {
isLoading = true
invalidate()
@@ -180,7 +177,7 @@ class MainScreen(carContext: CarContext) : Screen(carContext) {
.setHeader(
Header.Builder()
.setTitle(currentAddress ?: "Gasolineras Diésel")
.addEndHeaderAction(reserveAction)
.addEndHeaderAction(refreshAction)
.build()
)
.build()
@@ -25,7 +25,6 @@ class GasStationRepository {
suspend fun getCheapestAndNearest(
userLocation: Location,
forceRefresh: Boolean = false,
maxDistanceKm: Double? = null,
destinationLocation: Location? = null,
getRealDistances: Boolean = false
): List<GasStationInfo> {
@@ -75,7 +74,7 @@ class GasStationRepository {
} ?: emptyList()
// Radio máximo: 30km si es local, 400km si hay destino (ruta larga)
val finalMaxDist = maxDistanceKm ?: if (destinationLocation == null) 30.0 else 400.0
val finalMaxDist = if (destinationLocation == null) 30.0 else 400.0
val filtered = allStations.filter { (it.distance / 1000.0) <= finalMaxDist }