This section provides the source code for the Tourist Route. For the complete Eclipse project ZIP file, see Forum Nokia.
The example includes the following classes:
package com.nokia.example.location.tourist; import javax.microedition.lcdui.Display; import javax.microedition.midlet.MIDlet; import javax.microedition.midlet.MIDletStateChangeException; import com.nokia.example.location.tourist.model.ConfigurationProvider; import com.nokia.example.location.tourist.model.ProviderStatusListener; import com.nokia.example.location.tourist.model.TouristData; import com.nokia.example.location.tourist.ui.MessageUI; import com.nokia.example.location.tourist.ui.TouristUI; /** * Tourist Route MIDlet class. */ public class TouristMIDlet extends MIDlet implements ProviderStatusListener { /** A static reference to Display object. */ private static Display display = null; /** A Reference to TouristData. */ private TouristData data = null; /** Lock object */ private Object mutex = new Object(); public TouristMIDlet() { super(); } protected void startApp() throws MIDletStateChangeException { display = Display.getDisplay(this); if (ConfigurationProvider.isLocationApiSupported()) { ConfigurationProvider.getInstance().autoSearch(this); } else { MessageUI.showApiNotSupported(); } } protected void pauseApp() { } protected void destroyApp(boolean unconditional) throws MIDletStateChangeException { } /** * Getter method for Display reference. * * @return reference to Display object. */ public static Display getDisplay() { return display; } /** * Event indicating location provider is selected. MIDlet use may therefore * begin. * * @see com.nokia.example.location.tourist.model.ProviderSelectedListener#providerSelectedEvent() */ public void providerSelectedEvent() { // Attempt to acquire the mutex synchronized (mutex) { // Start scanning location updates. Also set the TouristData // reference data. MessageUI.showLocationProviderState(); // Inform the user that MIDlet is looking for location data. data = new TouristData((ProviderStatusListener) this); } } /** * Event indication about the first location update. This method sets * Tourist UI visible. By using mutex object, we ensure TouristaData (data) * is created on providerSelectedEvent. * * @see com.nokia.example.location.tourist.model.ProviderStatusListener#firstLocationUpdateEvent() */ public void firstLocationUpdateEvent() { // Attempt to acquire the mutex synchronized (mutex) { TouristUI ui = new TouristUI(data); data.setTouristUI(ui); display.setCurrent(ui); } } }
package com.nokia.example.location.tourist; /** * 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); } }
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; /** * 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; /* * 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"; // 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"; } /** * Private constructor to force using getInstance() method. */ private ConfigurationProvider() { queryUI = new ProviderQueryUI(); } /** * 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; } /** * 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; } /** * 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; } } /** * 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; } } }
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; /** * 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; /** * 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); } } /** * 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; } /** * 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; } /** * 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; } 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); } }
package com.nokia.example.location.tourist.model; /** * 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(); }
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; /** * 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; /** * 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()); } } } /** * Setter method to TouristUI reference. * * @param ui - * Reference to TouristUI. */ public void setTouristUI(TouristUI ui) { touristUI = ui; } /** * Adds new ProximityListener to the location provider. This method is * called when constructing instance of this calss 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 } } /** * 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(); } } /** * 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(); } } /** * 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(); } } /** * 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(); } } }
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; /** * 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; /** * 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); } /** * 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(); } } /** * 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; } /** * 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); } /** * 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]); } /** * 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; } /** * 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) { } } } /** * 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); } } }
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; /** * 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); // 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; /** * 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); } /** * 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); } /** * 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); } /** * Check wheter 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; } /** * 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; } /** * 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(); } } }
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; /** * Viewer class containing a general suppose Alert notification(s). */ public class MessageUI { /** * 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); } /** * 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); } /** * 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); } }
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; /** * 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); } /** * 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; } /** * 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) { } } } /** * 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); } /** * 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); } } /** * 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); } } /** * 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); } } /** * 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); } } }
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; /** * 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."; /** * Construct the UI with default values. */ public ProviderQueryUI() { infoItem.setText(SEACHING_FREE_PROVIDERS); searchForm.append(infoItem); } /** * 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); } /** * 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; } }
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; /** * 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); } /** * Enable supported Location API features on the UI and disable unsupported * features. */ protected void checkSupportedFeatures() { if (ConfigurationProvider.getInstance().isOrientationSupported()) { addCommand(compassCmd); } else { removeCommand(compassCmd); } } 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; } /** * 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); } } /** * 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); } } }