Implementing the ArrowKeys MIDlet

This example MIDlet consists of only one class, called ArrowKeys. For complete source code, see section Sensor API source code.

  1. Create the ArrowKeys class file

  2. Import the required classes. This example uses the four main Sensor API classes required for opening and using sensors:

    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. Create the main class and initial parameters to be used in the example.

    public class ArrowKeys extends MIDlet implements CommandListener, DataListener {
        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 static boolean isStopped = false;
    
  4. Add an exit command, a command listener, and the StringItem that will be set to indicate the device orientation.

        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. Open the sensor connection inside the startApp() method. The openSensor() method used here is defined at a later time. The sensor connection stays opened as long as the MIDlet is running (isStopped returns false). When the MIDlet stops, the sensor connection is closed with the close() method.

    	public void startApp() {
    		SensorConnection sensor = openSensor();
    		if (sensor==null) return;
    		sensor.setDataListener(this, BUFFER_SIZE);
    		while(!isStopped()){
    			try{
    				wait();
    			}catch(InterruptedException ie){}
    		}
    		sensor.removeDataListener();
    		try{
    			sensor.close();
    		}catch(IOException ioe){
    			ioe.printStackTrace();
    		}
    	}
  6. Add the rest of the mandatory methods.

    	public void destroyApp(boolean par) {}
    
    	public void pauseApp() {}
  7. Create a method to open the sensor connection. This is called in startApp().

    	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 methods to mark the sensor connection to be closed when the MIDlet is shut down. These are used in the startApp() method.

    	private static synchronized boolean isStopped() {
    		return isStopped;
    	}
    
    	private synchronized void setStopped(boolean isStopped){
    		ArrowKeys.isStopped = isStopped;
    		notify();
    	}
  9. Create a method to compare the absolute values of axis_x and axis_y. The comparison indicates whether the device is more tilted to X or Y axis direction.

    • x defines LEFT-RIGHT axis

    • y defines UP-DOWN axis

    	/**
    	 * The method returns an action event (UP,DOWN,LEFT,RIGHT) that
    	 * corresponds to the given axis x and y accelerations.
    	 * @param axis_x
    	 * @param axis_y
    	 * @return the corresponding action key
    	 */
    	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. Create a method to listen and parse the accelerometer values. In this example, the data2actionEvents() method distinguishes between the two accelerometer modes (raw values are returned as an integer and m/s^2 values are returned as a double). If the received data does not conform to ChannelInfo.TYPE_INT, it is assumed to be double.

    	/**
    	 * The method returns action events that
    	 * corresponds to the given acceleration data.
    	 * Valid return values are:
    	 * Canvas.UP
    	 * Canvas.DOWN
    	 * Canvas.RIGHT
    	 * Canvas.LEFT
    	 * @param data the acceleration data
    	 * @return the action event array
    	 */
    
    	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. Use the dataReceived() method to listen to accelerometer events. The data is received in the returned value events from data2actionEvents() method. In data2actionEvents(), the accelerometer values are mapped to key presses (Canvas.UP, Canvas.DOWN, Canvas.LEFT and Canvas.RIGHT). To illustrate the key press values, they are mapped onto the arrow StringItem.

    	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("");
    			}
    		}
    	}
    }