En este post te mostraré cómo obtener la última ubicación del usuario usando el lenguaje de programación Kotlin en Android.
Te enseñaré a conocer la última ubicación conocida y a escuchar los cambios de ubicación para saber cuando la posición del usuario cambia.
De este modo podrías vigilar la ubicación del usuario conforme se mueve, y ya después de eso puedes hacer cualquier cosa con la ubicación, por ejemplo enviarla a un servidor, colocarla en un mapa, etcétera.
Primero hay que agregar una librería. Vamos a build.gradle
y agregamos la última versión de los Google Play Services. Aquí vas a encontrar la última versión, en mi caso lo encontré así y entonces lo que agregamos al archivo es:
implementation 'com.google.android.gms:play-services-location:18.0.0'
Elegimos sync now y esperamos a que el Gradle se sincronice, descargue librerías, etcétera.
Mi gradle quedó así al final, solo como ejemplo:
plugins {
id 'com.android.application'
id 'kotlin-android'
}
android {
compileSdk 31
defaultConfig {
applicationId "me.parzibyte.a"
minSdk 21
targetSdk 31
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = '1.8'
}
buildFeatures {
viewBinding true
}
}
dependencies {
implementation 'androidx.core:core-ktx:1.3.2'
implementation 'androidx.appcompat:appcompat:1.2.0'
implementation 'com.google.android.material:material:1.3.0'
implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.3.1'
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.3.1'
implementation 'androidx.navigation:navigation-fragment-ktx:2.3.5'
implementation 'androidx.navigation:navigation-ui-ktx:2.3.5'
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
testImplementation 'junit:junit:4.+'
androidTestImplementation 'androidx.test.ext:junit:1.1.2'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
implementation 'com.google.android.gms:play-services-location:18.0.0'
}
Lo que importa es la línea 52.
Nota: recuerda que esta versión puede cambiar, y que siempre debes usar la última versión. Te dejé un link anteriormente para ir a al página oficial y conocer la versión actualizada.
A partir de Android 6, debes obtener permisos en tiempo de ejecución además de indicarlo en el AndroidManifest. Yo acabo de hacer un post de cómo obtener los permisos de ubicación, te invito a leerlo.
Lo que vamos a hacer es crear una variable de tipo FusedLocationProviderClient
. Esta variable nos va a permitir acceder a dos cosas:
Es importante acceder a la última ubicación conocida (con lastLocation) en el init de nuestra app, pues debemos tener algo para empezar, ya que no sabemos cuándo va a cambiar la ubicación del usuario, por ello es que al inicio usaremos la última conocida.
fusedLocationClient.lastLocation.addOnSuccessListener {
if (it != null) {
imprimirUbicacion(it)
} else {
Log.d(LOG_TAG, "No se pudo obtener la ubicación")
}
}
También vamos a necesitar un LocationCallback
pues vamos a suscribirnos a los cambios de ubicación, y cada que la ubicación cambie va a invocar a ese callback con la nueva ubicación del usuario.
Por otro lado, necesitamos un LocationRequest
para solicitar notificaciones del cambio de ubicación. En esta solicitud de ubicación indicamos cada cuánto queremos recibirlas, la prioridad y otras cosas que puedes personalizar.
val locationRequest = LocationRequest.create().apply {
interval = 10000
fastestInterval = 5000
priority = LocationRequest.PRIORITY_HIGH_ACCURACY
}
locationCallback = object : LocationCallback() {
override fun onLocationResult(locationResult: LocationResult?) {
locationResult ?: return
Log.d(LOG_TAG, "Se recibió una actualización")
for (location in locationResult.locations) {
imprimirUbicacion(location)
}
}
}
fusedLocationClient.requestLocationUpdates(
locationRequest,
locationCallback,
Looper.getMainLooper()
)
Estamos solicitando las actualizaciones de ubicación en la línea 15, pero antes definimos el callback en la línea 7. En esa función vamos a recibir un arreglo de Location,
y lo que yo hago es imprimirla.
Aquí la función que imprime:
fun imprimirUbicacion(ubicacion: Location) {
Log.d(LOG_TAG, "Latitud es ${ubicacion.latitude} y la longitud es ${ubicacion.longitude}")
}
Nota: yo solo accedo a latitude
y longitude
pero puedes acceder a otras propiedades como la precisión o el proveedor.
Nota 2: es importante encerrar el acceso a la ubicación en un try catch esperando una excepción de tipo SecurityException
ya que puede que el usuario no haya dado permiso.
Ahora te mostraré el código completo de mi fragmento. Yo estoy solicitando los permisos de ubicación y cuando los obtengo es cuando invoco a la última ubicación, la imprimo y luego me suscribo a las actualizaciones de ubicación.
Si te sirve de algo, te lo dejo aquí. Recuerda tomarlo como guía, y no copies y pegues sin entender el código.
package me.parzibyte.a
import android.Manifest
import android.os.Bundle
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.content.pm.PackageManager
import android.location.Location
import android.os.Build
import android.os.Looper
import android.util.Log
import androidx.core.content.ContextCompat
import com.google.android.gms.location.*
class EnviarUbicacionService : Fragment() {
private lateinit var fusedLocationClient: FusedLocationProviderClient
/////
private lateinit var locationCallback: LocationCallback
private val CODIGO_PERMISOS_UBICACION_SEGUNDO_PLANO = 2106
private val LOG_TAG = "EnviarUbicacion"
private var haConcedidoPermisos = false
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
verificarPermisos()
}
fun imprimirUbicacion(ubicacion: Location) {
Log.d(LOG_TAG, "Latitud es ${ubicacion.latitude} y la longitud es ${ubicacion.longitude}")
}
fun onPermisosConcedidos() {
// Hasta aquí sabemos que los permisos ya están concedidos
fusedLocationClient = LocationServices.getFusedLocationProviderClient(activity)
try {
fusedLocationClient.lastLocation.addOnSuccessListener {
if (it != null) {
imprimirUbicacion(it)
} else {
Log.d(LOG_TAG, "No se pudo obtener la ubicación")
}
}
//////
val locationRequest = LocationRequest.create().apply {
interval = 10000
fastestInterval = 5000
priority = LocationRequest.PRIORITY_HIGH_ACCURACY
}
locationCallback = object : LocationCallback() {
override fun onLocationResult(locationResult: LocationResult?) {
locationResult ?: return
Log.d(LOG_TAG, "Se recibió una actualización")
for (location in locationResult.locations) {
imprimirUbicacion(location)
}
}
}
fusedLocationClient.requestLocationUpdates(
locationRequest,
locationCallback,
Looper.getMainLooper()
)
} catch (e: SecurityException) {
Log.d(LOG_TAG, "Tal vez no solicitaste permiso antes")
}
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_enviar_ubicacion_service, container, false)
}
override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<out String>,
grantResults: IntArray
) {
if (requestCode == CODIGO_PERMISOS_UBICACION_SEGUNDO_PLANO) {
val todosLosPermisosConcedidos =
grantResults.all { it == PackageManager.PERMISSION_GRANTED }
if (grantResults.isNotEmpty() && todosLosPermisosConcedidos) {
haConcedidoPermisos = true;
onPermisosConcedidos()
Log.d(LOG_TAG, "El usuario concedió todos los permisos")
} else {
Log.d(LOG_TAG, "Uno o más permisos fueron denegados")
}
}
}
private fun verificarPermisos() {
val permisos = arrayListOf(
Manifest.permission.ACCESS_COARSE_LOCATION,
Manifest.permission.ACCESS_FINE_LOCATION,
)
// Segundo plano para Android Q
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
permisos.add(Manifest.permission.ACCESS_BACKGROUND_LOCATION)
}
val permisosComoArray = permisos.toTypedArray()
if (tienePermisos(permisosComoArray)) {
haConcedidoPermisos = true
onPermisosConcedidos()
Log.d(LOG_TAG, "Los permisos ya fueron concedidos")
} else {
solicitarPermisos(permisosComoArray)
}
}
private fun solicitarPermisos(permisos: Array<String>) {
Log.d(LOG_TAG, "Solicitando permisos...")
requestPermissions(
permisos,
CODIGO_PERMISOS_UBICACION_SEGUNDO_PLANO
)
}
private fun tienePermisos(permisos: Array<String>): Boolean {
return permisos.all {
return ContextCompat.checkSelfPermission(
requireActivity(),
it
) == PackageManager.PERMISSION_GRANTED
}
}
}
Ya te enseñé cómo convertir una aplicación web de Vue 3 en una PWA. Al…
En este artículo voy a documentar la arquitectura que yo utilizo al trabajar con WebAssembly…
En un artículo anterior te enseñé a crear un PWA. Al final, cualquier aplicación que…
Al usar Comlink para trabajar con los workers usando JavaScript me han aparecido algunos errores…
En este artículo te voy a enseñar cómo usar un "top level await" esperando a…
Ayer estaba editando unos archivos que son servidos con el servidor Apache y al visitarlos…
Esta web usa cookies.