Home MundoTec Software Código fuente Tutorial / pdf Minijuegos
Cerrar

Tutorial Desarrollo de Aplicaciones en Android

Tutorial DESARROLLO DE APLICACIONES ANDROID.







4.2.6 Control de la señal GPS

ContactMap utiliza la señal GPS para conocer la ubicación actual del usuario, pudiendo así tanto dibujarla en el mapa mostrado como comunicársela a los demás usuarios a través de su actualización en el servidor.

Conocer la propia ubicación

Para poder acceder y controlar a la señal GPS se utilizan las clases presente en el paquete android.location . En general, este paquete incluye clases que permiten manejar diferentes dispositivos de localización. Algunos de los elementos presentes en este paquete son:

- Location : clase que representa una localización geográfica.
- LocationManager : clase para gestionar el dispositivo de localización.
- Geocoder : clase que permite traducir entre direcciones físicas y coordenadas en latitud y longitud, y viceversa.
- LocationListener : interfaz que permite implementar una clase que captura los eventos asociados al dispositivo de localización.

Las clases básicas para obtener la señal de GPS son LocationManager para controlar el dispositivo, LocationListener para escuchar sus cambios y Location para guardar la información de localización. En la clase ContactMap está declarada una variable global para controlar el dispositivo, llamada mLocation , y se define un subclase llamada MyLocationListener que atiende los posibles eventos asociados al GPS.
En primer lugar, es necesario acceder al dispositivo de localización, el GPS en este caso. Para acceder a este o a cualquier otro servicio integrado en el dispositivo móvil, se utiliza el método getSystemService() de la clase Activity y sus clases derivadas, como es ContactMap . Para referenciar al GPS es necesario pasar la constante Context.LOCATION_SERVICE . De esta forma, en mLocation se tendrá un controlador válido para el dispositivo GPS.
Antes de poder obtener la posición actual a través de mLocation , Android precisa que se defina una clase que permita gestionar sus posibles eventos asociados al GPS, como que la posición cambie (exista un desplazamiento del usuario) o que el dispositivo de localización se active o desactive. Para ello, ha de definirse una clase que implemente la interfaz LocationListener . Esta clase se implementa dentro de ContactMap , y recibe el nombre de MyLocationListener . La varible global mListener es una instancia de esta clase. Así pues, una vez definida la interfaz se asocia dicha clase al controlador del GPS con el método requestLocationUpdates() de mLocation .. Además, es necesario otorgar a la aplicación permiso correspondiente para acceder al dispositivo de localización mediante la declaración en el fichero “AndroidManifest.xml” de la etiqueta correspondiente:

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

Código 27. Declaración en el manifiesto del permiso de acceso al GPS

En este punto ya es posible acceder a la localización del usuario usando el método getLastKnownLocation() del objeto mLocation , que es el que tiene el control del GPS. La localización vendrá dada en un objeto Location , y de ella se puede obtener, entre otras cosas, la latitud y longitud con los métodos getLatitude() y getLongitude() , respectivamente.
A continuación se muestra el código correspondiente. De nuevo, se declaran en el método onCreate() de la clase ContactMap , para poder realizarse nada más arrancar la aplicación. El fragmento de código mostrado se centra únicamente en lo explicado en líneas anteriores, omitiendo lo demás:

Aplicaciones Android
Código 28. Acceso al dispositivo GPS y a la posición actual

Capturar eventos del GPS

La implementación de la clase MyLocationListener , encargada de escuchar los eventos del GPS, es muy simple. Cada vez que se detecta que la posición del GPS cambia, se invoca el método onLocationChanged() de esta clase. La única acción que es necesario hacer cuando cambie la posición del usuario es actualizarla para poder mostrarla correctamente en el mapa e informar al servidor en la próxima conexión. Como se ha explicado en un apartado anterior (ver 4.2.2) ContactMap mantiene en todo momento una lista de los contactos que se pueden mostrar en el mapa llamada mFriendList , donde además se incluye al propio usuario. Es en esta lista donde se busca y se actualiza la nueva localización del usuario.
El Código 29 expone la implementación básica de la clase MyLocationListener . Únicamente se completa el método onLocationChanged() que es el necesario para actualizar la ubicación del usuario.

