90

How can I check the current status of the GPS receiver? I already checked the LocationListener onStatusChanged method but somehow it seems that is not working, or just the wrong possibility.

Basically I just need to know if the GPS icon at the top of the screen is blinking (no actual fix) or solid (fix is available).

Jason Plank
  • 2,336
  • 5
  • 31
  • 40
nr1
  • 1,019
  • 2
  • 10
  • 8

17 Answers17

150

As a developer of SpeedView: GPS speedometer for Android, I must have tried every possible solution to this problem, all with the same negative result. Let's reiterate what doesn't work:

  1. onStatusChanged() isn't getting called on Eclair and Froyo.
  2. Simply counting all available satellites is, of course, useless.
  3. Checking if any of the satellites return true for usedInFix() isn't very helpful also. The system clearly loses the fix but keeps reporting that there are still several sats that are used in it.

So here's the only working solution I found, and the one that I actually use in my app. Let's say we have this simple class that implements the GpsStatus.Listener:

private class MyGPSListener implements GpsStatus.Listener {
    public void onGpsStatusChanged(int event) {
        switch (event) {
            case GpsStatus.GPS_EVENT_SATELLITE_STATUS:
                if (mLastLocation != null)
                    isGPSFix = (SystemClock.elapsedRealtime() - mLastLocationMillis) < 3000;
                
                if (isGPSFix) { // A fix has been acquired.
                    // Do something.
                } else { // The fix has been lost.
                    // Do something.
                }
                
                break;
            case GpsStatus.GPS_EVENT_FIRST_FIX:
                // Do something.
                isGPSFix = true;
                
                break;
        }
    }
}

OK, now in onLocationChanged() we add the following:

@Override
public void onLocationChanged(Location location) {
    if (location == null) return;
    
    mLastLocationMillis = SystemClock.elapsedRealtime();
    
    // Do something.
            
    mLastLocation = location;
}

And that's it. Basically, this is the line that does it all:

isGPSFix = (SystemClock.elapsedRealtime() - mLastLocationMillis) < 3000;

You can tweak the millis value of course, but I'd suggest to set it around 3-5 seconds.

This actually works and although I haven't looked at the source code that draws the native GPS icon, this comes close to replicating its behaviour.

starball
  • 20,030
  • 7
  • 43
  • 238
