I am creating a simple location app that stores the user's location upon command. I am attempting to use both the GPS provider and Network provider to establish the user's location.
The process is achieved using Asynctask and works likes this:
Checks last known GPS location and Network location. If locations are within acceptable parameters the thread finishes.
If last know locations are unacceptable, updates are requested of both GPS and Network.
Once 4 network locations have been found or either 3 gps locations have been found the thread finishes.
A dialog box is displayed when waiting for a location fix.
PROBLEM: GPS seems to be updating whereas Network does not. I have tried solely updating NETWORK_PROVIDER, waited for over an hour with no results. I have tried with both WIFI on and off.
The code for the GetGPS class:
public class GetGPS extends AsyncTask<String, Void, String> implements LocationListener {
private ProgressDialog progDialog;
public double[] DataArray = {0.0,0.0,0.0};
private Integer NetworkCount = 0;
private Integer GPSCount = 0;
private LocationManager mlocManager;
private Context mContext;
private Activity mActivity;
final LocationListener locationListener = (LocationListener) this;
private boolean HasCompleted = false;
public GetGPS(Context context, Activity activity) {
mContext = context;
mActivity = activity;
}
#Override
public void onPreExecute() {
Log.v(getClass().getName(),"PreExecute");
mlocManager = (LocationManager) mContext.getSystemService(Context.LOCATION_SERVICE);
//Stop any current GPS updates
mlocManager.removeUpdates((LocationListener) this);
//Get current system time in millis
long currentTime = System.currentTimeMillis();
//Check last known GPS location and corresponding accuracy and time
String GPSlocationProvider = LocationManager.GPS_PROVIDER;
Location lastKnownGPSLocation = mlocManager.getLastKnownLocation(GPSlocationProvider);
if (lastKnownGPSLocation != null) {
long lastGPStime = lastKnownGPSLocation.getTime();
long timeDifference = currentTime - lastGPStime;
float lastGPSaccuracy = lastKnownGPSLocation.getAccuracy();
Log.v(getClass().getName(),"Found last known GPS! GPS time diff:"+timeDifference+", Accuracy:"+lastGPSaccuracy);
if ((lastGPSaccuracy < 50) && (timeDifference < 180000)) {
Log.v(getClass().getName(),"Acceptable last known GPS location!");
DataArray[0] = (double) lastKnownGPSLocation.getLatitude();
DataArray[1] = (double) lastKnownGPSLocation.getLongitude();
DataArray[2] = (double) lastKnownGPSLocation.getAccuracy();
HasCompleted = true;
}
else {
Log.v(getClass().getName(),"Last known location GPS too inaccurate!");
}
}
else {
Log.v(getClass().getName(),"No previous GPS locations!");
}
//Check last known network location and corresponding accuracy and time
String NetworklocationProvider = LocationManager.NETWORK_PROVIDER;
Location lastKnownNetworkLocation = mlocManager.getLastKnownLocation(NetworklocationProvider);
if (lastKnownNetworkLocation != null) {
long lastNetworktime = lastKnownNetworkLocation.getTime();
long timeDifference = currentTime - lastNetworktime;
float lastNetworkaccuracy = lastKnownNetworkLocation.getAccuracy();
Log.v(getClass().getName(),"Found last known Network location! Time diff:"+timeDifference+", Accuracy:"+lastNetworkaccuracy);
if ((lastNetworkaccuracy < 50) && (timeDifference < 180000)) {
Log.v(getClass().getName(),"Acceptable last known Network location!");
DataArray[0] = (double) lastKnownNetworkLocation.getLatitude();
DataArray[1] = (double) lastKnownNetworkLocation.getLongitude();
DataArray[2] = (double) lastKnownNetworkLocation.getAccuracy();
HasCompleted = true;
}
else {
Log.v(getClass().getName(),"Last known Network location too inaccurate!");
}
}
else {
Log.v(getClass().getName(),"No previous Network locations!");
}
// Request both providers to update
mlocManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0,(LocationListener) this);
mlocManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 0, 0,(LocationListener) this);
//Display dialog box
progDialog = new ProgressDialog(mActivity);
progDialog.setButton(DialogInterface.BUTTON_NEGATIVE, "Stop", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
Log.v(getClass().getName(),"ProgDialog cancelled");
mlocManager.removeUpdates(locationListener);
GetGPS.this.cancel(true);
}
});
progDialog.setMessage("Acquiring location..");
progDialog.setIndeterminate(true);
progDialog.setCancelable(false);
progDialog.show();
Log.v(getClass().getName(),"Show progdialog");
}
#Override
public String doInBackground(String... params) {
while (HasCompleted == false) {
}
Log.v(getClass().getName(),"doInBackground completed");
return "Success";
}
#Override
public void onPostExecute(String result) {
Log.v(getClass().getName(),"onPostExecute");
//Destroy Dialog box
progDialog.dismiss();
//TODO: Deal with data
}
public void onLocationChanged(Location location) {
Log.v(getClass().getName(),"onLocationChanged, Provider:'"+location.getProvider()+"'");
// Check if from Network
if (location.getProvider().equals("network")){
Log.v(getClass().getName(),"Network location Changed");
NetworkCount = NetworkCount + 1;
if (NetworkCount > 3) {
DataArray[0] = (double) location.getLatitude();
DataArray[1] = (double) location.getLongitude();
DataArray[2] = (double) location.getAccuracy();
Log.v(getClass().getName(),"Location stored from Network!");
Log.v("data array 0","" + DataArray[0]);
mlocManager.removeUpdates((LocationListener) this);
HasCompleted = true;
}
else {
Log.v(getClass().getName(),"Network location count:"+NetworkCount);
}
}
//Check if from GPS
if (location.getProvider().equals("gps")){
Log.v(getClass().getName(),"GPS location Changed");
GPSCount = GPSCount + 1;
if (GPSCount > 2) {
DataArray[0] = (double) location.getLatitude();
DataArray[1] = (double) location.getLongitude();
DataArray[2] = (double) location.getAccuracy();
Log.v(getClass().getName(),"Location stored from GPS!");
//Log.v("data array 0","" + DataArray[0]);
mlocManager.removeUpdates((LocationListener) this);
HasCompleted = true;
}
else {
Log.v(getClass().getName(),"GPS location count:"+GPSCount);
}
}
}
#Override
public void onProviderDisabled(String provider) {
Log.v("OnProviderDisabled", "OnProviderDisabled:"+provider);
}
#Override
public void onProviderEnabled(String provider) {
Log.v("onProviderEnabled", "onProviderEnabled:"+provider);
}
#Override
public void onStatusChanged(String provider, int status,
Bundle extras) {
Log.v("onStatusChanged", "onStatusChanged, provider:"+provider+", Status:"+status);
}
}
The GetGPS class is called in the following manner:
Context mContext = getActivity().getApplicationContext();
Activity mActivity = getActivity();
GetGPS getgps = new GetGPS(mContext, mActivity);
getgps.execute();
Any help as to where i'm going wrong? Thank you in advance forany help.
Also: Is there a simple way to implement a timeout for the Asynctask? I.e. If no location fix is found after a set time period, the thread stops.
About your first question, please check that both GPS and Network providers are enabled at your phone:
I'm also not sure if implementing LocationListener inside AsyncTask is the proper way, I tried to read about it and came across this. Try to read it and implement the solution he propose. (What I would do is create an independent location Singelton class which will have nethods such as stop and start and will be controlled from the AsyncTask).
At your doInBackground method, I would change the While loop, and just put a Thread.sleep(50) inside. to prevent busy waiting.
About your second question, the simplest thing to do here is check the time that has passed in the while loop of doInBackground and stop the thread there is necessary.
Related
in my application i have a button that start counting the time how long the user has been in the same location. and i want the app stop the timer counting if the user goes out from the area he started so i made a class that give me the current location (LAT,LONG,PLACE NAME) and i don't know how to use the "onLocationChanged" and Should I use it? or something else for what i need?
public class MapCurrentPlace extends Service implements LocationListener {
private final Context mContext;
public static final String MY_TEMP = "sharedFile";
SharedPreferences setting;
SharedPreferences.Editor editor;
// flag for GPS status
boolean isGPSEnabled = false;
// flag for network status
boolean isNetworkEnabled = false;
// flag for GPS status
boolean canGetLocation = false;
Location location; // location
double latitude; // latitude
double longitude; // longitude
String placeName = "";
// The minimum distance to change Updates in meters
private static final long MIN_DISTANCE_CHANGE_FOR_UPDATES = 0; // 10 meters
// The minimum time between updates in milliseconds
private static final long MIN_TIME_BW_UPDATES = 500; // 1 minute
// Declaring a Location Manager
protected LocationManager locationManager;
public MapCurrentPlace(Context context) {
this.mContext = context;
getLocation();
}
public String getPlaceName(){
Geocoder gc = new Geocoder(mContext);
try {
List<Address> list = gc.getFromLocation(latitude, longitude, 1);
if(list.size()>0){
String city = list.get(0).getLocality();
String street = list.get(0).getAddressLine(0);
placeName = city+", "+street+"";
}
} catch (IOException e) {
placeName = "";
e.printStackTrace();
}
return placeName;
}
public Location getLocation() {
try {
locationManager = (LocationManager) mContext
.getSystemService(Context.LOCATION_SERVICE);
// getting network status
isNetworkEnabled = locationManager
.isProviderEnabled(LocationManager.NETWORK_PROVIDER);
// getting GPS status
isGPSEnabled = locationManager
.isProviderEnabled(LocationManager.GPS_PROVIDER);
if (!isGPSEnabled) {
// no network provider is enabled
this.canGetLocation = false;
return null;
} else {
this.canGetLocation = true;
if (isNetworkEnabled) {
locationManager.requestLocationUpdates( LocationManager.NETWORK_PROVIDER,MIN_TIME_BW_UPDATES,0, this);
if (locationManager != null) {
location = locationManager.getLastKnownLocation(LocationManager.NETWORK_PROVIDER);
if (location != null) {
latitude = location.getLatitude();
longitude = location.getLongitude();
}
}
}
// if GPS Enabled get lat/long using GPS Services
if (isGPSEnabled) {
if (location == null) {
locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER,MIN_TIME_BW_UPDATES,0, this);
if (locationManager != null) {
location = locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER);
if (location != null) {
latitude = location.getLatitude();
longitude = location.getLongitude();
}
}
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
return location;
}
/**
* Stop using GPS listener
* Calling this function will stop using GPS in your app
* */
public void stopUsingGPS(){
if(locationManager != null){
locationManager.removeUpdates(MapCurrentPlace.this);
}
}
/**
* Function to get latitude
* */
public double getLatitude(){
if(location != null){
latitude = location.getLatitude();
}
// return latitude
return latitude;
}
/**
* Function to get longitude
* */
public double getLongitude(){
if(location != null){
longitude = location.getLongitude();
}
// return longitude
return longitude;
}
/**
* Function to check GPS/wifi enabled
* #return boolean
* */
public boolean canGetLocation() {
return this.canGetLocation;
}
public boolean getIsNetworkEnabled() {
return this.isNetworkEnabled;
}
/**
* Function to show settings alert dialog
* On pressing Settings button will lauch Settings Options
* */
public void showSettingsAlert(){
AlertDialog.Builder alertDialog = new AlertDialog.Builder(mContext);
// Setting Dialog Title
alertDialog.setTitle("GPS OFF");
// Setting Dialog Message
alertDialog.setMessage("Allow GPS");
// On pressing Settings button
alertDialog.setPositiveButton("SETTING", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog,int which) {
Intent intent = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS);
mContext.startActivity(intent);
}
});
// on pressing cancel button
alertDialog.setNegativeButton("CANCEL", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
dialog.cancel();
}
});
// Showing Alert Message
alertDialog.show();
}
#Override
public void onLocationChanged(Location location) {
}
#Override
public void onProviderDisabled(String provider) {
}
#Override
public void onProviderEnabled(String provider) {
}
#Override
public void onStatusChanged(String provider, int status, Bundle extras) {
}
#Override
public IBinder onBind(Intent intent) {
// TODO Auto-generated method stub
return null;
}
}
locationManager.getLastKnownLocation
this one returns last know location fix. This might be kind of old if no apps requested location for a while.
Location updates will be fired each time device location was changed.
So it's up to you what to use. If you want to get up-to date location, use location updates. But bear in mind that location updated drain battery.
Also you might want to look into Geofences: http://developer.android.com/training/location/geofencing.html
The onLocationChanged method will be called when the current location is changed. This is a default method called because you have implemented the LocationListener.
I do not know how you are implementing the timer (perhaps an AsyncTask so your app can do other stuff as well - http://developer.android.com/reference/android/os/AsyncTask.html), but then in the OnLocationChanged method add some call to your timer variable to stop.
OnLocationChanged will trigger whenever the device travels the minDistance parameter of requestLocationUpdates, from the last relevant location of your device (i.e. since the event last triggered).
Of course, the trigger rate also takes into account the minTime parameter, which should be used to prevent saturation of the calls, and thus saving battery/data usage (e.g. car travelling too fast for your minDistance).
As you have it now (you supplied an inline 0 to the call), you need to manually check if the most recent OnLocationChanged data is a relevant change to your scenario.
It's much more transparent to just use that parameter as the system tries to make it hardware-independent and battery-efficient. You already have a MIN_DISTANCE_CHANGE_FOR_UPDATES in your boilerplate code, just add it as a parameter instead of '0' as you have, and set it to the number of meters you need:
// The minimum distance to change Updates in meters
private static final long MIN_DISTANCE_CHANGE_FOR_UPDATES = 30; //30 meters
//...
//at some point while your service/activity is running
locationManager.requestLocationUpdates( LocationManager.NETWORK_PROVIDER,
MIN_TIME_BW_UPDATES,MIN_DISTANCE_CHANGE_FOR_UPDATES,
this);
//...
#Override
public void onLocationChanged(Location currentLocation) {
//triggered when System asserts a 30 meter variation from last relevant location
doMyStuff();
}
Side note: remember most GPS hardware has issues with very fine locations, especially at sub-10m.
You can also keep the value at 0, receive updates at the granularity the hardware updates itself, and do the checks manually - just use the Location parameter supplied with the event and do your own checks to decide, which is pretty much a geo-fencing check:
Location lastRelevantLocation;
#Override
public void onLocationChanged(Location currentLocation) {
// called when the listener is notified with a location update from the GPS
// hardware update rate might call this more than your CPU should handle
if(checkOutsidePerimeter(location)){
lastRelevantLocation = currentLocation;
doMyStuff();
}
}
Remember minTime if you want to keep tabs on granularity.
I have a LocationService which starts onResume() of the MainActivity and stops onDestroy().
#Override
protected void onResume() {
super.onResume();
//Start the service using alaram manager
//If its not running currently
if (isLocationServiceRunning(this)) {
am = (AlarmManager) getSystemService(ALARM_SERVICE);
Intent intent = new Intent(this, LocationService.class);
pi = PendingIntent.getService(this, 0, intent,
PendingIntent.FLAG_UPDATE_CURRENT);
am.cancel(pi);
am.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP,
SystemClock.elapsedRealtime(), 1 * 60 * 1000, pi);
}
}
#Override
protected void onDestroy() {
super.onDestroy();
if (isLocationServiceRunning(this)) {
stopService(new Intent(this, LocationService.class));
if (am != null && pi != null) {
am.cancel(pi);
}
}
}
LocationService.java
public class LocationService extends Service implements LocationListener {
public static double curLat = 0.0;
public static double curLng = 0.0;
private LocationManager mgr;
private String best;
private Location location;
private Location currentBestLocation;
private static final int TWO_MINUTES = 1000 * 60 * 2;
#Override
public IBinder onBind(Intent arg0) {
return null;
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
mgr = (LocationManager) getSystemService(LOCATION_SERVICE);
boolean gps_enabled = mgr
.isProviderEnabled(LocationManager.GPS_PROVIDER);
if (gps_enabled) {
// If GPS is enabled, set criteria as ACCURACY_FINE
// and get the best provider(which usually will be GPS_PROVIDER)
Criteria criteria = new Criteria();
criteria.setAccuracy(Criteria.ACCURACY_FINE);
best = mgr.getBestProvider(criteria, true);
// getLastKnownLocation so that user don't need to wait
location = mgr.getLastKnownLocation(best);
if (location == null) {
// request for a single update, and try again.
// Later will request for updates every 10 mins
mgr.requestSingleUpdate(criteria, this, null);
location = mgr
.getLastKnownLocation(LocationManager.GPS_PROVIDER);
}
if (location != null) {
// If the GPS gives a location, update curLat and curLng
dumpLocation(location);
} else {
// If the location is still null, go for NETWORK_PROVIDER
best = LocationManager.NETWORK_PROVIDER;
location = mgr.getLastKnownLocation(best);
if (location != null) {
// If the NETWORK gives a location, update curLat and curLng
dumpLocation(location);
}
}
// Register the Location Manager for updates, with both the
// providers
// Since GPS updates are expensive, we ask update every 10 mins and
// unregister updates if GPS is disabled in onProviderDisabled
// callback
mgr.requestLocationUpdates(LocationManager.GPS_PROVIDER,
10 * 60 * 1000, 50, this);
// NETWORK_PROVIDER updates every 20 secs
mgr.requestLocationUpdates(LocationManager.NETWORK_PROVIDER,
20 * 1000, 0, this);
return START_NOT_STICKY;
} else {
// If GPS is disables, go with NETWORK_PROVIDER
best = LocationManager.NETWORK_PROVIDER;
location = mgr.getLastKnownLocation(best);
if (location != null) {
dumpLocation(location);
}
// Register NETWORK_PROVIDER for updates every 20 secs
mgr.requestLocationUpdates(LocationManager.NETWORK_PROVIDER,
20 * 1000, 0, this);
return START_NOT_STICKY;
}
}
private void dumpLocation(Location l) {
// Called to update the curLat and curLng.
currentBestLocation = l;
SimpleDateFormat s = new SimpleDateFormat("dd/MM/yyyy:hh:mm:ss",
Locale.ENGLISH);
String format = s.format(l.getTime());
try {
Geocoder coder = new Geocoder(this);
List<Address> address;
Address location = null;
address = coder.getFromLocation(l.getLatitude(), l.getLongitude(),
1);
location = address.get(0);
} catch (Exception e) {
Log.e("Exception while getting address", e.getMessage() + "");
}
curLat = l.getLatitude();
curLng = l.getLongitude();
}
#Override
public void onLocationChanged(Location location) {
// called when location is changed, since we registered Location
// Providers
// for updates
if (isBetterLocation(location, currentBestLocation)) {
dumpLocation(location);
} else {
Log.d("Not a Better Location", "Ignore");
}
}
#Override
public void onProviderDisabled(String provider) {
// Check if best(the currently being used provider) is not null
if (best != null) {
// if best and disabled provider are same, the remove updates
if ((provider.equalsIgnoreCase(LocationManager.GPS_PROVIDER) && best
.equals(LocationManager.GPS_PROVIDER))
|| provider
.equalsIgnoreCase(LocationManager.NETWORK_PROVIDER)
&& best.equals(LocationManager.NETWORK_PROVIDER)) {
if (mgr != null) {
mgr.removeUpdates(this);
}
}
}
}
#Override
public void onProviderEnabled(String provider) {
// This will be taken care in the onStartCommand where if gps_enabled
// case is used.
}
#Override
public void onStatusChanged(String provider, int status, Bundle extras) {
// No need to care about, because any thing like OUT_OF_SERVICE occurs,
// location being fetched will be null and such cases are handled above.
if ((provider.equals(LocationManager.GPS_PROVIDER))
&& (LocationProvider.OUT_OF_SERVICE == status)) {
if (mgr != null) {
mgr.removeUpdates(this);
}
}
}
#Override
public void onDestroy() {
super.onDestroy();
// triggered when we call stopService(LocationService);
// which is done in onDestroy of MainActivity
// Because LocationService must be stopped
// when application is closed to avoid data usage
if (mgr != null) {
mgr.removeUpdates(this);
}
}
protected boolean isBetterLocation(Location location,
Location currentBestLocation) {
if (currentBestLocation == null) {
// A new location is always better than no location
return true;
}
// Check whether the new location fix is newer or older
long timeDelta = location.getTime() - currentBestLocation.getTime();
boolean isSignificantlyNewer = timeDelta > TWO_MINUTES;
boolean isSignificantlyOlder = timeDelta < -TWO_MINUTES;
boolean isNewer = timeDelta > 0;
// If it's been more than two minutes since the current location, use
// the new location
// because the user has likely moved
if (isSignificantlyNewer) {
return true;
// If the new location is more than two minutes older, it must be
// worse
} else if (isSignificantlyOlder) {
return false;
}
// Check whether the new location fix is more or less accurate
int accuracyDelta = (int) (location.getAccuracy() - currentBestLocation
.getAccuracy());
boolean isLessAccurate = accuracyDelta > 0;
boolean isMoreAccurate = accuracyDelta < 0;
boolean isSignificantlyLessAccurate = accuracyDelta > 200;
// Check if the old and new location are from the same provider
boolean isFromSameProvider = isSameProvider(location.getProvider(),
currentBestLocation.getProvider());
// Not significantly newer or older, so check for Accuracy
if (isMoreAccurate) {
// If more accurate return true
return true;
} else if (isNewer && !isLessAccurate) {
// Same accuracy but newer, return true
return true;
} else if (isNewer && !isSignificantlyLessAccurate
&& isFromSameProvider) {
// Accuracy is less (not much though) but is new, so if from same
// provider return true
return true;
}
return false;
}
// Checks whether two providers are the same
private boolean isSameProvider(String provider1, String provider2) {
if (provider1 == null) {
return provider2 == null;
}
return provider1.equals(provider2);
}
}
The service surely starts and stops as expected and I can see the location details in log, which are fine.
The problem if when I move to a complete different location(300 miles), the curLat and curLng values still remain as that of the old, when I open the application.
Is it because I am not running the service when the device is in motion(because my application is not running)?
Because when I open some other application like FourSquare(which gets the correct location) and then reopen my application, then it shows the correct location.
What else should I do to refresh the location properly.
I think your problem is here
best = mgr.getBestProvider(criteria, true);
// getLastKnownLocation so that user don't need to wait
location = mgr.getLastKnownLocation(best);
if (location == null) {
// request for a single update, and try again.
// Later will request for updates every 10 mins
mgr.requestSingleUpdate(criteria, this, null);
location = mgr
.getLastKnownLocation(LocationManager.GPS_PROVIDER);
}
because there was previously a location location = mgr.getLastKnownLocation(best); returns that location without starting the provider (see the android documentation. So the location is not null and mgr.requestSingleUpdate(criteria, this, null); is never run.
To get up to date location data a provider must be started.
so a correction could be:
best = mgr.getBestProvider(criteria, true);
// getLastKnownLocation so that user don't need to wait
mgr.requestSingleUpdate(best, this, null);
location = mgr.getLastKnownLocation(best);
Also I'm not sure if it is intended but this service will use the network provider even when GPS data is available and more accurate (due to the 10 minute and 2 minute times chosen for GPS updates and data obsolescence.
P.S. Is there a specific reason you do not want to use FusedLocationProvider that is part of Google Play Services? I have found it to be simpler and it is supposedly optimized for selected best providers and conserving battery.
You code looks perfectly fine if you want to get the location in the foreground. I have gone through in the deep and get to know that in the onDestroy you have stopped the service and alarms also. hence as and when the current app is going to background and the onDestroy is called by system then the code fails to update the location in the background. more over when you launch the application again it will start the service and very first time get the older location which was cached.
when other application updates the location you will get that location according to documentation of the mgr.getLastKnownLocation(best).
Hence to solve this problem do not use alarm here to start service in repeating manner or destory it.
simply start the service and in the onStartCommand ask for the update of the location. and if you want to get rid of the location updates, use removeLocationUpdates(LocationListener) .
Examples are given here http://developer.android.com/training/location/receive-location-updates.html
My best guess is dump "isBetterLocation" and try without it to see what will happen. Based on those checks (which are rather complicated), I think the mistake is either in "isSignificantlyOlder" or in the last return statement (otherwise you would get the new location, correct?)
Have you debugged it to check if the current logic is correct, and if it is, for what distances?
Here is an example to receive location update using Google Play Services
This is MyActivity class
public class MyActivity extends Activity implements
ConnectionCallbacks, OnConnectionFailedListener {
public static final int PLAY_SERVICES_NOT_AVAILABLE_REQUEST = 9000;
public static final int CONNECTION_FAILED_REQUEST = 1000;
private LocationClient mLocationClient;
private LocationRequest mLocationrequest;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_myactivity);
LocationManager mLocationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
mLocationClient = new LocationClient(this, this, this);
boolean isGPSEnabled = mLocationManager
.isProviderEnabled(LocationManager.GPS_PROVIDER);
boolean isNetworkEnabled = mLocationManager
.isProviderEnabled(LocationManager.NETWORK_PROVIDER);
Toast.makeText(this, "GPS: " + isGPSEnabled, Toast.LENGTH_SHORT).show();
Toast.makeText(this, "Network: " + isNetworkEnabled, Toast.LENGTH_SHORT)
.show();
if (isGooglePlayServicesAvailable()) {
mLocationClient.connect();
} else {
// play services not available
}
}
private void defineLocationRequest() {
mLocationrequest = new LocationRequest();
mLocationrequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY)
.setInterval(5000);
}
private PendingIntent getCallBackIntent() {
return PendingIntent
.getService(getApplicationContext(), 0, new Intent(this,
MyIntentService.class),
PendingIntent.FLAG_UPDATE_CURRENT);
}
private boolean isGooglePlayServicesAvailable() {
int resultCode = GooglePlayServicesUtil
.isGooglePlayServicesAvailable(this);
if (resultCode == ConnectionResult.SUCCESS) {
Log.d("Car Tracking", "play services available.");
return true;
} else {
Log.d("Car Tracking", "play services not available(resultCode:) "
+ resultCode);
GooglePlayServicesUtil.getErrorDialog(resultCode, this,
PLAY_SERVICES_NOT_AVAILABLE_REQUEST).show();
return false;
}
}
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
// TODO Auto-generated method stub
switch (requestCode) {
case PLAY_SERVICES_NOT_AVAILABLE_REQUEST:
if (resultCode == Activity.RESULT_OK) {
// check again
}
break;
case CONNECTION_FAILED_REQUEST:
if (resultCode == Activity.RESULT_OK) {
// try to connect LocationClient Againg
}
break;
}
}
#Override
public void onConnectionFailed(ConnectionResult arg0) {
// TODO Auto-generated method stub
if (arg0.hasResolution()) {
try {
arg0.startResolutionForResult(this, CONNECTION_FAILED_REQUEST);
} catch (SendIntentException e) {
Log.d("TAG",
"Exception in resolving connection failed: "
+ e.toString());
}
}
}
#Override
public void onConnected(Bundle arg0) {
// TODO Auto-generated method stub
defineLocationRequest();
mLocationClient.requestLocationUpdates(mLocationrequest,
getCallBackIntent());
}
#Override
public void onDisconnected() {
// TODO Auto-generated method stub
}
#Override
protected void onDestroy() {
// TODO Auto-generated method stub
mLocationClient.removeLocationUpdates(getCallBackIntent());
super.onDestroy();
}
}
Now, this is MyIntentService Class's onHandleIntent Method.
protected void onHandleIntent(Intent intent) {
// TODO Auto-generated method stub
if (intent != null) {
Bundle extra = intent.getExtras();
Location location = (Location) extra
.get(LocationClient.KEY_LOCATION_CHANGED);
}
Here, the location object will give you most recent location update
Also add
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
in your manifest
You can use the LocationClient from Google Play Services, its easy to use and proven very efficient.
Here is the link to example
Use Fused Location Provider (new feature available since 4.2 - https://developer.android.com/google/play-services/location.html) - it just gets fast current location and sending updates.
Example: http://www.motta-droid.com/2013/11/location-requests-for-your-app-how-to.html
Just run singleton above in a Service and adjust location update params to your needs.
The only issue You should care about - if it can't determine your current location at all. For example, if just GPS location provider available to your device and you're indoors.
I observed your code..You are updating the location but you are not receiving the updated location information. here is the code how to get the location from a Service
// Send an Intent with an action named "custom-event-name". The Intent sent
// should
// be received by the ReceiverActivity.
private static void sendMessageToActivity(Location l, String msg) {
Intent intent = new Intent("GPSLocationUpdates");
// You can also include some extra data.
intent.putExtra("Status", msg);
Bundle b = new Bundle();
b.putParcelable("Location", l);
intent.putExtra("Location", b);
LocalBroadcastManager.getInstance(context).sendBroadcast(intent);
}
in you main activity or which has to receive the location Info write this code.
LocalBroadcastManager.getInstance(getActivity()).registerReceiver(
mMessageReceiver, new IntentFilter("GPSLocationUpdates"));
private BroadcastReceiver mMessageReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
Bundle b = intent.getBundleExtra("Location");
lastKnownLoc = (Location) b.getParcelable("Location");
if (lastKnownLoc != null) {
tvLatitude.setText(String.valueOf(lastKnownLoc.getLatitude()));
tvLongitude
.setText(String.valueOf(lastKnownLoc.getLongitude()));
}
}
};
I hope this will work...
I you do not mind waiting for GPS to achieve a first-fix this might help you. The first-fix should only be a matter of seconds if a fix have been found recently.
I have implemented some code that sends callback as soon as there is a first-fix and on locationchange based on GPSTracker from http://www.androidhive.info/2012/07/android-gps-location-manager-tutorial/.
With this implementation you can do:
private GPSTracker gps;
private FirstFixListener firstFixListener;
private LocationUpdateListener locationUpdateListener;
private void startGPS() {
gps = GPSTracker.getInstance(context);
// create listeners
firstFixListener = new MyFirstFixListener();
locationUpdateListener = new MyLocationUpdateListener();
// start the gps
gps.startUsingGPS(firstFixListener, locationUpdateListener);
}
private void stopGPS() {
// stop the gps and unregister callbacks
gps.stopUsingGPS(firstFixListener, locationUpdateListener);
}
private class MyFirstFixListener implements FirstFixListener {
#Override
public void onFirsFixChanged(boolean hasGPSfix) {
if (hasGPSfix == true) {
// accurate position
Location position = gps.getLocation();
}
}
}
private class MyLocationUpdateListener implements LocationUpdateListener {
#Override
public void onLocationChanged(Location location) {
// hand you each new location from the GPS
// you do not need this if you only want to get a single position
}
}
And here is my implementation of GPSTracker:
public class GPSTracker extends Service implements LocationListener {
private static final String TAG = "GPSTracker";
/**
* Register to receive callback on first fix status
*
* #author Morten
*
*/
public interface FirstFixListener {
/**
* Is called whenever gps register a change in first-fix availability
* This is valuable to prevent sending invalid locations to the server.
*
* #param hasGPSfix
*/
public void onFirsFixChanged(boolean hasGPSfix);
}
/**
* Register to receive all location updates
*
* #author Morten
*
*/
public interface LocationUpdateListener {
/**
* Is called every single time the GPS unit register a new location
* The location param will never be null, however, it can be outdated if hasGPSfix is not true.
*
* #param location
*/
public void onLocationChanged(Location location);
}
private Context mContext;
// flag for GPS status
private List<FirstFixListener> firstFixListeners;
private List<LocationUpdateListener> locationUpdateListeners;
boolean isGPSFix = false;
boolean isGPSEnabled = false;
private GPSFixListener gpsListener;
// flag for GPS status
boolean canGetLocation = false;
Location location; // location
double latitude; // latitude
double longitude; // longitude
long mLastLocationMillis;
private boolean logLocationChanges;
// Declaring a Location Manager
protected LocationManager locationManager;
/** removed again as we need multiple instances with different callbacks **/
private static GPSTracker instance;
public static GPSTracker getInstance(Context context) {
if (instance != null) {
return instance;
}
return instance = new GPSTracker(context);
}
private GPSTracker(Context context) {
this.mContext = context;
gpsListener = new GPSFixListener();
firstFixListeners = new ArrayList<GPSTracker.FirstFixListener>();
locationUpdateListeners = new ArrayList<GPSTracker.LocationUpdateListener>();
}
public boolean hasGPSFirstFix() {
return isGPSFix;
}
private void addFirstFixListener(FirstFixListener firstFixListener) {
this.firstFixListeners.add(firstFixListener);
}
private void addLocationUpdateListener(
LocationUpdateListener locationUpdateListener) {
this.locationUpdateListeners.add(locationUpdateListener);
}
private void removeFirstFixListener(FirstFixListener firstFixListener) {
this.firstFixListeners.remove(firstFixListener);
}
private void removeLocationUpdateListener(
LocationUpdateListener locationUpdateListener) {
this.locationUpdateListeners.remove(locationUpdateListener);
}
public void setLogLocationChanges(boolean logLocationChanges) {
this.logLocationChanges = logLocationChanges;
}
public Location getLocation() {
return location;
}
private Location startLocationListener() {
canGetLocation = false;
try {
locationManager = (LocationManager) mContext
.getSystemService(Service.LOCATION_SERVICE);
// getting GPS status
isGPSEnabled = locationManager
.isProviderEnabled(LocationManager.GPS_PROVIDER);
if (isGPSEnabled) {
if (location == null) {
locationManager.requestLocationUpdates(
LocationManager.GPS_PROVIDER, 0, 0, this);
locationManager.addGpsStatusListener(gpsListener);
if (locationManager != null) {
location = locationManager
.getLastKnownLocation(LocationManager.GPS_PROVIDER);
if (location != null) {
latitude = location.getLatitude();
longitude = location.getLongitude();
}
}
}
} else {
showSettingsAlert();
}
} catch (Exception e) {
e.printStackTrace();
}
return location;
}
public void stopUsingGPS(FirstFixListener firstFixListener,
LocationUpdateListener locationUpdateListener) {
if (firstFixListener != null)
removeFirstFixListener(firstFixListener);
if (locationUpdateListener != null)
removeLocationUpdateListener(locationUpdateListener);
stopUsingGPS();
}
/**
* Stop using GPS listener Calling this function will stop using GPS in your
* app
* */
public void stopUsingGPS() {
Log.d("DEBUG", "GPS stop");
if (locationManager != null) {
locationManager.removeUpdates(GPSTracker.this);
location = null;
if (gpsListener != null) {
locationManager.removeGpsStatusListener(gpsListener);
}
}
isGPSFix = false;
location = null;
}
public void startUsingGPS(FirstFixListener firstFixListener,
LocationUpdateListener locationUpdateListener) {
Log.d("DEBUG", "GPS start");
if (firstFixListener != null)
addFirstFixListener(firstFixListener);
if (locationUpdateListener != null)
addLocationUpdateListener(locationUpdateListener);
startLocationListener();
}
/**
* Function to get latitude
* */
public double getLatitude() {
if (location != null) {
latitude = location.getLatitude();
} else {
Log.e("GPSTracker", "getLatitude location is null");
}
// return latitude
return latitude;
}
/**
* Function to get longitude
* */
public double getLongitude() {
if (location != null) {
longitude = location.getLongitude();
} else {
Log.e("GPSTracker", "getLongitude location is null");
}
// return longitude
return longitude;
}
/**
* Function to check GPS/wifi enabled
*
* #return boolean
* */
public boolean canGetLocation() {
return this.canGetLocation;
}
/**
* Function to show settings alert dialog On pressing Settings button will
* lauch Settings Options
* */
public void showSettingsAlert() {
AlertDialog.Builder alertDialog = new AlertDialog.Builder(mContext);
// Setting Dialog Title
alertDialog.setTitle("GPS settings");
// Setting Dialog Message
alertDialog
.setMessage("GPS is not enabled. Do you want to go to settings menu?");
// On pressing Settings button
alertDialog.setPositiveButton("Settings",
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
Intent intent = new Intent(
Settings.ACTION_LOCATION_SOURCE_SETTINGS);
mContext.startActivity(intent);
}
});
// on pressing cancel button
alertDialog.setNegativeButton("Cancel",
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
dialog.cancel();
}
});
// Showing Alert Message
alertDialog.show();
}
#Override
public void onLocationChanged(Location location) {
if ( location == null)
return;
this.location = location;
mLastLocationMillis = SystemClock.elapsedRealtime();
canGetLocation = true;
if (isGPSFix) {
if (locationUpdateListeners != null) {
for (LocationUpdateListener listener : locationUpdateListeners) {
listener.onLocationChanged(location);
}
}
}
}
#Override
public void onProviderDisabled(String provider) {
canGetLocation = false;
}
#Override
public void onProviderEnabled(String provider) {
}
#Override
public void onStatusChanged(String provider, int status, Bundle extras) {
}
#Override
public IBinder onBind(Intent arg0) {
return null;
}
private boolean wasGPSFix = false;
// http://stackoverflow.com/questions/2021176/how-can-i-check-the-current-status-of-the-gps-receiver
// answer from soundmaven
private class GPSFixListener implements GpsStatus.Listener {
public void onGpsStatusChanged(int event) {
switch (event) {
case GpsStatus.GPS_EVENT_SATELLITE_STATUS:
isGPSFix = (SystemClock.elapsedRealtime() - mLastLocationMillis) < 3000;
if (isGPSFix != wasGPSFix) { // only notify on changes
wasGPSFix = isGPSFix;
for (FirstFixListener listener : firstFixListeners) {
listener.onFirsFixChanged(isGPSFix);
}
}
break;
case GpsStatus.GPS_EVENT_FIRST_FIX:
// Do something.
break;
}
}
}
}
I am developing an application which lists restaurants closest to the user. When the refresh button is clicked, it lists the restaurants for the user's current location. I am using the Location Manager and requesting updates only when the activity comes to foreground(onResume) to avoid the constant usage of the battery. When the app goes in onPause(), the location updates are stopped. It works fine on the emulator when I pass the updates through the terminal.
Problem:
When I physically change my location (say drive 5 miles away) and I open my app and the activity to show the nearest restaurants, it takes long time(4-5 minutes) for the location to refresh and till then the app continues to show the restaurants for the previous location. But if I physically change my location and access Google Maps and then open my restaurant application, then it works instantaneously. I have ensured that the GPS is switched on. What can I do to get the Location Manager to kick in and refresh the location as soon as I open my activity?
Thank you in advance!
package com.mortley.android.restaurantsaver;
public class NearbyRestaurantActivity extends ListActivity implements OnClickListener, LocationListener{
private Button refreshButton, searchRestaurants;
ImageButton goToSearch;
private double[] lastKnownLocation;
private EditText locationEditText;
private LocationManager locManager;
private LocationListener locListener;
private boolean gps_enabled = false;
private boolean network_enabled = false;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.nearbyrestaurants);
refreshButton = (Button)findViewById(R.id.reloadButton);
refreshButton.setOnClickListener(this);
searchRestaurants = (Button)findViewById(R.id.searchButton);
searchRestaurants.setOnClickListener(this);
goToSearch = (ImageButton)findViewById(R.id.goLocationButton);
goToSearch.setOnClickListener(this);
locationEditText = (EditText)findViewById(R.id.addressTextBox);
locationEditText.setVisibility(View.GONE);
goToSearch.setVisibility(View.GONE);
locManager = (LocationManager) this.getSystemService(Context.LOCATION_SERVICE);//??
locManager.requestLocationUpdates( LocationManager.GPS_PROVIDER, 1000, 100, this);
//checks network connectivity
boolean checkConnection = isNetworkAvailable();
if(!checkConnection){
Toast.makeText(getApplicationContext(), "Check your Network Connectivity", Toast.LENGTH_LONG).show();
}
if(checkConnection){
//sets current location parameters for the user
lastKnownLocation = RestaurantHelper.getLastKnownLocation(this);
//Log.v("NearbyRestaurantActivity", "This"+this);
RestaurantApplication application = (RestaurantApplication) this.getApplication();
RestaurantAdapter restaurantAdapter = new RestaurantAdapter(this, R.layout.restaurantrow, R.id.label,new ArrayList<RestaurantReference>());
restaurantAdapter.setLastKnownLocation(lastKnownLocation);
//set a global variable for the RestaurantAdapter in the RestaurantApplication class.
application.setRestaurantAdapter(restaurantAdapter);
//Set the adapter first and then update it when the RestaurantHttpAsyncTask makes a web service call.
setListAdapter(restaurantAdapter);
//Make a webservice call in a different thread passing Keyword for URL as a string array.
RestaurantHttpAsyncTask m_progressTask;
String[] keywords = {"", "american", "asian", "italian","mexican"};
//String[] keywords = {"indian"};
m_progressTask = new RestaurantHttpAsyncTask(NearbyRestaurantActivity.this, keywords);
m_progressTask.setRestaurantAdapter(restaurantAdapter);
m_progressTask.execute();
}
}
#Override
public void onClick(View v) {
//Refresh button helps to refresh the restaurant list on location change. Again it makes a call to the webservice using Async Task
if(v.getId() == refreshButton.getId() ){
try {
gps_enabled = locManager.isProviderEnabled(LocationManager.GPS_PROVIDER);
} catch (Exception ex) {
ex.printStackTrace();
}
try {
network_enabled = locManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER);
} catch (Exception ex) {
ex.printStackTrace();
}
// don't start listeners if no provider is enabled
if (!gps_enabled && !network_enabled) {
Toast.makeText(getApplicationContext(), "Sorry, Location is not determined. Please enable your Network Providers.", Toast.LENGTH_LONG).show();
}
//check network connectivity before refresh
boolean checkConnection = isNetworkAvailable();
if(!checkConnection){
Toast.makeText(getApplicationContext(), "Check your Network Connectivity", Toast.LENGTH_LONG).show();
}
if(checkConnection){
RestaurantApplication application = (RestaurantApplication) this.getApplication();
RestaurantAdapter restaurantAdapter = new RestaurantAdapter(this, R.layout.restaurantrow, R.id.label, new ArrayList<RestaurantReference>());
restaurantAdapter.setLastKnownLocation(lastKnownLocation);
//set a global variable for the RestaurantAdapter in the RestaurantApplication class.
application.setRestaurantAdapter(restaurantAdapter);
//Set the adapter first and then update it when the RestaurantHttpAsyncTask makes a web service call.
setListAdapter(restaurantAdapter);
//Make a webservice call in a different thread passing Keyword for URL as a string array.
RestaurantHttpAsyncTask m_progressTask, m_progressTask1;
String[] keywords = {"", "american", "asian", "italian","mexican", "chinese", "indian"};
//String[] keywords = {"Chinese"};
m_progressTask = new RestaurantHttpAsyncTask(NearbyRestaurantActivity.this, keywords);
m_progressTask.setRestaurantAdapter(restaurantAdapter);
m_progressTask.execute();
}
}
if(v.getId() == goToSearch.getId() ){
Activity child = this;
while(child.getParent() != null){
child = child.getParent();
}
TabGroup1Activity parent = (TabGroup1Activity)getParent();
InputMethodManager imm = (InputMethodManager)getSystemService(
Context.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(locationEditText.getWindowToken(), InputMethodManager.HIDE_NOT_ALWAYS);
//changes ** restaurantAdapter to RestaurantAdapter1 to test & application to application1
RestaurantApplication application1 = (RestaurantApplication) this.getApplication();
RestaurantAdapter restaurantAdapter1 = new RestaurantAdapter(this, R.layout.restaurantrow, R.id.label, new ArrayList<RestaurantReference>());
restaurantAdapter1.setLastKnownLocation(lastKnownLocation);
//set a global variable for the RestaurantAdapter in the RestaurantApplication class.
application1.setRestaurantAdapter(restaurantAdapter1);
//Set the adapter first and then update it when the RestaurantHttpAsyncTask makes a web service call.
setListAdapter(restaurantAdapter1);
//Make a webservice call in a different thread passing Keyword for URL as a string array.
RestaurantHttpAsyncTaskTextSearch m_progressTask, m_progressTask1;
String keywords = locationEditText.getText().toString();
if(keywords.equals("")){
keywords = "Pizza in Palo Alto";
}
keywords = keywords.replaceAll(" ", "%20");
keywords = keywords.replaceAll(",", "%20");
m_progressTask = new RestaurantHttpAsyncTaskTextSearch (NearbyRestaurantActivity.this, keywords);
m_progressTask.setRestaurantAdapter(restaurantAdapter1);
m_progressTask.execute();
locationEditText.setVisibility(View.GONE);
goToSearch.setVisibility(View.GONE);
}
if(v.getId() == searchRestaurants.getId() ){
if(goToSearch.isShown() == true){
goToSearch.setVisibility(View.GONE);
locationEditText.setVisibility(View.GONE);
}
else if(goToSearch.isShown() == false){
//check network connectivity before refresh
boolean checkConnection = isNetworkAvailable();
if(!checkConnection){
Toast.makeText(getApplicationContext(), "Check your Network Connectivity", Toast.LENGTH_LONG).show();
}
if(checkConnection){
goToSearch.setVisibility(View.VISIBLE);
locationEditText.setVisibility(View.VISIBLE);
}
}
}
}
//Method to check network connectivity
public boolean isNetworkAvailable() {
ConnectivityManager connectivityManager
= (ConnectivityManager)getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo activeNetworkInfo = connectivityManager.getActiveNetworkInfo();
if (activeNetworkInfo != null && activeNetworkInfo.isConnectedOrConnecting()) {
//Log.d("network", "Network available:true");
return true;
} else {
//Log.d("network", "Network available:false");
return false;
}
}
#Override
protected void onResume() {
super.onResume();
locManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 50, 100, this);
//Log.v("NearbyRestaurantActivity", "In OnResume()");
}
#Override
protected void onPause() {
super.onPause();
locManager.removeUpdates(this);
//Log.v("NearbyRestaurantActivity", "In onPause()");
}
#Override
public void onLocationChanged(Location location) {
// TODO Auto-generated method stub
}
#Override
public void onProviderDisabled(String provider) {
// TODO Auto-generated method stub
}
#Override
public void onProviderEnabled(String provider) {
// TODO Auto-generated method stub
}
#Override
public void onStatusChanged(String provider, int status, Bundle extras) {
// TODO Auto-generated method stub
}
}
public class RestaurantHelper {
public static double[] getLastKnownLocation(Activity activity){
double lat = 0.0;
double lon = 0.0;
LocationManager lm = (LocationManager) activity.getSystemService(Context.LOCATION_SERVICE);
Location location = lm.getLastKnownLocation(LocationManager.NETWORK_PROVIDER);
if(location == null){
lat = 0.0;
lon = 0.0;
}
else{
//Log.v("Latitude", Double.toString(location.getLatitude()));
//Log.v("Longitude", Double.toString(location.getLongitude()));
lat = location.getLatitude();
lon = location.getLongitude();
}
return new double[]{lat,lon};
}
}
The primary reason why you aren't getting updated location information quickly is that you're relying on the NETWORK_PROVIDER in the RestaurantHelper.getLastKnownLocation() method, but registering a LocationListener for the GPS_PROVIDER in onCreate().
So, this code in RestaurantHelper.getLastKnownLocation():
Location location = lm.getLastKnownLocation(LocationManager.NETWORK_PROVIDER);
...should be changed to:
Location location = lm.getLastKnownLocation(LocationManager.GPS_PROVIDER);
In theory, this should then give you the latest GPS location, which should have been refreshed when you register the listener. Conversely, you could also change to listening to the NETWORK_PROVIDER in onCreate() and leave RestaurantHelper.getLastKnownLocation() as is. It depends on your accuracy requirement - if you want high accuracy locations to return the nearest location to the nearest building level (e.g., 5-30m), you should use the GPS_PROVIDER. But, if you can live with coarser accuracy, the NETWORK_PROVIDER typically returns a new location much faster than GPS, especially if you're indoors, and sometimes this can be fairly accurate if derived from WiFi.
Another approach would be to listen to both GPS_PROVIDER and NETWORK_PROVIDER by registering both via two requestLocationUpdates() lines in onCreate(), and then checking to see most recent timestamp on the Location from lm.getLastKnownLocation(LocationManager.GPS_PROVIDER); and lm.getLastKnownLocation(LocationManager.NETWORK_PROVIDER);, and using the one that was updated more recently.
I would also recommend the following changes to make your code reliable on a large number of Android devices:
Specify the requestLocationUpdates() minDistance parameter as 0 when listening for GPS or NETWORK location updates - the minDistance parameter has a history of being unreliable and unpredictable in the way its interpreted by OEMs, until Android 4.1.
Switch to the new Fused Location Provider - this should be much more reliable when calling the getLastKnownLocation() method than the Android framework location APIs, and more consistent across different devices. Note that this relies on Google Play Services SDK, which is only available on Android 2.2 and higher.
I have 2 advice for you
LocationClient video, is the new way of doing location stuff. It has improvements over the LocationManager that can be a pain to manage and develop.
If you need to use LocationManager, you must know that requestLocationUpdates is buggy (very buggy). Since all its implementations on custom hardware differ. There is a hack/workaround that works. Before you call requestLocationUpdates, just kick start it with the following
Code :
HomeScreen.getLocationManager().requestLocationUpdates(
LocationManager.NETWORK_PROVIDER, 0, 0, new LocationListener() {
#Override
public void onStatusChanged(String provider, int status, Bundle extras) {
}
#Override
public void onProviderEnabled(String provider) {
}
#Override
public void onProviderDisabled(String provider) {
}
#Override
public void onLocationChanged(final Location location) {
}
});
requestLocationUpdates is buggy. Use network provider always to trigger onLocationChanged(...)
locManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 1000, 100, this)
only after using network provider use gps provider back to back:
locManager.requestLocationUpdates( LocationManager.GPS_PROVIDER, 1000, 100, this)
do not forget to check if gps is enabled or not before requesting location update by gps.
When my map activity is called I make a call in the onCreate to addUserMapPoint. This function contains two instances where I try to get the location information using myOverlay.getMyLocation. On the initial load of this activity the result of the first attempt returns a null GeoPoint and after the main UI thread completes the second attempt located in the listener thread of myOverlay.runOnFirstFix(new Runnable()… is call after a second and does contain a GeoPoint that does contain a lat and lon. The call inside this listener function does appear to put the dot on the map and the line mapController.animateTo(gp) does move the map to my location. My app has a refresh button that when clicked fires off this activity again. I need the lat and lon in order to fetch location data from another service. After the refresh, the second time through the map activity code I was expecting the first call to myOverlay.getMyLocation() would now be able to get the GeoPoint, but it is still null.
If I’m not able to get the GeoPoint by this first call to myOverlay.getMyLocation then how can I pass the lat and lon value from the second call found in the myOverlay.runOnFirstFix(new Runnable()… thread. You will notice that I have been trying to add the lat and lon to MyApp which is helper bean class but the lat and lon in this class is null even after the refresh. If I manually set a lat and lon manually in the addUserMapPoint function the first time the activity is accessed these values are retained. I’m guessing that this is because it is being set on the main UI thread.
public class MapActivity extends com.google.android.maps.MapActivity {
private MapView mapView = null;
private MapController mapController = null;
private MyLocationOverlay myOverlay = null;
public static MyApp app;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_map);
app = (MyApp) getApplicationContext();
mapView = (MapView) findViewById(R.id.mapview);
mapView.setBuiltInZoomControls(true);
mapController = mapView.getController();
List<Overlay> mapOverlays = mapView.getOverlays();
mapOverlays.clear();
addUserMapPoint(mapView);
if (!app.isLocServOff()) {
//map other points – service call to get items from our service near lat and lon
addOtherMapPoints(mapOverlays);
} else {
Toast.makeText(app.getApplicationContext(),"Current location could not be found.",Toast.LENGTH_LONG).show();
}
}
private void addUserMapPoint(MapView mapView){
myOverlay = new MyLocationOverlay(app.getApplicationContext(), mapView);
myOverlay.disableCompass();
myOverlay.enableMyLocation();
if(app.getMyLat()==null||app.getMyLon()==null){
GeoPoint gp = myOverlay.getMyLocation();
if(gp!=null){
app.setMyLat(Helper.latLon1E6Calc(gp.getLatitudeE6()));
app.setMyLon(Helper.latLon1E6Calc(gp.getLongitudeE6()));
app.setLocServOff(false);
}else{
app.setLocServOff(true);
}
}
myOverlay.runOnFirstFix(new Runnable() {
public void run() {
GeoPoint gp = myOverlay.getMyLocation();
if(gp!=null){
app.setMyLat(Helper.latLon1E6Calc(gp.getLatitudeE6()));
app.setMyLon(Helper.latLon1E6Calc(gp.getLongitudeE6()));
app.setLocServOff(false);
mapController.animateTo(gp);
}else{
app.setLocServOff(true);
}
}
});
mapView.getOverlays().add(myOverlay);
}
}
Your help is being requested for the following question.
How can I get a GeoPoint that contains a lat and lon in the main UI thread or how can I pass these values from GeoPoint I am able to get from the myOverlay.runOnFirstFix(new Runnable()… thread?
If you are going to suggest that I use Handler or runOnUiThread please provide code example that passes the lat and lon back to something that can be used by the main UI thread/map view. I have tried things like the following code that did not produce the desired outcome. I was able to get the toast message to show up, but was not able to get the lat and lon passed in a way I could use.
final Handler handler = new Handler();
myOverlay.runOnFirstFix(new Runnable() {
#Override public void run() {
handler.post(new Runnable() {
#Override public void run() {
GeoPoint gp = myOverlay.getMyLocation();
if(gp!=null){
app.setMyLat(Helper.latLon1E6Calc(gp.getLatitudeE6()));
app.setMyLon(Helper.latLon1E6Calc(gp.getLongitudeE6()));
app.setLocServOff(false);
mapController.animateTo(gp);
}else{
app.setLocServOff(true);
}
//Toast.makeText(getApplicationContext(),"wowoowowowoowoowowow",Toast.LENGTH_LONG).show();
}
});
}
});
I've also used code like the following to get the lat and lon and it works, but because the current location would sometimes be a different lat and lon than whas was being returned becuase for example I could not get a gps signal but yet an old value was returned. I added checks to see if the lat/lon data was older than 2 minutes, but I still could not match up the most recent lat and lon with that that is returned by myOverlay.getMyLocation.
LocationManager locMgr = (LocationManager)appcontext.getSystemService(Context.LOCATION_SERVICE);
MyLocationListener locLstnr = new MyLocationListener();
//fetch current location for current location
locMgr.requestSingleUpdate(LocationManager.GPS_PROVIDER, locLstnr, appcontext.getMainLooper());
Bellow you can find some examples on how to get the current location in the UI thread, but first of all, some background information.
GPS may take some time (15 seconds to 1 minute) to get the first fix after the request for new location is made. This is the reason you your first attempt to get it from myOverlay fails, and only after the first fix you can get the value.
During this blackout period you can use getLastKnownLocation() to get the last good known GPS location if you are in a hurry. If not availble it returns null
The code:
Last Known Location
LocationManager locMgr=(LocationManager)context.getSystemService(Context.LOCATION_SERVICE);
Location loc = locMgr.getLastKnownLocation(LocationManager.GPS_PROVIDER);
if(loc != null){
//we have a valid location. Check location date
}
Requesting a Single Location Update
LocationManager locMgr=(LocationManager)context.getSystemService(Context.LOCATION_SERVICE);
locMgr.requestSingleUpdate(LocationManager.GPS_PROVIDER, locationListener, appcontext.getMainLooper);
Requesting a Continuous Location Update
LocationManager locMgr = (LocationManager)context.getSystemService(Context.LOCATION_SERVICE);
//use 0 for minDistance and minDistance between updates if you need the maximum update frequency.
locMgr.requestLocationUpdates(LocationManager.GPS_PROVIDER, minDistance, minTime, locationListener);
Location Listener for Single and Continuous position update
This is the last piece of code, and is the place where you get the new fresh locations requested above.
When a new location that match your request critirea defined above is retrieved by GPS, this listener is immediately called, unless you device is busy doing something else that can't be interrupted (i.e. callback is on a paused thread or that hit a lock).
From within the onLocationChanged() you can set any class level filed as appropriate. If you registered the listener from the UI thread, then this will be running running on the UI.
LocationListener locationListener = new LocationListener() {
public void onLocationChanged(Location fix) {
fix.setTime(fix.getTime() + timeZoneOffset); //Add Timezone offset if needed
//here you have a fresh new location in fix...
//You can set the value of any class level field from here
}
public void onProviderDisabled(String provider) {
}
public void onProviderEnabled(String provider) {
}
public void onStatusChanged(String provider, int status, Bundle extras) {
}
};
Regards.
handler.post(new Runnable() {
#Override public void run() {
GeoPoint gp = myOverlay.getMyLocation();
if(gp!=null){
app.setMyLat(Helper.latLon1E6Calc(gp.getLatitudeE6()));
app.setMyLon(Helper.latLon1E6Calc(gp.getLongitudeE6()));
app.setLocServOff(false);
// HERE WE HAVE VALID gp VALUE AND WE NEED TO SHARE IT
mapController.animateTo(gp);
}else{
app.setLocServOff(true);
}
}
});
I think that your app.set/get|MyLat/Lon not working because you call them from different threads. To fix it synchronize set and get methods for MyLat/Long. (create Object for synchronization and sync on it)
Or if you like your way with handler this should work:
final Handler handler = new Handler(); // BE SURE TO RUN THIS LINE ON UI THREAD
...
myOverlay.runOnFirstFix(new Runnable() {
#Override public void run() {
// THIS PART WORKS AS BEFORE
final GeoPoint gp = myOverlay.getMyLocation();
mapController.animateTo(gp);
...
// AND THIS RUNNABLE TO UPDATE MyLat/MyLong FROM UI THREAD
handler.post(new Runnable() {
#Override public void run() {
app.setMyLat(Helper.latLon1E6Calc(gp.getLatitudeE6()));
app.setMyLon(Helper.latLon1E6Calc(gp.getLongitudeE6()));
});
}
});
Some of the most important points you must take into account while seeking Device's location are:
Satellite GPS fix is not guaranteed to be received in adequate amount of time. E.g. the device is inside a building / not under open sky.
Make sure the satellite GPS listeners are not kept active for long. Keeping the listener ON will imply keeping the GPS radio on all the time making it the biggest battery drain reason.
In the below code example, the poll method in LinkedBlockingQueue doesn't return until either a specified time interval is over or a Location is queued in.
Use something like the below to get the current Location:
Location getCurrentLocation() {
long startmillis = 0;
LinkedBlockingQueue<Location> mQueue = new LinkedBlockingQueue<Location>();
try{
long millisSinceLastCollection = System.currentTimeMillis() - startmillis;
startmillis = System.currentTimeMillis();
mQueue.clear();
// Register for Satellite GPS listener as well as Network GPS listener.
registerGPSListeners();
// Wait for a maximum of one minutes for a fix
Location firstfix = mQueue.poll(1, TimeUnit.MINUTES);
if(firstfix != null && firstfix.getProvider().equals(LocationManager.GPS_PROVIDER)) {
return firstfix;
}
long elapsedmillis = System.currentTimeMillis() - startmillis;
long remainingmillis = ONE_MINUTE_IN_MS - elapsedmillis;
if (remainingmillis <= 0){
return firstfix;
}
Location secondfix = mQueue.poll(remainingmillis, TimeUnit.MILLISECONDS);
if(secondfix != null && secondfix.getProvider().equals(LocationManager.GPS_PROVIDER)) {
return secondfix;
}
/*
* In case we receive fix only from Network provider, return it.
*/
if(firstfix != null && firstfix.getProvider().equals(LocationManager.NETWORK_PROVIDER)) {
return firstfix;
}
} catch(Exception e){
Logger.e("GPS: Exception while listening for the current location", e);
} finally {
Logger.i("GPS: Unsubscribing from any existing GPS listeners");
unregisterGPSListeners();
}
}
// GPS issue fix edit.
private void registerGPSListeners() {
LocationManager locationManager = (LocationManager)AirWatchApp.getAppContext().getSystemService(Context.LOCATION_SERVICE);
if(locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER))
locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 30000, 100, oneShotNetworkGPSLocationListener, MyAppApp.getAppContext().getMainLooper());
if(locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER)){
locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 30000, 100, oneShotSatelliteGPSLocationListener, AirWatchApp.getAppContext().getMainLooper());
}
}
private void unregisterGPSListeners(){
final LocationManager locationManager = (LocationManager)MyApp.getAppContext().getSystemService(Context.LOCATION_SERVICE);
locationManager.removeUpdates(oneShotSatelliteGPSLocationListener);
locationManager.removeUpdates(oneShotNetworkGPSLocationListener);
}
//One shot location listener
protected LocationListener oneShotSatelliteGPSLocationListener = new LocationListener() {
public void onLocationChanged(Location location) {
try {
mQueue.put(location);
} catch (InterruptedException e) {
Logger.e("Exception in putting new Location to the queue", e);
}
Logger.d("GPS: Location received from Satellite GPS Provider");
unregisterGPSListeners();
}
public void onProviderDisabled(String provider) {}
public void onStatusChanged(String provider, int status, Bundle extras) {}
public void onProviderEnabled(String provider) {}
};
//One shot location listener
protected LocationListener oneShotNetworkGPSLocationListener = new LocationListener() {
public void onLocationChanged(Location location) {
try {
mQueue.put(location);
} catch (InterruptedException e) {
Logger.e("Exception in putting new Location to the queue", e);
}
Logger.d("GPS: Location received from Network GPS Provider");
// Stop Listener for one-shot location fix from Network GPS provider.
final LocationManager locationManager = (LocationManager)AirWatchApp.getAppContext().getSystemService(Context.LOCATION_SERVICE);
locationManager.removeUpdates(oneShotNetworkGPSLocationListener);
Logger.d("GPS: Unsubscribed the network location listener.");
}
public void onProviderDisabled(String provider) {}
public void onStatusChanged(String provider, int status, Bundle extras) {}
public void onProviderEnabled(String provider) {}
};
Android modifies the user interface and handles input events from one single user interface thread(main thread).
If the programmer does not use any concurrency constructs, all code of an Android application runs in this thread.
GPS is the best way to determine a user's location, but pinging a global positioning satellite too much will quickly drain a mobile device's battery, take long time to get user location and this method doesn't always work indoors. You are not getting your location in first attempt that's why you are getting null over there.
Android's Network Location Provider figures out a user's location based on cell tower and Wi-Fi signals. It not only uses less battery power than GPS, but it's also faster and it works whether the user is outside or inside.
I am giving my Working Code below that show progress dialog, listen for user's location & after getting location show user's location overlay on Google-map
I assume that you have give below permissions in your Menifest file
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-permission android:name="android.permission.INTERNET"/>
My main class
public class MyLocationOnMap extends MapActivity {
private MapView mapView;
private MyLocationOverlay itemizedoverlay;
private LocationManager locationManager;
private String provider;
private MyLocationListener locationListener;
MyBroadCastreceiver myBroadCastreceiver;
/**
* My current Location <i>longitude</i>.
*/
static int longitude;
/**
* My current Location <i>latitude</i>.
*/
static int latitude;
/**
*My progress indicator.
*/
ProgressDialog loadingDialog;
public static final String INTENT_FILTER_TAG="my location broadcast receiver";
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.my_location_on_map);
loadingDialog = new ProgressDialog(this);
loadingDialog.setTitle("Hot Spots!");
loadingDialog.setMessage("Please wait ...");
loadingDialog.setIndeterminate(true);
loadingDialog.setCancelable(false);
loadingDialog.show();
// Configure the Map
mapView = (MapView) findViewById(R.id.mapview);
mapView.setBuiltInZoomControls(true);
mapView.setStreetView(true);
/**
* Get your location manager and Location Listener...
*/
locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
locationListener=new MyLocationListener();
myBroadCastreceiver = new MyBroadCastreceiver();
if (locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER)) {
Log.i("GPS_Enabled", "GPS enable! listening for gps location.");
locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 10000, 0, locationListener);
registerReceiver(myBroadCastreceiver, new IntentFilter(INTENT_FILTER_TAG));
} else if (locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER)) {
Log.i("Network_Enabled", "Network enable! listening for Network location.");
locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 10000, 0, locationListener);
registerReceiver(myBroadCastreceiver, new IntentFilter(INTENT_FILTER_TAG));
} else {
loadingDialog.dismiss();
Toast.makeText(this, "No Provider enable!", Toast.LENGTH_LONG).show();
}
}//End of onCreate......
/**
* My BroadCast Receiver, that is called when i get the location of user.
* #author Rupesh Yadav.
*
*/
class MyBroadCastreceiver extends BroadcastReceiver{
#Override
public void onReceive(Context context, Intent intent) {
//Remove location update when you get user`s location very first time.
locationManager.removeUpdates(locationListener);
//Remove the broadcast listener that update my location on map.
unregisterReceiver(myBroadCastreceiver);
GeoPoint point = new GeoPoint(latitude, longitude);
mapView.getController().animateTo(point);
List<Overlay> mapOverlays = mapView.getOverlays();
Drawable drawable = MyLocationOnMap.this.getResources().getDrawable(R.drawable.hs_mapoverlay);
itemizedoverlay = new MyLocationOverlay(drawable, MyLocationOnMap.this);
OverlayItem overlayitem = new OverlayItem(point, "Hello!", "My Current Location :)");
itemizedoverlay.addOverlay(overlayitem);
mapOverlays.add(itemizedoverlay);
loadingDialog.dismiss();
}
}
/**
* My Location listener...
*/
class MyLocationListener implements LocationListener{
#Override
public void onLocationChanged(Location location) {
latitude=(int) ((location.getLatitude())*1E6);
longitude=(int) ((location.getLongitude())*1E6);
//Send broadcast to update my location.
Intent sendLocationIntent=new Intent(INTENT_FILTER_TAG);
sendBroadcast(sendLocationIntent);
}
#Override
public void onProviderDisabled(String provider) {
// TODO Auto-generated method stub
}
#Override
public void onProviderEnabled(String provider) {
// TODO Auto-generated method stub
}
#Override
public void onStatusChanged(String provider, int status, Bundle extras) {
// TODO Auto-generated method stub
}
}
#Override
protected boolean isRouteDisplayed() {
// TODO Auto-generated method stub
return false;
}
}
MyLocationOverlay class
public class MyLocationOverlay extends ItemizedOverlay<OverlayItem> {
Context mContext;
private ArrayList<OverlayItem> hsOverlays = new ArrayList<OverlayItem>();
public MyLocationOverlay(Drawable defaultMarker) {
super(boundCenterBottom(defaultMarker));
// TODO Auto-generated constructor stub
}
public MyLocationOverlay(Drawable defaultMarker, Context context) {
super(boundCenterBottom(defaultMarker));
mContext = context;
}
#Override
protected OverlayItem createItem(int i) {
// TODO Auto-generated method stub
return hsOverlays.get(i);
}
#Override
public int size() {
// TODO Auto-generated method stub
return hsOverlays.size();
}
/**
* add new OverlayItem objects to map OverlayItem ArrayList.
*
* #param overlay
*/
public void addOverlay(OverlayItem overlay) {
hsOverlays.add(overlay);
populate();
}
/**
* Called when user clicks on map overlay.
*/
#Override
protected boolean onTap(int index) {
// TODO Auto-generated method stub
// return super.onTap(index);
OverlayItem item = hsOverlays.get(index);
AlertDialog.Builder dialog = new AlertDialog.Builder(mContext);
dialog.setTitle(item.getTitle());
dialog.setMessage(item.getSnippet());
dialog.show();
return true;
}
You can modify the Location Listener & Broadcasr Receiver according to your need.
I hope this will solve your problem.
Best regards!
I have used this class for detecting my lat & lon:
Hope this is useful for you too.
Example how to use:
GPSUtility.getInstance(Context).getLatitude();
GPSUtility.getInstance(CamPhotoModeAct.this).getLongitude()
public class GPSUtility {
public static final String TAG = "GPSUtility";
private Context ctx;
Timer timer1;
LocationManager lm;
LocationResult locationResult;
boolean gps_enabled=false;
boolean network_enabled=false;
private double latitude;
private double longitude;
private static SharedPreferences SHARED_PREF;
private static SharedPreferences.Editor EDITOR_SHARED_PREF;
private static GPSUtility this_instance;
public GPSUtility(Context ctx){
this.ctx = ctx;
SHARED_PREF = ctx.getSharedPreferences(ConstantsG.SHARED_PREF_FILE, Context.MODE_PRIVATE);
EDITOR_SHARED_PREF = SHARED_PREF.edit();
this.getLocation(innerLocationResult);
}
public static GPSUtility getInstance(Context ctx){
if(this_instance == null)
this_instance = new GPSUtility(ctx);
return this_instance;
}
public static void updateLocation(Context ctx){
GPSUtility.getInstance(ctx);//this writes the latitude and longitude in sharable preference file
}
public double getLatitude(){
String latitudeStr = SHARED_PREF.getString(ConstantsG.KEY_LATITUDE,null);
if(latitudeStr == null){
latitude = 0.0;
}
else{
latitude = Double.parseDouble(latitudeStr);
}
return latitude;
}
public double getLongitude(){
String longitudeStr = SHARED_PREF.getString(ConstantsG.KEY_LONGITUDE,null);
if(longitudeStr == null){
longitude = 0.0;
}
else{
longitude = Double.parseDouble(longitudeStr);
}
return longitude;
}
private void updateWithNewLocation(Location location) {
if (location != null) {
latitude = location.getLatitude();
EDITOR_SHARED_PREF.putString(ConstantsG.KEY_LATITUDE, String.valueOf(latitude) );
longitude = location.getLongitude();
EDITOR_SHARED_PREF.putString(ConstantsG.KEY_LONGITUDE, String.valueOf(longitude));
EDITOR_SHARED_PREF.commit();
}
}
public boolean getLocation(LocationResult result)
{
//I use LocationResult callback class to pass location value from GPSUtility to user code.
locationResult=result;
if(lm==null)
lm = (LocationManager) this.ctx.getSystemService(Context.LOCATION_SERVICE);
//exceptions will be thrown if provider is not permitted.
try {
gps_enabled = lm.isProviderEnabled(LocationManager.GPS_PROVIDER);
} catch (Exception ex) {
Log.e(TAG, "Exception error: " + ex.getLocalizedMessage(), ex);
}
try {
network_enabled = lm
.isProviderEnabled(LocationManager.NETWORK_PROVIDER);
} catch (Exception ex) {
Log.e(TAG, "Exception error: " + ex.getLocalizedMessage(), ex);
}
//Toast.makeText(context, gps_enabled+" "+network_enabled, Toast.LENGTH_LONG).show();
//don't start listeners if no provider is enabled
if(!gps_enabled && !network_enabled){
Toast.makeText(this.ctx, "You should enable gps or be connected to network.", Toast.LENGTH_LONG).show();
return false;
}
if(gps_enabled)
lm.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, locationListenerGps);
if(network_enabled)
lm.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 0, 0, locationListenerNetwork);
timer1=new Timer();
timer1.schedule(new GetLastLocation(), 10000);
return true;
}
LocationListener locationListenerGps = new LocationListener() {
public void onLocationChanged(Location location) {
timer1.cancel();
locationResult.gotLocation(location);
lm.removeUpdates(this);
lm.removeUpdates(locationListenerNetwork);
}
public void onProviderDisabled(String provider) {}
public void onProviderEnabled(String provider) {}
public void onStatusChanged(String provider, int status, Bundle extras) {}
};
LocationListener locationListenerNetwork = new LocationListener() {
public void onLocationChanged(Location location) {
timer1.cancel();
locationResult.gotLocation(location);
lm.removeUpdates(this);
lm.removeUpdates(locationListenerGps);
}
public void onProviderDisabled(String provider) {}
public void onProviderEnabled(String provider) {}
public void onStatusChanged(String provider, int status, Bundle extras) {}
};
class GetLastLocation extends TimerTask {
#Override
public void run() {
//Context context = getClass().getgetApplicationContext();
Location net_loc=null, gps_loc=null;
if(gps_enabled)
gps_loc=lm.getLastKnownLocation(LocationManager.GPS_PROVIDER);
if(network_enabled)
net_loc=lm.getLastKnownLocation(LocationManager.NETWORK_PROVIDER);
//if there are both values use the latest one
if(gps_loc!=null && net_loc!=null){
if(gps_loc.getTime()>net_loc.getTime())
locationResult.gotLocation(gps_loc);
else
locationResult.gotLocation(net_loc);
return;
}
if(gps_loc!=null){
locationResult.gotLocation(gps_loc);
return;
}
if(net_loc!=null){
locationResult.gotLocation(net_loc);
return;
}
locationResult.gotLocation(null);
}
}
public static abstract class LocationResult{
public abstract void gotLocation(Location location);
}
LocationResult innerLocationResult = new LocationResult() {
#Override
public void gotLocation(Location location) {
updateWithNewLocation(location);
}
};
}
I have some Android code that needs to get the best available location QUICKLY, from GPS, network or whatever is available. Accuracy is less important than speed.
Getting the best available location is surely a really standard task. Yet I can't find any code to demonstrate it. The Android location code expects you to specify criteria, register for updates, and wait - which is fine if you have detailed criteria and don't mind waiting around.
But my app needs to work a bit more like the Maps app does when it first locates you - work from any available provider, and just check the location isn't wildly out of date or null.
I've attempted to roll my own code to do this, but am having problems. (It's inside an IntentService where an upload happens, if that makes any difference. I've included all the code for info.) What's wrong with this code?
#Override
protected void onHandleIntent(Intent arg0) {
testProviders();
doUpload();
}
private boolean doUpload() {
int j = 0;
// check if we have accurate location data yet - wait up to 30 seconds
while (j < 30) {
if ((latString == "") || (lonString == "")) {
Log.d(LOG_TAG, "latlng null");
Thread.sleep(1000);
j++;
} else {
Log.d(LOG_TAG, "found lat " + latString + " and lon " + lonString);
break;
}
//do the upload here anyway, with or without location data
//[code removed for brevity]
}
public boolean testProviders() {
Log.e(LOG_TAG, "testProviders");
String location_context = Context.LOCATION_SERVICE;
locationmanager = (LocationManager) getSystemService(location_context);
List<String> providers = locationmanager.getProviders(true);
for (String provider : providers) {
Log.e(LOG_TAG, "registering provider " + provider);
listener = new LocationListener() {
public void onLocationChanged(Location location) {
// keep checking the location - until we have
// what we need
//if (!checkLoc(location)) {
Log.e(LOG_TAG, "onLocationChanged");
locationDetermined = checkLoc(location);
//}
}
public void onProviderDisabled(String provider) {
}
public void onProviderEnabled(String provider) {
}
public void onStatusChanged(String provider, int status,
Bundle extras) {
}
};
locationmanager.requestLocationUpdates(provider, 0,
0, listener);
}
Log.e(LOG_TAG, "getting updates");
return true;
}
private boolean checkLoc(Location location) {
float tempAccuracy = location.getAccuracy();
int locAccuracy = (int) tempAccuracy;
Log.d(LOG_TAG, "locAccuracy = " + locAccuracy);
if ((locAccuracy != 0) && (locAccuracy < LOCATION_ACCURACY)) {
latitude = location.getLatitude();
longitude = location.getLongitude();
latString = latitude.toString();
lonString = longitude.toString();
return true;
}
return false;
}
public void removeListeners() {
// Log.e(LOG_TAG, "removeListeners");
if ((locationmanager != null) && (listener != null)) {
locationmanager.removeUpdates(listener);
}
locationmanager = null;
// Log.d(LOG_TAG, "Removed " + listener.toString());
}
#Override
public void onDestroy() {
super.onDestroy();
removeListeners();
}
Unfortunately, this finds the network provider, but only ever outputs latlng null 30 times - it never seems to get a location at all. I never even get a log statement of locationChanged.
It's funny, because from ddms I can see output like:
NetworkLocationProvider: onCellLocationChanged [305,8580]
NetworkLocationProvider: getNetworkLocation(): returning cache location with accuracy 75.0
seeming to suggest that the network provider does have some location info after all, I'm just not getting at it.
Can anyone help? I think working example code would be a useful resource for the Android/StackOverflow community.
You are definitely trying to do this the hard way. Here are some snippets from a new app I am working on. It uses Criteria to get all providers capable of returning a fine level of accuracy without a cost.
If no providers are enabled a dialog is displayed that prompts the user to turn on their location settings. If the user hits ok an Intent is actually fired that sends them to the settings on their phone. If there are providers enabled the app takes the most recent last known location from any of the enabled providers. For my app I just need to know what general area the user is in and it's likely that the last known location is from their home area.
If providers are enabled the loop also requests location updates as quickly as possible. This is ideal for my app but you can change this to conserve battery my modifying the arguments to the requestLocationUpdates method.
The optimization that this code has that the examples on the Android app don't really show is that all of the enabled providers are started simultaneously. All of the providers will return separate updates on to the onLocationChanged method. In my app I remove the location listener after one of the providers returns a location with a good enough accuracy.
Start Location Updates:
void getCurrentLocation() {
List<String> providers = locationManager.getProviders(criteria, true);
if (providers != null) {
Location newestLocation = null;
for (String provider : providers) {
Location location = locationManager.getLastKnownLocation(provider);
if (location != null) {
if (newestLocation == null) {
newestLocation = location;
} else {
if (location.getTime() > newestLocation.getTime()) {
newestLocation = location;
}
}
locationManager.requestLocationUpdates(provider, 0, 0, this);
}
}
} else {
LocationDialogFragment dialog = new LocationDialogFragment();
dialog.show(getSupportFragmentManager(),
LocationDialogFragment.class.getName());
}
}
Receive Location Update:
#Override
public void onLocationChanged(Location location) {
float bestAccuracy = -1f;
if (location.getAccuracy() != 0.0f
&& (location.getAccuracy() < bestAccuracy) || bestAccuracy == -1f) {
if (location.getAccuracy() < Const.MIN_ACCURACY) {
locationManager.removeUpdates(this);
}
}
bestAccuracy = location.getAccuracy();
}
Location Settings Dialog:
public class LocationDialogFragment extends DialogFragment {
#Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
builder.setMessage(R.string.location_dialog_message)
.setPositiveButton(R.string.location_dialog_positive_button,
new OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
Intent settingsIntent = new Intent(
Settings.ACTION_LOCATION_SOURCE_SETTINGS);
startActivity(settingsIntent);
}
})
.setNegativeButton(R.string.location_dialog_negative_button,
new OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
Toast.makeText(getActivity(),
R.string.no_location_message, Toast.LENGTH_LONG)
.show();
}
});
return builder.create();
}
}
Thread.sleep() in production code is a serious code smell IMHO. If you find you're having to do that, you're probably doing something that's not supposed to work that way. In this case, I think it's the source of your problem -- you're not letting Android go back to process this thread's message queue to dispatch any location updates it finds. I suspect an IntentService is just not going to work for your scenario.