How to call javascript function from java when webview page is different - java

I am working on an android project using HTML and webview to display.
I have
display.loadUrl("file:///android_asset/index.html");
display.setWebViewClient(new WebViewClient() {
public void onPageFinished(WebView view, String url)
{
display.loadUrl("javascript:openDialog()");
}
});
and this works perfectly. But the javascript function i want to call is in another page (chat.html). How do i call the javascript functions on this pages from java?

If you are the owner of the webpage (chat.html), you can integrate a JS-function which invokes a native method. And in this native method you can call your target-JS:
chat.html:
function callItNow() {
if (typeof Android != "undefined"){
if (Android.caller!= "undefined") {
Android.caller();
}
}
}
in native Code, define a class:
class MyJavascriptBridge {
public void caller() {
//now you know you are on the right place (chat.html)
webView.loadUrl("javascript:openDialog()");
}
}
and of course you have to declare the bridge to your webview:
webView.addJavascriptInterface(new MyJavascriptBridge(), "Android");

suppose you'r function in javascript is hello.
webview.loadUrl("javascript:hello();");
I think that'll do it.

Related

Android WebView evaluateJavaScript Callback is not being invoked

evaluateJavascript(String script, ValueCallback<String> resultCallback) method is added to WebView on Android in SDK 19.
Android documentation quotes:
If non-null, resultCallback will be invoked with any
result returned from that execution.
I am using this method as shown below, but somehow my callback is not being invoked. I can see from debugging that the evaluateJavascript() is called, but the call back is not being invoked in Android API 19, 20 & 21. From API 22 (LOLLIPOP_MR1) onwards, everything is working as expected.
Calling webview.loadURL("") before evaluateJavascript() makes it work on all the API levels. I want to understand why and would appreciate if somebody can shed some light / share any links about this. If I can understand why, I want to see if calling loadURL() could be avoided. There is another unrelated problem which makes loadURL() a non-preferable solution.
Code:
private void webViewTest() {
WebView webview = new WebView(this);
webview.getSettings().setJavaScriptEnabled(true);
Log.d("TEST", "BEFORE"); // LOGGED
// webview.loadUrl(""); // Enabling this makes it work on all Android versions
webview.evaluateJavascript("(function(){return 'test'})()", new ValueCallback<String>() {
#Override
public void onReceiveValue(String s) {
Log.d("TEST", "From JS: " + s); // NEVER LOGGED on API 19-21
}
});
Log.e("TEST", "AFTER"); // LOGGED
}
runOnUiThread(new Runnable() {
#Override
public void run() {
webViewTest();
}
});
Please find an example of this problem at https://github.com/bashok001/TestApp
From the docs:
Asynchronously evaluates JavaScript in the context of the currently displayed page. In ContentViewCore EvaluateJavascript checks for renderview. Looks like for that particular versions of WebView integrated within 19,20,21 - the RenderView is just not created until loadUrl get called.
I found following FIX that could be relevant:
It's modify WebContentsAndorid native implementation of EvaluateJavaScript from :
void WebContentsAndroid::EvaluateJavaScript(JNIEnv* env,
jobject obj,
jstring script,
jobject callback,
jboolean start_renderer) {
RenderViewHost* rvh = web_contents_->GetRenderViewHost();
DCHECK(rvh);
if (start_renderer && !rvh->IsRenderViewLive()) {
if (!static_cast<WebContentsImpl*>(web_contents_)->
CreateRenderViewForInitialEmptyDocument()) {
...
}
}
...
to :
void WebContentsAndroid::EvaluateJavaScript(JNIEnv* env,
jobject obj,
jstring script,
jobject callback) {
RenderViewHost* rvh = web_contents_->GetRenderViewHost();
DCHECK(rvh);
if (!rvh->IsRenderViewLive()) {
if (!static_cast<WebContentsImpl*>(web_contents_)->
CreateRenderViewForInitialEmptyDocument()) {
...
}
}
....
And in ContentViewCore there was following code which passing false as start_renderer:
public void evaluateJavaScript(String script, JavaScriptCallback callback) {
assert mWebContents != null;
mWebContents.evaluateJavaScript(script, callback, false);
}
This mean that on WebView built prior to mentioned fix calls to evaluateJavaScript does not create RenderView and as result WebContext can't handle java script execution.
So when you use loadUrl you force creation of render view and all starts working as expected.

Android WebView getFavicon() returning null

I'm trying to get the favicon of the loaded page after using
WebView webView = new WebView(getActivity());
webView.loadUrl("http://" + url);
I'm attaching the asynchronous WebViewClient to the WebView to get the favicon after it loads
webView.setWebViewClient(new WebViewClient()
{
#Override
public void onPageFinished(WebView view, String url)
{
String linkTitle = view.getTitle();
Bitmap favicon = view.getFavicon();
onLinkUrlFinished(url, linkTitle);
}
});
The favicon getting back is always null, even for websites such as google/facebook that has favicons for sure.
Another thread says to use WebIconDatabase but it's deprecated:
Display the Android WebView's favicon
The API on android site refers to WebViewClient.onReceivedIcon which doesnt even exist.http://developer.android.com/reference/android/webkit/WebView.html#getFavicon%28%29
What's going on here?
In order to use onReceiveIcon(), you should use setWebChromeClient.
This is what I do and it's working for me.
webView.setWebChromeClient(new WebChromeClient() {
#Override
public void onProgressChanged(WebView view, int newProgress) {
super.onProgressChanged(view, newProgress);
progressBar.setProgress(newProgress);
}
#Override
public void onReceivedIcon(WebView view, Bitmap icon) {
super.onReceivedIcon(view, icon);
webImage.setImageBitmap(icon);
}
});
WebIconDatabase is deprecated as of API 19. According to the comments in the code:
#deprecated This class is only required when running on devices up to
{#link android.os.Build.VERSION_CODES#JELLY_BEAN_MR2}
So unless you don't want to support API 18 and below, you should still be using WebIconDatabase:
WebIconDatabase.getInstance().open(getDir("icons", MODE_PRIVATE).getPath());
And then, regardless what API you want to support, you need to specify in a custom WebChromeClient:
public class MyCustomWebChromeClient extends WebChromeClient {
#Override
public void onReceivedIcon(WebView view, Bitmap icon) {
super.onReceivedIcon(view, icon);
// do whatever with the arguments passed in
}
}
Remember to register your custom WebChromeClient with your WebView:
mWebView.setWebChromeClient(new MyCustomWebChromeClient());
The key is to open the WebIconDatabase so WebView has somewhere to put the icons, and override WebChromeClient.onReceivedIcon. For additional information, see this StackOverflow article.
I know its an old thread but, for those facing problems getting favicon using webview client.
Kotlin:
override fun onPageFinished(view: WebView?, url: String?) {
super.onPageFinished(view, url)
tabTitle.text = view?.title // read website title
loadImg(view) // method to load the favicon
}
private fun loadImg (view: WebView?){
// u can directly use tabImage.setBitmap instead of assigning tabImg as val
val tabImg: ImageView = findViewById(R.id.tabImage)
// creating handler object to delay the associated thread a little bit after onPageFinished is called.
val handler = Handler()
val runnable = Runnable {
if(view?.favicon != null) {
tabImg.setImageResource(0) //remove the default image
tabImg.setImageBitmap(view?.favicon) // set the favicon
}
}
handler.postDelayed(runnable, 200) // delay time 200 ms
}
It worked for me, hope it helps new readers, plz up vote if it helps u, so that u can help others!
Best regards
So in the end I didn't end up using the deprecated API, instead I found out that if you put /favicon.ico after the domain, it'll give you the ico file, which I used in the end to fetch the image. The Uri API will have a getHost() method that will give you the host without having to manually parse it
String faviconUrl = Uri.parse(url).getHost() + "/favicon.ico";
For google for example the icon url will be www.google.com/favicon.ico

Opening a page in Webview by passing URL parameters

I am working on an Android Application that opens an HTML page in a webview within the app. The HTML page is stored in the assets folder and I call it by means of
loadUrl("file:///android_asset/a.html");
Now, the page is such that it accepts parameters from the URL (javascript). I need to know how do we pass URL parameters to this html file that is stored inside the assets folder. Writing them like this:
loadUrl("file:///android_asset/a.html?q=2&w=3");
doesn't work.
Is there any other way?
Riya
If your HTML page handles the parameters via javascript (and i can't think of any other way it can handle them), you can call a javascript function in your code with the parameters after the page is loaded, and pass parameters to it.
webview.getSettings().setJavaScriptEnabled(true);  
webview.setWebViewClient(new WebViewClient() {  
#Override  
public void onPageFinished(WebView view, String url)  {  
webview.loadUrl("javascript:(function() { setParameters(2,3)})()");  
}
});
webview.loadUrl("file:///android_asset/a.html");  
I tried the method suggested by shaish and many others for passing url parameters to an 'asset folder' url. It seems that this is a well known bug for android 3 and 4 (http://code.google.com/p/android/issues/detail?id=17535). Simply calling webview.loadUrl("file:///android_asset/a.html?q=2&w=3") works on a blackberry 10 simulator.
A similar explanation like shaish but adapted to my own case:
My problem was that anytime I switched to another app, when coming to the webapp, the webview kept reloading. I guess that's because of the following line in my onCreate() method: myWebView.loadUrl(url); I had the idea to pass these state variables in the url, but as you know it is not possible yet.
What I did was to save the state of some variables using onSaveInstanceState(Bundle outState) {...} and restore them with onRestoreInstanceState(Bundle savedInstanceState){...}.
In onCreate method after setting up myWebView I did the following:
myWebView.setWebViewClient(new WebViewClient() {
#Override
public void onPageFinished(WebView view, String urlString)
{
Log.i("onPageFinished", "loadVariables("+newURL+")");
if(newURL!="")
myWebView.loadUrl("javascript:loadVariables("+"\""+newURL+"\")");
}
#Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
view.loadUrl(url);
return true;
}
});
jsInterface = new JSInterface(this,myWebView);
myWebView.addJavascriptInterface(jsInterface, "Android");
if (savedInstanceState != null)
{
// retrieve saved variables and build a new URL
newURL = "www.yoururl.com";
newURL +="?var1=" + savedInstanceState.getInt("key1");
newURL +="?var2=" + savedInstanceState.getInt("key2");
Log.i("myWebApp","NEW URL = " + newURL);
}
myWebView.loadUrl("www.yoururl.com");
So, what it happens is that first I load the page and then I pass the variables when the page finished to load.
In javascript loadVariables function looks like this:
function loadVariables(urlString){
// if it is not the default URL
if(urlString!="www.yoururl.com")
{
console.log("loadVariables: " + urlString);
// parse the URL using a javascript url parser (here I use purl.js)
var source = $.url(urlString).attr('source');
var query = $.url(urlString).attr('query');
console.log("URL SOURCE = "+source + " URL QUERY = "+query);
//do something with the variables
}
}

stringByEvaluatingJavascriptFromString (iOS method, what is Android equivalent?)

In an iOS app, I used
stringFromJavaScript = [webView stringByEvaluatingJavascriptFromString:#"document.getElementById(\"image\").getAttribute(\"src")"];
To get the src directory of the image that was being displayed on the webView. I want to do the same for Android. What are my options?
Basically the intent is to capture the path so that I can email this same picture...
ie.
"picture.php?image=%#",stringFromJavascript
This way, that same image would be loaded when the user clicks the link, or posts it to facebook etc.
Yeah, I miss this method greatly in Android ;)
To execute JavaScript and get response you can do as follows:
Define JavaScript callback interface in your code:
class MyJavaScriptInterface {
#JavascriptInterface
public void someCallback(String jsResult) {
// your code...
}
}
Attach this callback to your WebView
MyJavaScriptInterface javaInterface = new MyJavaScriptInterface();
yourWebView.addJavascriptInterface(javaInterface, "HTMLOUT");
Run your JavaScript calling window.HTMLOUT.someCallback from the script:
yourWebView.loadUrl("javascript:( function () { var resultSrc = document.getElementById(\"image\").getAttribute(\"src\"); window.HTMLOUT.someCallback(resultSrc); } ) ()");
Hope this helps!

My GWT app isn't running a native function off of a JSNI call. Any takers?

I'm attempting to run a native Java function off of a JSNI call in my GWT app. It looks something like this:
package foo.client;
public class AAA implements EntryPoint, UIupdate {
public native void initChangeFunc() /*-{
$wnd.jsChangeView = function () {
this.#foo.client.AAA::changeToHistory();
alert("got here");
};
}-*/;
public void changeToHistory() {
Window.alert("Hello World");
//Change view here.
this.changeView("history");
this.changeHistoryView("bydate");
};
...
public void onModuleLoad() {
...
this.initChangeFunc();
}
}
Attaching the jsChangeView() function call to a link onclick() in the front-end and clicking it results in a "got here" alert, but not a "Hello World" alert, and the other two functions aren't running either. GWT isn't my area of expertise, and this isn't my app, so I know I'm missing something basic here. Any takers?
this.#foo.client.AAA::changeToHistory() is only referencing the method (a "function pointer" if you like, or, in JavaScript, just a "function"), it doesn't call it. You have to write this.#foo.client.AAA::changeToHistory()() to actually make the call.
It's more obvious when the method has arguments, e.g.: this.#foo.client.AAA::changeToHistory(Ljava/lang/String;I) vs. this.#foo.client.AAA::changeToHistory(Ljava/lang/String;I)("foo", 3).

Categories

Resources