I've been trying for several hours to run some Javascript in an android webview in which there is html parsed by Jsoup. And despite all my attempts to make it work, I'm still not able to do it.
I've searched all over Google and tried all the answers I found, however none of them did the trick I tried using a method and here's what I ended up with:
Document doc = Jsoup.connect("http://example.com/").get();
Elements web_body = doc.select("body").first().children();
Elements web_head = doc.select("head").first().children();
String javascript = "document.querySelector('h1').innerText='f';"; //Replaces 'Example Domain' with 'f': just an example js code.
WebView webview = findViewById(R.id.webview_id);
webview.getSettings().setJavaScriptEnabled(true);
webview.getSettings().setDomStorageEnabled(true);
String html_content = "<html>" + "<head>" + web_head.toString() + "</head>" + "<body>" + web_body.toString() + "</body>" + "</html>";
webview.loadDataWithBaseURL("", html_content, "text/html", "UTF-8", "");
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT) {
webview.evaluateJavascript(javascript, null);
} else {
webview.loadUrl("javascript:(function(){" + javascript + "})()");
}
The code above shows the example page but javascript doesn't work at all: the app doesn't show any error messages nor crashes.
I'm using a phone with Android O (8.0).
I hope the above information will be useful. I'm still a beginner in Java and any help would be greatly appreciated!
Well I replaced:
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT) {
webview.evaluateJavascript(javascript, null);
} else {
webview.loadUrl("javascript:(function(){" + javascript + "})()");
}
with
webview.setWebViewClient(new WebViewClient() {
public void onPageFinished(WebView webview, String url) {
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT) {
webview.evaluateJavascript(javascript, null);
} else {
webview.loadUrl("javascript:(function(){" + javascript + "})()");
}
}
});
and it just WORKS!
Related
Background:-
I am trying to call a #JavascriptInterface annotated method processVideo() form the loadUrl() of webview to get callback when someone click on any video on the webpage.
The following is my approach and it's working perfectly
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_download);
webView = findViewById(R.id.web_view_facebook);
webView.getSettings().setJavaScriptEnabled(true);
webView.getSettings().setPluginState(WebSettings.PluginState.ON);
webView.getSettings().setBuiltInZoomControls(true);
webView.getSettings().setDisplayZoomControls(true);
webView.getSettings().setUseWideViewPort(true);
webView.getSettings().setLoadWithOverviewMode(true);
webView.addJavascriptInterface(this, "Downloader");
webView.setWebViewClient(new WebViewClient() {
#Override
public void onLoadResource(WebView view, String url) {
webView.loadUrl("javascript:(function prepareVideo() { "
+ "var el = document.querySelectorAll('div[data-sigil]');"
+ "for(var i=0;i<el.length; i++)"
+ "{"
+ "var sigil = el[i].dataset.sigil;"
+ "if(sigil.indexOf('inlineVideo') > -1){"
+ "delete el[i].dataset.sigil;"
+ "console.log(i);"
+ "var jsonData = JSON.parse(el[i].dataset.store);"
+ "el[i].addEventListener('click',function(){Downloader.processVideo(jsonData['src'],jsonData['videoID']);});"
+ "}" + "}" + "})()");
}
});
webView.loadUrl("https://m.facebook.com");
}
#JavascriptInterface
public void processVideo(final String vidData, final String vidID) {
try {
Toast.makeText(this, "Download Started", Toast.LENGTH_LONG).show();
} catch (Exception e) {
Toast.makeText(this, "Download Failed: " + e.toString(), Toast.LENGTH_LONG).show();
}
}}
Now The Problem:-
But as i load custom javascript function in loadUrl(), i lost the ability to play video on click.
i think it will happen as i intercepted and load my custom code
So what i want to achieve is when any user click on video, then the video should play and also i get the callback that i wanted at first place
What i have tried so far:-
to play the video on click, i used
addEventListener('click',function(){
var video = document.getElementById(jsonData['videoID']);
video.play(); }
This one is working perfectly to play the video.
but when i use this solution with my callback like following
addEventListener('click',function(){
Downloader.processVideo(jsonData['src'],jsonData['videoID']);
var video = document.getElementById(jsonData['videoID']);
video.play(); }
then I am not able to get callback in my processVideo().
If i use them individually, both functionality (callback and play video) working perfectly inside addEventListener(), but not working together.
This code line: var video = document.getElementById('jsonData['videoID']') throws an error because you used more than two 's in one argument.
Try replacing by var video = document.getElementById(jsonData['videoID']).
Hope this helps.
I have solved this issue and i am pasting complete code here:
webView.setWebViewClient(new WebViewClient()
{
#Override
public void onPageFinished(WebView view, String url)
{
//query_string = "" + url;
// view.loadUrl("javascript:(function() { document.getElementsByTagName('video')[0].play(); })()");
}
#Override
public void onLoadResource(WebView view, String url)
{
// query_string=""+url;
webView.loadUrl("javascript:(function prepareVideo() " +
"{ "
+ "var el = document.querySelectorAll('div[data-sigil]');"
+"var ID;"
+"var SRC;"
+ "for(var i=0;i<el.length; i++)"
+ "{"
+ "var sigil = el[i].dataset.sigil;"
+ "if(sigil.indexOf('inlineVideo') > -1)" +
"{"
+ "delete el[i].dataset.sigil;"
+ "console.log(i);"
+ "var jsonData = JSON.parse(el[i].dataset.store);"
+"ID=jsonData['videoID'];"
+"SRC=jsonData['src'];"
// +"document.getElementsByTagName(\"'+jsonData['videoID']+'\")[0].play();"
+ "el[i].setAttribute('onClick','FBDownloader.processVideo(\"'+jsonData['src']+'\",\"'+jsonData['videoID']+'\");');"
+ "}"
+ "}"
+ "})()");
}
});
JAVA ACTIVITY FUNCTION WHICH IS CALL FROM JAVASCRIPT WEBVIEW
public void processVideo(final String vidData, final String vidID)
{
this.vidData=vidData;
this.vidID=vidID;
Get_Download_Permission();
showtoast("video ID ="+this.vidID);
webView.loadUrl("https://www.facebook.com/video/embed?video_id="+this.vidID);//193766537709747/");
}
how to hide my website header in android webview? I have tried javascript function in my mainactivity.java but not worked, Please hep, Thanks in advance!
This is Im tried
#Override
public void onPageFinished(WebView view, String url)
{
myWebView.loadDataWithBaseURL("javascript:(function() { " +
"document.get('header')[0].style.display='none'; " +
"})()");
}
hope you have added
mWebView.getSettings().setJavaScriptEnabled(true);
myWebView.loadDataWithBaseURL("javascript:(function() { " +
"var header = document.getElementById(\"header\"); " +
"header.parentNode.removeChild(header);" +
"})()");
}
I'm having a problem with my code when running on a Android 4.4.2 KitKat (API 19) emulator...
When I emulate my project on a Android 4.3 (API 18) emulator, it works normally and creates the mathematical expressions with MathJax:
Image of emulator
But when I use a Android 4.4.2 emulator, the app don't work correctly:
Image of emulator
Here is the code of my project:
package com.testes.testesapp;
import android.app.Activity;
import android.graphics.Color;
import android.os.Bundle;
import android.view.View;
import android.webkit.WebView;
import android.widget.Button;
import android.widget.EditText;
public class MainActivity extends Activity implements View.OnClickListener {
private int exampleIndex = 0;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
WebView webView = (WebView) findViewById(R.id.webview);
webView.getSettings().setJavaScriptEnabled(true);
//webView.getSettings().setBuiltInZoomControls(true);
webView.loadDataWithBaseURL("http://test", "<script type='text/x-mathjax-config'>"
+ "MathJax.Hub.Config({ "
+ "showMathMenu: false, "
+ "jax: ['input/TeX','output/HTML-CSS'], " // output/SVG
+ "extensions: ['tex2jax.js','toMathML.js'], "
+ "TeX: { extensions: ['AMSmath.js','AMSsymbols.js',"
+ "'noErrors.js','noUndefined.js'] }, "
// + "'SVG' : { blacker: 30, "
// + "styles: { path: { 'shape-rendering': 'crispEdges' } } } "
+ "});</script>"
+ "<script type='text/javascript' "
+ "src='file:///android_asset/MathJax/MathJax.js'"
+ "></script>"
+ "<span id='math'></span>","text/html","utf-8","");
EditText edit = (EditText) findViewById(R.id.edit);
edit.setBackgroundColor(Color.LTGRAY);
edit.setTextColor(Color.BLACK);
edit.setText("");
Button btnShow = (Button) findViewById(R.id.btnShow);
btnShow.setOnClickListener(this);
Button btnClear = (Button) findViewById(R.id.btnClear);
btnClear.setOnClickListener(this);
Button btnExample = (Button) findViewById(R.id.btnExample);
btnExample.setOnClickListener(this);
}
private String doubleEscapeTeX(String s) {
String t="";
for (int i=0; i < s.length(); i++) {
if (s.charAt(i) == '\'') t += '\\';
if (s.charAt(i) != '\n') t += s.charAt(i);
if (s.charAt(i) == '\\') t += "\\";
}
return t;
}
private String getExample(int index) {
return getResources().getStringArray(R.array.tex_examples)[index];
}
public void onClick(View v) {
if (v == findViewById(R.id.btnShow)) {
WebView webView = (WebView) findViewById(R.id.webview);
EditText edit = (EditText) findViewById(R.id.edit);
webView.loadUrl("javascript:document.getElementById('math').innerHTML='\\\\["
+ doubleEscapeTeX(edit.getText().toString()) + "\\\\]';");
webView.loadUrl("javascript:MathJax.Hub.Queue(['Typeset',MathJax.Hub]);");
}
else if (v == findViewById(R.id.btnClear)) {
WebView webView = (WebView) findViewById(R.id.webview);
EditText edit = (EditText) findViewById(R.id.edit);
edit.setText("");
webView.loadUrl("javascript:document.getElementById('math').innerHTML='';");
}
else if (v == findViewById(R.id.btnExample)) {
WebView webView = (WebView) findViewById(R.id.webview);
EditText edit = (EditText) findViewById(R.id.edit);
edit.setText(getExample(exampleIndex++));
if (exampleIndex > getResources().getStringArray(R.array.tex_examples).length - 1)
exampleIndex=0;
webView.loadUrl("javascript:document.getElementById('math').innerHTML='\\\\["
+ doubleEscapeTeX(edit.getText().toString()) + "\\\\]';");
webView.loadUrl("javascript:MathJax.Hub.Queue(['Typeset',MathJax.Hub]);");
}
}
}
When I press the "Example" or the "Show" button, LogCat emits the errors:
I/chromium(1254): [INFO:CONSOLE(1)] "Uncaught TypeError: Cannot set property 'innerHTML' of null", source: http://test/ (1)
I/chromium(1254): [INFO:CONSOLE(1)] "Uncaught ReferenceError: MathJax is not defined", source: http://test/ (1)
I have no idea how to fix this problem, and would like somebody's help to solve this. Thanks.
I got this problem too.
Here is my solution:
change your base URL from "http://test" to "http://test/"
change this code webView.loadUrl(..) to webView.evaluateJavascript(..);. Especially for code where we want to load innerHTML. Don't forget to add anotation #TargetApi(Build.VERSION_CODES.KITKAT) because it's only for Android 4.4 and above
never put webView.loadWithBaseUrl(..) in the same process with webView.loadUrl(..). Because If webView.loadUrl(..) was loaded before webView.loadWithBaseUrl(..) finished, it will raise error as OP stated above.
For number 3, your codes is already conform with this (because in your codes, webView.loadUrl(..) execution was already separated with webView.loadWithBaseUrl(..) by using OnClick event. So, don't pay attention to it.
But if your apps need to load them at one event, consider to separate them by using this code:
`
private void initiateWebView(){
webViewEquationDisplay.setWebViewClient(new WebViewClient() {
#Override
public void onPageFinished(WebView view, String url) {
super.onPageFinished(view, url);
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT){
loadUrlKitKat(equationSymbolFinal+equationToBeDisplayedFinal);
}
else{
webViewEquationDisplay.loadUrl("javascript:document.getElementById('math').innerHTML='<font color=\"yellow\">`"+equationToBeDisplayedFinal+"`</font>';");
}
webViewEquationDisplay.loadUrl("javascript:MathJax.Hub.Queue(['Typeset',MathJax.Hub]);");
}
});
final String mathJaxOfflineUrl = "file:///android_asset/MathJax/MathJax.js";
webViewEquationDisplay.loadDataWithBaseURL("http://bar/", "<script type='text/x-mathjax-config'>"
+"MathJax.Hub.Config({ "
+"showMathMenu: false, "
+"jax: ['input/AsciiMath','output/HTML-CSS'], "
+"extensions: ['asciimath2jax.js'], "
+"AsciiMath: { fixphi: true, useMathMLspacing: true, displaystyle: false, decimalsign: \".\" }, "
+"});</script>"
+"<script type='text/javascript' "
+"src='"+mathJaxOfflineUrl+"'"
+"></script><span id='math'></span>","text/html","utf-8","");
}
#TargetApi(Build.VERSION_CODES.KITKAT)
private void loadUrlKitKat(String param){
webViewEquationDisplay.evaluateJavascript("javascript:document.getElementById('math').innerHTML='<font color=\"#97FD97\">`"+param+"`</font>';",null);
}
`
good luck
I had the same problem when I moved different javascript functions out of the main page to an separate .js file. For some reason, Android can't find externally-loaded JavaScript webview functions from Java - only the ones in the main page. Once I moved the function back from the JS file, it immediately started working.
Try making a "proxy" function like this directly inside the main HTML:
function proxy() {
call_some_other_function_from_JS_file();
}
This worked for me. I'm sure there must be a way to get find these functions, because I didn't have this problem on iOS. Someone please comment if you know a better way.
webview.evaluateJavascript("javascript:document.getElementById('math').innerHTML='"+doubleEscapeTeX(questn)+"';",null);
webview.loadUrl("javascript:MathJax.Hub.Queue(['Typeset',MathJax.Hub]);");
This is the sollution for api 19 or above
I am calling a simple JS function to set values of some html contents, but its not working
Here is the JS function
function SetEdits(name,email,pic,date)
{
document.getElementById("myPic").src=pic;
document.getElementById("fullname").value=name;
document.getElementById("email").value=email;
}
and here is the code from android activity
edit.loadUrl("edit.html");
edit.loadUrl("javascript:SetEdits('"+name+"','"+email+"','"+picture+"','"+date+"')");
its not settings these fileds.. is there any problem with the synax where i am calling this function in native activity?
You're probably ending up evaluating the JavaScript before the "edit.html" page has loaded. Try this:
// I'm assuming the real path is something like file:///android_asset/edit.html
edit.loadUrl("edit.html");
edit.setWebViewClient(new WebViewClient() {
#Override
public void onPageFinished(WebView view, String url) {
if (Uri.parse(url).getPath() == "edit.html") {
view.loadUrl("javascript:SetEdits('" + name+"','" + email + "','" +
picture + "','" + date + "')");
}
}
Have you enabled the javascript for the webview ?
You have to setup a javascript interface between your app and html.
Here is some details Webapp guid
I'm trying to get JWPlayer to return an alert when a few specific events happen from a flash player playing a local video. If you notice from the code below, onComplete, JWPlayer should return an alert, which can then be intercepted by onJsAlert from setWebChromeClient so I can do stuff with that information. Am I doing something wrong?
A possible reason, I can find here: JWplayer Javascript interaction not working that it's being loaded locally. Is there any way I can bypass this issue? Would it be easier to load somehow by calling localhost? Is that even possible?
For those of you curious about why I generate an HTML file instead of just having one move from the assets - after scouring the Internet to figure out how to get a local flv player working correctly, the best option was to generate the HTML file with the custom information and write the file to the same directory as the FLV (hence the FileWriter function).
HTML code for JWPlayer embed:
private void createVideoHtml(File flvDirectory, File htmlFile, String videofilename)
{
String htmlPre = "<!DOCTYPE html><html lang=\"en\"><head><meta charset=\"utf-8\"></head><body style='margin:0; padding:0;'>";
String htmlCode =
"<script type='text/javascript' src='"+ flvDirectory.getAbsolutePath() + "/jwplayer.js'></script>" +
"<div id='mediaspace'>EZ Stream TV FLV Player</div>" +
"<script type='text/javascript'>" +
"jwplayer('mediaspace').setup({" +
"'flashplayer': '"+ flvDirectory.getAbsolutePath() + "/player.swf', 'file': '" + videofilename + "', 'backcolor': 'FFFFFF', 'frontcolor': '000000', 'lightcolor': '000000'," +
"'screencolor': '000000', 'volume': '100', 'autostart': 'true', 'mute': 'false', 'quality': 'false', 'controlbar': 'bottom', 'width': '100%', 'height': '100%'," +
"events: { " +
"onComplete: function() { alert('COMPLETED');}" +
"}});" +
"</script>";
String htmlPost = "</body></html>";
String finalHTML = htmlPre + htmlCode + htmlPost;
try {
FileWriter f = new FileWriter(htmlFile);
PrintWriter p = new PrintWriter(f);
p.print(finalHTML);
p.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
Code for webview and handling the Javscript alert:
webView = (WebView)findViewById(R.id.web_player);
webView.getSettings().setBuiltInZoomControls(false);
webView.getSettings().setJavaScriptEnabled(true);
webView.getSettings().setLoadsImagesAutomatically(true);
webView.getSettings().setPluginsEnabled(true);
webView.getSettings().setJavaScriptCanOpenWindowsAutomatically(true);
webView.getSettings().setAllowFileAccess(true);
webView.setInitialScale(60);
webView.setBackgroundColor(Color.BLACK);
getWindow().addFlags(128);
webView.getSettings().setUserAgentString("Mozilla/5.0 (Windows; U; Windows NT 6.1; en-GB; rv:1.9.1.2) Gecko/20090729 Firefox/3.5.2 (.NET CLR 3.5.30729)");
webView.setScrollBarStyle(WebView.SCROLLBARS_OUTSIDE_OVERLAY);
webView.setWebChromeClient(new WebChromeClient() {
#Override
public boolean onJsAlert(WebView view, String url, String message, final android.webkit.JsResult result)
{
Log.d(TAG, message);
new AlertDialog.Builder(view.getContext()).setMessage(message).setCancelable(true).show();
result.confirm();
return true;
}
});
You can refer to the code below for JWPlayer to Webview
private void createVideoHtml(File flvDirectory, File htmlFile, String videofilename)
{
String htmlPre = "<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"></head><body style='margin:0; padding:0;'>";
String htmlCode =
"<script type='text/javascript' src='"+ flvDirectory.getAbsolutePath() + "/jwplayer.js'></script>" +
"<div id='mediaspace'>EZ Stream TV FLV Player</div>" +
"<script type='text/javascript'>" +
"jwplayer('mediaspace').setup({" +
"'flashplayer': '"+ flvDirectory.getAbsolutePath() + "/player.swf', 'file': '" + videofilename + "', 'backcolor': 'FFFFFF', 'frontcolor': '000000', 'lightcolor': '000000'," +
"'screencolor': '000000', 'volume': '100', 'autostart': 'true', 'mute': 'false', 'quality': 'false', 'controlbar': 'bottom', 'width': '100%', 'height': '100%'," +
"events: { " +
"onComplete: function() { alert('COMPLETED');}" +
"}});" +
"</script>";
String htmlPost = "</body></html>";
String finalHTML = htmlPre + htmlCode + htmlPost;
try {
FileWriter f = new FileWriter(htmlFile);
PrintWriter p = new PrintWriter(f);
p.print(finalHTML);
p.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
webView = (WebView)findViewById(R.id.web_player);
webView.getSettings().setBuiltInZoomControls(false);
webView.getSettings().setJavaScriptEnabled(true);
webView.getSettings().setLoadsImagesAutomatically(true);
webView.getSettings().setPluginsEnabled(true);
webView.getSettings().setJavaScriptCanOpenWindowsAutomatically(true);
webView.getSettings().setAllowFileAccess(true);
webView.setInitialScale(60);
webView.setBackgroundColor(Color.BLACK);
getWindow().addFlags(128);
webView.getSettings().setUserAgentString("Mozilla/5.0 (Windows; U; Windows NT 6.1; en-GB; rv:1.9.1.2) Gecko/20090729 Firefox/3.5.2 (.NET CLR 3.5.30729)");
webView.setScrollBarStyle(WebView.SCROLLBARS_OUTSIDE_OVERLAY);
webView.setWebChromeClient(new WebChromeClient() {
#Override
public boolean onJsAlert(WebView view, String url, String message, final android.webkit.JsResult result)
{
Log.d(TAG, message);
new AlertDialog.Builder(view.getContext()).setMessage(message).setCancelable(true).show();
result.confirm();
return true;
}
});
I had the same problem while working with jwplayer, my conclusion was that the onComplete event isn't trustable in some cases.
Can you benchmark other events does work like the onTime event ?
Otherwise use the onIdle event and measure the time left ( getDuration - getPosition ) to get a custom onComplete event.