When embedding WebView in an application and loading html-pages in it, JavaScripts alert() do not work.Give me an example pls
The default WebChromeClient implemented by the embedded browser will discard javascript alerts, you should override the WebChromeClient implementation with your own version, this also allows you the ability to create your own custom alerts in place of the default one like so:
browser.setWebChromeClient(new MyWebChromeClient());
...
final class MyWebChromeClient extends WebChromeClient {
#Override
public boolean onJsAlert(WebView view, String url, String message, JsResult result) {
Log.d(LOG_TAG, message);
new AlertDialog.Builder(view.getContext()).setMessage(message).setCancelable(true).show();
result.confirm();
return true;
}
}
Related
I am having the following problem:
My website i prompting me with this JavaScript Popup
JS Popup Form
I need to create this wrapper for the webSite and I need somehow to process this dialog.
I have tried using ChromeWebView Client but my website is not loading at all with it.
I tried adding both WebView and ChromeWebView Clients to my WebView but it is still not working.
Here is some of my code:
String text = getIntent().getStringExtra("currentUrl");
this.myWebView.loadUrl(text);
WebViewClient webViewClientProceedSSL = new ProceedSSLClient(myWebView);
this.myWebView.setWebViewClient(webViewClientProceedSSL);
this.myWebView.setWebChromeClient(new WebChromeClient());
Do you have any ideas how can i fix that?
Edit 1:
I have forgotten to add my WebView Settings. Here are they:
webSettings.setJavaScriptEnabled(true);
webSettings.setAppCacheEnabled(true);
webSettings.setDomStorageEnabled(true);
webSettings.setJavaScriptCanOpenWindowsAutomatically(true);
webSettings.setGeolocationEnabled(true);
webSettings.setDomStorageEnabled(true);
Edit 2:
I have tried overriding both methods onJsAlert and onJsPrompt in the WebChromeClient class when setting it to the webView. This is not working as well...
myWebView.setWebChromeClient(new WebChromeClient() {
#Override
public boolean onJsAlert(WebView view, String url, String message, JsResult result) {
return super.onJsAlert(view, url, message, result);
}
});
I am not getting in that method at All - I tried logging something in it and debugging it. I never got in.
Add the following to the Webview
webView.getSettings().setLoadsImagesAutomatically(true);
webView.getSettings().setDomStorageEnabled(true);
webView.getSettings().setAppCacheEnabled(true);
webView.getSettings().setAllowFileAccess(true);
webView.getSettings().setJavaScriptEnabled(true);
Also try adding this method in WebViewClient
webview.setWebChromeClient(new WebChromeClient() {
#Override
public boolean onJsAlert(WebView view, String url, String message, JsResult result) {
//Required functionality here
return super.onJsAlert(view, url, message, result);
}
});
In our app have opening one url in webview there is way to close webview after some specific url detect.
how can possible to close webview ? I have try with window.close() in javascript.but could not work.have another way from android or ios app.
As per comments on question, I am putting a better term for closing the web view - going back to previous screen. You can do this as follows for Android and iOS :
Android :
finish()
iOS :
If you are using navigation controller :
self.navigationController?.popViewController(animated: true)
If you are presenting web view controller :
self.dismiss(animated: true, completion: nil)
The key is to check that url in the delegate function of web view.
Android :
public boolean shouldOverrideUrlLoading(WebView view, String url) {
if (url.equals("your_url")) {
finish()
return false;
} else {
return true;
}
}
iOS :
func webView(_ webView: UIWebView, shouldStartLoadWith request: URLRequest, navigationType: UIWebViewNavigationType) -> Bool {
if request.url?.absoluteString == "your_url" {
self.navigationController?.popViewController(animated: true)
// If controller is presented - self.dismiss(animated: true, completion: nil)
return false
}
return true
}
You can use shouldOverrideUrlLoading
Give the host application a chance to take over the control when a new url is about to be loaded in the current WebView. If WebViewClient is not provided, by default WebView will ask Activity Manager to choose the proper handler for the url. If WebViewClient is provided, return true means the host application handles the url, while return false means the current WebView handles the url.
Notes:
This method is not called for requests using the POST "method".
This method is also called for subframes with non-http schemes, thus it is strongly disadvised to unconditionally call loadUrl(String) with the request's url from inside the method and then return true, as this will make WebView to attempt loading a non-http url, and thus fail.
Here is the sample demo
public class MainActivity extends AppCompatActivity {
WebView myWebView;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
myWebView = findViewById(R.id.myWebView);
myWebView.setWebViewClient(new MyWebViewClient());
myWebView.getSettings().setJavaScriptEnabled(true);
myWebView.loadUrl("https://stackoverflow.com/users/7666442/nilesh-rathod?tab=topactivity");
}
public class MyWebViewClient extends WebViewClient {
#Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
if (url.equals("https://stackoverflow.com/users/7666442/nilesh-rathod?tab=profile")) {
finish() ;
Toast.makeText(MainActivity.this, "URL DETECTED", Toast.LENGTH_SHORT).show();
// perform your action here
return true;
} else {
view.loadUrl(url);
return true;
}
}
}
}
In iOS you would now be using Safari sfview so directly picking up a URL is blocked for security hence the only universal solution you can apply in both Android and iOS apps is deep linking.
You can trigger to close the safari sf view when you reach that specific URL using deep link.
Google deep link and follow the examples.
Use this can help you
myWebView.destroy();
myWebView = null;
I displaying the content in the webview. Content is loaded from the server API. Content can contain following links:
Register
How can i catch click on the #register.
I can do it with some HTML parser and append onClick event, but much better and easier will be to catch URL change to #register.
Many thanks for any advice.
Edit:
I tried the following example but without the luck
browser = (WebView) view.findViewById(R.id.intro_browser);
// Set Chrome instead of the standard WebView
browser.setWebViewClient(new WebViewClient() {
#Override
public boolean shouldOverrideUrlLoading(WebView view, String url){
Logger.d("TEST");
return true;
}
#Override
public void onLoadResource(WebView view, String url) {
Logger.d("URL IS:");
Logger.d(url);
if (url.startsWith("app://")) {
}
}
});
browser.getSettings().setJavaScriptEnabled(true);
browser.addJavascriptInterface(new WebViewJavaScriptInterface(getContext()),
Constants.Welcome.JAVASCRIPT_NAMESPACE);
//browser.getSettings().setAllowFileAccessFromFileURLs(true);
//browser.getSettings().setAllowUniversalAccessFromFileURLs(true);
browser.loadData(htmlContent, Constants.Welcome.MIME_TYPE, Constants.Welcome.ENCODING);
You need to create custom WebViewClient:
public class CustomWebViewClient extends WebViewClient {
#Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
boolean result = false;
if (Objects.equal(url, "#register")) {
result = true;
//Do what you want here
}
return result;
}
}
and then you need to give it to WebView:
webView.setWebViewClient(new CustomWebViewClient());
returning true in shouldOverrideUrlLoading means that you handled the Url while returning false means that WebView should handle it.
Add an intent filter in your manifest so that links starting with "yourapp://" or something similar will launch your app.
If you are generating the web content, then generate links for your app.
Register
If you are not generating the content, then use webview.load("javascript:// code to replace register links to yourapp://").
I have two activity, one main activity(A) is an CordovaActivity, then I use intent to start another activity(B), in B i have an WebView(not CordovaActivity), and after I use this webview to load a simple webpage (alert something), I found the js code is not executed at all, even if I enable javascript by calling setttings.setJsenabel(true);
I start activity B from A
Load Url from webview in Activity B
simple web page
in the device, it does not alert anything
However, if I change the webview to CordovaWebView instead of the original Android native one, it works.....
That's because plain WebView doesn't support showing alerts by itself. You need to provide a WebChromeClient to it that implements WebChromeClient.onJsAlert method. For example:
mywebView.setWebChromeClient(new WebChromeClient() {
#Override
public boolean onJsAlert(
WebView view, String url, String message, final JsResult result) {
new AlertDialog.Builder(view.getContext())
.setTitle("Alert")
.setMessage(message)
.setPositiveButton("Ok",
new AlertDialog.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
result.confirm();
}
}).setCancelable(false).create().show();
return true;
}
});
I have a webservice that I am trying to authenticate with in the background using a webview. When I initially send the request it will work appropriately (failure/success based on credentials), but after it seems like I am getting a cached response.
Here is my webview setup code:
WebView browser = new WebView(this);
WebSettings settings = browser.getSettings();
settings.setJavaScriptEnabled(true);
settings.setSavePassword(false);
settings.setCacheMode(WebSettings.LOAD_NO_CACHE);
settings.setAppCacheEnabled(false);
browser.setWebChromeClient(new WebChromeClient() {
public void onProgressChanged(WebView view, int progress) {
Log.d("BROWSERPROGRESS", Integer.toString(progress));
}
});
jsInterface = new AddAccountJSInterface();
browser.addJavascriptInterface(jsInterface, "ADDACCOUNTJSINTERFACE");
browser.setWebViewClient(new AddAccountClient(this));
So as you may see I have two additional classes controlling my webView:
An object that provides an interface for javascript (AddAccountJSInterface)
A WebViewClient
Additionally I do have a WebChromeClient, but it's only there for debugging and I'm pretty sure that it won't interfere with anything.
The JS interface simply provides an easy way of getting the body HTML for performing analysis, so I'm confident that isn't the issue either.
The WebViewClient has the following code in it which does most of the "custom" work for routing based on various responses from the webservice.
#Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
if(url.contains(INSTALL_PREFIX)) {
HashMap<String, String> params = extractParameters(url);
verificationComplete(params);
return true;
}
return false;
}
#Override
public void onPageFinished(WebView view, String url){
if(invalidShop(view)) {
Toast.makeText(context, context.getString(R.string.no_find_shop), Toast.LENGTH_SHORT).show();
shopAddressField.requestFocus();
replaceUiElements(loadingBar, addAccountButton);
} else if(url.contains(ADMIN_AUTH_LOGIN)) {
if(invalidLogin(view)) {
Toast.makeText(context, context.getString(R.string.invalid_login),Toast.LENGTH_SHORT).show();
emailField.requestFocus();
replaceUiElements(loadingBar, addAccountButton);
} else {
String email = emailField.getText().toString();
String password = passwordField.getText().toString();
String submitJS = String.format(FORM_SUBMISSION_JS, email, password);
jsInterface.setInnerHTML("");
browser.loadUrl(submitJS);
}
}
}
In my activity I have 3 text fields that I need to fill followed by clicking a button to submit it. The activity then takes in the data from 3 text fields (shopAddressField, usernameField, passwordField) and then executes some javascript that populates some form data (which was loaded in the invisible webView) then clicks the submit button.
It is the last part that is messing up, which appears to be caching the response from the server (perhaps using cookies?) and return that instead of asking the server if the data is correct or not.
A bit of clarification:
JSInterface is simply a Java object that allows me to execute javascript on my webview which is tied to a function within that object. In my case my JSInterface has one function which is setInnerHtml(String html).
This is the javascript that is executed on the webview:
javascript:window.ADDACOUNTJSINTERFACE.setInnerHTML(document.body.innerHTML)
And this is the setInnerHtml function:
public void setInnerHtml(String innerHtml) {
this.innerHtml = innerHtml;
}
So when I actually execute jsInterface.setInnerHtml("") I'm just over-writing the HTML that was pulled in (to be sure I'm not getting my old data from there for some reason).
As for my submitJS it is once again some Javascript that is executed on my webView as follows:
// submitJS will be something like this once all the credentials have been set
// Note: I know that the server will make jQuery available
// Note: Much of the Java string formatting has been removed to help clarify
// the code.
String submitJS =
"javascript:(function() {
$('login-input').value='username';
$('password').value='password';
$('sign-in-form').up().submit();
})()"
// I then simply get the webview to execute the javascript above
webView.loadData(submitJS);
So it turns out the problem wasn't based around the Caching, and possibly not cookies.
When executing javascript on your webView it does this in a separate thread and can be quite slow. This lead to a race condition which caused code to be executed in the wrong order.
I've solved this problem by using a Semaphore as a Mutex. This allows me to prevent my getter from returning before the Javascript on the webView is able to execute.
The interface I created now looks like this:
private class AddAccountJSInterface {
private final String TAG = getClass().getName().toUpperCase();
private Semaphore mutex = new Semaphore(1, false);
private String innerHTML;
public void aquireSemaphore() {
Log.d(TAG, "Attempting to lock semaphore");
try {
mutex.acquire();
} catch(InterruptedException e) {
Log.d(TAG, "Oh snap, we got interrupted. Just going to abort.");
return;
}
Log.d(TAG, "Semaphore has been aquired");
}
#SuppressWarnings("unused")
public void setInnerHTML(String html) {
this.innerHTML = html;
Log.d(TAG, "setInnerHTML is now releasing semaphore.");
mutex.release();
Log.d(TAG, "setInnerHTML has successfully released the semaphore.");
}
public synchronized String getInnerHTML() {
Log.d(TAG, "getInnerHTML attempting to aquire semaphore, may block...");
String innerHTML = "";
try {
mutex.acquire();
Log.d(TAG, "getInnerHTML has aquired the semaphore, grabbing data.");
innerHTML = this.innerHTML;
Log.d(TAG, "getInnerHTML no longer needs semaphore, releasing");
mutex.release();
} catch (InterruptedException e) {
Log.d(TAG, "Something has gone wrong while attempting to aquire semaphore, aborting");
}
return innerHTML;
}
}
Now the way I use this in my code is as follows:
// I have access to the jsInterface object which is an instance of the class above as well as a webView which I will be executing the javascript on.
String getInnerHtmlJS = "javascript:window.MYJSINTERFACE.setInnerHTML(document.body.innerHTML);"
jsInterface.aquireSemaphore()
// Execute my JS on the webview
jsInterface.loadUrl(getInnerHtmlJS)
// Now we get our inner HTML
// Note: getInnerHTML will block since it must wait for the setInnerHTML (executed via the JS) function to release the semaphore
String theInnerHTML = jsInterface.getInnerHTML();