Una de ubicación

Vamos a hacer una app sencillita y rápida que nos permita conocer un poco el funcionamiento de la ubicación en Android. Va a detectar nuestra ubicación, ya sea por medio del GPS o triangulando a través de nuestra wifi/conexión de datos y en la pantalla mostrará un enlace funcional a google maps con nuestras coordenadas, lo que nos permitirá comprobar al momento si la ubicación es correcta o no.

Nuestro proyecto se llamará PrimeraConGPS, en el paquete com.alberovalley.primeracongps, para la versión 2.2 de Android. Notaréis que no voy tan despacio y tan detallado como antes, pero es 1) porque lo que me “salto” es algo que ya hemos hecho con anterioridad (y tan rutinario como, en este caso, crear un proyecto nuevo) y 2) porque así resulta menos tostón de leer, pudiendo dedicarse uno a lo “nuevo” del artículo.

Con nuestro proyecto listo vamos a irnos al Manifest. Primero, para decirle a la app que se instale dónde quepa, no que si no puede instalarse en la memoria interna del móvil de error por falta de espacio. Para ello, dentro de la etiqueta <manifest>   incluiremos el siguiente texto: android:installLocation=”auto”. Así, la app intentará primero instalarse en la memoria interna. Si no hubiera espacio, automáticamente se dirigiría a instalarse en la tarjeta externa, sin que el usuario tenga conocimiento de ello. A mi, personalmente, me molesta mucho ir a instalarme una app descargada y que me salga “no tienes espacio libre para instalar” en vez de intentar buscar otras opciones, por lo que suelo poner esto en mis apps. Los otros valores posibles para este atributo son internalOnly y preferExternal. El primero es el valor por defecto. El segundo indica que sólo debe instalarse en la memoria externa, olvidándose de la interna tenga esta espacio o no. Este segundo valor da problemas a la hora de probar las apps en el AVD a menos que al crearlo se le configurase un espacio en disco como “memoria externa”. Hay que recordar que si desmontamos la SD, todas las apps en ejecución que estuvieran instaladas en la misma se cerrarían (matarían) automáticamente.

Además, ya que estamos trasteando el Manifest, iremos a la sección Permissions y añadiremos dos uses-permissions, los dos permisos de uso: de acceso a la ubicación: la tosca (que obtiene una ubicación aproximada a través de la red de datos, las antenas móviles y las señales wifi) y la fina, mas precisa (obtenida del GPS). Los nombres de los permisos son android.permission.ACCESS_COARSE_LOCATION y  android.permission.ACCESS_FINE_LOCATION. Al acabar, el Manifest debe quedar como sigue

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.alberovalley.primeracongps"
android:versionCode="1"
android:versionName="1.0"
android:installLocation="auto"
>
<uses-sdk android:minSdkVersion="8" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"></uses-permission>
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"></uses-permission>

<application android:icon="@drawable/icon" android:label="@string/app_name"  >
<activity android:name=".PrimeraConGPSActivity"
android:label="@string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>

</application>
</manifest>

Una vez listo el Manifest, nos vamos ahora con el layout. El main.xml está casi listo para lo que queremos hacer, sólo tenemos que añadirle al TextView un par de atributos, como son el id (necesario para manejar el textview desde las clases) y la que le indicará que los enlaces que muestre han de renderizarse como vínculos web. Ese atributo es android:autoLink=”web”. El layout debe quedar tal como sigue:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<TextView
android:id="@+id/enlace"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:autoLink="web"
/>
</LinearLayout>

Listo esto nos vamos a nuestra Actividad, la única clase que vamos a necesitar. Tenemos que darle a la clase un atributo privado para el TextView; una constante String para el inicio del enlace, que será “https://maps.google.com/maps?z=15&q=&#8221; y hacer que nuestra actividad implemente la interfaz con la que se gestiona la ubicación: LocationListener. No es el primer Listener que vemos, ya hemos trabajado con el onClickListener, que hace que nuestra actividad esté “a la escucha” del evento click. En este caso, la actividad estará “a la escucha” de eventos relacionados con la ubicación.
Antes de seguir, he de explicar que la ubicación se obtiene de un “provider”, un proveedor de ubicación. Dicho proveedor, cómo he comentado antes, puede ser el propio sensor GPS o la red de datos, una antena de cobertura móvil o una wifi. Sea lo que sea, será nuestro provider y se le hace mucha referencia a continuación:

