Fix marge conflicts

This commit is contained in:
Angel Osvaldo Frias Arriaga
2026-05-23 08:16:59 -06:00
14 changed files with 307 additions and 13 deletions

View File

@@ -55,4 +55,10 @@ dependencies {
androidTestImplementation(libs.androidx.junit)
debugImplementation(libs.androidx.compose.ui.test.manifest)
debugImplementation(libs.androidx.compose.ui.tooling)
implementation("com.google.android.gms:play-services-location:21.0.1")
implementation("com.squareup.retrofit2:retrofit:2.9.0")
implementation("com.squareup.retrofit2:converter-gson:2.9.0")
implementation("com.google.android.gms:play-services-maps:18.1.0")
}

View File

@@ -2,6 +2,13 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_LOCATION"/>
<application
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
@@ -11,6 +18,10 @@
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.CamionBasura">
<meta-data
android:name="com.google.geo.API_KEY"
android:value="AIzaSyAg5e7AeqVs4kWEsHgdYr6mGjJiHvZc0hg"/>
<activity
android:name=".MainActivity"
android:exported="true"
@@ -22,6 +33,10 @@
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
<category android:name="android.intent.category.LAUNCHER" />
</application>
<service
android:name=".service.CamionMonitorService"
android:foregroundServiceType="location" />
</manifest>

View File

@@ -1,29 +1,76 @@
package com.tuapp.camionbasura
import android.content.Intent
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.tuapp.camionbasura.model.ColoniaHorario
import com.tuapp.camionbasura.network.CamionRepository
import com.tuapp.camionbasura.service.CamionMonitorService
import com.tuapp.camionbasura.ui.theme.CamionBasuraTheme
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
class MainActivity : ComponentActivity() {
private val repository = CamionRepository()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
// Iniciar servicio de monitoreo
val intent = Intent(this, CamionMonitorService::class.java)
startService(intent)
setContent {
CamionBasuraTheme {
Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding ->
Greeting(
name = "Android",
modifier = Modifier.padding(innerPadding)
var horarios by remember { mutableStateOf<List<ColoniaHorario>>(emptyList()) }
LaunchedEffect(Unit) {
CoroutineScope(Dispatchers.IO).launch {
horarios = repository.obtenerHorarios()
}
}
MaterialTheme {
Column(modifier = Modifier.padding(16.dp)) {
Text(
text = "🚛 Camión de Basura - Celaya",
style = MaterialTheme.typography.headlineSmall
)
Spacer(modifier = Modifier.height(16.dp))
Text(
text = "Horarios por colonia:",
style = MaterialTheme.typography.titleMedium
)
Spacer(modifier = Modifier.height(8.dp))
LazyColumn {
items(horarios) { horario ->
Card(
modifier = Modifier
.fillMaxWidth()
.padding(vertical = 4.dp)
) {
Column(modifier = Modifier.padding(12.dp)) {
Text(
text = horario.colonia,
style = MaterialTheme.typography.titleSmall
)
Text(text = "Ruta: ${horario.routeId}")
Text(text = "Horario: ${horario.horarioEstimado}")
}
}
}
}
}
}
}
@@ -44,4 +91,5 @@ fun GreetingPreview() {
CamionBasuraTheme {
Greeting("Android")
}
}

View File

@@ -0,0 +1,12 @@
package com.tuapp.camionbasura.model
data class PushPayload(
val title: String,
val body: String
)
data class Notificacion(
val triggerEvent: String,
val condition: String,
val pushPayload: PushPayload
)

View File

@@ -0,0 +1,7 @@
package com.tuapp.camionbasura.model
data class ColoniaHorario(
val colonia:String,
val routeId:String,
val horarioEstimado:String
)

View File

@@ -0,0 +1,16 @@
package com.tuapp.camionbasura.model
data class Posicion(
val posicion: Int,
val lat: Double,
val lng: Double,
val speed: Int,
val timestamp: String
)
data class Ruta(
val routeId: String,
val name: String,
val status: String,
val positions: List<Posicion>
)

View File

@@ -0,0 +1,45 @@
package com.tuapp.camionbasura.network
import com.tuapp.camionbasura.model.ColoniaHorario
import com.tuapp.camionbasura.model.Notificacion
import com.google.gson.Gson
import com.google.gson.reflect.TypeToken
import java.net.URL
import com.tuapp.camionbasura.model.Ruta
class CamionRepository {
private val urlNotificaciones = "https://td.celaya.biz:8018/sistemasJSON/notificaciones.json"
suspend fun obtenerNotificaciones(): List<Notificacion> {
return try {
val json = URL(urlNotificaciones).readText()
val tipo = object : TypeToken<List<Notificacion>>() {}.type
Gson().fromJson(json, tipo)
} catch (e: Exception) {
emptyList()
}
}
private val urlHorarios = "https://td.celaya.biz:8018/sistemasJSON/colonias-rutas.json"
suspend fun obtenerHorarios(): List<ColoniaHorario> {
return try {
val json = URL(urlHorarios).readText()
val tipo = object : TypeToken<List<ColoniaHorario>>() {}.type
Gson().fromJson(json, tipo)
} catch (e: Exception) {
emptyList()
}
}
private val urlRutas = "https://td.celaya.biz:8018/sistemasJSON/rutas.json"
suspend fun obtenerRutas(): List<Ruta> {
return try {
val json = URL(urlRutas).readText()
val tipo = object : TypeToken<List<Ruta>>() {}.type
Gson().fromJson(json, tipo)
} catch (e: Exception) {
emptyList()
}
}
}

View File

@@ -0,0 +1,74 @@
package com.tuapp.camionbasura.service
import android.app.NotificationChannel
import android.app.NotificationManager
import android.app.Service
import android.content.Context
import android.content.Intent
import android.os.IBinder
import androidx.core.app.NotificationCompat
import com.tuapp.camionbasura.network.CamionRepository
import kotlinx.coroutines.*
class CamionMonitorService : Service() {
private val scope = CoroutineScope(Dispatchers.IO + SupervisorJob())
private val repository = CamionRepository()
private val CHANNEL_ID = "camion_channel"
override fun onCreate() {
super.onCreate()
crearCanalNotificacion()
iniciarMonitoreo()
}
private fun iniciarMonitoreo() {
scope.launch {
while (isActive) {
verificarNotificaciones()
delay(30_000L)
}
}
}
private suspend fun verificarNotificaciones() {
val notificaciones = repository.obtenerNotificaciones()
notificaciones.forEach { notificacion ->
if (notificacion.triggerEvent == "TRUCK_PROXIMITY") {
enviarNotificacion(
notificacion.pushPayload.title,
notificacion.pushPayload.body
)
return
}
}
}
private fun crearCanalNotificacion() {
val channel = NotificationChannel(
CHANNEL_ID,
"Camión de Basura",
NotificationManager.IMPORTANCE_HIGH
)
val manager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
manager.createNotificationChannel(channel)
}
private fun enviarNotificacion(titulo: String, mensaje: String) {
val manager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
val notificacion = NotificationCompat.Builder(this, CHANNEL_ID)
.setContentTitle(titulo)
.setContentText(mensaje)
.setSmallIcon(android.R.drawable.ic_dialog_info)
.setPriority(NotificationCompat.PRIORITY_HIGH)
.build()
manager.notify(1, notificacion)
}
override fun onBind(intent: Intent?): IBinder? = null
override fun onDestroy() {
super.onDestroy()
scope.cancel()
}
}

View File

@@ -0,0 +1,5 @@
package com.tuapp.camionbasura.util
object DistanciaUtil {
const val DISTANCIA_ALERTA_METROS = 100f
}