package net.mobilefight.trackbuilder;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import com.google.android.maps.MapActivity;
import com.google.android.maps.MapController;
import com.google.android.maps.MapView;
import com.google.android.maps.Overlay;
import com.google.android.maps.OverlayController;
import com.google.android.maps.Point;
import android.app.NotificationManager;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.IntentReceiver;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.RectF;
import android.graphics.Paint.Style;
import android.location.Location;
import android.location.LocationManager;
import android.location.LocationProvider;
import android.os.Bundle;
import android.os.Looper;
import android.view.KeyEvent;
import android.view.Menu;
import android.view.SubMenu;
public class TrackBuilder extends MapActivity {
protected LocationManager myLocationManager = null;
protected Location myLocation = null;
protected MyIntentReceiver myIntentReceiver = new MyIntentReceiver();
protected final long MINIMUM_DISTANCECHANGE_FOR_UPDATE = 5; // in Meters
protected final long MINIMUM_TIME_BETWEEN_UPDATE = 1000; // in Milliseconds
protected static final String LOCATION_CHANGED_ACTION =
new String("android.intent.action.LOCATION_CHANGED");
protected final IntentFilter myIntentFilter = new IntentFilter(LOCATION_CHANGED_ACTION);
protected final Intent myIntent = new Intent(LOCATION_CHANGED_ACTION);
protected boolean doUpdates = true;
protected ProgressDialog myProgressDialog;
protected MapView myMapView = null;
protected MapController myMapController = null;
protected OverlayController myOverlayController = null;
protected List<Location> path = new ArrayList<Location>();
protected SubMenu providers;
protected LocationProvider provider;
protected static final int SAVE_POINTS_REQUEST_CODE = 666;
protected static final int SAVE_PATH_REQUEST_CODE = 667;
protected static final int LOAD_PATH_REQUEST_CODE = 668;
protected boolean providersLoaded = false;
protected boolean loadingProviders = false;
protected boolean loadedPath = false;
class MyIntentReceiver extends IntentReceiver {
@Override
public void onReceiveIntent(Context context, Intent intent) {
if(TrackBuilder.this.doUpdates)
TrackBuilder.this.updateView();
}
}
protected class MyLocationOverlay extends Overlay {
@Override
public void draw(Canvas canvas, PixelCalculator calculator, boolean shadow) {
super.draw(canvas, calculator, shadow);
Paint paint = new Paint();
paint.setStyle(Style.FILL);
paint.setARGB(255, 105, 105, 105);
Point mapCentre = myMapView.getMapCenter();
// we are always drawing the long/lat of the mapCentre
canvas.drawText("longitude: " + mapCentre.getLongitudeE6() +
", latitude: " + mapCentre.getLatitudeE6(),
5, 15, paint);
// if there is GPS provider available, we draw the violet dot at the current
// position and the long/lat at the top of the screen
if (doUpdates && myLocation != null && provider != null) {
Double lat = TrackBuilder.this.myLocation.getLatitude() * 1E6;
Double lng = TrackBuilder.this.myLocation.getLongitude() * 1E6;
Point point = new Point(lat.intValue(), lng.intValue());
int[] myScreenCoords = new int[2];
calculator.getPointXY(point, myScreenCoords);
paint.setARGB(255, 80, 30, 150);
canvas.drawOval(new RectF(myScreenCoords[0] - 5, myScreenCoords[1] + 5,
myScreenCoords[0] + 5, myScreenCoords[1] - 5), paint);
canvas.drawText("longitude: " + point.getLongitudeE6() +
", latitude: " + point.getLatitudeE6(),
5, 26, paint);
}
int[] prevScreenCoords = new int[2];
boolean first = true;
// we are iterating all the path's points and draw the green dots at the
// points' positions and red lines between them
for (Location loc : path) {
Point point = new Point((int) loc.getLatitude(), (int) loc.getLongitude());
int[] screenCoords = new int[2];
calculator.getPointXY(point, screenCoords);
paint.setARGB(80, 156, 192, 36);
canvas.drawOval(new RectF(screenCoords[0] - 5, screenCoords[1] + 5,
screenCoords[0] + 5, screenCoords[1] - 5), paint);
if (!first) {
paint.setARGB(80, 255, 0, 0);
canvas.drawLine(prevScreenCoords[0], prevScreenCoords[1],
screenCoords[0], screenCoords[1], paint);
}
prevScreenCoords[0] = screenCoords[0];
prevScreenCoords[1] = screenCoords[1];
first = false;
}
}
}
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
myLocationManager = (LocationManager)getSystemService(Context.LOCATION_SERVICE);
myMapView = new MapView(this);
setContentView(myMapView);
myMapController = this.myMapView.getController();
myOverlayController = this.myMapView.createOverlayController();
MyLocationOverlay myLocationOverlay = new MyLocationOverlay();
myOverlayController.add(myLocationOverlay, true);
myMapController.zoomTo(2);
}
@Override
public void onResume() {
super.onResume();
doUpdates = true;
registerReceiver(myIntentReceiver, myIntentFilter);
}
@Override
public void onFreeze(Bundle icicle) {
doUpdates = false;
unregisterReceiver(myIntentReceiver);
super.onFreeze(icicle);
}
private void updateView() {
if (provider != null) {
myLocation = myLocationManager.getCurrentLocation(provider.getName());
myMapView.invalidate();
}
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
boolean supRetVal = super.onCreateOptionsMenu(menu);
menu.add(0, 0, getString(R.string.map_menu_zoom_in));
menu.add(0, 1, getString(R.string.map_menu_zoom_out));
menu.add(0, 2, getString(R.string.map_menu_satellite));
menu.add(0, 3, getString(R.string.map_menu_traffic));
menu.add(0, 4, getString(R.string.map_menu_set_point));
menu.add(0, 5, getString(R.string.map_menu_remove_point));
menu.add(0, 6, getString(R.string.map_menu_clear_points));
// we remember the reference to submenu in order to modify it in the future
providers = menu.addSubMenu(0, 7, getString(R.string.map_menu_providers));
menu.add(0, 8, getString(R.string.map_menu_save_points));
menu.addSeparator(0, 9);
menu.add(0, 10, getString(R.string.map_menu_save_path));
menu.add(0, 11, getString(R.string.map_menu_load_path));
menu.addSeparator(0, 12);
menu.add(0, 13, getString(R.string.map_menu_exit));
return supRetVal;
}
@Override
protected void onActivityResult(int requestCode, int resultCode,
final String data, final Bundle extras) {
super.onActivityResult(requestCode, resultCode, data, extras);
switch (requestCode) {
case SAVE_POINTS_REQUEST_CODE: // this is the result from save points subactivity
if (resultCode == RESULT_OK) {
// display the progress dialog
myProgressDialog = ProgressDialog.show(TrackBuilder.this,
"Please wait...", "Saving track...", true);
// start the thread, which will do the hard work
new Thread() {
public void run() {
boolean resultOK = TrackFactory.saveFile(path, TrackBuilder.this, data, extras.getInteger("step"));
myProgressDialog.dismiss();
// we have to call Looper.prepare() before calling showAlert
Looper.prepare();
if (resultOK) {
showAlert("File saved", "Track successfully saved as " + data, "OK", false);
}
else {
showAlert("Error", "Track cannot be saved as " + data, "OK", false);
}
// in order to display alert dialog we have to call Looper.loop()
Looper.loop();
// after Looper.loop() we have to call quit() on our looper
Looper.myLooper().quit();
}
}.start();
}
break;
case SAVE_PATH_REQUEST_CODE:
if (resultCode == RESULT_OK) {
myProgressDialog = ProgressDialog.show(TrackBuilder.this,
"Please wait...", "Saving path...", true);
new Thread() {
public void run() {
boolean resultOK = TrackFactory.savePath(path, TrackBuilder.this, data);
myProgressDialog.dismiss();
Looper.prepare();
if (resultOK) {
showAlert("File saved", "Path successfully saved as " + data, "OK", false);
}
else {
showAlert("Error", "Path cannot be saved as " + data, "OK", false);
}
Looper.loop();
Looper.myLooper().quit();
}
}.start();
}
break;
case LOAD_PATH_REQUEST_CODE:
if (resultCode == RESULT_OK) {
myProgressDialog = ProgressDialog.show(TrackBuilder.this,
"Please wait...", "Loading path...", true);
loadedPath = false;
new Thread() {
public void run() {
List<Location> newPath = new ArrayList<Location>();
boolean resultOK = TrackFactory.loadPath(newPath, TrackBuilder.this, data);
myProgressDialog.dismiss();
Looper.prepare();
if (resultOK) {
path.clear();
path.addAll(newPath);
showAlert("File loaded", "Path file " + data + " successfully loaded", "OK", false);
}
else {
showAlert("Error", "Path file " + data + " cannot be loaded", "OK", false);
}
// we have to release the main thread before calling Looper.lopp()!
loadedPath = true;
Looper.loop();
Looper.myLooper().quit();
}
}.start();
// we have to wait on the worker thread to invalidate the map view at the
// proper moment
while (true) {
if (loadedPath) {
break;
}
try {
Thread.sleep(500);
}
catch (InterruptedException e) {
// do nothing
}
}
// invalidate the map view in order to redraw the screen
myMapView.invalidate();
}
else if (resultCode == LoadPath.RESULT_EMPTY) {
showAlert("Error", "There are no saved path files!", "OK", false);
}
break;
}
}
@Override
public boolean onOptionsItemSelected(Menu.Item item){
if (item.getGroup() == 0) { // main menu
switch (item.getId()) {
case 0: return zoomIn();
case 1: return zoomOut();
case 2: return toggleSatellite();
case 3: return toggleTraffic();
case 4: return setPoint();
case 5: return removePoint();
case 6: return clearAllPoints();
case 7: return updateProviders();
case 8: return savePoints();
case 10: return savePath();
case 11: return loadPath();
case 13: return exit();
}
}
else { // submenu
if (item.getId() == 0) { // none
myLocationManager.removeUpdates(myIntent);
doUpdates = false;
myLocation = null;
provider = null;
myMapView.invalidate();
}
else {
List<LocationProvider> locProviders = myLocationManager.getProviders();
provider = locProviders.get(item.getId()-1);
myLocationManager.requestUpdates(provider, MINIMUM_TIME_BETWEEN_UPDATE,
MINIMUM_DISTANCECHANGE_FOR_UPDATE, myIntent);
onResume();
}
}
return false;
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
switch (keyCode) {
case KeyEvent.KEYCODE_I: return zoomIn();
case KeyEvent.KEYCODE_O: return zoomOut();
case KeyEvent.KEYCODE_S: return toggleSatellite();
case KeyEvent.KEYCODE_T: return toggleTraffic();
case KeyEvent.KEYCODE_P: return setPoint();
case KeyEvent.KEYCODE_R: return removePoint();
case KeyEvent.KEYCODE_C: return clearAllPoints();
case KeyEvent.KEYCODE_X: return savePoints();
}
return false;
}
// menu methods
private boolean zoomIn() {
this.myMapController.zoomTo(Math.min(21, this.myMapView.getZoomLevel() + 1));
return true;
}
private boolean zoomOut() {
this.myMapController.zoomTo(Math.max(1, this.myMapView.getZoomLevel() - 1));
return true;
}
private boolean toggleSatellite() {
myMapView.toggleSatellite();
return true;
}
private boolean toggleTraffic() {
myMapView.toggleTraffic();
return true;
}
private boolean setPoint() {
Point mapCentre = myMapView.getMapCenter();
Location centreLocation = new Location();
centreLocation.setLatitude(mapCentre.getLatitudeE6());
centreLocation.setLongitude(mapCentre.getLongitudeE6());
path.add(centreLocation);
myMapView.invalidate();
return true;
}
private boolean removePoint() {
if (!path.isEmpty()) {
path.remove(path.size()-1);
myMapView.invalidate();
}
return true;
}
private boolean exit() {
this.finish();
return true;
}
private boolean updateProviders() {
synchronized (this) {
if (providersLoaded) {
// the worker thread has finished and we simply update the
// providers sumbenu with retrieved providers
providers.removeGroup(1);
providers.add(1, 0, getString(R.string.map_submenu_none));
Collection<LocationProvider> locProviders = myLocationManager.getProviders();
int counter = 1;
for (LocationProvider provider : locProviders) {
providers.add(1, counter, provider.getName());
counter++;
}
return true;
}
else if (loadingProviders) { // the working thread hasn't finished yet
return true;
}
// we have to set this flag at this moment to avoid creation of multiple threads
loadingProviders = true;
}
myProgressDialog = ProgressDialog.show(TrackBuilder.this,
"Please wait...", "Retrieving available providers...", true);
new Thread() {
// this working thread simply calls getProviders() method in order
// to initialize the providers
public void run() {
myLocationManager.getProviders();
myProgressDialog.dismiss();
synchronized (TrackBuilder.this) {
providersLoaded = true;
}
}
}.start();
return true;
}
private boolean clearAllPoints() {
path.clear();
this.myMapView.invalidate();
return true;
}
private boolean savePoints() {
if (path.size() < 2) {
NotificationManager nm = (NotificationManager)
getSystemService(NOTIFICATION_SERVICE);
nm.notifyWithText(100, "You have to put at least two points on the map!",
NotificationManager.LENGTH_SHORT, null);
}
else {
// start save points subactivity
Intent i = new Intent(TrackBuilder.this, SavePoints.class);
// we are adding the bundle to the intent with 'path' set to false
Bundle extras = new Bundle();
extras.putBoolean("path", false);
i.putExtras(extras);
startSubActivity(i, SAVE_POINTS_REQUEST_CODE);
}
return true;
}
private boolean savePath() {
if (path.size() < 2) {
NotificationManager nm = (NotificationManager)
getSystemService(NOTIFICATION_SERVICE);
nm.notifyWithText(100, "You have to put at least two points on the map!",
NotificationManager.LENGTH_SHORT, null);
}
else {
Intent i = new Intent(TrackBuilder.this, SavePoints.class);
// we are adding the bundle to the intent with 'path' set to true
Bundle extras = new Bundle();
extras.putBoolean("path", true);
i.putExtras(extras);
startSubActivity(i, SAVE_PATH_REQUEST_CODE);
}
return true;
}
private boolean loadPath() {
Intent i = new Intent(TrackBuilder.this, LoadPath.class);
startSubActivity(i, LOAD_PATH_REQUEST_CODE);
return true;
}
}
|