Implementing the location functions

Utils class

  1. Create the Utils class file.

  2. Assign the class into the com.nokia.example.location.tourist package.

    package com.nokia.example.location.tourist;
    
    
  3. Create the Utils class. Write the code for creating a String presentation that is formatted for a specified amount of decimals. Don't use rounding rules.

    /**
     * A container class for general purpose utility method(s).
     */
    public class Utils
    {
        /**
         * Creates a String presentation of double value that is formatted to have a
         * certain number of decimals. Formatted output value does not include any
         * rounding rules. Output value is just truncated on the place that is
         * defined by received decimals parameter.
         * 
         * @param value -
         *            double value to be converted.
         * @param decimals -
         *            number of decimals in the String presentation.
         * @return a string representation of the argument.
         */
        public static String formatDouble(double value, int decimals)
        {
            String doubleStr = "" + value;
            int index = doubleStr.indexOf(".") != -1 ? doubleStr.indexOf(".")
                    : doubleStr.indexOf(",");
            // Decimal point can not be found...
            if (index == -1) return doubleStr;
            // Truncate all decimals
            if (decimals == 0)
            {
                return doubleStr.substring(0, index);
            }
    
            int len = index + decimals + 1;
            if (len >= doubleStr.length()) len = doubleStr.length();
    
            double d = Double.parseDouble(doubleStr.substring(0, len));
            return String.valueOf(d);
        }
    }
    

