Gasolinera app
This commit is contained in:
@@ -1,7 +1,6 @@
|
|||||||
package com.example.prueba
|
package com.example.prueba
|
||||||
|
|
||||||
import android.Manifest
|
import android.Manifest
|
||||||
import android.annotation.SuppressLint
|
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.content.pm.PackageManager
|
import android.content.pm.PackageManager
|
||||||
import android.location.Geocoder
|
import android.location.Geocoder
|
||||||
@@ -28,7 +27,6 @@ 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.text.font.FontWeight
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.unit.sp
|
|
||||||
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
|
||||||
@@ -74,7 +72,6 @@ fun GasStationApp(repository: GasStationRepository) {
|
|||||||
|
|
||||||
var destinationQuery by remember { mutableStateOf("") }
|
var destinationQuery by remember { mutableStateOf("") }
|
||||||
var isRouteActive by remember { mutableStateOf(false) }
|
var isRouteActive by remember { mutableStateOf(false) }
|
||||||
var isReserveFilterActive by remember { mutableStateOf(false) }
|
|
||||||
var destinationLocation by remember { mutableStateOf<android.location.Location?>(null) }
|
var destinationLocation by remember { mutableStateOf<android.location.Location?>(null) }
|
||||||
|
|
||||||
var hasPermission by remember {
|
var hasPermission by remember {
|
||||||
@@ -96,6 +93,7 @@ fun GasStationApp(repository: GasStationRepository) {
|
|||||||
withContext(Dispatchers.IO) {
|
withContext(Dispatchers.IO) {
|
||||||
try {
|
try {
|
||||||
val geocoder = Geocoder(context, Locale.getDefault())
|
val geocoder = Geocoder(context, Locale.getDefault())
|
||||||
|
@Suppress("DEPRECATION")
|
||||||
val addresses = geocoder.getFromLocation(location.latitude, location.longitude, 1)
|
val addresses = geocoder.getFromLocation(location.latitude, location.longitude, 1)
|
||||||
if (!addresses.isNullOrEmpty()) {
|
if (!addresses.isNullOrEmpty()) {
|
||||||
val address = addresses[0]
|
val address = addresses[0]
|
||||||
@@ -107,28 +105,13 @@ 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) {
|
if (hasPermission) {
|
||||||
val loc = lastLocation ?: LocationStorage.getLastLocation(context)
|
val fusedLocationClient = LocationServices.getFusedLocationProviderClient(context)
|
||||||
loc?.let {
|
|
||||||
// Primero recta para rapidez
|
// Cargar ubicación inicial guardada para no empezar de cero
|
||||||
stations = repository.getCheapestAndNearest(
|
LocationStorage.getLastLocation(context)?.let { lastLocation = it }
|
||||||
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 locationRequest = LocationRequest.Builder(Priority.PRIORITY_HIGH_ACCURACY, 15000)
|
val locationRequest = LocationRequest.Builder(Priority.PRIORITY_HIGH_ACCURACY, 15000)
|
||||||
.setMinUpdateDistanceMeters(500f)
|
.setMinUpdateDistanceMeters(500f)
|
||||||
@@ -139,24 +122,10 @@ 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)
|
||||||
if (selectedTab == 0) {
|
scope.launch { updateAddress(location) }
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val fusedLocationClient = LocationServices.getFusedLocationProviderClient(context)
|
|
||||||
try {
|
try {
|
||||||
if (ContextCompat.checkSelfPermission(context, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
|
if (ContextCompat.checkSelfPermission(context, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
|
||||||
fusedLocationClient.requestLocationUpdates(locationRequest, locationCallback, android.os.Looper.getMainLooper())
|
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(
|
Scaffold(
|
||||||
modifier = Modifier.fillMaxSize(),
|
modifier = Modifier.fillMaxSize(),
|
||||||
topBar = {
|
topBar = {
|
||||||
@@ -187,6 +186,7 @@ fun GasStationApp(repository: GasStationRepository) {
|
|||||||
withContext(Dispatchers.IO) {
|
withContext(Dispatchers.IO) {
|
||||||
try {
|
try {
|
||||||
val geocoder = Geocoder(context, Locale.getDefault())
|
val geocoder = Geocoder(context, Locale.getDefault())
|
||||||
|
@Suppress("DEPRECATION")
|
||||||
val results = geocoder.getFromLocationName(destinationQuery, 1)
|
val results = geocoder.getFromLocationName(destinationQuery, 1)
|
||||||
if (!results.isNullOrEmpty()) {
|
if (!results.isNullOrEmpty()) {
|
||||||
destinationLocation = android.location.Location("").apply {
|
destinationLocation = android.location.Location("").apply {
|
||||||
@@ -257,8 +257,8 @@ fun GasStationApp(repository: GasStationRepository) {
|
|||||||
scope.launch {
|
scope.launch {
|
||||||
stations = repository.getCheapestAndNearest(
|
stations = repository.getCheapestAndNearest(
|
||||||
userLocation = loc, forceRefresh = true,
|
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
|
isRefreshing = false
|
||||||
}
|
}
|
||||||
@@ -269,7 +269,7 @@ fun GasStationApp(repository: GasStationRepository) {
|
|||||||
if (isLoading && stations.isEmpty()) {
|
if (isLoading && stations.isEmpty()) {
|
||||||
CircularProgressIndicator(modifier = Modifier.align(Alignment.Center))
|
CircularProgressIndicator(modifier = Modifier.align(Alignment.Center))
|
||||||
} else if (stations.isEmpty()) {
|
} 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 {
|
} else {
|
||||||
GasStationList(stations = stations, onLogClick = { showFuelDialog = it })
|
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)
|
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) {
|
FilledTonalIconButton(
|
||||||
Icon(Icons.Default.Add, null, modifier = Modifier.size(16.dp))
|
onClick = onLogClick,
|
||||||
Spacer(modifier = Modifier.width(4.dp))
|
modifier = Modifier.size(40.dp)
|
||||||
Text("Registrar")
|
) {
|
||||||
|
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 gasStations: List<GasStationInfo> = emptyList()
|
||||||
private var currentAddress: String? = null
|
private var currentAddress: String? = null
|
||||||
private var isLoading = true
|
private var isLoading = true
|
||||||
private var isReserveFilterActive = false
|
|
||||||
private val repository = GasStationRepository()
|
private val repository = GasStationRepository()
|
||||||
|
|
||||||
init {
|
init {
|
||||||
@@ -54,17 +53,13 @@ class MainScreen(carContext: CarContext) : Screen(carContext) {
|
|||||||
LocationStorage.getLastLocation(carContext)?.let { lastLoc ->
|
LocationStorage.getLastLocation(carContext)?.let { lastLoc ->
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
updateAddress(lastLoc)
|
updateAddress(lastLoc)
|
||||||
// Primero recta
|
|
||||||
gasStations = repository.getCheapestAndNearest(
|
gasStations = repository.getCheapestAndNearest(
|
||||||
userLocation = lastLoc,
|
userLocation = lastLoc,
|
||||||
maxDistanceKm = if (isReserveFilterActive) 40.0 else null,
|
|
||||||
getRealDistances = false
|
getRealDistances = false
|
||||||
)
|
)
|
||||||
invalidate()
|
invalidate()
|
||||||
// Luego real
|
|
||||||
gasStations = repository.getCheapestAndNearest(
|
gasStations = repository.getCheapestAndNearest(
|
||||||
userLocation = lastLoc,
|
userLocation = lastLoc,
|
||||||
maxDistanceKm = if (isReserveFilterActive) 40.0 else null,
|
|
||||||
getRealDistances = true
|
getRealDistances = true
|
||||||
)
|
)
|
||||||
invalidate()
|
invalidate()
|
||||||
@@ -85,12 +80,15 @@ class MainScreen(carContext: CarContext) : Screen(carContext) {
|
|||||||
gasStations = repository.getCheapestAndNearest(
|
gasStations = repository.getCheapestAndNearest(
|
||||||
userLocation = location,
|
userLocation = location,
|
||||||
forceRefresh = forceRefresh,
|
forceRefresh = forceRefresh,
|
||||||
maxDistanceKm = if (isReserveFilterActive) 40.0 else null,
|
|
||||||
getRealDistances = true
|
getRealDistances = true
|
||||||
)
|
)
|
||||||
isLoading = false
|
isLoading = false
|
||||||
invalidate()
|
invalidate()
|
||||||
}
|
}
|
||||||
|
if (forceRefresh) {
|
||||||
|
fusedLocationClient.removeLocationUpdates(this)
|
||||||
|
fetchLocationAndGasStations(false)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -100,13 +98,12 @@ class MainScreen(carContext: CarContext) : Screen(carContext) {
|
|||||||
override fun onGetTemplate(): Template {
|
override fun onGetTemplate(): Template {
|
||||||
val listBuilder = ItemList.Builder()
|
val listBuilder = ItemList.Builder()
|
||||||
|
|
||||||
val reserveAction = Action.Builder()
|
val refreshAction = Action.Builder()
|
||||||
.setTitle(if (isReserveFilterActive) "Todo" else "Reserva")
|
.setTitle("Refrescar")
|
||||||
.setOnClickListener {
|
.setOnClickListener {
|
||||||
isReserveFilterActive = !isReserveFilterActive
|
|
||||||
isLoading = true
|
isLoading = true
|
||||||
invalidate()
|
invalidate()
|
||||||
fetchLocationAndGasStations(forceRefresh = false)
|
fetchLocationAndGasStations(forceRefresh = true)
|
||||||
}
|
}
|
||||||
.build()
|
.build()
|
||||||
|
|
||||||
@@ -119,7 +116,7 @@ class MainScreen(carContext: CarContext) : Screen(carContext) {
|
|||||||
|
|
||||||
if (gasStations.isEmpty()) {
|
if (gasStations.isEmpty()) {
|
||||||
return MessageTemplate.Builder("Sin resultados")
|
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 {
|
.addAction(Action.Builder().setTitle("Reintentar").setOnClickListener {
|
||||||
isLoading = true
|
isLoading = true
|
||||||
invalidate()
|
invalidate()
|
||||||
@@ -180,7 +177,7 @@ class MainScreen(carContext: CarContext) : Screen(carContext) {
|
|||||||
.setHeader(
|
.setHeader(
|
||||||
Header.Builder()
|
Header.Builder()
|
||||||
.setTitle(currentAddress ?: "Gasolineras Diésel")
|
.setTitle(currentAddress ?: "Gasolineras Diésel")
|
||||||
.addEndHeaderAction(reserveAction)
|
.addEndHeaderAction(refreshAction)
|
||||||
.build()
|
.build()
|
||||||
)
|
)
|
||||||
.build()
|
.build()
|
||||||
|
|||||||
@@ -25,7 +25,6 @@ class GasStationRepository {
|
|||||||
suspend fun getCheapestAndNearest(
|
suspend fun getCheapestAndNearest(
|
||||||
userLocation: Location,
|
userLocation: Location,
|
||||||
forceRefresh: Boolean = false,
|
forceRefresh: Boolean = false,
|
||||||
maxDistanceKm: Double? = null,
|
|
||||||
destinationLocation: Location? = null,
|
destinationLocation: Location? = null,
|
||||||
getRealDistances: Boolean = false
|
getRealDistances: Boolean = false
|
||||||
): List<GasStationInfo> {
|
): List<GasStationInfo> {
|
||||||
@@ -75,7 +74,7 @@ class GasStationRepository {
|
|||||||
} ?: emptyList()
|
} ?: emptyList()
|
||||||
|
|
||||||
// Radio máximo: 30km si es local, 400km si hay destino (ruta larga)
|
// 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 }
|
val filtered = allStations.filter { (it.distance / 1000.0) <= finalMaxDist }
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user