Aplicaciones Android
Código 29. Implementación de la clase MyLocationListener

Ventana de alerta al usuario

Pueden existir casos donde el dispositivo móvil no disponga de localizador GPS, o donde éste se desactive por el usuario o no pueda facilitar una señal. En ese caso, ContactMap no podrá mostrar la ubicación del usuario ni notificársela al servidor, por lo que no será conocida por el resto de usuarios.
Para conocer el estado del GPS, se utiliza el método isProviderEnabled() del controlador mLocation . Este simplemente devuelve un valor booleano donde indica si el GPS está activado o no. En caso de estar activado, se utiliza el GPS sin mayor problema. Si no, la aplicación ContactMap lanza un aviso al usuario.
El siguiente código muestra cómo se realizan estos dos casos en la aplicación.

Aplicaciones Android
Código 30. Comprobación del estado del GPS

La clase AlertDialog , del paquete android.app , permite lanzar ventanas de aviso al usuario asociando a esta texto, botones, o incluso vistas más completas. En el Código 30 se aprecia como se incluye un título con setTitle() , un mensaje con setMessage() y un botón sin acción asociado con setNeutralButton() .
Todos estos mensajes de texto no se incluyen de forma directa en el código, sino que se adjuntan como recurso externo. En concreto, han sido situados en la carpeta “\res\values”, dentro de un fichero que debe llevar por nombre “strings.xml”. De esta forma, Android facilita la inclusión de diferentes versiones de texto, por ejemplo, para cambiarlos de idioma. El texto declarado en “strings.xml” es el siguiente:

< ?xml version="1.0" encoding="iso-8859-1"?>
< resources>
(...)
< string name="noGPStitle">Sin localización
< string name="noGPSmessage">No hay dispositivo de localización
habilitado. No se mostrará la propia ubicación
< string name="neutralButton">Aceptar
(...)
< /resources>

Código 31. Declaración de recursos de texto

No debe olvidarse referencia el tipo de codificación “iso-8859-1” para poder incluir caracteres con tildes y la “ñ”. En caso contrario, Android dará un mensaje de error de difícil interpretación.

Establecer ubicación propia en el emulador

Si se está trabajando sobre el emulador de Android, como es el caso, no se dispondrá de información de localización a través de ningún dispositivo GPS. Cuando se realicen las llamadas correspondientes, la información que se devuelve es la que esté almacenada en el emulador.
Para fijar en el emulador unas coordenadas geográficas, deben realizarse los siguientes pasos:

1. Abrir una línea de comandos con Inicio >Ejecutar >cmd
2. Lanzar una conexión telner al emulador, mediante la instrucción:

> telnet localhost 5554

3. Introducir los datos de coordendas con la siguiente orden, sustituyendo LONGITUD y LATITUD por los valores deseados:

> geo fix LONGITUD LATITUD

4.2.7 Control de la señal Wi-Fi

ContactMap conecta con el servidor de forma periódica para actualizar la localización del usuario y para conocer las localizaciones de sus contactos. Esta comunicación se realiza a través de Internet con conexiones HTTP.
El paquete android.net.wifi provee a Android de diversas clases para gestionar el dispositivo Wi-Fi del aparato móvil. En concreto, se utilizará la clase WifiManager para controlar este elemento de conexión.
Al igual que con el GPS, al dispositivo de Wi-Fi se accede utilizando el método getSystemService() de la clase ContactMap , que da acceso a distintos servicios integrados en el dispositivo móvil. En esta ocasión, la llamada debe realizarse utilizando la constante Context.WIFI_SERVICE . De esta forma, se adquiere un objeto WifiManager válido con el que controlar la Wi-Fi.
Para poder utilizar sin problemas el dispositivo de Wi-Fi, Android exige que se autorice expresamente a la aplicación a usar dicho dispositivo. Esta autorización se manifiesta a través del permiso correspondiente en el fichero “AndroidManifest.xml”:

"android.permission.ACCESS_WIFI_STATE" />

Código 32. Declaración en el manifiesto del permiso de acceso al dispositivo Wi-Fi

