3

Affected devices so far:

  • Xperia 1 II
  • Xiamoi Redmi Note 7

Use: In order to request location updates, I check that the location settings are adequate beforehand. If not, I show a small text, that the services must be enabled for my feature. If the user clicks on it, the system dialog to enable the location service will be prompted.

How I run the check: I run the check through LocationSettingsRequest and LocationServices and handle the ResolvableApiException. If the Service is disabled by the user, then on all my devices this will show the System Dialog, asking the user to enable the service (and enables it, if okay is clicked).

What happens instead on Redmi: But for whatever reason, on Xiamoi Redmi Note 7, the checkLocationSettings will always return with an ResolvableApiException, even if the service is already enabled. The Dialog won't appear and directly return a positive result (sure, because the service is enabled). Hence, the user is stuck and clicks forever on "enable".

Does somebody has any information why the LocationSettingsRequest is not working properly on those devices and knows how to fix it?

Example Code snippet

void enableLocationSettings(onLocationServiceRequiredCallback: ICallback) {
   LocationRequest locationRequest = LocationRequest.create()
         .setInterval(LOCATION_UPDATE_INTERVAL)
         .setExpirationDuration(LOCATION_UPDATE_EXPIRATION_DURATION)
         .setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);

    LocationSettingsRequest.Builder builder = new LocationSettingsRequest.Builder()
            .addLocationRequest(locationRequest);

    LocationServices
            .getSettingsClient(this)
            .checkLocationSettings(builder.build())
            .addOnSuccessListener(this, (LocationSettingsResponse response) -> {
                // update location as usual
            })
            .addOnFailureListener(this, error -> {
                if (error instanceOf ApiException) {
                    int statusCode = error.statusCode;
                    if(statusCode == LocationSettingsStatusCodes.RESOLUTION_REQUIRED) {
                        if(error instanceOf ResolvableApiException) {
                            PendingIntent resolution = error.resolution 
             // This will trigger the system dialog, see below
                            onLocationServiceRequiredCallback(resolution);
                        }
                    }
                }
                // error callback
            });  

 

Unfortunately the code for the callback will now be kotlin. Sorry for that. I can only post "example code snippets", since this is work related.

It uses ActivityResultContracts.StartIntentSenderForResult to start the system dialog.

resolution is the PendingIntent from above.

The contract:

private val locationServiceContract = registerForActivityResult(
    ActivityResultContracts.StartIntentSenderForResult()
) { activityResult ->
    lifecycleScope.launchWhenResumed {
        val result = if (activityResult.resultCode == Activity.RESULT_OK) {
            // This will retrigger the location fetch (looping)
            enableLocationSettings(...) 
        } else {
            // Do nothing
        }
    }
}

After the callback from above is called, this will get executed:

        locationServiceContract.launch(
            IntentSenderRequest.Builder(
                resolution
            ).build()
        )

Dump of LocationSettingsStates, aquired using the deprecated LocationSettings API (which is using GoogleApiClient):

isGpsPresent: true, 
isGpsUsable: false, 
isLocationPresent: true, 
isLocationUsable: true, 
isNetworkLocationPresent: true, 
isNetworkLocationUsable: true 
JacksOnF1re
  • 3,336
  • 24
  • 55

2 Answers2

1

Hi I use different code in order to check if GPS is enabled on a device. maybe its worth for you to try it out and see if it resolves your issue.

 private boolean isLocationEnabled(Context context) {
        LocationManager locationManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
        return LocationManagerCompat.isLocationEnabled(locationManager);
    }

I then send the user to settings through a dialog using

Intent intent = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS);
context.startActivity(intent);

I bet you are doing the same. After sending the user to enable GPS you can just run the method a second time on resume

let me know if this helps

