/*
* Copyright 2011 mapsforge.org
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.muxe.advancedtouristmap.routing;
import java.io.File;
import org.mapsforge.android.maps.GeoPoint;
import org.mapsforge.core.Edge;
import org.mapsforge.core.GeoCoordinate;
import org.mapsforge.core.Router;
import org.mapsforge.core.Vertex;
import org.muxe.advancedtouristmap.BaseActivity;
import org.muxe.advancedtouristmap.LocationPicker;
import org.muxe.advancedtouristmap.PositionInfo;
import org.muxe.advancedtouristmap.R;
import org.muxe.advancedtouristmap.Search;
import org.muxe.advancedtouristmap.Utility;
import org.muxe.advancedtouristmap.sourcefiles.RoutingFile;
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.location.LocationProvider;
import android.os.AsyncTask;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ImageButton;
import android.widget.RelativeLayout;
import android.widget.Spinner;
import android.widget.TextView;
import android.widget.Toast;
public class RouteCalculator extends BaseActivity {
static final String TAG = RouteCalculator.class.getSimpleName();
protected static final int INTENT_SEARCH = 0;
protected static final int INTENT_MAP = 1;
protected static final int START_FIELD = 0;
protected static final int DEST_FIELD = 1;
protected static final int DIALOG_CHOOSE_INPUT = 0;
private static final String SAVE_START_LAT = "saved_start_lat";
private static final String SAVE_START_LON = "saved_start_lon";
private static final String SAVE_DEST_LAT = "saved_dest_lat";
private static final String SAVE_DEST_LON = "saved_dest_lon";
private static final String SAVE_RF_POSITION = "saved_dest_lon";
GeoPoint startPoint;
GeoPoint destPoint;
ImageButton chooseStartButton;
ImageButton chooseDestButton;
private Button calcRouteButton;
private EditText startEditText;
private EditText destEditText;
private RelativeLayout refiningPositionRow;
private TextView refiningPositionText;
int viewToSet;
ProgressDialog progressDialog;
private LocationManager locationManager;
private LocationListener locationListener;
Location currentBestLocation;
Spinner routingFileSpinner;
private int spinnerSelection;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d("lifecycle", "routeCalculator onCreate");
this.advancedMapViewer.setViewWithHelp(this, R.layout.activity_calculate_route);
Intent startingIntent = getIntent();
if (startingIntent.hasExtra("lat") && startingIntent.hasExtra("lon")) {
this.destPoint = new GeoPoint(startingIntent.getDoubleExtra("lat", 0.0),
startingIntent.getDoubleExtra("lon", 0.0));
}
this.startEditText = (EditText) findViewById(R.id.calculate_route_edittext_start);
this.destEditText = (EditText) findViewById(R.id.calculate_route_edittext_dest);
this.chooseStartButton = (ImageButton) findViewById(R.id.calculate_route_button_choose_start);
this.chooseDestButton = (ImageButton) findViewById(R.id.calculate_route_button_choose_dest);
this.calcRouteButton = (Button) findViewById(R.id.calculate_route_button_calculate);
this.routingFileSpinner = (Spinner) findViewById(R.id.calculate_route_spinner_routing_file);
this.refiningPositionRow = (RelativeLayout) findViewById(R.id.routing_loading_position);
this.refiningPositionText = (TextView) findViewById(R.id.routing_refining_position_text);
OnClickListener startDestChooserListener = new OnClickListener() {
@Override
public void onClick(View v) {
if (v.getId() == RouteCalculator.this.chooseStartButton.getId()) {
RouteCalculator.this.viewToSet = RouteCalculator.START_FIELD;
} else if (v.getId() == RouteCalculator.this.chooseDestButton.getId()) {
RouteCalculator.this.viewToSet = RouteCalculator.DEST_FIELD;
}
// open a dialog to select method to chose start/dest
showDialog(DIALOG_CHOOSE_INPUT);
}
};
this.chooseStartButton.setOnClickListener(startDestChooserListener);
this.chooseDestButton.setOnClickListener(startDestChooserListener);
this.calcRouteButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
// TODO: if no routing file selected?
// still nullpointer exception on no routing files
RoutingFile rf = (RoutingFile) RouteCalculator.this.routingFileSpinner
.getSelectedItem();
if (RouteCalculator.this.startPoint == null) {
Toast.makeText(RouteCalculator.this,
getString(R.string.routing_no_start_selected), Toast.LENGTH_LONG)
.show();
return;
}
if (RouteCalculator.this.destPoint == null) {
Toast.makeText(RouteCalculator.this,
getString(R.string.routing_no_destination_selected),
Toast.LENGTH_LONG).show();
return;
}
new CalculateRouteAsync().execute(rf);
}
});
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == INTENT_SEARCH) {
if (resultCode == RESULT_OK) {
if (data != null && data.hasExtra("lon") && data.hasExtra("lat")) {
double lon = data.getDoubleExtra("lon", 0.0);
double lat = data.getDoubleExtra("lat", 0.0);
GeoPoint point = new GeoPoint(lat, lon);
if (this.viewToSet == RouteCalculator.START_FIELD) {
this.startPoint = point;
} else {
this.destPoint = point;
}
}
}
} else if (requestCode == INTENT_MAP) {
// TODO: not DRY yet
// TODO: find nearest vertex first?
if (resultCode == RESULT_OK) {
if (data != null && data.hasExtra("LONGITUDE") && data.hasExtra("LATITUDE")) {
double lon = data.getDoubleExtra("LONGITUDE", 0.0);
double lat = data.getDoubleExtra("LATITUDE", 0.0);
GeoPoint point = new GeoPoint(lat, lon);
if (this.viewToSet == RouteCalculator.START_FIELD) {
this.startPoint = point;
} else {
this.destPoint = point;
}
}
}
}
}
@Override
protected Dialog onCreateDialog(int dialogId) {
AlertDialog.Builder builder = new AlertDialog.Builder(this);
if (dialogId == DIALOG_CHOOSE_INPUT) {
final String[] items = getResources().getStringArray(
R.array.routing_point_picker_values);
final String[] items_keys = getResources().getStringArray(
R.array.routing_point_picker_keys);
builder.setTitle(R.string.dialog_title_find_location);
builder.setItems(items, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int item) {
if (items_keys[item].equals("ADDRESS")) {
if (RouteCalculator.this.advancedMapViewer.getCurrentMapBundle()
.isSearchable()) {
startActivityForResult(new Intent(RouteCalculator.this,
Search.class), INTENT_SEARCH);
} else {
// TODO:
Toast.makeText(RouteCalculator.this,
getString(R.string.addressfile_not_avaiable),
Toast.LENGTH_LONG).show();
}
} else if (items_keys[item].equals("POSITION")) {
startPositionSearch();
} else if (items_keys[item].equals("MAP")) {
startActivityForResult(new Intent(RouteCalculator.this,
LocationPicker.class), INTENT_MAP);
// new Intent(RouteCalculator.this, AdvancedMapViewer.class)
// .putExtra("mode", "LOCATION_PICKER"), INTENT_MAP);
}
}
});
return builder.create();
}
return null;
}
@Override
protected void onResume() {
super.onResume();
Log.d("lifecycle", "routeCalculator onResume");
if (this.locationManager == null) {
this.locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
}
if (this.startPoint != null) {
changeStartStop(START_FIELD, this.startPoint);
// this.startEditText.setText(this.startPoint.getLatitude() + " "
// + this.startPoint.getLongitude());
}
if (this.destPoint != null) {
changeStartStop(DEST_FIELD, this.destPoint);
// this.destEditText.setText(this.destPoint.getLatitude() + " "
// + this.destPoint.getLongitude());
}
RoutingFile[] routingFiles = this.advancedMapViewer.getCurrentMapBundle()
.getRoutingFilesArray();
ArrayAdapter<RoutingFile> adapter = new ArrayAdapter<RoutingFile>(this,
android.R.layout.simple_spinner_item, routingFiles);
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
this.routingFileSpinner.setAdapter(adapter);
Log.d(TAG, "this.spinnerSelection: " + this.spinnerSelection);
this.routingFileSpinner.setSelection(this.spinnerSelection);
}
void startPositionSearch() {
// TODO: exit strategy (timer and/or when signal stabilized)
// check if already running, if so, stop first
if (this.locationListener != null) {
stopPositionSearch();
}
//display animation
this.refiningPositionRow.setVisibility(View.VISIBLE);
// get cached locations first
Location currentLocation;
for (String provider : this.locationManager.getProviders(true)) {
currentLocation = this.locationManager.getLastKnownLocation(provider);
if (currentLocation != null) {
if (Utility.isBetterLocation(currentLocation, this.currentBestLocation)) {
this.currentBestLocation = currentLocation;
changeStartStop(RouteCalculator.this.viewToSet, new GeoPoint(
currentLocation.getLatitude(), currentLocation.getLongitude()));
Log.d(TAG, "got better cached location from: " + provider + " ("
+ currentLocation.getAccuracy() + ")");
RouteCalculator.this.refiningPositionText.setText(getString(R.string.refining_position) + " (" + currentLocation.getAccuracy() + " m)");
} else {
Log.d(TAG,
"dismissed location from: " + provider + " ("
+ currentLocation.getAccuracy() + ")");
}
}
}
this.locationListener = new LocationListener() {
@Override
public void onLocationChanged(Location location) {
// GeoPoint point = new GeoPoint(location.getLatitude(),
// location.getLongitude());
if (Utility.isBetterLocation(location, RouteCalculator.this.currentBestLocation)) {
RouteCalculator.this.currentBestLocation = location;
Log.d(TAG, "better location from: " + location.getProvider() + " ("
+ location.getAccuracy() + ")");
changeStartStop(RouteCalculator.this.viewToSet,
new GeoPoint(location.getLatitude(), location.getLongitude()));
//show accuracy in loading message
RouteCalculator.this.refiningPositionText.setText(getString(R.string.refining_position) + " (" + location.getAccuracy() + " m)");
} else {
Log.d(TAG, "dismissed location from: " + location.getProvider() + " ("
+ location.getAccuracy() + ")");
}
}
@Override
public void onProviderDisabled(String provider) {
}
@Override
public void onProviderEnabled(String provider) {
// do nothing
}
@Override
public void onStatusChanged(String provider, int status, Bundle extras) {
if (status == LocationProvider.AVAILABLE) {
} else if (status == LocationProvider.OUT_OF_SERVICE) {
} else {
// must be TEMPORARILY_UNAVAILABLE
}
}
};
this.locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 1000, 0,
this.locationListener);
this.locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 1000, 0,
this.locationListener);
}
@Override
protected void onPause() {
super.onPause();
Log.d("lifecycle", "routeCalculator onPause");
stopPositionSearch();
}
private void stopPositionSearch() {
if (this.locationListener != null) {
this.locationManager.removeUpdates(this.locationListener);
this.locationListener = null;
this.refiningPositionRow.setVisibility(View.GONE);
}
}
void changeStartStop(int field, GeoPoint point) {
String infoText = point.getLatitude() + " " + point.getLongitude();
if (this.advancedMapViewer.getCurrentMapBundle().isRoutable()) {
//TODO: may halt on slow phones
Router router = this.advancedMapViewer.getRouter();
if (router != null) {
Vertex nearestVertex = router.getNearestVertex(new GeoCoordinate(point
.getLatitude(), point.getLongitude()));
if (nearestVertex != null) {
infoText = PositionInfo.edgesToStringInfo(nearestVertex.getOutboundEdges());
if (infoText.equals("")) {
infoText = getString(R.string.positioninfo_unknown_road);
}
}
}
}
if (field == RouteCalculator.START_FIELD) {
this.startPoint = point;
this.startEditText.setText(infoText);
} else if (field == RouteCalculator.DEST_FIELD) {
this.destPoint = point;
this.destEditText.setText(infoText);
}
}
private class CalculateRouteAsync extends AsyncTask<RoutingFile, Void, Route> {
public CalculateRouteAsync() {
super();
}
@Override
protected void onPreExecute() {
RouteCalculator.this.progressDialog = ProgressDialog.show(RouteCalculator.this, "",
getString(R.string.loading_message), true);
}
@Override
protected Route doInBackground(RoutingFile... routingFiles) {
// calculate
RoutingFile rf = null;
try {
rf = routingFiles[0];
} catch (ArrayIndexOutOfBoundsException e) {
return null;
}
String path = RouteCalculator.this.advancedMapViewer.getBaseBundlePath()
+ File.separator + rf.getRelativePath();
Router router = RouteCalculator.this.advancedMapViewer.getRouter(path);
if (router == null) {
return null;
}
Vertex start = router.getNearestVertex(new GeoCoordinate(
RouteCalculator.this.startPoint.getLatitude(),
RouteCalculator.this.startPoint.getLongitude()));
Vertex dest = router.getNearestVertex(new GeoCoordinate(
RouteCalculator.this.destPoint.getLatitude(),
RouteCalculator.this.destPoint.getLongitude()));
if (start == null || dest == null) {
return null;
}
Edge[] edges = router.getShortestPath(start.getId(), dest.getId());
// try {
// Thread.sleep(10000);
// } catch (InterruptedException e) {
//
// }
if (edges.length > 0) {
Route route = new Route(edges);
Log.d(TAG, "done");
Log.d(TAG, "length: " + edges.length);
return route;
}
return null;
}
@Override
protected void onPostExecute(Route route) {
// remove progress bar
RouteCalculator.this.progressDialog.dismiss();
// show route
if (route != null) {
RouteCalculator.this.advancedMapViewer.currentRoute = route;
startActivity(new Intent(RouteCalculator.this, RouteList.class));
} else {
Log.d(TAG, "No Route Found");
Toast.makeText(RouteCalculator.this,
getString(R.string.routing_no_route_found), Toast.LENGTH_LONG).show();
}
}
}
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
double startLat = savedInstanceState.getDouble(SAVE_START_LAT);
double startLon = savedInstanceState.getDouble(SAVE_START_LON);
double destLat = savedInstanceState.getDouble(SAVE_DEST_LAT);
double destLon = savedInstanceState.getDouble(SAVE_DEST_LON);
int rfPosition = savedInstanceState.getInt(SAVE_RF_POSITION, -1);
Log.d(TAG, "rfPosition: " + rfPosition);
if (startLat != 0.0 && startLon != 0.0) {
this.startPoint = new GeoPoint(startLat, startLon);
}
if (destLat != 0.0 && destLon != 0.0) {
this.destPoint = new GeoPoint(destLat, destLon);
}
if (rfPosition >= 0) {
this.spinnerSelection = rfPosition;
}
}
@Override
protected void onSaveInstanceState(Bundle outState) {
if (this.startPoint != null) {
outState.putDouble(SAVE_START_LAT, this.startPoint.getLatitude());
outState.putDouble(SAVE_START_LON, this.startPoint.getLongitude());
}
if (this.destPoint != null) {
outState.putDouble(SAVE_DEST_LAT, this.destPoint.getLatitude());
outState.putDouble(SAVE_DEST_LON, this.destPoint.getLongitude());
}
if (this.routingFileSpinner.getSelectedItemPosition() != Spinner.INVALID_POSITION) {
Log.d(TAG, "saved Position: " + this.routingFileSpinner.getSelectedItemPosition());
outState.putInt(SAVE_RF_POSITION, this.routingFileSpinner.getSelectedItemPosition());
}
super.onSaveInstanceState(outState);
}
}