CompassView.java

/**
 * Copyright (c) 2012 Nokia Corporation. All rights reserved.
 * Nokia and Nokia Connecting People are registered trademarks of Nokia Corporation.
 * Oracle and Java are trademarks or registered trademarks of Oracle and/or its
 * affiliates. Other product and company names mentioned herein may be trademarks
 * or trade names of their respective owners.
 * See LICENSE.TXT for license information.
 */

package com.nokia.example.compass.views;

import com.nokia.example.compass.CompassMidlet;
import com.nokia.example.compass.LandmarkManager;
import com.nokia.example.compass.helpers.ImageLoader;
import com.nokia.example.compass.helpers.KeyCodes;
import java.io.IOException;
import java.util.Timer;
import java.util.TimerTask;
import java.util.Vector;
import javax.microedition.lcdui.Font;
import javax.microedition.lcdui.Graphics;
import javax.microedition.lcdui.Image;
import javax.microedition.lcdui.game.GameCanvas;
import javax.microedition.lcdui.game.Sprite;
import javax.microedition.location.Criteria;
import javax.microedition.location.Landmark;
import javax.microedition.location.Location;
import javax.microedition.location.LocationException;
import javax.microedition.location.LocationListener;
import javax.microedition.location.LocationProvider;
import javax.microedition.location.QualifiedCoordinates;
import javax.microedition.m2g.SVGImage;
import javax.microedition.m2g.ScalableGraphics;
import org.w3c.dom.Document;
import org.w3c.dom.svg.SVGElement;
import org.w3c.dom.svg.SVGMatrix;

/**
 *
 * @author tkor
 */
public class CompassView extends GameCanvas implements LocationListener, Runnable{

    private  Graphics g = null;
    private Graphics bg = null;
    private ScalableGraphics sg = null;

    private float angle;
    private float old_angle;

    private Image back;
    private Image cover;
    private Image arrow_left;
    private Image arrow_right;
    private Image landmark_background;
    private Image glass;
    private Image gps_no_signal;
    private Image gps_ok_signal;
    private Image gps_default;


    private SVGImage compass;
    private Document compass_doc;

    private SVGImage navi;
    private Document navi_doc;

    private Timer mTimer;
    private TimerTask UIloop;
    private TimerTask GPSanimator;

    public static final Font DEFAULT_FONT = Font.getFont(Font.FACE_SYSTEM, Font.STYLE_PLAIN,Font.SIZE_LARGE);
    public static final Font SMALL_FONT = Font.getFont(Font.FACE_SYSTEM, Font.STYLE_PLAIN, Font.SIZE_SMALL);

    private int currentLandmarkIndex;

    private boolean initialized = false;
    private boolean isValidGPS = false;

    private boolean showDefaultGPS = false;

    private boolean isLandscape = false;

    private AddLandmarkView addlandmark;
    private Vector landmarks;

    private SVGMatrix svgmatrix;

    private float degree = 0.0f;
    private float landmark_degree = 0.0f;
    private float old_landmark_degree = 0.0f;

    private double latitude = 0.0f;
    private double longitude = 0.0f;

    private QualifiedCoordinates currentCoordinates;

    private static final int default_anchor = Graphics.TOP|Graphics.LEFT;

    private Image bufferImage;
    private Sprite bufferSprite;

    private Criteria criteria;
    private Thread thread;
    private LocationProvider lp = null;
    public CompassView() {
        super(false);
        criteria=new Criteria();
        criteria.setCostAllowed(true);
        criteria.setSpeedAndCourseRequired(true);
        criteria.setPreferredPowerConsumption(Criteria.POWER_USAGE_HIGH);
        thread=new Thread(this);
        thread.start();
        loadResources();
        initTasks();
        addlandmark = new AddLandmarkView(this);

        //initial angle
        angle = 0;
        old_angle = -1;
        this.setFullScreenMode(true);
        refreshLandmarks();
        
        sg = ScalableGraphics.createInstance();
        bufferImage = Image.createImage(240, 320);
        bufferSprite = new Sprite(bufferImage);
    }

