La API de localización de Android

video[Tutorial] La API de localización de Android
 

Ejercicio: El API de localización de Android.

En este ejercicio crearemos una aplicación que es capaz de leer información de localización del dispositivo y actualizarla cada vez que se produce un cambio.

1. Crea un nuevo proyecto con los siguientes datos:

Phone and Tablet / Empty Activity
Name: Localizacion
Package name: org.example.localizacion
Language: Java o Kotlin
Minimum API level: API 21 Android 5.0 (Lollipop)

2. Por razones de privacidad acceder a la información de localización está en principio prohibido a las aplicaciones. Si estas desean hacer uso de este servicio han de solicitar el permiso adecuado. En concreto hay que solicitar ACCESS_FINE_LOCATION para acceder a cualquier tipo de sistema de localización o ACCESS_COARSE_LOCATION para acceder al sistema de localización basado en redes. Añade la siguiente línea en el fichero AndroidManifest.xml dentro de la etiqueta <manifest>:

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

Por lo tanto en este ejemplo  vamos autilizar, tanto la localización fina, que nos proporciona el GPS, como una menos precisa,que nos proporcionada  las torres de telefonía celular y las redes WiFi.

3. Sustituye el fichero res/layout/activity_main.xml por:

<ScrollView
   xmlns:android="http://schemas.android.com/apk/res/android"
   android:layout_width="match_parent"
   android:layout_height="match_parent">
     <TextView
        android:id="@+id/salida"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />
</ScrollView> 

En este ejemplo nos limitaremos a mostrar en modo de texto la información obtenida desde el API de localización. Para ello usaremos un TextView  dentro de un ScrollView, tal y como se muestra en la siguiente pantalla:

4. Abre la clase MainActivity y copia el siguiente código:

public class MainActivity extends AppCompatActivity 
                          implements LocationListener { 
   static final long TIEMPO_MIN = 10 * 1000 ; // 10 segundos
   static final long DISTANCIA_MIN = 5; // 5 metros
   static final String[] A = { "n/d", "preciso", "impreciso" };
   static final String[] P = { "n/d", "bajo", "medio","alto" };
   static final String[] E = { "fuera de servicio",
                     "temporalmente no disponible ", "disponible" };
   LocationManager manejador;
   String proveedor;
   TextView salida;

   @Override public void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      setContentView(R.layout.activity_main);
      salida = findViewById(R.id.salida);
      manejador = (LocationManager) getSystemService(LOCATION_SERVICE); 
      log("Proveedores de localización: \n ");
      muestraProveedores();

      Criteria criterio = new Criteria();
      criterio.setCostAllowed(false); 
      criterio.setAltitudeRequired(false);
      criterio.setAccuracy(Criteria.ACCURACY_FINE); 
      proveedor = manejador.getBestProvider(criterio, true);
      log("Mejor proveedor: " + proveedor + "\n");
      log("Comenzamos con la última localización conocida:");
      Location localizacion = manejador.getLastKnownLocation(proveedor); 
      muestraLocaliz(localizacion);
   } 
class MainActivity : AppCompatActivity(), LocationListener {

   val TIEMPO_MIN = (10 * 1000).toLong() // 10 segundos
   val DISTANCIA_MIN = 5.0F // 5 metros
   val A = arrayOf("n/d", "preciso", "impreciso")
   val P = arrayOf("n/d", "bajo", "medio", "alto")
   val E = arrayOf("fuera de servicio", "temporalmente no disponible", 
            "disponible")
   lateinit var manejador: LocationManager
   lateinit var proveedor: String

