Contenido del curso
Google Maps SDK
Servicios de Localización
- 7

Kotlin Flows para medir tiempo en Android
12:54 min - 8

Simulación de ubicación GPS en emulador y dispositivo Android
05:48 min - 9

Modelos de localización propios con Clean Architecture
08:36 min - 10

Callbacks de Android convertidos en Flows
Viendo ahora - 11

Inyección de dependencias para observar localización en Android
06:33 min - 12

LocationTracker con StateFlow para rastreo en Android
08:46 min - 13

State Flows para controlar localización y tiempo en Kotlin
10:00 min - 14

Configuración y pruebas de Location Tracker en Android
09:37 min
Integración Maps con Localización
Manejo de permisos
Integración cámara
- 23

Cómo guardar fotos en Android con PhotoHandler
11:59 min - 24

Conversión de Bitmaps a Byte Arrays con Extension Functions
05:58 min - 25

CameraViewModel con Hilt y StateFlow
08:40 min - 26

Configuración de métodos del ViewModel para gestión de cámara
09:40 min - 27

Integración de CameraX con Jetpack Compose en Android
14:23 min - 28

Creación de pantalla de previsualización de fotos con Jetpack Compose
08:44 min - 29

Galería de fotos en marcadores del mapa
11:55 min
Servicios en Android
Transmisiones en Android (Broadcast)
Callbacks de Android convertidos en Flows
Resumen
Obtener la ubicación de un usuario en Android no es solo pedirle al GPS que entregue coordenadas. Implica mapear modelos, validar permisos, vigilar el hardware y traducir callbacks del sistema operativo a flows de corrutinas. Aquí verás cómo construir esa capa de datos con una arquitectura limpia, lista para sobrevivir cambios futuros.
Por qué necesitas un mapper entre Location de Android y tu modelo de dominio
Android trae su propia clase Location cargada de propiedades que tu aplicación probablemente no necesita. Por eso el primer paso es crear una carpeta data con un LocationMapper que traduzca esa clase nativa a la tuya, definida en domain [00:14].
La solución es una extension function sobre android.location.Location llamada toLocation(), que devuelve tu modelo propio asignando latitud y longitud:
kotlin fun android.location.Location.toLocation(): Location { return Location( lat = latitude, long = longitude ) }
¿Para qué sirve un mapper en arquitectura limpia? Aísla tu dominio de las clases del framework. Si mañana migras de plataforma, solo cambias el mapper, no toda tu lógica de negocio.
Cómo definir un contrato con la interfaz LocationObserver
Dentro de domain/location agregas una interfaz llamada LocationObserver con una función observeLocation() que devuelva un Flow de tu modelo Location [01:05]. Esa interfaz es un contrato: cualquier clase que la implemente promete entregar ubicaciones siguiendo tu propio formato.
La ventaja es que tu capa de datos queda desacoplada. Si necesitas cambiar de proveedor de localización, escribes una nueva implementación y listo. El resto de la aplicación no se entera.
kotlin interface LocationObserver { fun observeLocation(interval: Long): Flow<Location> }
Revisa que importes tu propio Location y los Flow de corrutinas, no los de Java o Android.
Cómo implementar AndroidLocationObserver con callbackFlow
En la carpeta data creas la clase AndroidLocationObserver que implementa la interfaz [01:55]. Por inyección de dependencias recibes el Context de Android y construyes el cliente de Google Play Services:
kotlin class AndroidLocationObserver @Inject constructor( private val context: Context ) : LocationObserver { private val client = LocationServices.getFusedLocationProviderClient(context) }
Para devolver un Flow desde un sistema basado en callbacks, usas callbackFlow, un tipo de flow especial diseñado para traducir esos callbacks tradicionales de Android al mundo de las corrutinas [02:35].
Qué tres tareas debe resolver el observer
Dentro de callbackFlow cubres tres frentes:
- Verificar que el GPS y la red estén activos en el dispositivo.
- Validar que el usuario haya otorgado los permisos correctos.
- Mapear el
Locationde Android al modelo del dominio.
Cómo verificar si el GPS y la red están activos
Obtienes el LocationManager desde el context y declaras dos banderas, isGpsEnabled e isNetworkEnabled, ambas en false. Luego entras a un loop que las actualiza en cada iteración consultando locationManager.isProviderEnabled para GPS_PROVIDER y NETWORK_PROVIDER [03:20].
Si ninguno está habilitado, el flow espera con un delay de tres segundos antes de volver a verificar. Tres segundos es razonable: el usuario no va a activar permisos en milisegundos, así que no tiene sentido saturar el bucle.
Cómo validar permisos de localización en Android
Android distingue entre dos permisos clave para ubicación:
ACCESS_FINE_LOCATIONpara localización exacta (GPS).ACCESS_COARSE_LOCATIONpara localización aproximada (red).
Usas ActivityCompat.checkSelfPermission(context, ...) y comparas el resultado con PackageManager.PERMISSION_GRANTED. Si alguno falla, simplemente no emites valores, evitando una SecurityException del sistema operativo [05:00].
¿Por qué necesito declarar permisos en el manifiesto si ya los pido en código? Android exige que los permisos estén listados en el
AndroidManifest.xmlantes de compilar. El chequeo dinámico solo confirma si el usuario los aceptó en tiempo de ejecución.
En el manifiesto, antes de la etiqueta <application>, agregas:
xml <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" /> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
En cuanto los declaras, el error de compilación que pedía permisos requeridos desaparece.
Cómo emitir ubicaciones con trySend y LocationCallback
Con los permisos validados, pides la última ubicación al cliente con lastLocation.addOnSuccessListener. Si el resultado no es nulo, usas el operador let y llamas a trySend(location.toLocation()). Esa función trySend es la pieza clave que convierte un callback en una emisión del flow [06:30].
Pero lastLocation solo entrega un valor inicial. Para recibir actualizaciones continuas necesitas un LocationRequest y un LocationCallback:
kotlin val request = LocationRequest.Builder(interval) .setPriority(Priority.PRIORITY_HIGH_ACCURACY) .build()
val locationCallback = object : LocationCallback() { override fun onLocationResult(result: LocationResult) { result.locations.lastOrNull()?.let { location -> trySend(location.toLocation()) } } }
client.requestLocationUpdates(request, locationCallback, Looper.getMainLooper())
La prioridad PRIORITY_HIGH_ACCURACY indica al sistema que quieres precisión máxima, y el interval define cada cuánto pedir actualizaciones [07:50].
Por qué cerrar recursos con awaitClose
Cuando el consumidor del flow deja de observar, necesitas detener las actualizaciones para no consumir batería en vano. Lo haces con awaitClose, donde llamas a client.removeLocationUpdates(locationCallback) [09:10]. Sin este cierre, el cliente seguiría emitiendo aunque nadie escuche.
Usar sensores del sistema operativo es complejo: pedir permisos, vigilar hardware y traducir callbacks a flows requiere disciplina. ¿Ya tienes tu AndroidLocationObserver corriendo? Cuéntame en los comentarios si te apareció alguna SecurityException y cómo la resolviste.