I have website using javascript geolocation api and want it to open in a webview. I set up these permissions in the manifest file:
<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"/>
In my activity, I also set webview settings:
webview.getSettings().setDatabaseEnabled(true);
webview.getSettings().setDomStorageEnabled(true);
webview.getSettings().setGeolocationDatabasePath("/data/data/com.my.app/databases");
webview.getSettings().setGeolocationEnabled(true);
And I also handled javascript geolocation dialog:
webview.setWebChromeClient(new WebChromeClient(){
#Override
public void onGeolocationPermissionsShowPrompt(String origin, GeolocationPermissions.Callback callback) {
callback.invoke(origin, true, false);
}
});
Geolocation itself is working, but I am not able to cache geoposition into the database. It should be doing it by itself into CachedGeoposition.db, but when I start the webview, i get this strange SQLite error:
E/SQLiteLog(22653): (14) cannot open file at line 30174 of [00bb9c9ce4]
E/SQLiteLog(22653): (14) os_unix.c:30174: (2) open(/CachedGeoposition.db) -
D/WebKit (22653): ERROR:
D/WebKit (22653): SQLite database failed to load from /CachedGeoposition.db
D/WebKit (22653): Cause - unable to open database file
D/WebKit (22653):
D/WebKit (22653): external/webkit/Source/WebCore/platform/sql/SQLiteDatabase.cp
p(71) : bool WebCore::SQLiteDatabase::open(const WTF::String&, bool)
When I check existence of CachedGeoposition.db file in File Explorer, it is always there,
and permissions of the db are set to -rw-------.
Did I miss something in settings what could cause database not to open correctly?
I'm new to Android and trying to find solution for 2 days now. Any help would be greatly appreciated. Thank you.
I'll post a solution that worked for me in the hope that it might provide some ideas on where the error might be. It would also help if you could provide details about the device(emulator)/os on which you're testing.
I tested on (with no errors):
(Emulator) Galaxy Nexus (2.3.3) (data/data/package.name/files/CachedGeoposition.db file created)
(Emulator) Galaxy Nexus (3.0) (data/data/package.name/files/CachedGeoposition.db file created)
HTC One S (4.1.1) (data/data/package.name/files/CachedGeoposition.db file created)
Galaxy S3 (4.3) (couldn't see the file since my phone is not rooted and there are some 'run-as' bugs on 4.3)
Nexus 7 (4.4.4) - the webview above KIT KAT has changed a bit and the
file is no longer there, but no error was shown (even when providing a wrong 'databases' folder path)
(Emulator) Galaxy S4 (5.0) (same as on 4.4.4)
AndroidManifest permissions (pretty much the same):
<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"/>
Web view settings:
WebSettings webSettings = webview.getSettings();
webSettings.setJavaScriptEnabled(true);
webSettings.setDatabaseEnabled(true);
webSettings.setDomStorageEnabled(true);
webSettings.setGeolocationDatabasePath(getFilesDir().getPath());
webSettings.setGeolocationEnabled(true);
Web chrome client (the same):
webview.setWebChromeClient(new WebChromeClient(){
#Override
public void onGeolocationPermissionsShowPrompt(String origin, GeolocationPermissions.Callback callback) {
callback.invoke(origin, true, false);
}
});
Test html file for getting the geolocation:
<!DOCTYPE html>
<html>
<head>
<script>
var x = document.getElementById("demo");
function getLocation() {
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(showPosition, showError);
} else {
x.innerHTML = "Geolocation is not supported by this browser.";
}
}
function showPosition(position) {
var latlon = position.coords.latitude + "," + position.coords.longitude;
document.getElementById("locationHolder").innerHTML = latlon;
}
function showError(error) {
switch(error.code) {
case error.PERMISSION_DENIED:
x.innerHTML = "User denied the request for Geolocation.";
break;
case error.POSITION_UNAVAILABLE:
x.innerHTML = "Location information is unavailable";
break;
case error.TIMEOUT:
x.innerHTML = "The request to get user location timed out.";
break;
case error.UNKNOWN_ERROR:
x.innerHTML = "An unknown error occurred.";
break;
}
}
</script>
</head>
<body>
<p id="demo">Click the button to get your position.</p>
<div id="locationHolder">No location</div>
<button onclick="getLocation()">Get position</button>
</body>
</html>
Also, for a local test, you could created a file containing the html under '/assets/www/index.html' and use the following code to load it into the webview:
try {
String html = readAssetFile("www/index.html");
webview.loadDataWithBaseURL(null, html, "text/html", "UTF-8", null);
} catch (IOException e) {
}
Read asset file method:
private String readAssetFile(String filePath) throws IOException {
StringBuilder buffer = new StringBuilder();
InputStream fileInputStream = getAssets().open(filePath);
BufferedReader bufferReader = new BufferedReader(new InputStreamReader(fileInputStream, "UTF-8"));
String str;
while ((str=bufferReader.readLine()) != null) {
buffer.append(str);
}
fileInputStream.close();
return buffer.toString();
}
I couldn't reproduce your error without providing a wrong hardcoded path to the 'databases' folder.
The line
D/WebKit (22653): SQLite database failed to load from /CachedGeoposition.db
is interesting
Try setting the appropriate DB path in WebSettings.setDatabasePath as well, it could be that this is used as the root for the geo db path
I know this is an old question but anyone with this issue might find interesting focusing on this function:
mWebView.getSettings().setGeolocationDatabasePath(getFilesDir().getPath());
Geolocation uses databases to persist cached positions and permissions between sessions.
This call sets the location of the database.
Instead of setting a fixed path as parameter you should get it dynamically calling:
getFilesDir().getPath()
Related
i have created webview and updated android:hardwareAccelerated="true" in order to run embedded video tag but; only sound is running without video
mWebview = findViewById(R.id.webView);
mWebview.getSettings().setJavaScriptEnabled(true);
mChromeClient = new MyChromeClient(this);
mWebview.setWebChromeClient(mChromeClient);
mWebview.getSettings().setLoadWithOverviewMode(true);
mWebview.getSettings().setMediaPlaybackRequiresUserGesture(false);
mWebview.getSettings().setPluginState(WebSettings.PluginState.ON);
mWebview.getSettings().setRenderPriority(WebSettings.RenderPriority.HIGH);
mWebview.getSettings().setCacheMode(WebSettings.LOAD_NO_CACHE);
mWebview.getSettings().setDomStorageEnabled(true);
mWebview.getSettings().setAllowFileAccess(true);
mWebview.getSettings().setSavePassword(false);
mWebview.getSettings().setUseWideViewPort(true);
mWebview.getSettings().setBuiltInZoomControls(false);
mWebview.getSettings().setSupportZoom(false);
mWebview.getSettings().setNeedInitialFocus(false);
mWebview.getSettings().setLoadWithOverviewMode(true);
mWebview.getSettings().setLayoutAlgorithm(WebSettings.LayoutAlgorithm.NORMAL);
mWebview.getSettings().setLoadsImagesAutomatically(true);
CookieManager.getInstance().setAcceptCookie(true);
if (Build.VERSION.SDK_INT >= 17) {
mWebview.getSettings().setMediaPlaybackRequiresUserGesture(false);
}
mWebview.setLayerType(1, null);
mWebview.setWebViewClient(new MyWebviewClient());
String myvar = "";
String head = "<!DOCTYPE html><html><head><meta name=\"viewport\" content=\"width=device-width, user-scalable=yes\" /></head>";
String summary = "<style>table{ height:100%;}td.height{height:100%;}</style><table width=100% height=100%> <tr><td class=\"height\" style=\"text-align: center; vertical-align: middle;\"><video id='my-video' poster=\"\" controls autoplay style=\"width: 300px; height: 250px;vertical-align: middle;\"><source src='http://techslides.com/demos/sample-videos/small.mp4' type='video/mp4' /></video></td></tr></table><script>var myvideo = document.getElementsByTagName('video')[0]; myvideo.play()</script>";
String html = head + "<body style='background-color:#000000;'>" + summary + "</body></html>";
mWebview.loadData(html, "text/html", null);
adb log:
09/16 22:39:14: Launching app
$ adb install-multiple -r -t -p com.example.ad.myapplication F:\new\MyApplication\app\build\outputs\apk\debug\app-debug.apk
Split APKs installed
$ adb shell am start -n "com.example.ad.myapplication/com.example.ad.myapplication.MainActivity" -a android.intent.action.MAIN -c android.intent.category.LAUNCHER
Client not ready yet..Waiting for process to come online
Connected to process 3426 on device emulator-5554
Capturing and displaying logcat messages from application. This behavior can be disabled in the "Logcat output" section of the "Debugger" settings page.
D/MediaResourceGetter: no ethernet/wifi connection detected
W/MediaResourceGetter: non-file URI can't be read due to unsuitable network conditions
E/MediaResourceGetter: Unable to configure metadata extractor
[ 09-16 22:39:18.039 3426: 3478 D/ ]
HostConnection::get() New Host Connection established 0xa0f9b0b0, tid 3478
D/MediaPlayer: Couldn't open file on client side, trying server side
permissions :
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
Please advice how to display video with sound not only sound i tried all my known solutions
Thx in advance
"How to display video together with sound, not just sound only..."
Don't use percentages for width/height. Use real numbers.
Test this setup and see if it displays a picture with sound:
String myvar = "";
String head = "<!DOCTYPE html><html><head><meta name=\"viewport\" content=\"width=device-width, user-scalable=yes\" /></head>";
String summary = "<video id='my-video' controls autoplay><source src='http://techslides.com/demos/sample-videos/small.mp4' width='300' height='250' type='video/mp4' /></video>";
String html = head + "<body style='background-color:#000000;'>" + summary + "</body><script>var myvideo = document.getElementsByTagName('video')[0]; myvideo.play()</script></html>";
mWebview.loadData(html, "text/html", null);
Also note: <script> happens after <body> since the script needs that video element to exist first.
In my case I am trying to read a text file by Ajax from within a WebView of an Android app.
This is a JS code:
$.ajax({
url: "data.txt",
dataType: 'text',
success: function (data) {
var response = data;
},
error: function (e) {
alert("Response error: " + e.status);
}
});
I tried it in different ways - synchronous and asynchronous, XmlHTTP and $.ajax and always e.status = 0;
As to Android's permissions, here they are:
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
webView.getSettings().setJavaScriptEnabled(true);
webView.getSettings().setDomStorageEnabled(true);
webView.getSettings().getAllowFileAccess();
webView.getSettings().getAllowFileAccessFromFileURLs();
webView.getSettings().getAllowUniversalAccessFromFileURLs();
webView.getSettings().setAllowContentAccess(true);
webView.getSettings().getAllowContentAccess();
webView.getSettings().setAllowFileAccess(true);
webView.getSettings().setAllowFileAccessFromFileURLs(true);
webView.getSettings().setAllowUniversalAccessFromFileURLs(true);
What kind of problem this is?
I have a webview which loads an offline HTML . My offline HTML contains
Text
Local Images
Embedded videos
All works fine but when user doesn't have the internet connection the 'text' and 'images' load fine but 'embedded videos' part shows an ugly NO INTERNET CONNECTION error.
My question is how do I handle this error and replace it with my own custom error.
I want to keep showing all the other contents in my html even if there is no internet connection but replace embedded video error message with custom error message.
Anyone knows how to achieve this?
Cheers
My webview code
webView.ClearCache(true);
webView.ClearHistory();
string HTML_DATA = "";
if (File.Exists(localPath))
{
string HTML_LOCAL = File.ReadAllText(localPath);
HTML_DATA = HTML_LOCAL;
}
webView.Settings.JavaScriptEnabled = true;
webView.Settings.LoadWithOverviewMode = true;
webView.Settings.UseWideViewPort = true;
webView.SetWebViewClient(new WebViewClientClass());
webView.LoadDataWithBaseURL("file:///android_asset/", HTML_DATA, "text/html", "UTF-8", null);
Now the results are the following
With internet
Without internet
My question is how do I handle this error and replace it with my own custom error.
You can handle it in WebViewClient like codes below, but it will replace the whole page of WebView with your error page:
public class MyClient:WebViewClient
{
public Context _context;
public MyClient(Context c)
{
_context = c;
}
public override void OnReceivedError(WebView view, IWebResourceRequest request, WebResourceError error)
{
base.OnReceivedError(view, request, error);
var errMsg = error.DescriptionFormatted;
Toast.MakeText(_context, errMsg, ToastLength.Long).Show();
string htmlData = $"<html><body><div align='center'>This is the description for the load fail : {errMsg}</div></body>";
view.LoadUrl("about:blank");
view.LoadDataWithBaseURL(null, htmlData, "text/html", "UTF-8", null);
view.Invalidate();
}
...
Alternatively, you can handle the error in Html, you can define an error window with the same size of your webview, and hide/show it according to the internet states:
<DOCTYPE>
<html>
<head></head>
<body>
<iframe id="mFrame" width="854" height="480" src="https://www.youtube.com/embed/a5GMRrEJaVo" frameborder="0" allow="autoplay; encrypted-media" allowfullscreen></iframe>
<div id="errorWindow" width="854" height="480">
This is the div for your custom error view
</div>
<script>
(function(){
"use strict"
var mIframe=document.getElementById("mFrame");
var errorWindow=document.getElementById("errorWindow");
if(navigator.onLine)
{
//internet available
mIframe.hidden=false;
errorWindow.hidden=true;
}else
{
//internet not available
mIframe.hidden=true;
errorWindow.hidden=false;
}
})()
</script>
</body>
Notes: you will have to define following permission to have navigator.onLine work:
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
I'm trying to load a test web page (in my server). Page is:
<!DOCTYPE html>
<html>
<head>
<title>Test</title>
</head>
<body>
<iframe width="420" height="315" src="http://www.youtube.com/embed/XGSy3_Czz8k?autoplay=1"/>
</body>
</html>
But webView is not loading page. Not even onProgressChanged called after %40-50
Also, this problem occurs all sites that loads js script from url. Including youtube, fb etc.
WebConsole: XMLHttpRequest cannot load https://googleads.g.doubleclick.net/pagead/id. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'https://www.youtube.com' is therefore not allowed access.
Here my settings
FrameLayout contentFrame = (FrameLayout) findViewById(R.id.ContentFrame);
WebView mWebView = new WebView(this);
mWebView.setWebChromeClient(new WebChromeClient());
mWebView.setWebViewClient(new WebViewClient());
mWebView.getSettings().setJavaScriptEnabled(true);
mWebView.getSettings().setAllowUniversalAccessFromFileURLs(true);
mWebView.getSettings().setAllowFileAccessFromFileURLs(true);
mWebView.loadUrl("http://ozgur.dk/browser.html");
contentFrame.removeAllViews();
contentFrame.addView(mWebView);
Layout:
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
android:id="#+id/ContentFrame"
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
You can solve this by enabling a WebSetting called setAllowUniversalAccessFromFileURLs
This is happening on the Javascript layer.
You can read up about it here : CORS
Are you sure you are not pausing timers in somewhere? Because this happens when you call mWebView.pauseTimers() when page loading.
You're trying to do a cross-domain request, which is impossible since it's on a different domain than your page is on.
There is however a workaround that.
Using CORS - tutorial by Monsur Hossain
An example using CORS (By Monsur Hossain):
function createCORSRequest(method, url) {
var xhr = new XMLHttpRequest();
if ("withCredentials" in xhr) {
// Check if the XMLHttpRequest object has a "withCredentials" property.
// "withCredentials" only exists on XMLHTTPRequest2 objects.
xhr.open(method, url, true);
} else if (typeof XDomainRequest != "undefined") {
// Otherwise, check if XDomainRequest.
// XDomainRequest only exists in IE, and is IE's way of making CORS requests.
xhr = new XDomainRequest();
xhr.open(method, url);
} else {
// Otherwise, CORS is not supported by the browser.
xhr = null;
}
return xhr;
}
var xhr = createCORSRequest('GET', url);
if (!xhr) {
throw new Error('CORS not supported');
}
As a side note, if you want to run JavaScript on Android:
Execute JavaScript in Android without WebView - tutorial by Wesley Lin
An example using Rhino (by Wesley Lin):
Object[] params = new Object[] { "javaScriptParam" };
// Every Rhino VM begins with the enter()
// This Context is not Android's Context
Context rhino = Context.enter();
// Turn off optimization to make Rhino Android compatible
rhino.setOptimizationLevel(-1);
try {
Scriptable scope = rhino.initStandardObjects();
// Note the forth argument is 1, which means the JavaScript source has
// been compressed to only one line using something like YUI
rhino.evaluateString(scope, javaScriptCode, "JavaScript", 1, null);
// Get the functionName defined in JavaScriptCode
Object obj = scope.get(functionNameInJavaScriptCode, scope);
if (obj instanceof Function) {
Function jsFunction = (Function) obj;
// Call the function with params
Object jsResult = jsFunction.call(rhino, scope, scope, params);
// Parse the jsResult object to a String
String result = Context.toString(jsResult);
}
} finally {
Context.exit();
}
Starting with Android 9 (API level 28), cleartext support is disabled by default.
Better install security certificate on your server.
still
To circumvent add following line in Manifest
<application
...
android:label="#string/app_name"
android:usesCleartextTraffic="true"
...
viola...
Using Cord-ova to get device position I experience a weird behavior when trying to access position coordinates using the emulator (also deployed the code on my Galaxy S2 ... same issue).
document.addEventListener( "deviceready", onDeviceReady, false);
function onDeviceReady () {
if (navigator.geolocation) {
var options = { timeout: 10000, enableHighAccuracy: true, maximumAge: Infinity };
navigator.geolocation.getCurrentPosition(locationOnSuccess, locationOnSuccess, options);
} else {
alert("Geoloc does not exists");
}
}
var locationOnSuccess = function (pos){
alert('Latitude: ' + pos.coords.latitude);
}
navigator.geolocation exists as it fires the alert
This says latitude undefined and error I get is
11-13 08:44:05.296: D/CordovaLog(1861): file:///android_asset/www/index.html: Line 55 : Uncaught TypeError: Cannot read property 'latitude' of undefined
11-13 08:44:05.296: E/Web Console(1861): Uncaught TypeError: Cannot read property 'latitude' of undefined at file:///android_asset/www/index.html:55
I'm struggling for 1 day now and cannot find anybody having same issue, probably getting blind at looking for too long?
Anybody to help?
Many thanks, S.
Make sure you have enabled this:
(in app/res/xml/config.xml)
<feature name="Geolocation">
<param name="android-package" value="org.apache.cordova.GeoBroker" />
</feature>
(in app/AndroidManifest.xml)
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_LOCATION_EXTRA_COMMANDS" />
also check if your device supports Geolocation
In PhoneGap's documentation the statement to get location updates is defined like the one below.
navigator.geolocation.getCurrentPosition(onSuccess, onError);
So there are success and error cases. But you are calling the same method for both of the cases. So in the case that you don't have geo locations you cannot get the latitude and longitude values. That could cause the error. Instead of showing the error case in else statement, define an error function that shows this in the error case.
First of all change it to locationOnError
navigator.geolocation.getCurrentPosition(locationOnSuccess, locationOnError, options);
navigator.geolocation.watchPosition(coordinates);
function coordinates(p) {
if (lastTrackedLat == null) {
lastTrackedLat = p.coords.latitude;
}
if (lastTrackedLng == null) {
lastTrackedLng = p.coords.longitude;
}
var lastTrackedLatlng = new google.maps.LatLng(lastTrackedLat,lastTrackedLng);
var lat = p.coords.latitude;
var lng = p.coords.longitude;
var Latlng=new google.maps.LatLng(lat,lng);
}