quealegriamasalegre
  • 2,887
  • 1
  • 13
  • 35
  • Thank you! I know this approach, too. But unfortunately this involves the user to manually enable the settings, while in contrast using the ResolvableApiException can be used to open a system dialog, which will (if the user allows it) to directly enable location services. I really want to know why my worflow (which is also advertised by google itself) fails here. – JacksOnF1re Oct 28 '21 at 10:30
  • 1
    i understand. could you maybe log the resolvableApiException for your redmi and post it – quealegriamasalegre Oct 28 '21 at 15:52
  • 1
    could you also share the code where you cacthc the result of the user interaction. I still dont get wy it would eternally loop on the enable dialog – quealegriamasalegre Oct 28 '21 at 16:01
  • I updated the question. Unfortunately I can not 100% copy my work related code. But the snippet is hopefully enough to understand what is happening. I also updated the example code for the fetch. The PendingIntent will be inside the ResolvableApiException, for error statusCode RESOLUTION_REQUIRED (code 6). – JacksOnF1re Oct 29 '21 at 13:12
  • I also noticed the same happens with Xperia 1 II device. – JacksOnF1re Oct 29 '21 at 14:39
  • ok so I guess I have a hunch on what is happening. let me know if I am completely lost but you seem to be calling your location permissions code everytime your activity resumes and upon a renewed check everytime the onFailureListener callback is triggered. Endlessly showing you the same dialog. My best guess is that those devices are somehow failing to fulfill the specifics in your locationRequest and that is why they always trigger a failure – quealegriamasalegre Oct 29 '21 at 17:25
  • my suggestion is that you try to log the result of `LocationServices.getSettingsClient(this).checkLocationSettings(builder.build())` for those devices and see if you can find aspects of it that dont match up with your requirements (particularly explore LocationSttingsStates and LocationSettingsStatusCodes) https://developers.google.com/android/reference/com/google/android/gms/location/LocationSettingsResult – quealegriamasalegre Oct 29 '21 at 17:37
  • a hailmary that might be worth a shot is calling `checkLocationSettings(fusedLocProviderClient,builder.build())` instead of just `checkLocationSettings(builder.build())` as per the official documentation – quealegriamasalegre Oct 29 '21 at 17:42
  • .... and just in order to get it out of my system I think permission-wise it might be overkill to check for the specifics of your `locationRequest`. in my app I simply prompt the user to enable location using checkPermissions and the code in my answer, all aditional details such as update interval and expiration are dealt with automatically. I say it cause this might be the cause of the issue – quealegriamasalegre Oct 29 '21 at 17:49
  • Unfortunately I can't check this right now (remote). But I will hopefully this week and come back to you. "let me know if I am completely lost[...]", no you're not. That is how it is done right now. Since the suggestion to log the location state in detail, which might not fit the super detailed requirement from LocationRequest might be a good hint, I'll accept that, in terms of the bounty, before it expires. – JacksOnF1re Nov 01 '21 at 10:45
  • May I ask, in which official documentation you found that checkLocationSettings can be called with ProviderClient? `LocationServices.getSettingsClient(this).checkLocationSettings(fusedLocProviderClient, builder.build())` ? – JacksOnF1re Nov 01 '21 at 10:47
  • > it might be overkill to check for the specifics of your locationRequest[...] Do you mean to not add the specifics at all for a `LocationRequest` and `requestLocationUpdates`, or just not within `LocationSettingsRequest` request? – JacksOnF1re Nov 01 '21 at 10:49
  • here you see the documentation you asked for https://developers.google.com/android/reference/com/google/android/gms/location/SettingsApi#public-abstract-pendingresultlocationsettingsresult-checklocationsettings-googleapiclient-client,-locationsettingsrequest-locationsettingsrequest. Upon closer inspection fuselLocationproviderClient does not extend GoogleApiClient but maye you can get it alternatively – quealegriamasalegre Nov 01 '21 at 15:37
  • and yes, maybe try to remove the details in your locationRequest gradually and see if eventually the issue disappears on your redmi7 – quealegriamasalegre Nov 01 '21 at 15:39
  • This is the deprecated API, which I am not using :) `LocationServices.getSettingsClient(this).checkLocationSettings(..)` does not allow provider nor apiClient as argument. But for testing purposes it might be worth to check the result of the deprecated api. – JacksOnF1re Nov 01 '21 at 17:39
  • 1
    For further readers and to sum this up. Using the deprecated API is not an option. I used it for testing purposes anyway, to gain insights. It gives the same result as the current API. Meaning the statusCode is still `RESOLUTION_REQUIRED`, even if the LocationRequest has no configuration at all. So the old api did not give me any new insights, which I did not already know - unfortunately. I'll post a dump of LocationSettingsState at the end of my question. – JacksOnF1re Nov 04 '21 at 16:36
0

After doing a good amount of research I got a solution for this ...

The problem is basically, if the LocationSettingsStatusCodes.RESOLUTION_REQUIRED == true then the next consecutive call you make will always result in the same output

i.e if you are calling this function recursively you will get RESOLUTION_REQUIRED always so what you can do is modify your contract to something like this

private val locationServiceContract = registerForActivityResult(
    ActivityResultContracts.StartIntentSenderForResult()
) { activityResult ->
    lifecycleScope.launchWhenResumed {
        val result = if (activityResult.resultCode == Activity.RESULT_OK) {
            // Dont call this as it will retrigger the location fetch (looping)
           // enableLocationSettings(...) 
               requestForLocationUpdates() // create this method see method body below
              
        } else {
            // Do nothing
        }
    }
}

requestForLocationUpdates()

 private fun requestForLocationUpdates() {

          locationCallback = object : LocationCallback() {
               override fun onLocationResult(locationResult: LocationResult) {
                    if (locationResult.locations.size > 0) {
                          // update location as usual using locationResult.locations[0] or other item
                         fusedLocationClient.removeLocationUpdates(locationCallback)
                    }
               }
          }

          fusedLocationClient.requestLocationUpdates(
               locationRequest,
               locationCallback,
               Looper.getMainLooper()
          )
     }
AgentP
  • 6,261
  • 2
  • 31
  • 52