Android WebView onPageFinished BUG - java

After update API (27) in Android OREO this code is no longer working:
public void onPageFinished(WebView view, String url) {
super.onPageFinished(view, url);
view.loadUrl("javascript:(function() {document.getElementById(\"imPage\").style.display='none';})()");
}
I have also tried with:
webView.loadUrl(
"javascript:(function() { " +
"document.addEventListener(\"DOMContentLoaded\", function(event) {" +
"document.getElementById(\"imPage\").style.display='none';" +
"});" +
"})()");
Element not hiding and debug return:
I/chromium: [INFO:CONSOLE(1)] "Uncaught TypeError: Cannot read property 'style' of null", source: mywebsite/ (1)
So I think the javascript is injected before loading page, this explains why the line is 1, because I have other code called after loading page is finished but this code is called when page is white, not loaded.

In my own project I have been using evaluateJavascript(script,null) in onPageFinished to hide html elements. view.loadUrl() Should work the same way.
If you don't need the function be called at later time you could simplify your JS string and instead of \" try using '.
public void onPageFinished(WebView view, String url) {
super.onPageFinished(view, url);
view.loadUrl("javascript:document.getElementById('imPage').style.display='none';");}

document.getElementById(\"imPage\") must be returning null.
So there is either no imPageelement or you haven't loaded the page at the time.
I would suggest moving your entire js code into
document.addEventListener("DOMContentLoaded", function(event) {
//insert here
});

You have to enable Javascript Seetings like below :-
view.getSettings().setJavaScriptEnabled(true); //Yes you have to do it

Related

Android Webview cannot render the pdf sometimes and shows blank/white page instead

Open the pdf in the webview using google docs
Open the same pdf or different pdf again and again.
Sometimes it will show the blank/white page in the android untill we refresh the webpage again for 1 or 2 times.
I have made the sample on the pdf. The link for the project is shown below:
https://github.com/gopalawasthi123/PdfWebView
Hope this will help you Better.
public void SetWebView(WebView webview,string externalUrl){
webview.Tag = "webview";
webview.Settings.JavaScriptEnabled = true;
webview.Settings.SupportZoom ();
webview.Settings.SetAppCacheEnabled(true);
webview.Settings.DomStorageEnabled = true;
webview.ZoomOut ();
webview.ZoomIn ();
webview.Settings.BuiltInZoomControls = true;
webview.Settings.LoadWithOverviewMode = true;
webview.Settings.UseWideViewPort = true;
//webview.Settings.SetSupportZoom (true);
webview.Settings.SetPluginState (WebSettings.PluginState.On);
webview.Settings.GetPluginState ();
if (externalUrl.StartsWith("http://") || externalUrl.StartsWith("https://"))
webview.LoadUrl (externalUrl);
webview.SetWebViewClient (new MonkeyWebViewClient (imgViewBack, imgViewForward, imgRefresh));
webview.SetWebChromeClient (new WebChromeClient ());
}
You can reload the page until it displays the pdf in this way:
public void onPageFinished(WebView view, String url) {
if (view.getTitle().equals(""))
view.reload();
}
After testing second PDF URL file, WebView seems like that can not load large PDF file.
Reason:
WebView display HTML. The fact that this works at all is by a hack- google will convert simple PDFs into HTML. It doesn't seem like they support anything that big. Even if they did, I would expect loading a large page PDF converted to HTML would be so large I highly doubt you'd be able to load it without going OOM. Use an appropriate PDF library, make a real PDF rendering view, and make sure not to render more of the PDF at a time than you need (or else you'll go OOM anyway). In other words, don't rely on hacky solutions you never should have relied on in the first place.
Solution:
You should try alternatives like PDF.js running locally in your device, instead of a service like Google Docs preview.(Or download PDF first to local file path)
Put it in your assets folder and tweak the example:
wv.loadUrl("file:///android_asset/web/viewer.html");
Also, you can have Out Of Memory situations. An alternative to try is a native viewer like AndroidPdfViewer.
We can solve the Problem in the two ways.
1. One is to use the Js.Pdf Plugin on the server end. It surely solve the problem but if we have multiple pdf's in the Fragment then it may cause the out of memory situations
and app can crash.
2. Second option is we can recursively called the function to load webview. This will also cause the issue but with less frequency Below is the code:
private void showPdf(final String imageString) {
pdfView.invalidate();
pdfView.getSettings().setJavaScriptEnabled(true);
pdfView.getSettings().setSupportZoom(true);
pdfView.loadUrl("http://docs.google.com/gview?embedded=true&url=" + imageString);
pdfView.setWebViewClient(new WebViewClient() {
boolean checkhasOnPageStarted = false;
#Override
public void onPageStarted(WebView view, String url, Bitmap favicon) {
checkhasOnPageStarted = true;
}
#Override
public void onPageFinished(WebView view, String url) {
if (checkhasOnPageStarted ) {
pdfView.loadUrl(removePdfTopIcon);
} else {
showPdf(imageString);
}
}
});
}
I was having the exact same issue and found that there was always a chance the WebView would not load on the first load attempt, especially if the pdf was on the larger side. The code I put together below works 100% of the time. From my beginner's understanding, it safely utilizes a separate thread to loop through and test the load status of the WebView, re-attempting a load of the view until successful. As this question was posted a year ago, I have generalized my solution to best benefit new viewers.
public class WebViewActivity extends AppCompatActivity {
String PDFView;
WebView webView;
String PDFBrowserView;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//Get the intended "PDFView"
PDFView = getIntent().getExtras().get("PDFView").toString();
//Have to manually encode (?) the url to display it
try {
PDFView = URLEncoder.encode(PDFView, "UTF-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
//Full display url
PDFBrowserView = "https://docs.google.com/gview?embedded=true&url=" + PDFView;
//Initialize a new "WebView" instance
webView = new WebView(WebViewActivity.this);
webView.getSettings().setJavaScriptEnabled(true);
webView.getSettings().setAllowFileAccessFromFileURLs(true);
webView.getSettings().setAllowUniversalAccessFromFileURLs(true);
webView.getSettings().setBuiltInZoomControls(true);
webView.getSettings().setJavaScriptEnabled(true);
webView.getSettings().setPluginState(WebSettings.PluginState.ON);
webView.getSettings().setDomStorageEnabled(true);
webView.getSettings().setLoadWithOverviewMode(true);
webView.getSettings().setUseWideViewPort(true);
//This handles callbacks (?)
webView.setWebChromeClient(new WebChromeClient());
//Call this to load page if page is blank with pdf url until page is not blank
checkPageFinished();
}
public void checkPageFinished() {
//If view is blank:
if (webView.getContentHeight() == 0) {
//Run off main thread to control delay
webView.postDelayed(new Runnable() {
#Override
public void run() {
//Load url into the "WebView"
webView.loadUrl(PDFBrowserView);
}
//Set 1s delay to give the view a longer chance to load before
// setting the view (or more likely to display blank)
}, 1000);
//Set the view with the selected pdf
setContentView(webView);
webView.postDelayed(new Runnable() {
#Override
public void run() {
//If view is still blank:
if (webView.getContentHeight() == 0) {
//Loop until it works
checkPageFinished();
}
}
//Safely loop this function after 1.5s delay if page is not loaded
}, 1500);
}
}
}
There are some ways through which we can identify whether a Page/URL is loaded properly or not from onPageFinished() method of the Webview and based on that reload() url.
Option 1 : To check with contentHeight of the webview.
override fun onPageFinished(view: WebView?, url: String?) {
if(view?.contentHeight == 0){
view?.reload()
return
}
super.onPageFinished(view, url)
}
Option 2 : To check with title of the webview.
override fun onPageFinished(view: WebView?, url: String?) {
if(view?.title.isNullOrEmpty()){
view?.reload()
return
}
super.onPageFinished(view, url)
}
Tested in multiple devices and working within API 29
in Kotlin
val webSettings: WebSettings = webview.settings
webSettings.javaScriptEnabled = true
webSettings.useWideViewPort = true
webSettings.loadWithOverviewMode = true
webSettings.domStorageEnabled = true
webview.webViewClient = AppWebViewClients()
// val TERM_CONDITION_URL = "http://docs.google.com/gview?embedded=true&url="
// + "YOUR_DOC_URL_HERE"
bindind?.webview?.loadUrl(TERM_CONDITION_URL)
and here AppWebViewClients class
class AppWebViewClients : WebViewClient() {
override fun shouldOverrideUrlLoading(view: WebView, url: String?): Boolean {
view.loadUrl(url)
return true
}
override fun onPageFinished(view: WebView?, url: String?) {
if (view?.contentHeight == 0)
view?.reload();
else {
super.onPageFinished(view, url)
}
}
}
I think you should do as below:
public void onPageFinished(WebView view, String url) {
if (!view.getUrl().equals(url)) {
view.loadUrl(url);
return;
}
}

How to send only a page of website to default browser and load rest of the website pages in webview?

I'm working on webview in android studio where I need to load a website but I also don't want to load some specific pages of that website in webview.
Let's assume I load https://stackoverflow.com/questions/ in my webview but I want to send https://stackoverflow.com/questions/ask this url to default browser whenever user wants to visit this url.
for doing this, I've tried this code in my webViewClient() but it doesn't work!
#Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
if(url.contains("https://stackoverflow.com/questions/ask")){
Intent i = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
startActivity(i);
return true;
}
return false;
}
as mentioned in the comments: If the Problem is, that this method never triggers I suspect the URL to be in another form than you expect it to be.
Try logging passed URL's like this:
#Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
Log.i("WebView", "shouldOverrideUrlLoading called with URL=" + url);
if(url.contains("questions/ask")){
Intent i = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
startActivity(i);
return true;
}
return false;
}
I suspect that there isn't a https but a http in front or that is is omitted completely or just a relative path to the one were you are (i.e. just "questions/ask")

android WebView shouldOverrideUrlLoading()

Hi this doesnt work for me:
webView.setWebViewClient(new WebViewClient() {
#Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
s="url clicked: "+url;
view.loadDataWithBaseURL(null, s, "text/html", "utf-8", null);
return true;
}
});
I have links in my html, it shows during loading in main activity,
but when I click them I get a white blank screen
I want to be able to read what those urls are, in the program, and I assume this url override function is the purpose for this.
I have tried with loadData() as well.
Thank you
You're not passing the new url to the WebView.
Should be
view.loadDataWithBaseURL(url, null, "text/html", "utf-8", null);
Edit: This also gives me a blank screen because loadDataWithBaseUrl expects you to provide some text or html as the data parameter yourself. Just use
view.loadUrl(url);
I didn't write http:// in my links, and wrote only x. This was the problem, it works now.

