Bitácora de Javier Cancela

Archivo para la categoría "BlackBerry"

Notificaciones ‘push’ en dispositivos móviles

¿Qué es una notificación push?

Jeff Henshaw: Apples Push Notification Service

Es un mensaje que una aplicación servidora envía a una aplicación cliente indicándole que tiene algún tipo de información nueva disponible. Lo que distingue a las notificaciones push es que el servidor inicia la comunicación, no espera a que el cliente pregunte si hay algo nuevo. La ventaja es la inmediatez: la información llega al cliente (y por tanto al usuario) en cuanto está disponible en el servidor.

El ejemplo más clásico de este tipo de notificaciones es el correo electrónico. Un usuario tiene una aplicación de correo electrónico ejecutándose en su dispositivo. Alguien le envía un correo, que queda almacenado en el servidor. A partir de ahí pueden ocurrir dos cosas:

  • El cliente de correo electrónico está configurado para consultar al servidor, con una frecuencia predeterminada, si hay nuevos correos. Si la frecuencia es de una vez cada 5 minutos el usuario puede tardar ese tiempo en enterarse de que ha recibido un correo nuevo.
  • El cliente (y el servidor) soportan notificaciones push. En este caso el servidor envía un mensaje al cliente para avisarle de que ha llegado un nuevo correo, y el cliente a su vez se lo notifica de alguna forma al usuario para que lo lea si le interesa.

En el caso de las aplicaciones de mensajería instantánea la necesidad de la inmediatez es aún más manifiesta: para que la conversación sea fluida necesitamos que los mensajes nos lleguen en cuanto son enviados, e incluso queremos saber cuándo nuestro interlocutor está escribiendo algo.

¿Cómo se implementa una notificación push?

La manera habitual de implementar una notificación push es establecer una conexión TCP de larga duración. El cliente abre una conexión TCP con el servidor y la deja abierta. Sobre esta conexión TCP el servidor enviará las notificaciones al cliente, usando algún protocolo de aplicación. IMAP y XMPP son ejemplos de protocolos estándar usados para correo y mensajería respectivamente, aunque existen múltiples protocolos propietarios.

Notificaciones push en BlackBerry

indivisualist - BlackBerry

El éxito de las BlackBerry se debe en parte a su sistema de push email. Cuando leer el correo desde el móvil era una experiencia dolorosa en otros sistemas, las BlackBerry ya conseguían que sus usuarios recibiesen el correo de manera instantánea. RIM subscribe un acuerdo con los operadores de telefonía, por el cual se establece una conexión permanente con unos servidores especiales operados por RIM. Estos servidores reciben el correo del usuario desde los servidores BES, y lo notifican de forma inmediata a las BlackBerry.

La tecnología de notificaciones push de RIM está disponible para terceros a través de la BlackBerry Push API.

Notificaciones push en iPhone

Apple incorporó desde el principio un sistema de notificaciones push para el correo electrónico de Yahoo. La aparición de MobileMe añadió notificaciones push tanto para el correo como para el calendario y los contactos.

Cuando Apple diseñó la SDK del iPhone tomó la decisión de no permitir a los desarrolladores crear aplicaciones que se ejecutasen en segundo plano. Como consecuencia, las aplicaciones desarrolladas por terceros sólo se ejecutan mientras están en primer plano, deteniéndose cuando el usuario quiere realizar otra actividad.

La nueva versión de la SDK incorpora el Apple Push Notification service, un servicio que envía notificaciones a las aplicaciones del iPhone aunque estas no se estén ejecutando. Para ello la notificación pasa primero por los servidores de Apple, con los que el móvil mantiene siempre una conexión abierta. La notificación se envía al móvil y se muestra al usuario como un mensaje de texto o como una ventana de notificación, que servirán además para lanzar la aplicación y procesar la notificación recibida.

Notificaciones push en Android

Android proporciona notificaciones push para el correo electrónico, calendario y contactos, tanto en el HTC Dream como en el HTC Magic. Como en los casos anteriores, el sistema establece un canal siempre abierto con los los servidores del proveedor (en este caso Google).

Aunque los desarrolladores de Android tienen la opción de programar aplicaciones que se ejecuten en segundo plano el sistema se reserva el derecho de detener cualquier aplicación cuando así lo decida, en función de los recursos disponibles en la máquina. Así que podemos desarrollar un cliente que mantenga conexiones TCP de larga duración con el servidor ejecutándose en bakcground, pero no podemos garantizar que la aplicación se esté ejecutando para mantener esta conexión abierta. Claro que este problema se presenta aunque queramos traernos la información usando pull, es decir, consultando al servidor de periódicamente.

