I have created a cordova application. I am running an background service to perform some native task in the application. I need to trigger a java-script event once the background service complete its task. Is it possible to trigger js events from android?. Not able to find any solid answers for this. I need events because the application wound wait for the task in background service to complete. I want to event to notify the application that the task is complete. Is there any better way to implement this logic?.
Cordova itself doesn't expose its webview properties publicly for use by other Java classes, but you can do this with a minimal Cordova plugin which would allow your background service to access the Cordova webview in order to execute javascript in it from the Java layer. Then it's just a question of injecting the JS to trigger an event.
First you'd create a Cordova plugin to expose the necessary elements of Cordova to your background service:
public class MyPlugin extends CordovaPlugin{
private static final String TAG = "MyPlugin";
static MyPlugin instance = null;
static CordovaWebView cordovaWebView;
static CordovaInterface cordovaInterface;
#Override
public void initialize(CordovaInterface cordova, CordovaWebView webView) {
super.initialize(cordova, webView);
instance = this;
cordovaWebView = webView;
cordovaInterface = cordova;
}
#Override
public void onDestroy() {
instance = null;
}
private static void executeGlobalJavascript(final String jsString) {
if (instance == null) {return;}
instance.cordovaInterface.getActivity().runOnUiThread(new Runnable() {
#Override
public void run() {
try {
instance.cordovaWebView.loadUrl("javascript:" + jsString);
} catch (Exception e) {
Log.e(TAG, "Error executing javascript: "+ e.toString());
}
}
});
}
public static void triggerJavascriptEvent(final String eventName){
executeGlobalJavascript(String.format("document.dispatchEvent(new Event('%s'));", eventName));
}
}
Then your background service can call the public method exposed by that plugin class:
public class MyService {
public static void myMethod(){
MyPlugin.triggerJavascriptEvent("myserviceevent");
}
}
And finally, in your Cordova app's JS layer, you'd listen for your custom event:
document.addEventListener('myserviceevent', function(){
console.log("myserviceevent received");
}, false);
I've created an example Cordova project which contains the minimal custom plugins required to achieve this which you can download here: http://ge.tt/8UeL6lu2
Once downloaded, unzip then:
cd cordova-test
cordova platform add android
cordova run android
Related
In the uwp c# Windows Runtime Componment project, I want to expose a api that webview js can call it with some callback.
So I am trying:
// c#
[Windows.Foundation.Metadata.AllowForWeb]
public sealed class xx{
public void setOnChange(string name,System.Action action) {
xxx
}
}
// js
windows.xx.setOnChange("js",function(){xxx});
Then I got 'system.Action' is not a valid windows runtime parameter type error.
Then I try another way:
//c#
public delegate void Action();
[Windows.Foundation.Metadata.AllowForWeb]
public sealed class xx{
public void setOnChange(string name,Action action) {
xxx
}
}
// js
windows.xx.setOnChange("js",function(){xxx});
The c# can compile , and setOnChange get called, but js do not called.
Finially I find a solve:
//c#
[Windows.Foundation.Metadata.AllowForWeb]
public sealed class xx{
public void setOnChange(string name,EventHanlder<Object> action) {
xxx
}
}
// js
windows.xx.setOnChange("js",function(){xxx});
I guess EventHanlder<Object> is a special type in Windows Runtime Componment.
Ps: I have read document of https://learn.microsoft.com/en-us/windows/uwp/winrt-components/raising-events-in-windows-runtime-components .
Then I find that it can be done with a class wrap with event handler. It have a lot of code both in js and c#, then I try embed the event handler into parameters, then it works. A hidden/undocument feature of uwp?
In worklight, I am using WL.NativePage.show for android native call. As I am doing so much process in activity(native), It throws me error "The application may be doing too much work on its main thread".
As resolution I used threading for calculation(so much process) and It is working OK. But In this case, Native page showed up.
But I just want some calculation on input (From JS) in native and output(At JS) without rendering activity.
...
public class EmbeddedCalculator extends Activity {
public static Boolean isSuccessful = false;
private Calculation calculation = new Calculation();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Runnable runnable = new Runnable() {
#Override
public void run() {
// .. calculation - Higher process ..
}
};
Thread t= new Thread(runnable);
t.start();
}
}
Then why use WL.NativePage at all?
Since you did not mention the actual version of Worklight that you are using, I will just list possible alternatives:
Create a Cordova plug-in that will invoke native code and return the result: https://developer.ibm.com/mobilefirstplatform/documentation/getting-started-7-1/foundation/adding-native-functionality/ - tutorials and samples are available
Use the SendAction APIs to invoke native code (MobileFirst Platform Foundatin 6.3 and above): http://www-01.ibm.com/support/knowledgecenter/SSHS8R_7.1.0/com.ibm.worklight.dev.doc/devref/c_action_sender.html
I am creating a cordova plugin where i need to raise a custom defined event in the angular JS code.
For example I need to call the function below from native java code
var callFromJava=function(){
alert("Call received from Native code");
}
Now I need to call this from my activity in native code.
Update 1 Cordova file
public class CordovaApp extends CordovaActivity
{
super.onCreate(savedInstanceState);
super.init();
wv = new CordovaWebView(this);
Log.i("PARSEPUSH","URL of main "+wv.getUrl());
// Set by <content src="index.html" /> in config.xml.
Log.i("PARSEPUSH",launchUrl);
loadUrl(launchUrl);
public void callJS(){
//something goes here to call JS event.
}
}
I want to use Cordova loadUrl and sendJavascript() methods. I don't know how to use them.
public void callJS() {
if (this.appView != null) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
appView.stopLoading();
appView.evaluateJavascript("(function(){return document.location.href;})();", new ValueCallback<String>() { #Override
public void onReceiveValue(String value) {
//doSomething for the return value
}
});
} else {
appView.loadUrl("javascript:alert(document.location.href);");
}
}
Refer this code snippet to see if it's help.This is not relative with Codorva Phonegap just call Js from Java side.
If you want to write Cordova Plugin,you may could refer Devgirl's Weblog.She wrote many excellent articles about the Phone-gap extension.
I am trying to use a webview with an input box, take the string, and pass it to a second activity. For some reason, the button is not doing anything and I get the error:
Uncaught TypeError: Object [object Object] has no method 'changeActivity' at file:///android_asset/www/index.js:3
So my HTML says this:
<input id="name" value="" />
<button onclick="checkMovie()"> Check it! </button>
my JS says this:
function checkMovie() {
var movieName = document.getElementById('name').value;
webapi.changeActivity(movieName);}
and my Android code says this:
public class MainActivity extends Activity implements OnClickListener {
// Setting up known variables
JavaScriptInterface JSInterface;
Button find;
#SuppressLint("SetJavaScriptEnabled")
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// changing main layout to show the splash first
setContentView(R.layout.activity_main);
// Tie my webviews and set up the webview to handle JS
WebView webView = (WebView) findViewById(R.id.webView);
webView.getSettings().setJavaScriptEnabled(true);
// Expecting UI
webView.setWebChromeClient(new WebChromeClient());
// allowing to inject Java objects into a page's JavaScript
webView.addJavascriptInterface(new JavaScriptInterface(this), "webapi");
// Load the local file
webView.loadUrl("file:///android_asset/www/index.html");
find = new Button(MainActivity.this);
find.setOnClickListener(this);
WebSettings ws = webView.getSettings();
ws.setJavaScriptEnabled(true);
// Add the interface to record javascript events
webView.addJavascriptInterface(find, "find");
}
public class JavaScriptInterface {
Context mContext;
// Instantiate the interface and set the context
JavaScriptInterface(Context c) {
mContext = c;
}
// Call the function changeActivity defined in my JS of my HTML
public void changeActivity(String movieName) {
// Call the Movie App and store the intent to pass it onto the app.
Log.e("XXXXXXXXXXXXXXX", "X==========>" + movieName);
Intent i = new Intent(MainActivity.this, second.class);
i.putExtra("movie_name", movieName);
startActivity(i);
}
}
#Override
public void onClick(View v) {
// TODO Auto-generated method stub
if (v.equals(find)) {
Log.e("XXXXXXXXXXXXXXX", "XXXXXXXXXXXXXXX");
}
}
}
The problem is exactly that. Since you run on 4.2.2 you have to set the #JavascriptInterface annotation on top of your changeActivity method and on any other javascript methods you call in your android code.
#JavascriptInterface
public void changeActivity(String movieName) {
...
This is necessary for Android 4.2 or higher.
Follow this link to read more.
First on Android 4.2 and higher version, make sure you added the #JavascriptInterface annotation.
Then if you find it stopped working on release(production) mode, it is because of the proguard.
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
# -keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
if the device is 4.2.2, you have to set the annotation #JavascriptInterface.
But unfortunately, this interface is not existing in the SDK before 4.2.2.
for this case, you have two options:
change the target of your app, from 4.0/4.1 to 4.2
if you won't to change the target, create a java file android/webkit/JavascriptInterface.java in your own source code and copy the content from 4.2 SDK
I don't understand how it is possible to do this, since there are no WebActivities with PhoneGap, only your Activity classes and your index.html page. I have a MainActivity that looks like this...
public class MastersProjectActivity extends DroidGap
{
#JavascriptInterface
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
// Set by <content src="index.html" /> in config.xml
//super.loadUrl(Config.getStartUrl());
super.loadUrl("file:///android_asset/www/index.html");
Context myCntxt = getApplicationContext();
TelephonyManager tMgr = (TelephonyManager)myCntxt.getSystemService(Context.TELEPHONY_SERVICE);
String curPhoneNumber = tMgr.getLine1Number();
Log.d("PhoneNumber", curPhoneNumber);
}
}
...and I want to be able to use curPhoneNumber in the index.html page, which in PhoneGap contains the entirety of the UI. Any ideas on how I can do this? I'm completely new to Android development in general, and certainly with PhoneGap. To be honest I still don't really understand how the index.html page is rendered as an Android UI. Do I need to wrap the index.html page in a WebActivity or something? I'm not sure if this would create any issues in the way PhoneGap creates the .apk.
EDIT - I tried to implement but it didn't work for me, all I got was an alert literally saying "interface.getPhoneNumber()". I may have implemented the 'MyInterface' class incorrectly? Here's the class I made....
public class MyInterface extends DroidGap {
private MastersProjectActivity _activity;
private CordovaWebView _view;
public MyInterface(MastersProjectActivity activity, CordovaWebView view) {
this._activity = activity;
this._view = view;
}
#JavascriptInterface
public String getPhoneNumber() {
Context myCtxt = getApplicationContext();
return ((TelephonyManager)myCtxt.getSystemService(Context.TELEPHONY_SERVICE)).getLine1Number();
}
}
...and I exposed the javascript interface in my main activity like so...
#Override
public void onCreate(Bundle savedInstanceState)
{
.
.
.
super.appView.addJavascriptInterface(new MyInterface(this, appView), "interface");
}
...and then I call alert("interface.getPhoneNumber()") in the index.html file, but all I get is that string alerted, it doesn't actually call getPhoneNumber().
You can expose methods in an interface object, called MyInterface, which will be called from the Javascript running inside index.html. For example, you can have a method in MyInterface like:
#JavascriptInterface
public String getPhoneNumber(){
return (TelephonyManager)myCntxt.getSystemService(Context.TELEPHONY_SERVICE).getLine1Number();
}
Then, in your onCreate(), you will expose an instance of MyInterface, like this:
public void onCreate(Bundle savedInstanceState){
//...
super.appView.addJavascriptInterface(new MyInterface(this,appView), "interface");
}
Then, in your index.html, you will have some Javascript that calls this method.
For example,
<script type="text/javascript">
alert("interface.getPhoneNumber()");
</script>
Check out this question for details on how to do it.
EDIT: First, MyInterface does not need to extend DroidGap, e.g., it is a siple POJO.
Second, I made a small mistake in the JS: you don't need the double quotes.
<script type="text/javascript">
alert(window.interface.getPhoneNumber());
</script>
Third, as noted here, you will need to add this line after your super.onCreate():
super.init(); // Calling this is necessary to make this work
One small little think: You can use this._activity as your context, instead of calling getApplicationContext() in getPhoneNumber().