Is it possible to get a notification when any location provider is enabled/disabled and ascertain what action occurred?

10,307

Solution 1

I hope that this doesn't contradict your requirement

I know that I could keep the state of each provider and then when I receive notification that they have changed then I could work out what has changed, I am looking for a more "standard" method of doing this.

...but I would think the best, "standard", and most flexible way of doing this is to let your LocationProviderChangedReceiver implement the LocationListener interface and then implement onProviderEnabled() and onProviderDisabled() like so:

public void onProviderEnabled(String provider) {
  if(provider.equals(LocationManager.GPS_PROVIDER)){
    ...
  } else if (provider.equals(LocationManager.NETWORK_PROVIDER)){
    ...
  }
}

Note that you should add the ACCESS_FINE_LOCATION permission to your manifest if you haven't already. Also, other providers (beyond "Network" and "GPS") may apply depending on the context, like PASSIVE_PROVIDER or even FUSED_PROVIDER.

Updated answer:
If constantly listening for location changes is no option for you, the LocationManager also knows which provider is currently enabled:

LocationManager locationManager = (LocationManager) getSystemService(LOCATION_SERVICE);
locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER);
locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER);

...so that you could use these checks together with your BroadcastReceiver to avoid manually holding any enabled/disabled flags. Beyond that, there is an intent called android.location.GPS_ENABLED_CHANGE which you could try to receive (and process) as well. But since this intent is hidden in the official documentation, it might not be safe to use it, as mentioned in this answer.

Even another approach:
Android's default settings toggle widget (you know what I mean) displays the GPS enabled/disabled state by subscribing to the android.location.PROVIDERS_CHANGED intent (like you do) to trigger a request of the GPS state via the user settings:

@Override
public int getActualState(Context context) {
  ContentResolver resolver = context.getContentResolver();
  boolean on = Settings.Secure.isLocationProviderEnabled(
                 resolver, LocationManager.GPS_PROVIDER);
  return on ? STATE_ENABLED : STATE_DISABLED;
}

(from the official sources).
You could adapt this approach for other location providers as well.

Solution 2

This works for me:

Add receiver to the Manifest file:

<receiver
        android:name="com.eegeo.location.LocationProviderChangedReceiver">
        <intent-filter>
            <action android:name="android.location.PROVIDERS_CHANGED" />
        </intent-filter>
    </receiver>

Check both location providers in receiver:

public class LocationProviderChangedReceiver  extends BroadcastReceiver{

    @Override
    public void onReceive(Context context, Intent intent) {

        boolean anyLocationProv = false;
        LocationManager locationManager = (LocationManager) MyMainActivity.context.getSystemService(Context.LOCATION_SERVICE);

        anyLocationProv |= locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER);
        anyLocationProv |=  locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER);

        Log.i("", "Location service status" + anyLocationProv);


    }

}

Though this receiver is called more than once due to obvious reasons, but this will tell you the status.

Solution 3

Thanks to @usman 's answer we can apply the solution without extends the class by the following way;

AndroidManifest.xml;

<receiver android:name="com.eegeo.location.LocationProviderChangedReceiver">
    <intent-filter>
        <action android:name="android.location.PROVIDERS_CHANGED" />
    </intent-filter>
</receiver>

class (I never tested);

public class LocationProviderChangedReceiver {

    private mLocationManager = (LocationManager) getApplicationContext().getSystemService(Context.LOCATION_SERVICE);
    private BroadcastReceiver mReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            String ability = (isGPSProviderEnabled() && isNetworkProviderEnabled()) ? "CAN" : "CANNOT";
            Log.d("", "The location " + ability + " be caught in high accuracy!");
        }
    };


    public startProviderChangeObserving () {
        registerReceiver(mReceiver, new IntentFilter(LocationManager.PROVIDERS_CHANGED_ACTION));

    }


    public stopProviderChangeObserving () {
        unregisterReceiver(mReceiver);
    }   


    /**
    * Method to verify that the location providers wheter on or not on the device
    **/
    public boolean isGPSProviderEnabled() {
        return (Boolean) mLocationManager.isProviderEnabled(LocationManager.GPS_PROVIDER);
    }

    public boolean isNetworkProviderEnabled() {
        return (Boolean) mLocationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER);
    }

}
Share:
10,307
Steve Weet
Author by

Steve Weet

Long term developer with a preference for *Nix originally from the Informix World but later Ruby and .net Now managing Engineering teams at Funding Circle

Updated on July 20, 2022

Comments

  • Steve Weet
    Steve Weet almost 2 years

    I wish to receive a notification when the user enables or disables either Network or GPS locations, and importantly I wish to know which one they have changed and how. I have a broadcast receiver for the android.location.PROVIDERS_CHANGED broadcast intent and this is receiving the correct broadcast.

    I now need to try and determine which action has occurred i.e. enable or disable and which provider has been changed. I know that I could keep the state of each provider and then when I receive notification that they have changed then I could work out what has changed, I am looking for a more "standard" method of doing this. The broadcast intent does not seem to have any extras to indicate which provider has changed.

    This is the code I have currently.

        public class LocationProviderChangedReceiver extends BroadcastReceiver {
        private final static String TAG = "LocationProviderChangedReceiver";
    
        @Override
        public void onReceive(Context context, Intent intent) {
          if (intent.getAction().matches("android.location.PROVIDERS_CHANGED"))
          {
            Log.i(TAG,"Location Providers changed");
            Bundle bundle = intent.getExtras();
            if (bundle == null) {
              Log.d(TAG, "No extras data");
             } else {
               Log.d(TAG, "Bundle received of size " + bundle.size);
             } 
          }
        }
      }
    

    And this is a small extract from my Manifest

      <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
      <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
    
      <receiver 
        android:name=".LocationProviderChangedReceiver"
        android:exported="false">
        <intent-filter>
          <action android:name="android.location.PROVIDERS_CHANGED" />
          <category android:name="android.intent.category.DEFAULT" />
        </intent-filter>
      </receiver>
    

    This would be perfect if there was an extra within the Broadcast that stated which provider had changed and whether it was enabled or disabled. Unfortunately this is not the case. Is anyone aware of any mechanism by which I can determine what state has changed without maintaining my own state variables.

    In an ideal world I would monitor for changes constantly but only listen for location changes occasionally. I would like to avoid constantly monitoring for location changes.

  • Steve Weet
    Steve Weet about 11 years
    Sorry to change the question, and the requirements after you have answered but I was looking for a way to monitor this without listening for locations. I do request location updates at various points but I wanted to constantly monitor changes to providers without constantly listening for locations.
  • saschoar
    saschoar about 11 years
    Ok, in this case the LocationManager could also help. I updated my answer.