Although issues with shouldOverrideUrlLoading() are well discussed in SO (I probably read most of them), I still didn't find a solution to what seems to me such a generic problem, that I believe has been solved.
The QUESTION: How to stop in shouldOverrideUrlLoading() for external links, e.g. "http://www.example.com", for pages I loaded with webView.loadDataWithBaseURL(), where baseUrl is "file:///..."?
My override method for shouldOverrideUrlLoading() is not called when external links are touched (clicked).
Here it says " URLs that can't be resolved against the base URL are dropped on the floor (you won't get any callbacks for them, neither shouldOverrideUrlLoading nor onPageStarted)."
Android developer site says here that "If you loaded the page by calling ... loadDataWithBaseURL(), then you will receive the shouldOverrideUrlLoading() callback for this type of link on the page."
This is my code:
public void loadEpub(final EpubInfo epubInfo)
{
post(new Runnable()
{
#Override
public void run()
{
epubBaseUrl = "file://" + epubInfo.path;
if (!epubBaseUrl.endsWith("/"))
epubBaseUrl += "/";
String path = epubBaseUrl + epubInfo.baseUrl;
String page = generatePage(epubInfo);
EpubWebView.super.loadDataWithBaseURL(path, page, "text/html", "UTF-8", null);
}
});
}
where my baseUrl (path) is "file:///storage/sdcard0/Android/data/...".
Many thanks in advance.
Pinpointed the problem, and found a solution.
If your page runs in an iframe, clicking on external (http://www...) links does NOT trigger shouldOverrideUrlLoading()! Unless you add target="_top"
So there are at least 2 possible solutions:
1) if you can, run without an iframe
2) if you add to each href, target="_top", shouldOverrideUrlLoading() WILL BE triggered.
I went for solution 2). The following JavaScript code adds the target attribute to each link.
function change_links_target()
{
var all_document_links = mFrameDocument.getElementsByTagName("a");
for (i = 0; i < all_document_links.length; i++){
all_document_links[i].setAttribute("target", "_top");
}
}
Related
My question looks stupid at first glance and all my searches pointed out window.location and other JS stuff or the externalWebPage plugin. That's not what I'm looking for.
From the JAVA code, when I catch one specific exception during execution of a custom plugin, I want to force the page to move to "logout.html". I don't want to execute callback.error() or to deal with the error inside code in my webpage in any ways. I only want my transaction to be cancel and a web resource to be loaded in the current web UI.
Is there any way to do that?
Thanks in advance.
The CordovaWebView offers a showWebPage function to load any url from the native code.
From the plugin you should be able to do
this.webView.showWebPage("logout.html", false, true, null);
Also offers loadUrl
this.webView.loadUrl("file:///android_asset/www/logout.html");
And you can also use loadUrl to execute javascript so you can run the window.location from there without a callback.
this.webView.loadUrl("javascript:window.location.href='logout.html'");
You will either need to add it to your main.js interface so that you can callup page changed () and handle it in your angular code.
This can be a simple onPageNeedsChanged Handler call where you retain the context on page change context and just call it whenever you need.
Or you can call the onError callback from the caller, if it is a consistent error callback context to move you there, but sounds like you don't want to do this route.
So the easiest answer then is to just launch your own Activity with a preloaded web url and a web view. You already have access to the activity, so just make your own native activity with a full web view in it, hard coded url and then launch your activity on error.
i.e. I do it for sending an email, but it could be your own activity
cordova.getActivity().startActivity(Intent.createChooser(emailIntent, "Send mail..."));
You may even be able to get a reference to the Cordova web view, but not positive on that, but I assume you could through the tree of objects.
Does that work for you needs?
If not can you elaborate on your hesitation to handle in the onerror callback. It's fairly straight forward. Maybe I can help you there as an alternative? Retaining the callingContext and just using callingContext.error(withkey or instructions or object) is not too bad.
A35ble.writeValueToPodCharacteristic(this.device.macAddress, true, this.bytesToSend,
function (response) {
console.log("Success: " + response);
callbackContext.device.notificationReceivedFromPod(callbackContext.device.arrayBufferToString(response));
},
function (response) {
console.log("ERROR: " + response);
alert("Error Sending NSM Message: " + response);
}
);
For example I made a cordova plugin called A35ble that manages my bluetooth stuff and in this response I just show alert.
What I'm trying to achieve is basically something like android web view's shouldOverrideUrlLoading. I'm loading a specific URL into the web view which in turn have a series of redirects. The goal is to intercept the last URL and handle it by the application instead of the WebView component. It seems that something similar is available for the ios platform (onShouldStartLoadWithRequest) but didn't get to the android yet (https://github.com/facebook/react-native/pull/6478).
The workaround I've tried is to use onNavigationStateChange, parsing the URL and stopping the web view loading if needed:
onNavigationStateChange = (navState) => {
const url = new URL(navState.url);
const lastSegment = url.pathname.substr(url.pathname.lastIndexOf('/') + 1);
// Handle the get token ourselvs
if (lastSegment === GETTOKEN) {
this.refs[WEBVIEW_REF].stopLoading();
this.getToken()
}
this.setState({
url: navState.url,
backButtonEnabled: navState.canGoBack
});
}
The problem is that the stopLoading function is not really working in my case. I see that while executing the async function getToken() the browser continues the loading of the last URL.
Any suggestion would be appreciated.
I need help regarding SSL trust. I am loading a payment gateway page in WebView. It is a POST request and I am passing a payload.
This is happening successfully till now.(Shown below)
mWebView = (Android.Webkit.WebView)Control;
string payload = "myPayload";
byte[] valTest = Encoding.UTF8.GetBytes(payload.ToCharArray(0, payload.Length));
mWebView.Settings.JavaScriptEnabled = true;
mWebView.Settings.DomStorageEnabled=true;
mWebView.SetWebViewClient(new MyWebViewClient(this));
mWebView.SetWebChromeClient(new ChromeClient());
mWebView.PostUrl("https://mypage", valTest);
After filling up the form and submit I am getting a callback in the OnReceivedSslError method of the WebViewClient class. Here I ask it to proceed (as per various forums). Once this is done I am not getting any success callback. I need to be able to read javascript values once I get a response from the webview. I do not know how this is to be achieved.
Shown below is the OnSSLErrorReceived Callback method.
public override void OnReceivedSslError(Android.Webkit.WebView view, SslErrorHandler handler, Android.Net.Http.SslError error)
{
base.OnReceivedSslError(view, handler, error);
handler.Proceed();
}
}
Expectations-
1. On submitting the form it should be trusted(it is a self-signed certificate).
2. Once this above step is done we are expecting a response from the webpage. We need to read javascript values from the page. How can I achieve this?
I do not see any SuccessCallback or ResponseCallback.
I use the below to read the JS values.But this is not working.
view.EvaluateJavascript("document.getElementById('test').value", new MyCallbackClass());
The above issue has been resolved. Calling handler.Proceed in OnSSLError method was fine but the issue was with calling the base inside this callback. Once I removed base.OnSSLError my code worked fine.
After the page finishes I am able to read JS values in the OnPageFinished callback method.
Also the evaluate JS function is also working and I get callbacks.
I am working on an app and using IntelXDK to build it. I need to play some sounds if conditions are met, first I have tried HTML5 with JS and on desketop it's working but when built, it has no sound...
First attempt - HTML5 & JS
<audio src="snd/yes.mp3" id="yes"></audio>
<audio src="snd/no.mp3" id="no"></audio>
if(condition) {
$('#yes').trigger("play");
} else {
$('#no').trigger("play");
}
Then I tried the native IntelXDK version that goes like this:
if(condition) {
intel.xdk.player.playSound("snd/yes.mp3");
} else {
intel.xdk.player.playSound("snd/no.mp3");
}
No only that it doesn't work but it also f***s up the rest of my code not allowing the popup and page change to trigger.
Does anyone know how to fix this ?
Do I need to preload the sounds before playing them?
**UPDATE
I just discovered that if I use the HTML5 Audio tags and I give links to the sounds inside my server, the sounds are working, but if I try to get the same sounds from my snd folder they won't work...why is that?
The problem is that your "root" is not where you think it is, so your "snd" directory is not being found. This problem varies, unfortunately, with each target, it is not something the XDK controls, it has to do with how Cordova apps are constructed. You can use these functions to locate the root of your files and that should help you make this work.
// getWebPath() returns the location of index.html
// getWebRoot() returns URI pointing to index.html
function getWebPath() {
"use strict" ;
var path = window.location.pathname ;
path = path.substring( 0, path.lastIndexOf('/') ) ;
return 'file://' + path ;
}
function getWebRoot() {
"use strict" ;
var path = window.location.href ;
path = path.substring( 0, path.lastIndexOf('/') ) ;
return path ;
}
Have a little program with a WebView loading e.g. "www.google.com". I have tried to start searching automatic without ENTER by user, after scanning a barcode, like this:
final String scanedCode = "123456";
runOnUiThread(new Runnable() {
#Override
public void run() {
dispatchKeyEvent(new KeyEvent(100, scanedCode, 1, 0));
dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_ENTER));
}
);
The number "123456" will displayed in google search field, but the ENTER, or rather the automatic search is not started.
Have tried many ways and searched here, but I still cannot get these simple thing. Could someone give me some tips?
A more robust approach will be to inject your own javascript code directly into the WebView. This injection does not care which URL is loaded inside, so you can do it to any site.
It sounds like a big security hole but this really isn't. The browser is inside your app which means you are the browser therefore you can do anything you want while parsing the HTML code of the websites you're showing.
Here is a code example which loads some JS into a google hosted page:
final WebView webview = (WebView)findViewById(R.id.browser);
/* JavaScript must be enabled if you want it to work, obviously */
webview.getSettings().setJavaScriptEnabled(true);
/* WebViewClient must be set BEFORE calling loadUrl! */
webview.setWebViewClient(new WebViewClient() {
#Override
public void onPageFinished(WebView view, String url)
{
webview.loadUrl("javascript:(function() { " +
"document.getElementsByTagName('body')[0].style.color = 'red'; " +
"})()");
}
});
webview.loadUrl("http://code.google.com/android");
If you can run your own JS you can pretty much do anything you want - including traversing the DOM directly, accessing the search-box itself and making sure it has focus.
In order to figure out which JS code you want to inject, use Google Chrome on your PC and open google.com inside Chrome Developer Tools. Then try to type JS commands in the console until you get the required result. Typing JS lines in the console = injecting JS code into the page.