Implementing the UI elements

CompassUI class

  1. Create the CompassUI class file.

  2. Import the required classes.

    package com.nokia.example.location.tourist.ui;
    
    import java.io.IOException;
    
    import javax.microedition.lcdui.Canvas;
    import javax.microedition.lcdui.Command;
    import javax.microedition.lcdui.CommandListener;
    import javax.microedition.lcdui.Displayable;
    import javax.microedition.lcdui.Graphics;
    import javax.microedition.lcdui.Image;
    import javax.microedition.location.Orientation;
    
    import com.nokia.example.location.tourist.TouristMIDlet;
    import com.nokia.example.location.tourist.model.ConfigurationProvider;
    
    
  3. Create the CompassUI class and set it to extend Canvas and implement CommandListener and Runnable. Define the constants and commands used by this class.

    /**
     * Viewer class representing a compass.
     */
    public class CompassUI extends Canvas implements CommandListener, Runnable
    {
        /** Constant value for X coordinate. Used in arrays. */
        private final int X = 0;
    
        /** Constant value for Y coordinate. Used in arrays. */
        private final int Y = 1;
    
        /** Compass center X point */
        private int centerX;
    
        /** Compass center Y point */
        private int centerY;
    
        /** Constant value representing one degree. */
        private final float degree = (float) (2 * Math.PI / 360.0);
    
        /** Current compass azimuth. */
        private float azimuth = 0.0f;
    
        /**
         * Is orientation relative to the magnetic field of the Earth or true north
         * and gravity.
         */
        private boolean isMagnetic;
    
        /** Flag telling is the compass update thread active */
        private boolean threadActive = false;
    
        /** Sleep 1000ms during each compass update */
        private final long SLEEPTIME = 1000;
    
        /** A reference to Route UI */
        private Displayable route = null;
    
        /** A reference to Pitch and Roll UI */
        private Displayable pitchrollUI = null;
    
        /** Command that swithes current displayable to Route UI */
        private Command routeCmd = new Command("Route", Command.BACK, 1);
    
        /** Command that swithes current displayable to Pitch and Roll UI */
        private Command prCmd = new Command("Pitch and Roll", Command.OK, 1);
    
        /** Compss background image. */
        private Image compassImage = null;
    
    
  4. Create a constructor for the class.

        /**
         * Construct instance of this displayable.
         * 
         * @param route
         *            is a reference to Route UI.
         */
        public CompassUI(Displayable route)
        {
            this.route = route;
            pitchrollUI = new PitchRollUI(this);
    
            loadCompassImage();
    
            centerX = getWidth() / 2;
            centerY = getHeight() / 2;
    
            addCommand(routeCmd);
            addCommand(prCmd);
    
            setCommandListener(this);
        }
    
    
  5. Create a method for loading a background image for the compass.

        /**
         * Load compass backgound image from JAR file.
         */
        private void loadCompassImage()
        {
            String imageName = "/compass_small.gif";
    
            if (getWidth() > 160)
            {
                imageName = "/compass_large.gif";
            }
    
            try
            {
                compassImage = Image.createImage(getClass().getResourceAsStream(
                        imageName));
            }
            catch (IOException e)
            {
                e.printStackTrace();
            }
        }
    
    
  6. Create the two below methods the VM will call, depending on the status of the Canvas' visibility.

        /**
         * VM calls this method immediately prior to this Canvas being made visible
         * on the display.
         */
        protected void showNotify()
        {
            // Actives compass update thread.
            threadActive = true;
            new Thread(this).start();
        }
    
        /**
         * VM calls this method shortly after the Canvas has been removed from the
         * display.
         */
        protected void hideNotify()
        {
            // Stops the thread.
            threadActive = false;
        }
    
    
  7. Create a method to render the canvas.

        /**
         * Renders the canvas.
         * 
         * @param g -
         *            the Graphics object to be used for rendering the Canvas
         */
        protected void paint(Graphics g)
        {
            // clean up canvas
            g.setColor(255, 255, 255);
            g.fillRect(0, 0, getWidth(), getHeight());
            int spikeLen = 5;
            int len = (compassImage.getWidth() / 2) - spikeLen;
    
            // draw compass background
            g.drawImage(compassImage, (getWidth() - compassImage.getWidth()) / 2,
                    centerY, Graphics.LEFT | Graphics.VCENTER);
    
            // draw compass arrow
            g.setColor(0, 0, 255);
            drawArrow(g, degree * azimuth, len, spikeLen);
    
            // draw orientation type
            g.setColor(0, 0, 255);
            String otext = "True North";
            if (!isMagnetic)
            {
                otext = "Magnetic field";
            }
            g.drawString("Orientation: " + otext, 0, getHeight(), Graphics.BOTTOM
                    | Graphics.LEFT);
        }
    
    
  8. Create a method to draw the compass arrow.

        /**
         * Draw a compass arrow rotated to a certain angle.
         * 
         * @param g
         *            is a reference to Graphics object.
         * @param angle
         *            in degrees [0.0,360.0)
         * @param len
         *            is arrows length.
         * @param spikeLen
         *            is length of arrows spike.
         */
        private void drawArrow(Graphics g, float angle, int len, int spikeLen)
        {
            int a[] = rotate(angle, 0, -(len - spikeLen));
            int b[] = rotate(angle, -spikeLen, -(len - spikeLen));
            int c[] = rotate(angle, 0, -len);
            int d[] = rotate(angle, spikeLen, -(len - spikeLen));
            int e[] = rotate(angle + (degree * 180.0), 0, -len);
    
            // use red foreground color
            g.setColor(255, 0, 0);
            g.drawLine(centerX, centerY, centerX + a[X], centerY + a[Y]);
            g.drawLine(centerX + b[X], centerY + b[Y], centerX + d[X], centerY
                    + d[Y]);
            g.drawLine(centerX + b[X], centerY + b[Y], centerX + c[X], centerY
                    + c[Y]);
            g.drawLine(centerX + c[X], centerY + c[Y], centerX + d[X], centerY
                    + d[Y]);
    
            // use black foreground color
            g.setColor(0, 0, 0);
            g.drawLine(centerX, centerY, centerX + e[X], centerY + e[Y]);
        }
    
    
  9. Create the method for rotating the arrow.

        /**
         * Rotate point (x,y) by degrees that angle parameter defines. The new
         * coordinate calculation is performed with a 2x2 rotate matrix.
         * 
         * @param angle
         *            to be rotated
         * @param x
         *            coordinate
         * @param y
         *            coordinate
         * @return new coordinate pair in int array format [x,y]
         */
        private int[] rotate(double angle, int x, int y)
        {
            int rotated[] = new int[2];
            rotated[X] = (int) (Math.cos(angle) * x + Math.sin(angle) * y);
            rotated[Y] = (int) (-Math.sin(angle) * x + Math.cos(angle) * y);
            return rotated;
        }
    
    
  10. Create a run method from Runnable. Its purpose is to update the azimuth, pitch and roll and to repaint the canvas. The isOrientationMagnetic method returns a boolean value that indicates whether this Orientation is relative to the magnetic field of the Earth or relative to true north and gravity. The getCompassAzimuth method returns the terminal's horizontal compass azimuth in degrees relative to either magnetic or true north.

    For more information, see isOrientationMagnetic and getCompassAzimuth in the Location API specification.

        /**
         * run method from Runnable interface. Updates azimuth, pitch and roll
         * values and repaints the canvas.
         * 
         * If Orientation is supported on the terminal, compass sensor is either 2D
         * or 3D. If the terminals compass sensor providers only compass azimuth,
         * pitch and roll values are Float.NaN.
         * 
         * @see HideNotify() method.
         */
        public void run()
        {
            // Keep the thread running until another displayable is set visible.
            // See also hideNotify() method.
            while (threadActive)
            {
                Orientation orientation = ConfigurationProvider.getInstance()
                        .getOrientation();
    
                if (orientation != null)
                {
                    isMagnetic = orientation.isOrientationMagnetic();
                    azimuth = orientation.getCompassAzimuth();
                }
    
                repaint();
    
                try
                {
                    // Pause this thread for a secord before next update.
                    Thread.sleep(SLEEPTIME);
                }
                catch (InterruptedException e)
                {
                }
            }
        }
    
    
  11. Create a method for detecting command button presses.

        /**
         * Event indicating when a command button is pressed.
         * 
         * @see javax.microedition.lcdui.CommandListener#commandAction(javax.microedition.lcdui.Command,
         *      javax.microedition.lcdui.Displayable)
         */
        public void commandAction(Command command, Displayable d)
        {
            if (command == routeCmd)
            {
                TouristMIDlet.getDisplay().setCurrent(route);
            }
            else if (command == prCmd)
            {
                TouristMIDlet.getDisplay().setCurrent(pitchrollUI);
            }
        }
    
    }
    

