Posiblemente este sea uno de los artículos más esperados y es que la primera parte sobre Google Maps a reventado en Youtube y es de los últimos artículos con más visitas. Es por ello que vamos a seguir indagando y en esta ocasión prepararemos el terreno para añadir la localización en tiempo real.

Lo primero que haremos será recuperar el proyecto que hicimos en la primera parte, si todavía no lo has visitado puedes encontrarlo pulsando aquí.

Localización en tiempo real

Una vez abierto el proyecto tendremos que añadir un nuevo permiso, ya que obviamente si queremos saber la localización en tiempo real tendremos que tener acceso a dicha localización a través del permiso. Iremos al AndroidManifest.xml y añadiremos el permiso de ACCESS_FINE_LOCATION.

<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>

Una vez añadido crearemos una función que usaremos a lo largo de nuestra app para comprobar si el permiso ha sido aceptado o no.

 private fun isPermissionsGranted() = ContextCompat.checkSelfPermission(
        this,
        Manifest.permission.ACCESS_FINE_LOCATION
    ) == PackageManager.PERMISSION_GRANTED

A través de este método que acabamos de crear sabremos si los permisos están activados o no, así que ahora toca una nueva función que primero compruebe si el mapa ha sido inicializado, si no es así saldrá de la función gracias a la palabra return, si por el contrario map ya ha sido inicializada, es decir que el mapa ya ha cargado, pues comprobaremos los permisos.

private fun enableMyLocation() {
        if (!::map.isInitialized) return
        if (isPermissionsGranted()) {
            map.isMyLocationEnabled = true
        } else {
            requestLocationPermission()
        }
    }

Si los permisos están aceptados, llamaremos a la función map.isMyLocationEnabled = true, que nos activará la ubicación a tiempo real, pero si por el contrario no están aceptados llamaremos a requestLocationPermission(), que será la encargada de solicitar los permisos.

Obviamente te dará error porque todavía no la hemos creado, así que simplemente crearemos la función requestLocationPermission().

 companion object {
        const val REQUEST_CODE_LOCATION = 0
    }    

private fun requestLocationPermission() {
        if (ActivityCompat.shouldShowRequestPermissionRationale(this,
                Manifest.permission.ACCESS_FINE_LOCATION)) {
            Toast.makeText(this, "Ve a ajustes y acepta los permisos", Toast.LENGTH_SHORT).show()
        } else {
            ActivityCompat.requestPermissions(this,
                arrayOf(Manifest.permission.ACCESS_FINE_LOCATION),
                LOCATION_REQUEST_CODE)
        }
    }

Aunque este método ya lo deberías conocer por el capítulo de permisos en Android, haré una pequeña explicación.

Lo primero que creamos será un companion object con una variable constante dentro que será el código de respuesta para saber si al aceptarse permisos ha sido el nuestro.

Si entra por el if de significa que ya había rechazado los permisos antes y por ello le mostramos un toast avisándole de que vaya a los ajustes de la app y modifique los permisos. Si por el contrario entra por el else significará que nunca le hemos pedido los permisos y lo haremos a través de la función ActivityCompat.requestPermissions, pasándole la activity (this), el permiso o los permisos que queremos que acepte (en este caso uno) y el código de localización que creamos en el companion object.

El siguiente pasó será crear el método que nos avise si al pedirle los permisos los ha aceptado. Para ello tendremos que sobreescribir el método onRequestPermissionsResult().

    override fun onRequestPermissionsResult(
        requestCode: Int,
        permissions: Array<out String>,
        grantResults: IntArray
    ) {
        when(requestCode){
            REQUEST_CODE_LOCATION -> if(grantResults.isNotEmpty() && grantResults[0]==PackageManager.PERMISSION_GRANTED){
                map.isMyLocationEnabled = true
            }else{
                Toast.makeText(this, "Para activar la localización ve a ajustes y acepta los permisos", Toast.LENGTH_SHORT).show()
            }
            else -> {}
        }
    }

El método que acabamos de ver se llamará automáticamente cuando el usuario acepte o rechace los permisos, si los acepta volver a llamar a map.isMyLocationEnabled = true, si por el contrario los rechaza le mostraremos un toast avisando que si quiere la ubicación a tiempo real vaya a los ajustes y acepte los permisos.

Te recuerdo que si quieres ver el tema de los permisos más a fondo visites este enlace.

Ya tenemos el flujo de los permisos terminado pero si te fijas la primera función que hicimos enableLocation() que será la que inicie todo el flujo de los permisos. ¿Dónde la vamos a llamar? Pues en la función onMapReady() ya que tendremos que utilizarlo cuando el mapa esté funcionando.

  override fun onMapReady(googleMap: GoogleMap) {
        map = googleMap
        createMarker()
        enableLocation()
    }