soundmaven
  • 1,496
  • 1
  • 9
  • 10
  • Hi, I am wondering why counting available satellites is useless like in this example? If number of satellites found is 0 means no connection, or am I wrong? http://groups.google.com/group/android-developers/browse_thread/thread/21cd0993d02fd43f – per_jansson Nov 10 '10 at 13:51
  • 3
    Hi Stephen, can you please explain *why* isGPSFix works. Thanks, Nick – nickfox Dec 19 '10 at 05:14
  • By the way, I've noticed that in ICS they increased the time until the system starts reporting that the GPS fix has been lost. At least in 4.0.3 it's precisely 10 seconds. – soundmaven Feb 11 '12 at 08:42
  • Works great, and using a 10 second as opposed to 3-5 for ICS also works quite well. Fortunately fixing isn't a necessary feature of my app but its good to be able to know it. Thanks. – Tom May 10 '12 at 02:32
  • I implemented something similar to this, but in my quest to tidying up the code, I implemented both the LocationListener and GpsStatusListener on the same class. I don't know if this is relevant, but I am having trouble unregistering this listener in 'onPause' (I am calling both the methods to terminate each implemented class there). Any idea on what I am doing wrong? – ravemir Jan 16 '13 at 11:19
  • Is there an issue posted on google ? Cause [the one that seems relevant](http://code.google.com/p/android/issues/detail?id=9433) is not about API versions - rather devices ([reported](http://code.google.com/p/android/issues/detail?id=9433#c10) working on 1.6). Could it be that it only [needs time](http://stackoverflow.com/a/8733810/281545) ? – Mr_and_Mrs_D May 26 '13 at 11:27
  • How can one do this for the Fused Location API? – powder366 Sep 06 '15 at 21:11
  • 1
    But doesn't this break when `mLastLocationMillis` was set by a source other than the GPS? The system would than claim that `isGPSFix` is true but in reality it is just ***any*** fix (maybe one from the phone network) – avalancha Nov 04 '15 at 16:33
  • avalancha, yes, that might be the case. This answer is almost six years old and I moved on to other projects since then, so I haven't tested it with other sources. – soundmaven Jan 24 '16 at 21:28
  • `GpsStatus.Listener` seems to be deprecated; is there any update? – Marc Bacvanski Jul 14 '16 at 00:30
  • I'm finding that on Android 5.0.1 `onLocationChanged` never gets called... I can only get updates via the google play services fused location api, which is clearly not useful for determining whether or not gps is available. Any ideas how to fix this? – Jules Sep 27 '16 at 23:55
25

The GPS icon seems to change its state according to received broadcast intents. You can change its state yourself with the following code samples:

Notify that the GPS has been enabled:

Intent intent = new Intent("android.location.GPS_ENABLED_CHANGE");
intent.putExtra("enabled", true);
sendBroadcast(intent);

Notify that the GPS is receiving fixes:

Intent intent = new Intent("android.location.GPS_FIX_CHANGE");
intent.putExtra("enabled", true);
sendBroadcast(intent);

Notify that the GPS is no longer receiving fixes:

Intent intent = new Intent("android.location.GPS_FIX_CHANGE");
intent.putExtra("enabled", false);
sendBroadcast(intent);

Notify that the GPS has been disabled:

Intent intent = new Intent("android.location.GPS_ENABLED_CHANGE");
intent.putExtra("enabled", false);
sendBroadcast(intent);

Example code to register receiver to the intents:

// MyReceiver must extend BroadcastReceiver
MyReceiver receiver = new MyReceiver();
IntentFilter filter = new IntentFilter("android.location.GPS_ENABLED_CHANGE");
filter.addAction("android.location.GPS_FIX_CHANGE");
registerReceiver(receiver, filter);

By receiving these broadcast intents you can notice the changes in GPS status. However, you will be notified only when the state changes. Thus it is not possible to determine the current state using these intents.

sast
  • 478
  • 4
  • 8
  • I don't know how you're able to send that broadcast from you application because it seems that is a system protected broadcast, meaning only system can send it. Any app sending that intent should crash. – JoxTraex Jul 14 '16 at 00:39
  • It was possible to send it six years ago, when I wrote the answer. Apparently they have added some protection to the system since then. – sast Sep 16 '16 at 06:06
  • I was using these intents to tell when other apps were using the GPS. I believe starting with Nougat, you can't even listen for these intents anymore, that or they have changed! I don't want to be listening for location with my own app. Anyone have any other ideas? – Flyview Dec 29 '16 at 22:51
  • Yes I've got that crash: 05-14 10:25:24.113 17344-17344/xxx.yyy.zzz.debug E/AndroidRuntime: FATAL EXCEPTION: main Process: xxx.yyy.zzz.debug, PID: 17344 java.lang.SecurityException: Permission Denial: not allowed to send broadcast android.location.GPS_FIX_CHANGE from pid=17344, uid=10416 – unlimited101 May 14 '18 at 08:26
19

new member so unfortunately im unable to comment or vote up, however Stephen Daye's post above was the perfect solution to the exact same problem that i've been looking for help with.

a small alteration to the following line:

isGPSFix = (SystemClock.elapsedRealtime() - mLastLocationMillis) < 3000;

to:

isGPSFix = (SystemClock.elapsedRealtime() - mLastLocationMillis) < (GPS_UPDATE_INTERVAL * 2);

basically as im building a slow paced game and my update interval is already set to 5 seconds, once the gps signal is out for 10+ seconds, thats the right time to trigger off something.

cheers mate, spent about 10 hours trying to solve this solution before i found your post :)

chich
  • 411
  • 4
  • 4
