For information about the design and functionality of the MIDlet, see section Design.
To create the MIDlet:
Create the ArrowKeys.java
class file.
Import the required classes and packages.
import java.io.IOException; import javax.microedition.io.Connector; import javax.microedition.lcdui.*; import javax.microedition.midlet.MIDlet; import javax.microedition.sensor.ChannelInfo; import javax.microedition.sensor.Data; import javax.microedition.sensor.DataListener; import javax.microedition.sensor.SensorInfo; import javax.microedition.sensor.SensorConnection; import javax.microedition.sensor.SensorManager;
Set ArrowKeys
to extend MIDlet
and implement CommandListener
and DataListener
. MIDlets use the CommandListener
interface to receive high-level UI events from the platform, and
the DataListener
interface to receive data from the
sensors.
public class ArrowKeys extends MIDlet implements CommandListener, DataListener {
Create the required variables and the ArrowKeys
class constructor.
private static final int BUFFER_SIZE = 1; private static final boolean IS_TRIGGERING_EVENT_ALWAYS = false; private static StringItem arrow; private static int exEvent = -1; private static Command exitCommand = new Command("Exit", Command.EXIT, 1); private SensorConnection sensor; public ArrowKeys() { Form form = new Form("ArrowKeys"); form.addCommand(exitCommand); form.setCommandListener(this); arrow = new StringItem("Direction",""); form.append(arrow); Display.getDisplay(this).setCurrent(form); }
Use the startApp
method to call the openSensor
connection,
save the SensorConnection
object in the sensor
class variable, and finally attach the whole class to the sensor
as a DataListener
. The result of this method is that
the ArrowKeys.dataReceived
method is called every
time the sensor has new data.
public void startApp() { sensor = openSensor(); if (sensor==null) return; sensor.setDataListener(this, BUFFER_SIZE); }
Add the rest of the mandatory lifecycle methods. When the MIDlet
stops, the sensor connection is closed with the close
method.
public void destroyApp(boolean par) { sensor.removeDataListener(); try{ sensor.close(); }catch(IOException ioe){ ioe.printStackTrace(); } } public void pauseApp() {}
Use the openSensor
method to open the connection to the sensor.
It starts by looking for an accelerometer sensor and returns if it
cannot find one. After the method has an array of matching sensors,
it looks through them for one that returns an integer. If it cannot
find one, it selects the first one in the array. If the method throws
an exception when trying to open the connection, it returns null
.
private SensorConnection openSensor(){ SensorInfo infos[] = SensorManager.findSensors("acceleration", null); if (infos.length == 0) return null; try{ return (SensorConnection)Connector.open(infos[0].getUrl()); }catch(SecurityException se){ se.printStackTrace(); return null; } catch(IOException ioe){ ioe.printStackTrace(); System.out.println("Couldn't open sensor : " + infos[0].getUrl()+"!"); return null; } catch(IllegalArgumentException iae) { iae.printStackTrace(); return null; } }
Assign the exit command to close the MIDlet and set the sensor connection to be closed.
public void commandAction(Command command, Displayable screen) { // exit command if (command == exitCommand){ setStopped(true); destroyApp(false); notifyDestroyed(); } }
Add a method to mark the sensor connection to be closed when the MIDlet is shut down.
private synchronized void setStopped(boolean isStopped){ notify(); }
The getActionKey
method is used to calculate which key the x
and y axes correspond to.
private static int getActionKey(double axis_x, double axis_y){ // axis_x: LEFT or RIGHT if (Math.abs(axis_x)>Math.abs(axis_y)){ return axis_x<0?Canvas.RIGHT:Canvas.LEFT; } // axis_y: UP or DOWN return axis_y<0?Canvas.UP:Canvas.DOWN; }
The data2actionEvents
method transforms the data array the sensor
sends out into an array of key strokes. It uses the getActionKey
method to determine which input corresponds to which key.
private static int[] data2actionEvents(Data[] data){ ChannelInfo cInfo = data[0].getChannelInfo(); boolean isInts = cInfo.getDataType() == ChannelInfo.TYPE_INT? true: false; int[] events = new int[BUFFER_SIZE]; if (isInts){ int[][] ints = new int[2][BUFFER_SIZE]; for (int i=0; i<2; i++){ ints[i] = data[i].getIntValues(); } for (int i=0; i<BUFFER_SIZE; i++){ events[i] = getActionKey(ints[0][i], ints[1][i]); } return events; } double[][] doubles = new double[2][BUFFER_SIZE]; for (int i=0; i<2; i++){ doubles[i] = data[i].getDoubleValues(); } for (int i=0; i<BUFFER_SIZE; i++){ events[i] = getActionKey(doubles[0][i], doubles[1][i]); } return events; }
When the sensor
has new data, it calls the dataReceived
method. The dataReceived
method in turn calls the data2actionEvents
method, which translates the data into an array of integers, corresponding
to different key strokes. The dataReceived
method
then changes the text on the screen according to the key strokes.
public void dataReceived(SensorConnection sensor, Data[] d, boolean isDataLost) { int[] events = data2actionEvents(d); for (int i=0; i<BUFFER_SIZE; i++){ if (events[i] == exEvent && !IS_TRIGGERING_EVENT_ALWAYS) continue; exEvent = events[i]; switch(events[i]){ case Canvas.UP: arrow.setText("^"); break; case Canvas.DOWN: arrow.setText("v"); break; case Canvas.LEFT: arrow.setText("<"); break; case Canvas.RIGHT: arrow.setText(">"); break; default: arrow.setText(""); } } } }