Create the Utils
class
file.
Assign the
class into the com.nokia.example.location.tourist
package.
package com.nokia.example.location.tourist;
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); } }
Create the ConfigurationProvider
class
file.
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;
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;
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"; }
Create the constructor for this class.
/** * Private constructor to force using getInstance() method. */ private ConfigurationProvider() { queryUI = new ProviderQueryUI(); }
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; }
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; }
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; } }
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; } } }
Create the ControlPoints
class
file.
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;
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;
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); } }
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; }
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; }
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 QualifiedCoordinate
s 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; }
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); } }
Create the ProviderStatusListener
class
file.
Include the
class to the com.nokia.example.location.tourist.model
package.
package com.nokia.example.location.tourist.model;
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(); }
Create the TouristData
class
file.
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;
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;
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()); } } }
Create a setter
method to TouristUI
reference.
/** * Setter method to TouristUI reference. * * @param ui - * Reference to TouristUI. */ public void setTouristUI(TouristUI ui) { touristUI = ui; }
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 } }
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(); } }
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(); } }
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(); } }
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(); } } }