14

Ok, so let's try a combination of all the answers and updates so far and do something like this:

The GPS listener could be something like this:

GpsStatus.Listener listener = new GpsStatus.Listener() {
    void onGpsStatusChanged(int event) {
        if (event == GPS_EVENT_SATELLITE_STATUS) {
            GpsStatus status = mLocManager.getGpsStatus(null);
            Iterable<GpsSatellite> sats = status.getSatellites();
            // Check number of satellites in list to determine fix state
        }
    }
}

The APIs are a bit unclear about when and what GPS and satellite information is given, but I think an idea would be to look at how many satellites are available. If it's below three, then you can't have a fix. If it's more, then you should have a fix.

Trial and error is probably the way to go to determine how often Android reports satellite info, and what info each GpsSatellite object contains.

Christopher Orr
  • 110,418
  • 27
  • 198
  • 193
  • the problem with counting the available satellites is, that even if you have 5 satellites in view, this doesn't mean that there a fix is always possible. (You mentioned it correct by writing "If it's more, then you _should_ have a fix") – nr1 Jan 08 '10 at 10:56
  • Indeed. Though I don't know any more about what info is required to constitute a fix, or how/whether this can be retrieved from a `GpsSatellite` object. – Christopher Orr Jan 08 '10 at 13:15
  • Another thought.. you don't mention this in your question, but have you tried just using `LocationManager.requestLocationUpdates` with the time and distance settings set to 0? That should send you GPS fixes as soon as they happen. If you're not receiving anything, then you most likely don't have a fix. You could combine this with the status listener above if you like. – Christopher Orr Jan 08 '10 at 13:16
  • 1
    Iterating "sats" and checking usedInFix() in GpsSatellite maybe? – DeliriumTremens Mar 23 '12 at 15:06
8

After a few years of working with GPS on windows mobile, I realized that the concept of "losing" a GPS fix can be subjective. To simply listen to what the GPS tells you, adding a NMEAListener and parsing the sentence will tell you whether the fix was "valid" or not. See http://www.gpsinformation.org/dale/nmea.htm#GGA . Unfortunately with some GPSes this value will fluctuate back and forth even during the normal course of operation in a "good fix" area.

So, the other solution is to compare the UTC time of the GPS location against the phone's time (converted to UTC). If they are a certain time difference apart, you can assume you lost the GPS position.

Justin Breitfeller
  • 13,737
  • 4
  • 39
  • 47
  • Would you care to explain the time comparing method? In Stephen Daye's answer, he compared the time differences between when the last fix happened and the latest GPS_EVENT_SATELLITE_STATUS event... Not sure what it is measuring. – Rokey Ge Apr 03 '11 at 14:04
7

get into similar problem while working on my MSc project, it seems that Daye's answer mistakenly reported "no fix" while the device stays in a static location. I've modified the solution just a little bit which seems to work fine for me in a static location. I don't how would it affect the battery as it is not my main concern, but here's how i did it by re-requesting location updates when a fix has timed out.

private class MyGPSListener implements GpsStatus.Listener {
    public void onGpsStatusChanged(int event) {
        switch (event) {
        case GpsStatus.GPS_EVENT_SATELLITE_STATUS:
            if (Global.getInstance().currentGPSLocation != null)
            {
                if((SystemClock.elapsedRealtime() - mLastLocationMillis) < 20000)
                {
                    if (!hasGPSFix) 
                        Log.i("GPS","Fix Acquired");
                    hasGPSFix = true;
                } 
                else
                {
                    if (hasGPSFix) 
                    {
                        Log.i("GPS","Fix Lost (expired)");
                        lm.requestLocationUpdates(LocationManager.GPS_PROVIDER, 2000, 10, locationListener);
                    }
                    hasGPSFix = false;
                }
            }
            break;
        case GpsStatus.GPS_EVENT_FIRST_FIX:
            Log.i("GPS", "First Fix/ Refix");
            hasGPSFix = true;
            break;
        case GpsStatus.GPS_EVENT_STARTED:
            Log.i("GPS", "Started!");
            break;
        case GpsStatus.GPS_EVENT_STOPPED:
            Log.i("GPS", "Stopped");
            break;
        }
    }
}
Jeffery Lee
  • 123
  • 2
  • 5
