package com.ushahidi.android.app;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.graphics.drawable.Drawable;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.CountDownTimer;
import android.os.Looper;
import android.util.Log;
import android.view.MotionEvent;
import android.widget.ZoomButtonsController;
import com.google.android.maps.GeoPoint;
import com.google.android.maps.ItemizedOverlay;
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.OverlayItem;
public abstract class MapUserLocation extends MapActivity implements
LocationListener {
public static final String PREFS_NAME = "UshahidiService";
private static final String TAG = "Ushahidi/MapUserLocation";
protected static final int ONE_MINUTE = 60 * 1000;
protected static final int FIVE_MINUTES = 5 * ONE_MINUTE;
protected static final int ACCURACY_THRESHOLD = 30; // in meters
protected int gpsTimeout = 0;
// private CountDownTimer countDownTimer;
private AsyncTimer countDownTimer;
private AsyncTimerTask timerTask;
protected boolean didFindLocation;
protected MapView mapView;
protected ZoomButtonsController mapZoomButtonsController;
protected MapController mapController;
protected LocationManager locationManager;
protected UpdatableMarker updatableMarker;
protected Location currrentLocation;
/*
* Subclasses must implement a method which updates any relevant interface
* elements when the location changes. e.g. TextViews displaying the
* location.
*/
protected abstract void locationChanged(double latitude, double longitude,
boolean doReverseGeocode);
/*
* protected abstract void locationLatLonChanged(double latitude, double
* longitude);
*/
/* Override this to set a custom marker */
protected UpdatableMarker createUpdatableMarker(Drawable marker,
GeoPoint point) {
return new MapMarker(marker, point);
}
protected void setDeviceLocation() {
Log.d(TAG, "setDeviceLocation");
locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
// Location lastNetLocation = null;
// Location lastGpsLocation = null;
/*
* boolean netAvailable =
* locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER);
* boolean gpsAvailable =
* locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER);
*
* if (!netAvailable && !gpsAvailable) { AlertDialog.Builder builder =
* new AlertDialog.Builder(this);
* builder.setTitle(getString(R.string.location_disabled))
* .setMessage(getString(R.string.location_reenable))
* .setPositiveButton(android.R.string.yes, new
* DialogInterface.OnClickListener() { public void
* onClick(DialogInterface dialog, int id) { startActivity(new
* Intent(android.provider.Settings.ACTION_LOCATION_SOURCE_SETTINGS)); }
* }) .setNegativeButton(android.R.string.no, new
* DialogInterface.OnClickListener() { public void
* onClick(DialogInterface dialog, int id) { dialog.cancel(); } })
* .create() .show(); }
*/
/*
* if (netAvailable) { lastNetLocation =
* locationManager.getLastKnownLocation
* (LocationManager.NETWORK_PROVIDER); } if (gpsAvailable) {
* lastGpsLocation =
* locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER); }
* setBestLocation(lastNetLocation, lastGpsLocation); // If chosen
* location is more than a minute old, start querying network/GPS if
* (currrentLocation == null || (new Date()).getTime() -
* currrentLocation.getTime() > ONE_MINUTE) { if (netAvailable) {
* locationManager
* .requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 0, 0,
* this); } if (gpsAvailable) {
* locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER,
* 0, 0, this); } }
*/
useGPSProvider();
}
protected void useGPSProvider() {
Log.d(TAG, "useGPSProvider");
boolean gpsAvailable = locationManager
.isProviderEnabled(LocationManager.GPS_PROVIDER);
Log.d(TAG, "gpsAvailable: " + gpsAvailable);
if (!gpsAvailable) {
useNetworkProvider();
return;
}
locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0,
0, this);
startTimer();
}
protected void useNetworkProvider() {
Log.d(TAG, "useNetworkProvider");
boolean netAvailable = locationManager
.isProviderEnabled(LocationManager.NETWORK_PROVIDER);
if (!netAvailable) {
Log.d(TAG, "!netAvailable");
showLocationDisabledDialog();
return;
}
Log.d(TAG, "requestLocationUpdates(LocationManager.NETWORK_PROVIDER)");
locationManager.requestLocationUpdates(
LocationManager.NETWORK_PROVIDER, 0, 0, this);
}
protected void getLastKnownLocation() {
Log.d(TAG, "getLastKnownLocation");
Location lastNetLocation = locationManager
.getLastKnownLocation(LocationManager.NETWORK_PROVIDER);
Location lastGPSLocation = locationManager
.getLastKnownLocation(LocationManager.GPS_PROVIDER);
setBestLocation(lastNetLocation, lastGPSLocation);
}
/*
* private void startTimer() {
*
* Log.d(TAG, "startTimer");
*
* if(countDownTimer != null) { countDownTimer.cancel(); countDownTimer =
* null; }
*
* Log.d(TAG, "gpsTimeout: "+gpsTimeout);
*
* countDownTimer = new CountDownTimer(gpsTimeout * 1000L, 1000L) {
*
* @Override public void onTick(long millisUntilFinished) {
*
* Log.d(TAG, "millisUntilFinished: "+millisUntilFinished);
*
* if(didFindLocation) { Log.d(TAG, "didFindLocation: "+didFindLocation);
* stopLocating(); cancel(); } }
*
* @Override public void onFinish() {
*
* if(!didFindLocation) { useNetworkProvider(); } } };
* countDownTimer.start(); }
*/
private void startTimer() {
Log.d(TAG, "startTimer");
if (timerTask != null) {
timerTask.cancel(true);
timerTask = null;
}
timerTask = new AsyncTimerTask();
timerTask.execute();
}
private class AsyncTimerTask extends AsyncTask<Void, Void, Void> {
@Override
protected void onPreExecute() {
if (countDownTimer != null) {
countDownTimer.cancel();
countDownTimer = null;
}
super.onPreExecute();
}
@Override
protected Void doInBackground(Void... params) {
Looper.prepare();
countDownTimer = new AsyncTimer(gpsTimeout * 1000L, 1000L);
countDownTimer.start();
return null;
}
@Override
protected void onCancelled() {
if (countDownTimer != null)
countDownTimer.cancel();
super.onCancelled();
}
}
private class AsyncTimer extends CountDownTimer {
public AsyncTimer(long millisInFuture, long countDownInterval) {
super(millisInFuture, countDownInterval);
}
@Override
public void onFinish() {
if (!didFindLocation) {
useNetworkProvider();
}
}
@Override
public void onTick(long millisUntilFinished) {
Log.d(TAG, "millisUntilFinished: " + millisUntilFinished);
if (didFindLocation) {
Log.d(TAG, "didFindLocation: " + didFindLocation);
stopLocating();
timerTask.cancel(true);
}
}
}
protected void showLocationDisabledDialog() {
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(getString(R.string.location_disabled))
.setMessage(getString(R.string.location_reenable))
.setPositiveButton(android.R.string.yes,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
startActivity(new Intent(
android.provider.Settings.ACTION_LOCATION_SOURCE_SETTINGS));
}
})
.setNegativeButton(android.R.string.no,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
dialog.cancel();
}
}).create().show();
}
public void stopLocating() {
if (locationManager != null) {
try {
Log.d(TAG, "locationManager.removeUpdates");
locationManager.removeUpdates(this);
} catch (Exception ex) {
Log.e(TAG, "stopLocating", ex);
}
locationManager = null;
}
}
protected void updateMarker(double latitude, double longitude,
boolean center) {
updateMarker(getPoint(latitude, longitude), center);
}
protected void updateMarker(GeoPoint point, boolean center) {
if (updatableMarker == null) {
Drawable marker = getResources().getDrawable(
R.drawable.map_marker_green);
marker.setBounds(0, 0, marker.getIntrinsicWidth(),
marker.getIntrinsicHeight());
mapController.setZoom(14);
updatableMarker = createUpdatableMarker(marker, point);
mapView.getOverlays().add((Overlay) updatableMarker);
} else {
updatableMarker.update(point);
}
if (center) {
mapController.animateTo(point);
}
}
/**
* Convert latitude and longitude to a GeoPoint
*
* @param latitude
* Latitude
* @param longitude
* Longitude
* @return GeoPoint
*/
protected GeoPoint getPoint(double latitude, double longitude) {
return (new GeoPoint((int) (latitude * 1E6), (int) (longitude * 1E6)));
}
protected void setBestLocation(Location location1, Location location2) {
if (location1 != null && location2 != null) {
boolean location1Newer = location1.getTime() - location2.getTime() > FIVE_MINUTES;
boolean location2Newer = location2.getTime() - location1.getTime() > FIVE_MINUTES;
boolean location1MoreAccurate = location1.getAccuracy() < location2
.getAccuracy();
boolean location2MoreAccurate = location2.getAccuracy() < location1
.getAccuracy();
if (location1Newer || location1MoreAccurate) {
locationChanged(location1.getLatitude(),
location1.getLongitude(), true);
} else if (location2Newer || location2MoreAccurate) {
locationChanged(location2.getLatitude(),
location2.getLongitude(), true);
}
} else if (location1 != null) {
locationChanged(location1.getLatitude(), location1.getLongitude(),
true);
} else if (location2 != null) {
locationChanged(location2.getLatitude(), location2.getLongitude(),
true);
}
}
private class MapMarker extends ItemizedOverlay<OverlayItem> implements
UpdatableMarker {
private OverlayItem myOverlayItem;
//private long lastTouchTime = -1;
public MapMarker(Drawable defaultMarker, GeoPoint point) {
super(boundCenterBottom(defaultMarker));
update(point);
}
public void update(GeoPoint point) {
myOverlayItem = new OverlayItem(point, " ", " ");
populate();
}
@Override
protected OverlayItem createItem(int i) {
return myOverlayItem;
}
@Override
public int size() {
return 1;
}
@Override
public boolean onTouchEvent(MotionEvent event, MapView mapView) {
/*final int action = event.getAction();
final int x = (int) event.getX();
final int y = (int) event.getY();
if (action == MotionEvent.ACTION_DOWN) {
long thisTime = System.currentTimeMillis();
if (thisTime - lastTouchTime < 250) {
lastTouchTime = -1;
GeoPoint geoPoint = mapView.getProjection().fromPixels(
(int) event.getX(), (int) event.getY());
double latitude = geoPoint.getLatitudeE6() / 1E6;
double longitude = geoPoint.getLongitudeE6() / 1E6;
Log.i(getClass().getSimpleName(), String.format(
"%d, %d >> %f, %f", x, y, latitude, longitude));
locationChanged(latitude, longitude, true);
stopLocating();
return true;
} else {
lastTouchTime = thisTime;
}
}*/
return super.onTouchEvent(event, mapView);
}
}
@Override
protected boolean isRouteDisplayed() {
return false;
}
public void onLocationChanged(Location location) {
if (location != null) {
locationChanged(location.getLatitude(), location.getLongitude(),
true);
if (location.hasAccuracy()
&& location.getAccuracy() < ACCURACY_THRESHOLD) {
// accuracy is within ACCURACY_THRESHOLD, de-activate location
// detection
stopLocating();
}
}
}
public void onProviderDisabled(String provider) {
}
public void onProviderEnabled(String provider) {
}
public void onStatusChanged(String provider, int status, Bundle extras) {
}
@Override
protected void onResume() {
super.onResume();
SharedPreferences prefs = getSharedPreferences(PREFS_NAME, 0);
try {
gpsTimeout = Integer.parseInt(prefs.getString(
"gps_timeout_preference", "60"));
} catch (NumberFormatException nfe) {
Log.e(TAG, nfe.getMessage());
nfe.printStackTrace();
gpsTimeout = 60;
}
setDeviceLocation();
}
@Override
protected void onPause() {
super.onPause();
stopLocating();
}
@Override
protected void onDestroy() {
super.onPause();
stopLocating();
}
public abstract interface UpdatableMarker {
public abstract void update(GeoPoint point);
}
protected class MapOverlay extends Overlay {
private boolean isPinch = false;
private long lastTouchTime = -1;
@Override
public boolean onTap(GeoPoint p, MapView map) {
/*if (isPinch) {
return false;
} else {
Log.i(TAG, "TAP: "+p);
if (p != null) {
updateMarker(p, true);
return true; // We handled the tap
} else {
return false; // Null GeoPoint
}
}*/
return false;
}
@Override
public boolean onTouchEvent(MotionEvent event, MapView mapView) {
int fingers = event.getPointerCount();
if (event.getAction() == MotionEvent.ACTION_DOWN) {
isPinch = false; // Touch DOWN, don't know if it's a pinch yet
}
if (event.getAction() == MotionEvent.ACTION_MOVE && fingers == 2) {
isPinch = true; // Two fingers, it's a pinch
}
final int action = event.getAction();
final int x = (int) event.getX();
final int y = (int) event.getY();
if (action == MotionEvent.ACTION_DOWN) {
long thisTime = System.currentTimeMillis();
if (thisTime - lastTouchTime < 250) {
lastTouchTime = -1;
GeoPoint geoPoint = mapView.getProjection().fromPixels(
(int) event.getX(), (int) event.getY());
double latitude = geoPoint.getLatitudeE6() / 1E6;
double longitude = geoPoint.getLongitudeE6() / 1E6;
Log.i(getClass().getSimpleName(), String.format(
"%d, %d >> %f, %f", x, y, latitude, longitude));
locationChanged(latitude, longitude, true);
stopLocating();
return true;
} else {
lastTouchTime = thisTime;
}
}
return super.onTouchEvent(event, mapView);
}
}
}