ConfigurationProvider class

  1. Create the ConfigurationProvider class file.

  2. Import the required classes.

    package com.nokia.example.location.tourist.model;
    
    import javax.microedition.location.Criteria;
    import javax.microedition.location.LocationException;
    import javax.microedition.location.LocationProvider;
    import javax.microedition.location.Orientation;
    
    import com.nokia.example.location.tourist.ui.ProviderQueryUI;
    
    
  3. Create a class for handling location provider searches.

    /**
     * Model class that handles location providers search.
     */
    public class ConfigurationProvider
    {
        private static ConfigurationProvider INSTANCE = null;
    
        /** Array of free Criterias. */
        private static Criteria[] freeCriterias = null;
    
        /** String array of free criteria names. */
        private static String[] freeCriteriaNames = null;
    
        /** Array of Criterias that may cause costs */
        private static Criteria[] costCriterias = null;
    
        /** String array of non-free criteria names. */
        private static String[] costCriteriaNames = null;
    
        /** Reference to ProviderQueryUI viewer class. */
        private ProviderQueryUI queryUI = null;
    
        /** Selected criteria */
        private Criteria criteria = null;
    
        /** Selected location provider */
        private LocationProvider provider = null;
    
    
  4. Write a static block for constructing free criteria. The Criteria class is used for the selection of the location provider is defined by the values in this class.

    setHorizontalAccuracy and setVerticalAccuracy set the desired horizontal and vertical accuracy preferences. setPreferredResponseTime sets the desired maximum response time preference. setPreferredPowerConsumption sets the preferred maximum level of power consumption. setCostAllowed sets the preferred cost setting. setSpeedAndCourseRequired sets whether the location provider should be able to determine speed and course. setAltitudeRequired sets whether the location provider should be able to determine altitude. setAddressInfoRequired sets whether the location provider should be able to determine textual address information.

    For more information, see Criteria,setHorizontalAccuracy, setVerticalAccuracy, setPreferredResponseTime, setPreferredPowerConsumption, setCostAllowed, setSpeedAndCourseRequired, setAltitudeRequired, and setAddressInfoRequired in the Location API specification

        /*
         * Static block that constructs the content of free and non-free Criterias.
         * This code block is performed before the constructor.
         */
        static
        {
            // 1. Create pre-defined free criterias
    
            freeCriterias = new Criteria[2];
            freeCriteriaNames = new String[2];
    
            Criteria crit1 = new Criteria();
            crit1.setHorizontalAccuracy(25); // 25m
            crit1.setVerticalAccuracy(25); // 25m
            crit1.setPreferredResponseTime(Criteria.NO_REQUIREMENT);
            crit1.setPreferredPowerConsumption(Criteria.POWER_USAGE_HIGH);
            crit1.setCostAllowed(false);
            crit1.setSpeedAndCourseRequired(true);
            crit1.setAltitudeRequired(true);
            crit1.setAddressInfoRequired(true);
    
            freeCriterias[0] = crit1;
            freeCriteriaNames[0] = "High details, cost not allowed";
    
            Criteria crit2 = new Criteria();
            crit2.setHorizontalAccuracy(Criteria.NO_REQUIREMENT);
            crit2.setVerticalAccuracy(Criteria.NO_REQUIREMENT);
            crit2.setPreferredResponseTime(Criteria.NO_REQUIREMENT);
            crit2.setPreferredPowerConsumption(Criteria.POWER_USAGE_HIGH);
            crit2.setCostAllowed(false); // allowed to cost
            crit2.setSpeedAndCourseRequired(false);
            crit2.setAltitudeRequired(false);
            crit2.setAddressInfoRequired(false);
    
            freeCriterias[1] = crit2;
            freeCriteriaNames[1] = "Low details and power consumption, cost not allowed";
    
    

    Write a static block for constructing non-free criteria.

            // 2. Create pre-defined cost criterias
    
            costCriterias = new Criteria[3];
            costCriteriaNames = new String[3];
    
            Criteria crit3 = new Criteria();
            crit3.setHorizontalAccuracy(25); // 25m
            crit3.setVerticalAccuracy(25); // 25m
            crit3.setPreferredResponseTime(Criteria.NO_REQUIREMENT);
            crit3.setPreferredPowerConsumption(Criteria.NO_REQUIREMENT);
            crit3.setCostAllowed(true);
            crit3.setSpeedAndCourseRequired(true);
            crit3.setAltitudeRequired(true);
            crit3.setAddressInfoRequired(true);
    
            costCriterias[0] = crit3;
            costCriteriaNames[0] = "High details, cost allowed";
    
            Criteria crit4 = new Criteria();
            crit4.setHorizontalAccuracy(500); // 500m
            crit4.setVerticalAccuracy(Criteria.NO_REQUIREMENT);
            crit4.setPreferredResponseTime(Criteria.NO_REQUIREMENT);
            crit4.setPreferredPowerConsumption(Criteria.NO_REQUIREMENT);
            crit4.setCostAllowed(true);
            crit4.setSpeedAndCourseRequired(true);
            crit4.setAltitudeRequired(true);
            crit4.setAddressInfoRequired(false);
    
            costCriterias[1] = crit4;
            costCriteriaNames[1] = "Medium details, cost allowed";
    
            // Least restrictive criteria (with default values)
            Criteria crit5 = null;
    
            costCriterias[2] = crit5;
            costCriteriaNames[2] = "Least restrictive criteria";
        }
    
    
  5. Create the constructor for this class.

        /**
         * Private constructor to force using getInstance() method.
         */
        private ConfigurationProvider()
        {
            queryUI = new ProviderQueryUI();
        }
    
    
  6. Create a method for providing a single instance of the class.

        /**
         * Provide singleton instance of this class.
         * 
         * @return static instance of this class.
         */
        public static ConfigurationProvider getInstance()
        {
            if (INSTANCE == null)
            {
                // Enable use of this class when Location API is supported.
                if (isLocationApiSupported())
                {
                    INSTANCE = new ConfigurationProvider();
                }
                else
                {
                    INSTANCE = null;
                }
            }
    
            return INSTANCE;
        }
    
    
  7. Create a method for checking whether the Location API is supported.

        /**
         * Checks whether Location API is supported.
         * 
         * @return a boolean indicating is Location API supported.
         */
        public static boolean isLocationApiSupported()
        {
            String version = System.getProperty("microedition.location.version");
            return (version != null && !version.equals("")) ? true : false;
        }
    
        public LocationProvider getSelectedProvider()
        {
            return provider;
        }
    
    
  8. Create a method for searching the location provider with the use of the pre-defined criteria. The getInstance is a factory method used to get an actual LocationProvider implementation based on the defined criteria. LocationException is thrown when a location API specific error has occurred. The getOrientation method returns the terminal's current orientation.

    For more information, see getInstance, LocationException, and getOrientation in the Location API specification.

        /**
         * Search location provider by using pre-defined free and cost criterias.
         * 
         * @param listener - 
         *          a event listener that listens ProviderStatusLisneter events.
         */
        public void autoSearch(ProviderStatusListener listener)
        {
            try
            {
                for (int i = 0; i < freeCriterias.length; i++)
                {
                    criteria = freeCriterias[i];
    
                    provider = LocationProvider.getInstance(criteria);
                    if (provider != null)
                    {
                        // Location provider found, send a selection event.
                        listener.providerSelectedEvent();
                        return;
                    }
                }
    
                if (queryUI.confirmCostProvider())
                {
                    for (int i = 0; i < costCriterias.length; i++)
                    {
                        criteria = costCriterias[i];
    
                        provider = LocationProvider.getInstance(criteria);
                        if (provider != null)
                        {
                            // Location provider found, send a selection event.
                            listener.providerSelectedEvent();
                            return;
                        }
                    }
                }
                else
                {
                    queryUI.showNoFreeServiceFound();
                }
            }
            catch (LocationException le)
            {
                queryUI.showOutOfService();
            }
        }
    
        public Orientation getOrientation()
        {
            try
            {
                return Orientation.getOrientation();
            }
            catch (LocationException e)
            {
                return null;
            }
        }
    
    
  9. Create a method for determining whether orientation is supported.

        /**
         * Tells whether orientation is supported.
         * 
         * @return a boolean indicating is orientation supported.
         */
        public boolean isOrientationSupported()
        {
            try
            {
                // Test whether Orientation instance can be obtained.
                Orientation.getOrientation();
                return true;
            }
            catch (LocationException e)
            {
                return false;
            }
        }
    }