    public void refreshLandmarks() {
        currentLandmarkIndex = 0;
        landmarks = new LandmarkManager().getLandmarks();
    };

    private void loadResources() {
        int w = getWidth();
        int h = getHeight();
        System.out.println("width:" +w);
        System.out.println("height:" + h);
        try {

            back = ImageLoader.getInstance().loadImage("/back.png");
            cover = ImageLoader.getInstance().loadImage("/cover.png");
            arrow_left = ImageLoader.getInstance().loadImage("/arrow-left.png");
            arrow_right = ImageLoader.getInstance().loadImage("/arrow-right.png");
            landmark_background = ImageLoader.getInstance().loadImage("/background.png");
            glass = ImageLoader.getInstance().loadImage("/glass.png");
            compass = ImageLoader.getInstance().loadSVGImage("/compass.svg");
            compass_doc = compass.getDocument();
            navi = ImageLoader.getInstance().loadSVGImage("/navi.svg");
            gps_no_signal = ImageLoader.getInstance().loadImage("/gps-no-signal.png");
            gps_ok_signal = ImageLoader.getInstance().loadImage("/gps-ok.png");
            gps_default = ImageLoader.getInstance().loadImage("/gps-default.png");
            /*
             * The original compass image is drawn in a way that the needle center is in the top left corner of the image
             * Because rotation is applied using this top left corner as reference point, we can translate the needle to the
             * center of the screen and the rotation will now rotate the needle correctly. If the picture was drawn in a traditional
             * way the needle would rotate using the 0,0 point as its reference point and the needle would "fly" away from the screen.
             */
            SVGElement svge = (SVGElement) compass_doc.getElementById("Compass");
            SVGMatrix matrix = svge.getMatrixTrait("transform");

            matrix = matrix.mTranslate(120,120);
            matrix = matrix.mScale(0.6f);
            svgmatrix = matrix;
            svge.setMatrixTrait("transform", matrix);
            //do the same translation to naviarrow
            navi_doc = navi.getDocument();
            SVGElement navi_svge = (SVGElement)navi_doc.getElementById("Layer_4");
            SVGMatrix navi_matrix = navi_svge.getMatrixTrait("transform");
            navi_matrix.mTranslate(120,120);
            navi_matrix.mScale(0.6f);
            navi_svge.setMatrixTrait("transform", navi_matrix);

        }catch(IOException e) {
            CompassMidlet.getInstance().showAlert(e.toString());
        }
    }
    private void initTasks() {
        UIloop = new TimerTask() {

                public void run() {
                    render(bg);
                    if(isLandscape) {
                        System.out.println("Landscape");
                        bufferSprite.setRefPixelPosition(0, 239);
                        bufferSprite.setTransform(Sprite.TRANS_ROT270);

                    }else {
                        bufferSprite.setRefPixelPosition(0, 0);
                        bufferSprite.setTransform(Sprite.TRANS_NONE);

                    }


                    bufferSprite.paint(g);
                    flushGraphics();
                }
            };
       GPSanimator = new TimerTask() {
              public void run() {
                  if(showDefaultGPS) {
                      showDefaultGPS = false;
                  }else {
                      showDefaultGPS = true;
                  }
              }
            };
    }

    protected void showNotify() {
        if (!initialized) {
            mTimer = new Timer();
            g = getGraphics();
            bg = bufferImage.getGraphics();
            render(bg);
            if (isLandscape) {
                System.out.println("Landscape");
                bufferSprite.setRefPixelPosition(0, 239);
                bufferSprite.setTransform(Sprite.TRANS_ROT270);

            } else {
                bufferSprite.setRefPixelPosition(0, 0);
                bufferSprite.setTransform(Sprite.TRANS_NONE);

            }

            bufferSprite.paint(g);
            flushGraphics();
            initTasks();
            mTimer.schedule(UIloop, 0, 30);
            mTimer.schedule(GPSanimator, 0, 1000);
            initialized = true;
        }
    }
    protected void hideNotify() {
        //cleanup everything
        mTimer.cancel();
        initialized = false;
    }