La ausencia de conectividad a través de Wi-Fi provoca que ContactMap no pueda conectar con el servidor y, por tanto, tampoco actualizar ni su propia ubicación ni la de sus contactos. Sin embargo, en ese caso ContactMap muestra la última ubicación conocida de cada contacto gracias al uso de la base de datos SQLite, donde se van guardando las localizaciones conocidas. La utilización detallada de esta base de datos se expone en el apartado 4.2.12.
El siguiente código muestra el acceso a la Wi-Fi del dispositivo móvil, así como la comprobación de su estado.

Aplicaciones Android
Código 33. Acceso y comprobación del dispositivo de Wi-Fi

Para conocer el estado de la conexión Wi-Fi se utiliza el método isWifiEnabled() de la clase WifiManager , a la que pertenece el objeto instanciado wifi . Si está disponible, se lanza el servicio Update sin más (ver más adelante, apartado 4.2.18). Si no, se lanza una nueva ventana de aviso al usuario, y se actualiza la lista mFriendList desde la base de datos local SQLite en lugar desde el servidor remoto, llamando al método updateFriendsFromDataBase() .
Nótese como, de nuevo, se han utilizado en la ventana de alerta al usuario cadenas de texto declaradas en el archivo “strings.xml”, que se encuentra dentro de la carpeta de recursos “\res\values”.

Aplicaciones Android
Figura 26. Mensaje cuando no hay señal Wi-Fi disponible

El SDK 1.0 de Android permite conocer qué tipo de conexiones existen en el dispositivo móvil (Wi-Fi, GPRS, UMTS, Bluetooth, etc.), así como controlar su estado y el componente hardware correspondiente; sin embargo, esta versión del SDK no ofrece la posibilidad de elegir qué conexión se desea utilizar para el envío y la recepción de datos: por defecto, Android siempre intenta utilizar la conexión Wi-Fi, y en caso de no estar disponible utiliza las demás, consideradas de menor prioridad. Debido a este comportamiento tan peculiar, en la aplicación ContactMap se ha optado por realizar las conexiones con el servidor únicamente cuando la conexión Wi-Fi esté habilitada, descartando así realizar conexiones bajo otros protocolos que pueden llevar algún coste asociado para el usuario.
Además, en el caso de la conexión mediante Bluetooth, el SDK 1.0 limita sus funciones a la conexión de periféricos inalámbricos como auriculares, altavoces, manos libres, o similares. Es decir, actualmente el intercambio de datos mediante Bluetooth no está soportado por Android. Esta más que sorprendente decisión es argumentada por Google exponiendo problemas de falta de tiempo para depurar y certificar las API relacionadas con este estándar de comunicación, prometiendo además su pronta incorporación en siguientes versiones estables del SDK.

4.2.8 Conexiones HTTP

Los intercambios de información en formato XML entre la aplicación ContactMap y el servidor se realizan mediante el protocolo HTTP. En las peticiones se utiliza el método POST de dicho protocolo, encapsulando el documento XML correspondiente dentro de un campo de nombre “xml”. En las respuestas, el contenido XML viaja en el campo de datos.
Para estos flujos de información se utilizan las clases Update y HTTPConnection . En concreto, es esta última la encargada de las labores de más bajo nivel a la hora de establecer una conexión HTTP.
Android cuenta con numerosos paquetes y clases dedicados en exclusiva a los diferentes aspectos de las comunicaciones. Por ejemplo, de Java hereda, entre otros, javax.net o java.net . Dentro de sus paquetes exclusivos como plataforma, incluye una amplísima familia de paquetes bajo el nombre raíz android.net.* y sobre todo org.apache.http.* , de la que se hará uso en esta aplicación.
La clase HTTPConnection define tres variables globales de ámbito privado. Por un lado, esta mServer que indica la dirección a la que se deben dirigir las conexiones. Por otro, están mDataOut y mDataIn que representan, respectivamente, los datos que se envían y los datos recibidos. El único método relevante de esta clase es connect() , que utilizando los valores dados a estas variables establece una conexión por la que enviar una petición ( mDataIn ) y recibir su respuesta ( mDataOut ).
Antes de iniciar la conexión, se construye la petición HTTP. La clase BasicNameValuePairs permite crear pares del tipo variable-valor que se enviarán junto a una petición POST. Así pues, se define una única variable de nombre “xml” que contendrá como valor el existente en mDataOut , es decir, el documento XML con la petición a enviar al servidor. Este contenido ha de recibir el formato adecuado de codificación de caracteres para poder viajar dentro del protocolo HTTP. El formato correcto se le aplica con la clase UrlEncodedFormEntity .
Una vez se tiene preparados los datos de envío, se define la petición POST completa usando la clase HttpPost . En su constructor, se especifica con mServer la dirección completa del extremo destino, y con los método setHeader() y setEntity() se asocian las cabeceras requeridas y los datos para enviar.
A continuación se crea el cliente HTTP con la clase DefaultHtttpClient . Ésta cuenta con el método execute() , que precisa como parámetros la petición a enviar y se encarga de ejecutar la conexión. Para recibir la correspondiente respuesta, se define un objeto HttpResponse que recibe el resultado de execute() .
Para finalizar, la respuesta se procesa mediante las clases InputStreamReader y BufferedReader para poder ser finalmente almacenada en mDataOut , desde donde será derivada a la clase XMLExchange y MyXMLHandler para su procesado.
En el Código 34 se muestran los pasos para establecer y utilizar una conexión HTTP exitosamente.