En las versiones beta de la SDK se incluía un servicio llamada XMPPService o GTalkService, que permitía a los desarrolladores enviar notificaciones utilizando la infraestructura de Google Talk. Este servicio fue retirado de la versión final de la SDK por problemas de seguridad, como se explica aquí: Some information on APIs removed in the Android 0.9 SDK beta. No sería extraño que Google incluyese una versión mejorada en alguna de las próximas versiones de Android.

Escrito por Javier Cancela

13 \13\UTC julio \13\UTC 2009 a 7:00

Pequeño análisis del plugin de BlackBerry para Eclipse

Las vacaciones de semana santa, el trabajo y un problema en el servidor, que volvió inaccesible este blog durante varias horas del miércoles, se han conjurado para hacerme establecer un nuevo record de días sin publicar nada. Pero hay mucho sobre lo que escribir, así que confío en que la próxima semana compense esta que acaba.

Contaba en una entrada anterior que había aparecido un plugin de BlackBerry para Eclipse, sin que yo supiese siquiera que se estaba preparando uno. El JDE de RIM no es un entorno de desarrollo de mi gusto, así he instalado el plugin con la esperanza de que resulte una alternativa más interesante. Y así es.

Al contrario que los plugins habituales de Eclipse, el plugin de BlackBerry es un instalable de Windows que copia en la carpeta plugins del IDE tanto los habituales jar como exes y dlls. Una vez completada la instalación, Eclipse se muestra con una nueva opción en la barra de menús:

Desde esta opción podremos acceder a la aplicación de petición de firmas digitales o a algunas propiedades del proyecto:

La configuración en Eclipse está bastante dispersa, así que el habitual menú Project->Properties de Eclipse podemos definir varias propiedades específicas de Eclipse:

incluyendo el tipo de aplicación: CLDC, Midlet…

Y en el menú de ejecución (también en el de depuración) podemos configurar, entre otras cosas, el emulador que queremos usar con acceso a todos los parámetros del mismo:

El plugin encontró sin problemas todos los simuladores instalados, y también los que instalé tras el plugin.

En Window->Preferences podemos configurar una opción clave: los componentes BlackBerry a usar, es decir, con qué versión del sistema vamos a compilar. El plugin encuentra tantos las instalaciones completas de JDE como las instalaciones de los componentes independientes, y enlaza la ayuda correspondiente. En esta ventana establecemos también la versión de la JDK a usar:

En resumen, para los que estamos acostumbrados a Eclipse programar para BlackBerry se ha vuelto una experiencia familiar, y para los que no han usado Eclipse antes, este plugin supone una alternativa que sin duda resultará más satisfactoria.

Escrito por Javier Cancela

29 \29\UTC marzo \29\UTC 2008 a 22:02

Escrito en BlackBerry

Etiquetado con ,

Plugin de BlackBerry para Eclipse

Una de las cosas que se echaban de menos a la hora de desarrollar para BlackBerry era un plugin para Eclipse, similar a los que ya existen para Java ME o Android. Pues hoy me ha sorprendido leer en RIMarkable (BlackBerry JDE Plug-in for Eclipse) que ya está a disposición de los desarrolladores. Fue anunciado ayer por RIM (RIM Announces BlackBerry JDE Plug-in for Eclipse) y está disponible para descargar aquí. Intentaré probarlo esta semana.

Escrito por Javier Cancela

20 \20\UTC marzo \20\UTC 2008 a 13:09

Escrito en BlackBerry

Etiquetado con , ,

Mostrando un mapa estático de Yahoo! en con la BlackBerry

Veíamos en un artículo anterior (Cómo mostrar imágenes de mapas en el móvil) distintas opciones para mostrar mapas estáticos (simples imágenes de mapas, nada de JavaScript). Vamos a ver con un poco de código cómo utilizar la Yahoo! Map Image API en BlackBerry (que con algunos cambios se podrá adaptar a cualquier aplicación Java ME).

La API acepta varios parámetros para elegir la ubicación del mapa a mostrar, y nosotros vamos a utilizar como parámetros la longitud y latitud del punto central, así como el nivel de zoom y el tamaño de la imagen:

String url = "http://local.yahooapis.com/MapsService/V1/mapImage?appid=mi_yahoo_appid"
+ "&latitude=" + latitude + "&longitude=" + longitude +
"&image_height=" + height + "&image_width=" + width + "&zoom=" + zoom;

El parámetro mi_yahoo_appid es nuestro id de aplicación de Yahoo!, que se puede obtener gratuitamente aquí: Yahoo! Application ID. Con la url formada podemos realizar la llamada a la API.