   public override fun onCreate(savedInstanceState: Bundle?) {
      super.onCreate(savedInstanceState)
      setContentView(R.layout.activity_main)
      manejador = getSystemService(Context.LOCATION_SERVICE) as 
                                                          LocationManager
      log("Proveedores de localización: \n ")
      muestraProveedores()
      val criterio = Criteria().apply {
         isCostAllowed = false
         isAltitudeRequired = false
         accuracy = Criteria.ACCURACY_FINE
      }
      proveedor = manejador.getBestProvider(criterio, true)
      log("Mejor proveedor: $proveedor\n")
      log("Comenzamos con la última localización conocida:")
      muestraLocaliz(manejador.getLastKnownLocation(proveedor))
   } 

La primera línea que nos interesa es la llamada a getSystemService(LOCATION_SERVICE) que crea el objeto manejador de tipo LocationManager. La siguiente línea hace una llamada al metodo log(), que se definirá más adelante. Simplemente muestra por el TextView ,salida, el texto indicado. La siguiente   llamada a muestraProveedores() también es un método definido por nosotros, que listará todos los proveedores de localización disponibles.

En las tres siguientes líneas vamos a seleccionar uno de estos proveedores de localización. Comenzamos creando un objeto de la clase Criteria, donde se podrá indicar las características que ha de tener el proveedor buscado. En este ejemplo indicamosque no ha de tener coste económico, ha de poder obtener la altura y ha de tener precisión fina. Para consultar otras restricciones, consultar documentación de la clase Criteria[1]. Con estas restricciones parece que estamos interesados en el proveedor basado en GPS, aunque de no estar disponible, se seleccionará otro que cumpla el mayor número de restricciones. Para seleccionar el proveedor usaremos el método getBestProvider(). En este método hay que indicar el criterio de selección y un valor booleano, donde indicamos si solo nos interesa los sistemas que el usuario tenga actualmente habilitados. Nos devolverá un Stringcon el nombre del proveedor seleccionado.

Algunos proveedores, como el GPS, puedentardar un cierto tiempo en darnos una primera posición. No obstante, Android recuerda la última posición que fue devuelta por este proveedor. Es lo que nos devuelve la llamada a getLastKnownLocation(). El método muestraLocaliz() será definido más tarde y muestra en pantalla una determinada localización.

5. A continuación copia el resto del código:

// Métodos del ciclo de vida de la actividad
@Override protected void onResume() {
   super.onResume();
   manejador.requestLocationUpdates(proveedor, TIEMPO_MIN, DISTANCIA_MIN, 
                           this);
}

@Override protected void onPause() {
   super.onPause();
   manejador.removeUpdates(this);
}

// Métodos de la interfaz LocationListener
@Override public void onLocationChanged(Location location) {
   log("Nueva localización: ");
   muestraLocaliz(location);
}

@Override public void onProviderDisabled(String proveedor) {
   log("Proveedor deshabilitado: " + proveedor + "\n");
}

@Override public void onProviderEnabled(String proveedor) {
   log("Proveedor habilitado: " + proveedor + "\n");
}

@Override public void onStatusChanged(String proveedor, int estado, 
                                                         Bundle extras) {
   log("Cambia estado proveedor: " + proveedor + ", estado="
            + E[Math.max(0, estado)] + ", extras=" + extras + "\n");
}

// Métodos para mostrar información
private void log(String cadena) {
   salida.append(cadena + "\n");
}

private void muestraLocaliz(Location localizacion) {
   if (localizacion == null)
      log("Localización desconocida\n");
   else
      log(localizacion.toString() + "\n");
}

private void muestraProveedores() {
   log("Proveedores de localización: \n ");
   List<String> proveedores = manejador.getAllProviders();
   for (String proveedor : proveedores) {
      muestraProveedor(proveedor);
   }
}

private void muestraProveedor(String proveedor) {
   LocationProvider info = manejador.getProvider(proveedor);
   log("LocationProvider[ " + "getName=" + info.getName()
         + ", isProviderEnabled="
         + manejador.isProviderEnabled(proveedor) + ", getAccuracy="
         + A[Math.max(0, info.getAccuracy())] + ", getPowerRequirement="
         + P[Math.max(0, info.getPowerRequirement())]
         + ", hasMonetaryCost=" + info.hasMonetaryCost()
         + ", requiresCell=" + info.requiresCell()
         + ", requiresNetwork=" + info.requiresNetwork()
         + ", requiresSatellite=" + info.requiresSatellite()
         + ", supportsAltitude=" + info.supportsAltitude()
         + ", supportsBearing=" + info.supportsBearing()
         + ", supportsSpeed=" + info.supportsSpeed() + " ]\n");
}
} 
   override fun onPause() {
      super.onPause()
      manejador.removeUpdates(this)
   }

   // Métodos de la interfaz LocationListener
   override fun onLocationChanged(location: Location) {
      log("Nueva localización: ")
      muestraLocaliz(location)
   }

   override fun onProviderDisabled(proveedor: String) {
      log("Proveedor deshabilitado: $proveedor\n")
   }

   override fun onProviderEnabled(proveedor: String) {
      log("Proveedor habilitado: $proveedor\n")
   }

   override fun onStatusChanged(proveedor: String, estado: Int,
                                                       extras: Bundle) {
      log("Cambia estado proveedor: $proveedor, estado="+
                     " ${E[Math.max(0, estado)]}, extras= $extras\n")
   }

   // Métodos para mostrar información
   private fun log(cadena: String) = salida.append(cadena + "\n")

   private fun muestraLocaliz(localizacion: Location?) {
      if (localizacion == null)
         log("Localización desconocida\n")
      else
         log(localizacion!!.toString() + "\n")
   }

   private fun muestraProveedores() {
      log("Proveedores de localización: \n ")
      val proveedores = manejador.getAllProviders()
      for (proveedor in proveedores) {
         muestraProveedor(proveedor)
      }
   }

   private fun muestraProveedor(proveedor: String) {
      with(   manejador.getProvider(proveedor)) {
         log("LocationProvider[ " + "getName= $name, isProviderEnabled" + 
             "=${manejador.isProviderEnabled(proveedor)}, " +
             "getAccuracy=${A[Math.max(0, accuracy)]}, " +
             "getPowerRequirement=${P[Math.max(0, powerRequirement)]}, " +
             "hasMonetaryCost=${hasMonetaryCost()}, " +
             "requiresCell=${requiresCell()}, " +
             "requiresNetwork=${requiresNetwork()}, " +
             "requiresSatellite=${requiresSatellite()}, " +
             "supportsAltitude=${supportsAltitude()}, " +
             "supportsBearing=${supportsBearing()}, " +
             "supportsSpeed=${supportsSpeed()} ]\n")
      }
   }
} 