Aplicaciones Android
Código 34. Conexión HTTP para enviar y recibir datos.

Para poder realizar una conexión es necesario tener declarado en el manifiesto el permiso que da acceso a Internet android.permission.INTERNET . Este permiso, como ser recordará, ya fue declarado con anterioridad para poder hacer uso de los servicios de Google Maps.

4.2.9 Procesado de peticiones y respuestas en XML

Todos los intercambios que existen entre la aplicación ContactMap y el servidor se realizan utilizando documentos XML, tal y como se detalló en el apartado anterior 4.1.3. Las tareas relacionadas con la escritura y lectura de documentos XML para los intercambios de datos se realizan en las clases XMLExchange y MyXMLHandler .
La clase XMLExchange es la encargada de escribir las peticiones que se enviarán al servidor, así como de procesar las respuestas recibidas desde este. Define dos únicos métodos:

- writeXMLUpdate() : compone las peticiones hacia el servidor. Utiliza tanto la información obtenida del GPS y del dispositivo móvil para escribir los datos propio usuario, como de los contactos almacenados en el mismo dispositivo móvil para incluir sus números de teléfono.
- readXMLUpdate() : procesa las respuesta enviadas por el servidor, utilizando la clase MyXMLHandler en dicha tarea.

Para poder procesar con mayor facilidad y rapidez un documento XML, existe en Java una popular librería con tal fin llamada SAX. Esta permite recorrer los elementos y sus contenidos de forma automática. Con tal fin, la clase MyXMLHandler extiende a la clase DefaultHandler . De este modo, consigue parsear el documento recibido, pudiendo asociar una determinada acción a la lectura de cada uno de los elementos presentes en el árbol XML.
La respuesta consta, tal y como se describe en el DTD adjunto en el apartado 4.1.3, de elementos que albergan los datos del contacto. Cada vez que MyXMLHandler encuentra uno de estos elementos, representantes de un determinado contacto, actualiza los datos recién recibidos de dicho contacto en la lista mFriendList de la clase ContactMap .

Aplicaciones Android
Aplicaciones Android
Código 35. Procesado de la respuesta XML

4.2.10 Acceso a los contactos del dispositivo móvil

Cuando ContactMap solicita al servidor la localización de sus contactos, utiliza el número de teléfono como clave identificativa. Así mismo, la aplicación escribe el nombre de cada contacto al lado de su ubicación en el mapa, y utiliza tanto el teléfono como la dirección de correo electrónico para enviar, respectivamente, un mensaje de texto o un correo. Toda esta información se obtiene de los contactos almacenados en el dispositivo móvil. Las tareas relacionadas con la manipulación de los contactos se lleva a cabo a través de la clase Contacts .
Para acceder a la información de contactos y, en general, acceder a diferentes tipos de contenedores de información, se necesitan tres elementos:

- Un objeto a través del cual acceder al contenedor de información deseado. En este caso, se utiliza la clase ContentResolver , del paquete android.content , que permite entre otras cosas lanzar consultas sobre un determinado recurso.
- Un objeto que recoja el resultado de la consulta. La clase Cursor , del paquete android.database , permite almacenar y recorrer el resultado obtenido al realizar una consulta a un contenedor de información.
- Una referencia que indique a qué contenedor concreto se desea acceder. En Android, los contenedores de información suelen estar referenciados por una URI que los identifica y que permite su acceso concreto. En el caso que nos ocupa, se utiliza la clase Uri del paquete android.net.Uri .

