A. Ejercicios

A.1. Entornos de desarrollo

1.1. Abrir la herramienta ktoolbar de WTK 1.0.4. Abrir desde ella alguna de las aplicaciones de ejemplo que tenemos disponibles (UIDemo, demos o games). Probar a compilar estas aplicaciones y ejecutarlas en distintos emuladores (creando el paquete en caso necesario). Observar las diferencias que encontramos de unos emuladores a otros.

1.2. Vamos a hacer nuestra primera aplicación "Hola mundo" en J2ME. Para ello debemos:

a) Crear un nuevo proyecto con WTK 1.0.4. El proyecto se llamará PruebaAplicacion y el MIDlet principal será es.ua.j2ee.prueba.MIDletPrueba.

b) Una vez creado abriremos el proyecto desde Eclipse, crearemos la clase del MIDlet principal, e introduciremos en ella el siguiente código:

package es.ua.j2ee.prueba;

import javax.microedition.midlet.*; import javax.microedition.lcdui.*;

public class MIDletPrueba extends MIDlet {

protected void startApp() throws MIDletStateChangeException { Form f = new Form("Hola mundo"); Display d = Display.getDisplay(this); d.setCurrent(f); }

protected void pauseApp() { }

protected void destroyApp(boolean incondicional)
throws MIDletStateChangeException { } }

c) Guardar el fichero y desde WTK compilar y ejecutar la aplicación en emuladores, comprobando que funciona correctamente.

1.3. Añadir un fichero build.xml al proyecto en Eclipse para poderlo compilar y ejecutar desde este mismo entorno. Compilar y ejecutar desde este entorno.

1.4. Vamos a añadir recursos a nuestra aplicación. Mostraremos una imagen en la pantalla, introduciendo el siguiente código en el método startApp de nuestro MIDlet:

protected void startApp() throws MIDletStateChangeException {
    Form f = new Form("Hola mundo");
    try {
        f.append(Image.createImage("/logo.png"));
    } catch(Exception e) {}
   
    Display d = Display.getDisplay(this);
    d.setCurrent(f);
}

Para poder mostrar esta imagen deberemos añadirla como recurso a la suite. Añadir una imagen con nombre logo.png al directorio de recursos.

Compilar y ejecutar la aplicación para comprobar que la imagen se muestra correctamente. Utilizar para ello tanto ktoolbar como la herramienta ant desde Eclipse.

1.5. Ahora añadiremos sonido a la aplicación. Para ello deberemos utilizar la API multimedia que es una API adicional. Deberemos:

a) Incorporar la librería MMAPI a nuestro proyecto en Eclipse. Podemos localizar esta librería dentro del directorio de los emuladores que soporten esta API (normalmente en el subdirectorio lib). Podremos obtenerla por ejemplo de los emuladores de la Serie 60 de Nokia, o del MMEmulator de Sun.

b) Una vez hecho esto podremos utilizar esta API multimedia en el editor de Eclipse sin que nos muestre errores en el código. Modificaremos el código del MIDlet de la siguiente forma:

package es.ua.j2ee.prueba;

import javax.microedition.midlet.*; import javax.microedition.lcdui.*; import javax.microedition.media.*;

public class MIDletPrueba extends MIDlet {

protected void startApp() throws MIDletStateChangeException { Form f = new Form("Hola mundo"); try { f.append(Image.createImage("/logo.png")); } catch(Exception e) {}

try { Manager.playTone(80, 1000, 100); } catch(MediaException e) {} Display d = Display.getDisplay(this); d.setCurrent(f); }

protected void pauseApp() { }

protected void destroyApp(boolean incondicional)
throws MIDletStateChangeException { } }

c) Guardar y compilar desde WTK. Compilar seleccionando distintos emuladores distintos. ¿Con cuáles funciona y con cuáles obtenemos errores de compilación? ¿Por qué?

d) Modificar ahora el fichero build.xml para que compile utilizando esta API multimedia. Comprobar que compila y ejecuta correctamente el ejemplo.

1.6. Crear un proyecto con NetBeans. Utilizar la plantilla HelloMIDlet para crear el MIDlet principal de nuestra suite. Compilar y ejecutar el proyecto desde este entorno. Depurar la aplicación estableciendo un breakpoint al comienzo del método startApp.

A.2. Interfaz gráfica de alto nivel

2.1. Modificar el ejemplo "Hola mundo" de la sesión anterior para hacerlo parametrizable. Ahora en lugar de mostrar siempre el mensaje "Hola mundo", tomaremos el mensaje a mostrar del parámetro msg.bienvenida. Crear este parámetro dentro del fichero JAD, y leerlo dentro del MIDlet para mostrarlo como título del formulario.

