/*
* Copyright (C) 2009 University of Washington
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
package org.odk.collect.android.activities;
import android.app.Activity;
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.Bundle;
import android.widget.Toast;
import org.odk.collect.android.R;
import org.odk.collect.android.application.Collect;
import org.odk.collect.android.utilities.InfoLogger;
import org.odk.collect.android.widgets.GeoPointWidget;
import java.text.DecimalFormat;
import java.util.List;
public class GeoPointActivity extends Activity implements LocationListener {
private static final String LOCATION_COUNT = "locationCount";
private ProgressDialog mLocationDialog;
private LocationManager mLocationManager;
private Location mLocation;
private boolean mGPSOn = false;
private boolean mNetworkOn = false;
private double mLocationAccuracy;
private int mLocationCount = 0;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if ( savedInstanceState != null ) {
mLocationCount = savedInstanceState.getInt(LOCATION_COUNT);
}
Intent intent = getIntent();
mLocationAccuracy = GeoPointWidget.DEFAULT_LOCATION_ACCURACY;
if (intent != null && intent.getExtras() != null) {
if ( intent.hasExtra(GeoPointWidget.ACCURACY_THRESHOLD) ) {
mLocationAccuracy = intent.getDoubleExtra(GeoPointWidget.ACCURACY_THRESHOLD, GeoPointWidget.DEFAULT_LOCATION_ACCURACY);
}
}
setTitle(getString(R.string.app_name) + " > " + getString(R.string.get_location));
mLocationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
// make sure we have a good location provider before continuing
List<String> providers = mLocationManager.getProviders(true);
for (String provider : providers) {
if (provider.equalsIgnoreCase(LocationManager.GPS_PROVIDER)) {
mGPSOn = true;
}
if (provider.equalsIgnoreCase(LocationManager.NETWORK_PROVIDER)) {
mNetworkOn = true;
}
}
if (!mGPSOn && !mNetworkOn) {
Toast.makeText(getBaseContext(), getString(R.string.provider_disabled_error),
Toast.LENGTH_SHORT).show();
finish();
}
if ( mGPSOn ) {
Location loc = mLocationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER);
if ( loc != null ) {
InfoLogger.geolog("GeoPointActivity: " + System.currentTimeMillis() +
" lastKnownLocation(GPS) lat: " +
loc.getLatitude() + " long: " +
loc.getLongitude() + " acc: " +
loc.getAccuracy() );
} else {
InfoLogger.geolog("GeoPointActivity: " + System.currentTimeMillis() +
" lastKnownLocation(GPS) null location");
}
}
if ( mNetworkOn ) {
Location loc = mLocationManager.getLastKnownLocation(LocationManager.NETWORK_PROVIDER);
if ( loc != null ) {
InfoLogger.geolog("GeoPointActivity: " + System.currentTimeMillis() +
" lastKnownLocation(Network) lat: " +
loc.getLatitude() + " long: " +
loc.getLongitude() + " acc: " +
loc.getAccuracy() );
} else {
InfoLogger.geolog("GeoPointActivity: " + System.currentTimeMillis() +
" lastKnownLocation(Network) null location");
}
}
setupLocationDialog();
}
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putInt(LOCATION_COUNT, mLocationCount);
}
@Override
protected void onPause() {
super.onPause();
// stops the GPS. Note that this will turn off the GPS if the screen goes to sleep.
mLocationManager.removeUpdates(this);
// We're not using managed dialogs, so we have to dismiss the dialog to prevent it from
// leaking memory.
if (mLocationDialog != null && mLocationDialog.isShowing())
mLocationDialog.dismiss();
}
@Override
protected void onResume() {
super.onResume();
if (mGPSOn) {
mLocationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, this);
}
if (mNetworkOn) {
mLocationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 0, 0, this);
}
mLocationDialog.show();
}
@Override
protected void onStart() {
super.onStart();
Collect.getInstance().getActivityLogger().logOnStart(this);
}
@Override
protected void onStop() {
Collect.getInstance().getActivityLogger().logOnStop(this);
super.onStop();
}
/**
* Sets up the look and actions for the progress dialog while the GPS is searching.
*/
private void setupLocationDialog() {
Collect.getInstance().getActivityLogger().logInstanceAction(this, "setupLocationDialog", "show");
// dialog displayed while fetching gps location
mLocationDialog = new ProgressDialog(this);
DialogInterface.OnClickListener geopointButtonListener =
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
switch (which) {
case DialogInterface.BUTTON_POSITIVE:
Collect.getInstance().getActivityLogger().logInstanceAction(this, "acceptLocation", "OK");
returnLocation();
break;
case DialogInterface. BUTTON_NEGATIVE:
Collect.getInstance().getActivityLogger().logInstanceAction(this, "cancelLocation", "cancel");
mLocation = null;
finish();
break;
}
}
};
// back button doesn't cancel
mLocationDialog.setCancelable(false);
mLocationDialog.setIndeterminate(true);
mLocationDialog.setIcon(android.R.drawable.ic_dialog_info);
mLocationDialog.setTitle(getString(R.string.getting_location));
mLocationDialog.setMessage(getString(R.string.please_wait_long));
mLocationDialog.setButton(DialogInterface.BUTTON_POSITIVE, getString(R.string.accept_location),
geopointButtonListener);
mLocationDialog.setButton(DialogInterface. BUTTON_NEGATIVE, getString(R.string.cancel_location),
geopointButtonListener);
}
private void returnLocation() {
if (mLocation != null) {
Intent i = new Intent();
i.putExtra(
FormEntryActivity.LOCATION_RESULT,
mLocation.getLatitude() + " " + mLocation.getLongitude() + " "
+ mLocation.getAltitude() + " " + mLocation.getAccuracy());
setResult(RESULT_OK, i);
}
finish();
}
@Override
public void onLocationChanged(Location location) {
mLocation = location;
if (mLocation != null) {
// Bug report: cached GeoPoint is being returned as the first value.
// Wait for the 2nd value to be returned, which is hopefully not cached?
++mLocationCount;
InfoLogger.geolog("GeoPointActivity: " + System.currentTimeMillis() +
" onLocationChanged(" + mLocationCount + ") lat: " +
mLocation.getLatitude() + " long: " +
mLocation.getLongitude() + " acc: " +
mLocation.getAccuracy() );
if (mLocationCount > 1) {
mLocationDialog.setMessage(getString(R.string.location_provider_accuracy,
mLocation.getProvider(), truncateDouble(mLocation.getAccuracy())));
if (mLocation.getAccuracy() <= mLocationAccuracy) {
returnLocation();
}
}
} else {
InfoLogger.geolog("GeoPointActivity: " + System.currentTimeMillis() +
" onLocationChanged(" + mLocationCount + ") null location");
}
}
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 (mLocation != null) {
mLocationDialog.setMessage(getString(R.string.location_accuracy,
mLocation.getAccuracy()));
}
break;
case LocationProvider.OUT_OF_SERVICE:
break;
case LocationProvider.TEMPORARILY_UNAVAILABLE:
break;
}
}
}