Load all resources through a proxy in webview android

How can i load resources dynamically from an android webview using onLoadResource() callback from a webviewClient?
Below is the code i have written so far. when i get new resources, it loads for example, the whole page of one single imagine instead of updating and displaying the image on the same original url of the webpage.
If a webpage has 5 images and text, my current code will load 5 pages each time onLoadResource tries to load an image.
what i want it to do is to load the images in the same page and any other resources as well such as JS, jquery's etc.
#Override
public void onLoadResource(WebView view, String url) {
addRequestToProxy(url);
}
public void addRequestToProxy(String url){
//pass url to proxy and wait for respoonse
String response;
//handle response
if(mime-type == IMAGE){
String urlStr = "http://example.com/my.jpg";
String pageData = "<img src=\"data:" + contentResponse.getMimeType()
+ ";base64," + contentResponse.getContent() + "\" />";
mWebView.loadDataWithBaseURL(urlStr, pageData, "text/html", null,
urlStr);
}else{
mWebView.loadDataWithBaseURL(null, response, "text/html", null,
null);
}
Instead of overriding onLoadResource() it's better to do it this way - shouldInterceptRequest()
webview shouldInterceptRequest example

Running Javascript in WebView when using LoadData

I am very new to Android Dev and I am developing what I thought would be a simple app. I have some HTML code that is stored is the raw resources folder - the code includes some Javascript. When I run the code in Google Chrome it runs fine - however when I load it into the webview using the loadData function it doesn't run the Javascript.
I have enabled javascript with:
mWebView.getSettings().setJavaScriptEnabled(true);
I need to be able to run this Javascript within the browser - any help?
Try this:
webView.loadDataWithBaseURL("blarg://ignored", getData(), "text/html",
"utf-8", "");
Try call JS function from code. Like this:
mWebView.loadUrl("javascript:myFunction()");
What does the loaded HTML data look like? In my case, the raw HTML data I generated was in the format:
<html>
<head>
<style>..</style>
<script>..</script>
</head>
<body>...</body>
</html>
Testing in a normal browser, things worked as expected. However, once I had used WebView.loadData, the CSS seemed to be recognized but I found that the Javascript was not working.
What found that worked for me was moving the Javascript to external files (more specifically I put the scripts I needed to assets/ using content providers.
<html>
<head>
<link rel="stylesheet" type="text/css" href="content://PATHTOSTYLESHEET" />
<script src="content://PATHTOJS"></script>
</head>
<body>...</body>
</html>
According to What is the difference between Android webview's loadData and loadDataWithBaseURL it sounds even more simple if you set a proper base URL which your style/scripts are stored - using the appropriate file:// for anything you store locally.
I am following up on #Cobaia's answer above, with another (I think) useful feature:
Since I need to keep changing the embedded HTML while testing and debugging, I decided to grab the raw page from my local web server during the development of the page and pass it to the webView as follows:
String url, str;
str = getFromURL(url);
webView.loadDataWithBaseURL("blarg://ignored", str, "text/html", "UTF-8", "");
where getFromURL() is defined as:
public String getFromURL(String urlToRead) {
URL url;
HttpURLConnection conn;
BufferedReader rd;
String result = "";
char[] chunk = new char[8192];
int blen = chunk.length;
int amt;
try {
url = new URL(urlToRead);
conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("GET");
rd = new BufferedReader(new InputStreamReader(conn.getInputStream()));
while ((amt = rd.read(chunk, 0, blen)) > 0) {
result += new String(chunk, 0, amt);
}
rd.close();
} catch (Exception e) {
e.printStackTrace();
}
return result;
}
Note that I had to create a special controller (I'm using CodeIgniter) to allow downloading the file as a text file from the server.
Hope this hint helps others too!
The above solutions did not work for me because I was loading a string that held the HTML instead of a separate HTML file. The HTML in the string referenced JavaScripts that were in assets/www/. What worked was to use mWv.loadDataWithBaseURL("file:///android_asset/www/", HTML_AS_STRING, "text/html", "UTF-8", null);
See below for a full example. The example create a webview, loads the HTML from the string, and then calls a function in a separate JavaScript file. The function simply shows an alert.
public class MainActivity extends Activity{
private WebView mWv;
private static final String HTML_AS_STRING =
"<!DOCTYPE html>" +
"<html>" +
"<head>" +
"</head>" +
"<body>" +
"<p>Just some text in a paragraph.</p>" +
"<div align=\"center\" style=\"color:#0000FF\">" +
"<h3>This is a heading in a div element</h3>" +
"<p>This is some text in a div element.</p>" +
"</div>" +
"<font size=\"4\" color=\"0000FF\">" +
"This is some sized and colored text." +
"</font>"
+
"<script src=\"index.js\"></script>" +
"</body>" +
"</html>";
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main_activity);
mWv = (WebView)findViewById(R.id.webview);
mWv.getSettings().setJavaScriptEnabled(true);
mWv.setWebViewClient(new WebViewClient(){
#Override
public void onPageFinished(WebView view, String url){
mWv.loadUrl("javascript:showAlert()");
}
});
mWv.setWebChromeClient(new WebChromeClient());
mWv.loadDataWithBaseURL("file:///android_asset/www/", HTML_AS_STRING, "text/html", "UTF-8", null);
}
}
index.js (A separate file in assets/www/):
function showAlert() {
alert("A simple alert!");
}

Categories