LandmarkEditorUI class

  1. Create the LandmarkEditorUI class file.

  2. Import the required classes.

    package com.nokia.example.location.tourist.ui;
    
    import java.io.IOException;
    import java.util.Enumeration;
    
    import javax.microedition.lcdui.Alert;
    import javax.microedition.lcdui.AlertType;
    import javax.microedition.lcdui.Command;
    import javax.microedition.lcdui.CommandListener;
    import javax.microedition.lcdui.Displayable;
    import javax.microedition.lcdui.Form;
    import javax.microedition.lcdui.List;
    import javax.microedition.lcdui.StringItem;
    import javax.microedition.lcdui.TextField;
    import javax.microedition.location.AddressInfo;
    import javax.microedition.location.Landmark;
    import javax.microedition.location.LandmarkException;
    import javax.microedition.location.QualifiedCoordinates;
    
    import com.nokia.example.location.tourist.TouristMIDlet;
    import com.nokia.example.location.tourist.Utils;
    import com.nokia.example.location.tourist.model.ControlPoints;
    import com.nokia.example.location.tourist.model.TouristData;
    
    
  3. Create the LandmarkEditorUI class and set it to extend Form and implement CommandListener. Define the TextFields used by the form.

    /**
     * Viewer class enabling landmarks address info monifications.
     */
    class LandmarkEditorUI extends Form implements CommandListener
    {
        // Landmark fields, (*) = required field
        private TextField nameField = new TextField("Name (*):", "", 20,
                TextField.ANY);
    
        private TextField descField = new TextField("Description (*):", "", 40,
                TextField.ANY);
    
        // AddressInfo fields
        private TextField countryField = new TextField("Country:", "", 20,
                TextField.ANY);
    
        private TextField stateField = new TextField("State:", "", 20,
                TextField.ANY);
    
        private TextField cityField = new TextField("City:", "", 20, TextField.ANY);
    
        private TextField streetField = new TextField("Street:", "", 20,
                TextField.ANY);
    
        private TextField buildingNameField = new TextField("Building name (*):",
                "", 20, TextField.ANY);
    
    

    Define the coordinate information StringItems and the commands and constants used by the Landmark selector list. The QualifiedCoordinates class represents coordinates as latitude-longitude-altitude values that are associated with an accuracy value.

    For more information, see QualifiedCoordinates in the Location API specification.

        // Coordinate information
        private StringItem lat = new StringItem("Lat:", "");
    
        private StringItem lon = new StringItem("Lon:", "");
    
        private StringItem alt = new StringItem("Alt:", "");
    
        /** Landmark selector list */
        private List list = new List("Landmarks:", List.EXCLUSIVE);
    
        private Command saveNewCmd = new Command("Save", Command.OK, 1);
    
        private Command saveUpdatedCmd = new Command("Save", Command.OK, 1);
    
        private Command updateCmd = new Command("Update", Command.OK, 1);
    
        private Command removeCmd = new Command("Remove", Command.OK, 1);
    
        private Command listCmd = new Command("List", Command.OK, 1);
    
        private Command routeCmd = new Command("Route", Command.BACK, 1);
    
        public static final int MODE_UPDATE = 0;
    
        public static final int MODE_ADDNEW = 1;
    
        private QualifiedCoordinates coord = null;
    
        private Displayable route = null;
    
        private TouristData data = null;
    
    
  4. Create the constructor for the class.

        /**
         * Construct Landmark Editor UI Form.
         * 
         * @param route -
         *            a reference of Route UI.
         * @param data -
         *            a reference to TouristData model layer class.
         */
        public LandmarkEditorUI(Displayable route, TouristData data)
        {
            super("Landmark Editor");
    
            this.route = route;
            this.data = data;
    
            // route and list commands always available on landmark editor
            addCommand(routeCmd);
            addCommand(listCmd);
            setCommandListener(this);
    
            // route command always available on landmarks list
            list.addCommand(routeCmd);
            list.setCommandListener(this);
        }
    
    
  5. Create a method for initializing the UI components and showing the landmark editor. The getAltitude method returns the altitude component of this coordinate.

        /**
         * Initialize UI components and show the landmark editor.
         */
        public void showEditor(QualifiedCoordinates newCoord, int mode)
        {
            this.coord = newCoord;
    
            // initialize coordinate values
            lat.setText(Utils.formatDouble(newCoord.getLatitude(), 3));
            lon.setText(Utils.formatDouble(newCoord.getLongitude(), 3));
            alt.setText(Utils.formatDouble(newCoord.getAltitude(), 1));
    
            // initialize editable test field values
            nameField.setString("");
            descField.setString("");
            countryField.setString("");
            stateField.setString("");
            cityField.setString("");
            streetField.setString("");
            buildingNameField.setString("");
    
            deleteAll();
            append(nameField);
            append(descField);
            append(countryField);
            append(stateField);
            append(cityField);
            append(streetField);
            append(buildingNameField);
    
            append(lat);
            append(lon);
            append(alt);
    
    
            // Update existing landmark.
            if (mode == MODE_UPDATE)
            {
                Landmark lm = ControlPoints.getInstance().findNearestLandMark(
                        newCoord);
    
                if (lm != null)
                {
                    nameField.setString(lm.getName());
                    descField.setString(lm.getDescription());
    
                    AddressInfo info = lm.getAddressInfo();
                    if (info != null)
                    {
                        countryField.setString(info.getField(AddressInfo.COUNTRY));
                        stateField.setString(info.getField(AddressInfo.STATE));
                        cityField.setString(info.getField(AddressInfo.CITY));
                        streetField.setString(info.getField(AddressInfo.STREET));
                        buildingNameField.setString(info
                                .getField(AddressInfo.BUILDING_NAME));
                    }
                }
    
                removeCommand(updateCmd);
                removeCommand(saveNewCmd);
                addCommand(saveUpdatedCmd);
                addCommand(listCmd);
            }
            // Add new landmark to the landmark store.
            else if (mode == MODE_ADDNEW)
            {
                removeCommand(updateCmd);
                removeCommand(saveUpdatedCmd);
                addCommand(saveNewCmd);
                addCommand(listCmd);
            }
    
            TouristMIDlet.getDisplay().setCurrent(this);
        }
    
    
  6. Create a method for showing the landmark selector list.

        /**
         * Show landmark selector List. Content of the list is refreshed each time
         * this method is called.
         */
        public void showList()
        {
            list.deleteAll();
    
            Landmark lm = null;
            Enumeration landmarks = ControlPoints.getInstance().getLandMarks();
    
            // Check whether landmarks can be founds from the Landmark store.
            if (landmarks != null)
            {
                while (landmarks.hasMoreElements())
                {
                    lm = ((Landmark) landmarks.nextElement());
                    list.append(lm.getName(), null);
                }
    
                list.addCommand(updateCmd);
                list.addCommand(removeCmd);
            }
            // No landmarks found (list is empty)
            else
            {
                list.removeCommand(updateCmd);
                list.removeCommand(removeCmd);
            }
    
            TouristMIDlet.getDisplay().setCurrent(list);
        }
    
    
  7. Create a method for checking that the required fields are not missing.

        /**
         * Check whether any required fields are missing. (*) on the TextFields label
         * indicates that field is mandatory.
         * 
         * @return Name of the missing field name or null indicating no required
         *         fields are missing.
         */
        private String checkRequiredFields()
        {
            if (nameField.getString().equals(""))
            {
                return "Name";
            }
            else if (nameField.getString().equals(""))
            {
                return "Description";
            }
            else if (buildingNameField.getString().equals(""))
            {
                return "Building name";
            }
    
            return null;
        }
    
    
  8. Create a method for generating a landmark from UI component values.

        /**
         * Generate landmark from UI component values.
         * 
         * @return Genereated Landmark.
         */
        private Landmark generateLandmark()
        {
            String field = checkRequiredFields();
            if (field != null)
            {
                Alert alert = new Alert("Error", "Value for required field "
                        + field + " is missing.", null, AlertType.ERROR);
                alert.setTimeout(3000); // 3 secs
    
                TouristMIDlet.getDisplay().setCurrent(alert, this);
                return null;
            }
    
            AddressInfo info = new AddressInfo();
            info.setField(AddressInfo.COUNTRY, countryField.getString());
            info.setField(AddressInfo.STATE, stateField.getString());
            info.setField(AddressInfo.CITY, cityField.getString());
            info.setField(AddressInfo.STREET, streetField.getString());
            info.setField(AddressInfo.BUILDING_NAME, buildingNameField.getString());
    
            Landmark lm = new Landmark(nameField.getString(),
                    descField.getString(), coord, info);
    
            return lm;
        }
    
    
  9. Write code for detecting key presses.

        /**
         * Event indicating when a command button is pressed.
         * 
         * @see javax.microedition.lcdui.CommandListener#commandAction(javax.microedition.lcdui.Command,
         *      javax.microedition.lcdui.Displayable)
         */
        public void commandAction(Command command, Displayable displayable)
        {
            Landmark landmark = null;
    
            // Add new Landmark to the LandmarkStore
            if (command == saveNewCmd)
            {
                landmark = generateLandmark();
                if (landmark != null)
                {
                    try
                    {
                        ControlPoints.getInstance().addLandmark(landmark);
                        data.createProximityListener(coord);
                    }
                    catch (IOException e)
                    {
                        Alert alert = new Alert("Error",
                                "I/O Error during add operation", null,
                                AlertType.ERROR);
                        alert.setTimeout(3000); // 3 secs
                        TouristMIDlet.getDisplay().setCurrent(alert);
                    }
    
                    // New landmark is available on the list
                    showList();
                }
            }
            // Update existing landmark
            else if (command == saveUpdatedCmd)
            {
                landmark = generateLandmark();
                if (landmark != null)
                {
                    try
                    {
                        ControlPoints.getInstance().updateLandmark(landmark);
                        data.createProximityListener(coord);
                    }
                    catch (IOException e)
                    {
                        Alert alert = new Alert("Error",
                                "I/O Error during update operation", null,
                                AlertType.ERROR);
                        alert.setTimeout(3000); // 3 secs
                        TouristMIDlet.getDisplay().setCurrent(alert);
                    }
                    catch (LandmarkException e)
                    {
                        // Landmark instance passed as the parameter does not
                        // belong to this LandmarkStore or does not exist in the
                        // store any more.
                        Alert alert = new Alert("Error",
                                "Unexpected error: can not find landmark from "
                                + "the landmark store.", null,
                                AlertType.ERROR);
                        alert.setTimeout(3000); // 3 secs
                        TouristMIDlet.getDisplay().setCurrent(alert);
                    }
    
                    // Updated landmark is available on the list
                    showList();
                }
            }
            // Go back to the previous screen
            else if (command == routeCmd)
            {
                TouristMIDlet.getDisplay().setCurrent(route);
            }
            // Show landmark editor for the selected landmark.
            else if (command == updateCmd)
            {
                int index = list.getSelectedIndex();
                landmark = ControlPoints.getInstance().findLandmark(index);
    
                showEditor(landmark.getQualifiedCoordinates(), MODE_UPDATE);
            }
            // Remove selected Landmark from LandmarkStore
            else if (command == removeCmd)
            {
                try
                {
                    int index = list.getSelectedIndex();
                    landmark = ControlPoints.getInstance().findLandmark(index);
                    ControlPoints.getInstance().removeLandmark(landmark);
    
                    Alert alert = new Alert("Information",
                            "Landmark removed successfully.", null, AlertType.INFO);
                    alert.setTimeout(3000); // 3 secs
                    TouristMIDlet.getDisplay().setCurrent(alert);
                }
                catch (IOException e)
                {
                    Alert alert = new Alert("Error", "I/O Error during operation",
                            null, AlertType.ERROR);
                    alert.setTimeout(3000); // 3 secs
                    TouristMIDlet.getDisplay().setCurrent(alert);
                }
                catch (LandmarkException le)
                {
                    Alert alert = new Alert("Error",
                            "landmark can not be found from the landmark store.",
                            null, AlertType.ERROR);
                    alert.setTimeout(3000); // 3 secs
                    TouristMIDlet.getDisplay().setCurrent(alert);
                }
    
                showList();
            }
            // Show the list of Landmarks
            else if (command == listCmd)
            {
                showList();
            }
        }
    }
    