La clase ContentResolver utilizada para tener acceso a los contactos pertenece a uno de los paquetes más importantes de Android, el paquete android.content . Contiene clases que permiten acceder y publicar diferentes tipos de contenidos. Algunas de sus clases más relevantes son las siguientes:

- ServiceConnection : permite monitorizar el estado de un componente Service.
- BroadcastReceiver : uno de los componentes básicos de una aplicación Android, que permite a una aplicación escuchar y atender Intents.
- ContentProvider : otro de los componentes básicos de Android. Facilita a las aplicaciones publicar sus contenidos y hacerlos accesibles a otras aplicaciones del sistema.
- ContentResolver : ya mencionado, posibilita el acceso a modelos de contenidos.
- Context : clase que representa el ámbito de ejecución de una aplicación.
- Intent : clase de vital importancia en Android, que representa una acción y sus datos asociados. Recuérdese que, mediante un Intent, se lanza una solicitud para que determinada acción sea llevada a cabo por la aplicación más adecuada entre todas las disponibles en el sistema (ver más adelante en este mismo capítulo).

En la clase Contacts se define un total de tres métodos de acceso a la información de contactos. El método getContacts() devuelve un objeto Cursor que contiene todos los contactos presentes en el dispositivo móvil. Los otros dos métodos son de carácter más concreto: getContactNameByNumber() , que devuelve el nombre de un contacto según su número, y getContactEmailByNumber() , que obtiene el correo electrónico de un contacto también según su número de teléfono.
Los métodos mencionados de la clase Contacts acceden a la información de contactos de forma muy similar. Por ello, se explicará el más general de ellos, getContacts() . El Código 36 muestra su código fuente:

Aplicaciones Android
Código 36. Acceso a los contactos del dispositivo móvil

En primer lugar se construye un array de tipo string donde se especifican las columnas que se desean obtener en la consulta. La clase Contacts.People representa una de las tablas que contiene información sobre los contactos, y forma parte de un interesante paquete llamado android.providers . Mediante este paquete se pueden referenciar las tablas y columnas de todos los contenedores de datos facilitados por Android, como pueden ser contactos, llamadas, fotografías, audios o vídeos, entre otros.
Para esta consulta se requieren tres columnas: el nombre del contacto, Contacts.People.NAME , su número de teléfono, Contacts.People.NUMBER , y su identificador único dentro del conjunto de contactos, Contacts.People._ID . Este último campo permite acceder a otras tablas donde el contacto está referenciado usando el identificador como clave ajena.
De momento, ya se ha indicado qué columnas interesan, pero no la tabla que debe ser consultada en busca de dichas columnas. El siguiente paso necesario es poder referenciar en la consulta a qué contenedor exacto vamos a consultar. Cada contenedor de datos de Android suele tener asociada una URI única que lo identifica. La constante Contacts.People.CONTENT_URI hace referencia a la URI que interesa para esta consulta, por lo que es almacenada en un objeto Uri .
Solamente resta lanzar la consulta al contenedor de contactos. El objeto ContentResolver nos permite utilizar el método query() donde, parámetro a parámetro, se puede construir una completa sentencia SELECT como se define en SQL estándar. Los parámetros del método query() son:

- la tabla que se desea consultar, identificada por un objeto Uri .
- las columnas que se quieren obtener, presentes en el array previamente construido.
- los filtros de selección deseados o cláusula WHERE; en este caso, ninguna.
- los valores utilizados en la filtración o cláusula WHERE; igualmente, ninguno en este caso.
- el orden del conjunto resultado: por nombre de contacto y en orden ascendente.

El resultado se aloja en un objeto Cursor , que permite recorrer y leer las filas obtenidas en la consulta. Para llevar a cabo este control del resultado, la clase Cursor incluye, entre otros, los siguientes métodos:

- getCount() : devuelve el número total de filas.
- getPosition() : devuelve la posición actual del cursor.
- getString() : devuelve una cadena con el valor de la columna indicada.
- moveToNext() : mueve el cursor a la siguiente fila.
- moveToFirst() : mueve el cursor a la primera fila.

El acceso a la información de contactos y, en general, a cualquier contenedor de información de Android requiere de los permisos necesarios expresados en el manifiesto. Así pues, en el fichero “AndroiManifest.xml” de ContactMap debe figurar una línea como la siguiente:


Código 37. Declaración en el manifiesto del permiso de acceso a los contactos.

4.2.11 Acceso a información sobre el dispositivo móvil

ContactMap no sólo obtiene información de localización de los contactos en el servidor, sino que además actualiza su propia información de localización para que esté disponible para los demás usuarios. Para ello, es necesario que ContactMap conozca el propio número de teléfono del usuario, ya que es la clave a través de la cual se identifica en el servidor.
La mejor forma de proporcionar el número de teléfono es preguntándoselo al propio dispositivo móvil. La clase TelephonyManager , incluida en el paquete android.telephony , permite esta consulta. Dicho paquete ofrece un pequeño conjunto de clases que permiten acceder a diferentes datos acerca del dispositivo móvil sobre el que Android se ejecuta.
El siguiente fragmento de código muestra parte del método writeXMLUpdate() , de la clase XMLExchange . Este método escribe una petición para el servidor, donde se incluyen los datos de localización del usuario, precedidos por su número de teléfono.

Aplicaciones Android
Código 38. Acceso al número de teléfono del dispositivo móvil

El objeto TelephonyManager se obtiene tilizando el método getSystemService() que, como ya se vio con el GPS o la Wi-Fi, provee de los diferentes servicios integrados en el dispositivo móvil. Aquí se especifica la constante Context.TELEPHONY_SERVICE de la clase Context , que representa el contexto de la aplicación.
El método que proporciona el número de teléfono es getLine1Number() , pero la clase dispone de otros útiles métodos como getCallState() , que informa del estado de la llamada en curso, getCellLocation() , que informa de la celda GSM en la que se encuentra el dispositivo móvil, o getNetworkOperatorName() , que devuelve el nombre del operador de telefonía utilizado.
Como cabía esperar, el acceso a este tipo de información implica la declaración del siguiente permiso en el manifiesto de ContactMap:

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

Código 39. Declaración en el manifiesto del permiso de acceso al dispositivo

4.2.12 Uso de la base de datos SQLite

La información de localización de los contactos se actualiza de forma periódica mediante conexiones con el servidor. Sin embargo, en caso de no tener una señal Wi-Fi disponible ContactMap utiliza la información almacenada en la base de datos SQLite de Android para mostrar al menos las últimas ubicaciones conocidas.
Toda la gestión relacionada con la base de datos se lleva a cabo en la clase FriendDataBase . En ella están implementados dos métodos importantes: getFriends() que devuelve un objeto Cursor con todos los contactos almacenados en la base de datos, e insertFriend() que actualiza los datos de un contacto y en caso de no existir lo inserta. El manejo de la base de datos SQLite es muy similar al que se hace cuando se accede, por ejemplo, a la información de los contactos almacenados en el propio dispositivo móvil, ya explicado en el apartado 4.2.10.
En la clase FriendDataBase se declaran dos variables globales de ámbito privado. La primera, mDataBase , representa un objeto de la clase SQLiteDatabase . Esta clase forma parte del paquete android.database.sqlite , que incluye otras muchas clases e interfaces para el manejo de SQLite. La variable mDataBase representa un controlador de la base de datos y proporciona métodos para la creación, consulta o actualización de la misma. La otra variable global, llamada mTable , contiene la sentencia SQL necesaria para crear la base de datos en caso de ser la primera ejecución de ContactMap.

Crear la base de datos

Como ya se ha mencionado, la variable mTable contiene la instrucción en SQL necesaria para crear la base de datos. Esta implica una única tabla donde se almacena número de teléfono, latitud, longitud y fecha de actualización de cada contacto para el que se ha obtenido alguna vez información desde el servidor. Se puede comprobar que su estructura, mostrada a continuación, es igual a la base de datos en MySQL residente en el servidor (ver apartado 4.1.4):

Aplicaciones Android
Código 40. Script SQL de la base de datos SQLite de Android

