I'm currently having an issue with geolocation in a webview. I have a webapp. I'm currently not using phonegap or any other mobile framework. I've been unsuccessful at getting the built-in html5 geolocation javascript api to work on an application that runs in a webview in an android app. The site works fine otherwise from the chrome browser on android 2.0+ (geolocation supported).
I'm compiling against android api version 5.
I've read this post already
Phonegap's solution of writing a proxy which wraps the built in call and uses the host activity instead is good, but I'd prefer to use the built in to the webview (webkit) without using phone gap.
I've set the proper permissions in the manifest file:
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_GPS" />
<uses-permission android:name="android.permission.ACCESS_ASSISTED_GPS" />
<uses-permission android:name="android.permission.ACCESS_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
Here is an example code snippet:
webview = (WebView) findViewById(R.id.webview);
pbarDialog = new ProgressDialog(this);
pbarDialog.setCancelable(false);
pbarDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
webview.setWebViewClient(new MyWebViewClient());
webview.getSettings().setJavaScriptEnabled(true);
webview.setWebChromeClient(new MyChromeWebViewClient());
webview.setVerticalScrollBarEnabled(false);
WebSettings webSettings = webview.getSettings();
webSettings.setSavePassword(true);
webSettings.setSaveFormData(true);
webSettings.setJavaScriptEnabled(true);
webSettings.setSupportZoom(false);
webSettings.setJavaScriptCanOpenWindowsAutomatically(true);
webSettings.setGeolocationEnabled(true);
...
private class MyChromeWebViewClient extends WebChromeClient {
@Override
public void onProgressChanged(WebView view, int progress) {
// Activities and WebViews measure progress with different scales.
// The progress meter will automatically disappear when we reach 100%
activity.setProgress(progress * 100);
}
@Override
public boolean onJsAlert(WebView view, String url, String message, JsResult result) {
Log.d(LOG_TAG, message);
// This shows the dialog box. This can be commented out for dev
AlertDialog.Builder alertBldr = new AlertDialog.Builder(activity);
alertBldr.setMessage(message);
alertBldr.setTitle("Alert");
alertBldr.show();
result.confirm();
return true;
}
}
private class MyWebViewClient extends WebViewClient {
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
view.loadUrl(url);
return true;
}
@Override
public void onReceivedError(WebView view, int errorCode,
String description, String failingUrl) {
}
}
Has anyone else had issues with getting a web application to work in the webview?
The Geolocation API is accessed via a call to navigator. geolocation ; this will cause the user's browser to ask them for permission to access their location data. If they accept, then the browser will use the best available functionality on the device to access this information (for example, GPS).
Q 6 - Which of the following is correct about geolocation api in HTML5? A - HTML5 Geolocation API lets you share your location with your favorite web sites.
HTML5 geolocation lets you find the geographic coordinates of a website visitor's current location like the longitude and the latitude. The information provided by HTML 5 geolocation can be used to optimise the browsing experience.
Sharing my Working activity class, this is a complete solution which can demonstrate
Hope it save someone's time
/**
* Created by Hitesh.Sahu on 3/24/2017.
*/
public class WebViewActivity extends AppCompatActivity {
final private int REQUEST_CODE_ASK_MULTIPLE_PERMISSIONS = 124;
private int webViewPreviousState;
private final int PAGE_STARTED = 0x1;
private final int PAGE_REDIRECTED = 0x2;
private CoordinatorLayout rootView;
private WebView webView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_webview);
webView = (WebView) findViewById(R.id.webView);
rootView = (CoordinatorLayout) findViewById(R.id.root_view);
if (Build.VERSION.SDK_INT >= 23) {
// Marshmallow+ Permission APIs
fuckMarshMallow();
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
if (0 != (getApplicationInfo().flags & ApplicationInfo.FLAG_DEBUGGABLE)) {
WebView.setWebContentsDebuggingEnabled(true);
}
}
webView.setInitialScale(1);
webView.getSettings().setLoadWithOverviewMode(true);
webView.getSettings().setUseWideViewPort(true);
webView.setScrollBarStyle(WebView.SCROLLBARS_OUTSIDE_OVERLAY);
webView.setScrollbarFadingEnabled(false);
webView.getSettings().setJavaScriptCanOpenWindowsAutomatically(true);
webView.getSettings().setBuiltInZoomControls(true);
webView.setWebViewClient(new GeoWebViewClient());
// Below required for geolocation
webView.getSettings().setJavaScriptEnabled(true);
webView.getSettings().setGeolocationEnabled(true);
webView.setWebChromeClient(new GeoWebChromeClient());
webView.getSettings().setAppCacheEnabled(true);
webView.getSettings().setDatabaseEnabled(true);
webView.getSettings().setDomStorageEnabled(true);
webView.getSettings().setGeolocationDatabasePath(getFilesDir().getPath());
webView.loadUrl("file:///android_asset/index.html");
}
/**
* WebChromeClient subclass handles UI-related calls
* Note: think chrome as in decoration, not the Chrome browser
*/
public class GeoWebChromeClient extends android.webkit.WebChromeClient {
@Override
public void onGeolocationPermissionsShowPrompt(final String origin,
final GeolocationPermissions.Callback callback) {
// Always grant permission since the app itself requires location
// permission and the user has therefore already granted it
callback.invoke(origin, true, false);
// final boolean remember = false;
// AlertDialog.Builder builder = new AlertDialog.Builder(WebViewActivity.this);
// builder.setTitle("Locations");
// builder.setMessage("Would like to use your Current Location ")
// .setCancelable(true).setPositiveButton("Allow", new DialogInterface.OnClickListener() {
// public void onClick(DialogInterface dialog, int id) {
// // origin, allow, remember
// callback.invoke(origin, true, remember);
// }
// }).setNegativeButton("Don't Allow", new DialogInterface.OnClickListener() {
// public void onClick(DialogInterface dialog, int id) {
// // origin, allow, remember
// callback.invoke(origin, false, remember);
// }
// });
// AlertDialog alert = builder.create();
// alert.show();
}
}
/**
* WebViewClient subclass loads all hyperlinks in the existing WebView
*/
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;
}
Dialog loadingDialog = new Dialog(WebViewActivity.this);
@Override
public void onPageStarted(WebView view, String url, Bitmap favicon) {
super.onPageStarted(view, url, favicon);
webViewPreviousState = PAGE_STARTED;
if (loadingDialog == null || !loadingDialog.isShowing())
loadingDialog = ProgressDialog.show(WebViewActivity.this, "",
"Loading Please Wait", true, true,
new DialogInterface.OnCancelListener() {
@Override
public void onCancel(DialogInterface dialog) {
// do something
}
});
loadingDialog.setCancelable(false);
}
@RequiresApi(api = Build.VERSION_CODES.M)
@Override
public void onReceivedError(WebView view, WebResourceRequest request,
WebResourceError error) {
if (isConnected()) {
final Snackbar snackBar = Snackbar.make(rootView, "onReceivedError : " + error.getDescription(), Snackbar.LENGTH_INDEFINITE);
snackBar.setAction("Reload", new View.OnClickListener() {
@Override
public void onClick(View view) {
webView.loadUrl("javascript:window.location.reload( true )");
}
});
snackBar.show();
} else {
final Snackbar snackBar = Snackbar.make(rootView, "No Internet Connection ", Snackbar.LENGTH_INDEFINITE);
snackBar.setAction("Enable Data", new View.OnClickListener() {
@Override
public void onClick(View view) {
startActivityForResult(new Intent(Settings.ACTION_WIRELESS_SETTINGS), 0);
webView.loadUrl("javascript:window.location.reload( true )");
snackBar.dismiss();
}
});
snackBar.show();
}
super.onReceivedError(view, request, error);
}
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
@Override
public void onReceivedHttpError(WebView view,
WebResourceRequest request, WebResourceResponse errorResponse) {
if (isConnected()) {
final Snackbar snackBar = Snackbar.make(rootView, "HttpError : " + errorResponse.getReasonPhrase(), Snackbar.LENGTH_INDEFINITE);
snackBar.setAction("Reload", new View.OnClickListener() {
@Override
public void onClick(View view) {
webView.loadUrl("javascript:window.location.reload( true )");
}
});
snackBar.show();
} else {
final Snackbar snackBar = Snackbar.make(rootView, "No Internet Connection ", Snackbar.LENGTH_INDEFINITE);
snackBar.setAction("Enable Data", new View.OnClickListener() {
@Override
public void onClick(View view) {
startActivityForResult(new Intent(Settings.ACTION_WIRELESS_SETTINGS), 0);
webView.loadUrl("javascript:window.location.reload( true )");
snackBar.dismiss();
}
});
snackBar.show();
}
super.onReceivedHttpError(view, request, errorResponse);
}
@Override
public void onPageFinished(WebView view, String url) {
if (webViewPreviousState == PAGE_STARTED) {
if (null != loadingDialog) {
loadingDialog.dismiss();
loadingDialog = null;
}
}
}
}
/**
* Check if there is any connectivity
*
* @return is Device Connected
*/
public boolean isConnected() {
ConnectivityManager cm = (ConnectivityManager)
this.getSystemService(Context.CONNECTIVITY_SERVICE);
if (null != cm) {
NetworkInfo info = cm.getActiveNetworkInfo();
return (info != null && info.isConnected());
}
return false;
}
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
switch (requestCode) {
case REQUEST_CODE_ASK_MULTIPLE_PERMISSIONS: {
Map<String, Integer> perms = new HashMap<String, Integer>();
// Initial
perms.put(Manifest.permission.ACCESS_FINE_LOCATION, PackageManager.PERMISSION_GRANTED);
// Fill with results
for (int i = 0; i < permissions.length; i++)
perms.put(permissions[i], grantResults[i]);
// Check for ACCESS_FINE_LOCATION
if (perms.get(Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED
) {
// All Permissions Granted
// Permission Denied
Toast.makeText(WebViewActivity.this, "All Permission GRANTED !! Thank You :)", Toast.LENGTH_SHORT)
.show();
} else {
// Permission Denied
Toast.makeText(WebViewActivity.this, "One or More Permissions are DENIED Exiting App :(", Toast.LENGTH_SHORT)
.show();
finish();
}
}
break;
default:
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
}
}
@TargetApi(Build.VERSION_CODES.M)
private void fuckMarshMallow() {
List<String> permissionsNeeded = new ArrayList<String>();
final List<String> permissionsList = new ArrayList<String>();
if (!addPermission(permissionsList, Manifest.permission.ACCESS_FINE_LOCATION))
permissionsNeeded.add("Show Location");
if (permissionsList.size() > 0) {
if (permissionsNeeded.size() > 0) {
// Need Rationale
String message = "App need access to " + permissionsNeeded.get(0);
for (int i = 1; i < permissionsNeeded.size(); i++)
message = message + ", " + permissionsNeeded.get(i);
showMessageOKCancel(message,
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
requestPermissions(permissionsList.toArray(new String[permissionsList.size()]),
REQUEST_CODE_ASK_MULTIPLE_PERMISSIONS);
}
});
return;
}
requestPermissions(permissionsList.toArray(new String[permissionsList.size()]),
REQUEST_CODE_ASK_MULTIPLE_PERMISSIONS);
return;
}
Toast.makeText(WebViewActivity.this, "No new Permission Required- Launching App .You are Awesome!!", Toast.LENGTH_SHORT)
.show();
}
private void showMessageOKCancel(String message, DialogInterface.OnClickListener okListener) {
new AlertDialog.Builder(WebViewActivity.this)
.setMessage(message)
.setPositiveButton("OK", okListener)
.setNegativeButton("Cancel", null)
.create()
.show();
}
@TargetApi(Build.VERSION_CODES.M)
private boolean addPermission(List<String> permissionsList, String permission) {
if (checkSelfPermission(permission) != PackageManager.PERMISSION_GRANTED) {
permissionsList.add(permission);
// Check for Rationale Option
if (!shouldShowRequestPermissionRationale(permission))
return false;
}
return true;
}
}
Add onGeolocationPermissionsShowPrompt()
to MyChromeWebViewClient
as below:
private class MyChromeWebViewClient extends WebChromeClient {
@Override
public void onProgressChanged(WebView view, int progress) {
// Activities and WebViews measure progress with different scales.
// The progress meter will automatically disappear when we reach 100%
activity.setProgress(progress * 100);
}
@Override
public boolean onJsAlert(WebView view, String url, String message, JsResult result) {
Log.d(LOG_TAG, message);
// This shows the dialog box. This can be commented out for dev
AlertDialog.Builder alertBldr = new AlertDialog.Builder(activity);
alertBldr.setMessage(message);
alertBldr.setTitle("Alert");
alertBldr.show();
result.confirm();
return true;
}
public void onGeolocationPermissionsShowPrompt(String origin, GeolocationPermissions.Callback callback) {
callback.invoke(origin, true, false);
}
}
You need to import "android.webkit.GeolocationPermissions
".
Add this permission:
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
This will work I guess.
Your problem may be the same problem with Android WebView using Geolocation.
I think HTML5 use local database, so you need add some HTML5 requirements like this
// HTML5 API flags
webView.getSettings().setAppCacheEnabled(true);
webView.getSettings().setDatabaseEnabled(true);
webView.getSettings().setDomStorageEnabled(true);
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With