Ahora ejecutaremos la app y lo primero que pasará es que nos pedirá los permisos, al aceptarlo veremos que en nuestro mapa ahora se ve un botón extra en la parte superior derecha que al pulsarlo nos llevará a nuestra posición en tiempo real.

Ejemplo de localización en tiempo real.

Pero ¿Que pasaría si ahora salgo de la app, voy a ajustes y desactivo los permisos? Obviamente nuestra app los necesita y eso provocaría que la aplicación falle y es por eso que tenemos que añadir un último método.

    override fun onResumeFragments() {
        super.onResumeFragments()
        if (!::map.isInitialized) return
        if(!isLocationPermissionGranted()){
            map.isMyLocationEnabled = false
            Toast.makeText(this, "Para activar la localización ve a ajustes y acepta los permisos", Toast.LENGTH_SHORT).show()
        }
    }

Analicemos el código anterior. Lo primero que hacemos es comprobar si el mapa ha cargado, si no es así salimos del método. Luego volvemos a comprobar si los permisos siguen activos, si no siguen activos, desactivamos la localización en tiempo real y le mostramos un toast.

Implementando opciones extras

Con lo que tenemos hasta aquí ya podríamos decir que tenemos la localización en tiempo real completa, pero quiero hacer hincapié en un par de funciones extras.

Para empezar ¿Recuerdas el botón que apareció en el mapa? ¿El que te lleva a tu posición? pues podemos modificar su comportamiento. Para ello iremos a la parte superior de nuestra activity y añadiremos la implementación GoogleMap.OnMyLocationButtonClickListener.

class MainActivity : AppCompatActivity(), OnMapReadyCallback, GoogleMap.OnMyLocationButtonClickListener {

Al añadir eso nos saldrá un error en el nombre de nuestra clase, porque hemos implementado una función pero no la hemos añadido en el código así que añadimos el siguiente método.

    override fun onMyLocationButtonClick(): Boolean {
        Toast.makeText(this, "Boton pulsado", Toast.LENGTH_SHORT).show()
        return false
    }

Esta función se llamará automáticamente al pulsar el botón de centrarnos, yo he añadido un toast para mostrar cada vez que se pulse. Además fíjate que hay que devolver un Boolean, si devolvemos false el botón nos centrará en nuestra ubicación, si por el contrario devolvemos true al pulsar el botón no nos llevará a nuestra localización.

Para terminar tenemos que decir que nuestra clase implementa esa función es decir, que al pulsar el botón la función que se llame sea la nuestra, para ello añadimos en el método onMapReady la siguiente línea.

   override fun onMapReady(googleMap: GoogleMap) {
        map = googleMap
        createMarker()
        map.setOnMyLocationButtonClickListener(this)
        enableLocation()
    }

Si ejecutas verás que al pulsar el botón saldrá un Toast.

Por último vamos a implementar una función mas, que se ejecutará al pulsar sobre nuestra ubicación (el punto azul).

El proceso será casi igual al anterior. Lo primero será ir a la parte superior de la activity e implementar GoogleMap.OnMyLocationClickListener.

Esto junto a lo que añadimos anteriormente dejaría la activity así.

class MainActivity : AppCompatActivity(), OnMapReadyCallback, GoogleMap.OnMyLocationButtonClickListener, GoogleMap.OnMyLocationClickListener {

También se volverá a marcar nuestra activity con un error puesto que tenemos que añadir una nueva función.

 override fun onMyLocationClick(p0: Location) {
        Toast.makeText(this, "Estás en ${p0.latitude}, ${p0.longitude}", Toast.LENGTH_SHORT).show()
    }

Esta vez no devolvemos nada, pero recibimos un parámetro de localización al pulsar en el usuario que contendrá la latitud y longitud. En este ejemplo lanzo un Toast con dichas coordenadas.

Y para terminar volvemos al onMapReady() y añadimos la implementación.

   override fun onMapReady(googleMap: GoogleMap) {
        map = googleMap
        createMarker()
        map.setOnMyLocationButtonClickListener(this)
        map.setOnMyLocationClickListener(this)
        enableLocation()
    }

También te recuerdo que si prefieres verlo de una forma más interactiva tienes el capítulo en vídeo en Youtube.

Continúa con el curso: Capítulo 31 – Admob en Kotlin (Monetizando apps)


Te recuerdo que puedes seguirme en mis redes sociales en Aristi.Dev. Y si tienes dudas con este o cualquier otro artículo del blog únete al Discord de la comunidad y te ayudaremos.