Archivo para enero 2008
Ampliado el plazo de presentación de aplicaciones para el Android Developer Challenge
Eso anuncia hoy el Android Developers Blog. ¿Los motivos? La gran cantidad de información que han obtenido de los usuarios, gracias a la cual habrá una nueva versión de la SDK en unas semanas.
Es decir, que Google ha logrado uno de los objetivos de este concurso: obtener una comunidad de betatesters significativa para un software que todavía no tiene hardware sobre el que ejecutarse. La nueva SDK arreglará algunos de los múltiples fallos que se han detectado desde que se liberó la primera versión, y todavía podrá recibir mejoras antes de que sus librerías sean definitivamente incorporadas a los primeros modelos de teléfono.
La estrategia opuesta a la de Apple, cuya SDK debería publicarse este Febrero. Veremos como es de estable.
Predicciones de Deloitte sobre el mercado de las telecomunicaciones en el 2008
Un breve apunte para señalar este estudio que, aunque no es más que otra serie de previsiones, proviene de una de las consultoras más importantes del mercado.

Se puede descargar aquí: Telecommunications Predictions 2008
Zumobi de serie con Windows Mobile
Parece que, a falta de un buen navegador estándar para móviles, las soluciones de navegación a través de widgets están de moda. Leo en MobileCrunch que Zumobi y Microsoft han alcanzado un acuerdo para que Zumobi se distribuya con Windows Mobile. La navegación por internet no es uno de los puntos fuertes de este sistema operativo de Microsoft, y Zumobi puede ser una buena alternativa a que Yahoo Go! o alguna opción similar les gane el terreno.
Comparativa entre S60 y Windows Mobile
Una entrada rápida para que no parezca que esta bitácora está difunta (sólo aletargada en una semana de mucho trabajo).
En Symbian-Guru han publicado dos entradas (10 Things S60 Needs To Learn From WM y 10 Things WM Needs To Learn From S60, ’10 Cosas Que S60 Necesita Aprender de WM’ y ’10 Cosas Que WM Necesita Aprender de S60′). Son dos listas de un especialista en Symbian tras probar durante unos meses un Windows Mobile 5 (un Qtek 9100, concretamente).
La primera de ellas provocó una lista alternativa de otro usuario: 10 Things Windows Mobile Can Learn from S60.
Mi opinión: para realizar las comparaciones hay que tener en cuenta el recorrido de ambos sistemas. S60 representa la evolución del móvil al móvil-ordenador, mientras que WM ha evolucionado de un ordenador de mano a un ordenador de mano con teléfono móvil. En cualquier caso, viendo las listas resulta claro que ambos sistemas tienen mucho que aprender del otro.
S60 en el Mobile World Congress
El Mobile World Congress que se celebrará en Barcelona del 11 al 14 de Febrero promete ser uno de los eventos más interesantes de este año para los amantes de la tecnología móvil. Circulan múltiples rumores sobre lo que podremos ver en él, como Google presentando un dispositivo Android o Telefónica anunciando el iPhone.
Conjeturas aparte, algunos de los asistentes ya han anunciado qué piensan mostrarnos en el congreso. Es el caso de S60, que piensa hacer una demostración en su stand de diversas tecnologías, como la esperada interfaz de usuario táctil, dispositivos con sensores de movimiento, y nuevas tecnologías de navegación web.
Confiemos en que estén a la altura de lo que se espera de ellos (y de la competencia).
Vía: See into S60
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
Problema del mes: concurso para programadores de S60
Problem of the month es el nombre que han puesto en Nokia a este desafío mensual para programadores S60 que comienza hoy.
Mientras escribo esto todavía no hay más detalles en la página, pero uno de los blogs de Forum Nokia publica el enunciado de Enero:
Problem for January:S60 supports multiple different runtimes and programming languages. The aim in this months competion is to come up with the glue that combines different runtimes. We’re asking you to design and implement a framework that unifies the S60 runtimes. The goal is intercommunication of runtimes (f.ex. Symbian C++, Java, Flash, Python, Web Runtimes, you name it…). With this kind of runtime interaction it is possible to access totally new features and re-use existing solutions in a manner that is unheard of. Please provide us with your framework implementation and also atleast one working example of runtimes interworking. If you don’t have time to implement all the needed parts let us know your ideas and planned design to score more points in the contest.
En resumen: diseñar un framework capaz de intercomunicar aplicaciones nativas con JavaME, o aplicaciones Flash con aplicaciones Python, … unificando todos los runtimes de S60. Una cosa “sencilla” para empezar.
Como la página del concurso está aún vacía, no sé cuáles serán los premios ni las condiciones generales del concurso. Suena todo a cosa improvisada, así que veremos cuál es la aceptación de esta iniciativa.
Tutoriales de Java ME en symbianrecources.com
Symbianresources.com ha actualizado su lista de tutoriales Java ME. Symbianresources.com es un sitio de la Universidad de Ciencias Aplicadas de Hagenbert, en Austria, y publica tutoriales y ejercicios sobre programación de sistemas Symbian, tanto en Java como en C++. El material está sacado de los cursos impartidos en dicha universidad.
Un ejemplo de aplicación Java para BlackBerry – Parte IV y final
Para la gestión de los waypoints vamos a crear una nueva ventana. Para ello crearemos una nueva clase en el archivo PointScreen.java.
La clase PointScreen
public class PointScreen extends MainScreen implements ListFieldCallback, GPSDemoResResource
{
private Vector _points;
private ListField _listField;
private ResourceBundle _resources;
PointScreen hereda de MainScreen, la clase que proporciona la funcionalidad estándar de las ventanas de las aplicaciones BlackBerry. La ventana contendrá una referencia al ResourceBundle para la internacionalización de los textos, un objeto Vector que almacenará los waypoints y un objeto ListField, que se encarga de mostrar en pantalla una lista vertical de items seleccionables. La clase PointScreen debe implementar ListFieldCallback para manejar los eventos del ListField.
public PointScreen(Vector points, ResourceBundle resources)
{
_resources = resources;
LabelField title = new LabelField(resources.getString(GPSDEMO_POINTSCREEN_TITLE),
LabelField.ELLIPSIS | LabelField.USE_ALL_WIDTH);
setTitle(title);
_points = points;
_listField = new ListField();
_listField.setCallback(this);
add(_listField);
reloadWayPointList();
}
private void reloadWayPointList()
{
// Refreshes wayPoint list on screen.
_listField.setSize(_points.size());
}
El constructor recibe un objeto Vector con los waypoints a mostrar, así como la referencia al ResourceBundle. Establece el título de la ventana (los parámetros del constructor del LabelField indican que se usa todo el ancho de la pantalla, y que si no hay sitio para el texto se acorte con puntos suspensivos), crea el objeto ListField y establece al objeto ventana como manejador de los eventos, añade el ListField a la ventana y llama al método reloadWayPointList().
Este método establece el número de objetos en el ListField, provocando que el mismo se redibuje.
La interfaz ListFieldCallback
public void drawListRow(ListField listField, Graphics graphics, int index, int y, int width)
{
if ( listField == _listField && index < _points.size())
{
String name = _resources.getString(GSPDEMO_POINTSCREEN_LISTFIELD_ROWPREFIX) + index;
graphics.drawText(name, 0, y, 0, width);
}
}
El método drawListRow se invoca cada vez que es necesario redibujar el una fila del control ListField. Tras cerciorarnos de que el control está creado y de que la fila a dibujar existe, nos limitamos a sacar el texto de ResourceBundle (en este caso el texto es “Waypoint“) y añadirle el índice que tiene en el Vector. Luego dibujamos el texto en su posición correspondiente.
public Object get(ListField listField, int index)
{
if ( listField == _listField )
{
// If index is out of bounds an exception is thrown,
// but that’s the desired behavior in this case.
return _points.elementAt(index);
}
return null;
}
El método get permite recuperar un objeto del ListField a partir de su índice. Está implementado en el ejemplo pese a que no se usa.
public int getPreferredWidth(ListField listField)
{
// Use the width of the current LCD.
return Graphics.getScreenWidth();
}
public int indexOfList(ListField listField, String prefix, int start)
{
return -1; // Not implemented.
}
Los otros dos métodos de la interfaz devuelven el ancho preferido del control (que será el ancho de la pantalla) y el índice del primer objeto que comience por el prefijo indicado (este método no está implementado).
El menú
Para el menú, se crea una clase para cada una de los dos opciones que queremos añadir:
private class ViewPointAction extends MenuItem
{
private int _index;
public ViewPointAction( int index )
{
super(PointScreen.this._resources.getString(GPSDEMO_POINTSCREEN_MENUITEM_VIEW), 100000, 10);
_index = index;
}
public void run()
{
ViewScreen screen = new ViewScreen( (WayPoint)_points.elementAt(_index), _index, _resources );
UiApplication.getUiApplication().pushModalScreen( screen );
}
}
private class DeletePointAction extends MenuItem
{
private int _index;
public DeletePointAction( int index )
{
super(PointScreen.this._resources.getString(GPSDEMO_POINTSCREEN_MENUITEM_DELETE), 100000, 10);
_index = index;
}
public void run()
{
GPSDemo.removeWayPoint((WayPoint)_points.elementAt(_index));
}
}
protected void makeMenu(Menu menu, int instance)
{
if( _points.size() > 0 )
{
ViewPointAction viewPointAction = new ViewPointAction( _listField.getSelectedIndex() );
menu.add( viewPointAction );
menu.addSeparator();
DeletePointAction deletePointAction = new DeletePointAction( _listField.getSelectedIndex() );
menu.add( deletePointAction );
}
super.makeMenu(menu, instance);
}
Las dos clases heredan de MenuItem e implementan el método run que se ejecutará al invocar la opción de menú correspondiente. El motivo para crear las clases es almacenar, en el momento de creación de las instancias, el índice del elemento del ListField seleccionado al invocar el menú. No es la forma más óptima pero sí la más explícita.
Cada vez que pulsamos el botón de menú para que este se despliegue, se invoca el método makeMenu que construye el mismo. En él creamos los objetos ViewPointAction y DeletePointAction que sirven de opciones de menú para ver y borrar los waypoints. Así, cada vez que invocamos el menú las opciones del mismo hacen referencia al waypoint seleccionado en el ListField.
La ejecución de DeletePointAction se limitá a eleminar el waypoint de la colección (lo hace el método removeWayPoint de la clase GPSDemo). ViewPointAction muestra la información del waypoint seleccionado.
La clase ViewScreen
La información del waypoint se muestra en un nuevo tipo de ventana, la cual se invoca de forma modal a través de pushModalScreen.
private static class ViewScreen extends MainScreen
{
private ResourceBundle _resources;
private MenuItem _cancel;public ViewScreen(WayPoint point, int count, ResourceBundle resources)
{
super();
_resources = resources;
LabelField title = new LabelField(resources.getString(GPSDEMO_VIEWSCREEN_TITLE) + count,
LabelField.ELLIPSIS | LabelField.USE_ALL_WIDTH);
setTitle(title);
Date date = new Date(point._startTime);
String startTime = date.toString();
date = new Date(point._endTime);
String endTime = date.toString();
float avgSpeed = point._distance/(point._endTime – point._startTime);
add(new BasicEditField(resources.getString(GPSDEMO_VIEWSCREEN_STARTFIELD), startTime, 30, Field.READONLY));
add(new BasicEditField(resources.getString(GPSDEMO_VIEWSCREEN_ENDFIELD), endTime, 30, Field.READONLY));
add(new BasicEditField(resources.getString(GPSDEMO_VIEWSCREEN_HORIZONTALDISTANCEFIELD), Float.toString(point._distance), 30, Field.READONLY));
add(new BasicEditField(resources.getString(GPSDEMO_VIEWSCREEN_VERTICALDISTANCEFIELD), Float.toString(point._verticalDistance), 30, Field.READONLY));
add(new BasicEditField(resources.getString(GPSDEMO_VIEWSCREEN_AVESPEEDFIELD), Float.toString(avgSpeed), 30, Field.READONLY));
}
El constructor recibe el waypoint y el número que lo identifica (su índice en la colección), construye las fechas, calcula la velocidad y muestra los datos en controles BasicEditField.
El menú de esta ventana sólo tiene un elemento Cancel. En él se llama a popScreen con la instancia de la ventana, ya que al ser una ventana modal no puede permanecer en el stack.
El código fuente de PointScreen.java
/*
* PointScreen.java
*
* 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 com.rim.samples.docs.resource.*;
import net.rim.device.api.i18n.*;
import javax.microedition.io.*;
import java.util.*;
import java.io.*;
import javax.microedition.location.*;
import com.rim.samples.docs.gpsdemo.GPSDemo.WayPoint;
import com.rim.samples.docs.resource.*;
/*
* PointScreen is a screen derivative that renders the saved WayPoints.
*/
public class PointScreen extends MainScreen implements ListFieldCallback, GPSDemoResResource
{
private Vector _points;
private ListField _listField;
private ResourceBundle _resources;
public PointScreen(Vector points, ResourceBundle resources)
{
_resources = resources;
LabelField title = new LabelField(resources.getString(GPSDEMO_POINTSCREEN_TITLE),
LabelField.ELLIPSIS | LabelField.USE_ALL_WIDTH);
setTitle(title);
_points = points;
_listField = new ListField();
_listField.setCallback(this);
add(_listField);
reloadWayPointList();
}
private void reloadWayPointList()
{
// Refreshes wayPoint list on screen.
_listField.setSize(_points.size());
}
// ListFieldCallback methods ------------------------------------------------
public void drawListRow(ListField listField, Graphics graphics, int index, int y, int width)
{
if ( listField == _listField && index < _points.size())
{
String name = _resources.getString(GSPDEMO_POINTSCREEN_LISTFIELD_ROWPREFIX) + index;
graphics.drawText(name, 0, y, 0, width);
}
}
public Object get(ListField listField, int index)
{
if ( listField == _listField )
{
// If index is out of bounds an exception is thrown,
// but that’s the desired behavior in this case.
return _points.elementAt(index);
}
return null;
}
public int getPreferredWidth(ListField listField)
{
// Use the width of the current LCD.
return Graphics.getScreenWidth();
}
public int indexOfList(ListField listField, String prefix, int start)
{
return -1; // Not implemented.
}
// Menu items. ---------------------------------------------------------------
private class ViewPointAction extends MenuItem
{
private int _index;
public ViewPointAction( int index )
{
super(PointScreen.this._resources.getString(GPSDEMO_POINTSCREEN_MENUITEM_VIEW), 100000, 10);
_index = index;
}
public void run()
{
ViewScreen screen = new ViewScreen( (WayPoint)_points.elementAt(_index), _index, _resources );
UiApplication.getUiApplication().pushModalScreen( screen );
}
}
private class DeletePointAction extends MenuItem
{
private int _index;
public DeletePointAction( int index )
{
super(PointScreen.this._resources.getString(GPSDEMO_POINTSCREEN_MENUITEM_DELETE), 100000, 10);
_index = index;
}public void run()
{
GPSDemo.removeWayPoint((WayPoint)_points.elementAt(_index));
}
}
protected void makeMenu(Menu menu, int instance)
{
if( _points.size() > 0 )
{
ViewPointAction viewPointAction = new ViewPointAction( _listField.getSelectedIndex() );
menu.add( viewPointAction );
menu.addSeparator();
DeletePointAction deletePointAction = new DeletePointAction( _listField.getSelectedIndex() );
menu.add( deletePointAction );
}
super.makeMenu(menu, instance);
}
/**
* Renders a particular Waypoint.
*/
private static class ViewScreen extends MainScreen
{
private ResourceBundle _resources;
private MenuItem _cancel;
public ViewScreen(WayPoint point, int count, ResourceBundle resources)
{
super();
_resources = resources;
LabelField title = new LabelField(resources.getString(GPSDEMO_VIEWSCREEN_TITLE) + count,
LabelField.ELLIPSIS | LabelField.USE_ALL_WIDTH);
setTitle(title);
Date date = new Date(point._startTime);
String startTime = date.toString();
date = new Date(point._endTime);
String endTime = date.toString();
float avgSpeed = point._distance/(point._endTime - point._startTime);
add(new BasicEditField(resources.getString(GPSDEMO_VIEWSCREEN_STARTFIELD), startTime, 30, Field.READONLY));
add(new BasicEditField(resources.getString(GPSDEMO_VIEWSCREEN_ENDFIELD), endTime, 30, Field.READONLY));
add(new BasicEditField(resources.getString(GPSDEMO_VIEWSCREEN_HORIZONTALDISTANCEFIELD), Float.toString(point._distance), 30, Field.READONLY));
add(new BasicEditField(resources.getString(GPSDEMO_VIEWSCREEN_VERTICALDISTANCEFIELD), Float.toString(point._verticalDistance), 30, Field.READONLY));
add(new BasicEditField(resources.getString(GPSDEMO_VIEWSCREEN_AVESPEEDFIELD), Float.toString(avgSpeed), 30, Field.READONLY));
}
private class CancelMenuItem extends MenuItem
{
public CancelMenuItem()
{
// Reuse an identical resource below.
super(ViewScreen.this._resources, GPSDEMO_OPTIONSSCREEN_MENUITEM_CANCEL, 300000, 10);
}
public void run()
{
UiApplication uiapp = UiApplication.getUiApplication();
uiapp.popScreen(ViewScreen.this);
}
};
protected void makeMenu( Menu menu, int instance )
{
if ( _cancel == null )
_cancel = new CancelMenuItem(); // Create on demand.
menu.add(_cancel);
super.makeMenu(menu, instance);
}
}
}
A partir de aquí, la descripción del código continúa en la serie Leyendo nuestro GPS desde Java con la JavaME Location API (JSR-179).
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
Leyendo nuestro GPS desde Java con la JavaME Location API (JSR-179) – Parte I