StreamConnection s = (StreamConnection)Connector.open(url);
HttpConnection httpConn = (HttpConnection)s;
int status = httpConn.getResponseCode();
if (status == HttpConnection.HTTP_OK)
{
try
{
DocumentBuilder doc = DocumentBuilderFactory.newInstance().newDocumentBuilder();
DataInputStream dis = s.openDataInputStream();
Document d = doc.parse(dis);
Element el = d.getDocumentElement();
url = el.getFirstChild().getNodeValue() ;
dis.close();
}
catch(SAXException e)
{
System.err.println(e.toString());
}
catch(ParserConfigurationException e)
{
System.err.println(e.toString());
}

Realizamos la conexión de forma normal, y si todo ha ido bien Yahoo! nos devolverá un documento XML:

<?xml version="1.0" encoding="UTF-8"?>
<Result xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">

http://img.maps.yahoo.com/mapimage?MAPDATA=eJz6K.d6wXVM6myr2yRPfx6.kl.uMGgD3Tu4JtDQzr_33pFEsTT

SaosZ9OCtsiDrsLv9t65fzjz0CJm6JO2v_ZIHLflY9gto.xWMK9ovlRJVmrBLO4FoSsh3Ipsr
</Result>

Este documento contiene un único elemento, cuyo texto es otra url que contiene la imagen solicitada en formato PNG. Para acceder a esta url, en el package net.rim.device.api.xml.parsers encontramos la clase DocumentBuilder, que nos permite convertir un InputStream en un objeto Document de org.w3c.dom, a partir del cual accedemos fácilmente al valor del elemento. Con esta segunda url volvemos a abrir una conexión:

s = (StreamConnection)Connector.open(url);
httpConn = (HttpConnection)s;
status = httpConn.getResponseCode();
if (status == HttpConnection.HTTP_OK)
{
java.io.InputStream input = s.openInputStream();
byte[] data = new byte[1];
ByteVector bv = new ByteVector();
while ( -1 != input.read(data) )
{
bv.addElement(data[0]);
}
Bitmap bitmap = Bitmap.createBitmapFromPNG(bv.getArray(), 0, -1);
// Mostramos la imagen almacenada en el bitmap
}

Esta vez utilizamos el flujo de bytes devuelto para crear un Bitmap. No podemos utilizar el método getLength() de la conexión para averiguar el número de bytes total porque las cabeceras de la página no incluyen el campo Content-Length. Así que recorremos byte byte el stream para obtener un array de bytes con el que generar el Bitmap.
Ya tenemos nuestro Bitmap para mostrar. Bastará añadir, por ejemplo, un BitmapField y asignarle el Bitmap recién creado.

Escrito por Javier Cancela

14 \14\UTC marzo \14\UTC 2008 a 7:30

Acceder a Internet con Java en una BlackBerry

Entre las peculiaridades que presentan las BlackBerry como dispositivos programables, la que más quebraderos de cabeza suele causar es la conexión a Internet. El motivo no es la API utilizada, que es la habitual Generic Connection Framework de CLDC 1.0, sino los diferentes tipos de acceso a Internet que se pueden usar en los dispositivos BlackBerry. Los tipos de conexión dependen tanto servicio contratado, del modelo de BlackBerry, de la operadora… De hecho, lo que viene a continuación no es información contrastada, sino conclusiones que he sacado tras investigar en diversas páginas y manuales que he encontrado por Internet. Es además una exposición simplificada, destinada a desarrolladores que quieran programar en la BlackBerry aplicaciones que se conectan a Internet. Cualquier aportación o corrección es como siempre bienvenida.

Existen dos servicios BlackBerry distintos. Uno es el empresarial, llamado BlackBerry Enterprise Solution (o BES para abreviar), en el cual la empresa tiene un servidor (el BlackBerry Enterprise Server) que todas las BlackBerry corporativas usan como punto de acceso. A través de él acceden al correo corporativo o se descargan aplicaciones corporativas. Tambien proporciona un servicio, llamado Mobile Data Service, que actúa de proxy para acceder a Internet. Con BES, las únicas restricciones son las que la empresa configure en su servidor.

Para usuarios individuales, o para pequeñas empresas que no puedan permitirse la infraestructura BES, existe el servicio BlackBerry Internet Service (BIS). Este servicio permite acceder a Internet a través de una pasarela gestionada por RIM, habitualmente con un plan de datos con tarifa plana. Pero, y aquí empieza la complicación, sólo pueden acceder a esta pasarela aquellas aplicaciones que tengan permiso para ello. A día de hoy existen varias aplicaciones que funcionan bajo la pasarela de BIS (por ejemplo, hay una lista, no sé cómo de completa, aquí: ¿Entra en la Tarifa Plana?). Imagino que es neecsario un acuerdo con RIM para que una aplicación tenga acceso a esta pasarela, pero no he conseguido información al respecto.

Además de BIS, las operadoras pueden ofrecer una pasarela propia para acceder a Internet. Esta pasarela puede ser WAP o no (creo que las que ofrecen en España son WAP). Para utilizar estas pasarelas es necesario tener definido un APN (Access Point Name) con los parámetros especificados por el operador (nombre, y opcionalmente ip, puerto, usuario y contraseña), aunque también es posible incluir estos parámetros desde el código que abre la conexión.

El problema de las pasarelas que no son BIS es que se cobran aparte, por lo su coste no va incluido en la tarifa plana de BlackBerry; eso sí, es posible no contratar el servicio BIS y contratar una tarifa plana de datos convencional, que sí cubrirá todas nuestras conexiones a Internet. Pero si no se tiene tarifa plana, lo mejor es asegurarse de que no hay APNs configuradas y de que las aplicaciones que usamos funcionan en BIS.

Como conclusión, las aplicaciones Java que hagamos para BlackBerry no podrán acceder a Internet por BIS. Hay que tener en cuenta, además, que con el emulador de BlackBerry sólo podemos probar conexiones a través del MDS, ya que no hay forma de emular un servicio BIS ni una pasarela directa a Internet. Las diferencias son relevantes en el código, ya que por defecto casi todas las BlackBerry intentan conectarse por Mobile Data Service.  Los parámetros para especificar un tipo de conexión u otro, así como para especificar una pasarela wap se encuentran en este artículo de la base de conocimiento de BlackBerry: What Is – Different ways to make an HTTP or socket connection.

Referencias:

Lo que entra en el plan BB
BESADMIN.es
¿Entra en la Tarifa Plana?

Escrito por Javier Cancela

13 \13\UTC marzo \13\UTC 2008 a 20:00

Escrito en BlackBerry

Etiquetado con , , ,

Habemus claves de BlackBerry

 La gente de RIM me ha enviado unas claves nuevas y, esta vez sí, he conseguido registrarlas. No me han dado más detalles sobre el asunto, pero imagino que hubo algún problema por su parte en la activación de las claves anteriores. Tras el registro he podido probar el proceso de firma desde el JDE y ha funcionado bien: el entorno indica para cada uno de los tres tipos de claves (RBB, RCR y RRT, para APIs de aplicación, criptográficas y de run-time respectivamente) si la firma es requerida u opcional, y automáticamente calcula un hash del código, lo envía a RIM y lo devuelve firmado.

Aún no he tenido tiempo de probar la aplicación firmada en un dispositivo físico, pero ya queda menos para hacer pública una primera versión del plugin para BlackBerry.

OffTopic. Como parte de los cambios que estoy haciendo en esta bitácora, he añadido un widget de Ipoki. En realidad es el iFrame que aparece bajo el mapa de cada usuario en la web de Ipoki, añadido a mano a este tema de WordPress. Creo que queda bien.

Escrito por Javier Cancela

22 \22\UTC febrero \22\UTC 2008 a 9:00

Más sobre BlackBerry

Hoy he recibido un correo de RIM informándome de que me mandarán unas nuevas claves para que pruebe con ellas, y pidiéndome disculpas por las molestias. Sigue sin parecerme muy serio, porque aún no tengo las claves, ni me han dado más explicaciones.

Pero es que además hoy se ha caído el servicio BlackBerry en Estados Unidos, por segunda vez en la semana y de forma bastante estrepitosa (hablan de un apagón del 80%), en lo que parece ser la confirmación de que la calidad del servicio de RIM ha tocado fondo.

Visto lo visto, hasta he tenido suerte de que me respondan.

Escrito por Javier Cancela

20 \20\UTC febrero \20\UTC 2008 a 19:35

Escrito en BlackBerry

Etiquetado con ,

Intentando firmar una aplicación BlackBerry (no todo va a ser Symbian)

Como parte del proceso de desarrollo del plugin de Ipoki para BlackBerry, solicité a RIM unas claves para firmar la aplicación, que utiliza algunas clases marcadas como Signed en la documentación. Estas clases obligan a que la aplicación esté firmada antes de ser instalada en un dispositivo. Para solicitar las claves existen dos posibilidades: rellenar este formulario, o descargar esta versión del formulario en pdf, imprimirla, firmarla y enviarla por fax. La existencia de esta última opción es el primer indicio de que RIM todavía no ha entrado en el siglo XXI.

Conseguir la claves cuesta 20 dólares, una cifra bastante económica que en la página que describe el proceso de solicitud justifican por los costes administrativos. En realidad se hace fundamentalmente para tener los datos del titular de la tarjeta de crédito. En esta misma página se nos dice qué ocurrirá tras el envío de la solicitud (traducción propia):

Nota: Típicamente, las claves son enviadas por email en un plazo de 48 horas tras el envío de los formularios necesarios, pero ocasionalmente el proceso puede llevar hasta 10 días laborables. Si no ha recibido sus claves en el plazo de 10 días laborables desde el envío del formulario de registro, por favor póngase en contacto con el soporte de BlackBerry en el número 1-877-255-2377.

Sinceramente, sólo se me ocurre un motivo por el cual puedan tardar diez días en enviarte por correo tres archivos de menos de un KByte que obviamente un ordenador genera de forma inmediata: quieren comprobar mis datos con el FBI. ¿Y el motivo por el cual mi único opción de ponerme en contacto con soporte es un número de teléfono norteamericano (eso sí, gratuito para llamadas desde USA o Canadá)? Será para que el FBI pueda rastrear mi llamada…

Mi experiencia con el proceso transcurrió así: cubro mis datos, doy mi tarjeta y envío el formulario aceptando unas condiciones que por supuesto no he leído. La página me informa de que mi solicitud ha sido procesada, pero a mi correo no llega ninguna confirmación, ningún ticket de seguimiento, nada que me confirme que han tomado nota y que mis 20 dólares van a servir de algo. Tras cuatro días preguntándome si habría escrito bien mi dirección de correo electrónico recibo las claves, en tres correos distintos pero con el mismo texto explicando la instalación de las mismas. La instalación es simple:

To register the attachment, please follow the instructions below:

1) Double-click on the attachment.
2) If a dialog box appears that states that a private key cannot be found, complete steps 3 through 6 before you continue. Otherwise, proceed to step 7.
3) Click “Yes” to create a new key pair file.
4) Type a password for your private key, and type it again to confirm.
5) Click “Ok”
6) Move your mouse to generate date for a new private key.
7) In the “Registration PIN” field, type the PIN number that you supplied on the signature key request form.
8 ) In the Private Key password field, type a password of at least 8 characters. This is your private key password, which protects your private key. Please remember this password as you will be prompted for it each time signing is attempted.
9) Click “Register”.
10) Click “Exit”.