    public void render(Graphics g) {
        g.setColor(0x000000);
        g.setClip(0, 0, 240, 320);
        g.fillRect(0, 0, getWidth(), getHeight());
        drawCompass(g);
        drawLandmarks(g);
        drawSoftkeyLabels(g);
        addlandmark.draw(g);
        drawGPSStatus(g);
        //drawDebugData(g);

    }

    private void drawCompass(Graphics g) {
        g.drawImage(back, 0, 0, default_anchor);
        try {
            sg.bindTarget(g);
            sg.setRenderingQuality(sg.RENDERING_QUALITY_HIGH);
            if(angle != old_angle) {
                //rotate needle
                float rotation = angle-old_angle;
                rotation = (-1)*rotation;

                SVGElement svge = (SVGElement) compass_doc.getElementById("Compass");
                SVGMatrix matrix = svgmatrix.mRotate((rotation));
                old_angle = angle;
                svge.setMatrixTrait("transform", matrix);
                //rotate landmark direction
                float landmark_rotation = landmark_degree - old_landmark_degree;
                landmark_rotation = rotation + landmark_rotation;
                old_landmark_degree = landmark_degree;

                SVGElement navi_svge = (SVGElement)navi_doc.getElementById("Layer_4");
                SVGMatrix navi_matrix = navi_svge.getMatrixTrait("transform");
                navi_matrix.mRotate(landmark_rotation);
                navi_svge.setMatrixTrait("transform", navi_matrix);
            }

            sg.render(0, 0, compass);
            sg.render(0, 0, navi);

        }finally {
            sg.releaseTarget();
        }
        g.drawImage(cover, 0, 0, default_anchor);
    }

    private void drawLandmarks(Graphics g) {
        int anchor = default_anchor;
        String s = "";
        if (landmarks == null || landmarks.size() == 0)
        {
            s = "No landmarks.";
        }else {
            if(currentLandmarkIndex > 0) {
                Landmark landmark = (Landmark) landmarks.elementAt(currentLandmarkIndex -1);
                s = landmark.getName();
            }else if(currentLandmarkIndex == 0) {
                s = "none";
            }
        }

        int width = SMALL_FONT.stringWidth(s);
        int x = (240 - width) / 2;
        int y = 242;
        g.setFont(SMALL_FONT);
        g.setColor(0xFFFFFF);
        g.drawImage(landmark_background, 0, y, anchor);
        g.drawImage(arrow_left, 0, y, anchor);
        g.drawImage(arrow_right, 0, y, anchor);
        g.drawString(s, x, y+3, anchor);
        g.drawImage(glass, 0, y, anchor);
    }
    private void drawSoftkeyLabels(Graphics g) {
        g.setFont(SMALL_FONT);
        g.setColor(0xFFFFFF);
        g.drawString("Store Location", 0, 320, g.LEFT|g.BOTTOM);
        g.drawString("Exit", 240, 320, g.BOTTOM|g.RIGHT);

    }
    private void drawGPSStatus(Graphics g) {
        if(!isValidGPS) {
            if(showDefaultGPS) {
                g.drawImage(gps_default, 5, 5, default_anchor);
            }else {
                g.drawImage(gps_no_signal, 5, 5, default_anchor);
            }
        }else {
           g.drawImage(gps_ok_signal, 5, 5,default_anchor);
        }
    }
    private void drawDebugData(Graphics g) {
        g.setColor(0xFFFFFF);
        g.setFont(SMALL_FONT);
        g.drawString("" + degree, 0, 0, default_anchor);
        g.drawString("" + latitude, 0,SMALL_FONT.getHeight(),default_anchor);
        g.drawString("" + longitude, 0,SMALL_FONT.getHeight()*2,default_anchor);
        g.drawString(""+landmark_degree, 0, SMALL_FONT.getHeight()*3, default_anchor);
    }

