www.pudn.com > GMapViewer-src.zip > GMapViewer.java



package org.sreid.j2me.gmapviewer;

import java.util.*;
import java.io.*;
import javax.microedition.lcdui.*;
import javax.microedition.io.*;
import javax.microedition.midlet.*;
import org.sreid.j2me.util.*;

public class GMapViewer extends MIDlet {

	final boolean debug = false;

	/** This MIDlet's display. */
	final Display display;

	/** User preferences (persistent). */
	final Preferences prefs;

	/** Shared chunks of memory (byte array(s)), allocated before memory gets fragmented so as to avoid heap compaction later on. */
	final ResourcePool sharedBuffers;

	/** Background worker thread(s) used for: downloading map tiles */
	final AsyncProcessor async1;

	/** Background worker thread(s) used for: decoding map tiles, submitting searches */
	final AsyncProcessor async2;

	final GMapCanvas canvas;
	final MapTileCache cache;
	final MapTileManager mtm;
	final MapPinManager mpm;
	final MainMenu mainMenu;
	final MapPinMenu mapPinMenu;
	final SearchMenu searchMenu;
	final PrefsEditor prefsEditor;

	final Vector log = new Vector();

	private boolean hideErrors = false;

	private boolean clean; // if false, cleanup still needs to be run

	boolean online = true;

	public GMapViewer() {
		display = Display.getDisplay(this);
		prefs = new Preferences(this);
		prefs.loadPreferences();

		boolean success = false;
		try {
			int dt = prefs.getInt("downloadThreads", 1);
			if (dt < 1) dt = 1;
			if (dt > 4) dt = 4;

			byte[][] sb = new byte[dt][];
			for (int i = 0; i < dt; i++) {
				sb[i] = new byte[32000];
			}
			sharedBuffers = new ResourcePool(sb);
			async1 = new AsyncProcessor(dt);
			async2 = new AsyncProcessor(1);

			canvas = new GMapCanvas(this);
			cache = new MapTileCache(this);
			mtm = new MapTileManager(this); // depends: cache
			mpm = new MapPinManager(this);
			mainMenu = new MainMenu(this);
			mapPinMenu = new MapPinMenu(this);
			searchMenu = new SearchMenu(this);
			prefsEditor = new PrefsEditor(this); // depends: prefs
			success = true;
		}
		finally {
			if (!success) {
				try {
					// Some kind of exception/error, probably user set downloadThreads too high.
					prefs.put("downloadThreads", "1");
					prefs.savePreferences();
				}
				catch (Throwable t) { }
			}
		}
	}

	public void startApp() {
		debug("startApp()");
		clean = false;
		canvas.loadPosition();
		cache.openRMS();
		mpm.openRMS();
		mapPinMenu.loadList();
		display.setCurrent(mainMenu);
		canvas.repaint();

		// Clean up obsolete preferences
		String[] unused = new String[] { "canvas.xpos", "canvas.xoff", "canvas.ypos", "canvas.yoff" };
		for (int i = 0; i < unused.length; i++) {
			prefs.remove(unused[i]);
		}
	}

	public void pauseApp() {
		debug("pauseApp()");
		cleanup();
	}
  
	public void destroyApp(boolean unconditional) {
		debug("destroyApp()");
		if (!clean) cleanup();
		notifyDestroyed();
	}

	void quit() {
		if (clean) destroyApp(false);
		Dialog dlg = Dialog.createConfirmationDialog("Closing GMapViewer", "Closing GMapViewer. This may take a while. Please wait...", "", "");
		dlg.showOn(this, dlg);
		async2.enqueueJob(new Runnable() { public void run() {
			destroyApp(false);
		}});
	}

	private void cleanup() {
		if (clean) return;
		cache.compact();
		canvas.savePosition();
		prefs.savePreferences();
		cache.closeRMS();
		mpm.closeRMS();
		clean = true;
		debug("cleanup() done");
	}

	void debug(Object info) {
		if (debug) {
			System.err.println(info);
		}
	}

	void exception(String message, Throwable exception) {
		try {
			if (debug) {
				System.err.println(message);
				exception.printStackTrace();
			}
			log.addElement(message + "\n" + exception + "\n-----\n");
			if (log.size() > 20) log.removeElementAt(0);
			if (!hideErrors && mainMenu != null) {
				final Dialog dlg = Dialog.createConfirmationDialog("Error", message + "\n\nSee the error log (from the main menu) for more details.", "OK", "Hide errors");
				dlg.setCallback(new Runnable() { public void run() {
					if (dlg.getUserResponse() == null) {
						hideErrors = true;
					}
				}});
				dlg.showSerially(this, mainMenu);
			}
		}
		catch (Throwable t) {
			// Exception while handling an exception. Very bad, but can't do much about it.
			t.printStackTrace();
		}
	}

	void alert(final Alert alert) {
		display.setCurrent(alert, display.getCurrent());
	}

	void alert(String title, String message) {
		Alert a = new Alert(title, message, null, null);
		//a.setTimeout(Alert.FOREVER);
		alert(a);
	}


	/**
	 * Loads data from the server, checking our special 5-byte header.
	 * @param action the action specifier
	 * @param params URL parameters, must start with &.
	 * @param buffer the buffer to load data in to. Must be large enough to contain all the data.
	 * @param expectedHeader the expected header word.
	 * @return the number of bytes actually read
	 */
	int getDataFromServer(String action, String params, byte[] buffer, String expectedHeader) throws IOException {
		ContentConnection conn = null;
		InputStream in = null;
		try {
			String url = prefs.getString("gatewayURL", "") + "?p=" + prefs.getString("gatewayAuth", "") + "&a=" + action + params;
			debug("Fetching URL: " + url);
			conn = (ContentConnection)Connector.open(url);
			in = conn.openInputStream();

			// Get special 5-byte header
			StringBuffer sb = new StringBuffer();
			for (;;) {
				int c = in.read();
				if (c == -1 || c == (int)':') break;
				sb.append((char)c);
			}
			String header = sb.toString();

			// Read data
			int count = 0;
			for (;;) {
				int c = in.read(buffer, count, buffer.length - count);
				if (c < 0) break;
				count += c;
				if (count >= buffer.length) break;
			}

			if (header.equals(expectedHeader)) {
				return count;
			}
			else if (header.equals("ERROR")) {
				throw new IOException("Server reported error: " + new String(buffer, 0, count));
			}
			else {
				throw new IOException("Invalid response from server: " + Util.bytesToHex(buffer, 0, count));
			}
		}
		finally {
			try { if (in != null) in.close(); } catch (Exception e) { }
			try { if (conn != null) conn.close(); } catch (Exception e) { }
		}
	}
}