5

Well, putting together every working approach will result in this (also dealing with deprecated GpsStatus.Listener):

private GnssStatus.Callback mGnssStatusCallback;
@Deprecated private GpsStatus.Listener mStatusListener;
private LocationManager mLocationManager;

@Override
public void onCreate() {
    mLocationManager = (LocationManager) getSystemService(LOCATION_SERVICE);

    mLocationManager = (LocationManager) this.getSystemService(Context.LOCATION_SERVICE);
    if (checkPermission()) {
       mLocationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, GPS_UPDATE_INTERVAL, MIN_DISTANCE, this);
    }

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
        mGnssStatusCallback = new GnssStatus.Callback() {
            @Override
            public void onSatelliteStatusChanged(GnssStatus status) {
                satelliteStatusChanged();
            }

            @Override
            public void onFirstFix(int ttffMillis) {
                gpsFixAcquired();

            }
        };
        mLocationManager.registerGnssStatusCallback(mGnssStatusCallback);
    } else {
        mStatusListener = new GpsStatus.Listener() {
            @Override
            public void onGpsStatusChanged(int event) {
                switch (event) {
                    case GpsStatus.GPS_EVENT_SATELLITE_STATUS:
                        satelliteStatusChanged();
                        break;
                    case GpsStatus.GPS_EVENT_FIRST_FIX:
                        // Do something.
                        gpsFixAcquired();
                        break;
                }
            }
        };
        mLocationManager.addGpsStatusListener(mStatusListener);
    }
}

private void gpsFixAcquired() {
    // Do something.
    isGPSFix = true;
}

private void satelliteStatusChanged() {
    if (mLastLocation != null)
        isGPSFix = (SystemClock.elapsedRealtime() - mLastLocationMillis) < (GPS_UPDATE_INTERVAL * 2);

    if (isGPSFix) { // A fix has been acquired.
        // Do something.
    } else { // The fix has been lost.
        // Do something.
    }
}

@Override
public void onLocationChanged(Location location) {
    if (location == null) return;

    mLastLocationMillis = SystemClock.elapsedRealtime();

    mLastLocation = location;
}

@Override
public void onStatusChanged(String s, int i, Bundle bundle) {

}

@Override
public void onProviderEnabled(String s) {

}

@Override
public void onProviderDisabled(String s) {

}

Note: this answer is a combination of the answers above.

halfer
  • 19,824
  • 17
  • 99
  • 186
Gregory Stein
  • 325
  • 4
  • 14
  • 1
    As long as you credit other author(s), there is no problem with getting your own upvotes for new or derivative work. The rules here are more concerned about people plagiarising other folks' answers and using them elsewhere on Stack Overflow in order to attract rep dishonestly. Since you've mentioned you've built on other answers, I think this is fine. – halfer Oct 10 '17 at 20:46
3

If you just need to know if there's a fix, then check for the last known location provided by the GPS receiver and check the .getTime() value to know how old is that. If it's recent enough (like... a few seconds) you have a fix.

   LocationManager lm = (LocationManager)context.getSystemService(LOCATION_SERVICE); 
   Location loc = lm.getLastKnownLocation(LocationManager.GPS_PROVIDER);

   // Get the time of the last fix
   long lastFixTimeMillis = loc.getTime(); 

... and finally compare that to current date time (In UTC!). If it's recent enough you have a fix.

I do that in my app and so far so good.

Adan
  • 31
  • 1
2

You could try using LocationManager.addGpsStatusListener to get updated when the GPS status changes. It looks like GPS_EVENT_STARTED and GPS_EVENT_STOPPED might be what you're looking for.