MessageUI class

  1. Create the MessageUI class file.

  2. Import the required classes.

    package com.nokia.example.location.tourist.ui;
    
    import javax.microedition.lcdui.Alert;
    import javax.microedition.lcdui.AlertType;
    import javax.microedition.lcdui.Gauge;
    
    import com.nokia.example.location.tourist.TouristMIDlet;
    
    
  3. Create the MessageUI class.

    /**
     * Viewer class containing a general suppose Alert notification(s).
     */
    public class MessageUI
    {
    
  4. Create a method for showing an alert that the Location API is not supported.

        /**
         * Shows a alert that Location API is not supported.
         */
        public static void showApiNotSupported()
        {
            Alert alert = new Alert("Information",
                    "Device do not support Location API", null, AlertType.INFO);
            TouristMIDlet.getDisplay().setCurrent(alert);
        }
    
    
  5. Create a method for showing an alert that location data is unavailable.

        /**
         * Shows a alert that Location data is not available.
         */
        public static void showLocationDataNotAvailable()
        {
            Alert alert = new Alert("Information",
                    "Location data is not yet available.", null, AlertType.INFO);
            TouristMIDlet.getDisplay().setCurrent(alert);
        }
    
    
  6. Create a status dialog about the state of the location provider.

        /**
         * Show a status dialog informing about state of location provider.
         */
        public static void showLocationProviderState()
        {
            Gauge indicator = new Gauge(null, false, 50, 1);
            indicator.setValue(Gauge.CONTINUOUS_RUNNING);
    
            Alert alert = new Alert("Information",
                    "Please wait, looking for location data....", null,
                    AlertType.INFO);
            alert.setIndicator(indicator);
    
            TouristMIDlet.getDisplay().setCurrent(alert);
        }
    }
    