Note: All 3 keys (RBB, RCR, RRT) received should be installed on the same PC. The same password must be specified for all keys on the same PC.

Así que hago doble click a una de las claves, le doy a Yes y… no pasa nada. Pruebo varias veces con cada una de las claves y me cercioro de que efectivamente no funciona. Tras un poco de Google llego a la conclusión de que es culpa de la versión de Java. Teóricamente el JDE 4.1 funciona con Java5 y no con Java6. Esto no es cierto, yo sólo tengo Java6 instalado y el JDE me va perfectamente. Sin embargo, hago la prueba para ver si la firma falla por eso. Instalo Java5 y desinstalo Java6 y….¡Tachán! La clave me pide una contraseña para su instalación. Problema resuelto.

Ahora sólo tengo que introducir la clave de privada y la clave de registro y…

“Registration request failed because client ID xxxxxxxxxx is not set up to be registered (that is, the ID was not in the authorisation database)”

De este no me salva ni Google. Primero porque no hay ninguna referencia a un error con ese texto, y segundo porque tiene toda la pinta de ser algún error en el proceso de registro, no en mi ordenador. Lo único que me queda es ponerme en contacto con el soporte de BlackBerry para contarles lo que me ha ocurrido. Por suerte el correo con las claves incluía también una dirección de correo electrónico de soporte.

