001: /*
002: *
003: * Copyright (c) 2007, Sun Microsystems, Inc.
004: *
005: * All rights reserved.
006: *
007: * Redistribution and use in source and binary forms, with or without
008: * modification, are permitted provided that the following conditions
009: * are met:
010: *
011: * * Redistributions of source code must retain the above copyright
012: * notice, this list of conditions and the following disclaimer.
013: * * Redistributions in binary form must reproduce the above copyright
014: * notice, this list of conditions and the following disclaimer in the
015: * documentation and/or other materials provided with the distribution.
016: * * Neither the name of Sun Microsystems nor the names of its contributors
017: * may be used to endorse or promote products derived from this software
018: * without specific prior written permission.
019: *
020: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
021: * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
022: * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
023: * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
024: * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
025: * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
026: * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
027: * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
028: * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
029: * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
030: * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
031: */
032: package examples.cityguide;
033:
034: import java.io.IOException;
035: import java.io.InputStream;
036: import java.io.InputStreamReader;
037:
038: import java.util.Enumeration;
039: import java.util.Hashtable;
040: import java.util.NoSuchElementException;
041: import java.util.Vector;
042:
043: import javax.microedition.lcdui.*;
044: import javax.microedition.lcdui.List;
045: import javax.microedition.location.AddressInfo;
046: import javax.microedition.location.Coordinates;
047: import javax.microedition.location.Criteria;
048: import javax.microedition.location.Landmark;
049: import javax.microedition.location.LandmarkStore;
050: import javax.microedition.location.LocationException;
051: import javax.microedition.location.LocationProvider;
052: import javax.microedition.location.QualifiedCoordinates;
053: import javax.microedition.midlet.*;
054:
055: /**
056: * Main CityGuide MIDlet
057: */
058: public class CityGuideMIDlet extends MIDlet implements CommandListener {
059: /** landmark store name to store points of interest */
060: private static final String LANDMARKSTORE_NAME = "waypoints";
061:
062: /** welcome text for welcome screen */
063: private static final String WELCOME_TEXT = "Welcome to The City Guide. First JSR179 enabled city guide."
064: + " Select your favorite landmark categories and never miss interesting place while walking through the city."
065: + " Don't forget to run the citywalk.xml script.\n\n"
066: + "Enjoy the show!\n\n";
067:
068: /** explanation label for settings screen */
069: private static final String SETTINGS_TEXT = "The City Guide will alert you whenever you get close to landmark of selected category.";
070:
071: /** choice group label for categories */
072: private static final String CHOICEGRP_TEXT = "Watch for categories:";
073:
074: /** no landmark in proximity */
075: private static final String NOLANDMARK_TEXT = "Sorry no landmark in proximity now.\nKeep on walking.";
076:
077: /** map corners coordinates */
078: private static final Coordinates MAP_TOP_LEFT_COORDINATES = new Coordinates(
079: 14.387594174164514, 50.1049422484619, 310);
080: private static final Coordinates MAP_BOTTOM_RIGHT_COORDINATES = new Coordinates(
081: 14.40423976700292, 50.09531507039965, 310);
082:
083: /** initial visitor coordinates */
084: private static final Coordinates MAP_VISITOR_COORDINATES = new Coordinates(
085: 14.389796708964603, 50.09985002736201, 310);
086: private static final String[] PRELOAD_IMAGES = { "map",
087: "visitoron", "visitoroff", "anim1", "anim2", "anim3",
088: "anim4", "anim5", "anim6", "anim7", "anim8", "logo" };
089: private static CityGuideMIDlet instance;
090:
091: /** error screen uses last error occurred */
092: private String lastError = "";
093:
094: /** list of selected categories */
095: private Vector selectedCategories;
096:
097: /** landmark store containing points of interest */
098: private LandmarkStore landmarkStore = null;
099:
100: /** location provider */
101: private LocationProvider locationProvider = null;
102:
103: /** distance when proximity event is sent */
104: private float proximityRadius = 50.0f;
105:
106: /** image cache */
107: private ImageManager imageManager = null;
108:
109: /** landmarks successfully loaded */
110: private boolean landmarksLoaded = false;
111:
112: /** main MIDlet display */
113: private Display display;
114:
115: /** map canvas */
116: private MapCanvas mapCanvas;
117:
118: /** city map */
119: private CityMap cityMap;
120:
121: /** Screens and commands */
122: private Command WelcomeScreen_nextCommand;
123: private Command ProgressScreen_nextCommand;
124: private Command showErrorCommand;
125: private Command SettingsScreen_backCommand;
126: private Command DetailsScreen_backCommand;
127: private Command exitCommand;
128: private Command closeCommand;
129: private final Command detailsCommand;
130: private final Command settingsCommand;
131: private Gauge progressGauge;
132: private Form progressScreen;
133: private Form welcomeScreen;
134: private Form settingsScreen;
135: private Form detailsScreen;
136: private Alert errorAlert;
137:
138: /**
139: * Main City Guide MIDlet
140: */
141: public CityGuideMIDlet() {
142: exitCommand = new Command("Exit", Command.EXIT, 1);
143: closeCommand = new Command("Close", Command.SCREEN, 1);
144: showErrorCommand = new Command("Error", Command.SCREEN, 1);
145: detailsCommand = new Command("Detail", Command.SCREEN, 1);
146: settingsCommand = new Command("Settings", Command.SCREEN, 1);
147: display = Display.getDisplay(this );
148: imageManager = ImageManager.getInstance();
149: imageManager.getImage("logo");
150: createLocationProvider();
151:
152: if (locationProvider == null) {
153: System.out.println("Cannot run without location provider!");
154: destroyApp(false);
155: notifyDestroyed();
156: }
157:
158: instance = this ;
159: }
160:
161: public static CityGuideMIDlet getInstance() {
162: if (instance == null) {
163: instance = new CityGuideMIDlet();
164: }
165:
166: return instance;
167: }
168:
169: public void startApp() {
170: if (display.getCurrent() == null) {
171: // startApp called for the first time
172: showWelcomeScreen();
173: } else {
174: if (cityMap != null) {
175: cityMap.enable();
176: }
177: }
178: }
179:
180: public void pauseApp() {
181: if (cityMap != null) {
182: cityMap.disable();
183: }
184: }
185:
186: public void destroyApp(boolean unconditional) {
187: if (cityMap != null) {
188: cityMap.cleanup();
189: }
190: }
191:
192: /**
193: * Action handler
194: */
195: public void commandAction(Command c, Displayable s) {
196: if (c == detailsCommand) {
197: showDetailsScreen();
198: }
199:
200: if (c == settingsCommand) {
201: showSettingsScreen(true);
202: }
203:
204: if (c == WelcomeScreen_nextCommand) {
205: if (landmarkStore == null) {
206: showProgressScreen("Loading landmarks ...", 20);
207: }
208:
209: //load landmarks from resources
210: Thread loadLandmarksThread = new Thread() {
211: public void run() {
212: loadLandmarks();
213: }
214: };
215:
216: loadLandmarksThread.start();
217:
218: //load images from resources
219: Thread loadImagesThread = new Thread() {
220: public void run() {
221: loadImages();
222: }
223: };
224:
225: loadImagesThread.start();
226: }
227:
228: if (c == ProgressScreen_nextCommand) {
229: //initialize categories
230: showSettingsScreen(false);
231: //update map display with changed set of selected categories
232: selectedCategoriesChanged();
233: //initialize map display
234: showMapCanvas(true);
235: }
236:
237: if (c == SettingsScreen_backCommand) {
238: selectedCategoriesChanged();
239: showMapCanvas(true);
240: }
241:
242: if (c == DetailsScreen_backCommand) {
243: showMapCanvas(false);
244: }
245:
246: if (c == showErrorCommand) {
247: showErrorForm(lastError);
248: }
249:
250: if (c == closeCommand) {
251: if (display.getCurrent() == errorAlert) {
252: display.setCurrent(welcomeScreen);
253: }
254: }
255:
256: if (c == exitCommand) {
257: if ((display.getCurrent() == settingsScreen)
258: || (display.getCurrent() == mapCanvas)) {
259: display.setCurrent(welcomeScreen);
260: }
261:
262: if (display.getCurrent() == welcomeScreen) {
263: destroyApp(false);
264: notifyDestroyed();
265: }
266: }
267: }
268:
269: /**
270: * Initializes LocationProvider
271: * uses default criteria
272: */
273: void createLocationProvider() {
274: if (locationProvider == null) {
275: Criteria criteria = new Criteria();
276:
277: try {
278: locationProvider = LocationProvider
279: .getInstance(criteria);
280: } catch (LocationException le) {
281: System.out
282: .println("Cannot create LocationProvider for this criteria.");
283: le.printStackTrace();
284: }
285: }
286: }
287:
288: private void generateAndDisplayMap() {
289: if (cityMap == null) {
290: try {
291: cityMap = new CityMap(new String[] { "map",
292: "visitoron", "visitoroff" },
293: MAP_TOP_LEFT_COORDINATES,
294: MAP_BOTTOM_RIGHT_COORDINATES,
295: MAP_VISITOR_COORDINATES, selectedCategories,
296: ImageManager.getInstance(), LandmarkStore
297: .getInstance(LANDMARKSTORE_NAME),
298: LocationProvider.getInstance(new Criteria()));
299: } catch (LocationException e) {
300: e.printStackTrace();
301: destroyApp(false);
302: notifyDestroyed();
303:
304: return;
305: }
306: } else {
307: cityMap.setCategories(selectedCategories);
308: }
309:
310: if (mapCanvas == null) {
311: mapCanvas = new MapCanvas(cityMap);
312: mapCanvas.addCommand(exitCommand);
313: mapCanvas.addCommand(detailsCommand);
314: mapCanvas.addCommand(settingsCommand);
315: mapCanvas.setCommandListener(this );
316: }
317:
318: display.setCurrent(mapCanvas);
319: }
320:
321: /**
322: * Initializes map display and sets listener for location updates
323: * use LocationProvider defaults
324: */
325: public void showMapCanvas(boolean updateLandmarks) {
326: if (updateLandmarks || (mapCanvas == null)) {
327: Form progressForm = new Form(null);
328: progressForm.append(new StringItem("Generating map...",
329: null));
330: display.setCurrent(progressForm);
331:
332: Thread mapGeneratorThread = new Thread() {
333: public void run() {
334: generateAndDisplayMap();
335: }
336: };
337:
338: mapGeneratorThread.start();
339: } else {
340: display.setCurrent(mapCanvas);
341: }
342: }
343:
344: /**
345: * Show error screen with last error
346: */
347: public void showErrorForm(String message) {
348: if (errorAlert == null) {
349: errorAlert = new Alert("Error", message, null,
350: AlertType.ERROR);
351: errorAlert.addCommand(closeCommand);
352: errorAlert.setCommandListener(this );
353: }
354:
355: errorAlert.setString(message);
356: display.setCurrent(errorAlert);
357: }
358:
359: /**
360: * Progress screen while loading landmarks
361: * Especially while creating new landmark store from
362: * text resource file
363: */
364: public void showProgressScreen(String title, int size) {
365: if (progressGauge == null) {
366: progressGauge = new Gauge(title, false, size, 0);
367: progressGauge.setLayout(Item.LAYOUT_CENTER
368: | Item.LAYOUT_EXPAND | Item.LAYOUT_VCENTER);
369: }
370:
371: progressGauge.setValue(0);
372:
373: if (progressScreen == null) {
374: progressScreen = new Form(null);
375: progressScreen.append(progressGauge);
376: progressScreen.addCommand(exitCommand);
377: ProgressScreen_nextCommand = new Command("Next",
378: Command.SCREEN, 1);
379: progressScreen.setCommandListener(this );
380: }
381:
382: display.setCurrent(progressScreen);
383: }
384:
385: /**
386: * Settings screen let user select categories
387: * of points of interest he/she is interested in.
388: * Only those will be displayed on the map.
389: * Contains choice group of categories
390: */
391: public void showSettingsScreen(boolean show) {
392: if (settingsScreen == null) {
393: String[] categories = Util.asArray(landmarkStore
394: .getCategories());
395: Image[] images = new Image[categories.length];
396:
397: for (int i = 0; i < categories.length; i++)
398: images[i] = imageManager.getImage(categories[i]);
399:
400: Item[] items = new Item[] {
401: new StringItem(null, SETTINGS_TEXT),
402: new ChoiceGroup(CHOICEGRP_TEXT,
403: ChoiceGroup.MULTIPLE, categories, images) };
404: settingsScreen = new Form("Settings", items);
405:
406: boolean[] flags = new boolean[categories.length];
407:
408: for (int i = 0; i < flags.length; i++) {
409: flags[i] = true;
410: }
411:
412: ((ChoiceGroup) settingsScreen.get(1))
413: .setSelectedFlags(flags);
414: SettingsScreen_backCommand = new Command("Back",
415: Command.SCREEN, 1);
416: settingsScreen.addCommand(SettingsScreen_backCommand);
417: settingsScreen.setCommandListener(this );
418: }
419:
420: if (show) {
421: display.setCurrent(settingsScreen);
422: }
423: }
424:
425: /**
426: * Details screen contains detailed info about
427: * landmarks. It's used for landmarks which
428: * appear in proximity radius.
429: */
430: public void showDetailsScreen() {
431: MapLandmark[] mapLandmarks = cityMap.getMapLandmarks();
432: int numActive = 0;
433:
434: for (int i = 0; i < mapLandmarks.length; ++i) {
435: if (mapLandmarks[i].isActive()) {
436: ++numActive;
437: }
438: }
439:
440: Item[] items = null;
441:
442: if (numActive == 0) {
443: items = new Item[] { new StringItem(null, NOLANDMARK_TEXT) };
444: } else {
445: int NUMBER_OF_ITEMS = 7;
446: items = new Item[numActive * NUMBER_OF_ITEMS];
447:
448: Landmark l = null;
449: AddressInfo address = null;
450: int i = 0;
451:
452: for (int j = 0; j < mapLandmarks.length; ++j) {
453: if (mapLandmarks[j].isActive()) {
454: l = (Landmark) mapLandmarks[j].getLandmark();
455: address = l.getAddressInfo();
456: items[i] = new StringItem("Name:", l.getName());
457: items[i + 1] = new StringItem("Description:", l
458: .getDescription());
459: items[i + 2] = new StringItem("Street:", address
460: .getField(AddressInfo.STREET));
461: items[i + 3] = new StringItem("Postal Code:",
462: address.getField(AddressInfo.POSTAL_CODE));
463: items[i + 4] = new StringItem("City:", address
464: .getField(AddressInfo.CITY));
465: items[i + 5] = new StringItem("Phone No:", address
466: .getField(AddressInfo.PHONE_NUMBER));
467: items[i + 6] = new StringItem(" ", " ");
468: i += NUMBER_OF_ITEMS;
469: }
470: }
471: }
472:
473: detailsScreen = new Form("Details", items);
474: DetailsScreen_backCommand = new Command("Back", Command.SCREEN,
475: 1);
476: detailsScreen.addCommand(DetailsScreen_backCommand);
477: detailsScreen.setCommandListener(this );
478: display.setCurrent(detailsScreen);
479: }
480:
481: /**
482: * Simple welcome screen
483: */
484: public void showWelcomeScreen() {
485: if (welcomeScreen == null) {
486: Item[] items = new Item[] {
487: new StringItem(null, WELCOME_TEXT),
488: new ImageItem(null, imageManager.getImage("logo"),
489: Item.LAYOUT_CENTER, "logo"), };
490: welcomeScreen = new Form("City Guide", items);
491: WelcomeScreen_nextCommand = new Command("Next",
492: Command.SCREEN, 2);
493: welcomeScreen.addCommand(exitCommand);
494: welcomeScreen.addCommand(WelcomeScreen_nextCommand);
495: welcomeScreen.setCommandListener(this );
496: }
497:
498: display.setCurrent(welcomeScreen);
499: }
500:
501: /** is map display on */
502: public boolean isMapDisplayed() {
503: return (display.getCurrent() == mapCanvas);
504: }
505:
506: /** which categories are selected */
507: public Vector getSelectedCategories() {
508: return selectedCategories;
509: }
510:
511: /**
512: * Update map display - categories have changed
513: */
514: public void selectedCategoriesChanged() {
515: selectedCategories = new Vector();
516:
517: ChoiceGroup cg = (ChoiceGroup) settingsScreen.get(1);
518:
519: for (int i = 0; i < cg.size(); i++) {
520: if (cg.isSelected(i)) {
521: selectedCategories.addElement(cg.getString(i));
522: }
523: }
524: }
525:
526: /**
527: * Get landmarks of the categories in given extent
528: */
529: public Vector getLandmarks(String[] categories, double minLat,
530: double minLon, double maxLat, double maxLon) {
531: Vector landmarks = new Vector();
532:
533: for (int i = 0; i < categories.length; i++) {
534: Enumeration l = null;
535:
536: try {
537: l = landmarkStore.getLandmarks(categories[i], minLat,
538: minLon, maxLat, maxLon);
539: } catch (IOException ioe) {
540: ioe.printStackTrace();
541: }
542:
543: for (; l.hasMoreElements();) {
544: landmarks.addElement(l.nextElement());
545: }
546: }
547:
548: return landmarks;
549: }
550:
551: /**
552: * Load all images
553: */
554: private void loadImages() {
555: try {
556: String name = null;
557:
558: while (!landmarksLoaded) {
559: synchronized (this ) {
560: wait();
561: }
562: }
563:
564: imageManager.loadImagesCache(landmarkStore.getCategories());
565: imageManager.loadImagesCache(PRELOAD_IMAGES);
566: commandAction(ProgressScreen_nextCommand, null);
567: } catch (Exception ioe) {
568: ioe.printStackTrace();
569: }
570: }
571:
572: /**
573: * Load landmarks from text resources
574: */
575: private void loadLandmarks() {
576: final InputStream is = getClass().getResourceAsStream(
577: "/waypoints.txt");
578:
579: try {
580: landmarkStore = LandmarkStore
581: .getInstance(LANDMARKSTORE_NAME);
582:
583: if (null == landmarkStore) {
584: LandmarkStore.createLandmarkStore(LANDMARKSTORE_NAME);
585: landmarkStore = LandmarkStore
586: .getInstance(LANDMARKSTORE_NAME);
587: Util.readLandmarksFromStream(landmarkStore, is,
588: progressGauge);
589: }
590:
591: landmarksLoaded = true;
592:
593: synchronized (this ) {
594: notify();
595: }
596: } catch (Exception ioe) {
597: ioe.printStackTrace();
598: lastError = "Cannot read landmarks.\n Landmark store wasn't created."
599: + ioe.getMessage();
600:
601: try {
602: LandmarkStore.deleteLandmarkStore(LANDMARKSTORE_NAME);
603: commandAction(showErrorCommand, null);
604: } catch (Exception e) {
605: //do nothing
606: }
607: }
608: }
609: }
|