PitchRollUI class

  1. Create the PitchRollUI class file.

  2. Import the required classes.

    package com.nokia.example.location.tourist.ui;
    
    import javax.microedition.lcdui.Canvas;
    import javax.microedition.lcdui.Command;
    import javax.microedition.lcdui.CommandListener;
    import javax.microedition.lcdui.Displayable;
    import javax.microedition.lcdui.Graphics;
    import javax.microedition.location.Orientation;
    
    import com.nokia.example.location.tourist.TouristMIDlet;
    import com.nokia.example.location.tourist.model.ConfigurationProvider;
    
    
  3. Create the PitchRollUI class and set it to extend Canvas and implement CommandListener and Runnable. Define the constants, classes commands used by the class. Also create the constructor.

    /**
     * Viewer class that renders orientations pitch and roll values to meters.
     */
    public class PitchRollUI extends Canvas implements CommandListener, Runnable
    {
        private CompassUI compass;
    
        private float pitch = Float.NaN;
    
        private float roll = Float.NaN;
    
        /** Flag telling is the compass update thread active */
        private boolean threadActive = false;
    
        /** Sleep 1000ms during each compass update */
        private final long SLEEPTIME = 1000;
    
        private Command compassCmd = new Command("Compass", Command.BACK, 1);
    
        public PitchRollUI(CompassUI compass)
        {
            this.compass = compass;
    
            addCommand(compassCmd);
            setCommandListener(this);
        }
    
    
  4. Create the methods that control the compass' update thread.

        /**
         * VM calls this method immediately prior to this Canvas being made visible
         * on the display.
         */
        protected void showNotify()
        {
            // Actives compass update thread.
            threadActive = true;
            new Thread(this).start();
        }
    
        /**
         * VM calls this method shortly after the Canvas has been removed from the
         * display.
         */
        protected void hideNotify()
        {
            // Stops the thread.
            threadActive = false;
        }
    
    
  5. Create a run method for the class. It needs to update the azimuth, pitch and roll values and repaint the canvas. The getPitch method returns the terminal's tilt in degrees defined as an angle in the vertical plane orthogonal to the ground, and through the longitudinal axis of the terminal, whereas the getRoll method returns the terminal's rotation in degrees around its own longitudinal axis.

    For more information, see getPitch and getRoll in the Location API specification.

        /**
         * run method from Runnable interface. Updates azimuth, pitch and roll
         * values and repaints the canvas.
         * 
         * If Orientation is supported on the terminal, support level may be either
         * 2D or 3D depending on the compass sensor. If the sensor providers only
         * compass azimuth, pitch and roll values are Float.NaN.
         */
        public void run()
        {
            // Run this thread until this displayable is not visiable.
            // See also hideNotify() method.
            while (threadActive)
            {
                Orientation orientation = ConfigurationProvider.getInstance()
                        .getOrientation();
    
                if (orientation != null)
                {
                    pitch = orientation.getPitch();
                    roll = orientation.getRoll();
                }
    
                repaint();
    
                try
                {
                    // Pause this thread for a secord before next update.
                    Thread.sleep(SLEEPTIME);
                }
                catch (InterruptedException e)
                {
                }
            }
        }
    
    
  6. Create a method for rendering the canvas.

        /**
         * Renders the canvas.
         * 
         * @param g -
         *            the Graphics object to be used for rendering the Canvas
         */
        protected void paint(Graphics g)
        {
            // clean up canvas
            g.setColor(255, 255, 255);
            g.fillRect(0, 0, getWidth(), getHeight());
    
            paintPitch(g);
            paintRoll(g);
        }
    
    
  7. Create a method for drawing a meter with a scale.

        /**
         * Draws a customized meter with a scale.
         */
        protected void drawMeter(Graphics g, int min, int max, int smallStep,
                int largeStep, int baseline, int margin)
        {
            double position;
            double scale = 100.0; // scale value to minimize rounding error
            int x;
    
            g.setColor(0x0000ff);
            g.drawLine(margin, baseline, getWidth(), baseline);
    
            for (int i = min; i <= max; i += smallStep)
            {
                position = (((i + max) * scale * (getWidth() - margin)) / (2 * max))
                        / scale;
                x = margin + (int) position;
                g.drawLine(x, baseline - 3, x, baseline + 3);
            }
    
            for (int i = min; i <= max; i += largeStep)
            {
                position = (((i + max) * scale * (getWidth() - margin)) / (2 * max))
                        / scale;
                x = margin + (int) position;
                g.drawLine(x, baseline - 5, x, baseline + 5);
            }
        }
    
    
  8. Create a method for painting the pitch meter and placing the current pitch value to it.

        /**
         * Paint pitch meter and place the current pitch value to the meter.
         * 
         * @param g -
         *            the Graphics object to be used for rendering the Canvas
         */
        protected void paintPitch(Graphics g)
        {
            int baseline = 10;
            int textAreaWidth = 50;
            double position;
    
            g.setColor(0x000000);
            g.drawString("Pitch", 0, baseline - 5, Graphics.TOP | Graphics.LEFT);
            g.drawString("-90", textAreaWidth + 1, baseline, Graphics.TOP
                    | Graphics.LEFT);
            g.drawString("0", textAreaWidth + (getWidth() - textAreaWidth) / 2,
                    baseline, Graphics.TOP | Graphics.HCENTER);
            g.drawString("+90", getWidth(), baseline, Graphics.TOP
                    | Graphics.RIGHT);
    
            drawMeter(g, -90, 90, 10, 30, baseline, textAreaWidth);
    
            // Draw the marker only if terminals pitch is available.
            if (pitch != Float.NaN)
            {
                position = (((pitch + 90.0) * 100 * (getWidth() - textAreaWidth)) / (2 * 90)) / 100;
                g.setColor(0x000000);
                g.fillRect(textAreaWidth + (int) position - 3, baseline - 2, 5, 5);
            }
        }
    
    
  9. Create a method for painting the roll meter and placing the current roll value to it.

        /**
         * Paint roll meter and place the current pitch value to the meter.
         * 
         * @param g -
         *            the Graphics object to be used for rendering the Canvas
         */
        protected void paintRoll(Graphics g)
        {
            int baseline = 40;
            int textAreaWidth = 50;
            double position;
    
            g.setColor(0x000000);
            g.drawString("Roll", 0, baseline - 5, Graphics.TOP | Graphics.LEFT);
            g.drawString("-180", textAreaWidth + 1, baseline, Graphics.TOP
                    | Graphics.LEFT);
            g.drawString("0", textAreaWidth + (getWidth() - textAreaWidth) / 2,
                    baseline, Graphics.TOP | Graphics.HCENTER);
            g.drawString("+180", getWidth(), baseline, Graphics.TOP
                    | Graphics.RIGHT);
    
            drawMeter(g, -180, 180, 15, 60, baseline, textAreaWidth);
    
            // Draw the marker only if terminals roll is available.
            if (roll != Float.NaN)
            {
                position = (((roll + 180.0) * 100 * (getWidth() - textAreaWidth)) / (2 * 180)) / 100;
                g.setColor(0x000000);
                g.fillRect(textAreaWidth + (int) position - 3, baseline - 2, 5, 5);
            }
        }
    
    
  10. Create an event for monitoring command button key presses.

        /**
         * Event indicating when a command button is pressed.
         * 
         * @see javax.microedition.lcdui.CommandListener#commandAction(javax.microedition.lcdui.Command,
         *      javax.microedition.lcdui.Displayable)
         */
        public void commandAction(Command command, Displayable d)
        {
            if (command == compassCmd)
            {
                TouristMIDlet.getDisplay().setCurrent(compass);
            }
        }
    
    }
    