Cuando se llama al constructor de la clase, lo primero que se hace es abrir la base de datos, de nombre “ContactMapBD”, utilizando para ello el contexto de la aplicación y el método openOrCreateDatabase() . Como es de suponer, en caso de no existir la base de datos con el nombre indicado, dicho método la crea.
A continuación, ya se dispone en la variable mDataBase de una controlador con el que manejar la base de datos. El siguiente paso es ejecutar el script SQL contenido en la variable mTable . Así, si la base de datos ha tenido que ser creada, se creará también la tabla necesaria. En caso de existir, la sentencia SQL no produce ningún efecto. Para lanzar la sentencia se utiliza el método execSQL() de la clase SQLiteDatabase .
En el Código 41. Creación de la base de datos SQLite.Código 41 se expone el constructor de FriendDataBase :

public class FriendDataBase {
public FriendDataBase(Context ctx){
//apertura o creación de la base de datos
this.mDataBase =
ctx.openOrCreateDatabase("ContactMapBD",0,null);
Jaime Aranaz Tudela
Ingeniería en Informática
129Proyecto Fin de Carrera
Desarrollo de aplicaciones para dispositivos móviles sobre la plataforma Android de Google
//creación de la tabla
this.mDataBase.execSQL(mTable);
}
}

Código 41. Creación de la base de datos SQLite.

Consulta, actualización e inserción de filas Dentro de la clase FriendDataBase existen dos métodos que interactúan con la base de datos: getFriends() e insertFriend() . El primero devuelve todos los contactos y el segundo actualiza los datos de un contacto o, en caso de no existir, lo inserta. Seguidamente, se mostrará el código del segundo método al ser el más interesante de los dos:

Aplicaciones Android
Código 42. Consulta, actualización e inserción de filas en la base de datos SQLite

La primera acción es consultar la base de datos para conocer si existe el contacto al que se le van a actualizar los datos de localización. El método query() de la clase SQLiteDatabase permite lanzar una consulta, de forma que mediante sus parámetros se construye una sentencia SELECT completa: tabla a consultar, columnas que se desea devolver, posible cláusula WHERE y orden de las filas (ver el apartado 4.2.10). Esta consulta arrojará su resultado sobre un objeto de la clase Cursor .
En caso de devolver alguna fila, según indique el método getCount() de la clase Cursor , significa que el contacto ya existe y se puede actualizar con el método update() de SQLiteDatabase ; si no devuelve ninguna, el contacto no existe y debe ser insertado con el método insert() .
En cualquier caso, es necesario agrupar de alguna forma los valores para la tupla, ya sea para su inserción o su actualización. Dentro del paquete android.content (aquel importante que contiene clases como Intent o BroadcastReceiver ) existe una clase llamada ContentValues que permite asociar pares del tipo campo-valor perfectos para su utilización en bases de datos como SQLite. De esta forma, se compone un objeto ContentValues con la latitud, longitud y fecha del contacto para su utilización en insert() o update().

Cuándo guardar la información de los contactos en SQLite

Como se recordará, la información de localización de los contactos se obtiene a través de conexiones periódicas con el servidor. Cada vez que se realiza una conexión exitosa, la lista con los contactos mFriendList se actualiza. En caso de no ser posible la conexión, se utiliza la información presente en la base de datos SQLite. Pero, ¿en qué momento se guarda esta información en la base de datos para ser recuperada en caso de necesidad?
La información de localización de los contactos se guardará desde la lista mFriendList a la base de datos SQLite siempre que se cierre la aplicación ContactMap, o cada vez que exista el riesgo de que la aplicación pueda ser eliminada por falta de recursos. De esta forma, se pretende asegurar que siempre se tendrá acceso a la última información de localización obtenida.
La clase principal ContactMap , como clase que deriva de Activity, tiene un ciclo de vida bien definido y gestionado por el propio Android, tal y como se explicó en un capítulo anterior. Cada transición entre estados de la actividad (en ejecución, en pausa, etc.) implica la llamada del método correspondiente.
Por ejemplo, al crearse por primera vez la actividad se lanza siempre el método onCreate() . Así mismo, cuando la actividad es eliminada de forma intencionada por el usuario, se llama al método onDestroy() . Una de las situaciones bajo la cual la actividad ContactMap puede ser eliminada por el sistema por falta de recursos es cuando esta pasa a un segundo plano y, además, ya no es visible para el usuario. Tal circunstancia va precedida por la llamada al método onStop() .
Es en ambos estados del ciclo de vida, onStop() y onDestroy() , cuando se recorre la lista mFriendList con los contactos y se almacena, uno por uno, en la base de datos de SQLite para poder ser repuesta en caso de que no exista posibilidad de establecer conexión con el servidor. Cuando tal circunstancia se detecta, se utiliza el método updateFriendsFromDataBase() definido en ContactMap .