Para conseguir que se notifiquen cambios de posición hay que llamar al método requestLocationUpdates() y para indicar que se dejen de hacer las notificaciones hay que llamar a removeUpdates(). Dado que queremos ahorrar batería nos interesa que se reporten notificaciones solo cuando la aplicación esté activa. Por lo tanto tenemos que reescribir los métodos onResume() y onPause().

El método requestLocationUpdates() dispone de 4 parámetros: el nombre del proveedor, el tiempo entre actualizaciones en ms (se recomienda valores mayores de 60.000 ms), la distancia mínima (de manera que si es menor, no se notifica) y un escuchador de evenos que implemente el interface LocationListener.

Como nuestra actividad es un LocationListener tenemos que implementar los siguientes métodos:onLocationChanged() se activará cada vez que se obtenga una nueva posición. Los otros tres métodos pueden ser usados para cambiar de proveedor en caso de que se active uno mejor o deje de funcionar el actual. Sería buena idea llamar de nuevo aquí al método getBestProvider().

El resto del código resulta fácil de interpretar.

6. Observa cómo aparecen algunos errores. El sistema nos advierte de que estamos actuando de forma no correcta:

7. Ignora esta advertencia y ejecuta el proyecto.

8. Si ejecutas el proyecto en un dispositivo con una versión 6.0 o superior, podrás verificar que se produce el error. Para evitar que se produzca, en el dispositivo accede a Ajustes / Aplicaciones  y Notificaciones / Localización / Permisos: y activa el permiso de Ubicación:

Vuelve a ejecutar la aplicación y verificar que ya no se produce el error.

NOTA: Para aligerar el código del ejercicio no hemos incluido el código necesario para solicitar permiso a partir de la versión 6.0. Si vas a distribuir la aplicación, resulta impresindible realizar los pasos descritos en la sección Permisos en Android 6 Marshmallow.

9. Verifica el funcionamiento del programa, si es posible con un dispositivo real con el GPS activado. Si utilizas un emulador, selecciona en la barra de botones … / Location. Desde aquí podrás emular la posición del dispositivo.

10. En onCreate(), podéis modificar el código para forzar que se use el proveedor de red. Para ello prueba el siguiente código.

   //proveedor = manejador.getBestProvider(criterio, true);
proveedor = manejador.getProvider(LocationManager.NETWORK_PROVIDER)
                                                               .getName();

Preguntas de repaso: API Localización en Android