De momento no he tenido noticias de RIM; tampoco es que esperase que tuviesen un servicio 24×7 para esto. Por lo que he visto hasta ahora el servicio de firma de aplicaciones de RIM no es todo lo satisfactorio de se podría esperar. Y aún falta la firma real, en la cual se envía un hash del código que RIM debe devolver firmado. Veremos cómo acaba todo.

Escrito por Javier Cancela

17 \17\UTC febrero \17\UTC 2008 a 12:10

¿Para qué modelo de BlackBerry desarrollar?

A la hora de desarrollar software para dispositivos BlackBerry, y para que nuestro producto sea compatible con la mayor cantidad posible de modelos, es útil tener en cuenta dos cosas:

  • Con el JDE desarrollamos para una versión concreta del BlackBerry Handheld Software, el “sistema operativo” del dispositivo. Teóricamente existe compatibilidad hacia atrás, de forma que con el JDE 4.1 obtendremos una aplicación capaz de ejecutarse en dispositivos con versiones 4.1, 4.2 o 4.3 del BlackBerry Handheld Software. Así, conviene siempre utilizar la versión menor que permita desarrollar la aplicación que queremos.
  • Los dispositivos BlackBerry son actualizables: para cada familia de BlackBerrys se liberan versiones actualizadas del sistema, generalmente por parte de las propias operadoras. Una lista actualizada con el último sistema disponible para cada dispositivo se puede encontrar en los foros de BlackBerryFORUMS.

