Implementation

For information about the design and functionality of the MIDlet, see section Design.

To create the MIDlet:

  1. Create the ArrowKeys.java class file.

  2. 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;
  3. 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 {
  4. 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);
        }
  5. 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);
        }
  6. 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() {}
  7. 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;
    
            }
        }
  8. 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();
        }
  9. 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;
        }
  10. 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;
        }
  11. 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("");
                }
            }
        }
    }