Changing HTML in a WebView programmatically - android

I'm loading an html asset page into a WebView using
webMain.loadUrl("file:///android_asset/record.html");
which works fine, but inside the html are a number of places where I'd like to use information from the app. For instance, the HTML may contain text that reads "[Custom]". Is there a way I can replace that word with information passed from the application?

This is an old and already accepted question, however I am sure that the problem can be solved in more elegant way by using javascript.
Keep the html file in your assets folder and surround the text which you want to replace into with div elements with unique id's.
<html>
<head> ... <head>
<body>
Static text
<div id="replace1">replace me</div>
<div id="replace2">replace me too</div>
More static text ...
</body>
</html>
Now create a javascript function which will replace the innerHtml of a div with an id:
function replace(id, newContent)
{
document.getElementById(id).innerHTML = newContent;
}
This function will be best placed directly in the html file, update the <head> section to look like this:
<head>
...
<script type="text/javascript">
function replace(id, newContent)
{
document.getElementById(id).innerHTML = newContent;
}
</script>
</head>
Now we need to call the javascript function from from the WebView Android api:
WebView helpView = (WebView)findViewById(R.id.helpView);
helpView.getSettings().setJavaScriptEnabled(true);
helpView.setWebViewClient(new WebViewClient() {
#Override
public void onPageFinished(WebView view, String url) {
super.onPageFinished(view, url);
view.loadUrl("javascript:replace('replace1', 'new content 1')");
view.loadUrl("javascript:replace('replace2', 'new content 2')");
}
});
helpView.loadUrl("file:///android_asset/help.html");
Using this you will avoid reading potentially large data into memory and running expensive operations on it unnecessarily.

This is worked for me.
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Payment Demo</title>
</head>
<body>
<div>
<input type="text" id="uname " name="uname " value="">
<input type="text" id="pass" name="pass" value="">
</div>
</body>
</html>
This is java code.
WebView wb = (WebView) findViewById(R.id.webView1);
wb.loadUrl("file:///android_asset/web1.html");
wb.getSettings().setJavaScriptEnabled(true);
wb.setWebViewClient(new WebViewClient() {
#Override
public void onPageFinished(WebView web, String url) {
// TODO Auto-generated method stub
String uname = "email#mail.com";
String pass = "******";
web.loadUrl("javascript:(function(){document.getElementById('uname').value = '"+uname+"';})()");
web.loadUrl("javascript:(function(){document.getElementById('pass').value = '"+pass+"';})()");
}
});

Actually I do not understand why the file size of record.html will affect maintainence of the code. Read the html string (using Java reader class or what ever) from the html file in asset, use replaceAll function with Regex to replace all the [Custom] in the html file. How long the html is should not really affect how you maintain the code. It should rather be a performance problem, or the string is really really long that exceeds the java String limit.
some code I have used before :
InputStream is = getApplicationContext().getAssets().open("details/product_jsmodify.html");
Reader r = new InputStreamReader(is);
String details = Utils.readertoString(r);
details = details.replace("%product_name%",productName );
Utils is my class doing the conversion to string. I am not using Regex here as I am only replacing word for once. Then I load the string like Cata does. It is quite clean I suppose.

Yes you can do that by loading your page in a String and then load that string in your WebView.
Eg:
String summary = "<html><body>You scored <b>192</b> points.</body></html>";
webview.loadData(summary, "text/html", null);
Taken from here

This one worked for me, with the html along with the text and images.
InputStream is = getAssets().open(html_name);
int size = is.available();
byte[] buffer = new byte[size];
is.read(buffer);
is.close();
String str = new String(buffer);
str = str.replace("InitialTextToBeReplaced", "TextAfterReplacement");
//Now instead of webview.loadURL(""), I needed to do something like -
webView.loadDataWithBaseURL("file:///android_asset/", str, "text/html", "UTF-8",null);

Related

Is it possible to add custom CSS to a WebView in Xamarin using ONLY ONE stylesheet in my Assets folder?