ProviderQueryUI class

  1. Create the ProviderQueryUI class file.

  2. Import the required classes.

    package com.nokia.example.location.tourist.ui;
    
    import javax.microedition.lcdui.Alert;
    import javax.microedition.lcdui.AlertType;
    import javax.microedition.lcdui.Command;
    import javax.microedition.lcdui.CommandListener;
    import javax.microedition.lcdui.Displayable;
    import javax.microedition.lcdui.Form;
    import javax.microedition.lcdui.StringItem;
    
    import com.nokia.example.location.tourist.TouristMIDlet;
    
    
  3. Create the ProviderQueryUI class. Define the classes, constants and commands used by the class.

    /**
     * Viewer class that is responsible for all the UI actions when the application
     * is seaching for the location provider.
     */
    public class ProviderQueryUI
    {
        /** Status information Form */
        private Form searchForm = new Form("Seaching location provider...");
    
        /** StringItem showing the current status. */
        private StringItem infoItem = new StringItem("Status:", "");
    
        /** Provider cost selection command - Yes. */
        private Command yesCmd = new Command("Yes", Command.OK, 1);
    
        /** Provider cost selection command - No. */
        private Command noCmd = new Command("No", Command.STOP, 1);
    
        /** A boolean indicating may user allow location provider cost. */
        private boolean result = false;
    
        private static final String COST_QUERY_MESSAGE = "Cost free location providers can not be found. Do you with continue "
                + "search with providers that cost?";
    
        private static final String OUT_OF_SERVICE_MESSAGE = "All Location providers are currently out of service. Please unsure "
                + "that location-providing module is properly connected.";
    
        private static final String SEACHING_FREE_PROVIDERS = "Seaching for free location providers.";
    
        private static final String SEACHING_COST_PROVIDERS = "Seaching for providers that may cost.";
    
        private static final String NOT_FOUND_MESSAGE = "Try again after location-providing module is properly connected.";
    
        private static final String NO_FREE_SERVICE_SERVICE_MESSAGE = "Cost free location providers can not be found. Please ensure "
                + "that location-providing module is properly connected.";
    
    
  4. Create the constructor for the class.

        /**
         * Construct the UI with default values.
         */
        public ProviderQueryUI()
        {
            infoItem.setText(SEACHING_FREE_PROVIDERS);
            searchForm.append(infoItem);
        }
    
    
  5. Create methods for showing "out of service" and "no cost free location provider found" error messages.

        /**
         * Show out of service error message.
         */
        public void showOutOfService()
        {
            Alert alert = new Alert("Error", OUT_OF_SERVICE_MESSAGE, null,
                    AlertType.ERROR);
            alert.setTimeout(Alert.FOREVER);
            TouristMIDlet.getDisplay().setCurrent(alert, searchForm);
            infoItem.setText(NOT_FOUND_MESSAGE);
        }
    
        /**
         * Show no cost free location provider found error message.
         */
        public void showNoFreeServiceFound()
        {
            Alert alert = new Alert("Error", NO_FREE_SERVICE_SERVICE_MESSAGE, null,
                    AlertType.ERROR);
            alert.setTimeout(Alert.FOREVER);
            TouristMIDlet.getDisplay().setCurrent(alert, searchForm);
            infoItem.setText(NOT_FOUND_MESSAGE);
        }
    
    
  6. Create a method for querying the user about the cost of using the location provider.

        /**
         * Query the user whether the use of location provider may cost. The use of
         * this method is locked with synchronized keyword, so only one thread can
         * access this method at once.
         * 
         * @return a boolean indicating may user allow location provider cost.
         */
        public synchronized boolean confirmCostProvider()
        {
            Alert alert = new Alert("Confimnation", COST_QUERY_MESSAGE, null,
                    AlertType.CONFIRMATION);
            alert.addCommand(yesCmd);
            alert.addCommand(noCmd);
            alert.setTimeout(Alert.FOREVER);
    
            // Set the monitoring object to be this instance.
            final ProviderQueryUI hinstance = this;
    
            // Add a CommandLister as anomynous inner class
            alert.setCommandListener(new CommandListener()
            {
                /*
                 * Event indicating when a command button is pressed.
                 * 
                 * @see javax.microedition.lcdui.CommandListener#commandAction(javax.microedition.lcdui.Command,
                 *      javax.microedition.lcdui.Displayable)
                 */
                public void commandAction(Command command, Displayable d)
                {
                    if (command == yesCmd)
                    {
                        infoItem.setText(SEACHING_COST_PROVIDERS);
                        result = true;
                        synchronized (hinstance)
                        {
                            // Wake up the monitoring object
                            hinstance.notifyAll();
                        }
                    }
                    else if (command == noCmd)
                    {
                        result = false;
                        infoItem.setText(NOT_FOUND_MESSAGE);
                        synchronized (hinstance)
                        {
                            // Wake up the monitoring object
                            hinstance.notifyAll();
                        }
                    }
    
                }
            });
    
            TouristMIDlet.getDisplay().setCurrent(alert, searchForm);
    
            // Wait indefinitely for notification.
            try
            {
                wait();
            }
            catch (InterruptedException e)
            {
                e.printStackTrace();
            }
    
            TouristMIDlet.getDisplay().setCurrent(searchForm);
    
            return result;
        }
    
    }
    