Actualizar el software de la BlackBerry no es demasiado complejo, y para la mayoría de las BlackBerry recientes existe una actualización para la versión 4.2 del software. Para saber qué versión tiene un dispositivo basta con ir a Options->About.

Escrito por Javier Cancela

11 \11\UTC febrero \11\UTC 2008 a 18:38

Leyendo nuestro GPS desde Java con la JavaME Location API (JSR-179) – Parte II

Esta entrada completa la explicación del ejemplo tratado en la serie de entradas Un ejemplo de aplicación Java para BlackBerry. Ver enlaces al final.

Recapitulando

GPSDemo es una aplicación para BlackBerry que hace uso de la API JSR-179 para mostrar información de la posición y velocidad obtenidas de un dispositivo GPS o similar (ver la entrada Leyendo nuestro GPS desde Java con la JavaME Location API (JSR-179) – Parte I para una explicación de las posibles fuentes de información).

Habíamos dejado pendiente de describir el método startLocationUpdate() que se llama desde el constructor de la clase GPSDemo:

private void startLocationUpdate()
{
try
{
_locationProvider = LocationProvider.getInstance(null);
if ( _locationProvider == null )
{
Dialog.alert("GPS is not supported on this platform, exiting...");
System.exit(0);
}
_locationProvider.setLocationListener(new LocationListenerImpl(), _interval, 1, 1);
}
catch (LocationException le)
{
System.err.println("Failed to add a location listener. Exiting...");
System.err.println(le);
System.exit(0);
}
}

Lo primero que hacemos es obtener un proveedor de contenidos, con los criterios por defecto. En caso de no poder obtener uno, salimos de la aplicación.
La otra cosa que hace este método es establecer el objeto responsable de escuchar la información que llega del GPS. La variable _internal se estableció previamente al valor 1, con lo que la información se actualizará aproxamadamente una vez por segundo. Los dos últimos parámetros establecen el timeout y la caducidad de los datos.
Para actuar como listener del GPS una clase debe implementar la interfaz LocationListener.

La clase LocationListenerImpl

De forma más bien descriptiva, la clase listener de nuestro ejemplo se llama LocationListenerImpl:

