I have an Android App which uses WebView to show a map on Open Street Map and few weeks stop working properly.
A few weeks ago when the user tap on show my location on the web page Android shows a prompt to allow the app to load the current position but at the moment just an error is shown: Geolocation error: user denied Geolocation
.
I looked here on SO and on Google to find if some part of my code became deprecated or blocked but I didn't find anything useful. The only information I found was about ActionBarActivity
but also using AppCompatActivity
the problem was the same.
The MVP version of the MainActivity is the following (I deleted other unuseful lines for this example):
package com.vinaysomawat.careerhigh;
import android.Manifest;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Build;
import android.support.annotation.NonNull;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.ActionBarActivity;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.webkit.GeolocationPermissions;
import android.webkit.WebChromeClient;
public class MainActivity extends ActionBarActivity {
public String mGeolocationOrigin;
public GeolocationPermissions.Callback mGeolocationCallback;
private static final int REQUEST_FINE_LOCATION=0;
public class GeoWebViewClient extends WebViewClient {
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
// When user clicks a hyperlink, load in the existing WebView
view.loadUrl(url);
return true;
}
}
public class GeoWebChromeClient extends WebChromeClient {
@Override
public void onGeolocationPermissionsShowPrompt(String origin,
GeolocationPermissions.Callback callback) {
String perm = Manifest.permission.ACCESS_FINE_LOCATION;
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M ||
ContextCompat.checkSelfPermission(MainActivity.this, perm) == PackageManager.PERMISSION_GRANTED) {
// we're on SDK < 23 OR user has already granted permission
callback.invoke(origin, true, false);
} else {
if (!ActivityCompat.shouldShowRequestPermissionRationale(MainActivity.this, perm)) {
// ask the user for permission
ActivityCompat.requestPermissions(MainActivity.this, new String[] {perm}, REQUEST_FINE_LOCATION);
// we will use these when user responds
mGeolocationOrigin = origin;
mGeolocationCallback = callback;
}
}}
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
switch (requestCode) {
case REQUEST_FINE_LOCATION:
boolean allow = false;
if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// user has allowed this permission
allow = true;
}
if (mGeolocationCallback != null) {
// call back to web chrome client
mGeolocationCallback.invoke(mGeolocationOrigin, allow, false);
}
break;
}
}
WebView mywebview;
@Override
protected void onCreate(Bundle savedInstanceState) {
//Hide TopBar
getSupportActionBar().hide();
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mywebview = (WebView)findViewById(R.id.webView);
WebSettings webSettings = mywebview.getSettings();
// Empty Cache
mywebview.clearCache(true);
// Hide Zoom buttons
mywebview.getSettings().setDisplayZoomControls(false);
mywebview.getSettings().setJavaScriptCanOpenWindowsAutomatically(true);
mywebview.getSettings().setBuiltInZoomControls(true);
mywebview.setWebViewClient(new GeoWebViewClient());
// Below required for geolocation
mywebview.getSettings().setJavaScriptEnabled(true);
mywebview.getSettings().setGeolocationEnabled(true);
mywebview.setWebChromeClient(new GeoWebChromeClient());
mywebview.setWebChromeClient(new WebChromeClient() {
@Override
public boolean onCreateWindow(WebView view, boolean dialog, boolean userGesture, android.os.Message resultMsg)
{
WebView.HitTestResult result = view.getHitTestResult();
String data = result.getExtra();
Context context = view.getContext();
Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(data));
context.startActivity(browserIntent);
return false;
}
});
mywebview.loadUrl("https://mycurrentlocation.net/");
}
@Override
public void onBackPressed(){
if(mywebview.canGoBack()) {
mywebview.goBack();
} else
{
super.onBackPressed();
}
}
@Override
public boolean onCreateOptionsMenu(Menu menu){
getMenuInflater().inflate(R.menu.menu_main, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item){
int id = item.getItemId();
if(id == R.id.action_settings){
return true;
}
return super.onOptionsItemSelected(item);
}
}
When I click the button to show my current position, in Android Studio I get the following log:
E/Resource: printErrorResource, maybe not a error because module has entative access to resource called by =android.content.res.HwResourcesImpl.printErrorResource:2634 android.content.res.ResourcesImpl.openRawResource:444 android.content.res.Resources.openRawResource:1418 android.content.res.Resources.openRawResource:1362 android.support.v4.graphics.TypefaceCompatBaseImpl.createFromResourcesFontFile:304 android.support.v4.graphics.TypefaceCompat.createFromResourcesFontFile:232 I/ResourcesImplEx: The apk asset path = ApkAssets{path=/system/framework/framework-res.apk} The apk asset path = ApkAssets{path=/system/framework/framework-res-hwext.apk} The apk asset path = ApkAssets{path=/preas/oversea/overlay/GmsConfigOverlay.apk} The apk asset path = ApkAssets{path=/preas/oversea/overlay/GmsGsaConfigOverlay.apk} The apk asset path = ApkAssets{path=/preas/oversea/overlay/GoogleExtServicesConfigOverlay.apk} The apk asset path = ApkAssets{path=/preas/oversea/overlay/GoogleModuleMetadataConfigOverlay.apk} The apk asset path = ApkAssets{path=/preas/oversea/overlay/GooglePermissionControllerConfigOverlay.apk} The apk asset path = ApkAssets{path=/hw_product/overlay/frameworkResOverlay.apk} The apk asset path = ApkAssets{path=/odm/overlay/frameworkResOverlay.apk} The apk asset path = ApkAssets{path=/data/app/com.vinaysomawat.careerhigh-3JvsuNOhaJTzUZrEdMaQnA==/base.apk} I/ResourcesImplEx: The apk asset path = ApkAssets{path=/data/app/com.google.android.webview-Lw6x1ElnmD4d1UEVSG95iA==/base.apk} The apk asset path = ApkAssets{path=/data/app/com.google.android.webview-Lw6x1ElnmD4d1UEVSG95iA==/split_config.it.apk} The apk asset path = ApkAssets{path=/data/app/com.google.android.webview-Lw6x1ElnmD4d1UEVSG95iA==/split_weblayer.apk} The apk asset path = ApkAssets{path=/data/app/com.google.android.trichromelibrary_489608833-ndmJcgRaxXQPmZ7r_KqT-w==/base.apk}
and then this log is shown (I think this is the error banner that say that can't load the position):
D/HiTouch_PressGestureDetector: onAttached, package=com.vinaysomawat.careerhigh, windowType=2, mHiTouchRestricted=false D/mali_winsys: EGLint new_window_surface(egl_winsys_display *, void *, EGLSurface, EGLConfig, egl_winsys_surface **, EGLBoolean) returns 0x3000
Yes. I had a similar problem. Location permissions for an android webview and geolocation are no longer handled by the webChromeClient. They are now handled in the same way as an Android native app. I used the following as a guide and it worked well.