For information about the design and functionality of the MIDlet, see section Design.
The MIDlet consists of two classes:
Cottage360
is the MIDlet main class
PanoramaCanvas
implements the picture scrolling
To implement the Cottage360
class:
Create the Cottage360.java
class file.
Import the required classes.
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;
Set Cottage360
to extend MIDlet
and implement DataListener
and the PanoramaCanvas.ExitIf
interface. MIDlets use the DataListener
interface
to receive data from the sensors.
public class Cottage360 extends MIDlet implements DataListener, PanoramaCanvas.ExitIf {
Create the required
variables and the Cottage360
class constructor.
static final int BUFFER_SIZE = 3; private static PanoramaCanvas iCanvas; private static SensorConnection iConnection; private static final String PHOTO_NAME = "/cottage360.jpg"; public Cottage360() { }
The dataReceived
method is required by the DataListener
interface. The method is called every time the sensor has data to
send. In this class, the method only sends the data forward to the PanoramaCanvas
class.
public void dataReceived( SensorConnection con, Data[] aData, boolean aMissed) { iCanvas.setPosition(aData); }
The exit
method exits the MIDlet. It is received from the PanoramaCanvas.ExitIf
method, which the Cottage360
class extends. The exit
method calls the destroyApp
method. The pauseApp
method
is included as part of the MIDlet lifecycle requirements.
public void exit() { try { destroyApp( true ); } catch (MIDletStateChangeException e) { e.printStackTrace(); } notifyDestroyed(); } protected void destroyApp(boolean arg0) throws MIDletStateChangeException { try { if (iConnection!=null) iConnection.close(); } catch (IOException e) { e.printStackTrace(); } } protected void pauseApp() { }
Use the startApp
method to set the device display to stay on, store
the current display in a variable, and create a new PanoramaCanvas
instance. The method sets the current display to show the canvas
and opens a new sensor connection by calling the openAccelerationSensor
method. If the sensor connection is successful, the method registers
the Cottage360
class as a listener to that sensor.
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(); } }
The openAccelerationSensor
method first queries the SensorManager
to find all the accelerometer
sensors. If it cannot find any, it returns null
.
As devices based on the Symbian platform have two data return formats
for acceleration sensors, the method selects the correct sensor based
on its data format (integer in this case) using the ChannelInfo.TYPE_INT
parameter. If the method cannot find a sensor that returns integers,
it selects the first one in the array of found sensors. If the method
is unable to open the connection to the sensor, it returns null
; otherwise, it returns a SensorConnection
object.
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; } } }
To implement the PanoramaCanvas
class:
Create the PanoramaCanvas.java
class file.
Import the required 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;
Set PanoramaCanvas
to extend Canvas
. Create
the 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;
The PanoramaCanvas
class constructor sets the screen to full
screen mode and saves some needed variables.
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(); }
The setPosition
method is used to set the new position of the
picture. It takes the accelerometer data as a parameter, and uses
the getX
and getY
methods to calculate
the new coordinates.
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(); }
As the PanoramaCanvas
class extends the Canvas
class, it must implement the keyPressed
and keyRepeated
methods. The keyPressed
method takes a key code as a parameter, and based
on what key was pressed, it calculates the new x or y coordinates.
The keyRepeated
method simply calls the keyPressed
method with the received key code.
protected void keyPressed(int 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); }
The paint
method draws the picture on the screen. After the
x or y coordinates have changed, you must call the repaint
method to update the changes on the screen.
protected void paint( Graphics g ){ g.setColor(0, 0, 0); g.fillRect(0, 0, getWidth(), getHeight()); g.drawImage( iImage, x , y, Graphics.TOP | Graphics.LEFT ); if( x + iImageWidth - getWidth() < 0 ) g.drawImage( iImage, x + iImageWidth, y, Graphics.TOP | Graphics.LEFT ); }
The pointerPressed
method is set to activate the exit so that
the user can exit the MIDlet by pressing the Selection Key or tapping
on the screen. The sizeChanged
method is called to
center the picture on the screen after a size change.
protected void pointerPressed(int x, int y) {iExit.exit();} protected void sizeChanged(int w, int h) { y=-(iImageHeight-getHeight())/ 2; repaint(); }
The getX
and getY
methods detect the picture
size and create the picture scrolling functionality.
private int getX(Data[] aData){ int x_axis = 0; boolean isPortrait = getHeight()>getWidth(); int index= isPortrait? 0: 1; try{ for (int i=0; i<3; i++){ x_axis += aData[index].getIntValues()[0]; } x_axis = (int)(x_axis/3); }catch (IllegalStateException e) { for (int i=0; i<3; i++){ x_axis += (int)aData[index].getDoubleValues()[0]; } x_axis = (int)(x_axis/3); } 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<3; i++){ y_axis += aData[index].getIntValues()[0]; } y_axis = (int)(y_axis/3); }catch (IllegalStateException e) { for (int i=0; i<3; i++){ y_axis += (int)aData[index].getDoubleValues()[0]; } y_axis = (int)(y_axis/3); } 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 //This is to center the image if the screen is higher than the image. if (iImageHeight<getHeight()) return -(iImageHeight-getHeight())/2; return Math.abs(y)>iImageHeight-getHeight()?(getHeight()-iImageHeight):y; //bottom limit }
The ExitIf
interface calls the exit. The interface is used in
the Cottage360.destroyApp
method.
static interface ExitIf { public void exit(); }; }