im trying to parse url generated by Bootstrap`s Bootpage.js which looks like
https://example.com/#page-2
but JSOUP cant parse it and showing main url.
how to get normal link from Bootpage or how to make JSOUP to parse it.
Parsing code:
Jsoup.connect("https://example.com/#page-2").followRedirects(true).get();
(See UPDATE below, first/accepted solution didn't met the android requirement, but is left for reference.)
Desktop Solution
HtmlUnit doesn't seem able to handle this site (often the case, lately). So I don't have a plain java solution either, but you could use PhantomJS: download the binary for your os, create a script file, start the process from within your java code and parse the output with a dom parser like jsoup.
Script file (here called simple.js):
var page = require('webpage').create();
var fs = require('fs');
var system = require('system');
var url = "";
var fileName = "output";
// first parameter: url
// second parameter: filename for output
console.log("args length: " + system.args.length);
if (system.args.length > 1) {
url=system.args[1];
}
if (system.args.length > 2){
fileName=system.args[2];
}
if(url===""){
phantom.exit();
}
page.settings.userAgent = 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/37.0.2062.120 Safari/537.36';
page.settings.loadImages = false;
page.open(url, function(status) {
console.log("Status: " + status);
if(status === "success") {
var path = fileName+'.html';
fs.write(path, page.content, 'w');
}
phantom.exit();
});
Java code (example to get title and cover-url):
try {
//change path to phantomjs binary and your script file
String outputFileName = "srulad";
String phantomJSPath = "phantomjs" + File.separator + "bin" + File.separator + "phantomjs";
String scriptFile = "simple.js";
String urlParameter = "http://srulad.com/#page-2";
new File(outputFileName+".html").delete();
Process process = Runtime.getRuntime().exec(phantomJSPath + " " + scriptFile + " " + urlParameter + " " + outputFileName);
process.waitFor();
Document doc = Jsoup.parse(new File(outputFileName + ".html"),"UTF-8"); // output.html is created by phantom.js, same path as page.js
Elements elements = doc.select("#list_page-2 > div");
for (Element element : elements) {
System.out.println(element.select("div.l-description.float-left > div:nth-child(1) > a").first().attr("title"));
System.out.println(element.select("div.l-image.float-left > a > img.lazy").first().attr("data-original"));
}
} catch (IOException | InterruptedException e) {
e.printStackTrace();
}
Output:
სიყვარული და მოწყალება / Love & Mercy
http://srulad.com/assets/uploads/42410_Love_and_Mercy.jpg
მუზა / The Muse
http://srulad.com/assets/uploads/43164_large_qRzsimNz0eDyFLFJcbVLIxlqii.jpg
...
UPDATE
Parsing of websites with javascript based dynamic content in Android is possible using WebView and jsoup.
The following example app uses a javascript enabled WebView to render a Javascript dependent website. With a JavascriptInterface the html source is returned, parsed with jsoup and as a proof of concept the titles and the urls to the cover-images are used to populate a ListView. The buttons decrement or increment the page number triggering an update of the ListView. Note: tested on an Android 5.1.1/API 22 device.
add internet permission to your AndroidManifest.xml
<uses-permission android:name="android.permission.INTERNET" />
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/page_down"
android:id="#+id/buttonDown"
android:layout_weight="0.5" />
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/page_up"
android:id="#+id/buttonUp"
android:layout_weight="0.5" />
</LinearLayout>
<ListView
android:layout_width="match_parent"
android:layout_height="0dp"
android:id="#+id/listView"
android:layout_gravity="bottom"
android:layout_weight="0.5" />
</LinearLayout>
MainActivity.java
public class MainActivity extends AppCompatActivity {
private final Handler uiHandler = new Handler();
private ArrayAdapter<String> adapter;
private ArrayList<String> entries = new ArrayList<>();
private ProgressDialog progressDialog;
private class JSHtmlInterface {
#android.webkit.JavascriptInterface
public void showHTML(String html) {
final String htmlContent = html;
uiHandler.post(
new Runnable() {
#Override
public void run() {
Document doc = Jsoup.parse(htmlContent);
Elements elements = doc.select("#online_movies > div > div");
entries.clear();
for (Element element : elements) {
String title = element.select("div.l-description.float-left > div:nth-child(1) > a").first().attr("title");
String imgUrl = element.select("div.l-image.float-left > a > img.lazy").first().attr("data-original");
entries.add(title + "\n" + imgUrl);
}
adapter.notifyDataSetChanged();
}
}
);
}
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ListView listView = (ListView) findViewById(R.id.listView);
adapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, android.R.id.text1, entries);
listView.setAdapter(adapter);
progressDialog = ProgressDialog.show(this, "Loading","Please wait...", true);
progressDialog.setCancelable(false);
try {
final WebView browser = new WebView(this);
browser.setVisibility(View.INVISIBLE);
browser.setLayerType(View.LAYER_TYPE_NONE,null);
browser.getSettings().setJavaScriptEnabled(true);
browser.getSettings().setBlockNetworkImage(true);
browser.getSettings().setDomStorageEnabled(false);
browser.getSettings().setCacheMode(WebSettings.LOAD_NO_CACHE);
browser.getSettings().setLoadsImagesAutomatically(false);
browser.getSettings().setGeolocationEnabled(false);
browser.getSettings().setSupportZoom(false);
browser.addJavascriptInterface(new JSHtmlInterface(), "JSBridge");
browser.setWebViewClient(
new WebViewClient() {
#Override
public void onPageStarted(WebView view, String url, Bitmap favicon) {
progressDialog.show();
super.onPageStarted(view, url, favicon);
}
#Override
public void onPageFinished(WebView view, String url) {
browser.loadUrl("javascript:window.JSBridge.showHTML('<html>'+document.getElementsByTagName('html')[0].innerHTML+'</html>');");
progressDialog.dismiss();
}
}
);
findViewById(R.id.buttonDown).setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
uiHandler.post(new Runnable() {
#Override
public void run() {
int page = Integer.parseInt(browser.getUrl().split("-")[1]);
int newPage = page > 1 ? page-1 : 1;
browser.loadUrl("http://srulad.com/#page-" + newPage);
browser.loadUrl(browser.getUrl()); // not sure why this is needed, but doesn't update without it on my device
if(getSupportActionBar()!=null) getSupportActionBar().setTitle(browser.getUrl());
}
});
}
});
findViewById(R.id.buttonUp).setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
uiHandler.post(new Runnable() {
#Override
public void run() {
int page = Integer.parseInt(browser.getUrl().split("-")[1]);
int newPage = page+1;
browser.loadUrl("http://srulad.com/#page-" + newPage);
browser.loadUrl(browser.getUrl()); // not sure why this is needed, but doesn't update without it on my device
if(getSupportActionBar()!=null) getSupportActionBar().setTitle(browser.getUrl());
}
});
}
});
browser.loadUrl("http://srulad.com/#page-1");
if(getSupportActionBar()!=null) getSupportActionBar().setTitle(browser.getUrl());
} catch (Exception e) {
e.printStackTrace();
}
}
}
Related
I have an architecture where I am having a webview in MainActivity of my Android app. A page having a "Take Photo" button loads whenever user opens the app. On clicking "Take Photo" button, javascript will call dispatchTakePictureIntent() method defined in the Android app, which will redirect user to the Camera. Once, the user clicks the photo and submits it, I want to send the photo back to JS which in turn will make a AJAX call to the server to store the image.
I am facing trouble in developing the flow once user submits the photo. In my current implementation, I am able to store the image file locally and retrieve a file path, but I am unable to figure out how to use this file path in JS to get the file and send it on the server. Kindly help me out with this.
I have taken reference from this article: https://developer.android.com/training/camera/photobasics#TaskPath
MainActivity.java
public class MainActivity extends Activity {
private static final String TAG = MainActivity.class.getSimpleName();
private static final String FILE_PATH_PREFIX = "file:";
private String imageFilePath;
private String callbackFunction;
#SuppressLint("SetJavaScriptEnabled")
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
AndroidPlatform.setUp(this, APPLICATION_NAME);
setContentView(R.layout.activity_main);
webView = (WebView) findViewById(R.id.webview);
webView.getSettings().setJavaScriptEnabled(true);
webView.getSettings().setGeolocationEnabled(true);
webView.getSettings().setAppCacheEnabled(true);
webView.getSettings().setDatabaseEnabled(true);
webView.getSettings().setDomStorageEnabled(true);
webView.getSettings().setAllowFileAccess(true);
webView.getSettings().setGeolocationDatabasePath(getFilesDir().getPath());
webView.addJavascriptInterface(this, "Android");
webView.setWebChromeClient(new WebChromeClient());
webView.setWebViewClient(new WebViewClient());
webView.loadUrl("URL to the page having take photo button");
}
#JavascriptInterface
public void dispatchTakePictureIntent(String _callbackFunction) {
this.callbackFunction = _callbackFunction;
Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
if (takePictureIntent.resolveActivity(getPackageManager()) != null) {
File photoFile = createImageFile();
if (photoFile != null) {
this.imageFilePath = FILE_PATH_PREFIX + photoFile.getAbsolutePath();
Uri photoURI = FileProvider.getUriForFile(this,
"com.example.fileprovider",
photoFile);
takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoURI);
startActivityForResult(takePictureIntent, REQUEST_IMAGE_CAPTURE);
} else {
Toast.makeText(getApplicationContext(), R.string.please_retry, Toast.LENGTH_SHORT).show();
}
} else {
Toast.makeText(getApplicationContext(), R.string.no_camera_found, Toast.LENGTH_SHORT).show();
}
}
private File createImageFile() {
String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
String imageFileName = "storephoto_" + timeStamp + "_";
File storageDir = getExternalFilesDir(Environment.DIRECTORY_PICTURES);
File image = null;
try {
image = File.createTempFile(imageFileName,".jpg", storageDir);
} catch (IOException e) {
Log.e(TAG, "Exception in createImageFile:", e);
}
return image;
}
#TargetApi(Build.VERSION_CODES.KITKAT)
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == REQUEST_IMAGE_CAPTURE) {
if(resultCode == RESULT_OK && callbackFunction != null && imageFilePath != null) {
// What to do with imageFilePath?
//
// How to use it in Javascript to read and send image to backend server?
//
// Sample code if we send imageFilePath to JS:
// String callbackModule = String.format(TAKE_PICTURE_JS_MODULE, callbackFunction, imageFilePath);
// webView.evaluateJavascript(callbackModule, null);
}
}
}
}
Javascript
$takePhotoButton.click(function(e) {
e.preventDefault();
Android && Android.dispatchTakePictureIntent("takePhotoCallbackFn");
});
Note: Here it is necessary that user submits the photo through camera only and not via choosing/uploading an existing file.
new google play console alert that's been poping up for lots of users.
issue code: SNYK-JS-JQUERY-174006
vulnerable jquery 3.1.1 version
file: assets/www/js/jquery.min.js
unlike this post I am not specifying any JS version but I do have a JS enabled WebView in the App.
Is there a way to resolve this issue ?
Update:
as per request here is my webview code. However as I mentioned in the comment I'm not using jQuery. and suspecting that one of the dependencies (libraries) using it. but I'm not sure how to pin point the location as a project wide search for *.js files didn't return any results.
My WebViewe code:
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
String ua = "Mozilla/5.0 (X11; Linux i686) AppleWebKit/537.31 (KHTML, like Gecko) Chrome/26.0.1410.63 Safari/537.31";
view = inflater.inflate(R.layout.fragment_a, container, false);
_wvAGate = view.findViewById(R.id.wvA);
setDesktopMode(_wvAGate, true);
_wvAGate.getSettings().setJavaScriptEnabled(true);
WebSettings ws = _wvAGate.getSettings();
ws.setJavaScriptEnabled(true);
ws.setAllowFileAccess(true);
ws.setBuiltInZoomControls(true);
ws.setDisplayZoomControls(false);
try {
Timber.d("Enabling HTML5-Features");
Method m1 = WebSettings.class.getMethod("setDomStorageEnabled", new Class[]{Boolean.TYPE});
m1.invoke(ws, Boolean.TRUE);
Method m2 = WebSettings.class.getMethod("setDatabaseEnabled", new Class[]{Boolean.TYPE});
m2.invoke(ws, Boolean.TRUE);
Method m3 = WebSettings.class.getMethod("setDatabasePath", new Class[]{String.class});
m3.invoke(ws, "/data/data/" + getActivity().getPackageName() + "/databases/");
Method m4 = WebSettings.class.getMethod("setAppCacheMaxSize", new Class[]{Long.TYPE});
m4.invoke(ws, 1024 * 1024 * 8);
Method m5 = WebSettings.class.getMethod("setAppCachePath", new Class[]{String.class});
m5.invoke(ws, "/data/data/" + getActivity().getPackageName() + "/cache/");
Method m6 = WebSettings.class.getMethod("setAppCacheEnabled", new Class[]{Boolean.TYPE});
m6.invoke(ws, Boolean.TRUE);
} catch (Exception e){
Timber.e("AGate exception",e);
}
_wvAGate.getSettings().setUserAgentString(ua);
_wvAGate.getSettings().setUseWideViewPort(true);
_wvAGate.getSettings().setLoadWithOverviewMode(true);
_wvAGate.setWebChromeClient(new WebChromeClient() {
#Override
public boolean onConsoleMessage(ConsoleMessage consoleMessage) {
return super.onConsoleMessage(consoleMessage);
}
});
_wvAGate.setWebViewClient(new WebViewClient() {
#SuppressLint("NewApi")
#Override
public WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request) {
return null;
}
});
_wvAGate.addJavascriptInterface(new WebAppInterface(getActivity()), "Gate");
}
public void setDesktopMode(WebView webView, boolean enabled) {
String newUserAgent = webView.getSettings().getUserAgentString();
if (enabled) {
try {
String ua = webView.getSettings().getUserAgentString();
String androidOSString = webView.getSettings().getUserAgentString().substring(ua.indexOf("("), ua.indexOf(")") + 1);
newUserAgent = webView.getSettings().getUserAgentString().replace(androidOSString, "(X11; Linux x86_64)");
} catch (Exception e) {
e.printStackTrace();
}
} else {
newUserAgent = null;
}
webView.getSettings().setUserAgentString(newUserAgent);
webView.getSettings().setUseWideViewPort(enabled);
webView.getSettings().setLoadWithOverviewMode(enabled);
webView.reload();
}
I would appreciate if you have any solutions regarding this issue.
cheers!
Here's my code for injecting CSS into a Webview:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
getSupportActionBar().setTitle("Ubqari");
ww = (WebView) findViewById(R.id.ww);
ww.getSettings().setJavaScriptEnabled(true);
ww.getSettings().setDomStorageEnabled(true);
ww.setWebViewClient(new WebViewClient(){
public void onPageFinished(WebView view, String url) {
injectCSS();
}
});
ww.loadUrl("http://ubqari.org");``
}
And here's injectCSS function:
private void injectCSS() {
try {
InputStream inputStream = getAssets().open("style.css");
byte[] buffer = new byte[inputStream.available()];
inputStream.read(buffer);
inputStream.close();
String encoded = Base64.encodeToString(buffer, Base64.NO_WRAP);
ww.loadUrl("javascript:(function() {" +
"alert('Hello! I am an alert box!');"+
"var parent = document.getElementsByTagName('head').item(0);" +
"var style = document.createElement('style');" +
"style.type = 'text/css';" +
// Tell the browser to BASE64-decode the string into your script !!!
"style.innerHTML = window.atob('" + encoded + "');" +
"parent.appendChild(style)" +
"})()");
} catch (Exception e) {
e.printStackTrace();
}
}
I don't know what's going wrong in this code. My style.css code is correct but the problem is in the onPageFinished section. It's not injecting the CSS when the page has finished loading. Any expert can answer me?
Say, I set my WebView into example.com then I clicked on the page and load the example.com/about.
In the WebView I've injected Javascript to modify the HTML on the example.com, and it worked, and the problem is, how to inject another JavaScript to modify example.com/about. I mean how to inject multiple JavaScript to a multiple page of WebView?
public class MainActivity extends AppCompatActivity {
WebView mWebView;
private int getScale() {
Display display = ((WindowManager) getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();
int width = display.getWidth();
Double val = new Double(width) / new Double(100);
val = val * 100d;
return val.intValue();
}
#Override
protected void onCreate(Bundle savedInstanceState) {
this.requestWindowFeature(Window.FEATURE_NO_TITLE);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mWebView = (WebView) findViewById(R.id.activity_main_webview);
// Enable Javascript
WebSettings webSettings = mWebView.getSettings();
mWebView.setWebViewClient(new WebViewClient());
webSettings.setJavaScriptEnabled(true);
webSettings.setAllowUniversalAccessFromFileURLs(true);
mWebView.setWebViewClient(new WebViewClient());
mWebView.setWebViewClient(new WebViewClient() {
#Override
public void onPageFinished(WebView view, String url) {
// Inject CSS when page is done loading
injectCSS();
mWebView.loadUrl("javascript:var greeting = function (name) {\n" +
" console.log(\"Great to see you,\" + \" \" + name);\n" +
"};");
super.onPageFinished(view, url);
//Inject JS to edit html
injectScriptFile(view, "script.js"); // see below ...
// test if the script was loaded
view.loadUrl("javascript:setTimeout(test(), 500)");
}
private void injectScriptFile(WebView view, String scriptFile) {
InputStream input;
try {
input = getAssets().open(scriptFile);
byte[] buffer = new byte[input.available()];
input.read(buffer);
input.close();
// String-ify the script byte-array using BASE64 encoding !!!
String encoded = Base64.encodeToString(buffer, Base64.NO_WRAP);
view.loadUrl("javascript:(function() {" +
"var parent = document.getElementsByTagName('head').item(0);" +
"var script = document.createElement('script');" +
"script.type = 'text/javascript';" +
// Tell the browser to BASE64-decode the string into your script !!!
"script.innerHTML = window.atob('" + encoded + "');" +
"parent.appendChild(script)" +
"})()");
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
#Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
view.loadUrl(url);
return false;
}
});
mWebView.loadUrl("example.com");
mWebView.getSettings().setBuiltInZoomControls(true);
mWebView.getSettings().setDisplayZoomControls(false);
}
private void injectCSS() {
try {
InputStream inputStream = getAssets().open("style.css");
byte[] buffer = new byte[inputStream.available()];
inputStream.read(buffer);
inputStream.close();
String encoded = Base64.encodeToString(buffer, Base64.NO_WRAP);
mWebView.loadUrl("javascript:(function() {" +
"var parent = document.getElementsByTagName('head').item(0);" +
"var style = document.createElement('style');" +
"style.type = 'text/css';" +
// Tell the browser to BASE64-decode the string into your script !!!
"style.innerHTML = window.atob('" + encoded + "');" +
"parent.appendChild(style)" +
"})()");
} catch (Exception e) {
e.printStackTrace();
}
}
#Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
mWebView.saveState(outState);
}
#Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
mWebView.restoreState(savedInstanceState);
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
super.onCreateOptionsMenu(menu); // Add menu items, second value is the id, use this in the onCreateOptionsMenu
menu.add(0, 1, 0, "Back");
menu.add(0, 2, 0, "Refresh");
menu.add(0, 3, 0, "Forward");
getMenuInflater().inflate(R.menu.menu_main, menu);
return true; // End of menu configuration
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case 3: //If the ID equals 3 , go forward
mWebView.canGoForward();
item.setIcon(R.drawable.forward);
return true;
}
return super.onOptionsItemSelected(item);
}
#Override
public boolean onKeyDown(int keyCode, KeyEvent event) { // Enables browsing to previous pages with the hardware back button
if ((keyCode == KeyEvent.KEYCODE_BACK) && mWebView.canGoBack()) { // Check if the key event was the BACK key and if there's history
mWebView.goBack();
return true;
} // If it wasn't the BACK key or there's no web page history, bubble up to the default
// system behavior (probably exit the activity)
return super.onKeyDown(keyCode, event);
}
}
I need to inject some javaScript files into loading page.
my code:
#Override
public void doUpdateVisitedHistory(WebView view, String url, boolean isReload) {
super.doUpdateVisitedHistory(view, url, isReload); //To change body of overridden methods use File | Settings | File Templates
mWebView.loadUrl("javascript:(function(){var script = document.createElement('script');script.setAttribute('src', 'file:///android_asset/jquery.js'); script.setAttribute('type', 'text/javascript'); document.body.appendChild(script)})();");
mWebView.loadUrl("javascript:(function(){var script = document.createElement('script');script.setAttribute('src', 'file:///android_asset/rangy-core.js'); script.setAttribute('type', 'text/javascript'); document.body.appendChild(script)})();");
mWebView.loadUrl("javascript:(function(){var script = document.createElement('script');script.setAttribute('src', 'file:///android_asset/rangy-serializer.js'); script.setAttribute('type', 'text/javascript'); document.body.appendChild(script)})();");
mWebView.loadUrl("javascript:(function(){var script = document.createElement('script');script.setAttribute('src', 'file:///android_asset/android.selection.js'); script.setAttribute('type', 'text/javascript'); document.body.appendChild(script)})();");// .
}
but it's not work =(
Change code to
mWebView.loadUrl("javascript:function loadScript(url, callback)" +
"{" +
" var head = document.getElementsByTagName('head')[0];" +
" var script = document.createElement('script');" +
" script.type = 'text/javascript';" +
" script.src = url;" +
" script.onreadystatechange = callback;" +
" script.onload = callback;" +
" head.appendChild(script);" +
"}");
mWebView.loadUrl("javascript:loadScript('file:///android_asset/jquery.js','callback')");
mWebView.loadUrl("javascript:loadScript('file:///android_asset/rangy-core.js','callback')");
mWebView.loadUrl("javascript:loadScript('file:///android_asset/rangy-serializer.js','callback')");
mWebView.loadUrl("javascript:loadScript('file:///android_asset/android.selection.js','callback')");
Now I have Error : 06-11 10:24:15.432: ERROR/Web Console(7962): Not allowed to load local resource: file:///android_asset/jquery.js at null:0
You can use js method to load js file and pass url as a parameter from java
Here is the full working example
[Activity.java]
public class MainActivity extends Activity {
WebView mWebView;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mWebView = (WebView) findViewById(R.id.webview);
WebSettings webSettings = mWebView.getSettings();
webSettings.setJavaScriptEnabled(true);
mWebView.addJavascriptInterface(new JavaScriptInterface(), "jsinterface");
mWebView.loadUrl("file:///android_asset/sq.html");
}
final class JavaScriptInterface {
JavaScriptInterface() {
}
#JavascriptInterface
public void windowLoaded() {
mWebView.loadUrl("javascript:loadScript('test.js', 'testing')");
Log.i("browser", "browser loaded");
}
}
}
[sq.html]
<html>
<head>
<script>
onload = function () {
window.jsinterface.windowLoaded();
}
//JS
function loadScript(url, callback)
{
console.log("loading script " + url);
// adding the script tag to the head as suggested before
var head = document.getElementsByTagName('head')[0];
var script = document.createElement('script');
script.type = 'text/javascript';
script.src = url;
// then bind the event to the callback function
// there are several events for cross browser compatibility
//script.onreadystatechange = callback;
script.onload = callback;
// fire the loading
head.appendChild(script);
}
</script>
</head>
<body>replace this
</body>
</html>
[test.js] inside assets folder
document.body.innerHTML = "<p>testing</p>";
on successful execution webview will show testing by loading test.js dynamically.
Here is how i ended up doing it. I used the Content:// protocol and set up a contentprovider to handle returning a file descriptor to the system
Here is my fileContentProvider:
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import org.apache.commons.io.IOUtils;
import android.content.ContentProvider;
import android.content.ContentValues;
import android.database.Cursor;
import android.net.Uri;
import android.os.ParcelFileDescriptor;
import android.util.Log;
public class FileContentProvider extends ContentProvider {
#Override
public ParcelFileDescriptor openFile(Uri uri, String mode) {
Log.d("FileContentProvider","fetching: " + uri);
ParcelFileDescriptor parcel = null;
String fileNameRequested = uri.getLastPathSegment();
String[] name=fileNameRequested.split("\\.");
String prefix=name[0];
String suffix=name[1];
// String path = getContext().getFilesDir().getAbsolutePath() + "/" + uri.getPath();
//String path=file:///android_asset/"+Consts.FILE_JAVASCRIPT+"
/*check if this is a javascript file*/
if(suffix.equalsIgnoreCase("js")){
InputStream is = null;
try {
is = getContext().getAssets().open("www/"+Consts.FILE_JAVASCRIPT);
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
File file = stream2file(is,prefix,suffix);
try {
parcel = ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY);
} catch (FileNotFoundException e) {
Log.e("FileContentProvider", "uri " + uri.toString(), e);
}
}
return parcel;
}
/*converts an inputstream to a temp file*/
public File stream2file (InputStream in,String prefix,String suffix) {
File tempFile = null;
try {
tempFile = File.createTempFile(prefix, suffix);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
tempFile.deleteOnExit();
FileOutputStream out = null;
try {
out = new FileOutputStream(tempFile);
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
try {
IOUtils.copy(in, out);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return tempFile;
}
#Override
public boolean onCreate() {
return true;
}
#Override
public int delete(Uri uri, String s, String[] as) {
throw new UnsupportedOperationException("Not supported by this provider");
}
#Override
public String getType(Uri uri) {
throw new UnsupportedOperationException("Not supported by this provider");
}
#Override
public Uri insert(Uri uri, ContentValues contentvalues) {
throw new UnsupportedOperationException("Not supported by this provider");
}
#Override
public Cursor query(Uri uri, String[] as, String s, String[] as1, String s1) {
throw new UnsupportedOperationException("Not supported by this provider");
}
#Override
public int update(Uri uri, ContentValues contentvalues, String s, String[] as) {
throw new UnsupportedOperationException("Not supported by this provider");
}
}
in the manifest i defined the provider:
<provider android:name="com.example.mypackage.FileContentProvider"
android:authorities="com.example.fileprovider"
/>
Here is the javascript o inject into the webview:
webView.loadUrl("javascript:(function() { "
+ "var script=document.createElement('script'); "
+ " script.setAttribute('type','text/javascript'); "
+ " script.setAttribute('src', 'content://com.example.fileprovider/myjavascriptfile.js'); "
/* + " script.onload = function(){ "
+ " test(); "
+ " }; "
*/ + "document.body.appendChild(script); "
+ "})();");
and here is the myjavascriptfile.js (as an example):
function changeBackground(color) {
document.body.style.backgroundColor = color;
}
You don't really need the content provider. Instead of
mWebView.loadUrl("file:///android_asset/sq.html");
you can use the following to load the original html file. Then the script will be able to upload test.js from the assets directory
mWebView.loadDataWithBaseURL("file:///android_asset/", data,
"text/html", null, null);