Reputation: 2251
I have an app that uses a WebView where sites may request to use the device's geolocation. I have the following in my main activity:
public void onGeolocationPermissionsShowPrompt(final String origin, final GeolocationPermissions.Callback callback) {
m_geolocationCallback = null;
m_geolocationOrigin = null;
// If we don't have location permissions, we must request them first
if (ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
// Show rationale if necessary
if (ActivityCompat.shouldShowRequestPermissionRationale(MainActivity.this, Manifest.permission.ACCESS_FINE_LOCATION)) {
new AlertDialog.Builder(MainActivity.this)
.setMessage(getString(R.string.permission_location_rationale, origin))
.setNeutralButton(android.R.string.ok, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
m_geolocationOrigin = origin;
m_geolocationCallback = callback;
ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, PERMISSIONS_REQUEST_ACCESS_FINE_LOCATION);
}
})
.show();
}
else {
m_geolocationOrigin = origin;
m_geolocationCallback = callback;
ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, PERMISSIONS_REQUEST_ACCESS_FINE_LOCATION);
}
}
// Otherwise just tell webview that permission has been granted
else {
callback.invoke(origin, true, false);
}
}
To handle the permission request result, MainActivity
also has the following:
@Override
public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) {
switch (requestCode) {
case PERMISSIONS_REQUEST_ACCESS_FINE_LOCATION:
// Permission granted
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
if (m_geolocationCallback != null) {
m_geolocationCallback.invoke(m_geolocationOrigin, true, false);
}
}
// Permission denied
else {
// In this case, the user checked "Don't ask again" and denied permission
if (Build.VERSION.SDK_INT >= 23 && !shouldShowRequestPermissionRationale(permissions[0])) {
Toast.makeText(this, R.string.permission_location_denied, Toast.LENGTH_LONG).show();
}
if (m_geolocationCallback != null) {
m_geolocationCallback.invoke(m_geolocationOrigin, false, false);
}
}
return;
}
}
The issue I'm running into is that whenever a site requests to use geolocation and I deny it (resulting in invoke(origin, false false)
being called), the onGeolocationPermissionsShowPrompt
method is called immediately and I am stuck in a loop of endlessly denying permission to the app.
If I check "don't ask again" in the permission request, it works fine, but I want to be able to ask the user for permission again at a later point in time.
I've tried fixing this by changing my rejection callback invocation to
m_geolocationCallback.invoke(m_geolocationOrigin, false, true)
, but this does not produce the desired effect. This will prevent stop the continuous calls to onGeolocationPermissionsShowPrompt
, but it also means that the given origin can no longer use geolocation even if permissions are later enabled, and I found no way to undo this (even by uninstalling/reinstalling the app).
Any ideas on how to get around this issue?
Upvotes: 1
Views: 2236
Reputation: 51
we can fix it like below:
// set param 'retain' true, to prevent permission request continuously
callback?.invoke(origin, isGranted, true)
if (!isGranted) {
// clear permission state, so the user can request permission again
Handler().postDelayed({ GeolocationPermissions.getInstance().clear(origin) }, 500)
}
Upvotes: 0
Reputation: 6979
This looks like a bug in Android Webview. The endless loop happens if callback.invoke
is called asynchronously with granted=false, but does not occur if it is called synchronously inside onGeolocationPermissionsShowPrompt
.
My workaround is to save the time when the user denies the geolocation permission. If onGeolocationPermissionsShowPrompt
is called again within a small amount of time and checkSelfPermission
does not return PERMISSION_GRANTED, run callback.invoke(origin, false, false)
and do not re-prompt the user.
Upvotes: 1