Erich Douglass
  • 51,744
  • 11
  • 75
  • 60
2

I may be wrong but it seems people seem to be going way off-topic for

i just need to know if the gps icon at the top of the screen is blinking (no actual fix)

That is easily done with

LocationManager lm = (LocationManager) getSystemService(LOCATION_SERVICE);
boolean gps_on = lm.isProviderEnabled(LocationManager.GPS_PROVIDER);

To see if you have a solid fix, things get a little trickier:

public class whatever extends Activity {
    LocationManager lm;
    Location loc;
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);        
        lm = (LocationManager) getSystemService(LOCATION_SERVICE);
        loc = null;
        request_updates();        
    }

    private void request_updates() {
        if (lm.isProviderEnabled(LocationManager.GPS_PROVIDER)) {
            // GPS is enabled on device so lets add a loopback for this locationmanager
            lm.requestLocationUpdates(LocationManager.GPS_PROVIDER,0, 0, locationListener);
        }      
    }

    LocationListener locationListener = new LocationListener() {
        public void onLocationChanged(Location location) {
            // Each time the location is changed we assign loc
            loc = location;
        }

         // Need these even if they do nothing. Can't remember why.
         public void onProviderDisabled(String arg0) {}
         public void onProviderEnabled(String provider) {}
         public void onStatusChanged(String provider, int status, Bundle extras) {}
    };

Now whenever you want to see if you have fix?

if (loc != null){
    // Our location has changed at least once
    blah.....
}

If you want to be fancy you can always have a timeout using System.currentTimeMillis() and loc.getTime()

Works reliably, at least on an N1 since 2.1.

regomodo
  • 644
  • 3
  • 9
  • 18
  • 3
    Your solution is overcomplex for what it does. However it does very little. What if after gps resolves my location I go to underground station thus making gps lose the fix? – meandre Jul 17 '13 at 08:05
1

I know this is a little late. However why not use the NMEAListener if you want to know if you have a fix. From what I've read, the NMEAListener will give you the NMEA sentences and from there you pick the correct sentence.

The RMC sentence contains the fix status which is either A for OK or V for warning. The GGA sentence contains the Fix Quality (0 invalid, 1 GPS or 2 DGPS)

I can't offer you any java code as I'm only just starting out with Android, but I have done a GPS library in C# for Windows apps, which I'm looking to use with Xamarin. I only came across this thread because I was looking for provider information.

From what I've read so far about the Location object I'm not all that comfortable about methods like getAccuracy() and hasAccuracy(). I'm used to extracting from the NMEA sentences HDOP and VDOP values to determine how accurate my fixes are. Its quite common to have a fix, but have a lousy HDOP which means your horizontal accuracy is not very good at all. For example sitting at your desk debugging with an external Bluetooth GPS device hard up against a window, you are quite likely to get a fix, but very poor HDOP and VDOP. Place your GPS device in a flower pot outside or something similar or add an external aerial to the GPS and immediately you get good HDOP and VDOP values.

user2153142
  • 431
  • 1
  • 4
  • 7
1

With LocationManager you can getLastKnownLocation() after you getBestProvider(). This gives you a Location object, which has the methods getAccuracy() in meters and getTime() in UTC milliseconds

Does this give you enough info?

Or perhaps you could iterate over the LocationProviders and find out if each one meetsCriteria( ACCURACY_COARSE )

Mark Borgerding
  • 8,117
  • 4
  • 30
  • 51
1

so many posts...

GpsStatus.Listener gpsListener = new GpsStatus.Listener() {
                        public void onGpsStatusChanged(int event) {
                            if( event == GpsStatus.GPS_EVENT_FIRST_FIX){
                                showMessageDialog("GPS fixed");
                            }
                        }
                 };

adding this code, with addGpsListener... showMessageDialog ... just shows a standard dialog window with the string

did the job perfectly for me :) thanks a lot :=) (sry for this post, not yet able to vote)

cV2
  • 5,229
  • 3
  • 43
  • 53
1

