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.
Related
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.
All I wanted to do is to get the current location using GPS before I move to next activity. So I disabled the buttons that takes me to next activities. The buttons are enabled only after getting a location using the GPS. The following code runs perfectly. But I found one bug..
Last time I used the app it was quite right. Then I moved to my University. There I launched the app and then moved to next activity as location was retrieved by then. I got the location of my home which is about 10 kilometers away. Then after closing the application, I launched it once again and this time it retrieved the correct location.
Well, this is happening because of the line below:
location = locationManagerAsync.getLastKnownLocation(providerAsync);
My app is retrieving the last location and enabling the buttons. But it is able to keep the current location to use for the next time I launch the app. As a result, on the second attempt, the location is retrieved perfectly.
I used a code from a question on stackoverflow.com which is given below.
private class MyLocationTracker extends AsyncTask<Void, Void, Void> implements LocationListener {
private Context ContextAsync;
public MyLocationTracker (Context context){
this.ContextAsync = context;
}
Dialog progress;
private String providerAsync;
private LocationManager locationManagerAsync;
double latAsync=0.0;
double lonAsync=0.0;
Location location;
#Override
protected void onPreExecute() {
super.onPreExecute();
progress = ProgressDialog.show(ContextAsync, "Loading location", "Please wait...");
}
#Override
protected Void doInBackground(Void... arg0) {
// TODO Auto-generated method stub
locationManagerAsync = (LocationManager) ContextAsync.getSystemService(ContextAsync.LOCATION_SERVICE);
Criteria criteria = new Criteria();
criteria.setAccuracy(Criteria.ACCURACY_COARSE);
criteria.setCostAllowed(true);
criteria.setPowerRequirement(Criteria.NO_REQUIREMENT);
providerAsync = locationManagerAsync.getBestProvider(criteria, false);
if (locationManagerAsync.isProviderEnabled(LocationManager.GPS_PROVIDER)) {
providerAsync = LocationManager.GPS_PROVIDER;
} else if (locationManagerAsync.isProviderEnabled(LocationManager.NETWORK_PROVIDER)) {
providerAsync = LocationManager.NETWORK_PROVIDER;
} else if (locationManagerAsync.isProviderEnabled(LocationManager.PASSIVE_PROVIDER)) {
providerAsync = LocationManager.PASSIVE_PROVIDER;
//Toast.makeText(ContextAsync, "Switch On Data Connection!!!!", Toast.LENGTH_LONG).show();
}
location = locationManagerAsync.getLastKnownLocation(providerAsync);
// Initialize the location fields
if (location != null) {
// System.out.println("Provider " + provider + " has been selected.");
latAsync = location.getLatitude();
lonAsync = location.getLongitude();
} else {
//Toast.makeText(ContextAsync, " Location not available", Toast.LENGTH_SHORT).show();
}
return null;
}
#Override
protected void onPostExecute(Void result) {
super.onPostExecute(result);
progress.dismiss();
onLocationChanged(location);
Log.v("latAsync_lonAsync",latAsync+"_"+lonAsync);
myCurrentLatitude = latAsync;
myCurrentLongitude = lonAsync;
// the buttons that were disabled in the onCreate() method
police.setEnabled(true);
medical.setEnabled(true);
fireService.setEnabled(true);
cityCorporation.setEnabled(true);
telePhnCall.setEnabled(true);
getRoute.setEnabled(true);
}
#Override
public void onLocationChanged(Location location) {
// TODO Auto-generated method stub
locationManagerAsync.requestLocationUpdates(providerAsync, 0, 0, this);
}
#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
}
}
And this is the line that I am using to call the AsyncTask from the Oncreate() method..
new MyLocationTracker(TypeOfEmergency.this).execute();
Now the problem is, I do not want the last location to be retrieved and stored in the variables. I tried the code after removing the line
location = locationManagerAsync.getLastKnownLocation(providerAsync);
But the app crashes in that case. Need a quick fix.
Here is the error log :: (Please have a look to the comments before having a look to the error log)
With the code something is basically wrong: the single line of code in onLocationChanged
locationManagerAsync.requestLocationUpdates(providerAsync, 0, 0, this);
needs to be called at the end of doInBackground (which does not need to be done in background). This line of code requests location updates from the provider (e.g. the GPS provider). When a new location is avaiable the onLocationChanged of your location listener is called (which is MyLocationTracker in your example due to the this in the line above. in onLocationChanged you need to react to a new location by enabling or disabling the functionality, you mentioned in your question.
The call to getLastKnownLocation does exectly what the name suggests: It returns the last known position, which may be quite old.
i would appreciate your help , if anyone can help me.
i need to have an accurate location tracker in my app and i want it to act like this.
it finds the first location of the person with network , meanwhile i start requesting GPS localisation. when gps gives me a location , i want to not listen anymore to network locations. After that i want to request a location from network only and only if Gps is not fixed(cant give me a location). When Gps is fixed again i want to stop listening to network,and this is the loop i want to implement since GPS is more acurate and i want to have network localisation as a backup.
I Have seen this post , but i cant seem to understand how to adapt it to my needs,
How can I check the current status of the GPS receiver?
Ideas are needed, thank you in advance Stackoverflow Comunity.
1. Stop Listening for Network Location Updates:
It is very simple to do so. Simply call
your_loc_manager.removeUpdates(network_location_listener);
2. Getting Location from Network Provider if GPS Provider is not giving any fix
You can try something like given below (instead of the timer you can try using GpsStatus Listener...)
inside the method for getting Network Provider updates
boolean network_enabled ;
try {
network_enabled = locManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER);
} catch(Exception ex) {
ex.printStackTrace();
}
if(!network_enabled)
return;
locManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 0, 100, networkLocationListener);
return;
Inside the method for getting GPS Locations
boolean gps_enabled;
try {
gps_enabled = locManager.isProviderEnabled(LocationManager.GPS_PROVIDER);
} catch(Exception ex) {
ex.printStackTrace();
}
if(!gps_enabled) {
// call your method for getting network location updates
return;
}
locManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 50, locationListenerGps);
new Timer().schedule(new TimerTask() {
#Override
public void run() {
// You should call your method of getting Network locations based on some
// condition which would tell whether you really need to ask for network location
}
}, 20000 /* Time Value after which you want to stop trying for GPS and switch to Network*/);
EDIT 1:
Also "I don't need to know when to get updates from GPS , i need to know when to get updates from network (i mean i need to know when gps is searching , not when it is fixed). and how to control the interchangeability between network and GPS"
...If the fix has been lost then only you need to look for network updates so I had suggested to call
if (hasGPSFix) {
Log.i("GPS","Fix Lost (expired)");
lm.requestLocationUpdates(LocationManager.GPS_PROVIDER, 2000, 10, locationListener);
lm.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 2000, 10, locationListener); //===> new line which I am suggesting
}
hasGPSFix = false;
This way, you will get the location update from Network while GPS will keep on trying for a fix. Once GPS fix is achieved hasGPSFix will change its value and the existing code in the said link will stop looking for network updates. At least this is what I could understand that you want to achieve. May be I have not understood you completely.
EDIT 2:
Here is my class for handling both GPS and NETWORK providers
The mapActivity or wherever you need to get location updates, you need to create an instance of this MyLocation class. And also, to keep on getting the location updates you need to implement LocationResult in your calling activity. public abstract void gotLocation(Location location, boolean isGpsData); will tell you whether the update you have received just now is from network or GPS. Also, in case of data being returned from GPS, it automatically closes the netowrk listener. You will need to call getFineLocation() method if you want data from GPS and so on. You will need to enhance the given code for your purpose (like you need to enhance closeListeners() method to close only one provider when you just want to remove only the network updates, etc...), but it gives a basic implementation of what you want (along with the GpsStatus.Listener implementation, of course, so both combined should serve your purpose well).
public class MyLocation {
LocationManager locManager;
LocationResult locationResult;
boolean gps_enabled = false;
boolean network_enabled = false;
public boolean getCoarseLocation(Context context, LocationResult result) {
locationResult = result;
if(locManager == null)
locManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
try {
network_enabled = locManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER);
} catch(Exception ex) {
ex.printStackTrace();
}
if(!network_enabled)
return false;
locManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 0, 100, networkLocationListener);
return true;
}
public boolean getFineLocation(Context context, LocationResult result) {
locationResult = result;
if(locManager == null)
locManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
try {
gps_enabled = locManager.isProviderEnabled(LocationManager.GPS_PROVIDER);
} catch(Exception ex) {
}
if(!gps_enabled)
return false;//No way to get location data
locManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 100, locationListenerGps);
return true;
}
LocationListener locationListenerGps = new LocationListener() {
public void onLocationChanged(Location location) {
locationResult.gotLocation(location,true);
locManager.removeUpdates(networkLocationListener);
}
public void onProviderDisabled(String provider) {}
public void onProviderEnabled(String provider) {}
public void onStatusChanged(String provider, int status, Bundle extras) {}
};
LocationListener networkLocationListener = new LocationListener() {
public void onLocationChanged(Location location) {
locationResult.gotLocation(location,false);
}
public void onProviderDisabled(String provider) {}
public void onProviderEnabled(String provider) {}
public void onStatusChanged(String provider, int status, Bundle extras) {}
};
public void closeListeners() {
if(locManager == null)
return;
if(locationListenerGps != null)
locManager.removeUpdates(locationListenerGps);
if(networkLocationListener != null)
locManager.removeUpdates(networkLocationListener);
}
public static abstract class LocationResult {
public abstract void gotLocation(Location location, boolean isGpsData);
}
public static boolean hasNetworkProvider(Context mContext) {
List<String> myProvidersList = ((LocationManager) mContext.getSystemService(Context.LOCATION_SERVICE)).getProviders(false);
return myProvidersList.contains("network");
}
public static boolean hasGPSProvider(Context mContext) {
List<String> myProvidersList = ((LocationManager) mContext.getSystemService(Context.LOCATION_SERVICE)).getProviders(false);
return myProvidersList.contains("gps");
}
}
In my current application, the location data that is returned is outdated. I use a method that gets updates based on min time/distance between updates. However, lets say i turn my phone off, drive to another city, and then turn the phone back on. In this case my GPS reading will be off. How do i force the app to get the current location NOT getLastKnownLocation().
I have heard of a locationListener but everything i have read is very vague on how to use it.
Here is the code I have just in case this clarifies whats going on:
public class GPSHandling extends Service implements LocationListener{
private final Context myContext;
//flag for gps status
public boolean isGPSEnabled = false;
//flag for network status
public boolean isNetworkEnabled = false;
// used to determine if i can get a location either by network or GPS
public boolean canGetLocation = false;
Location myloc;
public double latitude;
public double longitude;
public int MIN_TIME_BTWN_UPDATE = 500*10; // in miliseconds, so 10sec
public int MIN_DISTANCE_BTWN_UPDATE = 10; // in meters
protected LocationManager locManager;
public GPSHandling(Context context){
this.myContext= context;
getLocation();
}
public Location getLocation(){
try{
locManager =(LocationManager) myContext.getSystemService(LOCATION_SERVICE);
// now get gps status
isGPSEnabled = locManager.isProviderEnabled(LocationManager.GPS_PROVIDER);
//now the same for network
isNetworkEnabled = locManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER);
if (!isGPSEnabled && !isNetworkEnabled){
// do nothing since neither provider are enabled (this.cangetlocation = false but its already set to that by default)
}
else{
this.canGetLocation=true;
//first get values for locManager from the network provider. send parameters telling when to update
if(isNetworkEnabled){
locManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, MIN_TIME_BTWN_UPDATE, MIN_DISTANCE_BTWN_UPDATE, this);
Log.d("provider", "network");
//if we are successful, then check to see if the location manager isnt null. attempt to get the current location from the manager
if (locManager != null){
myloc =locManager.getLastKnownLocation(LocationManager.NETWORK_PROVIDER);
// after getting the current location, attempt to get the latitude and longitude values
if (myloc != null){
latitude = myloc.getLatitude();
longitude = myloc.getLongitude();
}
}
}
//now get values for locManager from the GPS provider. send parameters telling when to update
if(isGPSEnabled){
locManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, MIN_TIME_BTWN_UPDATE, MIN_DISTANCE_BTWN_UPDATE, this);
Log.d("provider", "GPS");
}
//if we are successful, then check to see if the location manager isnt null. attempt to get the current location from the manager
if(locManager!= null){
myloc = locManager.getLastKnownLocation(LocationManager.GPS_PROVIDER);
}
// after getting the current location, attempt to get the latitude and longitude values
if(myloc != null){
latitude = myloc.getLatitude();
longitude = myloc.getLongitude();
}
}
} catch (Exception e) {
e.printStackTrace();
}
return myloc;
}
// get an update of the current latitude by using this class method
public double getMyLatitude(){
if (myloc!= null){
latitude = myloc.getLatitude();
}
return latitude;
}
//get an update of the current longitude by using this class method
public double getMyLongitude(){
if (myloc != null ){
longitude = myloc.getLongitude();
}
return longitude;
}
// use this method to find if app can get the current location
public boolean canGetMyLocation(){
return this.canGetLocation;
}
public void showGPSDialog(){
AlertDialog.Builder alert = new AlertDialog.Builder(myContext);
// Setting Dialog Title
alert.setTitle("Location Setting");
// Setting Dialog Message
alert.setMessage("GPS is not enabled, do you want to enable this now in the settins menue?");
// setting icon to dialog
//alert.setIcon(R.drawable.)
// on pressing settings button
alert.setPositiveButton("Settins", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
Intent intent = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS);
myContext.startActivity(intent);
}
});
//on pressing cancel button
alert.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
dialog.cancel();
}
});
// showing the alert dialog
alert.show();
}
public void stopUsingGPS(){
if(locManager !=null){
locManager.removeUpdates(GPSHandling.this);
}
}
#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
}
#Override
public IBinder onBind(Intent intent) {
// TODO Auto-generated method stub
return null;
}
}
You are already half using LocationListener in your code. You just didn't fully implement the code.
This method bellow is empty in your code:
#Override
public void onLocationChanged(Location location) {
// TODO Auto-generated method stub
}
This is the method that is called everytime GPS produces an location update.
You should complete it doing something like:
#Override
public void onLocationChanged(Location location) {
latitude = location.getLatitude();
longitude = location.getLongitude();
}
Regards.
Did you read the corresponding docs?
Look at Location API and LocationListener for the start.
LocationListener is actually pretty straight-forward to use and the only (clean) way to reliably retrieve the device's location.
Location data usually take up to 30 seconds to fully warm up. As you asked for the getLastKnownLocation() you, indeed, could have a outdated location returned.
Read more about it at Location strategies by Google itself.
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.