How to detect if user turned off Location in Settings?
Asked Answered
R

3

6

I want to detect if the user turned off the location at runtime. I can check if he turns it on or if the location was turned off by user before the app was started but I can't check if he turned it off after.

Code Sample: MapEntity extends LocationListener

class MapViewer(a: MainActivity, parentView: ViewGroup) : MapEntity(a, parentView) {

    override fun onProviderEnabled(provider: String?) {
        activity.hideGpsSnackbar()
    }

    override fun onProviderDisabled(provider: String?) {
        activity.showGpsSnackbar()
    }

}

For realtime GPS location checking, I'm using GnssStatus.Callback()

UPDATE:

I've created BroadcastReceiver according to the answer below.

abstract class GPSReceiver : BroadcastReceiver() {

    override fun onReceive(context: Context, intent: Intent) {
        try {
           val locationManager = context.getSystemService(LOCATION_SERVICE) as LocationManager

             if(locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER)) {
                    onGpsChanged(true)
                } else {
                    onGpsChanged(false)
                }
            } catch (ex: Exception) {
                App.log("IsGPSEnabled: $ex")
            }

        }

        abstract fun onGpsChanged(isEnabled: Boolean)
    }

Code inside one of my Activities:

private val gpsStatusReceiver = object : GPSReceiver() {

     override fun onGpsChanged(isEnabled: Boolean) {
         if (isEnabled){
             hideGpsSnackbar()
         } else {
             showGpsSnackbar()
         }
     }
}

override fun onStart() {
    super.onStart()
    registerReceiver(gpsStatusReceiver, IntentFilter())
}

override fun onStop() {
    super.onStop()
    unregisterReceiver(gpsStatusReceiver)
}

UPDATE

If you want to support Android 6.0, you cannot use abstract class. Because it will try to create object out of this class defined in AndroidManifest. Android 8.0+ will not check receiver inside AndroidManifest so you can instantiate object out of Abstract Class. So instead of it create interface.

Rathbun answered 1/8, 2019 at 9:49 Comment(0)
C
6

I'm actually doing it with a BroadcastReceiver.

I can share my code; it's java but I think you can easily convert it into kotlin.

  1. Create a Broadcastreceiver

Example:

public class GPSBroadcastReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        try {
            LocationManager locationManager = (LocationManager) context.getSystemService(LOCATION_SERVICE);
            if (locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER)) {
                //isGPSEnabled = true;
            } else {
                //isGPSEnabled = false;
            }
        }catch (Exception ex){
        }
    }
}
  1. Add your BroadcastReceiver to the manifest

Example:

    <receiver android:name=".others.GPSBroadcastReceiver">
        <intent-filter>
            <action android:name="android.location.PROVIDERS_CHANGED" />

            <category android:name="android.intent.category.DEFAULT" />
        </intent-filter>
    </receiver>

Then (for my case) I manage it in my ApplicationContext as follows:

private GPSBroadcastReceiver gpsBroadcastReceiver = new GPSBroadcastReceiver();

@Override
public void onCreate() {
    ....
    registerReceiver(gpsBroadcastReceiver, new IntentFilter());
}

@Override
public void onTerminate() {
    ...
    unregisterReceiver(gpsBroadcastReceiver);
}

That's just an example, there might be other ways for it but actually I'm fine with that one; good luck!

Edit:

try adding this permission in your manifest

<uses-permission android:name="android.permission.ACCESS_GPS" />

Edit: For Android 8.0+ register your BroadcastReceiver like this:

registerReceiver(gpsBroadcastReceiver,IntentFilter("android.location.PROVIDERS_CHANGED"))

Adding action inside AndroidManifest will not work.

Corrinecorrinne answered 1/8, 2019 at 10:5 Comment(8)
@martin1337 check edits, probably I forgot to mention to add permissionCorrinecorrinne
added permission but still nothingRathbun
@martin1337 is your log written in logcat?Corrinecorrinne
nope. I did not get any logs from receiver. Ive updated my solution a little bit. Receiver is abstract class and overriding method onGpsChanged should return status to my activity. But nothing happened.Rathbun
@martin1337 if you replace onGpsChanged(bool) with a log, you get anything? try debugging; put a breakpoint in your onReceive and check if it's called and whenCorrinecorrinne
Thats the problem. onReceive is never called. That means android.location.PROVIDERS_CHANGED will not be triggered at location enabled from settingsRathbun
I've found similar problem here #48659624 Maybe this is why. Im testing it on Android 9.0 so there is possibility that registering receiver inside manifest will not work. registerReceiver(gpsStatusReceiver, IntentFilter("android.location.PROVIDERS_CHANGED")) fixed the issue.Rathbun
@martin1337 oh damn, I forgot it couse I didn't need it.. thanks for the edit! and glad you solved the problem!Corrinecorrinne
B
0

You can use Broadcast Recievers for that purpose that will be triggered when state has changed. Check this Answer

Boschvark answered 1/8, 2019 at 9:55 Comment(1)
And why is onProviderEnabled and onProviderDisabled doesnt work here?Rathbun
S
0

Please use the following approach to enable location settings:

LocationServices
        .getSettingsClient(this)
        .checkLocationSettings(mLocationSettingsRequest)
        .addOnSuccessListener(this, object : OnSuccessListener<LocationSettingsResponse> {    
                override fun onSuccess(locationSettingsResponse: LocationSettingsResponse) {
                    Log.i(TAG, "LocationManager: All location settings are satisfied.");
                    mLocationCallback?.let {
                        fusedLocationClient?.requestLocationUpdates(mLocationRequest, mLocationCallback, Looper.myLooper());
                    }
                    //updateUI();
                }
        })
        .addOnFailureListener(this, object : OnFailureListener {
                override fun onFailure(e: Exception) {
                    var statusCode = (e as ApiException).getStatusCode()
                    when (statusCode) {
                        LocationSettingsStatusCodes.RESOLUTION_REQUIRED -> {
                            try {
                                // Show the dialog by calling startResolutionForResult(), and check the
                                // result in onActivityResult().
                                var rae = e as ResolvableApiException;
                                rae.startResolutionForResult(this@BaseLocationActivity, REQUEST_CHECK_SETTINGS);
                            } catch (sie: IntentSender.SendIntentException) {
                                Log.i(TAG, "PendingIntent unable to execute request.");
                            }
                        }
                        LocationSettingsStatusCodes.SETTINGS_CHANGE_UNAVAILABLE -> {
                            mRequestingLocationUpdates = false;
                        }    
                    }
                }
        })

Create location request

private fun createLocationRequest(interval: Long, distance: Float = 0f) {
    mLocationRequest = LocationRequest()
    mLocationRequest.interval = interval
    mLocationRequest.fastestInterval = interval

    Log.v(TAG, "distance => $distance")
    if (distance > 0) {
        mLocationRequest.smallestDisplacement = distance
    }
    mLocationRequest.priority = LocationRequest.PRIORITY_HIGH_ACCURACY
}
Saltandpepper answered 1/8, 2019 at 10:6 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.