2.2. Vamos a implementar una alarma utilizando alertas y temporizadores. En las plantillas de los ejercicios se proporciona una base para realizar esta aplicación, contenida en el directorio Alarma. Tenemos un formulario donde podemos establecer la fecha de la alarma y fijarla o anularla. Lo que deberemos hacer es:

a) Crear una tarea (TimerTask) que al ser ejecutada muestre una alerta de tipo alarma y reproduzca un sonido de aviso (utilizando la clase AlertType). Después de mostrarse esta alerta deberá volver a la pantalla actual.

b) Planificar la ejecución de esta alarma utilizando un temporizador (Timer). Esto lo haremos en el método commandAction como respuesta al comando de fijar alarma. También deberemos cancelar el temporizador en caso de que la alarma se anule.

2.3. Vamos a crear una pantalla sencilla para nuestra aplicación. Haremos el menú principal de nuestro juego, en el que deberemos tener las opciones Nuevo juego y Salir. Podemos encontrar la base de esta aplicación en el directorio AdivinaNumero de las plantillas de los ejercicios. Aquí tenemos definido el MIDlet principal, desde el cuál deberemos mostrar la pantalla que vamos a implementar.

a) ¿Qué tipo de displayable utilizaremos para realizar este menú?

b) Implementar esta pantalla encapsulando todo su contenido en una misma clase.

c) Añadir un comando que nos permita seleccionar la opción marcada del menú.

d) Incorporar un listener de comandos para dar respuesta a este comando de selección de opciones. Por ahora lo que haremos será mostrar una alerta que diga "Opcion no implementada todavia".

2.4. Ahora implementaremos el juego de adivinar un número del 1 al 100. Para ello partiremos de la base realizada en el ejercicio anterior.

La aplicación pensará un número aleatorio de 1 a 100, y se mostrará al usuario un cuadro de texto donde deberá introducir el número del que piensa que se trata. Una vez introducido, pulsará OK y entonces la aplicación le dirá si el número de demasiado alto, si es demasiado bajo o si ha acertado. En caso de que el número sea muy alto o muy bajo, volveremos al mismo cuadro de texto para volver a probar. Si ha acertado el juego finalizará, mostrando el número de intentos que ha necesitado y volviendo al menú principal.

Para implementar esta aplicación crearemos una nueva pantalla encapsulada en una clase de nombre EntradaTexto que será de tipo TextBox, donde el usuario introducirá el número. Al construir esta pantalla se deberá determinar un número aleatorio de 1 a 100, cosa que podemos hacer de la siguiente forma:

Random rand = new Random();
this.numero = Math.abs(rand.nextInt()) % 100 + 1;

Deberemos añadir un comando para que el usuario notifique que ha introducido el número. Como respuesta a este comando deberemos obtener el número que ha introducido el usuario y compararlo con el número aleatorio. Según si el número es menor, mayor o igual mostraremos una alerta con el mensaje correspondiente, y volveremos a la pantalla actual o al menú principal según si el usuario ha fallado o acertado respectivamente.

A.3. Interfaz gráfica de bajo nivel

3.1. Vamos a implementar una aplicación similar al juego que se conocía como "TeleSketch". Esta aplicación nos deberá permitir dibujar en la pantalla utilizando las teclas de los cursores.

La idea es dibujar en offscreen (en una imagen mutable), de forma que no se pierda el contenido dibujado. En cada momento conoceremos la posición actual del cursor, donde dibujaremos un punto (puede ser un círculo o rectángulo de tamaño reducido). Al pulsar las teclas de los cursores del móvil moveremos este cursor por la pantalla haciendo que deje rastro, y de esta manera se irá generando el dibujo.

Tenemos una plantilla en el directorio TeleSketch. Sobre esta plantilla deberemos realizar lo siguiente:

a) En el constructor de la clase deberemos crear una imagen mutable donde dibujar, con el tamaño del Canvas.

b) En el método actualiza deberemos dibujar un punto en la posición actual del cursor y llamar a repaint para repintar el contenido de la pantalla.

c) En paint deberemos volcar el contenido de la imagen offscreen a la pantalla.

d) Deberemos definir los eventos keyPressed y keyRepeated para mover el cursor cada vez que se pulsen las teclas arriba, abajo, izquierda y derecha. Podemos utilizar las acciones de juegos (game actions) para conocer cuáles son estas teclas.