4.2.13 Construcción del menú principal

Para poder acceder a algunas de las opciones ofrecidas por ContactMap, el usuario debe utilizar el menú principal de la aplicación. Este menú aparece al pulsar la tecla correspondiente en el dispositivo móvil.
La clase ContactMap deriva de la clase Activity y tiene por ello un ciclo de vida bien definido. Además, cuenta con otra serie de métodos nativos que permiten tomar el control de diversos eventos provocados por el usuario. Este es el caso de la utilización del menú principal, cuyo comportamiento viene determinado por los métodos onCreateOptionsMenu() y onMenuItemSelected() .

Crear un menú

El método onCreateOptionsMenu() es invocado cuando se pulsa la tecla correspondiente al menú, y permite configurar el tipo de menú que se quiere desplegar. Este menú viene representado generalmente por una ventana que se desliza sobre la interfaz mostrada en ese momento y que debe ofrecer las funcionalidades principales de la aplicación en curso, que en el caso de ContactMap son las siguientes:

- Listar los contactos
- Cambiar el tipo de mapa
- Realizar una llamada al contacto
- Mandar un SMS al contacto
- Mandar un correo electrónico al contacto

En el siguiente fragmento de código se enseña al lector la forma en la que se compone el menú principal de ContactMap.

Aplicaciones Android
Código 43. Construcción del menú principal

La clase Menu representa el menú principal de una aplicación y forma parte del paquete android.view , que ofrece un considerable número de clases para manejar los elementos de una interfaz de usuario y gestionar los eventos asociados. A través del objeto Menu se gestionan las opciones que componen el menú principal.
Un clase MenuItem (también del paquete android.view ) representa una opción dentro del menú y permite tener acceso a sus propiedades. Cada uno de estos objetos se crea a partir del método add() de Menu , donde se especifica entre otras cosas el orden que debe ocupar la aplicación dentro del menú y el texto que la debe acompañar.
Las opciones declaradas para el menú principal cuentan con un texto asociado que las describe y una imagen o icono representativo, ambos visibles para el usuario cuando despliegue el menú. Para lo primero, el texto, se incluye en la propia llamada a Menu.add() y se utiliza el texto declarado en el fichero de recursos “strings.xml”, dentro de la carpeta “\res\values” de la aplicación.
Para las imágenes, se pueden asociar a cada opción a través del método setIcon() de la clase MenuItem . Al igual que con el texto, las imágenes se incluyen haciendo una referencia a la carpeta de recursos “\res\drawable”, donde deberán estar ubicadas. Como información de utilidad al lector, se dirá que el SDK de Android incluye por defecto una importante colección de iconos útiles para todo tipo de aplicaciones. Estos iconos se encuentran disponibles en la carpeta “\tools\lib\res\default\drawable”.

Aplicaciones Android
Figura 27. Despliegue del menú principal

Controlar la opción seleccionada

En este punto, el usuario ya puede desplegar el menú y comprobar que opciones principales le ofrece ContactMap .Ahora es necesario poder conocer qué opción pulsa el usuario y actuar en consecuencia.
El otro método importante para controlar el menú y sus opciones es onMenuItemSelected() . Cada vez que el usuario selecciona una opción del menú principal, este método se llama recibiendo como parámetro un objeto MenuItem que va a permitir conocer qué opción exactamente ha sido la pulsada.
Utilizando una sencilla sentencia de control switch y el método getItemId() de MenuItem , se obtiene la opción pulsada y se ejecuta la acción correspondiente a la misma, tal y como se hace en el Código 44.

Aplicaciones Android
Código 44. Control del menú principal