I've seen multiple tutorials adding a GetBaseUrl separately for IOS and Android, but they don't answer my question. I was wondering if there is no other way to add custom CSS to a webview and only use 1 stylesheet (default.css). The reason why I want to do this is because my stylesheet is identical for both IOS and Android and contains little styling.
This is what I've tried:
I have a WebView:
<WebView.Source >
<HtmlWebViewSource Html="{Binding Data.Content}" />
</WebView.Source>
The Source of this WebView is a string that looks like this:
string contentString = #"<html>
<head>" +
"<link rel='stylesheet' href='default.css'"
"</head>" +
"<body style='text-align:left;background-color:white;font-size:16px;margin:0;'>" +
value +
"</body>" +
"</html>";
The default.css file mentioned above is in my Assets folder and has Build Action EmbeddedResource, located in the root directory of my project:
Can anyone help? Thanks in advance.
There are some ways to do this, one is to use HtmlWebViewSource, the html is like this:
htmlSource.Html = #"<html>
<head>
<link rel=""stylesheet"" href=""default.css"">
</head>
<body>
<h1>Xamarin.Forms</h1>
<p>The CSS and image are loaded from local files!</p>
<img src='XamarinLogo.png'/>
<p>next page</p>
</body>
</html>";
Another way is to use WebView.loadDataWithBaseURL, there is the same thread that you can take a look:
Rendering HTML in a WebView with custom CSS
The easiest way is to wrap your HTML with CSS (or replace link tags with its resulting content)
var html = AddHtmlAndCssTags("<div>my html</div>", ReadCss());
protected string ReadCss()
{
var resourceName = $"MyProj.Resources.Css.article.css";
var isExists = ResourceLoader.IsEmbeddedResourceExists(Assembly.GetAssembly(typeof(ResourceLoader)), resourceName);
if (isExists)
{
return ResourceLoader.GetEmbeddedResourceString(Assembly.GetAssembly(typeof(ResourceLoader)), resourceName);
}
return string.Empty;
}
protected string AddHtmlAndCssTags(string html, string css)
{
var cssTag = string.IsNullOrEmpty(css) ? string.Empty : $"<style>{css}</style>";
return $"<html><head>{cssTag}</head><body>{html}<body></html>";
}

android get data from activity and display in html

I wanted to put variable from my activity and display it in html. Below is how I insert the variable into webview but I'm not sure how am I suppose to get the data from html and display it in "textview" form?
EDIT
Java
webView = (WebView)findViewById(R.id.webView);
webView.setWebViewClient(new WebViewClient());
webView.getSettings().setJavaScriptEnabled(true);
webView.getSettings().setJavaScriptCanOpenWindowsAutomatically(true);
webView.getSettings().setDatabaseEnabled(true);
webView.getSettings().setDatabasePath("/data/data/com.example.hybrid/hybrid_user");
webView.loadUrl("file:///android_asset/www/index.html");
webView.loadUrl("javascript:callFromActivity(\""+userid+"\")");
html
<body>
<form>
<fieldset>
<legend>Hybrid Application</legend>
<p>
<label>UserName: </label>
</p>
<p id="mytext">Hello!</p>
<p>
<label>Mobile Number :</label>
<input type = "text"
id = "mobileNum" />
</p>
<button type="button"
onclick="updateRecord()">Submit</button>
<div id="output"></div>
</fieldset>
</form>
<script>
function errorHandler(transaction, error){
alert('Error:'+error.message+' (Code '+error.code+')');
return true;
}
window.onerror = errorHandler;
var shortName = 'hybrid_user';
var version = '1.0';
var displayName = 'Hybrid Database';
var maxSize = 65536;
var db = openDatabase(shortName, version, displayName, maxSize);
function updateRecord(id, textE1){
database.transaction(function(tx){
tx.executeSql("UPDATE hybrid_user SET mobile = ? WHERE userid = ?", [textE1.innerHTML, id], null, onError);
})
}
function callFromActivity(msg){
document.getElementById("mytext").innerHTML = msg;
}
</script>
I'm new to html and I don't know what's wrong with my code, it did not display out the userid in the html page. Any comments and answers will be appreciated!
Edit update
webView.loadUrl("javascript:callFromActivity(\""+userid+"\")");
Apparently is the system didn't go through this line of code. Is there anyway go through this code?
try change innerHTML for value like this:
function callFromActivity(msg) {
document.getElementById("mytext").value = msg;
}
webView.setWebViewClient(new WebViewClient(){
#Override
public void onPageFinished(WebView view, String url){
webView.loadUrl("javascript:callFromActivity(\""+userid+"\")");
}
});
I solved my problem with this. When the page loads finished it will execute the above code and get the userid from the activity.

Passing data from java class to Web View html