If you do not need an update on the very instant the fix is lost, you can modify the solution of Stephen Daye in that way, that you have a method that checks if the fix is still present.

So you can just check it whenever you need some GPS data and and you don't need that GpsStatus.Listener.

The "global" variables are:

private Location lastKnownLocation;
private long lastKnownLocationTimeMillis = 0;
private boolean isGpsFix = false;

This is the method that is called within "onLocationChanged()" to remember the update time and the current location. Beside that it updates "isGpsFix":

private void handlePositionResults(Location location) {
        if(location == null) return;

        lastKnownLocation = location;
        lastKnownLocationTimeMillis = SystemClock.elapsedRealtime();

        checkGpsFix(); // optional
    }

That method is called whenever I need to know if there is a GPS fix:

private boolean checkGpsFix(){

    if (SystemClock.elapsedRealtime() - lastKnownLocationTimeMillis < 3000) {
        isGpsFix = true;

    } else {
        isGpsFix = false;
        lastKnownLocation = null;
    }
    return isGpsFix;
}

In my implementation I first run checkGpsFix() and if the result is true I use the variable "lastKnownLocation" as my current position.

0

Setting time interval to check for fix is not a good choice.. i noticed that onLocationChanged is not called if you are not moving.. what is understandable since location is not changing :)

Better way would be for example:

  • check interval to last location received (in gpsStatusChanged)
  • if that interval is more than 15s set variable: long_interval = true
  • remove the location listener and add it again, usually then you get updated position if location really is available, if not - you probably lost location
  • in onLocationChanged you just set long_interval to false..
Hardy
  • 5,590
  • 2
  • 18
  • 27
0

Maybe it's the best possiblity to create a TimerTask that sets the received Location to a certain value (null?) regularly. If a new value is received by the GPSListener it will update the location with the current data.

I think that would be a working solution.

nr1
  • 1,019
  • 2
  • 10
  • 8
0

You say that you already tried onStatusChanged(), but that does work for me.

Here's the method I use (I let the class itself handle the onStatusChanged):

private void startLocationTracking() {
    final int updateTime = 2000; // ms
    final int updateDistance = 10; // meter
    final Criteria criteria = new Criteria();
    criteria.setCostAllowed(false);
    criteria.setAccuracy(Criteria.ACCURACY_FINE);
    final String p = locationManager.getBestProvider(criteria, true);
    locationManager.requestLocationUpdates(p, updateTime, updateDistance,
            this);
}

And I handle the onStatusChanged as follows:

void onStatusChanged(final String provider, final int status,
        final Bundle extras) {
    switch (status) {
    case LocationProvider.OUT_OF_SERVICE:
        if (location == null || location.getProvider().equals(provider)) {
            statusString = "No Service";
            location = null;
        }
        break;
    case LocationProvider.TEMPORARILY_UNAVAILABLE:
        if (location == null || location.getProvider().equals(provider)) {
            statusString = "no fix";
        }
        break;
    case LocationProvider.AVAILABLE:
        statusString = "fix";
        break;
    }
}

Note that the onProvider{Dis,En}abled() methods are about enabling and disabling GPS tracking by the user; not what you're looking for.

CvR
  • 1
  • Unfortunately, it often does not work. onLocationChanged() gets called once a second, but onStatusChanged doesn't get called at all. Sometimes I wish I could just query the current status instead of waiting for a notification that might not come for a long time, or ever. – Edward Falk Jul 04 '10 at 04:46
  • 2
    Right. I've learned a few things myself after that previous reply, and one is that not all Android platforms are made alike. I definitely see calls to onStatusChanged on both my HTC Hero and my Archos 5, but I'm not surprised that doesn't work everywhere. How about plan B: you use the GPSStatusListener that is shown in another answer, and simply see if any of the satellites returns true for usedInFix(). In my experience that comes closest to the behavior of the standard GPS icon. (Have you tried to find the source that implements that standard icon, BTW?) – CvR Jul 14 '10 at 21:59