    protected void keyPressed(int keyCode) {
        //System.out.println("key pressed:" + (keyCode));
    }

    protected void keyReleased(int keyCode) {

        System.out.println("key pressed:" + (keyCode));
        if (addlandmark.isShown()) {
            addlandmark.notifyKeyEvents(keyCode);
        } else {
            switch (keyCode) {
                case KeyCodes.LEFT:
                    if (currentLandmarkIndex == 0) {
                        currentLandmarkIndex = landmarks.size();
                    } else {
                        currentLandmarkIndex--;
                    }
                    break;
                case KeyCodes.RIGHT:
                    if (currentLandmarkIndex == (landmarks.size())) {
                        currentLandmarkIndex = 0;
                    } else {
                        currentLandmarkIndex++;
                    }
                    break;
                case KeyCodes.LEFT_SOFTKEY:
                    addlandmark.show();
                    break;
                case KeyCodes.RIGHT_SOFTKEY:

                    CompassMidlet.getInstance().destroyApp(true);
                    CompassMidlet.getInstance().notifyDestroyed();

                    break;
                default:
                    break;
            }
        }

    }

    public void locationReceived(Location loc) {
        if(loc.isValid()) {
            isValidGPS = true;
        }else {
            isValidGPS = false;
        }
        float course = loc.getCourse();
        QualifiedCoordinates coords = loc.getQualifiedCoordinates();
        if(coords != null) {
            setCurrentCoordinates(coords);
            latitude = coords.getLatitude();
            longitude = coords.getLongitude();
        }
        //temp = 161.35f;
        if(!Float.isNaN(course)) {
            if(degree != course) {
                if (course != 0.0f) {
                    degree = course;
                    angle = degree;
                    if (coords != null) {

                        calculateLandmarkAngle(coords);
                    }
                }
            }
        }

    }

    private void calculateLandmarkAngle(QualifiedCoordinates coordinates) {
        System.out.println("Calculating landmark angle");
        if(landmarks.size() > 0 && currentLandmarkIndex > 0) {
            Landmark current = (Landmark) landmarks.elementAt(currentLandmarkIndex -1);
            System.out.println("current landmark:" + current.getName());
            landmark_degree = coordinates.azimuthTo(current.getQualifiedCoordinates());
        }
    }

    private synchronized void setCurrentCoordinates(QualifiedCoordinates newcoordinates) {
        currentCoordinates = newcoordinates;
    }
    public synchronized QualifiedCoordinates getCurrentCoordinates() {
        return currentCoordinates;
    }

    protected void sizeChanged(int w, int h) {
        if(w > h) {//landscape mode
            isLandscape = true;
        }else {//portrait mode
            isLandscape = false;
        }
    }

    public void locationUpdated(LocationProvider provider, Location location)
    {
        if(location.isValid())
        {
            isValidGPS = true;
            float course = location.getCourse();
            QualifiedCoordinates coords = location.getQualifiedCoordinates();
            if(coords != null)
            {
                setCurrentCoordinates(coords);
                latitude = coords.getLatitude();
                longitude = coords.getLongitude();
            }
            //temp = 161.35f;
            if(!Float.isNaN(course))
            {
                if(degree != course)
                {
                    if (course != 0.0f)
                    {
                        degree = course;
                        angle = degree;
                        if (coords != null)
                        {
                            calculateLandmarkAngle(coords);
                        }
                    }
                }
            }
        }
        else
        {
            isValidGPS = false;
        }
    }

    public void providerStateChanged(LocationProvider provider, int newState) {}

    public void run() {
        try {
            lp = LocationProvider.getInstance(criteria);
            lp.setLocationListener(this, -1, -1, -1);
        }catch(LocationException le) {
            System.out.println("LOCATION ERROR:" + le.getMessage());
        }

    }

}