3.2. Ampliar la aplicación anterior permitiendo cambiar el color y el grosor del lápiz. Para hacer esto podemos añadir una serie de comandos con un conjunto de colores y grosores preestablecidos.

3.3. Si dibujamos usando los eventos de repetición el cursor muchas veces se moverá muy lentamente. En lugar de utilizar este evento para dibujar podemos crear un hilo que ejecute un bucle infinito. Dentro de este hilo mientras la tecla siga pulsada se irá moviendo el cursor, de esta manera se actualizará con la frecuencia que nosotros queramos sin tener que esperar al evento de repetición. Sabremos que una tecla se mantiene pulsada desde que se invoca un evento keyPressed hasta que se invoca un evento keyReleased para la misma tecla.

3.4. Utilizar API de Nokia para dibujar en un canvas a pantalla completa.

A.4. Juegos

4.1. Implementaremos un juego básico con WTK 2.0. El primer paso que vamos a realizar es mostrar un sprite que se mueva por la pantalla. Tenemos la imagen coche.png, que utilizaremos como sprite. Crearemos el ciclo del juego, en el que leeremos la entrada del usuario, según ésta moveremos el sprite por la pantalla (en las direcciones izquierda, derecha, arriba y abajo), y actualizaremos los gráficos.

4.2. Vamos a añadir un fondo a la escena. En la imagen fondo.png tenemos una serie de elementos con los que construir el fondo de la carretera. Podemos utilizar el fichero datos donde tenemos codificado un posible escenario. De este fichero podremos leer el índice del elemento que se debe mostrar en cada celda del fondo. Se encuentra codificado de la siguiente forma:

<ancho:int> <alto:int>
<celda_1_1:byte> <celda_1_2:byte> ... <celda_1_ancho>
<celda_2_1:byte> <celda_2_2:byte> ... <celda_2_ancho>
...
<celda_alto_1:byte> <celda_alto_2:byte> ... <celda_alto_ancho>

Podremos leer estos datos utilizando un DataInputStream, o bien crear nuestro propio mosaico para el fondo. Dibujaremos este fondo como capa en la pantalla, y en cada iteración lo iremos desplazando hacia abajo para causar el efecto de que el coche avanza por la carretera.

4.3. Como tarea avanzada podemos añadir otros coches a la carretera, y detectar colisiones con nuestro coche y los demás coches, o con nuestro coche y el fondo. De esta forma podremos chocar con los bordes de la carretera, teniendo que evitar salirnos de estos márgenes. Para comenzar, cuando choquemos podemos mostrar simplemente una alerta que nos avise de que hemos chocado y se vuelva a iniciar el juego.

A.5. Multimedia

5.1. Vamos a crear un reproductor de video que reproduzca un video con formato 3GPP. Lo reproduciremos dentro de un canvas. Para ello añadiremos un comando Start al canvas que nos permita comenzar la reproducción del video.

5.2. Añadir un listener para conocer cuando comienza a reproducirse y cuando se detiene el video. Este listener deberá modificar los comandos disponibles en el canvas. Cuando comience a reproducirse eliminará el comando Start y añadirá un comando Stop que nos servirá para detenerlo. Cuando el video se detenga, eliminaremos el comando Stop y volveremos a añadir el comando Start.

5.3. Permitir realizar captura de imágenes (snapshots) del video. Para ello cuando comience a reproducirse añadiremos un comando Snap con el que podremos capturar un fotograma del video. Una vez capturado, detendremos el video y mostraremos la imagen en el canvas.

A.6. RMS

6.1. Vamos a implementar un almacén de notas. En el directorio Notas tenemos la base de esta aplicación. Vamos a ampliarla haciendo que estas notas se almacenen de forma persistente utilizando RMS. Para ello deberemos completar el código de la clase Notas de la siguiente forma:

a) En el constructor se debe abrir el almacén de registros de nombre RS_NAME (creándolo si es necesario) y cargando todas las notas que tenemos almacenadas en él. Podemos utilizar la deserialización definida en el objeto Nota para leerlos. Conforme leamos las notas las añadiremos al vector notas.

El índice de cada registro en el almacén nos servirá para luego poder modificar o eliminar dicha nota. Por esta razón deberemos guardarnos este valor en alguna parte. Podemos utilizar para ello el campo id de cada objeto Nota creado.

b) En eliminaNota deberemos eliminar las notas del almacén. Para ello deberemos obtener el id asociado a la nota y eliminar dicho registro del almacén.

