Gasolinera app

This commit is contained in:
2026-08-10 12:42:28 +02:00
parent 3d839131bd
commit b6857bc422
4 changed files with 249 additions and 132 deletions
@@ -12,6 +12,7 @@ import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.compose.setContent import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge import androidx.activity.enableEdgeToEdge
import androidx.activity.result.contract.ActivityResultContracts import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.foundation.clickable import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.* import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyColumn
@@ -23,18 +24,20 @@ import androidx.compose.material3.pulltorefresh.PullToRefreshBox
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
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import com.example.prueba.data.GasStationInfo import com.example.prueba.data.GasStationInfo
import com.example.prueba.data.GasStationRepository import com.example.prueba.data.GasStationRepository
import com.example.prueba.data.PriceCategory
import com.example.prueba.ui.theme.PruebaTheme import com.example.prueba.ui.theme.PruebaTheme
import com.example.prueba.utils.FavoriteStorage import com.example.prueba.utils.FavoriteStorage
import com.example.prueba.utils.FuelRecord import com.example.prueba.utils.FuelRecord
import com.example.prueba.utils.FuelStorage import com.example.prueba.utils.FuelStorage
import com.example.prueba.utils.LocationStorage import com.example.prueba.utils.LocationStorage
import com.google.android.gms.location.* import com.google.android.gms.location.*
import com.google.android.gms.tasks.CancellationTokenSource
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
@@ -68,6 +71,8 @@ fun GasStationApp(repository: GasStationRepository) {
var isLoading by remember { mutableStateOf(false) } var isLoading by remember { mutableStateOf(false) }
var isRefreshing by remember { mutableStateOf(false) } var isRefreshing by remember { mutableStateOf(false) }
var selectedTab by remember { mutableIntStateOf(0) } var selectedTab by remember { mutableIntStateOf(0) }
var searchQuery by remember { mutableStateOf("") }
var isSearchActive by remember { mutableStateOf(false) }
var hasPermission by remember { var hasPermission by remember {
mutableStateOf( mutableStateOf(
@@ -104,17 +109,21 @@ fun GasStationApp(repository: GasStationRepository) {
} }
} }
LaunchedEffect(hasPermission) { LaunchedEffect(hasPermission, searchQuery, selectedTab) {
if (hasPermission) { if (hasPermission) {
val fusedLocationClient = LocationServices.getFusedLocationProviderClient(context) val fusedLocationClient = LocationServices.getFusedLocationProviderClient(context)
LocationStorage.getLastLocation(context)?.let { lastLoc -> val loc = lastLocation ?: LocationStorage.getLastLocation(context)
scope.launch {
updateAddress(lastLoc) loc?.let {
stations = repository.getCheapestAndNearest(context, lastLoc) stations = repository.getCheapestAndNearest(
} context = context,
userLocation = it,
municipalityQuery = if (selectedTab == 0) searchQuery.ifBlank { null } else null,
onlyFavorites = (selectedTab == 1)
)
} }
isLoading = true if (selectedTab != 2) isLoading = true
val locationRequest = LocationRequest.Builder(Priority.PRIORITY_HIGH_ACCURACY, 10000) val locationRequest = LocationRequest.Builder(Priority.PRIORITY_HIGH_ACCURACY, 10000)
.setMinUpdateDistanceMeters(500f) .setMinUpdateDistanceMeters(500f)
.build() .build()
@@ -124,9 +133,13 @@ fun GasStationApp(repository: GasStationRepository) {
val location = result.lastLocation ?: return val location = result.lastLocation ?: return
lastLocation = location lastLocation = location
LocationStorage.saveLocation(context, location) LocationStorage.saveLocation(context, location)
scope.launch { if (searchQuery.isBlank()) {
updateAddress(location) scope.launch {
stations = repository.getCheapestAndNearest(context, location) updateAddress(location)
stations = repository.getCheapestAndNearest(context, location)
isLoading = false
}
} else {
isLoading = false isLoading = false
} }
} }
@@ -145,25 +158,56 @@ fun GasStationApp(repository: GasStationRepository) {
Scaffold( Scaffold(
modifier = Modifier.fillMaxSize(), modifier = Modifier.fillMaxSize(),
topBar = { topBar = {
CenterAlignedTopAppBar( Column {
title = { CenterAlignedTopAppBar(
Column(horizontalAlignment = Alignment.CenterHorizontally) { title = {
Text("Gasolineras Baratas", style = MaterialTheme.typography.titleSmall) if (isSearchActive) {
Text( TextField(
text = currentAddress, value = searchQuery,
style = MaterialTheme.typography.bodyMedium, onValueChange = { searchQuery = it },
color = MaterialTheme.colorScheme.primary, placeholder = { Text("Buscar municipio...") },
maxLines = 1 modifier = Modifier.fillMaxWidth().padding(horizontal = 16.dp),
) singleLine = true,
colors = TextFieldDefaults.colors(
focusedContainerColor = Color.Transparent,
unfocusedContainerColor = Color.Transparent
),
trailingIcon = {
IconButton(onClick = {
searchQuery = ""
isSearchActive = false
}) {
Icon(Icons.Default.Close, contentDescription = "Cerrar")
}
}
)
} else {
Column(horizontalAlignment = Alignment.CenterHorizontally) {
Text("Gasolineras Diésel", style = MaterialTheme.typography.titleSmall)
Text(
text = currentAddress,
style = MaterialTheme.typography.bodyMedium,
color = MaterialTheme.colorScheme.primary,
maxLines = 1
)
}
}
},
actions = {
if (!isSearchActive && selectedTab != 2) {
IconButton(onClick = { isSearchActive = true }) {
Icon(Icons.Default.Search, contentDescription = "Buscar")
}
}
} }
} )
) }
}, },
bottomBar = { bottomBar = {
NavigationBar { NavigationBar {
NavigationBarItem( NavigationBarItem(
icon = { Icon(Icons.Default.Place, "Gasolineras") }, icon = { Icon(Icons.Default.Place, "Gasolineras") },
label = { Text("Gasolineras") }, label = { Text("Diésel") },
selected = selectedTab == 0, selected = selectedTab == 0,
onClick = { selectedTab = 0 } onClick = { selectedTab = 0 }
) )
@@ -203,7 +247,7 @@ fun GasStationApp(repository: GasStationRepository) {
lastLocation?.let { loc -> lastLocation?.let { loc ->
isRefreshing = true isRefreshing = true
scope.launch { scope.launch {
stations = repository.getCheapestAndNearest(context, loc, forceRefresh = true) stations = repository.getCheapestAndNearest(context, loc, forceRefresh = true, municipalityQuery = if(searchQuery.isNotBlank()) searchQuery else null)
isRefreshing = false isRefreshing = false
} }
} }
@@ -223,22 +267,23 @@ fun GasStationApp(repository: GasStationRepository) {
repository = repository, repository = repository,
lastLocation = lastLocation, lastLocation = lastLocation,
onLogClick = { showFuelDialog = it }, onLogClick = { showFuelDialog = it },
onUpdateStations = { stations = it } onUpdateStations = { stations = it },
searchQuery = searchQuery
) )
} }
} else { } else {
val favoriteStations = stations.filter { it.isFavorite } if (stations.isEmpty()) {
if (favoriteStations.isEmpty()) {
Text("No tienes gasolineras favoritas marcadas", modifier = Modifier.align(Alignment.Center)) Text("No tienes gasolineras favoritas marcadas", modifier = Modifier.align(Alignment.Center))
} else { } else {
GasStationList( GasStationList(
stations = favoriteStations, stations = stations,
context = context, context = context,
scope = scope, scope = scope,
repository = repository, repository = repository,
lastLocation = lastLocation, lastLocation = lastLocation,
onLogClick = { showFuelDialog = it }, onLogClick = { showFuelDialog = it },
onUpdateStations = { stations = it } onUpdateStations = { stations = it },
searchQuery = ""
) )
} }
} }
@@ -262,7 +307,6 @@ fun GasStationApp(repository: GasStationRepository) {
showFuelDialog?.let { station -> showFuelDialog?.let { station ->
FuelLogDialog( FuelLogDialog(
stationName = station.name, stationName = station.name,
pricePerLiter = station.price,
onDismiss = { showFuelDialog = null }, onDismiss = { showFuelDialog = null },
onConfirm = { liters, total -> onConfirm = { liters, total ->
val record = FuelRecord( val record = FuelRecord(
@@ -284,7 +328,6 @@ fun GasStationApp(repository: GasStationRepository) {
editRecordDialog?.let { record -> editRecordDialog?.let { record ->
FuelLogDialog( FuelLogDialog(
stationName = record.stationName, stationName = record.stationName,
pricePerLiter = record.pricePerLiter,
initialLiters = record.liters.toString(), initialLiters = record.liters.toString(),
initialTotal = record.totalPrice.toString(), initialTotal = record.totalPrice.toString(),
onDismiss = { editRecordDialog = null }, onDismiss = { editRecordDialog = null },
@@ -311,7 +354,8 @@ fun GasStationList(
repository: GasStationRepository, repository: GasStationRepository,
lastLocation: android.location.Location?, lastLocation: android.location.Location?,
onLogClick: (GasStationInfo) -> Unit, onLogClick: (GasStationInfo) -> Unit,
onUpdateStations: (List<GasStationInfo>) -> Unit onUpdateStations: (List<GasStationInfo>) -> Unit,
searchQuery: String
) { ) {
LazyColumn(contentPadding = PaddingValues(vertical = 8.dp)) { LazyColumn(contentPadding = PaddingValues(vertical = 8.dp)) {
items(stations) { station -> items(stations) { station ->
@@ -325,9 +369,10 @@ fun GasStationList(
onLogClick = { onLogClick(station) }, onLogClick = { onLogClick(station) },
onFavoriteClick = { onFavoriteClick = {
FavoriteStorage.toggleFavorite(context, station.id) FavoriteStorage.toggleFavorite(context, station.id)
lastLocation?.let { loc -> val loc = lastLocation ?: LocationStorage.getLastLocation(context)
loc?.let {
scope.launch { scope.launch {
onUpdateStations(repository.getCheapestAndNearest(context, loc)) onUpdateStations(repository.getCheapestAndNearest(context, it, municipalityQuery = if(searchQuery.isNotBlank()) searchQuery else null))
} }
} }
} }
@@ -386,7 +431,6 @@ fun FuelHistoryScreen(records: List<FuelRecord>, onDelete: (FuelRecord) -> Unit,
@Composable @Composable
fun FuelLogDialog( fun FuelLogDialog(
stationName: String, stationName: String,
pricePerLiter: Double,
initialLiters: String = "", initialLiters: String = "",
initialTotal: String = "", initialTotal: String = "",
onDismiss: () -> Unit, onDismiss: () -> Unit,
@@ -431,6 +475,21 @@ fun FuelLogDialog(
@Composable @Composable
fun GasStationItem(station: GasStationInfo, onClick: () -> Unit, onLogClick: () -> Unit, onFavoriteClick: () -> Unit) { fun GasStationItem(station: GasStationInfo, onClick: () -> Unit, onLogClick: () -> Unit, onFavoriteClick: () -> Unit) {
val priceColor = when (station.priceCategory) {
PriceCategory.CHEAP -> Color(0xFF2E7D32)
PriceCategory.NORMAL -> MaterialTheme.colorScheme.primary
PriceCategory.EXPENSIVE -> Color(0xFFC62828)
}
val brandColor = when {
station.name.contains("REPSOL", ignoreCase = true) -> Color(0xFFE30613)
station.name.contains("CEPSA", ignoreCase = true) -> Color(0xFFEC0000)
station.name.contains("BP", ignoreCase = true) -> Color(0xFF00A94F)
station.name.contains("GALP", ignoreCase = true) -> Color(0xFFFF6B00)
station.name.contains("SHELL", ignoreCase = true) -> Color(0xFFFFD500)
else -> MaterialTheme.colorScheme.surfaceVariant
}
Card( Card(
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
@@ -446,6 +505,11 @@ fun GasStationItem(station: GasStationInfo, onClick: () -> Unit, onLogClick: ()
) { ) {
Column(modifier = Modifier.weight(1f)) { Column(modifier = Modifier.weight(1f)) {
Row(verticalAlignment = Alignment.CenterVertically) { Row(verticalAlignment = Alignment.CenterVertically) {
Surface(
color = brandColor,
modifier = Modifier.size(12.dp).padding(end = 8.dp),
shape = androidx.compose.foundation.shape.CircleShape
) {}
Text( Text(
text = "${station.name} - ${station.municipality}", text = "${station.name} - ${station.municipality}",
style = MaterialTheme.typography.titleMedium, style = MaterialTheme.typography.titleMedium,
@@ -471,9 +535,9 @@ fun GasStationItem(station: GasStationInfo, onClick: () -> Unit, onLogClick: ()
shape = MaterialTheme.shapes.medium shape = MaterialTheme.shapes.medium
) { ) {
Text( Text(
text = "${String.format(Locale.getDefault(), "%.1f", station.distance / 1000)} km", text = "${String.format(Locale.getDefault(), "%.1f", station.distance / 1000)} km (recta)",
modifier = Modifier.padding(horizontal = 10.dp, vertical = 6.dp), modifier = Modifier.padding(horizontal = 10.dp, vertical = 6.dp),
style = MaterialTheme.typography.labelLarge, style = MaterialTheme.typography.labelMedium,
color = MaterialTheme.colorScheme.onSecondaryContainer color = MaterialTheme.colorScheme.onSecondaryContainer
) )
} }
@@ -482,14 +546,24 @@ fun GasStationItem(station: GasStationInfo, onClick: () -> Unit, onLogClick: ()
Row( Row(
modifier = Modifier.fillMaxWidth(), modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween, horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically verticalAlignment = Alignment.Bottom
) { ) {
Text( Column {
text = "${station.price} €/L", Text(
style = MaterialTheme.typography.headlineSmall, text = "Diésel A: ${station.price} €/L",
color = MaterialTheme.colorScheme.primary, style = MaterialTheme.typography.titleLarge,
fontWeight = androidx.compose.ui.text.font.FontWeight.Bold color = priceColor,
) fontWeight = FontWeight.Bold
)
station.pricePremium?.let { premium ->
Text(
text = "Premium: $premium €/L",
style = MaterialTheme.typography.bodyMedium,
color = MaterialTheme.colorScheme.secondary,
fontWeight = FontWeight.Medium
)
}
}
Button( Button(
onClick = onLogClick, onClick = onLogClick,
contentPadding = PaddingValues(horizontal = 12.dp, vertical = 4.dp), contentPadding = PaddingValues(horizontal = 12.dp, vertical = 4.dp),
@@ -147,68 +147,74 @@ class MainScreen(carContext: CarContext) : Screen(carContext) {
for (station in gasStations) { for (station in gasStations) {
val distanceStr = String.format(Locale.getDefault(), "%.1f km", station.distance / 1000) val distanceStr = String.format(Locale.getDefault(), "%.1f km", station.distance / 1000)
listBuilder.addItem(
Row.Builder()
.setTitle("${station.name} - ${station.municipality}")
.addText("${station.price} €/L • $distanceStr")
.addText(station.address)
.setOnClickListener {
val pane = Pane.Builder()
.addRow(Row.Builder().setTitle("Precio").addText("${station.price} €/L").build())
.addRow(Row.Builder().setTitle("Distancia").addText(distanceStr).build())
.addAction(
Action.Builder()
.setTitle(if (station.isFavorite) "Quitar Favorito" else "Añadir Favorito")
.setOnClickListener {
FavoriteStorage.toggleFavorite(carContext, station.id)
screenManager.pop()
fetchLocationAndGasStations(forceRefresh = false)
}
.build()
)
.addAction(
Action.Builder()
.setTitle("Ir ahora")
.setOnClickListener {
val uri = Uri.parse("geo:${station.latitude},${station.longitude}?q=${station.latitude},${station.longitude}")
val intent = Intent(CarContext.ACTION_NAVIGATE, uri)
carContext.startCarApp(intent)
}
.build()
)
.addAction(
Action.Builder()
.setTitle("Registrar Repostaje")
.setOnClickListener {
// Guardar record simplificado desde el coche
val record = FuelRecord(
date = System.currentTimeMillis(),
stationName = station.name,
address = station.address,
municipality = station.municipality,
liters = 0.0, // Se editará en el móvil después
totalPrice = 0.0,
pricePerLiter = station.price
)
FuelStorage.saveRecord(carContext, record)
screenManager.pop()
}
.build()
)
.build()
screenManager.push( val rowBuilder = Row.Builder()
object : Screen(carContext) { .setTitle("${station.name} - ${station.municipality}")
override fun onGetTemplate(): Template { .addText("A: ${station.price} €/L" + (station.pricePremium?.let { " • P: $it €/L" } ?: ""))
return PaneTemplate.Builder(pane) .addText("$distanceStr (recta) ${station.address}")
.setHeader(Header.Builder().setTitle(station.name).setStartHeaderAction(Action.BACK).build()) .setOnClickListener {
.build() val paneBuilder = Pane.Builder()
} .addRow(Row.Builder().setTitle("Diésel A").addText("${station.price} €/L").build())
}
) station.pricePremium?.let {
paneBuilder.addRow(Row.Builder().setTitle("Diésel Premium").addText("$it €/L").build())
} }
.build()
) val pane = paneBuilder
.addRow(Row.Builder().setTitle("Ubicación").addText("${station.municipality}\n${station.address}").build())
.addRow(Row.Builder().setTitle("Distancia").addText("$distanceStr (en línea recta)").build())
.addAction(
Action.Builder()
.setTitle(if (station.isFavorite) "Quitar Favorito" else "Añadir Favorito")
.setOnClickListener {
FavoriteStorage.toggleFavorite(carContext, station.id)
screenManager.pop()
fetchLocationAndGasStations(forceRefresh = false)
}
.build()
)
.addAction(
Action.Builder()
.setTitle("Ir ahora")
.setOnClickListener {
val uri = Uri.parse("geo:${station.latitude},${station.longitude}?q=${station.latitude},${station.longitude}")
val intent = Intent(CarContext.ACTION_NAVIGATE, uri)
carContext.startCarApp(intent)
}
.build()
)
.addAction(
Action.Builder()
.setTitle("Registrar")
.setOnClickListener {
val record = FuelRecord(
date = System.currentTimeMillis(),
stationName = station.name,
address = station.address,
municipality = station.municipality,
liters = 0.0,
totalPrice = 0.0,
pricePerLiter = station.price
)
FuelStorage.saveRecord(carContext, record)
screenManager.pop()
}
.build()
)
.build()
screenManager.push(
object : Screen(carContext) {
override fun onGetTemplate(): Template {
return PaneTemplate.Builder(pane)
.setHeader(Header.Builder().setTitle(station.name).setStartHeaderAction(Action.BACK).build())
.build()
}
}
)
}
listBuilder.addItem(rowBuilder.build())
} }
return ListTemplate.Builder() return ListTemplate.Builder()
@@ -15,7 +15,13 @@ class GasStationRepository {
.build() .build()
.create(GasolineraApi::class.java) .create(GasolineraApi::class.java)
suspend fun getCheapestAndNearest(context: Context, userLocation: Location, forceRefresh: Boolean = false): List<GasStationInfo> { suspend fun getCheapestAndNearest(
context: Context,
userLocation: Location,
forceRefresh: Boolean = false,
municipalityQuery: String? = null,
onlyFavorites: Boolean = false
): List<GasStationInfo> {
return try { return try {
if (cachedResponse == null || forceRefresh) { if (cachedResponse == null || forceRefresh) {
cachedResponse = api.getGasolineras() cachedResponse = api.getGasolineras()
@@ -23,47 +29,76 @@ class GasStationRepository {
val favorites = FavoriteStorage.getFavoriteIds(context) val favorites = FavoriteStorage.getFavoriteIds(context)
cachedResponse?.listaEESS?.mapNotNull { val allStations = cachedResponse?.listaEESS?.mapNotNull {
val lat = it.latitud.replace(",", ".").toDoubleOrNull() val lat = it.latitud.replace(",", ".").toDoubleOrNull()
val lon = it.longitud.replace(",", ".").toDoubleOrNull() val lon = it.longitud.replace(",", ".").toDoubleOrNull()
val price = it.precioDiesel.replace(",", ".").toDoubleOrNull() ?: it.precioGasolina95.replace(",", ".").toDoubleOrNull()
if (lat != null && lon != null && price != null) { if (lat != null && lon != null) {
val stationLoc = Location("").apply { val dieselPrice = it.precioDiesel.replace(",", ".").toDoubleOrNull()
latitude = lat val premiumPrice = it.precioDieselPremium.replace(",", ".").toDoubleOrNull()
longitude = lon
} if (dieselPrice != null || premiumPrice != null) {
val distance = userLocation.distanceTo(stationLoc) val stationLoc = Location("").apply {
GasStationInfo( latitude = lat
id = it.id, longitude = lon
name = it.rotulo, }
address = it.direccion, val distance = userLocation.distanceTo(stationLoc)
municipality = it.municipio, GasStationInfo(
price = price, id = it.id,
distance = distance, name = it.rotulo,
latitude = lat, address = it.direccion,
longitude = lon, municipality = it.municipio,
isFavorite = favorites.contains(it.id) price = dieselPrice ?: 0.0,
) pricePremium = premiumPrice,
distance = distance,
latitude = lat,
longitude = lon,
isFavorite = favorites.contains(it.id)
)
} else null
} else null } else null
}?.sortedBy { it.distance } } ?: emptyList()
?.take(50)
?.sortedWith(compareByDescending<GasStationInfo> { it.isFavorite }.thenBy { it.price }) val filteredStations = when {
?.take(10) ?: emptyList() onlyFavorites -> allStations.filter { it.isFavorite }
.sortedBy { it.distance }
!municipalityQuery.isNullOrBlank() -> allStations.filter { it.municipality.contains(municipalityQuery, ignoreCase = true) }
.sortedBy { it.distance }
else -> allStations.sortedBy { it.distance }.take(50)
}
if (filteredStations.isEmpty()) return emptyList()
val minPrice = filteredStations.minOf { it.price }
val avgPrice = filteredStations.map { it.price }.average()
filteredStations.map { station ->
val category = when {
station.price <= minPrice * 1.01 -> PriceCategory.CHEAP
station.price <= avgPrice -> PriceCategory.NORMAL
else -> PriceCategory.EXPENSIVE
}
station.copy(priceCategory = category)
}.sortedWith(compareByDescending<GasStationInfo> { it.isFavorite }.thenBy { it.price })
.take(if (onlyFavorites) 50 else 15)
} catch (e: Exception) { } catch (e: Exception) {
emptyList() emptyList()
} }
} }
} }
enum class PriceCategory { CHEAP, NORMAL, EXPENSIVE }
data class GasStationInfo( data class GasStationInfo(
val id: String, val id: String,
val name: String, val name: String,
val address: String, val address: String,
val municipality: String, val municipality: String,
val price: Double, val price: Double,
val pricePremium: Double? = null,
val distance: Float, val distance: Float,
val latitude: Double, val latitude: Double,
val longitude: Double, val longitude: Double,
val isFavorite: Boolean = false val isFavorite: Boolean = false,
val priceCategory: PriceCategory = PriceCategory.NORMAL
) )
@@ -19,6 +19,8 @@ data class Gasolinera(
val municipio: String, val municipio: String,
@SerializedName("Precio Gasoleo A") @SerializedName("Precio Gasoleo A")
val precioDiesel: String, val precioDiesel: String,
@SerializedName("Precio Gasoleo Premium")
val precioDieselPremium: String,
@SerializedName("Precio Gasolina 95 E5") @SerializedName("Precio Gasolina 95 E5")
val precioGasolina95: String, val precioGasolina95: String,
@SerializedName("Latitud") @SerializedName("Latitud")