I'm loading below html in my webView
https://mail-attachment.googleusercontent.com/attachment/?ui=2&ik=25c0c425c6&view=att&th=138db54ff27ad34b&attid=0.1&disp=inline&realattid=f_h5ahtmbe0&safe=1&zw&saduie=AG9B_P9YNooGjsk_jLefLptQ9q15&sadet=1343790299575&sads=-yBVsLKP_2mh7zMfYLCF7sL1u-w
Now what I want to do is to fill the textbox in the html that came from my java class variable and then automatically hit submit.
But I don't have any idea how to do this.
Any thougths will be appreciated.
First, your URL seems not available.
If you want to do data exchange between android app and your web app/web page you can achieve this via javascript.
Here is an example from Android official site:
Create a class like this:
public class JavaScriptInterface {
Context mContext;
/** Instantiate the interface and set the context */
JavaScriptInterface(Context c) {
mContext = c;
}
/** Show a toast from the web page */
#JavascriptInterface
public void showToast(String toast) {
Toast.makeText(mContext, toast, Toast.LENGTH_SHORT).show();
}
}
In your WebView:
WebView webView = (WebView) findViewById(R.id.webview);
webView.addJavascriptInterface(new JavaScriptInterface(this), "Android");
In your web page:
<input type="button" value="Say hello" onClick="showAndroidToast('Hello Android!')" />
<script type="text/javascript">
function showAndroidToast(toast) {
Android.showToast(toast);
}
</script>
If you wanna pass something to your webpage, just calling corresponding javascript function:
String str = "xxx";
myWebView.loadUrl("javascript:xxx('"+str+"')");
Here is the Reference:
http://developer.android.com/guide/webapps/webview.html
I would add that the load of the javascript function should be done when the html is loaded. To control that, you can use the following:
webview.getSettings().setJavaScriptEnabled(true);
webview.loadUrl("file:///android_asset/test.html");
webview.setWebViewClient(new WebViewClient(){
public void onPageFinished(WebView view, String url){
webview.loadUrl("javascript:init('" + theArgumentYouWantToPass + "')");
}
});
test.html
<!DOCTYPE html>
<html>
<head>
<title>Test</title>
</head>
<body>
hola
adios
</body>
<script type="text/javascript">
function init(val){
// Do whatever you want with your parameter val
}
</script>
</html>
Taken from Uncaught ReferenceError: myFunction is not defined at null:1 Android exception in webview
Just enable DOM Storage and write var x= to string:
webview.getSettings().setJavaScriptEnabled(true);
web.getSettings().setDomStorageEnabled(true);
webview.loadUrl(urlString);
webview.setWebViewClient(new WebViewClient(){
public void onPageFinished(WebView view, String url) {
super.onPageFinished(view, url);
String js = "javascript:var x =document.getElementById('username').value = '"+user+"';var y=document.getElementById('password').value='"+pass+"';";
if (Build.VERSION.SDK_INT >= 19) {
view.evaluateJavascript(js, new ValueCallback<String>() {
#Override
public void onReceiveValue(String s) {
}
});
}
else {
view.loadUrl(js);
}
}
Be careful to call javascript function like this, the str may include single quote or other special characters.
String str = "xxx";
myWebView.loadUrl("javascript:xxx('"+str+"')");
I suggest to encode the str in base64, and decode it on javascript side.
Android
String str = "xxx";
//encode in base64
String base64Str = Base64.encodeToString(str.getBytes(), Base64.NO_WRAP);
myWebView.loadUrl("javascript:xxx('"+ base64Str +"')");
Javascript
function xxx(val) {
//decode from base64
var str = atob(data)
}
Pass the paramter directly in the url
webView.loadUrl("file:///android_asset/animation.html?message=testing");
Get the paramter in html file
var url_string = window.location.href
var url = new URL(url_string);
var message= url.searchParams.get("message");
Solutions by Hungr would work, but using the same document they point out, I do the following:
in my Android code WebAppInterface class:
#JavascriptInterface
fun provideData(val input: String): String{
val output = ""
//some operation with input
return output
}
then in host activity for webview:
webView.addJavascriptInterface(WebAppInterface(this), "Provider")
Inside your JS or HTML:
document.getElementbyId("Text").innerhtml = Provider.provideData(input);

How to pass parameter into HTML file from android

I can show up HTML file content in android webview well.Now how could i pass parameter into HTML file.For ex.my HTML content has an video player
i need to pass dynamic values(URL) into HTML file for playing dynamic video.My HTML file is located on asset folder.How could i do this?
Thanks.
I came upon this problem today, however I needed this to work with UTF-8 encoding, so this was my approach, hopefully it will help someone and clarify some of the previous answers to this question.
HTML:
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
</head>
<body>
<h1>%ERR_TITLE%</h1>
<h2>%ERR_DESC%</h2>
</body>
</html>
Java:
String content = IOUtils.toString(getAssets().open("error.html"))
.replaceAll("%ERR_TITLE%", getString(R.string.error_title))
.replaceAll("%ERR_DESC%", getString(R.string.error_desc))
mWebView.loadDataWithBaseURL("file:///android_asset/error.html", content, "text/html", "UTF-8", null);
As for IOUtils:
http://commons.apache.org/proper/commons-io/download_io.cgi
Instead of passing directly the video URL (following you example), i would have used tokens in the Html file. For example:
<embed src="$VIDEO_URL$" autostart="false" />
where the $VIDEO_URL$ will be the token wich will be replaced during the runtime with a real video URL.
Also, since you cannot change the contents of your asset folder during runtime you should load the html file contents into a String variable and use the replace method to replace the token with a real URL and, finally, pass that string to your webview. Something like this:
//The html variable has the html contents of the file stored in the assets folder
//and real_video_url string variable has the correct video url
html = html.replace("$VIDEO_URL$", real_video_url);
webview.loadData(html, "text/html", "utf-8");
If i would like to have something dynamic in my HTML i would have an html with dynamic parts written like this:
<B>%NAME%</B>
Then i would load my HTML:
String template = Utils.inputStreamToString(assets.open("html/template.html"));
then
Then i would replace all dynamics parts with what i want like this:
String data = template.replaceAll("%NAME%", "Alice McGee");
then i would pass it to my webView!
WebView webView = new WebView(this);
webView.loadDataWithBaseURL("file:///android_asset/html/", data, "text/html", "utf-8", null);
I managed to pass variables in a different way.
My problem was that everytime 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 with the default URL (www.yoururl.com) and onPageFinished I call a new javascript method where I pass the variables.
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
}
}
here assets means what?
String template = Utils.inputStreamToString(assets.open("html/template.html"));

