package org.commcare.activities;
import android.Manifest;
import android.app.Activity;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.location.LocationProvider;
import android.os.Bundle;
import android.support.v4.content.ContextCompat;
import android.view.View;
import android.view.View.OnClickListener;
import org.commcare.activities.components.FormEntryConstants;
import org.commcare.dalvik.R;
import org.commcare.interfaces.TimerListener;
import org.commcare.preferences.CommCarePreferences;
import org.commcare.utils.GeoUtils;
import org.commcare.utils.StringUtils;
import org.commcare.utils.TimeoutTimer;
import org.commcare.views.dialogs.GeoProgressDialog;
import java.text.DecimalFormat;
import java.util.Set;
/**
* Activity that blocks user until the current GPS location is captured
*/
public class GeoPointActivity extends Activity implements LocationListener, TimerListener {
private GeoProgressDialog locationDialog;
private LocationManager locationManager;
private Location location;
private Set<String> providers;
public final static int DEFAULT_MAX_WAIT_IN_SECS = 60;
private TimeoutTimer mTimer;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setTitle(StringUtils.getStringRobust(this, R.string.application_name) +
" > " + StringUtils.getStringRobust(this, R.string.get_location));
locationManager = (LocationManager)getSystemService(Context.LOCATION_SERVICE);
providers = GeoUtils.evaluateProviders(locationManager);
setupLocationDialog();
long mLong = -1;
if (savedInstanceState != null) {
mLong = savedInstanceState.getLong("millisRemaining", -1);
}
if (mLong > 0) {
mTimer = new TimeoutTimer(mLong, this);
} else {
mTimer = new TimeoutTimer(CommCarePreferences.getGpsWidgetTimeoutInMilliseconds(), this);
}
mTimer.start();
}
@Override
protected void onPause() {
super.onPause();
// stops the GPS. Note that this will turn off the GPS if the screen goes to sleep.
if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED ||
ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
locationManager.removeUpdates(this);
}
// We're not using managed dialogs, so we have to dismiss the dialog to prevent it from
// leaking memory.
if (locationDialog != null && locationDialog.isShowing()) {
locationDialog.dismiss();
}
}
@Override
protected void onResume() {
super.onResume();
providers = GeoUtils.evaluateProviders(locationManager);
if (providers.isEmpty()) {
DialogInterface.OnCancelListener onCancelListener = new DialogInterface.OnCancelListener() {
@Override
public void onCancel(DialogInterface dialog) {
location = null;
GeoPointActivity.this.finish();
}
};
DialogInterface.OnClickListener onChangeListener = new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int i) {
switch (i) {
case DialogInterface.BUTTON_POSITIVE:
Intent intent = new Intent(android.provider.Settings.ACTION_LOCATION_SOURCE_SETTINGS);
startActivity(intent);
break;
case DialogInterface.BUTTON_NEGATIVE:
location = null;
GeoPointActivity.this.finish();
break;
}
dialog.dismiss();
}
};
GeoUtils.showNoGpsDialog(this, onChangeListener, onCancelListener);
} else {
for (String provider : providers) {
if ((provider.equals(LocationManager.GPS_PROVIDER) && ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) ||
(provider.equals(LocationManager.NETWORK_PROVIDER) && ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED)) {
locationManager.requestLocationUpdates(provider, 0, 0, this);
}
}
// TODO PLM: warn user and ask for permissions if the user has disabled them
locationDialog.show();
}
}
/**
* Sets up the look and actions for the progress dialog while the GPS is searching.
*/
private void setupLocationDialog() {
// dialog displayed while fetching gps location
OnClickListener cancelButtonListener = new OnClickListener() {
@Override
public void onClick(View v) {
location = null;
finish();
}
};
OnClickListener okButtonListener = new OnClickListener() {
@Override
public void onClick(View v) {
returnLocation();
}
};
locationDialog = new GeoProgressDialog(this, StringUtils.getStringRobust(this, R.string.found_location),
StringUtils.getStringRobust(this, R.string.finding_location));
locationDialog.setImage(getResources().getDrawable(R.drawable.green_check_mark));
locationDialog.setMessage(StringUtils.getStringRobust(this, R.string.please_wait_long));
locationDialog.setOKButton(StringUtils.getStringRobust(this, R.string.accept_location),
okButtonListener);
locationDialog.setCancelButton(StringUtils.getStringRobust(this, R.string.cancel_location),
cancelButtonListener);
}
private void returnLocation() {
if (location != null) {
Intent i = new Intent();
i.putExtra(FormEntryConstants.LOCATION_RESULT, GeoUtils.locationToString(location));
setResult(RESULT_OK, i);
}
finish();
}
@Override
public void onLocationChanged(Location location) {
this.location = location;
if (this.location != null) {
String accuracy = truncateDouble(this.location.getAccuracy());
locationDialog.setMessage(StringUtils.getStringRobust(this,
R.string.location_provider_accuracy, accuracy));
// If location is accurate, we're done
if (this.location.getAccuracy() <= CommCarePreferences.getGpsWidgetGoodAccuracy()) {
returnLocation();
}
// If location isn't great but might be acceptable, notify
// the user and let them decide whether or not to record it
locationDialog.setLocationFound(
this.location.getAccuracy() < CommCarePreferences.getGpsWidgetAcceptableAccuracy()
|| mTimer.getMillisUntilFinished() == 0
);
}
}
private String truncateDouble(float number) {
DecimalFormat df = new DecimalFormat("#.##");
return df.format(number);
}
@Override
public void onProviderDisabled(String provider) {
}
@Override
public void onProviderEnabled(String provider) {
}
@Override
public void onStatusChanged(String provider, int status, Bundle extras) {
switch (status) {
case LocationProvider.AVAILABLE:
if (location != null) {
locationDialog.setMessage(StringUtils.getStringRobust(this, R.string.location_accuracy,
"" + (int)location.getAccuracy()));
}
break;
case LocationProvider.OUT_OF_SERVICE:
break;
case LocationProvider.TEMPORARILY_UNAVAILABLE:
break;
}
}
@Override
public void notifyTimerFinished() {
onLocationChanged(location);
}
@Override
public void onSaveInstanceState(Bundle savedInstanceState) {
savedInstanceState.putLong("millisRemaining", mTimer.getMillisUntilFinished());
super.onSaveInstanceState(savedInstanceState);
}
}