ControlPoints class

  1. Create the ControlPoints class file.

  2. Import the required classes.

    package com.nokia.example.location.tourist.model;
    
    import java.io.IOException;
    import java.util.Enumeration;
    
    import javax.microedition.location.Coordinates;
    import javax.microedition.location.Landmark;
    import javax.microedition.location.LandmarkException;
    import javax.microedition.location.LandmarkStore;
    
    
  3. Create the ControlPoints class and define constants used by the class. LandmarkStore class provides methods to store, delete and retrieve landmarks from a persistent landmark store.

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

    /**
     * Model class that handles landmark store actions.
     */
    public class ControlPoints
    {
        private LandmarkStore store = null;
    
        private static final String STORENAME = "TOURIST_DEMO";
    
        private static final String DEFAULT_CATEGORY = null;
    
        private static ControlPoints INSTANCE = null;
    
    
  4. Create the constructor. The createLandmarkStore method creates a new landmark store with a specified name. The LandmarkException is thrown when an error related to handling landmarks has occurred.

    For more information, see createLandmarkStore and LandmarkException in the Location API specification.

        /**
         * Constructs instance of this class. Makes sure that landmark store
         * instance exists.
         */
        private ControlPoints()
        {
            String name = null;
            // Try to find landmark store called "TOURIST_DEMO".
            try
            {
                store = LandmarkStore.getInstance(STORENAME);
            }
            catch (NullPointerException npe)
            {
                // This should never occur.
            }
    
            // Check whether landmark store exists.
            if (store == null)
            {
                // Landmark store does not exist.
    
                try
                {
                    // Try to create landmark store named "TOURIST_DEMO".
                    LandmarkStore.createLandmarkStore(STORENAME);
                    name = STORENAME;
                }
                catch (IllegalArgumentException iae)
                {
                    // Name is too long or landmark store with the specified
                    // name already exists. This Exception should not occur,
                    // because we have earlier tryed to created instance of
                    // this landmark store.
                }
                catch (IOException e)
                {
                    // Landmark store couldn't be created due to an I/O error
                }
                catch (LandmarkException e)
                {
                    // Implementation does not support creating new landmark
                    // stores.
                }
    
                store = LandmarkStore.getInstance(name);
            }
        }
    
    
  5. Create a singleton patterns getInstance method.

        /**
         * Singleton patterns getInstance method. Makes sure that only one instance
         * of this class is alive at once.
         * 
         * @return instance of this class.
         */
        public static ControlPoints getInstance()
        {
            if (INSTANCE == null)
            {
                INSTANCE = new ControlPoints();
            }
    
            return INSTANCE;
        }
    
    
  6. Create a method for finding a landmark from the landmark store by the use of an index. The Landmark class represents a landmark, i.e. a known location with a name.

        /**
         * Find a Landmark from the landmark store using a index.
         * 
         * @param index -
         *            the landmark in the store.
         * @return Landmark from landmark store.
         */
        public Landmark findLandmark(int index)
        {
            Landmark lm = null;
    
            Enumeration cps = ControlPoints.getInstance().getLandMarks();
            int counter = 0;
    
            while (cps.hasMoreElements())
            {
                lm = (Landmark) cps.nextElement();
    
                if (counter == index)
                {
                    break;
                }
                counter++;
            }
    
            return lm;
        }
    
    
  7. getLandmarks method lists all landmarks stored in the store.

    The getLatitude and getLongtitude return the latitude/longtitude component of this coordinate. The getQualifiedCoordinates gets the QualifiedCoordinates of the landmark.

    For more information, see getLandmarks, getLatitude, getLongtitude, and getQualifiedCoordinates in the Location API specification.

        /**
         * Find nearest landmark to coordinate parameter from the landmarkstore.
         */
        public Landmark findNearestLandMark(Coordinates coord)
        {
            Landmark landmark = null;
    
            double latRadius = 0.1;
            double lonRadius = 0.1;
    
            float dist = Float.MAX_VALUE;
    
            try
            {
                // Generate enumeration of Landmarks that are close to coord.
                Enumeration enu = store.getLandmarks(null, coord.getLatitude()
                        - latRadius, coord.getLatitude() + latRadius, coord
                        .getLongitude() - lonRadius, coord.getLongitude() 
                        + lonRadius);
                float tmpDist;
                Landmark tmpLandmark = null;
    
                while (enu.hasMoreElements())
                {
                    tmpLandmark = (Landmark) enu.nextElement();
                    tmpDist = tmpLandmark.getQualifiedCoordinates().distance(coord);
    
                    if (tmpDist < dist)
                    {
                        landmark = tmpLandmark;
                    }
                }
            }
            catch (IOException ioe)
            {
                // I/O error happened when accessing the landmark store.
                return null;
            }
    
            return landmark;
        }
    
        public Enumeration getLandMarks()
        {
            Enumeration enu = null;
    
            try
            {
                enu = store.getLandmarks();
            }
            catch (IOException ioe)
            {
                // I/O error happened when accessing the landmark store.
            }
    
            return enu;
        }
    
    
  8. Create the various adding, updating and removing methods used by the class.

        public void addLandmark(Landmark landmark) throws IOException
        {
            store.addLandmark(landmark, DEFAULT_CATEGORY);
        }
    
        public void updateLandmark(Landmark landmark) throws IOException,
                LandmarkException
        {
            store.updateLandmark(landmark);
        }
    
        public void removeLandmark(Landmark landmark) throws IOException,
                LandmarkException
        {
            store.deleteLandmark(landmark);
        }
    
    }