TouristUI class

  1. Create the TouristUI class file.

  2. Import the required classes.

    package com.nokia.example.location.tourist.ui;
    
    import javax.microedition.lcdui.Canvas;
    import javax.microedition.lcdui.Command;
    import javax.microedition.lcdui.CommandListener;
    import javax.microedition.lcdui.Displayable;
    import javax.microedition.lcdui.Font;
    import javax.microedition.lcdui.Graphics;
    
    import javax.microedition.location.AddressInfo;
    import javax.microedition.location.QualifiedCoordinates;
    
    import com.nokia.example.location.tourist.TouristMIDlet;
    import com.nokia.example.location.tourist.Utils;
    import com.nokia.example.location.tourist.model.ConfigurationProvider;
    import com.nokia.example.location.tourist.model.TouristData;
    
    
  3. Create the TouristUI class and set it to extend Canvas and implement CommandListener. Define the constants and commands used by the class and create the constructor.

    /**
     * Viewer class that renders current location updates.
     */
    public class TouristUI extends Canvas implements CommandListener
    {
        /** The current state of the location provider as a String */
        private String providerState = "Unknown";
    
        /** Proximity monitoring state. */
        private String proximityState = "Waiting";
    
        private AddressInfo info;
    
        private QualifiedCoordinates coord;
    
        private float speed;
    
        /** Command that shows compass canvas */
        private Command compassCmd = new Command("Compass", Command.OK, 1);
    
        /** Command that shows Landmark editor UI */
        private Command editorCmd = new Command("Editor", Command.STOP, 1);
    
        /** Rerefence to the Landmark editor UI */
        private LandmarkEditorUI editorUI = null;
    
        /** Rerefence to the Compass UI */
        private CompassUI compassUI = null;
    
        public TouristUI(TouristData data)
        {
            editorUI = new LandmarkEditorUI(this, data);
            compassUI = new CompassUI(this);
    
            checkSupportedFeatures();
    
            addCommand(editorCmd);
            setCommandListener(this);
        }
    
    
  4. Create a method for enabling the supported and disabling the unsupported features of the Location API on the UI.

        /**
         * Enable supported Location API features on the UI and disable unsupported
         * features.
         */
        protected void checkSupportedFeatures()
        {
            if (ConfigurationProvider.getInstance().isOrientationSupported())
            {
                addCommand(compassCmd);
            }
            else
            {
                removeCommand(compassCmd);
            }
        }
    
    
  5. Create methods for setting the provider and proximity states.

        public void setProviderState(String state)
        {
            providerState = state;
        }
    
        public void setProximityState(String state)
        {
            proximityState = state;
        }
    
        public void setInfo(AddressInfo info, QualifiedCoordinates coord,
                float speed)
        {
            this.info = info;
            this.coord = coord;
            this.speed = speed;
        }
    
    
  6. Create a method for rendering the canvas.

        /**
         * Renders the canvas.
         * 
         * @param g -
         *            the Graphics object to be used for rendering the Canvas
         */
        protected void paint(Graphics g)
        {
            Font f = Font.getFont(Font.FACE_SYSTEM, Font.STYLE_PLAIN,
                    Font.SIZE_SMALL);
            g.setFont(f);
    
            // use font height as a line height
            int lineHeight = f.getHeight();
            // current line counter
            int line = 0;
    
            // clean the backround
            g.setColor(0xffffff);
            g.fillRect(0, 0, getWidth(), getHeight());
    
            g.setColor(0x0000ff);
            g.drawString("Provider state: " + providerState, 0, lineHeight
                    * (line++), Graphics.LEFT | Graphics.TOP);
            g.drawString("Proximity monitoring: " + proximityState, 0, lineHeight
                    * (line++), Graphics.LEFT | Graphics.TOP);
    
            if (coord != null)
            {
                double lat = coord.getLatitude();
                double lon = coord.getLongitude();
    
                g.drawString("Lat, Lon (" + Utils.formatDouble(lat, 3) + ", "
                        + Utils.formatDouble(lon, 3) + ")", 0, lineHeight
                        * (line++), Graphics.TOP | Graphics.LEFT);
                g.drawString("Speed: " + Utils.formatDouble(speed, 2) + " m/s", 0,
                        lineHeight * (line++), Graphics.TOP | Graphics.LEFT);
            }
    
            // Check if AddressInfo is available
            if (info != null)
            {
                String country = info.getField(AddressInfo.COUNTRY);
                String state = info.getField(AddressInfo.STATE);
                String city = info.getField(AddressInfo.CITY);
                String street = info.getField(AddressInfo.STREET);
                String buildingName = info.getField(AddressInfo.BUILDING_NAME);
    
                g.setColor(0x000000);
    
                if (country != null)
                    g.drawString("Country: " + country, 0, lineHeight * (line++),
                            Graphics.TOP | Graphics.LEFT);
                if (state != null)
                    g.drawString("State: " + state, 0, lineHeight * (line++),
                            Graphics.TOP | Graphics.LEFT);
                if (city != null)
                    g.drawString("City: " + city, 0, lineHeight * (line++),
                            Graphics.TOP | Graphics.LEFT);
                if (street != null)
                    g.drawString("Street: " + street, 0, lineHeight * (line++),
                            Graphics.TOP | Graphics.LEFT);
                if (buildingName != null)
                    g.drawString("Building name: " + buildingName, 0, lineHeight
                            * (line++), Graphics.TOP | Graphics.LEFT);
            }
        }
    
    
  7. Create an event for detecting command key presses.

        /**
         * Event indicating when a command button is pressed.
         * 
         * @see javax.microedition.lcdui.CommandListener#commandAction(javax.microedition.lcdui.Command,
         *      javax.microedition.lcdui.Displayable)
         */
        public void commandAction(Command command, Displayable d)
        {
            if (command == editorCmd)
            {
                if (coord != null)
                {
                    editorUI.showEditor(coord, LandmarkEditorUI.MODE_ADDNEW);
                }
                else
                {
                    MessageUI.showLocationDataNotAvailable();
                }
            }
            else if (command == compassCmd)
            {
                TouristMIDlet.getDisplay().setCurrent(compassUI);
            }
        }
    
    }