package com.ushahidi.android.app;
import java.util.Date;
import android.app.AlertDialog;
import android.app.Dialog;
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.os.Bundle;
import android.support.v4.app.DialogFragment;
import android.util.Log;
import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.GooglePlayServicesUtil;
import com.google.android.gms.maps.CameraUpdate;
import com.google.android.gms.maps.CameraUpdateFactory;
import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.MarkerOptions;
import com.ushahidi.android.app.activities.BaseActivity;
import com.ushahidi.android.app.util.Util;
import com.ushahidi.android.app.views.View;
public abstract class MapUserLocation<V extends View> extends BaseActivity<V>
implements LocationListener {
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 static final String TAG_ERROR_DIALOG_FRAGMENT = "errorDialog";
protected static final int ZOOM = 15;
protected GoogleMap map;
protected LocationManager locationManager;
protected UpdatableMarker updatableMarker;
protected Location currrentLocation;
protected MapUserLocation(Class<V> view, int layout, int menu) {
super(view, layout, menu);
}
/*
* 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);
/* Override this to set a custom marker */
protected UpdatableMarker createUpdatableMarker(LatLng point) {
return new MapMarker(point);
}
protected void 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);
}
}
}
public void stopLocating() {
if (locationManager != null) {
try {
locationManager.removeUpdates(this);
} catch (Exception ex) {
Log.e(getClass().getSimpleName(), "stopLocating", ex);
}
locationManager = null;
}
}
protected void updateMarker(double latitude, double longitude,
boolean center) {
updateMarker(getPoint(latitude, longitude), center);
}
protected void updateMarker(LatLng point, boolean center) {
if (map != null) {
if (updatableMarker == null) {
CameraUpdate p = CameraUpdateFactory.newLatLng(point);
map.moveCamera(p);
updatableMarker = createUpdatableMarker(point);
} else {
updatableMarker.update(point);
}
if (center) {
CameraUpdate c = CameraUpdateFactory.newLatLng(point);
map.moveCamera(c);
}
CameraUpdate zoom = CameraUpdateFactory.zoomTo(ZOOM);
map.animateCamera(zoom);
map.getUiSettings().setZoomControlsEnabled(false);
}
}
/**
* Convert latitude and longitude to a GeoPoint
*
* @param latitude
* Latitude
* @param longitude
* Lingitude
* @return GeoPoint
*/
protected LatLng getPoint(double latitude, double longitude) {
return (new LatLng(latitude, longitude));
}
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());
} else if (location2Newer || location2MoreAccurate) {
locationChanged(location2.getLatitude(),
location2.getLongitude());
}
} else if (location1 != null) {
locationChanged(location1.getLatitude(), location1.getLongitude());
} else if (location2 != null) {
locationChanged(location2.getLatitude(), location2.getLongitude());
}
}
/**
* Check if Google Maps exist on the device
*
* @return
*/
protected boolean checkForGMap() {
int status = GooglePlayServicesUtil.isGooglePlayServicesAvailable(this);
if (status == ConnectionResult.SUCCESS) {
return (true);
} else if (GooglePlayServicesUtil.isUserRecoverableError(status)) {
ErrorDialogFragment.newInstance(status).show(
getSupportFragmentManager(), TAG_ERROR_DIALOG_FRAGMENT);
} else {
Util.showToast(this, R.string.no_maps);
finish();
}
return false;
}
public static class ErrorDialogFragment extends DialogFragment {
static final String ARG_STATUS = "status";
static ErrorDialogFragment newInstance(int status) {
Bundle args = new Bundle();
args.putInt(ARG_STATUS, status);
ErrorDialogFragment result = new ErrorDialogFragment();
result.setArguments(args);
return (result);
}
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
Bundle args = getArguments();
return GooglePlayServicesUtil.getErrorDialog(
args.getInt(ARG_STATUS), getActivity(), 0);
}
@Override
public void onDismiss(DialogInterface dlg) {
if (getActivity() != null) {
getActivity().finish();
}
}
}
private class MapMarker implements UpdatableMarker {
public MapMarker(LatLng point) {
update(point);
}
public void update(LatLng point) {
if (point != null)
map.addMarker(new MarkerOptions().position(point));
}
}
public void onLocationChanged(Location location) {
if (location != null) {
locationChanged(location.getLatitude(), location.getLongitude());
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();
setDeviceLocation();
}
@Override
protected void onPause() {
super.onPause();
stopLocating();
}
@Override
protected void onDestroy() {
super.onPause();
stopLocating();
}
public abstract interface UpdatableMarker {
public abstract void update(LatLng point);
}
}