Developing Panorama MIDlet

Panorama.java

  1. Import the required classes. This example uses the common MIDlet and some LCDUI classes, plus a number of Mobile Sensor-specific imports. DeviceControl from Nokia UI API is used to keep the backlight on continuously.

    import java.io.IOException;
    
    import javax.microedition.io.Connector;
    import javax.microedition.lcdui.Display;
    import javax.microedition.lcdui.Image;
    import javax.microedition.midlet.MIDlet;
    import javax.microedition.midlet.MIDletStateChangeException;
    import javax.microedition.sensor.ChannelInfo;
    import javax.microedition.sensor.Data;
    import javax.microedition.sensor.DataListener;
    import javax.microedition.sensor.SensorConnection;
    import javax.microedition.sensor.SensorInfo;
    import javax.microedition.sensor.SensorManager;
    
    import com.nokia.mid.ui.DeviceControl;
    
  2. Create the main class and initial parameters to be used in the example. iCanvas is an instance of PanoramaCanvas, which is created later.

    public class Panorama extends MIDlet implements DataListener, PanoramaCanvas.ExitIf {
    
    	static final int BUFFER_SIZE = 3;
    	private static PanoramaCanvas iCanvas;
    	private static SensorConnection iConnection;
    	private static final String PHOTO_NAME = "/for_more_see_roundus_dot_com.jpg";
    	
     public Panorama() {
    	}
    
  3. Create the data listening method. Note the reference to setPosition method, which is created in PanoramaCanvas.

    	public void dataReceived( SensorConnection con, Data[] aData, boolean aMissed) {
    		iCanvas.setPosition(aData);
    	}
    
  4. Create a method for exiting the application. This is received from PanoramaCanvas.ExitIf method that was extended in step 1.

    	public void exit() {
    		try {
    			destroyApp( true );
    		}
    		catch (MIDletStateChangeException e)
    		{
    			e.printStackTrace();
    		}
    		notifyDestroyed();
    	}
    
  5. Create the mandatory MIDlet methods. startApp initializes PanoramaCanvas and sets it as the current Display, opens the sensor connection, and avoids backlight fadeout is by using DeviceControl.setLights.

    	protected void startApp() throws MIDletStateChangeException
    	{
    		DeviceControl.setLights( 0, 100 );
    		Display disp = Display.getDisplay( this );
    		try
    		{
    			iCanvas = new PanoramaCanvas( Image.createImage(PHOTO_NAME), this );
    			disp.setCurrent( iCanvas );
    			iConnection = openAccelerationSensor();
    			if (iConnection != null)
    				iConnection.setDataListener( this, BUFFER_SIZE );
    		}
    		catch (IOException e)
    		{
    			e.printStackTrace();
    		}
    	}
    
    	protected void pauseApp()
    	{
    	}
    
    	protected void destroyApp(boolean arg0) throws MIDletStateChangeException
    	{
    		try
    		{
    			if (iConnection!=null)
    				iConnection.close();
    		}
    		catch (IOException e)
    		{
    			e.printStackTrace();
    		}
    	}
    
  6. Create a method for opening the sensor connection. As devices based on the Symbian platform have two data return formats for acceleration sensors, the following method selects the correct one by its data format (integer in this case) with the ChannelInfo.TYPE_INT parameter.

    	private SensorConnection openAccelerationSensor(){
    
    		SensorInfo[] infos = SensorManager.findSensors("acceleration", null);
    		if (infos.length==0) return null;
    
    		// INT data type is preferred
    		int i=0;
    		for (i=0; i<infos.length && infos[i].getChannelInfos()[0].getDataType()!=ChannelInfo.TYPE_INT; i++);
    
    		try{
    			return i==infos.length ? (SensorConnection)Connector.open(infos[0].getUrl()):
    				(SensorConnection)Connector.open(infos[i].getUrl());
    		}catch (Exception e) {
    			return null;
    		}
    	}	
    }

PanoramaCanvas.java