Android 4.0.1 breaks WebView HTML 5 local storage?

I have a simple html5 test page which uses LocalStorage to display / save / redisplay a piece of data.
This code works perfectly in Android 2.3.x but logs an exception in 4.0.1 on line 18 of the html which is the frist localStorage.getItem() call and at this point the JS stops.
Exception: Uncaught Error: SECURITY_ERR: DOM Exception 18 at /data/data/my.app.name/app_htmlData:18
I've also tried setting the database path to getCacheDir() with the same result.
String htmlContent = "HTML content listed below";
File sharedDir = getActivity().getDir("htmlData", Context.MODE_PRIVATE);
WebView browser = (WebView)v.findViewById(R.id.wvBrowser);
browser.setWebChromeClient(new WebChromeClient(){
public void onExceededDatabaseQuota(String url, String databaseIdentifier, long currentQuota, long estimatedSize, long totalUsedQuota, WebStorage.QuotaUpdater quotaUpdater) {
quotaUpdater.updateQuota(estimatedSize * 2);
}
});
browser.setWebViewClient(new WebViewClient(){
#Override
public void onPageFinished(WebView view, String url){
view.loadUrl("javascript:doTest()");
});
browser.getSettings().setDatabaseEnabled(true);
browser.getSettings().setDatabasePath(sharedDir.getPath());
browser.getSettings().setDomStorageEnabled(true);
browser.loadDataWithBaseURL(mSharedDir.getPath(),
htmlContent,
"text/html",
"utf-8",
null);
The HTML that the page is rendering is as follows:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head>
<title>Simple localStorage test</title>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>
<script type="text/javascript">
function doTest() {
$('#stuff').append('<p>reading</p>');
var item = read();
$('#stuff').append('<p>writing</p>');
localStorage['bar'] = new Date().toUTCString();
$('#stuff').append('<p> </p><p>reading again</p>');
read();
}
function read() {
var item = localStorage.getItem('bar');
if (item == null || (item == undefined)) {
item = '';
}
$('#stuff').append('<p> item: ' + item + '</p>');
return item;
}
</script>
</head>
<body>
<p>-Simple localStorage test-</p>
<div id="stuff"></div>
</body>
</html>
Source available here
Via some discussion with a Google engineer it seems that they've made the decision that the file:// scheme is insecure.
A work around for this is to do the following
browser.loadDataWithBaseURL("http://www.example.com",
htmlContent,
"text/html",
"utf-8",
null);
For android versions less than 4.4, loading data into a webview with a file scheme as a directory:
browser.loadDataWithBaseUrl("file:///android_asset/", html, "text/html", "UTF-8", null);
wont work with localStorage. If I add a filename it does work on older OS versions
browser.loadDataWithBaseUrl("file:///android_asset/test.html", html, "text/html", "UTF-8", null);

Categories

Resources