Implementation

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

The SvgCanvas2 class handles the setting up and drawing of the pre-generated static SVG image. The constructor of the class first sets up the 2D rendering context by using a ScalableGraphics object:

    public SvgCanvas2(boolean suppressKeyEvents) {
        super(suppressKeyEvents);

        // *** setup an instance of ScalableGraphics
        sg = ScalableGraphics.createInstance();
        sg.setRenderingQuality(sg.RENDERING_QUALITY_HIGH);

Rendering quality is set to RENDERING_QUALITY_HIGH, which is also the default setting on Series 40 and Nokia Asha software platform devices. This quality level is dependent on the device and the MIDlet code, and is mapped to definitions in the SVG specification (shape, text, image, and color rendering). If you are concerned about portability, check the specifications of the devices the MIDlet is targeted for, since the results are different depending on device capabilities.

The constructor then creates an SVGImage using the content2.svg file as a source, and sets the image dimensions to fit the device screen using the SVGImage.setViewportWidth and SVGImage.setViewportHeight methods:

        // *** load an svg image from a file
        try {
            InputStream svgStream = getClass().getResourceAsStream("content2.svg");
            svgImage = (SVGImage) (SVGImage.createImage(svgStream, null));

            // ** set the width and height of the document to match the screen capabilities
            svgImage.setViewportWidth(getWidth());
            svgImage.setViewportHeight(getHeight());

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

The GameCanvas.paint method handles the painting of the GameCanvas. The ScalableGraphics object renders the SVGImage set up in the constructor:

    public void paint(Graphics g) {
        // *** clear the display
        g.setColor(255, 255, 255);
        g.fillRect(0, 0, getWidth(), getHeight());

        // *** render the SVG image
        sg.bindTarget(g);
        sg.setTransparency(1f);
        sg.render(0, 0, svgImage);
        sg.releaseTarget();
    }

The ScalableGraphics.bindTarget method sets where the SVG image is rendered, that is, the target Graphics object. Transparency is set to 1.0, which means fully opaque. The image is drawn in the top-left corner of the GameCanvas, at coordinates 0,0.

When the user selects Zoom In from the options menu, the following code is run:

    public void zoomIn() {
        SVGSVGElement myEl = (SVGSVGElement) (svgImage.getDocument().getDocumentElement());
        myEl.setCurrentScale(myEl.getCurrentScale() * 1.2f);
        repaint();
    }

This modifies the scale property of the SVG document and redraws the GameCanvas, causing the SVG image to be displayed larger. The other menu options for manipulating the image work similarly.

The following code, which is run when the user selects Restore View from the options menu, restores the original zoom and rotation settings:

    public void restoreView() {
        SVGSVGElement myEl = (SVGSVGElement) svgImage.getDocument().getDocumentElement();
        myEl.setCurrentRotate(0);
        myEl.setCurrentScale(1);
        SVGPoint origin = myEl.getCurrentTranslate();
        origin.setX(0);
        origin.setY(0);
        repaint();
    }

Implementation specific to the Series 40 full touch version of the MIDlet

In the LoadStaticMidlet class, map the Move and Zoom commands to action button 1:

/**
         * IconCommand is supported from Java Runtime 2.0.0 for Series 40
         * onwards. Creates a new IconCommand Zoom with the given label,
         * unselected icon, selected icon, type and priority.
         */
        cmZoom = new IconCommand("Zoom", zoomIcon, zoomIcon, IconCommand.ICON_OK, hotKey++);
        svgCanvas.addCommand(cmZoom);

        /**
         * IconCommand is supported from Java Runtime 2.0.0 for Series 40
         * onwards. Creates a new IconCommand Move with the given label,
         * unselected icon, selected icon, type and priority.
         */
        cmMove = new IconCommand("Move", moveIcon, moveIcon, IconCommand.ICON_OK, hotKey++);

        // ...

Implement gesture event handling in the SvgCanvas2 class. The initializeGesture method sets up gesture event handling for the MIDlet. The gestureAction method implements the actual event handling. The MIDlet registers drag gestures, which it uses to move the image, and pinch gestures, which it uses to zoom in and out of the image.

/**
     * Initializes gestures and set it to receive gesture events
     */
    public void initializeGesture() {

        /**
         * Set the listener to register events for SvgCanvas2 (this,) and
         * register the GestureListener for the UI element (,this)
         */
        GestureRegistrationManager.setListener(this, this);

        /**
         * Create an interactive zone and set it to receive taps
         */
        GestureInteractiveZone myGestureZone = new GestureInteractiveZone(GestureInteractiveZone.GESTURE_PINCH | GestureInteractiveZone.GESTURE_DRAG);

        /**
         * Set the location (relative to the container) and size of the
         * interactive zone:
         */
        myGestureZone.setRectangle(0, 0, getWidth(), getHeight());

        /**
         * Register the interactive zone for GestureCanvas (this)
         */
        GestureRegistrationManager.register(this, myGestureZone);
    }

    /**
     * Method to open the sensor connection.
     */
    private SensorConnection openAccelerationSensor() {
        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;

        }
    }

    /**
     * Paint method.
     */
    public void paint(Graphics g) {
        // *** clear the display
        g.setColor(255, 255, 255);
        g.fillRect(0, 0, getWidth(), getHeight());

        // *** render the SVG image
        sg.bindTarget(g);
        sg.setTransparency(1f);
        //sg.render(0, 0, svgImage);
        sg.render((getWidth() - svgImage.getViewportWidth()) >> 1, (getHeight() - svgImage.getViewportHeight()) >> 1, svgImage);
        sg.releaseTarget();

    }

    protected void sizeChanged(int w, int h) {
        svgImage.setViewportWidth(w);
        svgImage.setViewportHeight(h);
        repaint();
    }

    /**
     * Restore the original view of the SVG Image.
     */
    public void restoreView() {
        SVGSVGElement myEl = (SVGSVGElement) svgImage.getDocument().getDocumentElement();
        myEl.setCurrentRotate(0);
        myEl.setCurrentScale(1);
        SVGPoint origin = myEl.getCurrentTranslate();
        origin.setX(0);
        origin.setY(0);
        repaint();
    }

    /**
     * Zoom in on the SVG Image.
     */
    public void zoomIn() {
        SVGSVGElement myEl = (SVGSVGElement) (svgImage.getDocument().getDocumentElement());
        myEl.setCurrentScale(myEl.getCurrentScale() * 1.2f);
        repaint();
    }

    /**
     * Zoom out on the SVG Image.
     */
    public void zoomOut() {
        SVGSVGElement myEl = (SVGSVGElement) (svgImage.getDocument().getDocumentElement());
        myEl.setCurrentScale(myEl.getCurrentScale() * 0.8f);
        repaint();
    }

    public void zoom(float zoom) {
        SVGSVGElement myEl = (SVGSVGElement) (svgImage.getDocument().getDocumentElement());
        myEl.setCurrentScale(myEl.getCurrentScale() * zoom);
        repaint();
    }

    /**
     * Rotate out on the SVG Image.
     */
    public void rotateOut() {
        SVGSVGElement myEl = (SVGSVGElement) (svgImage.getDocument().getDocumentElement());
        myEl.setCurrentRotate(myEl.getCurrentRotate() + 10);
        repaint();
    }

    /**
     * Rotate in on the SVG Image.
     */
    public void rotateIn() {
        SVGSVGElement myEl = (SVGSVGElement) (svgImage.getDocument().getDocumentElement());
        myEl.setCurrentRotate(myEl.getCurrentRotate() - 10);
        repaint();
    }

    public void pointerPressed(int x, int y) {
    }

    /**
     * Key repeat method.
     */
    protected void keyRepeated(int keyCode) {
        keyPressed(keyCode);
    }

    /**
     * Handle key presses.
     */
    protected void keyPressed(int keyCode) {
        SVGSVGElement svgDoc = (SVGSVGElement) svgImage.getDocument().getDocumentElement();
        int action = getGameAction(keyCode);
        SVGPoint origin = svgDoc.getCurrentTranslate();
        switch (action) {
            case RIGHT:
                origin.setX(origin.getX() + 5f);
                break;
            case LEFT:
                origin.setX(origin.getX() - 5f);
                break;
            case UP:
                origin.setY(origin.getY() - 5f);
                break;
            case DOWN:
                origin.setY(origin.getY() + 5f);
                break;
        }
        repaint();
    }
    /*
     * Private members
     */
    private ScalableGraphics sg;
    private SVGImage svgImage;

    /**
     * Defines the gestureAction method for the class. This method is called
     * every time a gesture event occurs for a UI element that uses
     * GestureListener. The method is called with all the information related to
     * the gesture event.
     */
    public void gestureAction(Object arg0, GestureInteractiveZone arg1, GestureEvent gestureEvent) {

        switch (gestureEvent.getType()) {

            /**
             * Receives drag events and moves the image.
             */
            case GestureInteractiveZone.GESTURE_DRAG:
                if (iconCommandState == IMAGE_MOVE) {
                    SVGSVGElement svgDoc = (SVGSVGElement) svgImage.getDocument().getDocumentElement();

                    SVGPoint origin = svgDoc.getCurrentTranslate();
                    origin.setX(origin.getX() + gestureEvent.getDragDistanceX());
                    origin.setY(origin.getY() + gestureEvent.getDragDistanceY());
                    repaint();
                }

                break;

            /**
             * This gesture event is supported from Java Runtime 2.0.0 for
             * Series 40 onwards. Receives pinch events and check the pinch
             * distance change to scale the current image.
             */
            case GestureInteractiveZone.GESTURE_PINCH:

                if (iconCommandState == IMAGE_ZOOM) {
                    int distanceChange = gestureEvent.getPinchDistanceChange();
                    if (distanceChange < 0) {
                        zoomOut();
                    } else if (distanceChange > 0) {
                        zoomIn();
                    }
                    repaint();
                }

                break;
        }
    }

In the SvgCanvas2 class, open a connection to the accelerometer sensor, and implement the dataReceived method for handling the returned sensor data. Use the sensor data to determine the new x axis of the device (in case the user has rotated the device) and rotate the image accordingly.

    /**
     * Method to open the sensor connection.
     */
    private SensorConnection openAccelerationSensor() {
        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;

        }
    }
    
    // ...

    /**
     * Notification of the received sensor data.
     */
    public void dataReceived(SensorConnection arg0, Data[] arg1, boolean arg2) {
        // get sensor data for x
        int x = getX(arg1);

        // Rotate SVG Image with rotation
        if (x > 3) {
            rotateOut();
        } else if (x < -3) {
            rotateIn();
        }

        repaint();

    }

    private int getX(Data[] aData) {
        int x_axis = 0;

        try {
            for (int i = 0; i < BUFFER_SIZE; i++) {
                x_axis += (int) aData[0].getDoubleValues()[0];
            }
            x_axis = (int) (x_axis / BUFFER_SIZE);
        } catch (IllegalStateException e) {
        }

        return x_axis;
    }
}