I am writing a basic web browser that can only go to a certain website (developed and maintained by another company) for my work, however in order for the log in and the time spent on the site to be counted (two VERY important things for my company) you need to log out with a certain button on the site.
I looked at the page source and all that button does is call a javascript function (named something like doLogoff() or something similar) which on a normal browser simply closes the window that is created after you log in.
In my application everything is done in ONE window, there are no tabs (there are no need for them) and I'm not entirely sure what the call to close the window does to my application, but the site on the WebView simply stays on that page and only goes back to the login page if you click on a link.
Is there anyway to detect when a certain JavaScript function is called in a WebView? If I can bind that function and make sure the log out is actually being performed, then I can just make the webview go to the login page myself.
You can do that with a JavascriptInterface. The following example comes from the documentation. It works the other way round. You can create a function in javascript that will trigger java code in your activity.
You declare your interface in your java code.
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();
}
}
You add the interface to your WebView
WebView webView = (WebView) findViewById(R.id.webview);
webView.addJavascriptInterface(new JavaScriptInterface(this), "Android");
And in your web page you can call the java method from a script
<script type="text/javascript">
function showAndroidToast(toast) {
Android.showToast(toast);
}
</script>
First, create the JavascriptInterface, prescribed by NathanZ.
Then override the function that you want to hook into, like this:
webview.loadUrl("javascript:" +
"var functionNameOriginal = functionName;" +
"functionName = function(args) {" +
"Android.showToast();" +
"functionNameOriginal(args);" +
"}");
Related
I wanted to implement in-app purchases to my web app without adding a side menu.
I use the RocketWeb Application Template for the Webapp found on codecanyon.
Is it possible to implement a trigger in the App and on the Website to make this happen:
User presses Button1 (HTML) that triggers a function in the App, and an In-app Purchase Modal pops up.
I want to do that because Google isn't too happy about adding direct payments.
The Modals n Co should be no problem, but I'm wondering if it's possible to "Bridge" App to Website basically.
Yes, its possible. You can bridge the Webview to your App - JS in WebView
What you need to do:
Make sure JS is enabled in your WebView
WebView myWebView = (WebView) findViewById(R.id.webview);
WebSettings webSettings = myWebView.getSettings();
webSettings.setJavaScriptEnabled(true);
Create JS listener interface, that can handle messages sent from the WebView:
public class WebAppInterface {
Context mContext;
/** Instantiate the interface and set the context */
WebAppInterface(Context c) {
mContext = c;
}
/** Show a toast from the web page */
#JavascriptInterface
public void openPurchaceModal(String toast) {
//TODO open your purchase screen using mContext as your context
}
}
Caution: If you've set your targetSdkVersion to 17 or higher, you must add the #JavascriptInterface annotation to any method that you want available to your JavaScript, and the method must be public. If you do not provide the annotation, the method is not accessible by your web page when running on Android 4.2 or higher.
Set your WebView's listener:
//Adds listener to the webView, under global JS object `Bridge` (You can use any name you want here)
webView.addJavascriptInterface(new WebAppInterface(this), "Bridge");
Use your Bridge object in JS inside the WebView, wherever you need it, like this:
Bridge.openPurchaceModal();
You can have multiple methods defined in your listener, and call them like:
Bridge.[method name]();
You also can have primitive arguments like String, int, boolean etc' to these methods:
...
#JavascriptInterface
public void showToast(String toast) {
Toast.makeText(mContext, toast, Toast.LENGTH_SHORT).show();
}
...
And use it:
Bridge.showToast("toast text");
I have a WebView in my app that displays a page not belonging to me. My desired behavior is, if any link is tapped by the user, the device's Browser app is launched, and the resulting page is loaded there. Unfortunately, this page is doing some weird things, so shouldOverrideUrlLoading() does not fire.
My attempted solution is to hook some javascript into pushState and use an interface to run Android code to launch the browser app.
Here is my interface:
public class LaunchExternalBrowserHack {
Context mContext;
LaunchExternalBrowserHack(Context c) {
mContext = c;
}
#JavascriptInterface
public void launchExternalBrowser(String url) {
openUrl(url);
}
}
I'm injecting some javascript into the page in onPageFinished():
public void onPageFinished(WebView view, String url) {
mWebView.loadUrl(javascript);
}
Here is my javascript:
private final String javascript = "javascript:history.pushState = function (state, title, url) { console.log(url); console.log(location.href); Uphoria.launchExternalBrowser(location.origin, url); };";
And, of course, I'm adding the interface to the WebView:
mWebView.addJavascriptInterface(new LaunchExternalBrowserHack(getContext()), "Android");
So this seems to work. The Browser app is launching and the next page is opening.
However, the WebView is also moving forward, too. I want to prevent this, but I cannot find a way to prevent the WebView moving forward while still allowing me to capture the forwarding url and launch the Browser app. As I mentioned earlier, with this webpage, shouldOverrideUrlLoading is not firing.
Ideas?
The user is trying to move away from the page. to stop him from moving out, return false. to let him move away, don't return anything but you can execute any code that you may want. This event is fired immediately when navigation event occurs including page refresh.
private final String javascript = "window.onbeforeunload = function(){ dosomething(); }; window.unload = function(){ dosomething(); }; function dosomething() { /* Write code here */ };"
You need to include the entire javascript code above, since some browsers use onbeforeunload and some use unload
EDIT
After careful reading of your problem I found my solution to be of little use.
Here's another approach using history. If your history is blank, that means its the first page. but if you have some items in history, it means that navigation has occurred.
You can check it as following
private final String javascript = "javascript:"+"if(window.history.length>=1 && document.referrer=='') Android.launchExternalBrowser(location.href);";
This answer is wrong. Kept here because it has importantly relevant comments.
The reason that you are not getting a result from your Javascript, is that you are not calling a function directly. You are assigning a function in your Javascript that returns a value, but it will not return that to your code, it will return that to whatever calls history.pushState().
private final String javascript = "javascript:" + "var pushState = history.pushState; history.pushState = function () " +
"{ pushState.apply(history, arguments); console.log(arguments); return location.href; " +
"};";
Instead, you can test whether something like this returns you a value:
private final String javascript = "(function() { return location.href; })();";
First a bit of background
I'm working on a Web application, that will be running within a WebBrowser, within a WPF application.
This is a temporary necessity while we're gradually moving functionality to the web app. As long as that's not finished, the WPF client is still needed. Ultimately the WPF client will phase out completely.
Now to the issue at hand
When the user closes the client (webpage), the webbrowser should catch that event and also close the window it is a child to.
I found this link describing what I would need: WebBrowser and javascript window.close()
Alas, I don't think the answer described there would still work, as it's not possible to even do a window.close(), because I'm not the one opening the window I'm running on. Browsers have (rightfully) tightened their security since then.
The question
Is there a way to trigger a Window close from the client, that bubbles up to the WPF?
Thanks.
I have used a WebBrowser control to call methods in a WPF application from the JavaScript before using WebBrowser.InvokeScript and WebBrowser.ObjectForScripting
See this MSDN article How to: Implement Two-Way Communication Between DHTML Code and Client Application Code
Also see this CodeProject article which looks like it might solve your problem
Call a C# Method From JavaScript Hosted in a WebBrowser
[ComVisible(true)]
public class ScriptManager
{
// Variable to store the form of type Form1.
private Window _window;
// Constructor.
public ScriptManager(Window window)
{
// Save the form so it can be referenced later.
_window = window;
}
// This method can be called from JavaScript.
public void MethodToCallFromScript()
{
// Call a method on the form.
_window.Close();
}
}
from code behind of Window:
webBrowser1.ObjectForScripting = new ScriptManager(this);
That worked, thanks!
I did the following:
[ComVisible(true)]
public class ScriptManager
{
protected Window Window { get; set; }
public ScriptManager(Window window)
{
this.Window = window;
}
public void CloseWindow()
{
this.Window.Close();
}
}
And in my Window (Loaded Event):
// Build browser
this.Browser = new WebBrowser();
this.Browser.Navigate(this.GetUri());
this.Browser.ObjectForScripting = new ScriptManager(this);
The client Javascript then does:
$scope.Close = function() {
window.external.CloseWindow();
}
I want to create a plugin for phone which pass and returns the value between javascript and android.
Can anybody suggest any ideas on how to do this?
Actually, this is not very difficult. Here, I will show you how to call native code from javascript within the page and vice-versa:
Calling native code from within web view:
When creating the web view add javascript interface (basically java class whose methods will be exposed to be called via javascript in the web view.
JavaScriptInterface jsInterface = new JavaScriptInterface(this);
webView.getSettings().setJavaScriptEnabled(true);
webView.addJavascriptInterface(jsInterface, "JSInterface");
The definition of the javascript interface class itself (this is exemplary class I took from another answer of mine and opens video in native intent)
public class JavaScriptInterface {
private Activity activity;
public JavaScriptInterface(Activity activiy) {
this.activity = activiy;
}
public void startVideo(String videoAddress){
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(Uri.parse(videoAddress), "video/3gpp"); // The Mime type can actually be determined from the file
activity.startActivity(intent);
}
}
Now if you want to call this code form the HTML of the page you provide the following method:
<script>
function playVideo(video){
window.JSInterface.startVideo(video);
}
</script>
Easy isn't it?
Calling javascript code from native code:
This is also simple suppose in the code of the HTML loaded in WebView you have javascript function defined:
<script>
function function(){
//... do something
}
</script>
Then you call this function through the WebView in the native code like that:
webView.loadUrl("javascript:function()");
Here's a tutorial for creating a PhoneGap Plugin. Also the instructions for the ChildBrowser plugin are especially good.
I am working on a project where I have a lot of interaction between JavaScript and managed code. In fact, I need the JS application to interface with the Silverlight application in the page right from the beginning. So I need the Silverlight application to load before the JS code gets executed.
But while doing so, most of the times I get the error that the object was not found as the Silverlight application has not yet been loaded. So I need the Silverlight to load before the JS executes.
Is there a way I can put a stop on the JS application until the Silverlight loads and then start executing?
First, you need to add an event handler to the Loaded event of your app, from that event handler you can call the javascript function like this:
using System.Windows.Browser;
namespace SilverlightApplication3
{
public partial class MainPage : UserControl
{
public MainPage()
{
InitializeComponent();
this.Loaded += new RoutedEventHandler(MainPage_Loaded);
}
void MainPage_Loaded(object sender, RoutedEventArgs e)
{
HtmlPage.Window.CreateInstance("SomeFunction", new string[] { "parameter1", "parameter2" });
}
}
}
Note that you need the System.Windows.Browser namespace to use HtmlPage