/**
- @file ServicioEscuharBeacons.java
- @brief Este archivo contiene la implementación del servicio para escuchar beacons.
*/
/**
-
@brief Servicio para escuchar beacons BLE (Bluetooth Low Energy).
*/
public class ServicioEscuharBeacons extends Service {private double distanciaPromedio = -1.0;
private int muestras = 0;
private final int NUM_MUESTRAS_PROMEDIO = 10;
public static final String ACTION_SOLICITAR_PERMISOS_BLUETOOTH = “SOLICITAR_PERMISOS_BLUETOOTH”;
public static final String ACTION_SOLICITAR_PERMISOS_UBICACION = “SOLICITAR_PERMISOS_UBICACION”;
public static final int CODIGO_PETICION_PERMISO_BLUETOOTH = 1;
public static final int CODIGO_PETICION_PERMISO_UBICACION = 2;private Handler handler = new Handler();
private PowerManager.WakeLock mWakeLock;
private Runnable obtenerUbicacionRunnable;
private static final double UMBRAL_CAMBIO = 2;
private ControladorBLE controladorBLE;
private String dispositivoBuscado;
private int ID_AIRSENSE = 1;private static final long LOCATION_UPDATE_INTERVAL = 10000; // 10 seconds interval
private int ID_CO2 = 2;
NotificationManager mNotificationManager;
private static int notificationId = 1;private FusedLocationProviderClient fusedLocationClient;
@Override
public void onCreate() {
super.onCreate();
controladorBLE = new ControladorBLE(this);
fusedLocationClient = LocationServices.getFusedLocationProviderClient(this);obtenerUbicacionRunnable = new Runnable() { @Override public void run() { obtenerLatitudLongitud(); handler.postDelayed(this , LOCATION_UPDATE_INTERVAL); } }; handler.postDelayed(obtenerUbicacionRunnable , LOCATION_UPDATE_INTERVAL); buscarEsteDispositivoBTLE(); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { NotificationChannel channel1 = new NotificationChannel("1" , "Conexion AirSense" , NotificationManager.IMPORTANCE_HIGH); channel1.enableVibration(true); channel1.setShowBadge(true); NotificationChannel channel2 = new NotificationChannel("2" , "Alertas CO2" , NotificationManager.IMPORTANCE_HIGH); channel2.enableVibration(true); channel2.setShowBadge(true); NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); notificationManager.createNotificationChannel(channel1); notificationManager.createNotificationChannel(channel2); } // Configurar notificación persistente setupPersistentNotification("Has superado los limites oficiales" , "Nivel alto de CO2" , "2" , ID_CO2);
}
private void setupPersistentNotification(String titulo , String text , String canal , int id) {
Notification notification = crearNotificacion(titulo , text , canal , id);
notification.visibility = Notification.VISIBILITY_PUBLIC;// Modificación aquí startForeground(id , notification);
}
private void mostrarNotificacion(Notification notification , int id) {
NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.notify(id , notification);}
private void buscarEsteDispositivoBTLE() {
controladorBLE.buscarEsteDispositivoBTLE(dispositivoBuscado , new BLECallback() {
@Override
public void onSuccess(String valor_medicion , String id_medicion , String nombre , double Rssi , double txpower) {
double distancia = Double.parseDouble(String.valueOf(calcularDistancia(txpower , Rssi , 2)));
double distanciaPromedio = calcularPromedio(distancia);if (distanciaPromedio < 0 || Math.abs(distancia - distanciaPromedio) < UMBRAL_CAMBIO) { Intent intent = new Intent("airsense-conectado"); intent.putExtra("distancia" , String.format("%.2f" , distanciaPromedio)); intent.putExtra("valor_medicion" , valor_medicion); intent.putExtra("id_medicion" , id_medicion); LocalBroadcastManager.getInstance(getApplicationContext()).sendBroadcast(intent); } } });
}
public void obtenerLatitudLongitud() {
if (ActivityCompat.checkSelfPermission(this , android.Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED &&
ActivityCompat.checkSelfPermission(this , Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
return;
}fusedLocationClient.getCurrentLocation(LocationRequest.PRIORITY_HIGH_ACCURACY , null) .addOnSuccessListener(TaskExecutors.MAIN_THREAD , new OnSuccessListener<Location>() { @Override public void onSuccess(Location location) { if (location != null) { double latitud = location.getLatitude(); double longitud = location.getLongitude(); Intent latitud1 = new Intent("LATITUD"); latitud1.putExtra("latitud" , latitud); latitud1.putExtra("longitud" , longitud); LocalBroadcastManager.getInstance(getApplicationContext()).sendBroadcast(latitud1); } else { Log.e("MiServicio" , "Ubicación nula"); Toast.makeText(getApplicationContext() , "Ubicación nula" , Toast.LENGTH_SHORT).show(); } } }) .addOnFailureListener(new OnFailureListener() { @Override public void onFailure(@NonNull Exception e) { // Manejar fallo al obtener la ubicación Toast.makeText(getApplicationContext() , "Error al obtener la ubicación" , Toast.LENGTH_SHORT).show(); e.printStackTrace(); } });
}
@SuppressLint(“InvalidWakeLockTag”)
@Override
public int onStartCommand(Intent intent , int flags , int startId) {
if (intent != null) {
String action = intent.getAction();
if (“dispositivoBuscado”.equals(action)) {
if (intent.getStringExtra(“dispositivoBuscado”) != null) {
String dispositivoBuscado = intent.getStringExtra(“dispositivoBuscado”);
this.dispositivoBuscado = dispositivoBuscado;
}// Adquirir WakeLock PowerManager mgr = (PowerManager) getSystemService(Context.POWER_SERVICE); if (this.mWakeLock == null) { this.mWakeLock = mgr.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK , "MyWakeLock"); } if (!this.mWakeLock.isHeld()) { this.mWakeLock.acquire(); } obtenerLatitudLongitud(); buscarEsteDispositivoBTLE(); controladorBLE.detenerBusquedaDispositivosBTLE(); } } return Service.START_STICKY;
}
@Override
public void onDestroy() {
handler.removeCallbacks(obtenerUbicacionRunnable);
controladorBLE.detenerBusquedaDispositivosBTLE();NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); notificationManager.cancel(1); // Liberar WakeLock if (this.mWakeLock != null) { if (this.mWakeLock.isHeld()) { this.mWakeLock.release(); } this.mWakeLock = null; } super.onDestroy();
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}/**
- @param titulo Título de la notificación.
- @return La notificación creada.
- @brief Crea una notificación para el servicio.
*/
private Notification crearNotificacion(String titulo , String text , String canal , int id) {
NotificationCompat.Builder builder = new NotificationCompat.Builder(this , canal)
.setSmallIcon(R.drawable.logo)
.setContentTitle(titulo)
.setContentText(text)
.setPriority(NotificationCompat.PRIORITY_MAX)
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
.setColor(Color.rgb(49 , 143 , 169))
.setOngoing(false);Intent notificationIntent = new Intent(this , Home.class); PendingIntent pendingIntent; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { pendingIntent = PendingIntent.getService(this , id , notificationIntent , PendingIntent.FLAG_IMMUTABLE); } else { pendingIntent = PendingIntent.getService(this , id , notificationIntent , PendingIntent.FLAG_IMMUTABLE); } builder.setContentIntent(pendingIntent); return builder.build();
}
/**
- @brief Actualiza la notificación con un nuevo mensaje.
*/
/private void actualizarNotificacion(String titulo, int id) {
Notification notification = crearNotificacion(titulo, id);
NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.notify(id , notification);
}/
/**
- @param txPower Potencia de transmisión conocida.
- @param rssi Intensidad de la señal recibida.
- @param n Factor de corrección ambiental.
- @return La distancia calculada en metros.
- @brief Calcula la distancia entre dos dispositivos usando la potencia de la señal y la intensidad de la señal recibida (RSSI).
*/
private double calcularDistancia(double txPower , double rssi , double n) {
if (rssi == 0) {
return -1.0; // Valor inválido
}
return Math.pow(10 , ((txPower – rssi) / (10 * n)));
}
/**
- @param nuevaMuestra El nuevo valor de la muestra.
- @return El promedio móvil calculado.
- @brief Calcula el promedio móvil de las últimas muestras
*/
private double calcularPromedio(double nuevaMuestra) {
if (muestras < NUM_MUESTRAS_PROMEDIO) {
// Aún no se han acumulado suficientes muestras, simplemente suma las muestras anteriores y la nueva
distanciaPromedio = (distanciaPromedio * muestras + nuevaMuestra) / (muestras + 1);
muestras++;
} else {
// Si se han acumulado suficientes muestras, calcula el promedio móvil eliminando la muestra más antigua
distanciaPromedio = distanciaPromedio + (nuevaMuestra – distanciaPromedio) / NUM_MUESTRAS_PROMEDIO;
}
return distanciaPromedio;
}
}