I just want to call the following code of javascript on longclick on webview to disable copying/selection content of web view
var touching = null;
$('selector').each(function() {
this.addEventListener("touchstart", function(e) {
e.preventDefault();
touching = window.setTimeout(longTouch, 500, true);
}, false);
this.addEventListener("touchend", function(e) {
e.preventDefault();
window.clearTimeout(touching);
}, false);
});
function longTouch(e) {
// do something!
}
For that you have to define a class in java for the webInterface to interact with it, say
WebInterface.class
public class WebAppInterface {
Context mContext;
WebAppInterface(Context c) {
mContext = c;
}
#JavascriptInterface
public void yourMethod(String parameter) {
//Do your stuff here
}
}
you have to initialize this interface with your webview as below, considering you named your interface as 'interface' in your code
browser.addJavascriptInterface(new WebAppInterface(this), "interface");
then comes the web part, all you have to do is just define a function there and call your method from android interface and your app shall interact from your webview inside application
Your Web Code
<script type="text/javascript">
function callYourMethod(parameter) {
interface.yourMethod(parameter); //this value goes to android code
}
</script>
For further query and understanding you can visit this link too javascript Interface Android
Related
As an example, let's assume for a project, I have a WebView instance I'm using in my main activity loading a local HTML file:
class MainActivity : AppCompatActivity() {
lateinit var myWebView:WebView
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
myWebView = findViewById(R.id.webview)
myWebView.getSettings().setAllowContentAccess(true);
myWebView.getSettings().setAllowFileAccess(true);
myWebView.settings.javaScriptEnabled = true;
myWebView.setWebChromeClient(WebChromeClient());
myWebView.loadUrl("file:///android_asset/websrc/mainPage.html")
myWebView.addJavascriptInterface(WebAppInterface(this), "Android")
}
}
class WebAppInterface(private val mContext: Context) {
#JavascriptInterface fun getStatus() {
MainActivity().myWebView.evaluateJavascript("setStatus('good to go!')", null)
//Or: MainActivity().myWebView.loadURL("javascript:setStatus('good to go!')")
}
}
I have a JavascriptInterface also, where from I would like to invoke Android.getStatus() in my HTML file:
<html>
<head>
...
</head>
<body>
<div id="status"></div>
</body>
<script>
document.addEventListener('DOMContentLoaded', function() {
Android.getStatus()
})
function setStatus(status) {
document.querySelector('#status').innerHTML = status
}
</script>
</html>
So far, I think understand why this wouldn't work. From what I've come to understand from answers like this or this, And especially in the documentation for WebView, I understand that the JavascriptInterface and the WebView are running on different threads, so something like setStatus() wouldn't be defined. So far, I've tried using runOnUiThread and Handler(). See an example for runOnUiThread:
#JavascriptInterface fun getStatus() {
MainActivity().runOnUiThread(
object : Runnable {
override fun run() {
MainActivity().myWebView.evaluateJavascript("setStatus('good to go!')", null)
//Or: MainActivity().myWebView.loadURL("javascript:setStatus('good to go!')")
}
}
)
}
This (and use of Handler), however, gives me: E/AndroidRuntime: FATAL EXCEPTION: JavaBridge and lateinit property myWebView has not been initialized. I really don't understand why. Should mention I'm new to Kotlin and Android.
Any help?
All I needed was to pass myWebView to the JavaScript interface along with this and use Runnable...
Creating the JS interface:
myWebView.addJavascriptInterface(WebAppInterface(this, myWebView), "Android")
And then inside a #JavascriptInterface function:
myWebView.post(Runnable {
myWebView.loadUrl("javascript:customFunction()")
})
OK so this works on Android... I'm stuck on iOS.
I have a custom HTML WebView which has a JavaScript event:
const string html= #"<button type=""button"" onClick=CSharp.SendButton(' + buttonCode + ')"">Click here</button>";
On Android, I have a webview custom renderer:
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (Control != null)
{
Control.Settings.JavaScriptEnabled = true;
Control.AddJavascriptInterface(new JSBridge(Forms.Context), "CSharp");
}
base.OnElementPropertyChanged(sender, e);
}
And JSBridge is a bridge between Javascript and C#:
public class JSBridge : Java.Lang.Object
{
Context context;
public JSBridge(Context context)
{
this.context = context;
}
[Export]
[JavascriptInterface]
public void SendButton(string code)
{
// I can pass the buttonCode here
}
}
How can I do something similar on iOS?
Please note: I am aware of the HybridWebView examples on the Microsoft website: I dont want to use a seperate Javascript file, I want to fire the event from my HTML and catch it in my renderer.
We are implementing an app where we have communication between Javascript and c#. Our UIWebView has a button to invoke some native functionality. On a UIWebView i have an handler on ShouldStartLoad.
webView.ShouldStartLoad = myHandler;
bool myHandler (UIWebView webView, NSUrlRequest request, UIWebViewNavigationType navType)
{
}
This gets called everytime page loads. Indeed, i would like to only call it from an event from WebView such as on a button click.
From Javascript i have
window.location.href = "myapp://action?par1=abc&par2=def";
How to call a particular function from custom url?
Calling JavaScript from c#
I am trying to call back JavaScript from c# but it is not calling TestShow() function
wkWebView.EvaluateJavaScript(string.Format("TestShow()"), (r, e) =>
{
Console.WriteLine("In EvaluateJavaScript");
if (e != null) Console.WriteLine(e);
});
JavaScript side i have a Alert but it is not showing that alert
function TestShow()
{
alert("Hello! I am an alert box!!");
}
You can either continue using UIWebView and parse the NSUrlRequest to see if it is the call you're looking for. Then return true/false accordingly.
A better option would be to use WKWebView and create a custom message handler. Something like this:
1) Implement IWKScriptMessageHandler (tested on the default UIView created by Xamarin UIViewController)
public class UniversalView : UIView, IWKScriptMessageHandler
{
public void DidReceiveScriptMessage(WKUserContentController userContentController, WKScriptMessage message)
{
var msg = message.Body.ToString();
System.Diagnostics.Debug.WriteLine(msg);
}
}
2) Create user controller with a handler for "myapp" (this = the IWKScriptMessageHandler)
var userController = new WKUserContentController();
userController.AddScriptMessageHandler(this, "myapp");
3) Create a config with the controller
var config = new WKWebViewConfiguration
{
UserContentController = userController
};
4) Create the WKWebView with the config
var webView = new WKWebView(new CGRect(10, 100, 500, 500), config);
5) Call "myapp" from your JS code
<html><head><meta charset = "utf-8"/></head><body>
<button onclick="callCsharp()">Click</button>"
<script type="text/javascript">
function callCsharp(){
window.webkit.messageHandlers.myapp.postMessage("action?par1=abc&par2=def");
}</script></body></html>";
EDIT: In regards to evaluating JS from C# you need to be sure the HTML page has finished loading or otherwise the call will result in an error. You can handle navigation events by implementing IWKNavigationDelegate
public class UniversalView : UIView, IWKScriptMessageHandler, IWKNavigationDelegate
{
[Export("webView:didFinishNavigation:")]
public void DidFinishNavigation(WKWebView webView, WKNavigation navigation)
{
webView.EvaluateJavaScript("callCsharp()", (result, error) =>
{
if (error != null) Console.WriteLine(error);
});
}
Assign it to the WKWebView you created:
var webView = new WKWebView(new CGRect(10, 100, 500, 500), config)
{
WeakNavigationDelegate = this
};
I am prototyping a HTML5 app using the Xamarin framework. I am trying to connect javascript to C# code and vice versa.
What is the best way to do this? So far I have tried creating Mono C# bindings to the Cordova framework, which is not an ideal solution and am looking for something more straightforward that takes advantage of a common .Net centric approach. So I have tried setting up a HttpListener to handle app http requests from javacript calls. This method works well with Monotouch, but doesn't with Mono for Android
Javascript call:
<script type="text/javascript">
function myFunction()
{
var request = new XMLHttpRequest();
request.open('GET','http://127.0.0.1:8001/callnative', false);
request.send();
if(request.status == 200){
alert("Callback!");
}
else{
alert("Error");
}
}
</script>
Here is the C# code in the Activity class:
[Activity (Label = "AnroidHTML5Prototype", MainLauncher = true)]
public class Activity1 : Activity
{
int count = 1;
public HttpListener listener;
protected override void OnCreate (Bundle bundle)
{
base.OnCreate (bundle);
SetupListener ();
// Set our view from the "main" layout resource
SetContentView (Resource.Layout.Main);
// Get our button from the layout resource,
// and attach an event to it
Button button = FindViewById<Button> (Resource.Id.myButton);
button.Click += delegate {
button.Text = string.Format ("{0} clicks!", count++);
};
LoadWebView ();
}
private void LoadWebView()
{
string lfn = "index.html";
string html = string.Empty;
// context could be ApplicationContext, Activity or
// any other object of type Context
using (var input = this.ApplicationContext.Assets.Open(lfn))
using (StreamReader sr = new System.IO.StreamReader(input))
{
html = sr.ReadToEnd();
}
WebView webView = FindViewById<WebView> (Resource.Id.webView1);
webView.SetWebChromeClient(new WebChromeClient());
webView.Settings.JavaScriptEnabled = true;
webView.LoadData (html, "text/html", null);
webView.Settings.AllowFileAccess = true;
webView.Settings.BlockNetworkLoads = false;
}
private void SetupListener() {
listener = new HttpListener();
listener.Prefixes.Add("http://*:8001/");
listener.Start();
listener.BeginGetContext(new AsyncCallback(HandleRequest), listener);
}
public void HandleRequest (IAsyncResult result)
{
RunOnUiThread(delegate{
//Get the listener context
HttpListenerContext context = listener.EndGetContext(result);
//Start listening for the next request
listener.BeginGetContext(new AsyncCallback(HandleRequest), listener);
string response = String.Empty;
if (context.Request.Url.ToString ().Contains ("callnative")) {
response = "native code reply";
}
byte[] responseBytes = System.Text.Encoding.UTF8.GetBytes(response);
context.Response.ContentType = "text/json";
context.Response.StatusCode = (int)HttpStatusCode.OK;
context.Response.ContentLength64 = responseBytes.Length;
context.Response.OutputStream.Write(responseBytes, 0, responseBytes.Length);
context.Response.OutputStream.Close();
// WebView webView = FindViewById<WebView> (Resource.Id.webView1);
// webView.LoadUrl("javascript:(function() { " +
// "alert('Callback!'); " +
// "})()");
});
}
}
Similar code works fine in Monotouch, but in Mono for Android, I get the following errors:
[Web Console] XMLHttpRequest cannot load http:// 127.0.0.1:8001/callnative. Cannot make any requests from null.:1
[Web Console] Uncaught Error: NETWORK_ERR: XMLHttpRequest Exception 101:1
Which I think has to do with Chrome Browser file permissions, so I set the AllowFileAccess setting on the WebView to true, but still get the same error.
Is there a better way to accomplish a bridge between javascript and C# in Mono, or a way to get the HTTPListener code to work with Android?
This article comes up in the Xamarin documentation search, but is a 404:
http://docs.xamarin.com/recipes/android/controls/webview/call_c#_from_javascript
Thanks
Derrick
I was able to connect JS and C# in Mono for Android by extending the WebViewClient and overriding the ShouldOverrideUrlLoading method:
Activity Class:
using Android.Runtime;
using Android.Views;
using Android.Widget;
using Android.OS;
using Android.Webkit;
using System.IO;
using System.Net;
using System.Text;
namespace AnroidHTML5Prototype
{
[Activity (Label = "AnroidHTML5Prototype", MainLauncher = true)]
public class Activity1 : Activity
{
protected override void OnCreate (Bundle bundle)
{
WebView webView = new WebView (this);
webView.Settings.JavaScriptEnabled = true;
webView.SetWebChromeClient(new WebChromeClient());
string html = "<html><head><title></title></head><body><h1 id='button'>Hello Android</h1><h1 id='message' style='color:red'></h1><script>document.getElementById('button').onclick=function(){window.location='/callnative';}; function callback(msg){document.getElementById('message').innerHTML = msg;}</script></body></html>";
webView.LoadData (html, "text/html", null);
webView.SetWebViewClient (new MyWebViewClient(webView));
base.OnCreate (bundle);
SetContentView (webView);
}
}
}
Extended WebViewClient class:
using System;
using Android.Webkit;
using Java.IO;
using Java.Net;
namespace AnroidHTML5Prototype
{
public class MyWebViewClient : WebViewClient
{
WebView webView;
public MyWebViewClient (WebView webView) : base()
{
this.webView = webView;
}
public override bool ShouldOverrideUrlLoading (WebView view, string url)
{
if (url == "/callnative")
{
webView.LoadUrl ("javascript:callback('Hello from Android Native');");
return true;
}
return base.ShouldOverrideUrlLoading(view, url);
}
}
}
In Android the localhost IP (the IP of the computer the emulation is running on) is not 127.0.0.1
it's 10.0.2.2 ref
that may or may not be the source of your error.
to anyone interested - I was able to run initial code sample without
RunOnUiThread(delegate{
Also in my html file I used jQuery AJAX call
<script type="text/javascript" src="../../js/jquery-1.9.1.js"></script>
...
<script type="text/javascript">
$(document).ready(function () {
$('#example-1').click(function () {
$.ajax({
async: true,
url: 'http://localhost:8801/test/url/test.htm'
});
$(this).load('ProgressDialog.htm');
});
});
</script>
You may also note that localhost is used instead of 127.0.0.1 so basically same code works on both Xamarin.iOS and Xamarin.Android .
We did crossplatform desktop HTML app that shares same codebase for Mac and Windows using Mono with WebKit.NET and Monobjc as WebKit webview controls. Completely free toolset btw, including InApps for Mac( yep Miguel, 999$ per platform is a way too much for most guys including myself ;) From work on that app I know that only stuff like
.EvaluateJavaScript(..)
needs to be invoked on main (aka UI) thread (and all the UI stuff of course), so not sure why you used RunOnUiThread in your question's code sample.
Hope that will help someone:)
I have an application with just a visible WebView component to it which is used to display some dynamically generated HTML (can run Javascript too). It's enabled for the WebView.
For a few pages I am trying to set the focus of one of the input text boxes after the page load has finished - not through Body onLoad(), but instead by a JS call after the page has finished loading i.e. in onPageFinished(). Most of the javascript executes fine
webview.loadURL("javascript:JSMethod();");
But when I use the same call to do a document.getElementById('text1').focus() - the cursor does reach the element but the Soft Keyboard won't pop out. The same javascript code when executed from a button on the page does have the desired effect.
I am attaching the source code which has 3 buttons
Focus Text1 - Put the cursor on the text1 and pops up the Softkeyboard.
Java Focus Text1 - Calls Java to execute the same JS. Only shows the cursor there and doesn't pop out the keyboard
Java Focus Text1 And Keyboard Open - Calls JS from Java and Forces Keyboard open.
My understanding is that the JS execution should work the same whether executed from the browser using a button/event or as sent from Java through WebView.loadURL().
Here's my Queries
Am I missing something when using Button#2? That's how my current code is and I only see the cursor is set but the SoftKeyboard won't open.
When I use logic as written in Button#3, I some times don't see the cursor on the field but gives me the desired effect and the cursor becomes visible when I type something in the popped up keyboard.
Could the behavior I see in Button#2 be a bug in Android JS execution? Or could it be that the WebView doesn't have focus that's why only the cursor is displayed? I also tried webview.requestFocus() - I can't write requestFocusOnTouch() as it's the only View I have and am expecting it's focused automatically.
The Java code which demos the behavior is
import android.app.Activity;
import android.content.Context;
import android.os.Bundle;
import android.util.Log;
import android.view.inputmethod.InputMethodManager;
import android.webkit.JsResult;
import android.webkit.WebChromeClient;
import android.webkit.WebView;
import android.webkit.WebViewClient;
public class WebViewProjectTestJSHTMLFocusActivity extends Activity {
Activity _current = null;
WebView wv = null;
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
_current = this;
//setContentView(R.layout.main);
String data = "<html><body>" +
"<script>function focusser() { document.getElementById(\"text1\").focus(); } </script>" +
"<script>function javaFocusser() { javautil.javaFocus(false); } </script>" +
"<script>function javaFocusserKeyboard() { javautil.javaFocus(true); } </script>" +
"Text 1<input type='text' id='text1'/><br/>" +
"Text 2<input type='text' id='text2'/><br/>" +
"<input type='button' value='Focus Text1' onClick='focusser()'/>" +
"<input type='button' value='Java Focus Text1' onClick='javaFocusser()'/>" +
"<input type='button' value='Java Focus Text1 And Keyboard Open' onClick='javaFocusserKeyboard()'/>" +
"</body></html>";
wv = new WebView(this);
wv.getSettings().setJavaScriptEnabled(true);
// Set some HTML
wv.loadDataWithBaseURL("file:///android_asset/", data, "text/html", "UTF-8", null);
// Call back required after page load finished
wv.setWebViewClient(new CustomWebViewClient());
// Enable Alert calls
wv.setWebChromeClient(new CustomWebChromeClient());
// For JS->Java calls
wv.addJavascriptInterface(this, "javautil");
setContentView(wv);
}
/**
* Calls the same javascript and forces the keyboard to open
*/
public void javaFocus(final boolean shouldForceOpenKeyboard) {
Thread t = new Thread("Java focusser thread") {
public void run() {
wv.loadUrl("javascript:focusser();");
if(shouldForceOpenKeyboard) {
InputMethodManager mgr = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
mgr.showSoftInput(wv, InputMethodManager.SHOW_IMPLICIT);
}
}
};
// Run on the View Thread.
_current.runOnUiThread(t);
}
/**
* Calls focus method after the page load is complete.
*/
final class CustomWebViewClient
extends WebViewClient {
#Override
public void onPageFinished(WebView view, String url) {
// javaFocus(true);
Log.d("TestExamples", "focusser call complete");
}
}
final class CustomWebChromeClient
extends WebChromeClient {
#Override
public boolean onJsAlert(WebView view, String url, String message, JsResult result) {
Log.d("TestExamples", "JS Alert :: " + message);
return false;
}
}
}
Solution Update 24-06-2011
To make this work, you need to use wv.requestFocus(View.FOCUS_DOWN) just before the actual JS focus call. I modified the javaFocus() method above to the correct version below. Earlier when I mentioned that I was using requestFocus(), I was using that when the WebView was initialized in the method onCreate(). The primary difference is now we're forcing the WebView to get focus each time just before the Javascript document.getElementById("text1").focus(); is executed.
public void javaFocus(final boolean shouldForceOpenKeyboard) {
Thread t = new Thread("Java focusser thread") {
public void run() {
wv.requestFocus(View.FOCUS_DOWN);
wv.loadUrl("javascript:focusser();");
if(shouldForceOpenKeyboard) {
InputMethodManager mgr = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
mgr.showSoftInput(wv, InputMethodManager.SHOW_IMPLICIT);
}
}
};
// Run on the View Thread.
_current.runOnUiThread(t);
}
Also to ensure that this issue wasn't fixed because of focus triggered through touch etc, I am using a background thread to initiate the javaFocus() after 5 seconds of WebView Displayed. The modified onCreate() is below.
..... More onCreate code before....
// Enable Alert calls
wv.setWebChromeClient(new CustomWebChromeClient());
// For JS->Java calls
wv.addJavascriptInterface(this, "javautil");
setContentView(wv);
new Thread("After sometime Focus") {
public void run() {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
javaFocus(true);
}
}.start();
.... onCreate() ends after this....
It could be that the webview doesn't have the application focus. Try executing;
wv.requestFocus(View.FOCUS_DOWN);
Here is the complete solution.
protected void onCreate(Bundle savedInstanceState) {
// setfocus on page load finished
myWebView.setWebViewClient(new WebViewClient() {
#Override
public void onPageFinished(WebView view, String url) {
super.onPageFinished(myWebView, url);
myWebView.requestFocus(View.FOCUS_DOWN);
Toast.makeText(getApplicationContext(), "Done!", Toast.LENGTH_SHORT).show();
}
});
}