PanoramaCanvas determines whether the image is in portrait or landscape orientation and calculates the image scrolling from the orientation changes of the device. It is also possible to traverse the image with the arrow key presses.

  1. Import the needed classes. PanoramaCanvas mostly uses the LCDUI Canvas and related classes.

    import java.io.IOException;
    
    import javax.microedition.lcdui.Canvas;
    import javax.microedition.lcdui.Graphics;
    import javax.microedition.lcdui.Image;
    import javax.microedition.sensor.Data;
    
    import com.nokia.mid.ui.DeviceControl;
    
  2. Set some required variables.

    public class PanoramaCanvas extends Canvas
    {
    
    	private static final int STEP = 5;
    	private static ExitIf iExit;
    	private static Image iImage;
    	private static int iImageHeight;
    	private static int iImageWidth;
    	private static int x = 0;
    	private static int y = 0;
    	private static int ii = 0;
    
    
  3. Continue setting values for the parameters. Add a repaint() method for drawing the changes on the screen.

    	public PanoramaCanvas( Image aImage, ExitIf aExit) throws IOException
    	{
    		super();
    		setFullScreenMode( true );
    
    		iExit = aExit;
    		iImage = aImage;
    		iImageHeight = iImage.getHeight();
    		iImageWidth = iImage.getWidth();
    		y=-(iImageHeight-getHeight())/ 2;
    
    		repaint();
    	}
    
  4. Add a method to use the received accelerometer values and react to them by moving the image on the screen. In this example, full screen mode is also forced as default.

    	void setPosition(Data[] aData){
    
    		if (ii++%100==0){
    			DeviceControl.setLights(0, 100);   //to keep backlight on
    			setFullScreenMode( true );
    		}
    
    		x = getX(x+=getX(aData));
    		y = getY(y+=getY(aData));
    
    		repaint();
    	}
    
  5. Add functions to deal with directional key presses. In this case, all key presses scroll the image as much as is set in the STEP variable. keyRepeated handles a similar function for held down keys.

    	/**
    	 * deal with any key presses
    	 */
    	protected void keyPressed(int keyCode) {
    		System.out.println("keyCode =" +keyCode);
    		switch(keyCode){
    		case -1:	//up
    		case Canvas.UP:
    			y = getY(y+=STEP);
    			break;
    		case -2: //down
    		case Canvas.DOWN:
    			y = getY(y+=-STEP);
    			break;
    		case -3:	//left
    		case Canvas.LEFT:
    			x = getX(x+=STEP);
    			break;
    		case -4: //right
    		case Canvas.RIGHT:
    			x = getX(x+=-STEP);
    			break;
    		default:
    		}
    		repaint();
    
    	}
    
    
    	protected void keyRepeated(int keyCode){
    		keyPressed(keyCode);
    
    	}
    
  6. Create the paint method to move the image on the Canvas.

    	protected void paint( Graphics g ){
    		g.drawImage( iImage, x , y, Graphics.TOP | Graphics.LEFT );
    		if( x + iImageWidth - getWidth() < 0 )
    			g.drawImage( iImage, x + iImageWidth, y, Graphics.TOP | Graphics.LEFT );
    
    	}
    
  7. Create a few additional functions. Here, pointerPressed is set to activate the exit so that the user can exit the MIDlet by pressing on the Selection Key or tapping on the screen and sizeChanged is called to center the image on the screen in cases where a size change occurs.

    	protected void pointerPressed(int x, int y) {iExit.exit();}
    
    	protected void sizeChanged(int w, int h) {
    		y=-(iImageHeight-getHeight())/ 2;
    		repaint();
    	}
    
  8. Detect image size and create the image scrolling functionality accordingly.

    	private int getX(Data[] aData){
    		int x_axis = 0;
    		boolean isPortrait = getHeight()>getWidth();
    		int index= isPortrait? 0: 1;
    
    		try{
    			for (int i=0; i<Panorama.BUFFER_SIZE; i++){
    				x_axis += aData[index].getIntValues()[0];
    			}
    			x_axis = (int)(x_axis/Panorama.BUFFER_SIZE);
    		}catch (IllegalStateException e) {
    			for (int i=0; i<Panorama.BUFFER_SIZE; i++){
    				x_axis += (int)aData[index].getDoubleValues()[0];
    			}
    			x_axis = (int)(x_axis/Panorama.BUFFER_SIZE);
    		}
    
    		return isPortrait?-x_axis%iImageWidth:x_axis%iImageWidth;
    	}
    
    	private int getY(Data[] aData){
    		int y_axis = 0;
    		boolean isPortrait = getHeight()>getWidth();
    		int index= isPortrait? 1: 0;
    
    		try{
    			for (int i=0; i<Panorama.BUFFER_SIZE; i++){
    				y_axis += aData[index].getIntValues()[0];
    			}
    			y_axis = (int)(y_axis/Panorama.BUFFER_SIZE);
    		}catch (IllegalStateException e) {
    			for (int i=0; i<Panorama.BUFFER_SIZE; i++){
    				y_axis += (int)aData[index].getDoubleValues()[0];
    			}
    			y_axis = (int)(y_axis/Panorama.BUFFER_SIZE);
    		}
    
    
    		return y_axis%iImageHeight;
    	}
    
    	private int getX(int x){
    		x = x>0?-iImageWidth+x:x;
    		return x % iImageWidth;
    	}
    
    	private int getY(int y){
    		y = y>0?0:y;	// upper limit
    		return Math.abs(y)>iImageHeight-getHeight()?getHeight()-iImageHeight:y;	//bottom limit
    	}
    
  9. Set the ExitIf interface to call exit(). ExitIf is used in Panorama.destroyApp() method.

    	static interface ExitIf
    	{
    		public void exit();
    	};