ProviderStatusListener class

  1. Create the ProviderStatusListener class file.

  2. Include the class to the com.nokia.example.location.tourist.model package.

    package com.nokia.example.location.tourist.model;
    
    
  3. Create the class and add the two notification events described below to it.

    /**
     * Listener interface for location providers status information.
     */
    public interface ProviderStatusListener
    {
        /**
         * A Notification event that location provider has been selected.
         */
        public void providerSelectedEvent();
    
        /**
         * A Notification event about the first location update.
         */
        public void firstLocationUpdateEvent();
    }
    

TouristData class

  1. Create the TouristData class file.

  2. Import the required classes.

    package com.nokia.example.location.tourist.model;
    
    import java.util.Enumeration;
    
    import javax.microedition.location.AddressInfo;
    import javax.microedition.location.Coordinates;
    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.ProximityListener;
    import javax.microedition.location.QualifiedCoordinates;
    
    import com.nokia.example.location.tourist.ui.TouristUI;
    
    
  3. Create the TouristData class and set it to implement LocationListener and ProximityListener. The LocationListener represents a listener that receives events associated with a particular LocationProvider. The ProximityListener is an interface representing a listener to events associated with detecting proximity to some registered coordinates. The Coordinates class represents coordinates as latitude-longitude-altitude values.

    For more information, see LocationListener, ProximityListener, and Coordinates in the Location API specification.

    /**
     * Model class that handles LocationListeners and ProximityListeners events.
     */
    public class TouristData implements LocationListener, ProximityListener
    {
        /** A Reference to Tourist UI Canvas */
        private TouristUI touristUI = null;
    
        /** Coordinate detection threshold radius in meters */
        public static final float PROXIMITY_RADIUS = 100.0f;
    
        /** Previous coordinates outside the distance threshold area (20m) */
        private Coordinates prevCoordinates = null;
    
        /** The first location update done. */
        private boolean firstLocationUpdate = false;
    
        private ProviderStatusListener statusListener = null;
    
    
  4. Create a constructor for the class. setLocationListener adds a LocationListener for updates at the defined interval.

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

        /**
         * Construct instance of this model class.
         */
        public TouristData(ProviderStatusListener listener)
        {
            statusListener = listener;
    
            ConfigurationProvider config = ConfigurationProvider.getInstance();
    
            // 1. Register LocationListener
            LocationProvider provider = config.getSelectedProvider();
            if (provider != null)
            {
                int interval = -1; // default interval of this provider
                int timeout = 0; // parameter has no effect.
                int maxage = 0; // parameter has no effect.
    
                provider.setLocationListener(this, interval, timeout, maxage);
            }
    
            // 2. Register ProxymityListener to each Landmark found from the
            // Landmark store.
            ControlPoints cp = ControlPoints.getInstance();
    
            Enumeration enumeration = cp.getLandMarks();
            if (enumeration != null)
            {
                while (enumeration.hasMoreElements())
                {
                    Landmark lm = (Landmark) enumeration.nextElement();
                    createProximityListener(lm.getQualifiedCoordinates());
                }
            }
        }
    
    
  5. Create a setter method to TouristUI reference.

        /**
         * Setter method to TouristUI reference.
         * 
         * @param ui -
         *            Reference to TouristUI.
         */
        public void setTouristUI(TouristUI ui)
        {
            touristUI = ui;
        }
    
    
  6. Create a method for adding a new ProximityListener to a location provider. addProximityListener adds a ProximityListener for updates when proximity to the specified coordinates is detected.

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

        /**
         * Adds new ProximityListener to the location provider. This method is
         * called when constructing instance of this class and when a new landmark
         * is saved by using LandmarkEditorUI.
         * 
         * @param coordinates
         */
        public void createProximityListener(Coordinates coordinates)
        {
            try
            {
                LocationProvider.addProximityListener(this, coordinates,
                        PROXIMITY_RADIUS);
            }
            catch (LocationException e)
            {
                // Platform does not have resources to add a new listener
                // and coordinates to be monitored or does not support
                // proximity monitoring at all
            }
        }
    
    
  7. Create a update event from LocationListener to start a new thread in order to prevent blocking. The Location class represents the standard set of basic location information. The isValid method returns whether this Location instance represents a valid location with coordinates or an invalid one where all the data, especially the latitude and longitude coordinates, may not be present. The AddressInfo class holds textual address information about a location and the getAddress method returns the AddressInfo associated with this Location object. The getSpeed method returns the terminal's current ground speed in meters per second (m/s) at the time of measurement.

    For more information, see Location, isValid, AddressInfo, and getSpeed in the Location API specification.

        /**
         * locationUpdated event from LocationListener interface. This method starts
         * a new thread to prevent blocking, because listener method MUST return
         * quickly and should not perform any extensive processing.
         * 
         * Location parameter is set final, so that the anonymous Thread class can
         * access the value.
         */
        public void locationUpdated(LocationProvider provider,
                final Location location)
        {
            // First location update arrived, so we may show the UI (TouristUI)
            if (!firstLocationUpdate)
            {
                firstLocationUpdate = true;
                statusListener.firstLocationUpdateEvent();
            }
    
            if (touristUI != null)
            {
                new Thread()
                {
                    public void run()
                    {
                        if (location != null && location.isValid())
                        {
                            AddressInfo address = location.getAddressInfo();
                            QualifiedCoordinates coord = location.getQualifiedCoordinates();
                            float speed = location.getSpeed();
                            
                            touristUI.setInfo(address, coord, speed);
                            touristUI.setProviderState("Available");
                            touristUI.repaint();
                        }
                        else
                        {
                            touristUI.setProviderState("Not valid location data");
                            touristUI.repaint();
                        }
                    }
                }.start();
            }
        }
    
    
  8. Create a state changed event from LocationListener to start a new thread in order to prevent blocking.

        /**
         * providerStateChanged event from LocationListener interface. This method
         * starts a new thread to prevent blocking, because listener method MUST
         * return quickly and should not perform any extensive processing.
         * 
         * newState parameter is set final, so that the anonymous Thread class can
         * access the value.
         */
        public void providerStateChanged(LocationProvider provider,
                final int newState)
        {
            if (touristUI != null)
            {
                new Thread()
                {
                    public void run()
                    {
                        switch (newState) {
                            case LocationProvider.AVAILABLE:
                                touristUI.setProviderState("Available");
                                break;
                            case LocationProvider.OUT_OF_SERVICE:
                                touristUI.setProviderState("Out of service");
                                break;
                            case LocationProvider.TEMPORARILY_UNAVAILABLE:
                                touristUI
                                        .setProviderState("Temporarily unavailable");
                                break;
                            default:
                                touristUI.setProviderState("Unknown");
                                break;
                        }
    
                        touristUI.repaint();
                    }
                }.start();
            }
        }
    
    
  9. Create a proximity event for ProximityListener, to be called only once when the terminal enters the proximity of the registered coordinates. The getAddressInfo method returns the AddressInfo associated with this Location object.

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

        /**
         * proximity event from ProximityListener interface. The listener is called
         * only once when the terminal enters the proximity of the registered
         * coordinates. That's why no this method should not need to start a new
         * thread.
         */
        public void proximityEvent(Coordinates coordinates, Location location)
        {
            if (touristUI != null)
            {
                touristUI.setProviderState("Control point found!");
    
                Landmark lm = ControlPoints.getInstance().findNearestLandMark(
                        coordinates);
    
                // landmark found from landmark store
                if (lm != null)
                {
                    touristUI.setInfo(lm.getAddressInfo(), lm
                            .getQualifiedCoordinates(), location.getSpeed());
                }
                // landmark not found from the landmark store, this should not never
                // happen!
                else
                {
                    touristUI.setInfo(location.getAddressInfo(), location
                            .getQualifiedCoordinates(), location.getSpeed());
                }
    
                touristUI.repaint();
            }
        }
    
    
  10. Create state changed event from ProximityListener to notify that the state of the monitoring has changed.

        /**
         * monitoringStateChanged event from ProximityListener interface. Notifies
         * that the state of the proximity monitoring has changed. That's why this
         * method should not need to start a new thread.
         */
        public void monitoringStateChanged(boolean isMonitoringActive)
        {
            if (touristUI != null)
            {
                if (isMonitoringActive)
                {
                    // proximity monitoring is active
                    touristUI.setProximityState("Active");
                }
                else
                {
                    // proximity monitoring can't be done currently.
                    touristUI.setProximityState("Off");
                }
    
                touristUI.repaint();
            }
        }
    }