private class LocationListenerImpl implements LocationListener
{
// Members. --------------------------------------------------------------
private int captureCount;

public void providerStateChanged(LocationProvider provider, int newState)
{
// No operation defined.
}
...

La interfaz tiene dos métodos. providerStateChanged nos avisa de los cambios de estado de nuestro proveedor de localización, básicamente cuando deja de estar disponible por algún tipo de problema; como estamos pensando sólo en el GPS, que no va a quedar fuera de servicio, no implementamos este método.
El otro método es el que nos avisa de que el GPS ha suministrado una nueva localización:

public void locationUpdated(LocationProvider provider, Location location)
{
if(location.isValid())
{
float heading = location.getCourse();
double longitude = location.getQualifiedCoordinates().getLongitude();
double latitude = location.getQualifiedCoordinates().getLatitude();
float altitude = location.getQualifiedCoordinates().getAltitude();
float speed = location.getSpeed();
// Horizontal distance.
float horizontalDistance = speed * _interval;
_horizontalDistance += horizontalDistance;
// Horizontal distance for this waypoint.
_wayHorizontalDistance += horizontalDistance;
// Distance over the current interval.
float totalDist = 0;
// Moving average grade.
for(int i = 0; i 0) _verticalDistance = _verticalDistance + altGain;
captureCount += _interval;
// If we’re mod zero then it’s time to record this data.
captureCount %= CAPTURE_INTERVAL;
// Information to display on the device.
StringBuffer sb = new StringBuffer();
sb.append("Longitude: " + longitude+ "\n");
sb.append("Latitude: " + latitude+ "\n");
sb.append("Altitude: " + altitude + " m\n");
sb.append("Heading relative to true north: " + heading + "\n");
sb.append("Speed : " + speed + +" m/s\n");
sb.append("Grade : ");
if(Float.isNaN(grade))
sb.append(" Not available");
else
sb.append(grade+" %");
GPSDemo.this.updateLocationScreen(sb.toString());
}
}

Mucho código, pero muy simple. Se nos pasa como parámetros el LocationProvider, que no necesitamos, y un objeto Location que contiene, además de las coordenadas, la velocidad, la dirección y un método (isValid()) que nos dice si el objeto Location tiene coordenadas o no. En caso de utilizar como proveedor de contenidos un servicio de pago del operador, podríamos tener información adicional en el mismo objeto, como una dirección postal.

Código de GPSDemo.java

/**
* A GPS sample application using the JSR 179 APIs.
*
* Copyright (C) 2005 Research In Motion Limited.
*/
package com.rim.samples.docs.gpsdemo;
import net.rim.device.api.ui.*;
import net.rim.device.api.ui.component.*;
import net.rim.device.api.ui.container.*;
import com.rim.samples.docs.baseapp.*;
import net.rim.device.api.io.*;
import net.rim.device.api.system.*;
import net.rim.device.api.i18n.*;
import javax.microedition.io.*;
import java.util.*;
import java.io.*;
import javax.microedition.location.*;
import net.rim.device.api.util.*;
import com.rim.samples.docs.resource.*;
/* This application acts as a simple travel computer, recording route coordinates,
* speed, and altitude.
* Recording begins as soon as the application is invoked.
*/
public class GPSDemo extends BaseApp implements GPSDemoResResource
{
// Constants. ----------------------------------------------------------------
// The number of updates in seconds over which the altitude is calculated.
private static final int GRADE_INTERVAL=5;
// com.rim.samples.docs.gpsdemo.GPSDemo.ID
private static final long ID = 0x4e94d9bc9c54fed3L;
private static final int CAPTURE_INTERVAL=10;
// Statics. ------------------------------------------------------------------
private static ResourceBundle _resources =
ResourceBundle.getBundle(GPSDemoResResource.BUNDLE_ID, GPSDemoResResource.BUNDLE_NAME);
// The period of the position query in seconds.
private static int _interval = 1;
private static Vector _previousPoints;
private static float[] _altitudes;
private static float[] _horizontalDistances;
private static PersistentObject _store;
// Initialize or reload the persistent store.
static
{
_store = PersistentStore.getPersistentObject(ID);
if(_store.getContents()==null)
{
_previousPoints= new Vector();
_store.setContents(_previousPoints);
}
_previousPoints=(Vector)_store.getContents();
}
private long _startTime;
private float _wayHorizontalDistance;
private float _horizontalDistance;
private float _verticalDistance;
private ListField _listField;
private EditField _status;
private StringBuffer _messageString;
private String _oldmessageString;
private LocationProvider _locationProvider;
/* Instantiate the new application object and enter the event loop.
* @param args unsupported. no args are supported for this application
*/
public static void main(String[] args)
{
new GPSDemo().enterEventDispatcher();
}
// Constructors. -------------------------------------------------------------
public GPSDemo()
{
// Used by waypoints; represents the time since the last waypoint.
_startTime = System.currentTimeMillis();
_altitudes=new float[GRADE_INTERVAL];
_horizontalDistances=new float[GRADE_INTERVAL];
_messageString= new StringBuffer();
MainScreen screen = new MainScreen();
screen.setTitle(new LabelField(_resources.getString(GPSDEMO_TITLE), LabelField.USE_ALL_WIDTH));
_status = new EditField();
screen.add(_status);
screen.addKeyListener(this);
screen.addTrackwheelListener(this);
// Start the GPS thread that listens for updates.
startLocationUpdate();
// Render our screen.
pushScreen(screen);
}
/* Update the GUI with the data just received.
*/
private void updateLocationScreen(final String msg)
{
invokeLater(new Runnable()
{
public void run()
{
_status.setText(msg);
}
});
}
// Menu items. ---------------------------------------------------------------
// Cache the markwaypoint menu item for reuse.
private MenuItem _markWayPoint = new MenuItem(_resources, GPSDEMO_MENUITEM_MARKWAYPOINT, 110, 10)
{
public void run()
{
GPSDemo.this.markPoint();
}
};
// Cache the view waypoints menu item for reuse.
private MenuItem _viewWayPoints = new MenuItem(_resources, GPSDEMO_MENUITEM_VIEWWAYPOINTS, 110, 10)
{
public void run()
{
GPSDemo.this.viewPreviousPoints();
}
};
// Cache the close menu item for reuse.
private MenuItem _close = new MenuItem(_resources, GPSDEMO_MENUITEM_CLOSE, 110, 10)
{
public void run()
{
System.exit(0);
}
};
protected void makeMenu(Menu menu, int instance)
{
menu.add( _markWayPoint );
menu.add( _viewWayPoints );
menu.add( _close );
menu.addSeparator();
super.makeMenu(menu, instance);
}
/* Invokes the Location API with the default criteria.
*/
private void startLocationUpdate()
{
try
{
_locationProvider = LocationProvider.getInstance(null);
if ( _locationProvider == null )
{
Dialog.alert("GPS is not supported on this platform, exiting...");
System.exit(0);
}
// A single listener can be associated with a provider,
// and unsetting it involves the same call but with null,
// so there is no need to cache the listener instance.
// Request an update every second.
_locationProvider.setLocationListener(new LocationListenerImpl(), _interval, 1, 1);
}
catch (LocationException le)
{
System.err.println("Failed to add a location listener. Exiting...");
System.err.println(le);
System.exit(0);
}
}
/* Marks a point in the persistent store. Calculations are based on
* all data collected since the previous way point, or from the start
* of the application if no previous waypoints exist.
*/
private void markPoint()
{
long current = System.currentTimeMillis();
WayPoint p= new WayPoint(_startTime, current, _wayHorizontalDistance, _verticalDistance);
addWayPoint(p);
// Reset the waypoint variables.
_startTime = current;
_wayHorizontalDistance = 0;
_verticalDistance = 0;
}
// View the saved waypoints.
private void viewPreviousPoints()
{
PointScreen pointScreen = new PointScreen(_previousPoints, _resources);
pushScreen(pointScreen);
}
// Called by the framework when this application is losing focus.
protected void onExit()
{
if ( _locationProvider != null )
{
_locationProvider.reset();
_locationProvider.setLocationListener(null, -1, -1, -1);
}
}
/* Adds a new WayPoint and commits the set of saved waypoints
* to flash memory.
* @param p The point to add.
*/
/*package*/
synchronized static void addWayPoint(WayPoint p)
{
_previousPoints.addElement(p);
commit();
}
/* Removes a waypoint from the set of saved points and
* commits the modifed set to flash memory.
* @param p the point to remove
*/
/*package*/
synchronized static void removeWayPoint(WayPoint p)
{
_previousPoints.removeElement(p);
commit();
}
// Commit the waypoint set to flash memory.
private static void commit()
{
_store.setContents(_previousPoints);
_store.commit();
}
/* Implementation of the LocationListener interface.
*/
private class LocationListenerImpl implements LocationListener
{
// Members. --------------------------------------------------------------
private int captureCount;
// Methods. --------------------------------------------------------------
public void locationUpdated(LocationProvider provider, Location location)
{
if(location.isValid())
{
float heading = location.getCourse();
double longitude = location.getQualifiedCoordinates().getLongitude();
double latitude = location.getQualifiedCoordinates().getLatitude();
float altitude = location.getQualifiedCoordinates().getAltitude();
float speed = location.getSpeed();
// Horizontal distance.
float horizontalDistance = speed * _interval;
_horizontalDistance += horizontalDistance;
// Horizontal distance for this waypoint.
_wayHorizontalDistance += horizontalDistance;
// Distance over the current interval.
float totalDist = 0;
// Moving average grade.
for(int i = 0; i 0) _verticalDistance = _verticalDistance + altGain;
captureCount += _interval;
// If we’re mod zero then it’s time to record this data.
captureCount %= CAPTURE_INTERVAL;
// Information to display on the device.
StringBuffer sb = new StringBuffer();
sb.append("Longitude: " + longitude+ "\n");
sb.append("Latitude: " + latitude+ "\n");
sb.append("Altitude: " + altitude + " m\n");
sb.append("Heading relative to true north: " + heading + "\n");
sb.append("Speed : " + speed + +" m/s\n");
sb.append("Grade : ");
if(Float.isNaN(grade))
sb.append(" Not available");
else
sb.append(grade+" %");
GPSDemo.this.updateLocationScreen(sb.toString());
}
}
public void providerStateChanged(LocationProvider provider, int newState)
{
// No operation defined.
}
}
/* WayPoint describes a way point, a marker on a journey or point of interest.
* WayPoints are persistable.
* package
*/
static class WayPoint implements Persistable
{
public long _startTime;
public long _endTime;
public float _distance;
public float _verticalDistance;
public WayPoint(long startTime,long endTime,float distance,float verticalDistance)
{
_startTime=startTime;
_endTime=endTime;
_distance=distance;
_verticalDistance=verticalDistance;
}
}
}

Entradas relacionadas:
Un ejemplo de aplicación Java para BlackBerry – Parte I
Un ejemplo de aplicación Java para BlackBerry – Parte II
Un ejemplo de aplicación Java para BlackBerry – Parte III
Un ejemplo de aplicación Java para BlackBerry – Parte IV y final
Leyendo nuestro GPS desde Java con la JavaME Location API (JSR-179) – Parte I

Escrito por Javier Cancela

18 \18\UTC enero \18\UTC 2008 a 8:00

Seguir

Recibe cada nueva publicación en tu buzón de correo electrónico.