Nada mas implementada la interfaz, como es habitual, la clase saldrá marcada en rojo. Hay métodos a implementar, así que le diremos que nos cree las cabeceras igual que hacemos con el onClickListener. Los métodos son:
public void onLocationChanged(Location location) {} //cuándo el provider indique que hemos cambiado de ubicación se ejecuta el código de este método
public void onStatusChanged(String provider, int status, Bundle extras) {} //se le llama cuándo cambia el estado de uno de los providers, ya sea que estaba inaccesible y ahora vuelve a estar accesible, o que no puede obtener ubicación alguna. Útil para mostrar mensajes para que el usuario sepa por qué no podemos ubicarle.

public void onProviderEnabled(String provider) {}// se le llama cuándo el usuario habilita un proveedor (la red de datos, la wifi o el GPS)

public void onProviderDisabled(String provider) {}// se le llama cuándo el usuario inhabilita un proveedor (la red de datos, la wifi o el GPS)

Los tres últimos podríamos dejarlos sin código, si bien emplearemos unos Toast para mostrar mensajes emergentes que nos permitan visualizar los diferentes eventos cuándo nosotros mismos vayamos activando y desactivando GPS, wifi, etc. Nos centraremos en instanciar lo que necesitemos en el onCreate, inicializarlo en el onResume y emplear el onLocationChange para actualizar el enlace.

Empezaremos con las instancias: Necesitaremos un objeto LocationManager accesible en toda la actividad (un atributo a nivel de clase). Para instanciarlo usaremos el método getSystemService con el parámetro Context.LOCATION_SERVICE. Con eso dispondremos del servicio de ubicación de nuestro dispositivo. A continuación eligiremos el mejor provider posible de entre los que haya. Se trata de una cadena de texto que deberá estar accesible en toda la Actividad y se obtiene eligiendo el mejor provider del LoctionManager así: provider = locationManager.getBestProvider(criteria, false);
con un objeto de criterios (Criteria criteria = new Criteria();) y diciéndole que elija el mejor tanto de los habilitados como de los inhabilitados (true indicaría que sólo queremos que elija de entre los habilitados). Para conocer la ubicación, usamos un objeto Location que obtendremos del LocationManager con el método getLastKnownLocation(provider). Aquí ya podríamos obtener la latitud y longitud con location.getLatitude() y location.getLongitude() (ambos devuelven un primitivo double) de nuestra ubicación, pero eso lo encapsularemos en un método privado nuestro para poder usarlo en varios eventos de la Actividad. En el mismo método será en el que, una vez obtenidas las coordenadas, las uniremos a la cadena de la consulta para googlemaps y lo mostraremos en el TextView.

En el onResume le indicaremos al locationManager que nos de actualizaciones de ubicación periódicas con locationManager.requestLocationUpdates(provider, intervalo_tiempo_en_milisegundos, intervalo_espacio_metros, location_listener); Los parámetros que recibe son String, long, float, LocationListener (usaremos this ya que la actividad implementa la interfaz).

En el método onPause le indicamos que debe dejar de esperar actualizaciones de ubicación con locationManager.removeUpdates(this);
Los Toast que usaremos para indicar en qué método del Listener estamos serán como este:

Toast.makeText(this, “Nombre_método “, Toast.LENGTH_SHORT).show();

Nuestro método para crear el enlace en base a las coordenadas será el siguiente


private void obtenerLocalizacionYcrearEnlace() {
if (location != null) {
System.out.println("Provider " + provider + " has been selected.");
double lat =  (location.getLatitude());
double lng =  (location.getLongitude());
enlace.setText(gMapsLink + lat + "," + lng);
} else {
enlace.setText("Provider no disponible");
}
}