c) En cambiaNota modificaremos una nota del almacén sobrescribiéndola con la nueva nota proporcionada. Para ello obtendremos el id correspondiente a la antigua nota, y en ese registro escribiremos la nueva nota utilizando la serialización del objeto Nota.

d) En nuevaNota añadiremos una nueva nota al almacén, utilizando la serialización del objeto Nota. Nos guardaremos el id del registro en el que hemos guardado la nota por si necesitamos modificarlo posteriormente.

6.2. Obtener la información de la cantidad de memoria ocupada y disponible a partir del objeto RecordStore. Deberemos hacer que los métodos getMem y getFree de la clase Notas devuelvan esta información.

6.3. Utilizar un listener para actualizar la lista de notas en memoria. Añadiendo este listener sobre el almacén de registros ya no necesitaremos modificar el vector de notas en los métodos eliminaNota, cambiaNota y nuevaNota, podemos usar estos métodos sólo para modificar el almacén, y actualizar el vector en memoria únicamente desde nuestro listener. De esta forma la lista de notas que se muestra en pantalla se actualizará siempre que se produzca un cambio en el almacén, aunque no haya sido producido por el usuario desde nuestro MIDlet.

A.7. Red

7.1. Vamos a acceder desde el móvil a nuestra tienda virtual. Mostraremos en el móvil una pantalla en la que aparecerá la lista de productos que hay disponibles en nuestra tienda. Al pulsar sobre cada uno de ellos nos mostrará información detallada sobre el producto.

Tenemos la aplicación base implementada en el directorio Tienda. Deberemos añadir en el método leeProductos de la clase ListaProductos el código necesario para leer la lista de productos de la red.

Para ello conectaremos a la URL donde tenemos el servlet de nuestra tienda y leeremos la información de los productos que nos envía en la respuesta. La información que se envía consiste en una serie de objetos Producto serializados. Para leerlos podremos deserializar objetos Producto del flujo de entrada hasta que se produzca una EOFException, indicándonos que se ha llegado al final del flujo.

7.2. En este ejercicio implementaremos una aplicación de chat para el móvil. Podemos encontrar el código base de la aplicación en el directorio Chat, sobre el que deberemos hacer lo siguiente:

a) Lo primero que se mostrará será una pantalla de login, donde el usuario deberá introducir el login con el que participar en el chat. Deberemos enviar este login al servidor para iniciar la sesión. Para ello abriremos una conexión con la URL del servlet proporcionando los siguientes parámetros:

?accion=login&id=<nick_del_usuario>

Si el login es correcto, el servidor nos devolverá un código de respuesta 200 OK. Además deberemos leer la cabecera URL-Reescrita, donde nos habrá enviado la URL rescrita que deberemos utilizar de ahora en adelante para mantener la sesión.

Esto deberemos hacerlo en el método login de la clase PantallaLogin. Este método deberá devolver la URL rescrita.

b) Una vez hemos entrado en el chat, utilizaremos la técnica de polling para obtener los mensajes escritos en el chat y mostrarlos en la pantalla. Utilizando la URL rescrita, conectaremos al servlet del chat proporcionando el siguiente parámetro:

?accion=lista

Esto nos devolverá como respuesta una serie de mensajes, codificados mediante un objeto DataOutputStream de la siguiente forma:

<nick1> <mensaje1>
<nick2> <mensaje2>
...
<nickN> <mensajeN>

De esta forma podremos utilizar un objeto DataInputStream para ir leyendo con el método readUTF las cadenas del nick y del texto de cada mensaje del chat:

String nick = dis.readUTF();
String texto = dis.readUTF();

Esto lo haremos dentro del método actualizaLista de la clase ListaMensajes. Añadiremos cada mensaje leído como un StringItem al formulario:

this.append(new StringItem("<" + nick + ">", texto));	 

Esto hará que el número de elementos del formulario vaya creciendo conforme llegan mensajes del chat. Para evitar que llegue a crecer demasiado, cosa que lo haría incómodo para la interfaz de los móviles, podemos eliminar los mensajes más antiguos cuando se rebase una cierta capacidad máxima:

if(this.size() >= CAPACIDAD) {
    this.delete(0);
}

c) Lo último que deberemos hacer es permitir enviar mensajes al chat. Para ello enviaremos los mensajes en el POST, a la URL rescrita proporcionando el siguiente parámetro:

?accion=enviar

Esto lo haremos en el método envia de la clase EnviarMensaje. El mensaje se deberá codificar en binario, escribiendo la cadena del mensaje con el método writeUTF de un objeto DataOutputStream. Si obtenemos una respuesta 200 OK el mensaje habrá sido enviado correctamente.