...or most probably, I am doing it wrong. What I want is to display a Toast every one meter I walk inside home. The code below gives me wrong results, as the moment I install the app on my phone I get a Toast without even moving!
public class MainActivity extends Activity {
private LocationListener mLocationListener;
private String mLocationProvider;
private LocationManager mLocationManager;
private Location mCurrentLocation;
private int mCounter = 0;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mLocationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
mLocationListener = new MyLocationListener();
Criteria criterion = new Criteria();
criterion.setAccuracy(Criteria.ACCURACY_FINE);
criterion.setCostAllowed(true);
criterion.setPowerRequirement(Criteria.POWER_HIGH);
mLocationProvider = mLocationManager.getBestProvider(criterion, true);
}
#Override
protected void onResume() {
super.onResume();
mCurrentLocation = mLocationManager.getLastKnownLocation(mLocationProvider);
mLocationManager.requestLocationUpdates(mLocationProvider, 5000, 1, mLocationListener);
}
#Override
protected void onPause() {
super.onPause();
mLocationManager.removeUpdates(mLocationListener);
}
private class MyLocationListener implements LocationListener {
#Override
public void onLocationChanged(Location newlocation) {
float distance = mCurrentLocation.distanceTo(newlocation);
if (distance >= 1) {
mCounter++;
Toast.makeText(MainActivity.this, String.format("Message #%d: you walked one more meter", mCounter), Toast.LENGTH_SHORT).show();
mCurrentLocation = newlocation;
}
}
}
}
A GPS signal is not precise enough to give exact locations for a 1m radius. There can be deviation peeks up to 50 - 100m in real situations using GPS. This depends much on the environment you are at. GPS will be reflected by buildings, water etc. An average deviation is 10 - 20m. This will get even worse if your inside of a building using a GPS provider instead of a Network provider.
Furthermore you will never get the same coordinates twice in a row, because of this. Even if you don't move! To avoid that you could temporarly save the location and compare it with the new location. If the distance between them hits a defined boarder use the new location.
Change your location provider to GPS. And you have instantiated the LocationListener before you request the new Location(in onResume(); onResume() will be called after onCreate()). This might be the reason for your app showing Toast on start up.. Try to instantiate LocationListener after the requestLocationUpdates()..
Related
I have an AlarmManager that triggers every 30 minutes to an IntentService, and this intent service is to get the user's location each time. I have 2 ways of getting location: first it checks the getLastKnownLocation(), and if it was within the last 2 minutes it uses that, this part works perfectly.
The second way is if the last location was old or returns null, in which I want to get the new location once. For some reason this never calls onLocationChanged(). This results in my IntentService not returning coordinates much of the time, it only returns them if the getLastKnownLocation() was recent.
Here is my code for how it is set up, why is it that if I want to get a new location, it is never called? Check the comments in the code to see what is called and what is never called.
LocationListener locationListener;
LocationManager locationManager;
private final double MIN_COORD_DIFF = 0.0006;
public CoordinateAlarmReceiver(){
super("CoordinateAlarmReceiver");
}
#Override
protected void onHandleIntent(Intent intent) {
//THIS IS CALLED CORRECTLY
MyLog.i("coordinate alarm received");
locationManager = (LocationManager)getSystemService(Context.LOCATION_SERVICE);
Location lastLocation = locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER);
//if last location was in past 2 minutes, use that
if(lastLocation != null && lastLocation.getTime() > Calendar.getInstance().getTimeInMillis() - 2 * 60 * 1000) {
//THIS IS CALLED CORRECTLY
storeLocation(lastLocation);
MyLog.i("Last location was recent, using that");
}
else { //otherwise get new location
//THIS IS CALLED CORRECTLY
MyLog.i("Last location was old, getting new location");
locationListener = new LocationListener() {
#Override
public void onLocationChanged(Location location) {
//THIS IS NEVER CALLED
MyLog.i("Got new coordinates");
storeLocation(location);
locationManager.removeUpdates(this);
}
#Override
public void onStatusChanged(String s, int i, Bundle bundle) {
}
#Override
public void onProviderEnabled(String s) {
}
#Override
public void onProviderDisabled(String s) {
}
};
locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, locationListener);
}
}
You should use a Service instead of a IntentService. The IntentService finish when complete the task however, the service is running and can listen the location change events. Try to use a service.
Hope it helps you!!
I have a background service which should track the user movement with his car, and send the data to my server. I have two variables for sending the location, either 60 seconds have passed or the user has moved 100 meters.
On my service here is how I start listening to locations:
mLocationRequest = LocationRequest.create();
mLocationRequest.setSmallestDisplacement(100);
mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
mLocationRequest.setInterval(60 * 1000);
mLocationClient = new LocationClient(context, connectionCallbacks, onConnectionFailedListener);
mLocationClient.connect();
and the listeners:
ConnectionCallbacks connectionCallbacks = new ConnectionCallbacks() {
#Override
public void onConnected(Bundle arg0) {
mLocationClient.requestLocationUpdates(mLocationRequest, locationListenerAPI);
//process the last location, if available
Location loc = mLocationClient.getLastLocation();
if (loc != null) {
prepareLocation(loc);
}
}
#Override
public void onDisconnected() {
}
};
LocationListener locationListenerAPI= new LocationListener() {
#Override
public void onLocationChanged(Location location) {
//A location has been read, process it
prepareLocation(location);
}
};
So, while the service is running and I driver around with the car, onLocationChanged only fires a few times, it does not take into consideration either the time or meters to fire a location read. I have data connection and the GPS icon is visible on notification bar the whole time the service is running.
Any ideas why is not working ?
In the end, I just did it like this:
I've setInterval(5000) and setFastestInterval(5000) and then, for each read location, if distance between them is bigger than 100m, I send the location to my server. I gave up on using smallestDisplacement. This seem to work reliably.
I'm developing an application that needs to get location updates fast, doesn't matter the accuracy of them. I need to be able to get about one reading per second. How can I do this?
Other than specifying 0 for both the minimum distance and minimum time values in requestLocationUpdates(), you have no control over the rate. Android will give you all of the fixes it receives, but whether that is 30 fixes per second or 30 seconds per fix will depend upon hardware, environment (e.g., is the user indoors?), and so forth.
You could build a layer between the Android location updates and your receiver.
In your own layer, catch the Android location updates, and pass the same location 30 times per second to your receiver, until you get a new location.
EDIT
Something like this (not tested):
public class MyLocationManager implements LocationListener{
private List<MyLocationListener> listeners;
private Location lastLocation;
private Handler handler;
public MyLocationManager(){
LocationManager locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
lastLocation = locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER);
locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, this);
listeners = new ArrayList<MyLocationListener>();
handler = new Handler();
sendLocationUpdates();
}
private void sendDelayedLocationUpdates(){
handler.postDelayed(locationUpdater, 200);
}
public void addMyLocationListener(MyLocationListener mListener){
listeners.add(mListener);
}
public void removeMyLocationListener(MyLocationListener mListener){
listeners.remove(mListener);
}
#Override
public void onLocationChanged(Location location) {
lastLocation = location;
}
public interface MyLocationListener{
public void onLocationChanged(Location location);
}
private Runnable locationUpdater = new Runnable(){
#Override
public void run(){
for(MyLocationListener mListener : listeners){
mListener.onLocationChanged(lastLocation);
}
sendDelayedLocationUpdates();
}
};
}
I have been working for 2 days looking for this bug. I have searched stackoverflow and Android documentation with no luck.
My onLocationChanged code has a counter in it that counts how many times it has been called and if I back arrow out of the activity screen on the phone and return, the counter will go up by 2 for each update. If I back arrowing out and update the GPS, the counter records that onLocationChanged is still getting called even though the screen is in the background and onPause has been called. If I go in and out of the activity with the backarrow, I can get more than two updates per GPS input send depending on how many times the activity screen is entered.
All the GPS code works but these multiple instances can't be good and they mess up other things I am trying to do, like distance traveled between two updates.
Here is what I think is the relevant parts of my code. Obviously I left out the main part but the point is that after returning to this screen after back-arrowing out then a single send of a GPS data point increments the n variable by more than one depending on how many times I have returned to the screen.
I must be doing something obvious but I can't find it.
public class Data extends Activity {
protected LocationListener ll;
protected LocationManager lm;
static int n = 0;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.data);
LocationManager lm = (LocationManager)getSystemService(Context.LOCATION_SERVICE);
LocationListener ll = new mylocationlistener();
lm.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, ll);
}
class mylocationlistener implements LocationListener{
#Override
public void onLocationChanged(Location location) {
if (location != null){
n = n + 1;
textData.setText("\n" + n);
}
}
and
#Override
protected void onPause() {
if(lm != null) {
lm.removeUpdates(ll);
}
ll = null;
lm = null;
super.onPause();
}
The only clue I have is that if I take the lm.removeUpdates(ll) out of the if(lm != null) then the code crashes which makes me think that lm must be null and that lm.removeUpdates(ll) must not be correct but it matches the all the examples I could find as well as the Android documentation as far as I can tell.
Please help.
LocationListener ll = new mylocationlistener();
This LocationListener is local to your method onCreate().So is your LocationManager lm.So when you are removing updates its not working with the manager and listener that you declared as the class variable.
Just write as
lm = (LocationManager)getSystemService(Context.LOCATION_SERVICE);
ll = new mylocationlistener(); in your onCreate().
I'm using the code shown below to get an updated value for location every time a button is clicked. When my activity is resumed I get an update every second, so that when I call getLastKnownLocation I expect to have a location that have been updated in the last second.
Is that the correct way to do that?
I would expect the onLocationChanged event to be triggered every time I execute a 'geo fix' command (or max after 1s since I request update every 1s), but it's only triggered the first time. Why?
Any help/suggestion welcome!
Thanks
package org.digitalfarm.atable;
...
public class Atable extends Activity {
private Button mSearchButton;
private TextView mytext;
private LocationManager locationManager;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
mSearchButton = (Button)this.findViewById(R.id.button);
mytext = (TextView) findViewById(R.id.dude);
locationManager = (LocationManager)getSystemService(Context.LOCATION_SERVICE);
final Criteria criteria = new Criteria();
criteria.setAccuracy(Criteria.ACCURACY_FINE);
mSearchButton.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
String provider = locationManager.getBestProvider(criteria, true);
Location location = locationManager.getLastKnownLocation(provider);
}
});
}
//Start a location listener
LocationListener onLocationChange=new LocationListener() {
public void onLocationChanged(Location loc) {
//sets and displays the lat/long when a location is provided
String latlong = "Lat: " + loc.getLatitude() + " Long: " + loc.getLongitude();
mytext.setText(latlong);
}
public void onProviderDisabled(String provider) {
// required for interface, not used
}
public void onProviderEnabled(String provider) {
// required for interface, not used
}
public void onStatusChanged(String provider, int status,
Bundle extras) {
// required for interface, not used
}
};
//pauses listener while app is inactive
#Override
public void onPause() {
super.onPause();
locationManager.removeUpdates(onLocationChange);
}
//reactivates listener when app is resumed
#Override
public void onResume() {
super.onResume();
locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER,1000,100.0f,onLocationChange);
}
}
The time, specified in requestLocationUpdates is the shortest possible time interval for which a maximum of one update will occur. So you have registered for at most one update every second, but the actual update may take more that that to occur, and, furthermore, no guarantee is given for that.
From the documentation:
minTime - the minimum time interval for notifications, in milliseconds. This field is only used as a hint to conserve power, and actual time between location updates may be greater or lesser than this value.
Try to set that time interval to 0 and if there are no other problems (although it all seems fine to me), you should start getting the updates "as frequently as possible".