Nuestra actividad quedará al final tal que así:

package com.alberovalley.primeracongps;

import android.app.Activity;
import android.content.Context;
import android.location.Criteria;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.os.Bundle;
import android.text.method.LinkMovementMethod;
import android.widget.TextView;
import android.widget.Toast;

public class PrimeraConGPSActivity extends Activity implements LocationListener {
private TextView enlace;
private LocationManager locationManager;
private String provider;
private static final String gMapsLink = "https://maps.google.com/maps?z=15&q=";
private Location location;
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
enlace = (TextView)findViewById(R.id.enlace);

// obtener el location manager
locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
// Definir los criterios para elegir el location provider -> usamos
// por defecto
Criteria criteria = new Criteria();
provider = locationManager.getBestProvider(criteria, false);
location = locationManager.getLastKnownLocation(provider);

// Inicializamos los valores de latitud y longitud
obtenerLocalizacionYcrearEnlace();
}

private void obtenerLocalizacionYcrearEnlace() {
if (location != null) {
System.out.println("Provider " + provider + " has been selected.");
double lat =  (location.getLatitude());
double lng =  (location.getLongitude());
enlace.setText(gMapsLink + lat + "," + lng);
} else {
enlace.setText("Provider no disponible");
}
}

/* Solicita las actualizaciones en el arranque */
@Override
protected void onResume() {
super.onResume();
locationManager.requestLocationUpdates(provider, 400, 1, this);
}

/* Elimina las actualizaciones del locationlistener cuando la Actividad está pausada*/
@Override
protected void onPause() {
super.onPause();
locationManager.removeUpdates(this);
}

@Override//si se cambia de ubicación
public void onLocationChanged(Location location) {
obtenerLocalizacionYcrearEnlace();
}

@Override//si se cambia de estado
public void onStatusChanged(String provider, int status, Bundle extras) {
Toast.makeText(this, "Cambio de status: " ,
Toast.LENGTH_SHORT).show();

}

@Override//si se habilita un provider
public void onProviderEnabled(String provider) {
Toast.makeText(this, "Habilitado nuevo provider " + provider,
Toast.LENGTH_SHORT).show();

}

@Override//si el provider no está habilitado
public void onProviderDisabled(String provider) {
Toast.makeText(this, "Deshabilitado el provider: " + provider,
Toast.LENGTH_SHORT).show();
}
}

Y al ejecutarlo, veremos un enlace con las coordenadas de cada uno. Esto es mejor probarlo con el móvil (o tablet, según de lo que dispongamos) ya que si bien se supone que podemos darle datos falsos de ubicación a la app por diversos medios, yo mismo he tenido algunos problemas para lograr que el emulador reconociera dichos datos, tanto por Telnet como por DDMS, mientras que con el móvil todo ha ido como un tiro.

Al pulsar sobre el enlace que hemos generado, podremos abrirlo con el navegador o, si está instalada, con la app de googlemaps. Comprobad qué tal os funciona, probadla en la calle a ver si actualiza con los cambios de ubicación, etc.

El código completo de esta app puede encontrarse en el repositorio correspondiente de github

Anuncios

3 comentarios to “Una de ubicación”

  1. Excelente tutorial… felicitaciones..!!! ahora te pregunto si yo no quisiera que se visualizara gmapslink, solo la latitud y la longitud pero que aun tuviera la oportunidad de poder abrir el google maps como haria??

  2. Lo primero que se me ocurre sería formar el enlace en html (con la etiqueta ). En el href pones el enlace que describo, pero la etiqueta envuelve sólo los valores de latitud-longitud. Para que funcione, debes usar para el textview lo que devuelve Html.fromHtml(“” + latitud + ” – ” + longitud + ” );
    (Lo digo a ojo y sin probar, pero la solución anda por ahí 😉

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión / Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión / Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión / Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión / Cambiar )

Conectando a %s

A %d blogueros les gusta esto: